【问题标题】:Binding C++ vector to Lua将 C++ 向量绑定到 Lua
【发布时间】:2021-05-19 15:28:52
【问题描述】:

我正在尝试手动将指针向量从 C++ 绑定到 Lua。

我仅限于支持部分 C++11 的编译器,因此无法使用现有绑定库之一,因为它们现在似乎都在使用 C++17。

例如,我有一个包含指向子类的指针列表的类。从 Lua 的角度来看,children 的向量是只读的 - 我不需要添加、删除等。只需阅读。

class Child
{
public:
    std::string name;
};

class Parent
{
public:
    std::vector <Child *>children;
};
...
    Parent parent;
    Child * m = new Child;
    m->name = "Mary";
    parent.children.push_back(m);
    Child * b = new Child;
    b->name = "Bob";
    parent.children.push_back(b);
...

子绑定。

static int Child_name(lua_State * lua) {
    // this should get a point to a Child object and return the name
    lua_pushstring(lua, "child name");
    return 1;
}
static const struct luaL_Reg Child_FunctionList[] = {
    { "name", Child_name },
    { NULL, NULL }
};
static int Child_tostring(lua_State * lua) {
    lua_pushstring(lua, "Child");
    return 1;
}
static const struct luaL_Reg Child_MetaList[] = {
    { "__tostring", Child_tostring },
    { NULL, NULL }
};
void Child_Register(lua_State * lua)
{
    luaL_newlib(lua, Child_FunctionList);
    if(luaL_newmetatable(lua, "ChildMetaTable"))
        luaL_setfuncs(lua, Child_MetaList, 0);
    lua_setmetatable(lua, -2);
    lua_pop(lua, 1);
}

父绑定。

static int Parent_count(lua_State * lua) {
    // used by both the Parent function and metatable __len
    lua_pushinteger(lua, parent.children.size());
    return 1;
}
static int Parent_children(lua_State * lua)
{
    // stack -1=number(1)
    int idx = lua_tonumber(lua, -1);
    Child ** c = static_cast<Child **>(lua_newuserdata(lua, sizeof(Parent *)));
    *c = parent.children[idx];
    luaL_getmetatable(lua, "ChildMetaTable"); // [-0, +1, m]
    lua_setmetatable(lua, -2);
    // return new userdata - does not work
    return 1;
}
static const struct luaL_Reg Parent_FunctionList[] = {
    { "count", Parent_count },
    { "children", Parent_children },
    { NULL, NULL }
};
static int Parent_tostring(lua_State * lua) {
    lua_pushstring(lua, "Parent");
    return 1;
}
static int Parent_index(lua_State * lua) {
    // stack -1=number(1) -2=table
    int idx = lua_tonumber(lua, -1);
    Child * c = parent.children[idx];
    // what to return here?
    return 0;
}
static const struct luaL_Reg Parent_MetaList[] = {
    { "__tostring", Parent_tostring },
    { "__len", Parent_count },
    { "__index", Parent_index },
    { NULL, NULL }
};
void Parent_Register(lua_State * lua) {
    luaL_newlib(lua, Parent_FunctionList);
    if(luaL_newmetatable(lua, "ParentMetaTable"))
        luaL_setfuncs(lua, Parent_MetaList, 0);
    lua_setmetatable(lua, -2);
    lua_setglobal(lua, "Parent");
}

Parent 绑定会生成一个全局表,这是有意的。测试父表:

>print(Parent)
Parent
>print(#Parent)
2
>print(Parent.count())
2

但是尝试访问孩子也不起作用

>c = Parent[1]
>print(c)
Child
>print(type(c))
userdata
>print(c.name())
[string "main"]:8: attempt to index a ChildMetaTable value (global 'c')

我在Parent_index 中迷路了,我需要一个指向 C 父对象而不是 Lua 表的指针。我了解该方法是使用 userdata 或 lightuserdata 但看不到如何将类绑定到 Lua 以执行此操作。 Child 绑定也是如此,这会导致 ChildMetatable 但没有 Lua 表。

编辑:我在 Parent 下添加了一个子函数,但仍然无法正常工作。还将lua_setmetatable 的一些索引从堆栈底部更改为堆栈顶部(负数)

Edit2:这是因为我试图让 Parent:children 既作为表又作为用户数据。所以我可以返回带有 C 对象指针的 userdata 以及带有 __index 的 ChildMetaTable 以确定如何处理子方法。

【问题讨论】:

  • 您应该在Parent_count 中使用lua_pushinteger 而不是lua_pushnumber,因为将浮点数作为计数很奇怪。
  • 您可能不想在Parent_index 中使用lua_pushlightuserdata,因为轻量级用户数据没有元表。您需要使用lua_newuserdatauv 创建完整元数据
  • 谢谢。我已更改为 lua_pushinteger 并更改了 lua_setmetatable 以使用堆栈顶部的索引。我试过创建用户数据,然后设置到Parent_children 中的元表,但还没有。

标签: c++ lua


【解决方案1】:

我试图做的是同时拥有一个 Lua 表和用户数据。

首先,最好将父__index 方法替换为表中为子对象创建新用户数据的函数。

static int Parent_children(lua_State * lua)
{
    int idx = luaL_checkinteger(lua, -1);
    Parent * p = &parent;
    luaL_argcheck(lua, (idx >= 0) && (idx < (int)p->children.size()), 1, "index out of range");

    Child ** pc = static_cast<Child **>(lua_newuserdata(lua, sizeof(Child *)));
    *pc = parent.children[idx];
    luaL_getmetatable(lua, "ChildMetaTable"); // [-0, +1, m]
    lua_setmetatable(lua, -2);
    return 1;
}
static const struct luaL_Reg Parent_MetaList[] = {
    { "__tostring", Parent_tostring },
    { NULL, NULL }
};

由于子函数不是 Lua 表,因此可以从 __index 方法调用子函数。 Child_FunctionList 保持不变。

static int Child_index(lua_State * lua)
{
    const char * fn_name = luaL_checkstring(lua, -1);
    for(const luaL_Reg * fn = Child_FunctionList; fn->name != NULL; fn++)
    {
        if(strcmp(fn_name, fn->name) == 0)
        {
            lua_pushcfunction(lua, fn->func);
            return 1;
        }
    }
    return 0;
}
static const struct luaL_Reg Child_MetaList[] = {
    { "__tostring", Child_name },
    { "__index", Child_index },
    { NULL, NULL }
};

子方法从用户数据中获取一个 C 指针。

static int Child_name(lua_State * lua)
{
    Child * c = *reinterpret_cast<Child **>(luaL_checkudata(lua, -1, "ChildMetaTable"));
    lua_pushstring(lua, c->name.c_str());
    return 1;
}

最后为孩子注册表值没有意义,因此可以删除但需要注册元表。

void Child_Register(lua_State * lua)
{
    if(luaL_newmetatable(lua, "ChildMetaTable"))
        luaL_setfuncs(lua, Child_MetaList, 0);
    lua_pop(lua, 1);
}

这可能不是最佳解决方案,但它正在朝着那里前进。

编辑: 全局 parent 可以作为扩展 luaL_newlib 宏中的上值传递,而不是使用全局 parent

luaL_newlibtable(lua, Parent_FunctionList);
lua_pushlightuserdata(lua, parent);
luaL_setfuncs(lua, Parent_FunctionList, 1);
...

static int Parent_children(lua_State * lua) {
Parent * parent= (Parent *)(lua_topointer(lua, lua_upvalueindex(1)));
...

【讨论】:

    猜你喜欢
    • 2013-02-08
    • 2011-11-19
    • 1970-01-01
    • 2017-10-23
    • 2010-11-10
    • 2012-01-11
    • 2016-02-06
    • 2011-08-15
    • 1970-01-01
    相关资源
    最近更新 更多