skrystos
skrystos

Reputation: 107

Getting char* and size_t from C to ruby, and pass them from Ruby to C without FFI (using SWIG)

I have question because I'm out of ideas here. I have c library wrapped with SWIG. I do not use FFI because I don't want to have any 3rd party dependencies in my GEM. The thing is I faced some problem. I have function in C that is creating char* buffer and returns it size. I need to return to ruby size of the buffer and buffer itself. With size_t isn't big problem, I can store it on ruby side as int, and cast it later after going back to C, but I have problem with char*.

When I return char* from C to ruby it's converted to string. So I cannot return it to C as char* anymore. I was trying to do some dirty things like taking memory address of char*, store it as string and pass it back to C but doesn't work neither.

what is use case. I need to link two ruby threads using 3rd party c library. So I need to create link instance on thread one and pass char* and size_t pointers to thread2, an there initialize link. So there is no option of changing this approach.

The question is, is it possible to pass reference to char* pointer C -> Ruby -> C and how to do that. I'm digging for solutions since 2 days but I have no good ideas here.

Thx in advance :)

Example function that returns char* from C to Ruby

char* link_rb() {
    size_t in_process_link_size = 0;
    link_create(NULL, 0, &in_process_link_size);
    unsigned char* in_process_link = NULL;
        if (in_process_link_size != 0) {
            in_process_link = (unsigned char*)malloc(in_process_link_size);
            if (in_process_link != NULL)
                in_process_link_size = link_create(in_process_link, in_process_link_size, NULL);
        }
    
    return in_process_link;
}

link_create is not my function I need to use it here (cannot change it).

And on the other side I need do something like this:

tracer_handle_t tracer_create_rb(unsigned char const* link) {
    size_t link_size = 40;

    char* buffer = (char*) &link;
    
    size_t tracer = inprocesslinktracer_create(link, link_size);
    return tracer;
}

inprocesslinktracer_create is as well not my function. Came from SDK

Let's assume for now that I will return only char* and pass it as well as single parametr. In general I need as well size of the buffer, I can go to struct here but first I want to figure it out here.

Upvotes: 0

Views: 71

Answers (1)

John Bollinger
John Bollinger

Reputation: 181159

The question is, is it possible to pass reference to char* pointer C -> Ruby -> C

Yes.

SWIG does by default assume that objects of type char * should be wrapped so as to give access to the pointed-to data, not the pointer itself. This is different from how it handles other pointers. However, SWIG's mapping behavior is customizable via Typemaps, and where necessary, you can help it out by writing a shim (not needed for the example presented).

The example code could be served with simple input and output typemaps:

mymodule.i

%module mymodule

/* Relying on SWIG to #include <ruby.h> */
%typemap(in) unsigned char const *AS_NUMBER {
    $1 = (unsigned char const *) NUM2ULL($input);
}
%typemap(out) char *link_rb {
    $result = ULL2NUM((unsigned long long) $1);
}

%{
#include "tracer.h"
%}

/*
 * We don't want SWIG to process all of tracer.h.  This is what we actually
 * want wrapped, and any additional declarations needed to fully define that.
 * These declarations need to be compatible with the those in tracer.h:
 */
#include <stddef.h>
typedef size_t tracer_handle_t;

char *link_rb(void);
tracer_handle_t tracer_create_rb(unsigned char const *AS_NUMBER);

The in typemap specifies a Ruby -> C conversion for parameters or objects matching the given name and datatype. Similarly, the out typemap specifies a C -> Ruby conversion for return values or objects matching the given name and datatype. Note that the AS_NUMBER has only the significance given it by that interface file. It is not special to SWIG.

In general I need as well size of the buffer, I can go to struct here but first I want to figure it out here.

As you like.

Upvotes: 0

Related Questions