Вложенные метатаблицы Lua в C

#c #lua #metatable #lua-api

#c #lua #Метатабли #lua-api

Вопрос:

В 3D-сцене у меня есть объект, у которого есть позиция, которую я хотел бы переместить с помощью Lua.

например. box.position.x = 10

box имеет метатаблицу («Объект»), а также позицию («Vec»). Объект имеет __newindex и __index установлен для вызова функций C NewIndexObject и IndexObject соответственно. То же самое с Vec ( NewIndexVec и IndexVec ).

Объект имеет идентификатор, поэтому его можно идентифицировать в списке, который хранится в сцене, и при box.position обращении к нему все в порядке, вызывается функция C IndexObject, и я могу извлечь идентификатор из стека, просто при box.position.x = 10 выполнении вызывается ‘NewIndexVec’ и единственное, что находится в стекеявляется {table, x, 10}, поэтому нет способа идентифицировать объект для изменения его позиции x.

Есть ли возможность переноса значений в локальное состояние? Помогите!

ОБНОВЛЕНИЕ: спасибо, что быстро перезвонили мне, ниже я максимально переработал код. Если вы запустите этот код, он, похоже, будет работать, но у меня есть комментарии, в которых я застрял, это просто получение первого объекта в массиве, но мне нужно выбрать его по его идентификатору, заранее спасибо

 struct Obj
{
    std::string id;
    int x,y,z;
    Obj()
    {
        x = 10; y = 20; z = 30;
        id = "12345";
    }
};

//array of external objects
std::vector<Obj> objects;

int NewObject(lua_State * L)
{
    Obj obj;
    objects.push_back(obj);

    lua_newtable(L); 

    luaL_getmetatable(L, "MT_Object");
    lua_setmetatable(L, -2);

    lua_pushstring(L, "id");
    lua_pushstring(L, obj.id.c_str());
    lua_settable(L, 1);

    lua_newtable(L); 
    luaL_getmetatable(L, "MT_Vec");
    lua_setmetatable(L, -2);

    lua_pushinteger(L, obj.x);
    lua_setfield(L, -2, "x"); 

    lua_pushinteger(L, obj.y);
    lua_setfield(L, -2, "y"); 

    lua_pushinteger(L, obj.z);
    lua_setfield(L, -2, "z"); 

    lua_setfield(L, -2, "position");



    return 1;
}

int IndexVec(lua_State * L)
{
    // How do I get the correct object so I can pass its value back
    Obj amp;dunnoObj =  objects[0];

    std::string key = luaL_checkstring(L,-1);
    if(key == "x")
        lua_pushinteger(L,dunnoObj.x);
    else if(key == "y")
        lua_pushinteger(L,dunnoObj.y);
    else if(key == "z")
        lua_pushinteger(L,dunnoObj.z);
    return 1;
}


int NewIndexVec(lua_State * L)
{
    // How do I know which object's value to update
    Obj amp;dunnoObj =  objects[0];

    std::string key = luaL_checkstring(L,-2);
    int value = luaL_checkinteger(L,-1);

    if(key == "x")
        dunnoObj.x = value;
    else if(key == "y")
        dunnoObj.y = value;
    else if(key == "z")
        dunnoObj.z = value;

    return 0;
}

int main()
{
    lua_State * L = luaL_newstate();
    luaL_openlibs(L);


    luaL_Reg objreg[] =
    {
        { "new", NewObject },   
        { NULL, NULL }
    };
    luaL_newmetatable(L, "MT_Object");
    luaL_register(L, 0, objreg);
    lua_setglobal(L, "Object");


    luaL_Reg reg[] =
    {
        { "__index", IndexVec },    
        { "__newindex", NewIndexVec },
        { NULL, NULL }
    };
    luaL_newmetatable(L, "MT_Vec");
    luaL_register(L, 0, reg);
    lua_setglobal(L, "Vec");


    int res = luaL_dostring(L, "box = Object.new()   box.position.x = 1000   print(box.id .. " , " ..box.position.x .. " , " ..  box.position.y .. " , " .. box.position.z)");
    if(res)
        printf("Error: %sn", lua_tostring(L, -1));



    lua_close(L);

    return 0;
}
  

