C++和Lua交互教程(基于LuaBridge)

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

最近公司需要做向日葵远程控制软件跨平台项目,为了代码的可复用性,需嵌入跨平台脚本语言,我们选择了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。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/525786.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

与Serverless 的第一次亲密接触

Servrless概念 Serverless 是一个架构上的概念&#xff0c;从字面上理解就是无服务器架构。Serverless最初是用于描述依赖第三方服务实现对逻辑和状态进行管理的应用&#xff0c;典型的例子是单页 Web 和移动 App 这种富客户端应用&#xff0c;他们一般都使用基于云端的数据库…

eclipse把tomcant用到一个项目里_聊一个镜头工艺里容易被忽略,但很重要的项目...

在不改换门庭的情况下&#xff0c;一颗镜头一般都会伴随大家使用很长一段时间&#xff0c;也相信大多数人都遇到过剐蹭镜头前组的情况&#xff0c;这时候最容易引发的担忧就是“伤着镀膜了么&#xff1f;会不会影响成像效果&#xff1f;”其实换个角度来看&#xff0c;这个问题…

华为手机怎么看图片属性_华为手机怎么才能息屏显示时间?操作方法很简单,看完涨知识了...

现如今大家几乎都是手机不离身&#xff0c;甚至有些朋友机不离手。所以已经比较少人&#xff0c;会因为看时间而佩戴手表了&#xff0c;毕竟只要按下电源键就可以看时间了。其实现在的很多手机&#xff0c;不用亮屏也能看时间&#xff0c;下面我们就一起来看看是如何设置的吧。…

开源神器,无需一行代码就能搞定机器学习,不会数学也能上手

对于机器学习和数据科学的初学者来说&#xff0c;最大的挑战之一是需要同时学习太多知识&#xff0c;特别是如果你不知道如何编码。你需要快速地适应线性代数、统计以及其他数学概念&#xff0c;并学习如何编码它们&#xff0c;对于新用户来说&#xff0c;这可能会有点难以承受…

OpenStack不行了吗?悉尼峰会,OpenStack的白城反击战?

作者&#xff1a;李开&#xff0c;九州云99Cloud联合创始人&副总裁。 11月悉尼的春天忽然变得阴冷潮湿&#xff0c;和第一天抵达时候的风和日丽大相径庭&#xff0c;海风推动着飘忽的乌云&#xff0c;有点电影《魔戒》里黑暗军团压境的味道。 由于早上不小心睡过了头&…

档案盒正面标签制作_错题本科学制作方法、正确使用方式及窍门

错题本的作用不用多说&#xff0c;重点是如何制作错题本以及正确使用方法。小编整理了以下制作错图集的正确步骤&#xff0c;希望对大家有用。一、制作错题本的简单步骤步骤1&#xff1a;把所有的练习册和试卷找出来;分学科按学期顺序整理;以学年或学期为单位装订在一起&#x…

android触摸效果,Android UI实现单行文本水平触摸滑动效果

本文实例为大家分享了单行文本水平触摸滑动效果&#xff0c;通过EditText实现TextView单行长文本水平滑动效果。下一篇再为大家介绍 多行文本折叠展开效果&#xff0c;自定义布局View实现多行文本折叠和展开。1.初衷最近做应用的时候有用到TextView单行长文本&#xff0c;当文本…

机器人爱因斯坦、索菲亚对话人类:“人类必须自我修复”

人工智能的发展速度真的太快了&#xff0c;就在不久前机器人“索菲亚”获得沙特阿拉伯获得了公民身份之后&#xff0c;机器人版的爱因斯坦教授也登上了历史的舞台。 机器人版的爱因斯坦教授是由汉森机器人(Hanson Robotics)公司制造的&#xff0c;这家机器人制造商制造的另外一…

hook控制浏览器的方法_Java-Hook技术-入门实践+反射、动态代理、热修复再看看

延续之前的MonkeyLei&#xff1a;Android-模块化、组件化、插件化、热修复-插件化-起个头&#xff0c;我们复习下里面的关于反射和动态代理点的知识。然后尝试简单了解下Hook...看之前文章&#xff0c;记得多复习下反射代理&#xff0c;比如使用这些....&#xff1a;public cla…

vscode 头文件包含问题_使用clangd替代c/c++配置vscode c++项目

背景&#xff1a;最近从Clion切换到了vscode来进行代码开发&#xff0c;发现vscode自带的c/c插件除了能够使用debug功能&#xff0c;其余代码补全&#xff0c;跳转等功能都和基于clangd的clion有较大差距&#xff0c;经常出现匹配不上或者跳转不准确的问题&#xff0c;在这背景…

