c++ - 将 C++ 向量绑定到 Lua
问题描述
我正在尝试手动将指针向量从 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 Parent 对象而不是 Lua 表的指针。我了解该方法是使用 userdata 或 lightuserdata 但看不到如何将类绑定到 Lua 以执行此操作。Child 绑定也是如此,这会导致 ChildMetatable 但没有 Lua 表。
编辑:我在 Parent 下添加了一个子函数,但仍然无法正常工作。lua_setmetatable
还更改了从堆栈底部到堆栈顶部的一些索引(负数)
Edit2:这是因为我试图让 Parent:children 既充当表又充当用户数据。所以我可以返回带有 C 对象指针的 userdata 以及带有 __index 的 ChildMetaTable 以确定如何处理子方法。
解决方案
我试图做的是同时拥有一个 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 }
};
由于 child 不是 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)));
...
推荐阅读
- css - CSS - 将 div 淡入背景图像
- racket - Racket 中的表单验证
- c++ - 解决模板成员函数重载
- java - 使用数据文件进行 Junit 测试
- android - 如何在 RecyclerView 中使可点击项目的焦点区域为非矩形
- asp.net-mvc - 为什么我在 aspnet core 中的 IIS 上发布时授权失败?
- homebrew - 错误:木桶“java7”不可用:不存在同名木桶
- php - Paypal 使用 PHP cURL 获取订阅详细信息
- python - mosquitto_pub 在 crontab -e 时不发布,但在终端中运行相同的脚本时执行
- html - 在这种情况下,我如何将按钮推到 CSS flexbox 的底部?