Access to C data from Lua

The problem

If you’d like to combine your C program code to Lua code you’ll always face the challenge of having to interface data already available in the C part from Lua. Now as we know there’s no general way to pass along C pointers from anywhere you’d like, especially not from Lua code, so instead you’ll have to store the information somewhere else and figure out a way to retrieve it or you’ll need to create Lua structures acting as a proxy.

Possible solutions

  1. Light userdata: Light userdata is just a fancy way of wrapping a pointer into a Lua structure. Since it really is only a value expressing a pointer the possibilities are somewhat limited: While you can use those pointers in a table, both as a key and as a value, if you’re actually interested in the value you’ll have to ensure that they stay valid (which can be sort of tricky if data pointed to is allocated and deallocated out of the Lua realm) and if you pass them around you actually have no way of ensuring you’re passing or receiving the right data.

    What you can do is taking some unique static pointer (e.g. a forked Lua state or thread) and use it as an index in the registry or some other table to find relevant data at a later point.

    Another possibility is using it as a value, e.g. by putting a static pointer to some C structure into a table using a known key so different parts of the implementation can access it. The key could even be something managed from Lua land.

  2. Userdata: Rather than being just a pointer, regular userdata resembles memory allocated and managed by Lua. Also one can “tag” the data which allows to differentiate passed in userdata types later. The one big downside of using userdata is that the lifetime of an userdata object is determined by an variable being (and staying) in scope, as soon as it goes out of scope it eventually will get collected by the garbage collector and hence be dead. Preventing userdata from getting collected can be a major PITA or prevent collection forever and hence it is not recommended to use userdata to allocate memory for use outside of a Lua controlled program flow.

  3. Userdata as a proxy: If your C code already manages memory for structure or objects and you simply want to refer to that you can use userdata as typed proxy objects. That way you don’t have to worry about about the lifecycle of the userdata. However you’ll need to make sure that the data you’re referring to does not get freed while you’re still holding on to a userdata referencing it, which can also be a problem. Of course there’s also the possibility of keying the data and looking it up every time you’re trying to access it but that might be rather expensive. Using userdata as a proxy is actually rather straight forward:

    struct myproy
    {
        void *data;
    };
    
    static int my_constructor (lua_State *L)
    {
        struct myproxy *ud = (struct myproxy *) lua_newuserdata (L, sizeof (struct myproxy));
        ud->data = data;
        luaL_getmetatable (L, "myproxiedtype");
        lua_setmetatable (L, -2);
    }
    

    And later you can extract the info again from the passed userdata:

    static int my_user (lua_State *L)
    {
        struct myproy *ud = (struct myproxy *) luaL_checkudata (L, 1, "myproxiedtype");
        do_fancy_stuff_with_data (ud->data);
    }
    

In closing

Interfacing C and Lua can be bitchy, especially when using a state heavy approach like Lua together with a heap-heavy approach like C, and you can not completely remodel everything to be a total natural fit. So there might be a bit of improvisation needed and some ideas on which options are available. I hope I could help to clear that up a tiny bit and I will likely come back with a full recipe utilizing userdata some time soon.

Until then, take care.