Search code examples
c++dllluaffiluajit

creating a callback struct to pass into the LuaJIT FFI


So first I load in a DLL I need

local ffi = require("ffi")
local theDLL = ffi.load("thisDLL")

in the ffi cdef I have two different kinds of structs

ffi.cdef [[
    typedef struct StructSession StructSession;
    typedef struct {
        /*
        * begin_proj callback
        */
        bool (__cdecl *begin_proj)(char *proj);

        /*
        * save_proj_state
        */
        bool (__cdecl *save_proj_state)(unsigned char **buffer, int *len);
    } StructCallbacks;

I also have this function in the cdef

__declspec(dllexport) int __cdecl start_session(StructSession **session,
                                                           StructCallbacks *cb);

Now I would like to call this function

print(theDLL.start_session(a,b))

vars a and b are obviously placeholders, the question is how can I pass the structs the function needs? And say we get StructSession working, is making a callback to a function WITHIN LuaJIT even going to be possible for StructCallbacks?


Solution

  • Creating the StructCallbacks is easy; you can create it with ffi.new and create FFI callbacks for the fields (see the FFI semantics for information on callbacks).

    Creating the StructSession is trickier since it's an opaque type, but it's not much different from how you would do it in C.

    Here is how you would create one in C:

    StructSession* S = NULL;
    start_session(*S, foo);
    

    Notice how you are not directly allocating a StructSession. Instead, you allocate a pointer to one, and let start_session allocate the actual struct.

    So we now translate this to LuaJIT code:

    local S = ffi.new("StructSession*")
    lib.start_session(getPointer(S), foo) -- getPointer should take the pointer of S, but...
    

    ...the FFI doesn't provide any way to take the pointer of an object (This is intentional; it allows for optimizations).

    So how do we get a pointer to a StructSession? Well, recall that arrays are convertible to pointers, and we can access those through the FFI. So we instead create a single-slot array of pointers and pass that to start_session:

    local S_slot = ffi.new("StructSession*[1]")
    lib.start_session(S_slot, foo)
    local S = S_slot[0]
    

    And now you have a StructSession object.