C/C++编程:Lua与C/C++的交互
发布日期:2022-03-16 03:25:34 浏览次数:19 分类:技术文章

本文共 6709 字,大约阅读时间需要 22 分钟。

理论

Lua是一门动态脚本语言,运行依托于宿主语言,可以是C、C++、C#、golang等,只要实现了Lua解释器就可以。所以,Lua从设计来讲就是动态脚本语言,正是因为它是解释性语言,所以它更充当了这些宿主语言的“缝合”作用,是为“胶水”性语言

lua中的栈

lua中的栈是一个很奇特的数据结构,普通的栈只有一排索引,但是在lua中有两排索引,正数1索引的位置在栈底,负数-1索引的位置在栈顶。如下图所示。

在这里插入图片描述

根据结构图,我们不需要知道栈的大小,我们就可以确定的栈顶和栈底的位置

  • 当索引是1的时候对应的是栈底
  • 当索引是-1的时候对于的是栈顶。

常用的lua api

  • lua_State* L=luaL_newstate();luaL_newstate()函数返回一个指向堆栈的指针
  • lua_createtable(L,0,0);新建并压入一张表
  • lua_pushstring(L,0,0);压入一个字符串
  • lua_pushnumber(L,0,0);压入一个数字
  • lua_tostring(L,1);取出一个字符串
  • lua_tointeger(L,1);取出数字
  • double b=lua_tonumber();取出一个double类型的数字
  • lua_load()函数 当这个函数返回0时表示加载
  • luaL_loadfile(filename) 这个函数也是只允许加载lua程序文件,不执行lua文件。它是在内部去用lua_load()去加载指定名为filename的lua程序文件。当返回0表示没有错误。
  • luaL_dofile 这个函数不仅仅加载了lua程序文件,还执行lua文件。返回0表示没有错误。
  • lua_push*(L,data)压栈
  • lua_to*(L,index)取值
  • lua_pop(L,count)出栈
  • lua_close(L);释放lua资源
  • lua_getglobal(L, “val”);//获取全局变量的val的值,并将其放入栈顶

Lua中调用CPP函数

Lua可以调用C函数的能力极大的提高了Lua的可扩展性和可用性。对有些和操作系统相关的功能,或者是对效率要求较高的模块,我们完全可以通过C函数来实现,之后再通过Lua调用指定的C函数。

对于那些可被Lua调用的C函数而言,其接口必须遵循Lua要求的形式,即typedef int (*lua_CFunction)(lua_State* L).。解析一下该形式:

  • 该函数类型仅仅包含一个表示Lua环境的指针作为其唯一的参数,实现者可以通过该指针进一步获取Lua代码中实际传入的参数。
  • 返回值是整型,表示该C函数将返回给Lua代码的返回值数量,如果没有返回值,则return 0即可
  • 需要说明的是,C函数无法直接将真正的返回值返回给Lua代码,而是通过虚拟栈来传递Lua代码和C函数之间的调用参数和返回值的

可能出现的错误

a.cpp:8: 警告:不建议使用从字符串常量到‘char*’的转换/tmp/ccij5HeF.o:在函数‘main’中:a.cpp:(.text+0xe):对‘luaL_newstate()’未定义的引用a.cpp:(.text+0x24):对‘luaL_openlibs(lua_State*)’未定义的引用a.cpp:(.text+0x60):对‘luaL_loadbufferx(lua_State*, char const*, unsigned int, char const*, char const*)’未定义的引用a.cpp:(.text+0x9b):对‘lua_pcallk(lua_State*, int, int, int, int, int (*)(lua_State*, int, int))’未定义的引用a.cpp:(.text+0xe4):对‘lua_tolstring(lua_State*, int, unsigned int*)’未定义的引用a.cpp:(.text+0x107):对‘lua_settop(lua_State*, int)’未定义的引用a.cpp:(.text+0x140):对‘lua_close(lua_State*)’未定义的引用

解决方法:

