lua-users home
lua-l archive

[Date Prev][Date Next][Thread Prev][Thread Next] [Date Index] [Thread Index]


On Mon, Mar 12, 2012 at 5:18 PM, Tim Perkins <tprk77@gmail.com> wrote:
> I'm writing a lua module in C, to interface with some existing C code.
> The existing C code requires the programmer to register many
> callbacks. Registering a callback means calling a "register" function
> with the following arguments: a function pointer to the callback and a
> void pointer (for arguments to the callback). I want to make it so a
> lua function can be passed to the "register" function. (This is sort
> of similar to the expat example from the lua book, but there are many
> unique callbacks, not just three.)

One thing I've started doing, is that for C libs that look like:

  ctx_set_callback_a(ctx, fn* f, void* v); ... others ...
  ctx_do_stuff(ctx); // will call f with v

the obvious thing to do is map this directly to lua:

  ctx = ctx.new(...)
  ctx:set_callback_a(function () .. end)
  ctx:do_stuff()

This works, but it involves storing refs to the lua functions in a way
that the lua code can find them. This has a whole bunch of well-known
solutions that others on this thread have described, but another
approach, is to provide the table of callbacks *every time* to
do_stuff:

  ctx = ctx.new()
  cb = { do_a = function() ... end, -- others .... }
  ctx:do_stuff(cb)

The advantage of this, is that the lua stack is set up on call into C
with ctx, then the cb table, so the callbacks
are already on the lua stack at index [2]. When the C callbacks occur,
they need to find the lua_State (but this problem you
always have, and must have solved), and as soon as they have found it,
they can get the callback table from
[2] of the stack, and pull there associated lua function from that table.

Besides that this is easier to implement, I think its (slightly) more
idiomatic. Rather than making a whole wack of function calls to
describe what handlers you do or do not want, you instead create a lua
table describing what you want to handle. I feel
this is more luaish, and it cuts down in the number of interfaces to the module.

As a side-effect, it allows you to change the callback every time, if
you want (could be useful if the cbfn was associated with
a state machine, and you change the function as the state changes).

Down-side, is that the easy implementation of this means registering a
C handler for every single C callback, even if a lua
handler does not necessarily exist. This is not likely to be a
problem, the overhead of a C callback that then decides to do nothing
is probably low. If it does
matter, then you can dynamically register/unregister callbacks when
do_stuff() is called.

See https://github.com/sam-github/netfilter-lua/blob/master/nfq.c
line 113 for an example of doing it this way (though this
example has only a single cb, with multiples, you'd have a table at
that index, not just a function).

> I'm thinking the best approach would be to write one C callback that
> calls the appropriate lua function based on the void pointer
> arguments.

I wouldn't do this unless you have dozens of callbacks. In C, you have
only two unique bits of information passed to you when
a callback occurs from which you can choose your action: the C
function (obviously), and
whatever is in the void*, why not take advantage of the fact that the
C function implicitly tells you what lua callback
you want to make?

> At first I was thinking I would use lua userdata. It's easy to cast
> userdata to a void pointer, but then how would I put it back on the
> stack, so I can lua_call it?

Not sure how a userdata will help you, making one per callback seems
overkill, but yes,it's easy to do this.

Create a table somewhere in your lua_State, probably in your module,
maybe as a shared upvalue for your module.
Make it a weak valued table. You can use the void* value of the
userdata as a lightuserdata to key the
table, and the values of the table will be the full userdata. From C,
you convert your void* argument to lightuserdata,
key into the table, and get the full userdata.

See http://www.lua.org/pil/28.5.html for description of lightuserdata.

Cheers,
Sam