C++和Lua交互教程(基于LuaBridge)
来源:互联网 发布:n个骰子的点数 java 编辑:程序博客网 时间:2024/05/29 17:15
作者:查志旺 ,向日葵远程控制软件前端开发工程师。
最近公司需要做向日葵远程控制软件跨平台项目,为了代码的可复用性,需嵌入跨平台脚本语言,我们选择了Lua,理由是Lua由标准C编写而成,几乎在所有操作系统和平台上都可以编译,Lua脚本可以很容易的被C/C++ 代码调用,也可以反过来调用C/C++的函数,今天就跟大家分享下c++与Lua交互的一些问题。
为了方便c++和lua的交互,我引进了LuaBridge。因为它源码简单易用,只有头文件,没有.cpp文件,不需要编译,只需要引用头文件即可,方便快捷。
下面我用vs2008工具写了一个win32控制台项目例子来讲解下c++和Lua简单的互调。
一、准备工作
从lua官网上下载最新的http://www.lua.org/download.html,下个最新的lua-5.3.4.tar.gz解压出来后可以直接拿源码编译成lib或者直接加入到项目中.我是直接把它放到项目中,编译的时候,会出现多个main函数入口的错误(移除对应的文件)。
下载LuaBridge,下载地址:https://github.com/vinniefalco/LuaBridge列表下的Source/LuaBridge是源码,Manual.html是帮助文档。
创建win32控制台项目test_lua把lua源码和LuaBridge头文件放入到项目目录下并添加到项目工程中。
二、编写代码
1、先引用Lua和LuaBridge头文件如下:
//引用c文件的头文件所以需要加上extern "C"extern "C"{ #include "lua.h" #include "lauxlib.h" #include "lualib.h"}#include "LuaBridge\LuaBridge.h"
2、然后创建test_lua类和一个子类代码如下:
class test_lua{public: test_lua() { m_test_string = "c++ test string"; } ~test_lua() { }//test方法 void test(int a,int b) { printf("c++ test function %d+%d=%d\n", a, b, a+b); }//属性set方法 void SetName(std::string name) { m_name = name; }//属性get方法,注意需要后面加const std::string GetName() const { return m_name; }//供lua调用方法,返回多个参数方法 int cFunc(lua_State* L) {//返回参数1 lua_pushstring(L,"str1");//返回参数1 lua_pushstring(L,"str2");//返回参数个数 return 2; } std::string m_test_string; std::string m_name; static int m_static_data;};//test_lua静态变量定义(静态变量在类内只是声明)int test_lua::m_static_data;//test_lua子类class test_lua_child :public test_lua{ public: test_lua_child(std::string test) :m_test_child_string(test) { printf("call test_lua_child constructor\n"); } ~test_lua_child() { } std::string m_test_child_string;};
3、创建一个lua脚本文件a.lua内容为:
--lua 打印lua scriptprint("lua script") --调用成员变量m_test_string(test_str为注册的名字)print(test_lua.test_str)--调用c++静态变量(需要加上test命名空间)test.test_lua.static_data=12print("static_data: "..test.test_lua.static_data)--调用c++类test_lua属性nametest_lua.name="name_property";print("name: "..test_lua.name);--lua调用c++方法test_lua为c++类在lua的注册名,调用test方法test_lua:test(3,4)--调用c++调用方法返回多个值local ret1,ret2 = test_lua:cFunc()print("ret1="..ret1.." ret2="..ret2)--创建test_lua_child对象local test_lua_child = test.test_lua_child("test_string")--调用其变量print("child string:"..test_lua_child.test_child_string);--调用父类的name属性test_lua_child.name="child_name_property";print("name:"..test_lua_child.name);--lua 方法加法function lua_add_function(a,b) print("lua_add_function") return a+b;end--lua 方法字符串加法(..是相加语法)function lua_add_str_function(a,b) print("lua_add_str_function") return a..b;end
4、主函数编写
4.1、Lua的初始化和加载Lua的基本库
//初始化Lua (最后记得调用lua_close(lua_state)释放) lua_State* lua_state = luaL_newstate(); //加载Lua基本库 luaL_openlibs(lua_state);
4.2、用luabridge注册到lua中
在这里要注意的是多个类注册需要加一个namespace(test),且.endClass()后面不加分号
luabridge::getGlobalNamespace(lua_state) .beginNamespace("test") .beginClass<test_lua>("test_lua") .addConstructor<void (*) (void)> ()//无参构造函数的注册 .addData("test_str",&test_lua::m_test_string)//注册变量到lua .addStaticData("static_data", &test_lua::m_static_data)//注册静态变量到lua .addFunction("test", &test_lua::test)//注册test、方法到lua(addStaticFunction静态函数注册也类似) .addProperty("name",&test_lua::GetName,&test_lua::SetName)//属性方法的注册(addStaticProperty静态属性方法也类似) .addCFunction("cFunc",&test_lua::cFunc)//注册返回多个参数给lua的方法 .endClass() .deriveClass<test_lua_child, test_lua> ("test_lua_child")//子类的注册 .addConstructor<void (*) (std::string)> ()//有参构造函数的注册 .addData("test_child_string", &test_lua_child::m_test_child_string)//注册变量到lua .endClass() .endNamespace();//创建test_lua对象test_lua test; luabridge::setGlobal(lua_state, &test, "test_lua");//注册test_lua对象到lua
注:test_lua也可以在lua创建,因为构造函数也注册到lua,如test.test_lua(),上面的a.lua脚本有子类test_lua_child的创建和调用其父类的属性方法
4.3、注册完成后,再返回看上面写的a.lua脚本就知道每个调用的意义,添加运行Lua脚本 代码然后执行,代码如下:
//运行lua脚本 luaL_dofile(lua_state, "a.lua");//关闭Lualua_close(lua_state);编译执行结果为:lua scriptc++ test stringstatic_data: 12name: name_propertyc++ test function 3+4=7ret1=str1 ret2=str2call test_lua_child constructorchild string:test_stringname:child_name_property
4.4、c++调用lua方法,因为lua方法函数参数一样而且都是一个返回值,为了方便,采用模板形式(以两个参数为例)第一个参数(lua对象)和第二个参数(方法名)类型固定,后面参数用模板
template<typename R, typename T1, typename T2>
R call(lua_State* lua_state,const char* name, T1 arg1, T2 arg2){//读取方法名 lua_getglobal(lua_state, name);//判断是不是方法 if (lua_isfunction(lua_state, -1)) {//压入参数 luabridge::Stack<T1>::push(lua_state, arg1); luabridge::Stack<T2>::push(lua_state, arg2);//执行函数(参数为lua对象、参数个数,返回值个数,出错返回) lua_pcall(lua_state, 2, 1, 0); }//获取返回值 return luabridge::Stack<R>::get(lua_state, -1);}在运行lua脚本后面再加上如下调用代码://调用lua方法lua_add_function int ret = call<int>(lua_state,"lua_add_function", 5, 6); //调用lua方法lua_add_str_function std::string value = call<const char*>(lua_state,"lua_add_str_function","5", "6"); printf("lua_add_function result:%d\n", ret); printf("lua_add_str_function result:%s\n", value.c_str());
编译执行结果为:
lua scriptc++ test stringstatic_data: 12name: name_propertyc++ test function 3+4=7ret1=str1 ret2=str2call test_lua_child constructorchild string:test_stringname:child_name_propertylua_add_functionlua_add_str_functionlua_add_function result:11lua_add_str_function result:56
4.5、最后讲一下luaL_dostring
luaL_dostring跟luaL_dofile是一个作用,都是加载并运行lua脚本,只是对象不一样,看方法名就知道是一个是加载文件,另外一个是加载string,最后运行里面的lua脚本,luaL_dostring在lua嵌入到其他的脚本语言中经常用到,现在沿用上面的例子在lua_close之前加段代码简单说明下:
//定义lua脚本,调用test_lua类里的属性name并打印出来std::string lua_string = "print(\"run lua string test_lua name:\"..test_lua.name)";//加载string并运行luaL_dostring(lua_state, lua_string.c_str());编译运行得到的结果为:run lua string test_lua name:name_property
说到嵌入问题,现在做的向日葵远程控制软件的界面用的是xml,这里就涉及到lua嵌入到xml中,由于lua特性,在其他的系统这些xml都可以用,所以以后再也不用担心加个新界面每个系统还得重新再搞一套,lua嵌入xml中原理就是把lua脚本加入到一个节点中如:
<script><![CDATA[--lua代码print("run lua script") ]]></script>
解析xml对应的script节点内容,然后用luaL_dostring去加载运行就可以了。
最后配上现在的向日葵界面图
向日葵客户端:
向日葵控制端:
三、小结
希望通过上面的简单例子可以帮助大家快速上手c++嵌入lua脚本,从上面代码也可以看出lua和c++很容易互相调用,lua与c++是通过操作虚拟栈来交互的,例如上面调用lua方法,就是c++先把方法放入到栈顶,然后lua从栈顶取值操作,然后把结果又放回到栈顶,c++再从栈顶取值,lua调用c++也类似,就是c++把需要调用的先注册到lua中,lua就可以调用,想了解更多lua基本语法和原理的可以具体查看lua中的manual.html,LuaBridge的其它用法也可查看LuaBridge的manual.html。
- C++和Lua交互教程(基于LuaBridge)
- C++和Lua交互教程(基于LuaBridge)
- C++和Lua交互教程(基于LuaBridge)
- C++ 程序嵌 Lua(基于 LuaBridge)
- Lua和C交互的简易教程
- LuaBridge:Lua绑定C/C++对象
- C和lua交互
- lua和C交互
- 用LuaBridge为Lua绑定C/C++对象
- 用LuaBridge为Lua绑定C/C++对象
- 用LuaBridge为Lua绑定C/C++对象
- 用LuaBridge为Lua绑定C/C++对象
- lua和c的交互
- lua和C交互框架
- lua和c的交互
- lua和c的交互
- lua和c的交互
- lua和C交互框架
- 读取数据:DataReader对象(三)
- js css+html实现简单的日历
- iOS真机测试Profile文件更新不生效问题
- vue中 --- 变化检测问题(数组相关)
- 刷题记录
- C++和Lua交互教程(基于LuaBridge)
- 委托Delegation 笔记----C++学习之路
- deepin安装后无win10启动项
- android 的过场页面的实现
- druid连接池监控
- 为js生成的class添加点击事件
- 动态分布内存——free()函数与realloc()函数
- 文章标题
- 『机器学习实战』使用 k-近邻算法识别手写数字