【问题标题】:Passing existing C++ objects to Lua将现有 C++ 对象传递给 Lua
【发布时间】:2015-09-25 15:48:20
【问题描述】:

我目前正在开发自己的视频游戏引擎,并且正在尝试实现对 lua 脚本的支持,以便对游戏行为进行编码。但是,我目前正在努力使用 Lua 中的 C++ 类。我了解如何通过 lua 在堆上创建类的新实例 - 但这(可能)不是我想要做的。

我宁愿将一个已经存在于 C++ 中的对象传递给 Lua,然后在脚本中使用它。 (例如:引擎有一个怪物的实例,我想为怪物运行一个脚本,看看它是否看到了玩家——如果是,那么怪物会攻击玩家)。

我找到的唯一解决方案是这个:Passing existing C++ objects to Lua and calling the passed objects' member functions - 但是,原始发布者使用的是需要 boost(我真的不想使用)的 luabind。

因此,我的问题是:

如何将已在 C++ 中的堆上分配的对象传递给 Lua 脚本? (不使用 luabind)

这种方法是否正确?我发现的大部分内容都倾向于回答“如何在 Lua 中创建 C++ 类的实例”这个问题,而不仅仅是传递它,这让我开始思考我的想法是否正确。

注意:我不介意使用 luabind 之类的工具,我只是不想使用它们,以防它们依赖于外部库(例如 boost)。如果有任何像这样简单的解决方案,我会很乐意使用它。

【问题讨论】:

  • 你需要为类定义一个元表并编写一堆函数...
  • 如果你的对象被破坏了,但是 lua 仍然把它当作人质,会发生什么?

标签: c++ class lua


【解决方案1】:

https://github.com/Rapptz/sol

Sol 是可以支持类 C++11+ 语义的库的完美示例,包括使用模板传递类类型。如果我是你,我会查看 Sol 的源代码并尝试复制它的对象和数据传递方法——Lua 的大部分“元”功能可能建立在它的 userdata 结构上,所以我建议你开始看着那里。

顺便说一句,Sol 仅依赖于 Lua,并且仅包含标头。如果您使用的是 Lua 5.2(而不是 5.3,这是社区的一大抱怨,但没关系,因为 LuaJIT 也在 5.2 上,我相信)开箱即用是很有可能的。

【讨论】:

  • LuaJIT 是 5.1,不是 5.2
【解决方案2】:

我发现的大部分内容都倾向于回答“如何在 Lua 中创建 C++ 类的实例”这个问题,而不是仅仅传递它,这让我开始思考我的想法是否正确。

这些也是您问题的答案。您不能在 Lua 中创建 C++ 类。任何类的构造都将在 C++ 代码中完成。

如果您不打算使用绑定库,那么您将需要成为 Lua 的 C API 元表的导出(不用担心,这并不难)。 p>

您将 C++ 对象作为“用户数据”公开给 Lua。然后你提供一个带有__index 元方法的元表,它允许你为对象被索引时编写C++ 处理程序,例如在访问属性时(例如pt.x)或在调用方法时(例如@987654325 @)。您还需要一个__gc 元方法,以便在相应的 Lua 用户数据被垃圾回收时删除 C++ 对象。

可能有相当多的样板代码,这是许多C++ binding libraries 应该处理的。我从来没有用过,所以我不能推荐一个。但是,由于Law of Leaky Abstractions,我强烈建议您自己尝试一下,以便您了解它是如何工作的。

