最优雅的C++跟lua交互.

时间:2022-12-23 20:59:10

我先来吐槽一下我们这个项目.

我是做手机游戏的, 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<>,

后来被一个同事误以为是多余的一行代码, 把它删除了...

把特化当作重载, 我也是醉了. 战 五 渣 .

好了, 全部代码上完, 坐等下班~~