Lua学习笔记(十五)
时间:2010-09-11 来源:glshader
先写一个类, 没有什么意义, 测试用一下:
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++模板.
让我们先来看一下注册函数:
template<typename T, typename TFunc> 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<T, TFunc>, 1); lua_settable(pLuaState, LUA_GLOBALSINDEX); }
使用方法很简单:
RegisterMemberFunc(pLuaState, "Add", &m, &MyClass::add);
在这个函数中, 我们创建了一个userdata, 用于保存实例地址, 成员函数地址, 并且在pushcclosure时, 将userdata和Add函数绑定.
CallBack函数的定义如下:
template<typename T, typename TFunc> 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是另外一个模板函数:
template<typename T, typename RT, typename P1> int Call(T* pObj, RT (T::*pFunc)(P1), lua_State* pLuaState) { RT result = (pObj->*pFunc)(GetValue<P1>(pLuaState, 1)); PushValue(pLuaState, result); return 1; }
这个辅助函数用于解析成员函数的返回值, 参数类型. 为了节约篇幅, 我紧紧写出了只有一个参数的情况, 根据需求的不同, 我们可以编写更多个参数的情况, 另外, 在这里, 成员函数返回值类型不能为void.
GetValue, PushValue, 是两套模板函数, 用于对不同的参数类型进行不同的操作:
template<typename T> T GetValue(lua_State* pLuaState, const int index) { return T(); } template<> int GetValue(lua_State* pLuaState, const int index) { return static_cast<int>(lua_tointeger(pLuaState, index)); } template<> float GetValue(lua_State* pLuaState, const int index) { return static_cast<float>(lua_tonumber(pLuaState, index)); } template<> double GetValue(lua_State* pLuaState, const int index) { return static_cast<double>(lua_tonumber(pLuaState, index)); } template<> std::string GetValue(lua_State* pLuaState, const int index) { return std::string(lua_tostring(pLuaState, index)); } //---------------------------------------------------------------------------------------------- template<typename T> 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函数都不一样, 这同时带来了许多限制. 不过暂时用不到太多的数据类型.
现在, 我们将一切穿插起来:
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++代码中, 就能看到想要的结果了.
但是到这里, 还远远不够.