分类: Python/Ruby
2012-03-08 10:02:55
先写一个类, 没有什么意义, 测试用一下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | class MyClass { public: int add(int n) { return n + m_n; } int Foo2(int n) { return n*m_n; } public: int m_n; }; |
我创建MyClass的实例, 向Lua注册一个函数Func, 将实例和MyClass成员函数与Func绑定到一起. 当Lua代码调用Func的时候, 成员函数被调用. 这点需求折腾了我半天, 现在终于找到该怎么搞定了.
解决办法就是用C++模板.
让我们先来看一下注册函数:
1 2 3 4 5 6 7 8 9 10 11 | template void RegisterMemberFunc(lua_State* pLuaState, const char* pszName, T* pObj, TFunc pFunc) { lua_pushstring(pLuaState, pszName); char* pData = (char*)lua_newuserdata(pLuaState, sizeof(T*) + sizeof(TFunc)); memcpy(pData, &pObj, sizeof(T*)); memcpy(pData + sizeof(T*), &pFunc, sizeof(TFunc)); lua_pushcclosure(pLuaState, &CallBack lua_settable(pLuaState, LUA_GLOBALSINDEX); } |
使用方法很简单:
1 | RegisterMemberFunc(pLuaState, "Add", &m, &MyClass::add); |
在这个函数中, 我们创建了一个userdata, 用于保存实例地址, 成员函数地址, 并且在pushcclosure时, 将userdata和Add函数绑定.
CallBack函数的定义如下:
1 2 3 4 5 6 7 8 9 10 | template int CallBack(lua_State* pLuaState) { char* pData = (char*)lua_touserdata(pLuaState, lua_upvalueindex(1)); T* pObj = *(T**)(pData); TFunc* pFunc = (TFunc*)(pData + sizeof(T*)); return Call(pObj, *pFunc, pLuaState); } |
在这个回调函数中, 用 lua_upvalueindex获取存储在当前函数中的userdata, 里面就是我们保存的实例地址和成员函数地址. 获得必要的信息以后, 调用Call进行实际的操作.
Call是另外一个模板函数:
1 2 3 4 5 6 7 8 9 | template int Call(T* pObj, RT (T::*pFunc)(P1), lua_State* pLuaState) { RT result = (pObj->*pFunc)(GetValue PushValue(pLuaState, result); return 1; } |
这个辅助函数用于解析成员函数的返回值, 参数类型. 为了节约篇幅, 我紧紧写出了只有一个参数的情况, 根据需求的不同, 我们可以编写更多个参数的情况, 另外, 在这里, 成员函数返回值类型不能为void.
GetValue, PushValue, 是两套模板函数, 用于对不同的参数类型进行不同的操作:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 | template T GetValue(lua_State* pLuaState, const int index) { return T(); } template<> int GetValue(lua_State* pLuaState, const int index) { return static_cast } template<> float GetValue(lua_State* pLuaState, const int index) { return static_cast } template<> double GetValue(lua_State* pLuaState, const int index) { return static_cast } template<> std::string GetValue(lua_State* pLuaState, const int index) { return std::string(lua_tostring(pLuaState, index)); } //---------------------------------------------------------------------------------------------- template void PushValue(lua_State* pLuaState, T) { } template<> void PushValue(lua_State* pLuaState, int nValue) { lua_pushinteger(pLuaState, (lua_Integer)nValue); } template<> void PushValue(lua_State* pLuaState, float fValue) { lua_pushnumber(pLuaState, (lua_Number)fValue); } template<> void PushValue(lua_State* pLuaState, double dValue) { lua_pushnumber(pLuaState, (lua_Number)dValue); } template<> void PushValue(lua_State* pLuaState, const char* psz) { lua_pushstring(pLuaState, psz); } |
这里, 不得不编写模板的特例, 因为各种类型对应的Lua函数都不一样, 这同时带来了许多限制. 不过暂时用不到太多的数据类型.
现在, 我们将一切穿插起来:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | lua_State* pLuaState = luaL_newstate(); luaL_openlibs(pLuaState); MyClass m; m.m_n = 100; RegisterMemberFunc(pLuaState, "Add", &m, &MyClass::add); if (luaL_loadfile(pLuaState, "test.lua") || lua_pcall(pLuaState, 0, 0, 0)) { // something wrong printf("something wrong\n"); } lua_close(pLuaState); |
在test.lua中, 我们编写如下代码:
并且调进C++代码中, 就能看到想要的结果了.
但是到这里, 还远远不够.