C++-c++在lua中的应用

C++-c++在lua中的应用

晚风撩人 发布于 2017-05-27 字数 69 浏览 1342 回复 2

c++定义的一个对象,如何在lua中调被调用,并能动态实现对象的生成

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

虐人心 2017-09-24 2 楼

说下大体思路,由于LUA是脚本语言,我们知道脚本是解释执行的,所以在脚本中直接调用高级语言是不行的,因为脚本的解析器是理解不了高级语言的,基本上所有脚本要调用高级语言,都需要通过脚本提供的导入和到处的接口,使高级语言先变为脚本可以理解的接口形式。
详细可以参考LUA官方说明:http://lua-users.org/wiki/CppBindingWithLunar

一个实现的方式:http://www.cnblogs.com/ringofthec/archive/2010/10/26/luabindobj.html

浮生未歇 2017-07-08 1 楼

在lua中调用c++对象的方法, 有几个要注意的地方:

需要在lua中调用的方法 func 必须导出到lua中.
lua调用对象方法的时候, 必须能够获取到该对象, 因为必须使用 obj->(*func)(L) 这样的形式调用成员函数.
在lua中, 把func 和 obj 关联起来.
其中, 解决1的方法是lua提供的, 通过压入c 闭包到lua中就可以实现函数的导出, 这个是比较简单的.
对于2, 一般lua中持有c++对象是使用userdata来实现的(userdata 类型用来将任意 C 数据保存在 Lua 变量中. 这个类型相当于一块原生的内存, 除了赋值和相同性判断, Lua 没有为之预定义任何操作. 然而, 通过使用 metatable (元表), 程序员可以为 userdata 自定义一组操作. userdata 不能在 Lua 中创建出来, 也不能在 Lua 中修改. 这样的操作只能通过 C API, 这一点保证了宿主程序完全掌管其中的数据. metatable 中还可以定义一个函数,让 userdata 作垃圾收集时调用它 --- lua 5.1 参考手册).

下面是示例代码:
方法1:
创建c++对象的时候, 创建一个表

tt = {} tt[0] = obj [userdata] tt[1 ...] = func1, func2, ...
struct RegType
{
const char* name;
int (FooPort::*mfunc)(lua_State* L);
};

class LuaPort
{public:
static void RegisterClass(lua_State* L)
{
// 导出一个方法创建c++, 因为创建c++对象是在lua中发起的
lua_pushcfunction(L, &LuaPort::constructor);
lua_pushglobal(L, "Foo");

// 创建userdata要用的元表(其名为Foo), 起码要定义__gc方法, 以便回收内存
luaL_newmetatable(L, “Foo”);
lua_pushstring(L, “__gc”);
lua_pushcfunction(L, &LuaPort::gc_obj);
lua_settable(L, -3);
}
static int constructor(lua_State* L)
{
// 1. 构造c++对象
FooWrapper* obj = new FooWrapper(L);

// 2. 新建一个表 tt = {}
lua_newtable(L);

// 3. 新建一个userdata用来持有c++对象
FooWrapper** a = (FooWrapper** )lua_newuserdata(L, sizeof(FooWrapper*));
*a = obj;

// 4. 设置lua userdata的元表
luaL_getmetatable(L, “Foo”);
lua_setmetatable(L, -2);

// 5. tt[0] = userdata
lua_pushnumber(L, 0);
lua_insert(L, -2);
lua_settable(L, –3);

// 6. 向table中注入c++函数
for (int i = 0; FooWrapper::Functions[i].name; ++i)
{
lua_pushstring(L, FooWrapper::Functions[i].name);
lua_pushnumber(L, i);
lua_pushcclosure(L, &LuaPort::porxy, 1);
lua_settable(L, -3);
}

// 7. 把这个表返回给lua
return 1;
}
static int porxy(lua_State* L)
{
// 取出药调用的函数编号
int i = (int)lua_tonumber(L, lua_upvalueindex(1));

// 取tt[0] 及 obj
lua_pushnumber(L, 0);
lua_gettable(L, 1);
FooWrapper** obj = (FooWrapper**)luaL_checkudata(L, –1, “Foo”);
lua_remove(L, -1);

// 实际的调用函数
return ((*obj)->*(FooWrapper::Functions[i].mfunc))(L);
}
static int gc_obj(lua_State* L)
{
FooWrapper** obj = (FooWrapper**)luaL_checkudata(L, –1, “Foo”);
delete (*obj);
return 0;
}
};

