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