Julia - C interface with nonfundamental types

2019-04-29 15:37发布

I am extending a Julia package that uses a C library. I need to call some C functions from Julia. They look something like this:

struct contained {
    int x;
    int y;
    int z;
};
struct mystruct {
    int n;
    contained* arr;
};
mystruct* mk_mystruct(int n, contained* arr);
void use_mystruct(mystruct* foo);

I have also declared the corresponding types in Julia:

type contained
    x::Int64
    y::Int64
    z::Int64
end
type mystruct
    n::Int64
    arr::Array{contained, 1}
end

To ccall functions which take a contained* as an argument, everything works fine treating the contained* as Ptr{Int64}:

con = fill(0, 5, 3);
mys = ccall((:mk_mystruct, "mylib"), Ptr{mystruct}, (Int64, Ptr{Int64}), n, con)

I suppose this works because contained has the same memory layout as an array of Int64s. This is also how it is done elsewhere in the Julia package. But the only way I know to check the value of the returned mystruct is to dereference it with unsafe_load, at which point Julia crashes from a segfault. What is the right way to dereference a pointer in Julia?

The C library also includes pretty-printing functions, so instead of dereferencing the pointer in Julia I could treat the pointer as opaque and pass it back to this C function:

void print_mystruct(mystruct* foo, FILE* outputfile)

In the C code, this is called with outputfile=stdout. How would I set this up with ccall? This obviously does not work:

ccall((:print_mystruct, "mylib"), Void, (Ptr{mystruct}, Ptr{Void}), mys, stdout)

What should I put instead of Ptr{Void} and stdout? How does Julia implement I/O in the C interface?

标签: c julia
1条回答
叛逆
2楼-- · 2019-04-29 16:01

When you declare the type in Julia, you must declare the same types as C:

type contained
    x::Cint
    y::Cint
    z::Cint
end
type mystruct
    n::Cint
    arr::Ptr{contained}
end

The Julia type Array{contained, 1} would correspond to jl_value_t* in C and the Julia type Int would correspond to intptr_t in C.

I don't know of a platform-agnostic way to get a handle to stdout, as most platforms require expanding a C header macro to find out the real symbol name. For example, on macOS, it gets renamed to __stdoutp:

julia> unsafe_load(cglobal(:__stdoutp, Ptr{Void}))
Ptr{Void} @0x00007fff751f7348

julia> ccall(:fprintf, Csize_t, (Ptr{Void}, Cstring, Cint...), ans, "hi\n")
hi
0x0000000000000003

You may be interested in checking out the Clang.jl package which can automatically generate these definitions from parsing the header files.

查看更多
登录 后发表回答