这个方法的主要部分是把obj 和 obj的函数组织成lua中的一张表, 思路比较简单, 但是有一个问题就是新建一个obj时, 都要在新建一个表并在里面加导出所有的方法, 感觉这样是冗余的.

方法2
和方法1类似, 但是用过使用元表, 来避免方法1中重复注册方法的问题
这里只列出不一样的地方

static void Register(lua_State* L)
{
lua_pushcfunction(L, LuaPort::constructor);
lua_setglobal(L, “Foo”);

luaL_newmetatable(L, “Foo”);
lua_pushstring(L, “__gc”);
lua_pushcfunction(L, &LuaPort::gc_obj);
lua_settable(L, -3);

// ----------- 不一样的地方
// 创建一个方法元表
lua_newtable(L);

// 指定__index方法
int meta = lua_gettop(L);
lua_pushstring(L, “__index”);
lua_pushvalue(L, meta);
lua_settable(L, –3);

// 注册所有方法
for (int i = 0; FooWrapper::Functions[i].name; ++i)
{
lua_pushstring(L, FooWrapper::Functions[i].name);
lua_pushnumber(L, i);
lua_pushcclosure(L, &LuaPort::porxy, 1);
lua_settable(L, -3);
}

// 把这个表放入元表以便后用, 起名为methods
lua_pushstring(L, “methods”);
lua_insert(L, -2);
lua_settable(L, -3);
}

static int constructor(lua_State* L)
{
// 1. 构造c++对象
FooWrapper* obj = new FooWrapper(L);

// 2. 新建一个表 tt = {}
lua_newtable(L);

// 3. 新建一个userdata用来持有c++对象
FooWrapper** a = (FooWrapper** )lua_newuserdata(L, sizeof(FooWrapper*));
*a = obj;

// 4. 设置lua userdata的元表
luaL_getmetatable(L, “Foo”);
lua_pushvalue(L, -1);
lua_setmetatable(L, -3);

// ------------不一样的地方
// 5. tt[0] = userdata
lua_insert(L, -2);
lua_pushnumber(L, 0);
lua_insert(L, -2);
lua_settable(L, -4);

// 6. 绑定方法元表
lua_pushstring(L, “methods”);
lua_gettable(L, -2);
lua_setmetatable(L, -3);
lua_pop(L, 1);

// 返回表
return 1;
}

这样的话, 只是在注册类型的时候把函数导入到lua中, 在以后的每次创建对象时, 只要将方法表值为其元表就可以了, 这样就避免了多次导入函数
但是这个方法还是有问题, 其实本身userdata就可有有元表, 用这个元表就可以了.

方法3
直接使用一个表做 userdata 的元表, 方法表等等.

 static void Register(lua_State* L)
{
lua_pushcfunction(L, LuaPort::construct);

lua_setglobal(L, “Foo”);

luaL_newmetatable(L, “Foo”);
lua_pushstring(L, “__gc”);
lua_pushcfunction(L, &LuaPort::gc_obj);

lua_settable(L, -3);

// ----- 不一样的
// 把方法也注册进userdata的元表里
for (int i = 0; FooWrapper::Functions[i].name; ++i)
{
lua_pushstring(L, FooWrapper::Functions[i].name);
lua_pushnumber(L, i);
lua_pushcclosure(L, &LuaPort::porxy, 1);
lua_settable(L, -3);
}

// 注册__index方法
lua_pushstring(L, “__index”);
lua_pushvalue(L, -2);
lua_settable(L, -3);
}

static int constructor(lua_State* L)
{
FooWrapper* obj = new FooWrapper(L);
FooWrapper** a = (FooWrapper**)lua_newuserdata(L, sizeof(FooWrapper*));
*a = obj;
luaL_getmetatable(L, “Foo”);
lua_setmetatable(L, -2);
return 1;
}

static int porxy(lua_State* L)
{
int i = (int)lua_tonumber(L, lua_upvalueindex(1));
FooPort** obj = (FooPort**)luaL_checkudata(L, 1, “Foo”);
return ((*obj)->*(FooWrapper::FunctionS[i].mfunc))(L);
}

第三种方法最简捷,推荐使用