Calling back into Lua from C code

The problem

One thing I found incredibly hard when adapting a large C codebase with Lua code was the problem of calling back from C code into Lua callback when there’s no auxiliary passed around void * pointer available to also pass the current lua_State.

Of course it would always be possible to just create a new state on-the-fly but if you do you won’t have access to any of the already setup Lua environment so your Lua callbacks might end up to be very complicated and heavy.

A solution

This solution might not be a workable solution for you but it certainly was for me. Two points are rather important here:

  1. There is an initialisation function which will be called at a defined point in time where the Lua environment is setup nicely for all potential Lua callbacks to work
  2. There’s no relevant program state stored in the Lua state, all program state is handled by the C code and can be accessed by the appropriate Lua functions in the global environment

If those prerequisites are met then one can use the lua_newthread function to create a copy of the current lua_State and store it in a static variable.

In essence this is what I’m doing:

static lua_State *iL = NULL;

void my_init (lua_State *L)
{
        /* ... other initialisation code here ... */

        iL = lua_newthread (L);

        /* It's important to keep a reference to the new state somewhere,
         * otherwise the garbage collector will eventually collect it */
        luaL_ref (iL, LUA_REGISTRYINDEX);
}

At any later point you can use the iL variable, rather than the usual Lua C function signature with a single lua_State argument, to execute Lua code in order to manipulate the program state.

In closing

While this mechanism alone may in many cases only have limited usefulness since the callbacks themselves need to be C code and hence known at compile time (though it certainly is possible to e.g. let those callbacks execute specific Lua files from the filesystem and hence allow changes in behaviour without recompilation), it can be combined with other mechanisms to e.g. allow setting up Lua functions as callbacks – directly from Lua. But more about that in a later post. ;)

Until then, take care.