原来是因为lua是C语言模块,用g++调用c语言的库需要在包含头文件时加上extern “C”,就能正常编译了,即修改为

extern "C" {
#include "lua.h" #include "lualib.h" #include "lauxlib.h"}

原因:

因为Lua是用C语言写的,除非编译lua库时指定编译器强制以C++方式编译,否则在C++工程中应该这样包含lua头文件:

extern "C" {
#include "lua.h" #include "lualib.h" #include "lauxlib.h"}

准备

安装lua编译器(没有动态链接库)

在Linux系统安装Lua,使用下面的命令下载并生成Lua程序:

$ wget http://www.lua.org/ftp/lua-5.2.3.tar.gz$ tar zxf lua-5.2.3.tar.gz$ cd lua-5.2.3$ make linux test

在其它系统上安装Lua时,比如aix,ansi,bsd,generic,linux,mingw,posix,solaris,你需要将make linux test命令中的linux替换为相应的系统平台名称。

指向完成之后可以在src目录下生成liblua.a

错误

lua.c:67:31: 致命错误:readline/readline.h:没有那个文件或目录 #include 
^编译中断。make[2]: *** [lua.o] 错误 1make[2]: 离开目录“/home/oceanstar/workspace/cpp/lua-5.2.3/src”make[1]: *** [linux] 错误 2make[1]: 离开目录“/home/oceanstar/workspace/cpp/lua-5.2.3/src”make: *** [linux] 错误 2

解决:

yum install libtermcap-devel ncurses-devel libevent-devel readline-devel

注意

在linux下编译安装lua默认是不会生成liblua.so文件的,如果想要生成liblua.so文件需要修改makefile文件。 要么自己直接到去下载。生成方法可以参考

在这里插入图片描述

环境搭建好后,所有C/C++代码需要引用lua.hpp或lua.h, lualib.h, lauxlib.h,运行需要连接liblua.so

C/C++调用Lua

文件目录结构

在这里插入图片描述

lua测试代码

test.lua

name = "bob"age= 20mystr="hello lua"mytable={
name="tom",id=123456}function add(x,y) return 2*x+yend

示例1:通过C语言读取Lua中的变量

main.cpp

#include 
extern "C" {
#include "lua.h"#include "lauxlib.h"#include "lualib.h"};int main(){
lua_State *L = luaL_newstate(); luaL_openlibs(L); int retLoad = luaL_loadfile(L, "/home/oceanstar/CLionProjects/untitled1/src/test.lua"); if (retLoad == 0) {
printf("load file success retLoad:%d\n", retLoad); } if (retLoad || lua_pcall(L, 0, 0, 0)) {
printf("error %s\n", lua_tostring(L, -1)); return -1; } lua_getglobal(L, "name"); //lua获取全局变量name的值并且返回到栈顶 lua_getglobal(L, "age"); //lua获取全局变量age的值并且返回到栈顶,这个时候length对应的值将代替width的值成为新栈顶 //注意读取顺序 int age = lua_tointeger(L, -1); //栈顶 const char *name = lua_tostring(L, -2);//次栈顶 printf("name = %s\n", name); printf("age = %d\n", age); lua_close(L); return 0;}

测试结果:

在这里插入图片描述
这里面需要注意的是,当出现多个lua_getglobal()函数的时候由上到下,依次压入栈,在使用lua_to*(L,index)函数读取栈顶元素的时候,如果每次压入栈的数据类型都不一样,那么在读取的是就要注意读取顺序。

当使用lua_tointegers()的是,返回值类型的long long

#define LUA_INTEGER      long longtypedef LUA_INTEGER      lua_Integer;LUA_API lua_Integer      (lua_tointegerx) (lua_State *L, int idx, int *isnum);

当使用lua_tolstring()的时候,返回值类型是const char*

LUA_API const char *(lua_tolstring) (lua_State *L, int idx, size_t *len);

当使用lua_tonumber()的是,返回值类型是double

#define LUA_NUMBER	doubletypedef LUA_NUMBER      lua_Number;LUA_API lua_Number      (lua_tonumberx) (lua_State *L, int idx, int *isnum);

示例2:通过C语言调用Lua中的函数

main.cpp

#include 
extern "C" {
#include "lua.h"#include "lauxlib.h"#include "lualib.h"};int main(){
lua_State *L = luaL_newstate(); luaL_openlibs(L); int retLoad = luaL_loadfile(L, "/home/oceanstar/CLionProjects/untitled1/src/test.lua"); if (retLoad || lua_pcall(L, 0, 0, 0)) {
printf("error %s\n", lua_tostring(L, -1)); return -1; } //调用函数,依次压入参数 lua_getglobal(L, "add"); lua_pushnumber(L, 10); lua_pushnumber(L, 20); //查看压入栈的元素 for (int i=1;i<3;i++) {
printf("number:%f\n",lua_tonumber(L, -i)); } //lua_pcall(L,2,1,0):传入两个参数 期望得到一个返回值,0表示错误处理函数在栈中的索引值,压入结果前会弹出函数和参数 int pcallRet = lua_pcall(L, 2, 1, 0); //lua_pcall将计算好的值压入栈顶,并返回状态值 if (pcallRet != 0) {
printf("error %s\n", lua_tostring(L, -1)); return -1; } int val = lua_tonumber(L, -1); //在栈顶取出数据 printf("val:%d\n", val); lua_pop(L, -1); //弹出栈顶 //再次查看栈内元素,发现什么都没有,因为lua在返回函数计算值后会清空栈,只保留返回值 for (int i=1;i<3;i++) {
printf("number:%f\n",lua_tonumber(L, -i)); } lua_close(L); return 0;}