Ответ №1:

Если я вас правильно понял, вам не нужно ничего делать. Таблицы отслеживаются по ссылке, поэтому NewIndexVec не нужно ничего знать о box том, является ли его первый аргумент box.position .

Если этот ответ по какой-то причине не может сработать, мне понадобится дополнительная информация о вашей структуре данных, чтобы понять вашу проблему.

По сути, box.position необходимо вернуть some obj , для которого obj.x = 10 это допустимая операция, и изменяет именно то, что вы хотите, чтобы она изменилась.


Проблема в том, что вы пытаетесь сохранить одни и те же данные в двух разных местах. Сохраните все данные в структуре C , затем NewObject верните userdata, который притворяется таблицей. И объект, и position поле должны быть одинаковыми Obj* , но они могут иметь разные метатаблицы для имитации разных наборов полей.

Комментарии:

1. Спасибо за быстрый ответ, я обновил свой вопрос, включив код

Ответ №2:

Спасибо, я опубликовал код, который работает

 struct Obj
{
    unsigned int id;
    int x,y,z;
    Obj()
    {
        x = 10; y = 20; z = 30;
        id = rand();
    }
};

//array of external objects
std::map<unsigned int,Obj> objects;

int NewObject(lua_State * L)
{
    Obj obj;
    objects[obj.id] = obj;

    lua_pushinteger(L, obj.id);
    luaL_getmetatable(L, "MT_Object");
    lua_setmetatable(L, -2);

    return 1;
}


int IndexObj(lua_State * L)
{
    unsigned int objid = luaL_checkinteger(L,1);

    std::string key = luaL_checkstring(L,-1);

    if(key == "position" )
    {
        Obj *a = (Obj *)lua_newuserdata(L, sizeof(Obj));
        *a = objects[objid];

        luaL_getmetatable(L, "MT_Vec");
        lua_setmetatable(L, -2);
    }
    else if(key == "id")
        lua_pushinteger(L, objid);  
    else
        lua_pushnil(L);

    StackDump(L);
    return 1;
}

int IndexVec(lua_State * L)
{
    Obj *a = (Obj *)lua_touserdata(L, 1);

    std::string key = luaL_checkstring(L,-1);
    if(key == "x")
        lua_pushinteger(L,a->x);
    else if(key == "y")
        lua_pushinteger(L,a->y);
    else if(key == "z")
        lua_pushinteger(L,a->z);
    return 1;
}


int NewIndexVec(lua_State * L)
{
    Obj *a = (Obj *)lua_touserdata(L, 1);
    Obj amp;objRef =  objects[a->id];

    std::string key = luaL_checkstring(L,-2);
    int value = luaL_checkinteger(L,-1);

    if(key == "x")
        objRef.x = value;
    else if(key == "y")
        objRef.y = value;
    else if(key == "z")
        objRef.z = value;

    return 0;
}

int main()
{
    lua_State * L = luaL_newstate();
    luaL_openlibs(L);


    luaL_Reg objreg[] =
    {
        { "new", NewObject },   
        { "__index", IndexObj },
        { "__newindex", NewIndexObj },
        { NULL, NULL }
    };
    luaL_newmetatable(L, "MT_Object");
    luaL_register(L, 0, objreg);
    lua_setglobal(L, "Object");


    luaL_Reg reg[] =
    {
        { "__index", IndexVec },    
        { "__newindex", NewIndexVec },
        { NULL, NULL }
    };
    luaL_newmetatable(L, "MT_Vec");
    luaL_register(L, 0, reg);
    lua_setglobal(L, "Vec");

    int res = luaL_dostring(L,  "box = Object.new()   box.position.x = 1000 "
                                "print(box.id .. " , " ..box.position.x .. " , " ..  box.position.y .. " , " .. box.position.z)");
    if(res)
        printf("Error: %sn", lua_tostring(L, -1));

    lua_close(L);

    return 0;
}