Fulup Fulup - 1 year ago 63
JSON Question

Lua passing handle to function created with newlib

I'm using

luaL_newlib(luaState, afbFunction)
where
afbFunction
is a static
luaL_Reg
array. Unfortunately,
luaL_Reg
only support two fields:
name
and
func
. As a result, there is no way to pass a handle to a function and implement something like:

static const luaL_Reg afbFunction[] = {
{"notice" , LuaPrintMsg, MSG_NOTICE},
{"info" , LuaPrintMsg, MSG_INFO},
{"warning", LuaPrintWar, MSG_WARN},
{NULL, NULL} /* sentinel */
};


I have two motivations for not having as many C entry points as there are Lua functions:


  1. I have a generic wrapper for all C functions to process input/output
    parameters value from Lua (table) to C (jsonc).

  2. The number of Lua functions to interface come from a config file and may change without having to rebuild the code. To make a long story short, the config file has a sharelib path followed by the list of function to expose.



Does someone have a solution to have a unique C entry point for multiple Lua functions? When using
luaL_newlib()
, the C function receives as 2nd argument the lib table, but it seems to receive the full table and not the corresponding entry, also even testing the label is not possible.

Answer Source

Use upvalues!

If you don't use luaL_newlib (which is a macro defined as…

#define luaL_newlib(L,l)  \
  (luaL_checkversion(L), luaL_newlibtable(L,l), luaL_setfuncs(L,l,0))

…) but explicitly call luaL_setfuncs, you can make each function have some number of upvalues. It's fine to initialize all of them with nil, you can later patch them up. While you can't extend luaL_Reg with extra fields, you can define an extra struct and have a separate array for the other parameters.

Minimal sample implementation (may need more error checking):

(should be copy&paste-able as-is, text blocks should become comments)


#include <lua.h>
#include <lauxlib.h>
/*

First, define a struct that has the function's name first and anything you need after that. This example just uses a string, but you could use int, void*, ... and even have more than one value.

*/
typedef struct xlua_uvinfo {
    const char *name;  /* this is how we identify the functions */
    const char *uv;  /* this can change arbitrarily */
} xlua_uvinfo;
/*

Then we'll have a function that patches the libtable on the stack to put in the real upvalue data. You'll need to adjust the types (lua_pushX), order, and/or names of the fields that you push.

Note that as we're identifying functions by name, not all functions need to get upvalues. (So you can include normal functions in the luaL_Reg that will simply end up with dummy upvalue slots that will not be filled in here.)

*/
static void setupvalues( lua_State *L, const xlua_uvinfo *l ) {
    for (; l->name != NULL; l++) {
        /* get the function by name */
        if (lua_getfield( L, -1, l->name ) == LUA_TFUNCTION) {
            /* this needs to change according to what you have in the struct */
            lua_pushstring( L, l->uv );
            lua_setupvalue( L, -2, 1 );
        }
        /* pop the function */
        lua_pop( L, 1 );
    }
}
/*

Here's a dummy implementation of your function. I use string prefixes – that's easy to debug / inspect.

*/
#define MSG_NOTICE "NOTE"
#define MSG_INFO "INFO"
#define MSG_WARN "WARN"

static int LuaPrintMsg( lua_State *L ) {
   const char *prefix = lua_tostring( L, lua_upvalueindex(1) );
   printf( "%s: %s\n", prefix, lua_tostring( L, 1 ) );
   return 0;
}

/* plain luaL_Reg array */
static const luaL_Reg afbFunction[] = {
    {"notice" , LuaPrintMsg},
    {"info"   , LuaPrintMsg},
    {"warning", LuaPrintMsg},
    {NULL, NULL}
};
/*

And now the part that changes: Add an extra array for the upvalues and then adjust the luaopen_X function.

*/
/* extra upvalue array (must also be terminated by { NULL, NULL } !) */
static const xlua_uvinfo afbUpvalues[] = {
    {"notice" , MSG_NOTICE},
    {"info"   , MSG_INFO},
    {"warning", MSG_WARN},
    {NULL, NULL}
};

int luaopen_foo( lua_State *L ) {
    /* as mentioned above, we can't use `luaL_newlib`, expand manually */
    luaL_checkversion( L );
    /* create the empty table, pre-allocated to the right size */
    luaL_newlibtable( L, afbFunction );
    /* now push a dummy upvalue (more if you add extra upvalues!) */
    lua_pushnil( L );
    /* register the functions, giving 1 upvalue to each */
    luaL_setfuncs( L, afbFunction, 1 );
    /* now set the actual upvalues */
    setupvalues( L, afbUpvalues );
    return 1;
}

If you save that as foo.c, compile as (on Linux) gcc -shared -fPIC -o foo.so foo.c and then run lua -l foo, you'll get the desired behavior:

foo.info "foo"
--> INFO: foo
foo.notice "bar"
--> NOTE: bar
foo.warning "baz"
--> WARN: baz
Recommended from our users: Dynamic Network Monitoring from WhatsUp Gold from IPSwitch. Free Download