3
votes

I have a type definition corresponding to a C-struct as follows:

type fakeCStruct
  a::Uint8;
  b::Uint32;
end
var=fakeCStruct(3,4);

How can I pass a pointer to this type as an input argument (i.e. struct CStruct *) for a c-function in ccall?

The old documentation (v0.3) suggest using &var in the input argument list of ccall. However that statement is deleted in v0.4 documentation (also, &var does not work in v0.4).

3

3 Answers

0
votes

Use Ref{fakeCStruct}:

r = Ref(fakeCStruct(3, 4))
ccall((:somefunction, "lib"), Void, (Ref{fakeCStruct},), r)

From the Julia docs in the System Independent table here:

C name:                                                   |   Julia base type
T* (where T represents an appropriately defined type)     |     Ref{T}
0
votes

Allocate memory for the Julia object, then pass it by pointer:

type fakeCStruct
  a::UInt8;
  b::UInt32;
end

var = fakeCStruct(3,4)

var_p = Ptr{fakeCStruct}(pointer_from_objref(var))

ccall((:somefunc, "lib"), Void, (Ptr{fakeCStruct},), var_p)

(Using Ref{} doesn't work for me... using Julia 0.4.0)

0
votes

Although I'm definitely no expert on this I iterated to some code which works for me. Hopefully this will help people on their way. In the example foo.c is compiled to a shared library (.dll in my case) and called from foo.jl.

foo.c

struct mystruct {
    int a;
    int b;
};

typedef struct mystruct mystruct_t;

int recvstruct(mystruct_t* st) {
    printf("C %u, %u\n", st->a, st->b);
    return 0;
}

mystruct_t* updatestruct(mystruct_t* st) {
    // here we need to return the pointer, since Julia seems to 
    // pass a pointer to a copy of the struct
    st->a = 10;
    st->b = 11;
    return st;
}

foo.jl

struct mystruct
    a::Int32
    b::Int32
end

function let_c_print_struct()
    # Note that the function call automatically converts `mystruct(3, 4)` to `::Ref(..)`.
    ccall((:recvstruct, libfile), Int32, (Ref{mystruct},), mystruct(3, 4))
end

function let_c_update_struct()
    st = mystruct(5, 6)
    ret = ccall((:updatestruct, libfile), Ref{ mystruct }, (Ref{ mystruct }, ), st)

    @show st # st = mystruct(5, 6)
    @show ret[] # ret[] = mystruct(10, 11) 
end

Regarding lifetime of data inside the shared library: I found that objects stored in memory remain available (I would guess that they are outside of the scope of the garbage collector). Four observations support that the data remains available:

  • Julia keeps the library open unless it is explicitly closed.
  • The Julia language source code uses unsafe_string (dereferencing pointer) on values returning from a ccall. See search results for unsafe_string(ccall(.
  • I found that allocating data (even structs containing pointers) in C and passing references to this data around in Julia does work.
  • I found that sockets remain open.