作者:查志旺 ,向日葵远程控制软件前端开发工程师。

最近公司需要做向日葵远程控制软件跨平台项目,为了代码的可复用性,需嵌入跨平台脚本语言,我们选择了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方法,注意需要后面加conststd::string GetName() const{return m_name;}
//供lua调用方法,返回多个参数方法int cFunc(lua_State* L){
//返回参数1lua_pushstring(L,"str1");
//返回参数1lua_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 script
print("lua script")
--调用成员变量m_test_string(test_str为注册的名字)
print(test_lua.test_str)
--调用c++静态变量(需要加上test命名空间)
test.test_lua.static_data=12
print("static_data: "..test.test_lua.static_data)
--调用c++类test_lua属性name
test_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");
//关闭Lua
lua_close(lua_state);
编译执行结果为:
lua script
c++ test string
static_data: 12
name: name_property
c++ test function 3+4=7
ret1=str1 ret2=str2
call test_lua_child constructor
child string:test_string
name: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_functionint 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 script
c++ test string
static_data: 12
name: name_property
c++ test function 3+4=7
ret1=str1 ret2=str2
call test_lua_child constructor
child string:test_string
name:child_name_property
lua_add_function
lua_add_str_function
lua_add_function result:11
lua_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)相关推荐

  1. C++与lua的结合,LuaBridge的使用及遇到的坑

    LuaBridge介绍 LuaBridge 是一个简单好用的轻量级且无依赖的库,用于在C ++和 LUA(一种强大,快速,轻量级,可嵌入的脚本语言)之间来回映射数据,函数和类. github地址: h ...

  2. Cocos 2d-X Lua 游戏添加苹果内购(二) OC和Lua交互代码详解

    这是第二篇 Cocos 2d-X Lua 游戏添加苹果内购(一) 图文详解准备流程 这是前面的第一篇,详细的说明了怎样添加内购项目以及填写银行信息提交以及沙盒测试员的添加使用以及需要我们注意的东西,结 ...

  3. lua加密教程_我们相信加密! 教程

    lua加密教程 许多人认为加密是一个复杂的主题,这很难理解. 可以实现其某些方面,但是每个人都可以理解它在更高层次上的工作方式. 这就是我要处理的这篇文章. 用简单的术语解释它是如何工作的,然后使用一 ...

  4. cef在android中使用_嵌入Chrome cef之完整版(本教程基于cef1)

    本教程基于cef1 至于cef1与cef3的区别请参考 http://blog.csdn.NET/tts2game/article/details/17260865 总结之前先将我的参考资料整理如下 ...

  5. C++与Lua交互(四)

    通过前几篇,我们已经对Lua的C API有了一定的了解,如lua_push*.lua_is*.lua_to*等等.用C++调用Lua数据时,我们主要运用lua_getglobal与lua_push*配 ...

  6. Visual Studio图形调试器详细使用教程(基于DirectX11)

    前言 对于DirectX程序开发者来说,学会使用Visual Studio Graphics Debugger(图形调试器)可以帮助你全面了解渲染管线绑定的资源和运行状态,从而确认问题所在.现在就以我 ...

  7. linux 轻量化图形界面,YOXIOS 入门教程--基于Linux的 轻量化GUI图形系统和硬件平台(41页)-原创力文档...

    YOXIOS --基于 Linux 的轻量化 GUI图形系统和硬件平台 YOXIOS 入门教程 基于 Linux 的 轻量化 GUI图形系统和硬件平台 (V1.0 2020-05) 提示:阅读此文档需 ...

  8. Python人脸识别教程 - 基于Python的开源人脸识别库:离线识别率高达99.38%

    Python人脸识别教程 - 基于Python的开源人脸识别库:离线识别率高达99.38% 仅用 Python 和命令行就可以实现人脸识别的库开源了.该库使用 dlib 顶尖的深度学习人脸识别技术构建 ...

  9. H2O Wave教程---基于浏览器的实时显示工具---教程01

    H2O Wave教程-基于浏览器的实时显示工具-教程01 0 写在前面 1 开始-动手操作起来 2 分类:一个是脚本,一个是app 3 脚本怎么写 0 写在前面 总结一下自己学习H2Owave的学习情 ...

最新文章

  1. Nagios监控笔记上
  2. 机器学习中的有监督学习,无监督学习,半监督学习
  3. 工作三年,我没给家里一分钱,还把家里掏空了
  4. 个人作业-Week2:案例分析
  5. 给单片机焼写程序需要什么东西_单片机怎么烧写程序
  6. VS一直停留在“正在还原nuget程序包”
  7. 数字滤波器的差分方程和传递函数
  8. 作者:赵晨(1980-),男,深圳般若计算机系统股份有限公司气象大数据应用负责人...
  9. 今天的一下子跳出来了的超级玛丽
  10. cmake中添加引用动态链接_C和C++使用对方编译的动态链接库
  11. 移动硬盘驱动器读到,但不显示盘符
  12. Android微信支付遇到的大坑
  13. MySql索引优化及Explain工具使用
  14. MindSpore:环境问题案例
  15. 婚宴座位图html5,酒席座位安排图-婚宴座位安排图【蜜匠婚礼】
  16. Mac程序坞显示App预览的多窗口小窗(像Windows一样)——HyperDock
  17. IOS开发辅助小工具
  18. latex图片并排放置_乳胶Latex墨水新发展 打印白色和打印刚性材料
  19. speedtest测速
  20. JavaSE写仿QQ聊天室

热门文章

  1. mybatis 二级缓存失效_二级缓存updateBatchById失效
  2. ppt生成器_9款魔性#傻瓜生成器#,上班可以划水一天
  3. 拿破仑时代的炮兵究竟数学要多好?
  4. 如何理解傅立叶级数、傅立叶变换公式?
  5. 优秀!复旦直博生一作发15篇SCI,并担任12本SCI期刊审稿人
  6. BAT大神推荐:看懂英文文档,每天只需要10分钟做这件事……
  7. 细思极恐丨几个有趣的科学实验
  8. 台式计算机装系统,教你台式电脑重装系统方法
  9. Ubuntu14升级MySQL
  10. 【java机器学习】决策树算法