Код Lua C API не вызывает функции __newindex, но вызывает другие функции

Я написал некоторый код для разделения регистрации пользовательских функций и функций __newindex и __index на две отдельные функции. Цель моего кода — сделать функции и переменные видимыми для сценариста Lua, которые организованы на основе подуровней специфичности. Например, пользователю будут доступны следующие команды:

orc.chief.attack();
orc.chief.flee();
orc.chief.hp = 100;
orc.pawn.attack();
elf.wood.attack();
elf.wood.hp = 200;

Итак, в основном система с 2 уровнями, а затем вызов функции или переменная. Если я правильно понимаю Lua, это метатаблица в таблице в таблице. Когда пользователь устанавливает переменную, он должен активировать вызов __newindex (не только для обработки установки значения, но и для доступа к физическому объекту, который будет анимироваться с помощью двигателей). Я также предполагаю, что главная таблица в таблице orc просто видит множество назначенных ей функций, независимо от того, attack это или __newindex. Чтобы упростить добавление новых переменных и функций по мере разработки кода, я создал 2 функции: одну для создания функции и одну для создания переменной. Функция create просто регистрирует функции, а переменная create просто создает новый элемент таблицы и регистрирует функции для __newindex и __index. Ниже приведен код:

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);
}

Затем в моем main() я вызываю функции следующим образом:

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);

Когда я запускаю код и загружаю сценарии Lua, orc.chief.attack() вызывает мою функцию OrcAttack(), но orc.chief.hp = 100 никогда не вызывает мою функцию orcChief__newindex(). Я даже закомментировал вызовы registerFunctions на случай, если они каким-то образом мешают, и только registerIntegerVariable сам по себе не вызовет orcChief__newindex(). Есть идеи?


person Tim Eastham    schedule 28.11.2012    source источник


Ответы (1)


__newindex не вызывается, когда вы устанавливаете поле в таблице. Он вызывается, когда вы устанавливаете новое поле в таблице. Если поле уже существует, __newindex вызываться не будет.

Если вы хотите, чтобы __newindex вызывалась для каждой операции установки в таблице, вы не можете позволить операциям установки фактически изменять эту таблицу. Обычно это делается путем создания пустой таблицы, называемой прокси-таблицей, которую использует пользователь. Прокси-таблица на самом деле пуста и должна всегда оставаться такой; вы перехватываете все вызовы get и set, направляя их во внутреннюю таблицу, к которой пользователь никогда не видит доступа.

Или вы используете какие-то пользовательские данные вместо таблицы. __newindex всегда вызывается для них.

person Nicol Bolas    schedule 28.11.2012
comment
Эта фиктивная таблица всегда остается пустой, поэтому вызывается __newindex? - person Tim Eastham; 29.11.2012
comment
Я новичок в Lua, и все эти пользовательские данные меня сбивают с толку. Я предполагаю, что это должны быть полные пользовательские данные, так как я читал, что легкие пользовательские данные имеют только глобальный __newindex для всех из них, верно? Если я использую полные пользовательские данные, как мне сделать так, чтобы они выглядели так, как показано выше? Я теряю структуру массива? Буду ли я создавать пользовательские данные, а затем выполнять setfield пользовательских данных в таблице, чтобы у меня была таблица пользовательских данных? - person Tim Eastham; 29.11.2012
comment
Эта фиктивная таблица всегда остается пустой Да. - person Nicol Bolas; 29.11.2012
comment
Фиктивная таблица (иногда называемая прокси-таблицей из моего онлайн-исследования) сделала свое дело. Спасибо. - person Tim Eastham; 30.11.2012