手动将c++类注册进lua中
本片博文是基于上篇博文“tolua++初始化过程”的中tolua++将C++类注册进lua的思想来写的。使用tolua++是为了方便,而手动将C++类注册进lua则是为了熟悉整个过程,成为高手的路总是孤独的。
首先,定义一个要注册进lua环境的C++类,定义如下:
CTest:
1 class CTest 2 { 3 public: 4 CTest(); 5 ~CTest(); 6 7 private: 8 int a, b; 9 10 public: 11 void setA(int a); 12 void setB(int b); 13 int getAdd(); 14 };
这个类的主要功能就是为类属性a和b赋值,然后求和,仅此而已。分别对应的函数为void setA(int a);void setB(int b);int getAdd();。
下面为这个CTest类做一个外壳,用于注册进lua环境。代码如下:
static int wrap_new(lua_State* L) { CTest* ud = new CTest(); *(CTest**)lua_newuserdata(L, sizeof(CTest*)) = ud; luaL_getmetatable(L, "CTest"); if (lua_istable(L, -1)) { lua_setmetatable(L, -2); } return 1; } static int wrap_collecor(lua_State*L) { if (lua_isuserdata(L, 1)) { CTest* ud = (CTest*)lua_touserdata(L, 1); delete ud; } else { lua_error(L); } return 0; } static int wrap_setA(lua_State*L) { if (lua_isuserdata(L, 1)) { CTest*ud = (CTest*)lua_touserdata(L, 1); int a = lua_tointeger(L, 2); ud->setA(a); } else { lua_error(L); } return 0; } static int wrap_setB(lua_State*L) { if (lua_isuserdata(L, 1)) { CTest*ud = (CTest*)lua_touserdata(L, 1); int a = lua_tointeger(L, 2); ud->setB(a); } else { lua_error(L); } return 0; } static int wrap_getAdd(lua_State*L) { if (lua_isuserdata(L, 1)) { CTest*ud = (CTest*)lua_touserdata(L, 1); int sum = ud->getAdd(); lua_pushinteger(L, sum); } else { lua_error(L); } return 1; } static int wrap_index_event(lua_State*L) { if (!lua_isuserdata(L, 1)) { lua_error(L); return 0; } if (lua_getmetatable(L, 1)) { lua_pushvalue(L, 2); lua_rawget(L, -2); if (lua_isfunction(L, -1)) return 1; else lua_error(L); } }
下面的两个函数是将上面的函数注册进lua环境的动作:
void Register(lua_State*L, char* name, luaL_Reg* st) { luaL_getmetatable(L, name); if (lua_isnil(L, -1)) { luaL_newmetatable(L, name); for (int i = 0; st[i].func; i++) { lua_pushstring(L, st[i].name); lua_pushcfunction(L, st[i].func); lua_rawset(L, -3); } } lua_setglobal(L, name); return; } void Test_Register(lua_State*L) { luaL_Reg st[] = { { "__index", wrap_index_event }, { "__gc", wrap_collecor }, { "new", wrap_new }, { "setA", wrap_setA }, { "setB", wrap_setB }, {"getAdd", wrap_getAdd}, {NULL, NULL} }; Register(L, "CTest", st); }
从代码可以看到,在Test_Register中调用了Register函数,在Test_Register中将相应的外壳函数组成一个luaL_Reg型的数组,然后调用Register进行注册。假如要增加新的函数,则只需在luaL_Reg的数组中增加一条即可,但需要在{NULL, NULL}的前面。
接下来就可以在main函数中进行注册,并调用执行lua文件。main函数的代码如下:
int _tmain(int argc, _TCHAR* argv[]) { lua_State*L = luaL_newstate(); Test_Register(L); if (luaL_loadfile(L, "test.lua") || lua_pcall(L, 0, 0, 0)) { } lua_close(L); return 0; }
lua文件如下:
test = CTest:new() test:setA(1) test:setB(2) print(test:getAdd())
其中的技巧,在于为__index注册的回调函数,在其中查找对应类的成员函数的外壳函数。
在每一个回调函数中,栈底为userdata,如果有参数的话,则后面跟的就是参数。