如何在Lua中提取C++对象指针

我有一个名为“Point”的 C++ 类:

class Point
{
public:
  int x, y;

  //constructor
  Point(int x, int y)
  {
    this->x = x;
    this->y = y;
  }
};

我希望能够使用 Lua 脚本实例化一个 Point 对象,并从 Lua 栈中提取指向此对象的指针。

以下是我(目前不起作用的)尝试,希望能够澄清我正在尝试做什么;请注意,此代码实际上是从 [这个教程](http://loadcode.blogspot.com/2007/02/wrapping-c-classes-in-lua.html)中复制/粘贴修改的,我使用 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)

运行此代码,我得到以下错误:“bad argument #3(Point expected, got table)”,该错误位于 checkPoint() 函数中的行 lua_lcheckudata(L, index, "Point")。

如果有人能引导我走向正确的方向,我将不胜感激。

点赞
用户847349
用户847349

我建议不要写自己的绑定程序,而是使用经过测试和文档化的绑定程序,例如 luabridgeluabind

你的绑定将被简化为:

getGlobalNamespace (L)
    .beginClass<Point>("Point")
     .addConstructor<void (*) (int,int)>()
     .addData("X", &Point::x)
     .addData("Y", &Point::y)
    .endClass()
;

使用 luabridge 后,你的 Lua 将如下所示:

local point = Point(10,20)

有关提取值的示例,请参见 luabridge 中的 LuaRef。如果您使用 c++11,还有其他一些相当美丽的 Lua 绑定库。

2013-08-15 21:15:51
用户234175
用户234175

在您上述的用法和绑定中存在一些问题。

在您的测试脚本中,local point 不会被主机的 C++ 程序看到。这是因为它只是该脚本的本地变量。如果您希望从 C++ 中访问它,可以通过将 point 作为脚本的返回值或使其全局并使用 lua_getglobal(L, "point") 检索它。

checkPoint 函数中有一些不必要的代码。如果您查看 Lua C 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;
// ...

另一个重要的观点是关于将 C++ 对象公开给 Lua 时的 C++ 对象生命周期。因为您每当从脚本调用 Point.new 时都会使用 C++ 的 'new' 进行分配和构造,这意味着您间接地说“让 Lua 处理这个公开的 C++ 对象的生命周期”。这意味着您还需要实现一个 __gc 元方法作为 'finalizer' 或非确定性析构函数。如果没有这个函数,您将无法 '删除' point 对象并在 Lua 垃圾回收相应的用户数据时回收空间。

为了实现这一点,您可以按如下方式修改代码:

  • 写一个 deletePoint 函数,当用户数据垃圾回收时由 Lua 调用。

    static int deletePoint(lua_State *L)
    {
      Pointer **p = checkPoint(L, 1);
      delete *p;
      *p = NULL;  // 可能不需要,但为了安全起见
      return 0;
    }
    
  • 将其添加到您的 pointFuncs 中,这样 Lua 就知道它了。

    static const luaL_Reg pointFuncs[] =
    {
      {"new", newPoint},
      {"__gc", deletePoint},
      {NULL, NULL}
    };
    
2013-08-15 22:12:52