如何从Lua中提取C++对象指针
How to extract C++ object pointer from Lua
我在C++中有一个名为"Point"的类:
class Point
{
public:
int x, y;
//constructor
Point(int x, int y)
{
this->x = x;
this->y = y;
}
};
我的目标是能够用Lua脚本实例化Point对象,并从Lua堆栈中提取指向该对象的指针。
这是我(目前没有工作)的尝试,希望能澄清我到底想做什么;注意,这段代码本质上是从本教程中修改的复制/粘贴,我使用的是Lua 5.2:
static int newPoint(lua_State *L)
{
int n = lua_gettop(L);
if (n != 2)
return luaL_error(L, "expected 2 args for Point.new()", n);
// Allocate memory for a pointer to object
Point **p = (Point **)lua_newuserdata(L, sizeof(Point *));
double x = luaL_checknumber (L, 1);
double y = luaL_checknumber (L, 2);
//I want to access this pointer in C++ outside this function
*p = new Point(x, y);
luaL_getmetatable(L, "Point"); // Use global table 'Point' as metatable
lua_setmetatable(L, -2);
return 1;
}
static const luaL_Reg pointFuncs[] = {
{"new", newPoint},
{NULL, NULL}
};
//register Point to Lua
void registerPoint(lua_State *L)
{
lua_createtable(L, 0, 0);
// Register metatable for user data in registry
luaL_newmetatable(L, "Point");
luaL_setfuncs(L, pointFuncs, 0);
lua_pushvalue(L,-1);
lua_setfield(L,-2, "__index");
lua_setglobal(L, "Point");
}
Point* checkPoint(lua_State* L, int index)
{
void* ud = 0;
luaL_checktype(L, index, LUA_TTABLE);
lua_getfield(L, index, "__index");
ud = luaL_checkudata(L, index, "Point");;
return *((Point**)ud);
}
int main()
{
lua_State *L = luaL_newstate();
luaL_openlibs(L);
registerPoint(L);
luaL_dofile(L, "testz.lua");
lua_getglobal(L, "Point");
Point *foo = checkPoint(L, lua_gettop(L));
std::cout << "x: " << foo->x << " y: " << foo->y;
lua_close(L);
return 0;
}
这是Lua的脚本:
local point = Point.new(10,20)
运行此代码时,我得到以下错误:行中的"错误参数#3(应为Point,get-table)":ud=checkPoint()函数内的luaL_checkudata(L,index,"Point")。
如果有人能引导我朝着正确的方向前进,我将不胜感激。
在上述用法和绑定中存在几个问题。
在您的测试脚本中,local point
将不会被您的主机c++程序看到。这是因为,嗯,它是本地的脚本。如果希望从c++访问它,可以从脚本中返回point
作为值,也可以使其全局化并使用lua_getglobal(L, "point")
检索它。
checkPoint
函数有一些不必要的代码。如果您查看LuaC API提供的其他luaL_check*
函数,它们主要检查堆栈索引中的给定值是否是正确的类型。如果是,则将该类型转换为C++可以使用的类型。
所以在你的"checkPoint"功能中,你真正需要做的就是:
Point* checkPoint(lua_State* L, int index)
{
return *((Point **) luaL_checkudata(L, index, "Point"));
}
如果您将脚本更改为这样,例如:
local point = Point.new(10,20)
return point
您应该能够通过以下方式从C++访问point
:
// ...
luaL_dofile(L, "testz.lua");
Point *foo = checkPoint(L, -1);
std::cout << "x: " << foo->x << " y: " << foo->y;
// ...
另一个重要的观点是关于暴露于lua时C++对象的生存期。因为每当从脚本中调用Point.new
时,您都使用C++"new"运算符进行分配和构造,所以您间接地说让lua处理这个公开的C++对象的生存期。这意味着您还需要实现一个__gc
元方法来充当"终结器"或非确定性析构函数。如果没有这一点,您将无法在lua垃圾收集相应的用户数据时"删除"point
对象并回收空间。
要做到这一点,您可以按如下方式扩充代码:
编写一个由lua在userdatagc上调用的
deletePoint
函数。static int deletePoint(lua_State *L) { Pointer **p = checkPoint(L, 1); delete *p; *p = NULL; // probably not needed but to be safe return 0; }
把它添加到你的
pointFuncs
中,这样lua就知道了static const luaL_Reg pointFuncs[] = { {"new", newPoint}, {"__gc", deletePoint}, {NULL, NULL} };
我建议不要编写自己的绑定,而是使用经过测试和记录的绑定,如luabridge或luabind
您的绑定将减少为:
getGlobalNamespace (L)
.beginClass<Point>("Point")
.addConstructor<void (*) (int,int)>()
.addData("X", &Point::x)
.addData("Y", &Point::y)
.endClass()
;
你的lua看起来像
local point = Point(10,20)
与卢布里奇。
有关提取值的信息,请参见例如luabridge中的LuaRef。如果你使用c++11,还有一些其他非常漂亮的lua绑定库。
- 什么时候调用组成单元对象的析构函数
- 对RValue对象调用的LValue ref限定成员函数
- CMake-按正确顺序将项目与C运行时对象文件链接
- 空基优化子对象的地址
- 将对象数组的引用传递给函数
- 你能重载对象变量名本身返回的内容吗
- C++使用整数的压缩数组初始化对象
- 找不到成员对象:没有名为get_event()的成员,也处理多态性和向量
- 将对象移动到std::shared_ptr
- 代理对象的常量正确性
- 提升 ASIO 无法识别计时器对象
- 将Ref对象作为类成员
- 将包含C样式数组的对象初始化为成员变量(C++)
- 如何返回一个类的两个对象相加的结果
- 使用std::函数映射对象方法
- 是否需要删除包含对象的"pair"?
- 如何在自删除后将对象设置为nullptr
- 迭代时从向量和内存中删除对象
- 构造对象的歧义
- 使用"std::unordereded_map"映射到"std::list"对象