I have written some code to separate registering custom functions and the __newindex
and __index
functions into 2 separate functions. The goal of my code is to have functions and variables visible to the Lua script writer that are organized based upon sublevels of specificity. For example, the user would have available the following commands:
orc.chief.attack();
orc.chief.flee();
orc.chief.hp = 100;
orc.pawn.attack();
elf.wood.attack();
elf.wood.hp = 200;
So basically a system with 2 tiers and then a function call or a variable. If I understand Lua correctly, that is a metatable in a table in a table. When the user sets the variable, it should trip a __newindex
call (not only to handle setting the value but to access a physical object that will animate through motors). I also assume that the chief table in the table orc
just sees lots of functions assigned to it regardless whether it is attack
or __newindex
. To make it easier to add new variables and functions as the code develops, I have created 2 functions: one to create a function and one to create a variable. The function create
just registers the functions and the variable create
just makes a new table element and registers the functions for __newindex
and __index
. Below is the code:
int orcChiefhp;
luaL_Reg Orc_Module[] = {
{"attack", OrcAttack},
{"flee", OrcFlee},
{NULL, NULL}};
const luaL_Reg orcChief_metareg[] = {
{"__index", orcChief__index},
{"__newindex", orcChief__newindex},
{NULL, NULL}};
int OrcAttack(lua_State *L)
{
//code to cause the motors to swing the weapon...
return 0;//0 parameters come back as the data
}
int orcChief__newindex(lua_State *L)
{
const char *idx;
if(lua_isstring(L,2))
{
idx = lua_tostring(L,2);//gets the string so we can get the variable of the struct
if(strcmp(idx, "hp")==0)
{
lua_pushnumber(L, orcChiefhp);
}
else
lua_pushnil(L);
}
return 1;
}
void registerFunctions(lua_State *L, const char *libname, const char *sublibname, const luaL_Reg *funcs)
{
int isitnil;
lua_getglobal(L, libname);
isitnil = lua_isnil(L, -1);
if(isitnil)
{
lua_pop(L, 1);
lua_newtable(L); // create 'libname' table
}
// no sublib: just import our library functions directly into lib and we're done
if (sublibname == NULL)
{
luaL_setfuncs(L, funcs, 0);
}
// sublib: create a table for it, import functions to it, add to parent lib
else
{
lua_newtable(L);
luaL_setfuncs(L, funcs, 0);
lua_setfield(L, -2, sublibname);
}
if(isitnil)
lua_setglobal(L, libname);//this will pop off the global table.
else
lua_pop(L, 1);//the global table is still on the stack, pop it off
}
void registerIntegerVariable(lua_State *L, const char *libname, const char *sublibname, const char *variableName,
const char *metatableName, const luaL_Reg *metatableFuncs, int defaultValue)
{
int isLibnameNil;
int isSubnameNil;
lua_getglobal(L, libname);//get the libname
isLibnameNil = lua_isnil(L, -1);//check to see if it exists
if(isLibnameNil)//if it doesn't exist, create a new one
{
lua_pop(L, 1);//pop off the nil
lua_newtable(L); // create 'libname' table
}
// no sublib: just import our library functions directly into lib and we're done
if (sublibname == NULL)//if we want the functions at the lib level then just set the functions
{
lua_pushstring(L, variableName);//push the variable name
lua_pushnumber(L, defaultValue);//push the default value on the stack
lua_rawset(L, -3);//add the variable to the table (rawset is like settable but doesn't call __index)
luaL_newmetatable(L, metatableName);//create the metatable
luaL_setfuncs(L, metatableFuncs, 0);//set the metatable functions for __newindex and __index
lua_setmetatable(L, -2);//set the metatable to the libtable
}
// otherwise we need to create a table for the sublibname, import functions to it, add to parent lib.
else
{
lua_getfield(L, -1, sublibname);//see if the sublibname is under the global libname
isSubnameNil = lua_isnil(L, -1);//is it a nil
if(isSubnameNil)//if it is, then we need to create the sublibname
{
lua_pop(L, 1);//pop off the nil
lua_newtable(L);//creates the new sublibname table
}
lua_pushstring(L, variableName);//push the variable name
lua_pushnumber(L, defaultValue);//push the default value on the stack
lua_rawset(L, -3);//add the variable to the table and push it (rawset is like settable but doesn't call __index)
luaL_newmetatable(L, metatableName);//create the metatable
luaL_setfuncs(L, metatableFuncs, 0);//add the metamethods
lua_setmetatable(L, -2);//set the metatable to the sublibname
if(isSubnameNil)
lua_setfield(L, -2, sublibname);//now we need to add the sublibname to the libname
}
if(isLibnameNil)
lua_setglobal(L, libname);//set the global name if it was new
else
lua_pop(L, 1);
}
Then, in my main()
I call the functions like this:
execContext = luaL_newstate();
//adding lua basic library
luaL_openlibs(execContext);
//now register all the functions with Lua
registerFunctions(execContext, "orc", "chief", Orc_Module);
registerFunctions(execContext, "orc", "pawn", Orc_Module);
registerFunctions(execContext, "elf", "wood", Elf_Module);
//now register all the variables with Lua
registerIntegerVariable(execContext, "orc", "chief", "hp", "chief_meta", orcChief_metareg, 0);
When I run the code and pump in Lua scripts, orc.chief.attack()
calls my OrcAttack()
function but orc.chief.hp = 100
never calls my orcChief__newindex()
function. I have even commented out the registerFunctions
calls in case they were interfering somehow and just the registerIntegerVariable
by itself still won't trigger the orcChief__newindex().
Any ideas?