Lua是一门动态脚本语言,运行依托于宿主语言,可以是C、C++、C#、golang等,只要实现了Lua解释器就可以。所以,Lua从设计来讲就是动态脚本语言,正是因为它是解释性语言,所以它更充当了这些宿主语言的“缝合”作用,是为“胶水”性语言
lua中的栈是一个很奇特的数据结构,普通的栈只有一排索引,但是在lua中有两排索引,正数1索引的位置在栈底,负数-1索引的位置在栈顶。如下图所示。
根据结构图,我们不需要知道栈的大小,我们就可以确定的栈顶和栈底的位置
lua_State* L=luaL_newstate();
luaL_newstate()函数返回一个指向堆栈的指针Lua可以调用C函数的能力极大的提高了Lua的可扩展性和可用性。对有些和操作系统相关的功能,或者是对效率要求较高的模块,我们完全可以通过C函数来实现,之后再通过Lua调用指定的C函数。
对于那些可被Lua调用的C函数而言,其接口必须遵循Lua要求的形式,即typedef int (*lua_CFunction)(lua_State* L)
.。解析一下该形式:
可能出现的错误
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] 错误 1
make[2]: 离开目录“/home/oceanstar/workspace/cpp/lua-5.2.3/src”
make[1]: *** [linux] 错误 2
make[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
文件目录结构
lua测试代码
test.lua
name = "bob"
age= 20
mystr="hello lua"
mytable={name="tom",id=123456}
function add(x,y)
return 2*x+y
end
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 long
typedef 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 double
typedef LUA_NUMBER lua_Number;
LUA_API lua_Number (lua_tonumberx) (lua_State *L, int idx, int *isnum);
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+y
end
这时不进行编译,直接再运行一下./main,可以看到这个结果改变了从40变成了60,这是在我们没有进行重复编译的情况下直接产生的变化。
这就可以看出lua在C/C++语言中的嵌入特性,Lua中的函数就像是文本一样被读取,但是又确实是作为程序被执行。当我们的项目很大时,每次编译都需要十几分钟,这个时候如果合理的利用lua特性,仅仅是修改lua文件就可以避免这十几分钟的空白时间。
#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;
}