我先来吐槽一下我们这个项目.
我是做手机游戏的, cocos2dx引擎, lua编码.
这本来是一件很欢快的事情, 因为不用接触C++.
C++写久了的人写lua, 就会感觉任督二脉被打通了, 代码写起来都不用太多考虑,
就像涉世太深的人吹起牛逼肆无忌惮, 总是可以自圆其说.
然而, 事与愿违, 我们客户端的兄弟仍然要编写大量C++代码,
其原因是, 配置文件, 数据结构统统由后端决定,
而后端没有人会lua, 因此数据全部放在C++,
客户端每一次存取数据都会接触到C++.
然后, 就出现了今天的这篇随笔.
主要是解决了, lua和C++交互的问题.
当然tolua可以解决这些问题, 但是相比之下, 太麻烦, 因为C++那一块太庞大, 我并不需要整个都导入lua.
先来看看, 最原始的代码.
typedef struct { USHORT MapID; //地图ID ]; //地图名称 ]; //地图数据文件 }ScenceMapConfig;
我从在lua中取C++里的这么一个结构.
lua_newtable(lua); lua_pushstring(lua, "MapID"); lua_pushinteger(lua, pConfig->MapID); lua_settable(lua, -); lua_pushstring(lua, "MapName"); lua_pushstring(lua, pConfig->MapName); lua_settable(lua, -); lua_pushstring(lua, "MapDataFile"); lua_pushstring(lua, pConfig->MapDataFile); lua_settable(lua, -);
这里假设, pConfig 是指向这个结构的指针.
取了三个字段, 写了这么多代码, 重复的还有好几行.
lutils::luaOpenTable(lua); LUA_PUSHPAIR(pConfig, MapID); LUA_PUSHPAIR(pConfig, MapName); LUA_PUSHPAIR(pConfig, MapDataFile);
效果跟上面一样.
上面说的是从lua取C++的值.
下面说从lua传值到C++.
ScenceMapConfig config; lua_pushstring(lua, "MapID"); lua_gettable(lua, -); config.MapID = lua_tointeger(lua, -); lua_pop(lua, ); lua_pushstring(lua, "MapName"); lua_gettable(lua, -); strcpy(config.MapName, lua_tostring(lua, -)); lua_pop(lua, ); lua_pushstring(lua, "MapDataFile"); lua_gettable(lua, -); strcpy(config.MapDataFile, lua_tostring(lua, -)); lua_pop(lua, );
这里也是取了三个字段, 弊端跟上面一样.
下面看看怎么简化.
ScenceMapConfig config; config.MapID = lutils::luaGetValueByTable<); strcpy(config.MapName, lutils::luaGetValueByTable<std::).c_str()); strcpy(config.MapDataFile, lutils::luaGetValueByTable<std::).c_str());
干净利落, 相当简洁.
接下来是手动注册C++函数到lua.
如果直接注册全局函数, 命名污染太严重.
可以把函数按模块来划分.
auto lua = LuaEngine::getInstance()->getLuaStack()->getLuaState(); lutils::luaBeginModule(lua); LUA_ADDMODULE(getSceneMapVecType); LUA_ADDMODULE(getCityMapVecType); LUA_ADDMODULE(getBuildingUpgradeConfig); LUA_ADDMODULE(getSiverToCurrentConfig); lutils::luaEndModule(lua, "config");
在lua里就可以直接用 config.* 来调用这些函数了.
并且, 这个 config 可以在lua里扩展.
比如:
config = config or {}; function config.func() end
接下来, 睁大你们的双眼, 我要出王炸了.
template <class T> inline T luaGetValue(lua_State *lua, int idx) { #ifdef _MSC_VER static_assert(, ""); #else CC_ASSERT(); #endif } template <> inline int luaGetValue(lua_State *lua, int idx) { return lua_tointeger(lua, idx); } template <> inline short luaGetValue(lua_State *lua, int idx) { return lua_tointeger(lua, idx); } template <> inline float luaGetValue(lua_State *lua, int idx) { return lua_tonumber(lua, idx); } template <> inline bool luaGetValue(lua_State *lua, int idx) { ; } template <> inline std::string luaGetValue(lua_State *lua, int idx) { return SFStringHelper::setUtf8ToGbk(lua_tostring(lua, idx)); } // template<class T> inline void luaPushValue(lua_State *lua, const T &value) { #ifdef _MSC_VER static_assert(, ""); #else CC_ASSERT(); #endif } inline void luaPushValue(lua_State *lua, const int &value) { lua_pushinteger(lua, value); } inline void luaPushValue(lua_State *lua, const u_int &value) { lua_pushinteger(lua, value); } inline void luaPushValue(lua_State *lua, const long &value) { lua_pushinteger(lua, value); } inline void luaPushValue(lua_State *lua, const u_long &value) { lua_pushinteger(lua, value); } inline void luaPushValue(lua_State *lua, const short &value) { lua_pushinteger(lua, value); } inline void luaPushValue(lua_State *lua, const u_short &value) { lua_pushinteger(lua, value); } inline void luaPushValue(lua_State *lua, const char &value) { lua_pushinteger(lua, value); } inline void luaPushValue(lua_State *lua, const u_char &value) { lua_pushinteger(lua, value); } inline void luaPushValue(lua_State *lua, const float &value) { lua_pushnumber(lua, value); } inline void luaPushValue(lua_State *lua, const double &value) { lua_pushnumber(lua, value); } inline void luaPushValue(lua_State *lua, const bool &value) { lua_pushboolean(lua, value); } inline void luaPushValue(lua_State *lua, const char *value) { lua_pushstring(lua, SFStringHelper::setGbkToUtf8(value).c_str()); } inline void luaPushValue(lua_State *lua, const std::string &value) { luaPushValue(lua, value.c_str()); } inline void luaPushValue(lua_State *lua, const lua_CFunction call) { lua_pushcfunction(lua, call); } template <class T> inline void luaOpenTable(lua_State *lua, const T &key) { luaPushValue(lua, key); lua_newtable(lua); } inline void luaOpenTable(lua_State *lua) { lua_newtable(lua); } inline void luaCloseTable(lua_State *lua) { lua_settable(lua, -); } template<class T1, class T2> inline void luaPushPair(lua_State *lua, const T1 &key, const T2 &value) { luaPushValue(lua, key); luaPushValue(lua, value); luaCloseTable(lua); } #define LUA_PUSHPAIR(ptr, member) lutils::luaPushPair(lua, #member, (ptr)->member) #define LUA_PUSHARRAY(ptr, member, n) \ lutils::luaOpenTable(lua, #member); \ ; i != n; ++i) \ { \ lutils::luaPushPair(lua, i+, (ptr)->member[i]); \ } \ lutils::luaCloseTable(lua); #define LUA_ADDMODULE(member) lutils::luaPushPair(lua, #member, member); // 从lua表获取元素. template <class T1, class T2> T1 luaGetValueByTable(lua_State *lua, const T2 &key, int idx) { luaPushValue(lua, key); lua_gettable(lua, idx); T1 ret = luaGetValue<T1>(lua, -); lua_pop(lua, ); return ret; } // 添加模块到lua. inline void luaBeginModule(lua_State *lua) { lua_newtable(lua); } inline void luaEndModule(lua_State *lua, const char *name) { lua_setglobal(lua, name); }
其实, 在所有的 luaPushPair 上面都有一行 template<>,
后来被一个同事误以为是多余的一行代码, 把它删除了...
把特化当作重载, 我也是醉了. 战 五 渣 .
好了, 全部代码上完, 坐等下班~~