微服务拆分需要考虑的必要因素与坚持原则

前言&#xff1a;创业公司往往因为有限的时间和投入&#xff0c;把系统所有的功能都聚集在一起。随着业务的不断发展&#xff0c;技术人员开始不断地对架构进行解耦和拆分。微服务在最近几年大行其道&#xff0c;很多公司的研发人员都在考虑微服务架构&#xff0c;或者在做微服…

data后缀文件解码_Java语法进阶13-文件、IO流

FileFile是文件和目录路径名的抽象表示形式&#xff0c;即File类是文件或目录的路径&#xff0c;而不是文件本身&#xff0c;因此File类不能直接访问文件内容本身&#xff0c;如果需要访问文件内容本身&#xff0c;则需要使用输入/输出流。File类的对象用来表示文件和文件夹的对…

中小型互联网公司微服务实践-经验和教训

上次写了一篇文章叫Spring Cloud在国内中小型公司能用起来吗?介绍了Spring Cloud是否能在中小公司使用起来&#xff0c;这篇文章是它的姊妹篇。其实我们在这条路上已经走了一年多&#xff0c;从16年初到现在。在使用Spring Cloud之前我们对微服务实践是没有太多的体会和经验的…

对于机器学习,到底该选择哪种编程语言

开发者到底应该学习哪种编程语言才能获得机器学习或数据科学这类工作呢&#xff1f;这是一个非常重要的问题。我们在许多论坛上都有讨论过。现在&#xff0c;我可以提供我自己的答案并解释原因&#xff0c;但我们先看一些数据。毕竟&#xff0c;这是机器学习者和数据科学家应该…

android如何适配平板,适用于平板电脑、大屏设备和可折叠设备的自适应布局

将应用支持扩展到更大屏幕的设备(例如平板电脑、可折叠设备和 Chrome 操作系统设备)是扩大您的覆盖面和互动度的绝佳方式。平板电脑的增长率比去年同期 (YOY) 大幅增长了 30%&#xff0c;Chrome 操作系统设备比去年同期增长了 92%。我们还发现&#xff0c;用户使用平板电脑的时…

对于机器学习,到底该选择哪种编程语言?

开发者到底应该学习哪种编程语言才能获得机器学习或数据科学这类工作呢&#xff1f;这是一个非常重要的问题。我们在许多论坛上都讨论过这个问题。今天&#xff0c;我将给出我自己的答案并解释其中原因&#xff0c;但我们首先看一些数据。毕竟&#xff0c;这是机器学习者和数据…

置顶图片代码加链接html,css图片怎么加链接?

css可以通过在图片前加标签实现为图片加链接&#xff0c;语法&#xff1a;。使用此方法引入图片即可为图片添加链接。设定图片超链接&#xff0c;用css样式来实现&#xff0c;其实就是给这个图片的块添加超链接&#xff0c;我们可以通过在这个块的前面添加一个a标签来实现&…

linux上修改html,linux进程名修改

如何修改 Linux 中的进程名在编写网络服务器程序时&#xff0c;为了响应客户端的请CSS布局HTML小编今天和大家分享&#xff0c;我们经常需要新建进程来处理业务流程&#xff1b;而且又是为了关闭某个非法请CSS布局HTML小编今天和大家分享或者关闭长连接的客户端&#xff0c;这时…

AI 崛起,科学家的天下,程序员的谢幕

一边&#xff0c;在移动互联网时代掉队的微软迅速组织起来了一个万人的 AI 团队。 另一边&#xff0c;Facebook、Google 在对各个技术公司进行买买买&#xff0c;为了在 AI 时代的探索中铺好路。 多年前「深蓝」下国际象棋时&#xff0c;所有人都觉得人工智能还是遥不可及的学…

lammps计算聚合物例子_LAMMPS模拟聚合物结构,非晶态聚合物变形行为的模拟,纳米线变形模拟,单轴张力模拟,晶格参数计算...

推荐一个网站&#xff0c;上面有LAMMPS模拟聚合物结构,非晶态聚合物变形行为的模拟,纳米线变形模拟,单轴张力模拟,晶格参数计算的lammps脚本&#xff0c;如下面是晶格参数计算的lammps脚本&#xff0c;具体网址是&#xff1a;LAMMPS Inputs Archives - LAMMPS Tube​lammpstube…