文章目录
还是因为项目的原因,需要在C++中使用到lua脚本,使用到的就是普通的lua库,可以在这里下载。
1. 项目中导入lua模块
创建工程后,在项目中导入lua模块,一个是lua的头文件,还有就是lua的lib库,在项目中包含include和lib路径并设置lib名称即可使用。
关于lib库,比较简单的使用方式就是直接使用这个以-static结尾的静态库即可,比较方便,无需其他操作。
2. 使用
先创建一个lua脚本,比较简单,就定义了一个字符串、一个表格、两个函数(一个有参,一个无参)。如下:
mystr = "I'm lua"
myTable = {name = "xiaomign", id = 123456}
function print_hello()
print("hello lua")
end
function _add(a, b)
return a + b
end
至于更细致的lua语法,这里就不多加介绍了,在lua官网上有详细的介绍。
使用上比较简单,先附上一段代码:
#include <iostream>
#include <string>
#include "lua/lua.hpp"
using namespace std;
int main(int argc, char *argv[])
{
///< 创建lua句柄并初始化
lua_State *pState = luaL_newstate();
if (nullptr == pState)
{
cout << "Lua 初始化失败" << endl;
return -1;
}
///< 加载相关库文件
luaL_openlibs(pState);
///< 加载lua文件
if (luaL_loadfile(pState, "./test.lua"))
{
cout << "Lua 文件加载失败" << endl;
}
else
{
///< 执行lua文件
if (lua_pcall(pState, 0, 0, 0))
{
cerr << lua_tostring(pState, -1) << endl;
}
else
{
///< 获取值
lua_getglobal(pState, "mystr");
string str = lua_tostring(pState, -1);
cout << str << endl;
///< 获取表中数据
lua_getglobal(pState, "myTable");
lua_getfield(pState, -1, "name");
cout << lua_tostring(pState, -1) << endl;
lua_getglobal(pState, "myTable");
lua_getfield(pState, -1, "id");
cout << lua_tonumber(pState, -1) << endl;
///< 调用函数
lua_getglobal(pState, "print_hello");
lua_pcall(pState, 0, 0, 0);
///< 调用计算函数
lua_getglobal(pState, "_add");
lua_pushnumber(pState, 10);
lua_pushnumber(pState, 20);
if (lua_pcall(pState, 2, 1, 0))
{
cout << lua_tostring(pState, -1) << endl;
lua_close(pState);
}
else
{
if (lua_isnumber(pState, -1))
{
cout << lua_tonumber(pState, -1) << endl;
}
}
}
}
lua_close(pState);
system("pause");
return 0;
}
2.1 创建lua句柄
使用lua的第一步就是创建一个用于lua和C++之家通信的句柄。
这个句柄极为重要,后面所有涉及到lua的操作,均需要使用到这个句柄。
lua_State *pState = luaL_newstate();
只要返回的pState不是nullptr,就说明创建句柄成功。
2.2 加载lua的相关库
创建完句柄之后,需要加载lua的库文件,这一点和socket编程有点类似。
luaL_openlibs(pState);
2.3 加载lua文件
luaL_loadfile(pState, "./test.lua")
luaL_loadfile是一个宏,其原型为:
#define luaL_loadfile(L,f) luaL_loadfilex(L,f,NULL)
加载成功的话,返回值是0.
2.4 执行lua文件
执行的意思,可以理解为激活,就是说如果这个句柄处于激活状态,就可以顺利地执行相关的操作。
lua_pcall(pState, 0, 0, 0)
这玩意也是一个宏值,如下:
#define lua_pcall(L,n,r,f) lua_pcallk(L, (n), (r), (f), 0, NULL)
这个函数参数的含义如下:
1. 参数1:lua句柄
2. 参数2:参数个数
3. 参数3:返回值个数
4. 参数4:错误处理函数,0表示无,表示错误处理函数在栈中的索引
执行成功的话,返回值为0.
至此,前期准备工作结束,接下来就是对于lua脚本内部东西进行的处理了,如下方介绍。
2.5 正式操作lua脚本
2.5.1 获取值
获取值比较简单,因为在lua脚本中定义了一个名为mystr的变量,因此,这里面就可以使用lua_getglobal函数,通过“mystr”名字的方式,获得这个字符串。
lua_getglobal(pState, "mystr");
因为已知这玩意是个字符串,因此可以采用lua_tostring这个宏将其转化为字符串类型(返回的是C++中的string类)。
string str = lua_tostring(pState, -1);
cout << str << endl;
lua_tostring宏值原型如下:
#define lua_tostring(L,i) lua_tolstring(L, (i), NULL)
2.5.2 获取表值
接下来是获取表值,因为定义了一个表,名为“myTable”,有两个键值对,分别为“name”和“id”,操作如下。
首先取出表:
lua_getglobal(pState, "myTable");
然后取出表中值:
lua_getfield(pState, -1, "name");
cout << lua_tostring(pState, -1) << endl;
不过有一点值得注意,每次加载表格只能获取其中一个值,如果想要获取第二个值,需要重新加载表格。
lua_getglobal(pState, "myTable");
lua_getfield(pState, -1, "name");
cout << lua_tostring(pState, -1) << endl;
lua_getglobal(pState, "myTable");
lua_getfield(pState, -1, "id");
cout << lua_tonumber(pState, -1) << endl;
2.5.3 调用函数
函数可以分为有参函数和无参函数两种,下面进行介绍。
2.5.3.1 无参函数
无参函数比较简单,直接通过函数名获得函数,然后调用lua_pcall调用即可。
lua_getglobal(pState, "print_hello");
lua_pcall(pState, 0, 0, 0);
2.5.3.2 有参函数
无参函数相对来说要麻烦一些,需要先将参数传进去,然后再调用。
lua_getglobal(pState, "_add");
lua_pushnumber(pState, 10);///< 压入参数1
lua_pushnumber(pState, 20);///< 压入参数2
if (lua_pcall(pState, 2, 1, 0))
{
///< 调用失败
cout << lua_tostring(pState, -1) << endl;
lua_close(pState);
}
else
{
///< 调用成功后获得数据
if (lua_isnumber(pState, -1))
{
cout << lua_tonumber(pState, -1) << endl;
}
}
2.6 关闭句柄
在调用结束之后,需要关闭句柄,这一步的目的是卸载加载的库,以及安全释放lua句柄。
lua_close(pState);
3. 结果演示
4. C++调用lua的本质
C++和lua通信的本质是通过一个“虚拟栈”进行交互。
-1表示栈顶,1表示栈底
lua和c++是通过一个虚拟栈来交互的。
c++调用lua实际上是:由c++先把数据放入栈中,由lua去栈中取数据,然后返回数据对应的值到栈顶,再由栈顶返回c++。
lua调c++也一样:先编写自己的c模块,然后注册函数到lua解释器中,然后由lua去调用这个模块的函数。
在lua中,lua堆栈就是一个struct,堆栈索引的方式可是是正数也可以是负数,区别是:正数索引1永远表示栈底,负数索引-1永远表示栈顶