我有一个C++类叫做"Point":
class Point
{
public:
int x, y;
//constructor
Point(int x, int y)
{
this->x = x;
this->y = y;
}
};
Run Code Online (Sandbox Code Playgroud)
我的目标是能够使用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;
}
Run Code Online (Sandbox Code Playgroud)
这是Lua脚本:
local point = Point.new(10,20)
Run Code Online (Sandbox Code Playgroud)
运行此代码,我得到以下错误:"错误的参数#3(Point expected,got table)"在checkPoint()函数内的行:ud = luaL_checkudata(L,index,"Point").
如果任何人能引导我朝着正确的方向前进,我将不胜感激.
您的上述用法和绑定存在一些问题.
在您的测试脚本中,您的主机c ++程序local point 将不会看到它.这是因为,它是该脚本的本地.如果您希望它可以从c ++访问,则可以从point脚本返回值或将其设置为全局并使用它来检索它lua_getglobal(L, "point").
该checkPoint函数有一些不必要的代码.如果查看luaL_check*Lua C API提供的其他函数,它们主要检查堆栈索引处的给定值是否为正确类型.如果它然后将该类型转换为C++可以使用的东西.
所以在你的'checkPoint'功能中你真正需要做的就是:
Point* checkPoint(lua_State* L, int index)
{
return *((Point **) luaL_checkudata(L, index, "Point"));
}
Run Code Online (Sandbox Code Playgroud)
如果您将脚本更改为此,例如:
local point = Point.new(10,20)
return point
Run Code Online (Sandbox Code Playgroud)
您应该能够point通过以下方式从C++ 访问:
// ...
luaL_dofile(L, "testz.lua");
Point *foo = checkPoint(L, -1);
std::cout << "x: " << foo->x << " y: " << foo->y;
// ...
Run Code Online (Sandbox Code Playgroud)
另一个重点是关于暴露给lua时的C++对象生命周期.因为无论何时Point.new从脚本调用C++'new'运算符都要分配和构造,所以你间接地说让lua处理这个暴露的C++对象的生命周期.这意味着您还需要实现一个__gcmetamethod来充当"终结器"或非确定性析构函数.如果没有这个,point当lua垃圾收集相应的用户数据时,你将无法"删除" 对象并回收空间.
为此,您可以按如下方式扩充代码:
编写一个deletePoint在userdata gc上由lua调用的函数.
static int deletePoint(lua_State *L)
{
Pointer **p = checkPoint(L, 1);
delete *p;
*p = NULL; // probably not needed but to be safe
return 0;
}
Run Code Online (Sandbox Code Playgroud)把它添加到你的pointFuncslua了解它.
static const luaL_Reg pointFuncs[] =
{
{"new", newPoint},
{"__gc", deletePoint},
{NULL, NULL}
};
Run Code Online (Sandbox Code Playgroud)我建议不要编写自己的绑定,而是使用经过测试和记录的绑定,例如luabridge或luabind
您的绑定将减少为:
getGlobalNamespace (L)
.beginClass<Point>("Point")
.addConstructor<void (*) (int,int)>()
.addData("X", &Point::x)
.addData("Y", &Point::y)
.endClass()
;
Run Code Online (Sandbox Code Playgroud)
你的卢阿看起来像
local point = Point(10,20)
Run Code Online (Sandbox Code Playgroud)
与luabridge.
有关提取值的信息,请参阅luabridge中的LuaRef示例.如果你使用c ++ 11,还有一些非常漂亮的lua绑定库.