Using C struct in Julia without creating same struct in Julia again

Hi, we have ccall to call any c function. But, if the function we want to call has a parameter which is non-primitive type such as a struct, do we always have to create same struct in Julia? For example, I have this c code.

#include <StaticLibrary.h>

typedef struct 
{
    struct StaticLibrary* thing;
} Bar;

typedef struct 
{
    Bar b;
    char* t;
} Foo;

void myFunction(Foo* foo, char t*)
{
//code
}

I want to use myFunction with ccall I generate a shared library from my C code, and I use it. it works. But I need to create same structs in Julia too. so, Why? I can write same function in Julia too! And I also have a StaticLibrary and struct StaticLibrary* thing; is coming from there, I don’t have the declaration of that struct for creating it in julia. I hope, I could explain myself… Sorry for my English. So my question is “do we always have to create same struct in Julia to pass argument?”

If the interface of your C code is all about passing handles/opaque pointers, then there is no need to map those underlying structures on the Julia side. You could make everything a Ptr{Cvoid} or define a opaque singleton struct mutable struct Foo end and use Ptr{Foo}.

I tried mutable struct Foo end and using Ptr{Foo} . but I get an Error. It is Julia Part:

using Libdl
libJulia3D = dlopen("../build/libJulia3D.so")
    
j3d_window_create = dlsym(libJulia3D, "j3d_window_create")
j3d_window_initialize = dlsym(libJulia3D, "j3d_window_initialize")
j3d_window_destroy = dlsym(libJulia3D, "j3d_window_destroy")


mutable struct J3DWindow end

function init(window, width, height, title)
    ccall(j3d_window_create, Cchar, (Ptr{J3DWindow}, Csize_t, Csize_t, Cstring), window, width, height, title)
end

#Main
window = J3DWindow()

init(window, 600, 400, "abc")

and my c part is:

JULIA3D_CORE struct J3DWindow
{
    GLFWwindow* m_window;
    size_t m_width;
    size_t m_height;
    char* m_title;
};

JULIA3D_CORE bool j3d_window_create(J3DWindow* window, size_t width, size_t height, const char* title)
{
    window->m_width = width;
    window->m_height = height;
    window->m_title = title;
}

That’s because J3DWindow in C is not an opaque pointer.

typedef JULIA3D_CORE struct _J3DWindow
{
    GLFWwindow* m_window;
    size_t m_width;
    size_t m_height;
    char* m_title;
} *J3DWindow;

JULIA3D_CORE bool j3d_window_create(J3DWindow window, size_t width, size_t height, const char* title)
{
    window->m_width = width;
    window->m_height = height;
    window->m_title = title;
}

now, is it opaque right? if not, can you explain?

ohh sir, I changed
ccall(j3d_window_create, Cchar, (Ptr{J3DWindow}, Csize_t, Csize_t, Cstring), window, width, height, ) part as ccall(j3d_window_create, Cchar, (Ref{J3DWindow}, Csize_t, Csize_t, Cstring), window, width, height, ) it works, thanks for all your answers.

No.

In short, you should not expose the definition of _J3DWindow in the header file. To work with opaque pointers, static_cast/reinterpret_cast/C-style cast should be correctly used in the function definition.

This is not right. Ref{J3DWindow}/Ptr{J3DWindow} is a pointer to J3DWindow which is a pointer to _J3DWindow. But your C function expects a pointer to _J3DWindow.

yeah you are right but Ptr{J3DWindow} in Julia is _J3DWindow* in C. Yeah it is little bit confusing because of names.

Indeed. :sweat_smile:

BTW, you may find Clang.jl is useful for auto-generating Julia bindings from C headers.

1 Like

You might also want to look at using CBinding.jl for more faithfully preserved names and types in the bindings, so you might have fewer surprises.