【讨论】:

    【解决方案3】:

    如果你想要真正独立的绑定,这里是经典的 C 风格样板。只需将“Object”替换为您的类名并实现// { } 块。将push_Object 从静态转为公共将为您提供通用的现有对象推送器,该对象推送器还将对象缓存在元表中(否则多次推送会创建许多不同的用户数据,并带有明显的 gc 问题)。

    如果你愿意,你也可以 C++ify 或 library-fy,但我个人不这样做,因为添加例如__newindex-to-environment 代理和其他怪癖如果是库就不会那么简单了。实际上,所有样板都在push_mtpush_Objectforget_Objectcheck_Object 中,其他的都需要微调。

    请注意,这只会绑定一个单个类,而不是一次绑定所有类。

    // { class Object { ... } }
    
    static const char *tname = "Object";
    
    static void push_Object(lua_State *L, Object *object);
    static Object *check_Object(lua_State *L, int i);
    
    static int
    l_gc(lua_State *L)
    {
        Object **ud = luaL_checkudata(L, 1, tname);
    
        if (*ud) {
            // { delete *ud }
            *ud = NULL;
        }
        return 0;
    }
    
    static int
    l_tostring(lua_State *L)
    {
        Object **ud = luaL_checkudata(L, 1, tname);
    
        lua_pushfstring(L, "%s: %p", tname, *ud);
        return 1;
    }
    
    static int
    l_new(lua_State *L)
    {
        Object *object = NULL; // { = new Object }
    
        push_Object(L, object);
        return 1;
    }
    
    static int
    l_method(lua_State *L)
    {
        Object *object = check_Object(L, 1);
        lua_Integer int_arg = luaL_checkinteger(L, 2);
        const char *str_arg = luaL_checklstring(L, 3, NULL);
    
        // { object->method(int_arg, str_arg) }
    
        return 0;
    }
    
    static const luaL_Reg lib[] = {
        // functions
        { "new",    l_new    }, // () -> object
    
        // methods
        { "method", l_method }, // (object, int, string) -> none
    
        { NULL, NULL },
    };
    static lua_CFunction first_m = l_method;
    
    static void
    push_mt(lua_State *L)
    {
        if (luaL_newmetatable(L, tname)) {
            size_t m = 0; while (first_m != lib[m].func) m++;
            lua_createtable(L, 0, 0);
            luaL_register(L, NULL, &lib[m]);
            lua_setfield(L, -2, "__index");
    
            lua_pushcfunction(L, l_tostring);
            lua_setfield(L, -2, "__tostring");
    
            lua_pushcfunction(L, l_gc);
            lua_setfield(L, -2, "__gc");
    
            lua_pushstring(L, tname);
            lua_setfield(L, -2, "__metatable");
    
            // mt.objects = setmetatable({ }, { __mode = "v" })
            lua_createtable(L, 0, 0);
            lua_createtable(L, 0, 1);
            lua_pushstring(L, "v");
            lua_setfield(L, -2, "__mode");
            lua_setmetatable(L, -2);
            lua_setfield(L, -2, "objects");
        }
    }
    
    static void
    push_Object(lua_State *L, Object *object)
    {
        int top = lua_gettop(L);
    
        push_mt(L);
        lua_getfield(L, -1, "objects");
        // top+1 = mt
        // top+2 = mt.objects
    
        // ud = mt.objects[object]
        lua_pushlightuserdata(L, object);
        lua_gettable(L, top+2);
    
        if (lua_isnil(L, -1)) {
            lua_pop(L, 1);
    
            Object **ud = lua_newuserdata(L, sizeof(*ud));
            *ud = object;
    
            // setmetatable(ud, mt)
            lua_pushvalue(L, top+1);
            lua_setmetatable(L, -2);
    
            // mt.objects[object] = ud
            lua_pushlightuserdata(L, object);
            lua_pushvalue(L, -3);
            lua_pushvalue(L, top+2);
        }
    
        // return ud
        lua_replace(L, top+1);
        lua_settop(L, top+1);
        return; // ud at top
    }
    
    static void
    forget_Object(lua_State *L, Object *object)
    {
        int top = lua_gettop(L);
    
        push_mt(L);
        lua_getfield(L, -1, "objects");
        // top+1 = mt
        // top+2 = mt.objects
    
        // ud = mt.objects[object]
        lua_pushlightuserdata(L, object);
        lua_pushnil(L);
        lua_settable(L, top+2);
    
        lua_settop(L, top);
    }
    
    static Object *
    check_Object(lua_State *L, int i)
    {
        Object **ud = luaL_checkudata(L, i, tname);
        Object *object = *ud;
    
        if (object == NULL)
            luaL_error(L, "%s is finalized", tname);
    
        return object;
    }
    
    int
    luaopen_Object(lua_State *L)
    {
        push_mt(L); // register tname
    
        lua_createtable(L, 0, sizeof(lib)-1);
        luaL_register(L, NULL, lib);
        return 1;
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2019-03-23
      • 2017-07-21
      • 2015-06-07
      • 2013-09-15
      • 1970-01-01
      • 2013-08-04
      • 1970-01-01
      相关资源
      最近更新 更多