C++是一门非常复杂的语言,然而更可怕的是,这门语言的各种奇葩特性还在继续增加,很多C++的程序员都会觉得这完全是在给自己添堵嘛,都已经这么复杂了,何必还要使劲往里面塞东西呢,明明C++03或者说是C++98的标准就已经完全够用了。我个人的看法呢,其实后续的标准还是很有必要的,因为这里面的很多新标准,对于一些写底层库的作者来说,真的是非常需要,换句话说,如果没有type_traits、
右值语义、
可变参数模板这些特性,那我就不会重新造这个轮子,也就不会写这篇文章了,正是因为C++11的标准里面引入了这些特性,以往一些库的实现就显得很笨重、很复杂,所以我决定重新造一个代码轻量级但是功能却不会有删减的轮子。最后,友情提示那些反感C++新标准的小伙伴一下,其实这些新标准里面大部分的内容我们根本就不必关心,因为对于写逻辑层的功能来说,这些功能我们根本就用不上,能用上的也就一些新增的语法糖,例如auto、foreach、lambda,其它的功能有个毛用啊,不过呢,这些语法糖还是建议了解一下,可以省很多事哦。
我个人最早接触的lua绑定库是ELuna,它是由我的一个同事引入项目中的,并且以此为基础进行了大量魔改操作,然后当我接手这个代码的时候,可读性有点伤人。我后来又在网上搜了一下,感觉luatinker这个库评价挺好的,于是我就想在项目里引入luatinker。可惜的是,luatinker是基于lua5.1写的,而当时lua5.3已经出来了,大家都知道,lua5.3是原生支持64位整数的,而在这之前,所有的lua绑定库为了支持64位整数,都得用一些奇葩手段才能简单支持,最重要的是,我有把64位整数传入脚本的需求,所以只好先做移植了,好不容易移植过去了,写了几个例子一跑,惨了,有bug啊。好吧,那就继续改吧,等这些都做完了,静下来想了想,好像对于这个luatinker自己已经完全掌握了,而且这个库里面还有好多代码是c++11已经原生支持了的,干脆我自己用c++11实现一份吧,这样子代码肯定简洁的多,代码写得越少,bug也就写得越少嘛。所以总结就一句话,以下这个库的api设计跟luatinker几乎完全一致,只要你用过luatinker,那么用这个库,基本上是0成本,除非是luatinker缺失的功能。
好了,开始进入正题。我魔改过的luatinker:https://github.com/jallyx/LuaTinker,新轮子:https://gitee.com/jallyx/fusion/tree/master/src/lua。友情提示,std::string_view是c++17引入的,所以c++11编译这个代码需要先自己处理一下,方案1、删掉这个功能,具体做法就是哪儿编译出错就删哪儿,方案2、使用boost提供的boost::string_view或boost::string_ref,然后这样包装一下https://gitee.com/jallyx/fusion/blob/master/feature/string_view,方案3、使用c++17吧。
让我们先从luatinker的例子走起。
1 #include "lua/LuaFunc.h" 2 3 int cpp_func(int arg1, int arg2) { 4 return arg1 + arg2; 5 } 6 7 const char *lua_str = R"( 8 print('cpp_func(1,2) = ' .. cpp_func(1, 2)) --调用c++全局函数 9 function lua_func(arg1, arg2) --定义lua全局函数 10 return arg1 + arg2 11 end 12 )"; 13 14 int main(int argc, char **argv) 15 { 16 lua_State *L = lua::open(); // 新建lua虚拟机,并做一些必要的初始化工作。 17 lua::def(L, "cpp_func", cpp_func); // 向lua注册c++全局函数 18 lua::dostring(L, lua_str); // 执行lua脚本,如果是文件则需要调用lua::dofile。 19 printf("lua_func(3,4) = %d\n", LuaFunc(L, "lua_func").Call<int>(3, 4)); // 调用lua的全局函数 20 lua::close(L); // 关闭lua虚拟机,释放资源 21 return 0; 22 }
程序输出:
cpp_func(1,2) = 3 lua_func(3,4) = 7
似乎太简单了,我都不知道需要解释点啥,接下来进入第二个例子。
1 #include "lua/LuaBus.h" 2 3 static int cpp_int = 100; 4 5 const char *lua_str = R"( 6 print('cpp_int = ' .. cpp_int) -- 打印c++的全局变量 7 lua_int = 200 -- 定义lua的全局变量 8 )"; 9 10 int main(int argc, char **argv) 11 { 12 lua_State *L = lua::open(); // 新建lua虚拟机,并做一些必要的初始化工作。 13 lua::set(L, "cpp_int", cpp_int); // 向lua注册c++全局变量 14 lua::dostring(L, lua_str); // 执行lua脚本,如果是文件则需要调用lua::dofile。 15 printf("lua_int = %d\n", lua::get<int>(L, "lua_int")); // 获取lua的全局变量 16 lua::close(L); // 关闭lua虚拟机,释放资源 17 return 0; 18 }
程序输出:
cpp_int = 100 lua_int = 200
第三个例子,稍微有点复杂,主要是对c++类成员函数、成员变量、类继承的支持。在最后的lua代码中,对用到的c++类变量进行了一个内省输出的操作,如果大家对自己的定位是这个库的使用者,则完全没必要关心。PS:我写这段代码也只是因为luatinker的测试代码是这样的而已。
1 #include "lua/LuaBus.h" 2 3 struct A { 4 A(int v) : value(v) {} 5 int