结果:

在这里插入图片描述
由结果可以看出来,当调用完lua中的函数以后,会自动清空栈,只保留结果在栈顶。

注意:这个时候我们修改一下test.lua中的add函数:把2改为4

function add(x,y)	return 4*x+yend

这时不进行编译,直接再运行一下./main,可以看到这个结果改变了从40变成了60,这是在我们没有进行重复编译的情况下直接产生的变化。

这就可以看出lua在C/C++语言中的嵌入特性,Lua中的函数就像是文本一样被读取,但是又确实是作为程序被执行。当我们的项目很大时,每次编译都需要十几分钟,这个时候如果合理的利用lua特性,仅仅是修改lua文件就可以避免这十几分钟的空白时间。

实例3:通过C语言调用Lua中的table

#include 
extern "C" {
#include "lua.h"#include "lauxlib.h"#include "lualib.h"};int main(){
lua_State *L = luaL_newstate(); luaL_openlibs(L); int retLoad = luaL_loadfile(L, "/home/oceanstar/CLionProjects/untitled1/src/test.lua"); if (retLoad || lua_pcall(L, 0, 0, 0)) {
printf("error %s\n", lua_tostring(L, -1)); return -1; } printf("读取lua table中对应的值\n"); //将全局变量mytable压入栈 lua_getglobal(L, "mytable"); //压入表中的key lua_pushstring(L, "name"); //lua_gettable会在栈顶取出一个元素并且返回把查找到的值压入栈顶 lua_gettable(L, 1); const char *name = lua_tostring(L, -1); //在栈顶取出数据 printf("name:%s\n", name); lua_pushstring(L,"id");//压入id lua_gettable(L, 1);//在lua mytable表中取值返回到栈顶 int id = lua_tonumber(L, -1); //在栈顶取出数据 printf("id:%d\n", id); lua_close(L); return 0;}

在这里插入图片描述

Lua调用C/C++

文章目录

转载地址:https://blog.csdn.net/zhizhengguan/article/details/122462700 如侵犯您的版权,请留言回复原文章的地址,我们会给您删除此文章,给您带来不便请您谅解!

上一篇:算法:如何在内存中存储一个图呢?
下一篇:算法:图论基础

发表评论

最新留言

做的很好,不错不错
[***.243.131.199]2024年03月20日 21时01分50秒