質問

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?

役に立ちましたか?

解決

__newindex is not called when you set a field in a table. It is called when you set a new field in a table. If the field already exists, __newindex will not be called.

If you want __newindex to be called for every set operation on a table, you can't allow set operations to actually modify that table. This is generally done by creating an empty table, called a proxy table, which the user uses. The proxy table is actually empty and must always remain so; you intercept all of the get and set calls, piping them to an internal table that the user never sees don't have access to.

Or you use some userdata instead of a table. __newindex is always called for them.

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top