背景
之前的一篇文章lua与C++整合并互相调用_wp500的博客-CSDN博客,是对lua与C++整合进行了初步的尝试。不过在接下来实践中发现,开发的过程还是有很多不方便的地方。比如:
- 注册到lua环境中的C++函数入参必须是 lua_State *L
- 入参及返回类型,需要压栈及出栈
- lua类型与c++类型不同,需要进行转化
以上问题让开发者增加了很多的额外的工作量,为了简化整合的步骤。当前有很多组件来解决以上痛点,给开发人员增效。
常用的组件
- LuaPlus
- luabind
- tolua++
- luaBridge
方案
精力的原因,我没有详细对比这些组件,直接选择了luaBridge作为解决方案 。理由是LuaBridge其中的一个特性是集成到项目非常的便捷,只要把头文件加入到项目中就可以了。
luaBridge的特性
- 软件准守与MIT许可(该许可赋予被许可人非常的宽松权利)
- 可打印的开发手册(同样适用MIT许可)
- 整合只需要引入头文件,而不需要额外的Makefile,.cpp等文件。仅仅#include头文件(我看到这个点,就动心了。决定试用一下)
- 简单、轻量级、无其他依赖
- 也不需要宏定义、配置、或者使用其他的配置脚本等
- 支持对不同对象的生命周期进行管理
- 方便、安全的访问Lua栈
- 可以自动化绑定函数的入参
- 简化访问lua特定的对象
- debug简便、快捷
注意:这个我采用luaBridge版本是2.6对应整合的lua版本是5.1。我最开始用的是lua5.4发现不能很好的和luaBridge2.6进行整合,后来换成了lua5.1之后整合成功。而想要整合lua5.4版本也要把luaBridge升级到3.0。这个也是我遇到的一个困扰我挺长时间的一个大坑。
luaBridge资源
- luaBridge2.6 (https://github.com/vinniefalco/LuaBridge)
- luaBridge 2.6 的手册 (http://vinniefalco.github.io/LuaBridge/Manual.html#s2.6)
- luaBridge 3.0 (https://github.com/kunitoki/LuaBridge3)
- luaBridge 3.0 的手册 (https://kunitoki.github.io/LuaBridge3/Manual)
实践
这块原本想引用一起我项目中的代码,最后觉得还是手册里的样例更方便理解。
初始化lua引擎
这部分其实luaBridge还没有登场,是对lua引擎完成初始化工作
lua_State* L=luaL_newstate();
luaL_openlibs(L);
有了以上两句就创建了一个lua的引擎。到这里就完成了准备的工作
将C++函数、类、变量注册到lua引擎中
如果将C++函数注册到lua引擎中,首先要指定注册到哪个namespace(命名空间),这点和c++类似。Lua本身有一个全局的命名空间,还可以自定义多个子命名空间。而子命名空间都要隶属于全局的命名空间中。
注册的样例如下:
- 注册一个函数test
- 注册一个类A
- 注册A类的子类B
- 注册一个变量var1,var2到不同的命名空间
//先要获取全局命名空间
class A
{
public:
A( )
{
}
virtual void foo( int a )
{
printf("foo base
");
}
std::string Member;
};
class B : public A
{
public:
virtual void foo( int a )
{
printf("foo inherited
");
}
};
void foo( int b )
{
}
//注册普通函数
int test(int arg)
{
return 1;
}
//注册普通变量
int var1;
int var2;
luabridge::Namespace luans=luabridge::getGlobalNamespace(L);
luans.addFunction("test",test); //<-- 1. 注册了一个函数,这里入参和返回值直接被luabridge绑定
luans.beginClass<A>("Sobj")
.addConstructor<void (*) (void)> ()
.addFunction("foo", &A::foo)
.addData("Member",&A::Member)
.endClass() //<-- 2. 注册了一个类,并声明了对象的构造函数和其他成员函数,可以在lua中直接创建这个对象,并使用注册的成员函数及成员变量
.deriveClass<B, A>("SSec")
.addFunction("foo",&B::foo )
.endClass(); //<-- 3. 注册了A的子类
luans.addProperty ("var_global", &var1);//<-- 4.1 把var1注册到全局命名空间
luans.beginNamespace ("test") //
.addProperty ("var_test", &var2) //<-- 4.2 把var1注册到test的命名空间中
.endNamespace ();
- lua侧的代码
--case1
print(test(1))
--case2
local a = Sobj()
a:foo(2)
a.Member = "hello"
--case3
local b= SSec()
b::foo(2)
--case4.1
var_global=5;
--case4.2
test.var_test=6;
c++代码触发lua脚本的执行
我们大多数项目中是通过C++来调用lua脚本。按照之前的步骤我们初始化了Lua的引擎,并将C++相关函数、类、变量注册到了lua引擎中之后。我们可以调用lua脚本的执行,而方法非常简单只是一句就可以了
luaL_dostring(L,script);
cmake操作
由于我刚学习c++及cmake,这里在最后额外说一下相关的配置。
执行代码需要依赖Lua的动态链接库,使用cmake在CMakeLists.txt中加入
target_link_libraries(robot_control_demo PRIVATE "/usr/local/Cellar/lua@5.1/5.1.5_8/lib/liblua.5.1.5.dylib")
这里的路径是mac系统典型的动态链接库的路径,其他系统换成对应的路径即可。
最后执行编译工程
#工程目录下
mkdir build
#进入编译目录
cd build
cmake ..
cmake --build .