如何从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栈中提取指向这个对象的指针。

这是我的(目前不工作)尝试,希望澄清我正在尝试做什么; 注意这段代码基本上是修改了本教程的复制/粘贴,并且我使用的是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)

运行此代码时,我在checkPoint()函数内部的下列行中找到了以下错误:“bad argument#3(Point expected,got table)”:ud = luaL_checkudata(L,index,“Point”)。

如果有人能够引导我走向正确的方向,那将是非常感谢。


上述使用和绑定中有几个问题。

在您的测试脚本中, 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;
// ...

另一个重要的观点是关于暴露给lua的C ++对象的生命周期。 因为当你从脚本调用Point.new ,你正在用C ++的'new'操作符来分配和构造,所以你间接地说让lua处理这个暴露的C ++对象的生命周期。 这意味着你还需要实现一个__gc元方法来充当'finalizer'或非确定性析构函数。 如果没有这个,你将无法'删除' point对象,并在lua垃圾收集相应的用户数据时收回空间。

要做到这一点,你可以增加你的代码,如下所示:

  • 在userdata gc上写一个由lua调用的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()
    ;
    

    你的卢阿看起来像

    local point = Point(10,20)
    

    与luabridge。

    要提取值,请参阅Luabridge中的LuaRef。 如果你使用c ++ 11,还有一些非常漂亮的lua绑定库。

    链接地址: http://www.djcxy.com/p/92445.html

    上一篇: How to extract C++ object pointer from Lua

    下一篇: Calling lua function that returns table