我目前正在开发自己的视频游戏引擎,并且我试图实现对lua脚本的支持,以便对游戏行为进行编码。但是,我目前正在努力学习Lua中的C ++课程。我理解如何通过lua在堆上创建类的新实例 - 但这可能(可能)不是我想要做的。
我宁愿将已经存在于C ++中的对象传递给Lua,然后在脚本中使用它。 (示例:引擎有一个怪物的实例,我想为怪物运行一个脚本,看它是否看到了玩家 - 如果是,那么怪物会攻击玩家。)
我找到的唯一解决方案就是这一个:Passing existing C++ objects to Lua and calling the passed objects' member functions - 然而,原始海报使用luabind,这需要提升(我不想真正使用)。
因此,我的问题是:
如何将已在C ++堆上分配的对象传递给Lua脚本? (不使用luabind)
这种方法是否正确?我发现的大部分内容都倾向于回答问题"如何在Lua"中创建一个C ++类的实例。而不是仅仅通过它,这让我想到我的想法是否正确。
注意:我不介意使用像luabind这样的工具,我只是不想使用它们,以防它们依赖外部库,如boost。如果有这样的简单解决方案,那么我很乐意使用它。
答案 0 :(得分:1)
如果你想要真正独立的绑定,这里是经典的C风格样板。只需替换"对象"使用您的类名并实现// { }
块。将push_Object
从静态变为公共将为您提供通用的现有对象推送器,它还可以缓存metatable中的对象(否则多推将创建许多不同的用户数据,并且具有明显的gc-trouble)。 / p>
如果你愿意,也可以使用C ++ ify或library-fy,但我个人不这样做,因为如果它是库,__ newindex到环境代理和其他怪癖就不那么简单了。实际上,所有样板都在push_mt
,push_Object
,forget_Object
和check_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;
}
答案 1 :(得分:0)
Sol是一个完美的库,它可以支持类似C ++ 11 +的语义,包括使用模板传递类类型。如果我是你,我会查看Sol的源代码并尝试复制它的对象和数据传递方法 - 很多Lua" meta"功能可能建立在userdata
结构上,所以我建议你开始寻找它。
顺便说一下,Sol只依赖于Lua,并且只是头部包含。只要您使用Lua 5.2(而不是5.3,这是社区的一个大抱怨,但不应该是重要的,因为LuaJIT也是如此,开箱即用它是完全可能的。在5.2,我相信。)
答案 2 :(得分:0)
我发现的大部分内容都倾向于回答“如何在Lua中创建C ++类的实例”的问题,而不仅仅是传递它,这使我想到我的想法是否正确。
这些也是你问题的答案。你不能在Lua中创建一个C ++类。任何类构造都将在C ++代码中完成。
如果您不打算使用绑定库,那么您将需要在Lua的C API 和 metatables上进行导出(不用担心,这并不难)。
将C ++对象公开为Lua作为“userdata”。然后,您提供了一个带有__index
元方法的元表,它允许您在对象被索引时编写C ++处理程序,例如在访问属性时(例如pt.x
)或正在调用方法时(例如entity:attack(target)
)。您还需要一个__gc
元方法,以便在对相应的Lua用户数据进行垃圾回收时删除C ++对象。
可能会有相当多的样板代码,这是许多C++ binding libraries应该照顾的代码。我从来没用过,所以我不推荐一个。但是,由于Law of Leaky Abstractions,我强烈建议您自己动手,以便了解它是如何工作的。