为了账号安全,请及时绑定邮箱和手机立即绑定

lua——C/C++lua嵌入式开发

标签:
C C++

首先介绍一下Lua语言,Lua 是一个小巧的脚本语言,该语言的设计目的是为了嵌入应用程序中,从而为应用程序提供灵活的扩展和定制功能。


一:lua定位——C/C++嵌入式脚本语言

lua本身就是C写的,所以Lua脚本可以很容易的被C/C++代码调用,也可以反过来调用C/C++的函数
lua语法、解释器、执行原理都与python相似
唯一差距就是lua没有强大的类库作为支撑,Lua只是具备了一些比如数学运算和字符串处理等简单的基本功能。
所以lua不适合作为开发独立应用程序的语言。


二:数据类型——table

lua本质上只有一种数据类型,就是table,实际上就是hash表。它用这个来模拟数组,链表等等。 

比如说,lua代码:
data = {} --定义一个table
data.i = 1
data.name = "jason"
data.package = {1,2,2,3,56,7}
data.others = {}
data.others.a = 1
data.others.b = 1.1

等于C的结构体struct


三:协程

Lua所支持的协程全称被称作协同式多线程(collaborative multithreading)。Lua为每个coroutine提供一个独立的运行线路。然而和多线程不同的地方就是,coroutine只有在显式调用yield函数后才被挂起,同一时间内只有一个协程正在运行。他的主要优势就是线程执行序列显示可控。然而归根结底协程还是串行的。


下面重点说一下Lua与C/C++之间的交互


首先,Lua和C程序通过struct lua_State堆栈交换数据,这点和python类似,python是通过python对象进行交互。(*Lua 调用C函数用的堆栈是临时的,调用结束之后就被销毁了。)堆栈索引的方式可是是正数也可以是负数,正数索引1永远表示栈底,负数索引-1永远表示栈顶


【一】基本配置

首先导入相关的头文件及库(安装目录下的include和lib),这个就不多说了。顺便一提,我的lua版本为5.1。


然后就是创建交换数据用的 lua_State 堆栈了,


lua_State *L = lua_open();        //创建lua_State *L堆栈



然后用luaL_openlibs进行初始化


luaL_openlibs(L);                  //初始化

这个命名蛮尴尬的,官方的说明大概的意思就是把所有标准库加载到这个指定的L里把= =

顺带一提,加载某个标准库的话就是luaL_openlib了- -然而这根本就是两个不同的事嘛!


与其相应的 结尾肯定要释放咯~


lua_close(L);                   //释放


以上就是调试前基本的配置了。



【二】进行交互

C++与Lua交互的方式主要为luaL_dostring(通过直接传入字符串(Lua代码)的方式)以及luaL_dofile(加载指定Lua脚本文件)

例如:


const char *buf = "print('hello, lua!')";	//可以用dostring这种方式直接执行lua代码	luaL_dostring(L, buf);	//也可以用dofile这种方式打开lua文件	luaL_dofile(L, "...\Test.Lua);




【三】获取Lua参数

上文中说过,C++与Lua交互主要通过堆栈进行,对于C++这边,传参就是lua_push,获取就是lua_to。后面对应的数据类型

如将int入栈,即lua_pushnumber,如获取string参数,即lua_tostring,他们参数都是两个,堆栈L及一个索引参数,比如说-1为栈顶,-2就是第二个~



例:

Lua代码


--用于测试C++获取lua参数var1=111var2=222var3=333





C++代码


//获取lua参数	lua_pushstring(L, "var1"); //将变量的名字放入栈  lua_gettable(L, LUA_GLOBALSINDEX);//通过栈顶的索引定位到参数值并放至栈顶	lua_pushstring(L, "var2"); //将变量的名字放入栈 	lua_gettable(L, LUA_GLOBALSINDEX);//通过栈顶的索引定位到参数值并放至栈顶	int var1 = lua_tonumber(L, -1);	int var2 = lua_tonumber(L, -2);	cout << "var1:" << var1 << endl << "var2:" << var2 << endl;	lua_getglobal(L, "var3");        //lua定义的宏,直接找var3	int var3 = lua_tonumber(L, -1);	cout << "var3:" << var3 << endl;


输出为:


var1:222
var2:111
var3:333


我们先看上文中提到的数据类型,Lua的数据类型本质是Table,也就是说,我们首先将Key为var1的变量名放入栈顶,再通过gettable来获取对应的Value

再将Value放到栈顶。然后C++通过lua_to函数获取这个参数值。


我们先将var1放入栈顶,又将var2放入栈顶,这样当我们取-1的时候 实际去的是var2,-2是之前的var1,所以我们看到 var1和var2的值进行了互换。

同时,Lua也提供了一个lua_getglobal函数直接取这个变量,也适用于取函数(后文会提到)。


【四】调用lua方法

lua代码


--用于测试lua多返回function f1(a,b,c)local sum=a+breturn sum,cend




C++代码


//调用lua方法	lua_getglobal(L, "f1");    //在Lua中,函数等同于变量,所以你可以这样来取得这个函数	lua_pushnumber(L, 100);    //将参数1压栈	lua_pushnumber(L, 20);     //将参数2压栈	lua_pushstring(L, "function f1");  //将参数3压栈	//LUA_API int   (lua_pcall)(lua_State *L, int nargs, int nresults, int errfunc);	lua_pcall(L, 3, 2, 0); //调用函数,3个参数,2个返回值	string result1 = lua_tostring(L, -1);	int result2    = lua_tonumber(L, -2);	cout << "result1:" << result1 << endl;	cout << "result2:" << result2 << endl;




看,lua_getglobal同时适用于调取某个全局函数,然后通过lua_push来将参数压栈。

lua_pcall执行函数,其参数为堆栈,参数个数,返回值个数,错误处理(一般不需要Lua进行处理,所以是0)


再通过lua_to获取返回值,这里我们看到,Lua代码中返回的是sum(int),c(string)

所以,栈顶是c(string),第二个是sum(int)。



输出为:

result1:function f1
result2:120


【四】lua调用C++函数


lua代码


--用于测试lua调用C++函数function f2(a,b,c)local len=lua_Strlen(c)local sum=a+b+lenreturn sumend



C++代码


调用函数


int lua_Strlen(lua_State *L){	//首先取出脚本执行这个函数时压入栈的参数  	//假设这个函数提供一个参数,有一个返回值  	//get the first parameter  	const char *par = lua_tostring(L, -1);	cout << "at lua_Strlen,the str is :"<<par << endl;	//push the first result  	lua_pushnumber(L, strlen(par));	return 1;}



主函数



//lua调用C++函数	lua_register(L, "lua_Strlen", lua_Strlen); //注册函数	lua_getglobal(L, "f2");	lua_pushnumber(L, 100);	lua_pushstring(L, "20");	lua_pushstring(L, "abc");lua_pcall(L, 3, 1, 0); //调用函数,3个参数,1个返回值  	int result3 = lua_tonumber(L, -1);	cout <<"result3:" << result3 << endl;




我们先看调用函数,一样是通过to获取参数,通过push将函数打入栈中给Lua传递,

调用函数的参数为堆栈L,返回值为返回参数的个数。这里我们做一个简单的strlen函数


再来看主函数这边,首先需要将调用函数strlen进行注册,然后是调用Lua里的f2函数,和上面的类似。

大家看,我的第二个参数传入的是string类型:lua_pushstring(L, "20"); 说明Lua是可以进行隐性强转的。


即我们在C++调用Lua前先将调用函数入栈,之后调用Lua脚本的f2函数,当f2函数执行到local len=lua_Strlen(c)时会在栈中寻找相应的方法

于是调用了C++我们写好的调用函数lua_Strlen,参数之间都是通过栈顶进行交互传递的


输出为:

at lua_Strlen,the str is :abc
result3:123




完整测试代码:


Test.Lua


--用于测试C++获取lua参数var1=111var2=222var3=333--用于测试lua多返回function f1(a,b,c)local sum=a+breturn sum,cend--用于测试lua调用C++函数function f2(a,b,c)local len=lua_Strlen(c)local sum=a+b+lenreturn sumend




Lua_test.cpp


// lua_test.cpp : 定义控制台应用程序的入口点。//#include "stdafx.h"extern "C"{#include "lua.h"  #include "lauxlib.h"  #include "lualib.h"  }int lua_Strlen(lua_State *L){	//首先取出脚本执行这个函数时压入栈的参数  	//假设这个函数提供一个参数,有一个返回值  	//get the first parameter  	const char *par = lua_tostring(L, -1);	cout << "at lua_Strlen,the str is :"<<par << endl;	//push the first result  	lua_pushnumber(L, strlen(par));	return 1;}int main(){	lua_State *L = lua_open();        //创建lua_State *L堆栈	luaL_openlibs(L);                  //初始化	const char *buf = "print('hello, lua!')";	//可以用dostring这种方式直接执行lua代码	luaL_dostring(L, buf);	//也可以用dofile这种方式打开lua文件	luaL_dofile(L, "..\\Test.txt");	//获取lua参数	lua_pushstring(L, "var1"); //将变量的名字放入栈  lua_gettable(L, LUA_GLOBALSINDEX);//通过栈顶的索引定位到参数值并放至栈顶	lua_pushstring(L, "var2"); //将变量的名字放入栈 	lua_gettable(L, LUA_GLOBALSINDEX);//通过栈顶的索引定位到参数值并放至栈顶	int var1 = lua_tonumber(L, -1);	int var2 = lua_tonumber(L, -2);	cout << "var1:" << var1 << endl << "var2:" << var2 << endl;	lua_getglobal(L, "var3");        //lua定义的宏,直接找var3	int var3 = lua_tonumber(L, -1);	cout << "var3:" << var3 << endl;	//调用lua方法	lua_getglobal(L, "f1");    //在Lua中,函数等同于变量,所以你可以这样来取得这个函数	lua_pushnumber(L, 100);    //将参数1压栈	lua_pushnumber(L, 20);     //将参数2压栈	lua_pushstring(L, "function f1");  //将参数3压栈	//LUA_API int   (lua_pcall)(lua_State *L, int nargs, int nresults, int errfunc);	lua_pcall(L, 3, 2, 0); //调用函数,3个参数,2个返回值	string result1 = lua_tostring(L, -1);	int result2    = lua_tonumber(L, -2);	cout << "result1:" << result1 << endl;	cout << "result2:" << result2 << endl;	//lua调用C++函数	lua_register(L, "lua_Strlen", lua_Strlen); //注册函数	lua_getglobal(L, "f2");	lua_pushnumber(L, 100);	lua_pushstring(L, "20");	lua_pushstring(L, "abc");lua_pcall(L, 3, 1, 0); //调用函数,3个参数,1个返回值  	int result3 = lua_tonumber(L, -1);	cout <<"result3:" << result3 << endl;	lua_close(L);                   //释放	system("pause");	return 0;}




VS运行截图


https://img1.sycdn.imooc.com//5b4f04d60001e24f02670141.jpg




点击查看更多内容
3人点赞

若觉得本文不错,就分享一下吧!

评论

作者其他优质文章

正在加载中
感谢您的支持,我会继续努力的~
扫码打赏,你说多少就多少
赞赏金额会直接到老师账户
支付方式
打开微信扫一扫,即可进行扫码打赏哦
今天注册有机会得

100积分直接送

付费专栏免费学

大额优惠券免费领

立即参与 放弃机会
意见反馈 帮助中心 APP下载
官方微信

举报

0/150
提交
取消