简体   繁体   English

C中的嵌套Lua元表

[英]Nested Lua Metatables in C

In a 3D scene, I have an Object that has a position that I would like to move using Lua. 在3D场景中,我有一个对象,该对象的位置要使用Lua移动。

eg. 例如。 box.position.x = 10

box has a metatable ("Object") and so has position ("Vec"). 框具有一个元表(“对象”),位置也具有(“ Vec”)。 Object has __newindex and __index set to call C functions NewIndexObject and IndexObject respectively. Object设置了__newindex__index来分别调用C函数NewIndexObjectIndexObject Same with Vec ( NewIndexVec and IndexVec ). 与Vec相同( NewIndexVecIndexVec )。

Object has an id so it can be identified in a list that is stored in the scene and when box.position is accessed all is fine, the C function IndexObject is called and I can extract the id from the stack, it's just when box.position.x = 10 is executed 'NewIndexVec' is called and the only thing on the stack is {table, x, 10} so no way of identifying the object to change its x position. 对象有一个ID,因此可以在存储在场景中的列表中对其进行标识,并且在访问box.position时一切都很好,调用了C函数IndexObject,并且我可以从堆栈中提取ID,就在box.position.x = 10被执行'Ne​​wIndexVec'被调用,并且堆栈上唯一的东西是{table,x,10},因此无法识别对象来更改其x位置。

Is there anyway of pushing values onto a local state? 无论如何,有将价值推到本地状态的方法吗? Help! 救命!

UPDATE: thank you for getting back to me quickly, below I have distilled the code as much as possible. 更新:感谢您迅速与我联系,下面我尽可能地精炼了代码。 If you run this code it'll appear to work but I have comments where I'm stuck, it's just getting the first object in the array but I need to choose it by it's ID, Thanks in advance 如果您运行此代码,它似乎可以正常工作,但我在卡住的位置添加了注释,它只是获取数组中的第一个对象,但我需要通过其ID选择它,谢谢。

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 &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 &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: %s\n", lua_tostring(L, -1));



    lua_close(L);

    return 0;
}

If I understand you correctly, you don't have to do anything. 如果我理解正确,则您无需执行任何操作。 Tables are tracked by reference, so NewIndexVec doesn't need to know anything about box if its first argument is box.position . 表通过引用跟踪,所以NewIndexVec并不需要知道什么box当第一个参数是box.position

If this answer can't work for some reason, then I'd need more information about your data structure to understand your problem. 如果由于某种原因该答案无法解决,那么我需要有关您的数据结构的更多信息以了解您的问题。

Basically, box.position needs to return some obj for which obj.x = 10 is a valid operation and changes exactly what you want it to change. 基本上, box.position需要返回一些obj.x = 10是有效操作的obj ,并精确地更改您想要更改的内容。


The problem is that you're trying to keep the same data in two separate places. 问题是您试图将相同的数据保存在两个不同的位置。 Keep all the data in the C++ struct, then have NewObject return a userdata that pretends to be a table. 将所有数据保留在C ++结构中,然后让NewObject返回一个伪装成表的userdata。 Both the Object and the position field should be the same Obj* , but they may have different metatables to simulate different sets of fields. Object和position字段都应该是相同的Obj* ,但是它们可能具有不同的元表来模拟不同的字段集。

Thanks, I've posted the code that works 谢谢,我已经发布了有效的代码

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 &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: %s\n", lua_tostring(L, -1));

    lua_close(L);

    return 0;
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM