原文链接:https://blog.csdn.net/sm9sun/article/details/68946343
首先介绍一下Lua语言,Lua 是一个小巧的脚本语言,该语言的设计目的是为了嵌入应用程序中,从而为应用程序提供灵活的扩展和定制功能。
lua本身就是C写的,所以Lua脚本可以很容易的被C/C++代码调用,也可以反过来调用C/C++的函数
lua语法、解释器、执行原理都与python相似
唯一差距就是lua没有强大的类库作为支撑,Lua只是具备了一些比如数学运算和字符串处理等简单的基本功能。
所以lua不适合作为开发独立应用程序的语言。
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程序通过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);
上文中说过,C++与Lua交互主要通过堆栈进行,对于C++这边,传参就是lua_push,获取就是lua_to。后面对应的数据类型
如将int入栈,即lua_pushnumber,如获取string参数,即lua_tostring,他们参数都是两个,堆栈L及一个索引参数,比如说-1为栈顶,-2就是第二个~
例:
Lua代码
–用于测试C++获取lua参数
var1=111
var2=222
var3=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多返回
function f1(a,b,c)
local sum=a+b
return sum,c
end
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代码
--用于测试lua调用C++函数
function f2(a,b,c)
local len=lua_Strlen(c)
local sum=a+b+len
return sum
end
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++我们写好的调用函数lua_Strlen,参数之间都是通过栈顶进行交互传递的
输出为:
at lua_Strlen,the str is :abc
result3:123
完整测试代码:
Test.Lua
-用于测试C++获取lua参数
var1=111
var2=222
var3=333
--用于测试lua多返回
function f1(a,b,c)
local sum=a+b
return sum,c
end
--用于测试lua调用C++函数
function f2(a,b,c)
local len=lua_Strlen(c)
local sum=a+b+len
return sum
end
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;
}
Lua语言被不少大软件作为脚本语言,因为通过Lua语言,可以很方便地调用大型程序中的已有函数,这样可以将业务逻辑与算法逻辑分离开。我们也可以在自己的软件中,嵌入lua语言,预留API给用户做更多的自定义操作(如脚本编辑器)。本文谈Lua源码的编译、以及如何在自己的C程序中嵌入Lua。
编译Lua
在Lua官网的http://www.lua.org/download.html页面,可以下载到各个版本的Lua源码。源码的结构分为以下几个目录:
doc:文档
etc:方便学习的lua源码,是工程级别的lua的子集,含Makefile
source:lua源代码,含Makefile
test:一些Lua脚本,用于测试
根据源文件生成Lua,最终的结果有几种选择:
Lua library:静态的Lua库,这样,配合Lua的头文件,就可以将Lua嵌入到自己的程序中了
Standalone Lua:Lua解释器,可以读取Lua脚本并执行
Luac:lua编译器,将Lua脚本翻译成Lua字节码,这样可以加快Lua脚本的加载速度
源码附带的Makefile,没有针对Windows VS平台的选项。不过不用担心,自己根据需要,建个空工程,再把各个文件添加进去,就可以编译成功了。其中luac与lua需要建立的是控制台空工程,静态库需要建立的是静态库空工程(链接方式不一样)。
嵌入Lua
编译好lualib后,就可以配合头文件,在自己的C++程序中调用Lua了,以下是一个简单例子,它加载并执行一个Lua脚本"hello,lua":
extern "C" {
undefined
#include "LuaHeader/lua.h"
#include "LuaHeader/lualib.h"
#include "LuaHeader/lauxlib.h"
}
int main(int argc, char *argv[])
{
undefined
int s = 0;
lua_State *L = lua_open();
// load the libs
luaL_openlibs(L);
//run a Lua scrip here
luaL_dofile(L, "hello.lua");
lua_close(L);
return 1;
}
因为Lua是C语言写的,为了链接正常,注意要extern “C”。以下是hello.lua脚本的内容,它也可以修改为其它符合lua语法的脚本:
io.write("Please enter your name: ")
name = io.read()
io.write("Hi " .. string.format("%s",name) .. ", enjoy hacking with Lua\r\n");
用Lua中调用host程序的函数
lua常被选为各大程序的内嵌脚本语言的原因,就是它可以将host program的函数,注册为API供lua调用,这样可以使编程的逻辑易于调整。以下是一个特别简单的例子:
// function type must be lua_CFunction
extern "C" static int MYAPI_ShowHello(lua_State *L)
{
undefined
printf("Hello, World\r\n");
return 1;
}
int main(int argc, char *argv[])
{
undefined
int s = 0;
lua_State *lua = lua_open();
// load the libs
luaL_openlibs(lua);
// register C function into lua
lua_pushcclosure(lua, MYAPI_ShowHello, 0);
lua_setglobal(lua, "myHello");
//run a Lua scrip here
luaL_dofile(lua, "hello.lua");
lua_close(lua);
return 1;
}
通过以上pushcclosure与setglobal的注册,在lua中可以通过调用"myHello"来间接调用host程序的MYAPI_ShowHello函数。
"hello.lua"中的内容:
myHello()
https://blog.csdn.net/weixin_39510813/article/details/95241163
文章浏览阅读1.1w次,点赞7次,收藏34次。vue-grid-layout的使用、实例、遇到的问题和解决方案_vue-grid-layout
文章浏览阅读218次。然后连接一个数据源,就会在下面自动产生一个添加附件的组件。把这个控件复制粘贴到页面里,就可以单独使用来上传了。插入一个“编辑”窗体。_powerapps点击按钮上传附件
文章浏览阅读264次。(1) Abstraction (抽象)(2) Polymorphism (多态)(3) Inheritance (继承)(4) Encapsulation (封装)_"object(cnofd[\"ofdrender\"])十条"
文章浏览阅读133次。删除node_modules,重新npm install看是否成功。在 package.json 文件中的 scripts 中加入。修改你的第三方库的bug等。然后目录会多出一个目录文件。_修改 node_modules
文章浏览阅读883次。【代码】【】kali--password:su的 Authentication failure问题,&sudo passwd root输入密码时Sorry, try again._password: su: authentication failure
文章浏览阅读1w次,点赞13次,收藏97次。整理5个优秀的微信小程序开源项目。收集了微信小程序开发过程中会使用到的资料、问题以及第三方组件库。_微信小程序开源模板
文章浏览阅读128次。Centos7最简搭建NFS服务器_centos7 搭建nfs server
文章浏览阅读1.2k次,点赞2次,收藏3次。前言mybatis在持久层框架中还是比较火的,一般项目都是基于ssm。虽然mybatis可以直接在xml中通过SQL语句操作数据库,很是灵活。但正其操作都要通过SQL语句进行,就必须写大量的xml文件,很是麻烦。mybatis-plus就很好的解决了这个问题。..._mybaitis-plus ruledataobjectattributemapper' and 'com.picc.rule.management.d
文章浏览阅读325次。EECE 1080C / Programming for ECESummer 2022Laboratory 4: Global Functions PracticePlagiarism will not be tolerated:Topics covered:function creation and call statements (emphasis on global functions)Objective:To practice program development b_eece1080c
文章浏览阅读53次。被同机房早就1年前就学过的东西我现在才学,wtcl。设要求的数为\(x\)。设当前处理到第\(k\)个同余式,设\(M = LCM ^ {k - 1} _ {i - 1}\) ,前\(k - 1\)个的通解就是\(x + i * M\)。那么其实第\(k\)个来说,其实就是求一个\(y\)使得\(x + y * M ≡ a_k(mod b_k)\)转化一下就是\(y * M ...
文章浏览阅读1.3k次。首先,问题是如何出现的?晚上复查代码,发现一个activity没有调用自己的ondestroy方法我表示非常的费解,于是我检查了下代码。发现再finish代码之后接了如下代码finish();System.exit(0);//这就是罪魁祸首为什么这样写会出现问题System.exit(0);////看一下函数的原型public static void exit (int code)//Added ..._android 手动杀死app,activity不执行ondestroy
文章浏览阅读894次。Q: SylixOS 版权是什么形式, 是否分为<开发版税>和<运行时版税>.A: SylixOS 是开源并免费的操作系统, 支持 BSD/GPL 协议(GPL 版本暂未确定). 没有任何的运行时版税. 您可以用她来做任何 您喜欢做的项目. 也可以修改 SylixOS 的源代码, 不需要支付任何费用. 当然笔者希望您可以将使用 SylixOS 开发的项目 (不需要开源)或对 SylixOS 源码的修改及时告知笔者.需要指出: SylixOS 本身仅是笔者用来提升自己水平而开发的_select函数 导致堆栈溢出 sylixos