Lua 挺好用的,但实际上,我一接触它,首先面临的问题就是复杂的 C++ 与 Lua 间的交互问题,不解决这个,单纯用个 Lua 并没有什么意义。
这个绑定的问题是复杂的,从众多的开源库[1]中就能瞥见端倪。问题的复杂性在于不同语言间交互面临的类型与存储的管理。
C++ 与 Lua 间的绑定,目测有两个流派:手动派和自动派
手动派就是手写栈操作,自动派也至少分两种,其一是靠编译器插件,另一是靠 wrapper。
我选择的方案是最简单的 wrapper 方案,这相关的开源库就挺多。
选择了 wrapper 后,剩下要做的事情就是用这个 wrapper 把 C++ 类注册上,只要结合上
"全球最强" の 99 行 C++ 静态反射库,这并不困难。
结合两相之力,C++ 与 Lua 间的绑定在用户端就变成了 1 行 的事
代码
Ubpa/ULuaPPgithub.com
1. Lua wrapper
awesome-cpp 上相关的库有 LuaBridge,Luacxx,sol2,SWIG(编译器)
我选择了其中“最潮”的 sol2 (v3),它文档十分丰富,开源工作搞得非常好。
但其实代码已经很脏了,作者面临各种 bug 已经感觉修不过来了。没办法,再怎么说这也是当前最好之一的库了。主要是来源于问题的复杂性吧。我看库里边真是无所不用其极,连 &/&& 成员函数都有,要处理 unique_ptr, shared_ptr, move, reference 等等麻烦问题。库的代码也来到了 25k 行,就解决这个 wrapper 的问题。问题实在是很复杂。
我还给他修了
不管怎样,用了再说
对于一个 C++ 的类

利用 sol2 注册到 Lua 中需要写如下代码

2. auto bind
在第 1 节中提到了在使用 sol2 时需要写一定的注册代码,我们看到,这个代码,就跟"全球最强" の 99 行 C++ 静态反射库 里的声明极其类似。对于 Point 类,静态反射的声明代码为

假设用户已经声明了静态反射,那么在使用 sol2 时,就可以利用这个静态信息自动生成 sol2 注册代码,此时用户只需写 1 行代码。

这行代码里干了什么呢?
- 获取构造函数(名字为
"constructor"的 field)组装成sol::initializers - 将所有函数按名字分组,然后各组分别注册(多函数组注册成
sol::overload,但函数就简单注册)。如果函数名是operator+,operator-等特殊函数,则注册成sol::meta_function::* - 将所有变量注册
另外,如 "全球最强" の 99 行 C++ 静态反射库 所述,域还有属性列表,我这里还会将属性注册上,对于重载函数,由于域同名,在注册时会自动加上必要的索引_0,_1以区分不同的域的属性列表
这其中涉及非常繁重的元编程(复杂度
constexpr 要做好多的工作,最后只需用下边的函数就能获取到一个类的各重载函数组(这里省略了其调用函数的细节)
结果确实是 constexpr 的

USRefl 库确实用起来挺顺手的,就是 C++ 语言的能力还不够,导致部分内容写法上需要绕。现在体会到了 C++20 的好处了,特别是类常量模板参数和 template lambda,可惜我目前用的 MSVC 只有鸡肋的/std:c++latst。再等等吧,终究是错付了。
3. 示例
下边举一个复杂的例子,包含了上边所讲的所有特性
类

涉及:构造函数(重载),变量,成员函数重载,特殊成员函数(operator-),类属性,变量属性,重载函数属性静态反射声明

接着用 sol2 注册到 Lua 中

然后在 Lua 里跑下边的代码

结果如下

完美!
4. 结语
至此,C++ 绑定到 Lua 后,使得我们后续的开发可以直接在 lua 上进行。
这项工作将用于我的引擎之中,供给 UECS 使用。
脚本真香!
参考
- ^http://lua-users.org/wiki/BindingCodeToLua
本文探讨了C++与Lua语言交互的复杂性,并详细介绍了使用sol2库简化这一过程的方法。通过结合全球最强の99行C++静态反射库,实现了自动注册C++类到Lua的功能,极大提升了开发效率。
5903

被折叠的 条评论
为什么被折叠?



