环境配置参考上一篇博客lua环境配置 此篇主要介绍lua与C++的交互。包括基础的调用lua文件以及和lua栈相关的知识。
第一个实例
//1. 初始化Lua虚拟机
lua_State *lua_state;
lua_state = luaL_newstate();
int error;
//2. 打开所有lua的标准库
luaL_openlibs(lua_state);
//3. 运行脚本文件
error = luaL_dofile(lua_state, "hello.lua");
if (error == LUA_OK)
std::cout << " nothing wrong" << std::endl;
else
std::cout << lua_tostring(lua_state, -1);
lua_close(lua_state);
一、调用解释
1. luaL_newstate
可以理解为新建了一个环境(或者状态),用于C++和lua的交互。C++和lua之间所有的数据交互都会通过这个环境,相当于是一个桥梁的作用。
2. luaL_openlibs
上述用 luaL_newstate新建的环境中什么都没有,如果需要在hello.lua里边使用函数(例如本例中的print),则需要引入相应的库。print需要用到的是基础库base。
所以如前一篇配置文档代码里所列,也可以只导入需要的库,这样就可以忽略不需要的包。代码如下:
static const luaL_Reg lualibs[] =
{
{ "base", luaopen_base },
{ NULL, NULL}
};
//3.注册Lua标准库并清空栈
const luaL_Reg *lib = lualibs;
for(; lib->func != NULL; lib++)
{
luaL_requiref(lua_state, lib->name, lib->func, 1);
lua_pop(lua_state, 1);
}
而本例中所用到的luaL_openlibs,是打开了所有的标准库,包括(base,io, table, math等库)。我们去查看lua的源码文件可以知道其实luaL_openlibs也是对分开的各个库进行了注册(luaL_requiref)操作。从linit.c文件中摘录的源码如下(头文件定义在lualib.h里面):
static const luaL_Reg loadedlibs[] = {
{"_G", luaopen_base},
{LUA_LOADLIBNAME, luaopen_package},
{LUA_COLIBNAME, luaopen_coroutine},
{LUA_TABLIBNAME, luaopen_table},
{LUA_IOLIBNAME, luaopen_io},
{LUA_OSLIBNAME, luaopen_os},
{LUA_STRLIBNAME, luaopen_string},
{LUA_MATHLIBNAME, luaopen_math},
{LUA_UTF8LIBNAME, luaopen_utf8},
{LUA_DBLIBNAME, luaopen_debug},
#if defined(LUA_COMPAT_BITLIB)
{LUA_BITLIBNAME, luaopen_bit32},
#endif
{NULL, NULL}
};
LUALIB_API void luaL_openlibs (lua_State *L) {
const luaL_Reg *lib;
/* "require" functions from 'loadedlibs' and set results to global table */
for (lib = loadedlibs; lib->func; lib++) {
luaL_requiref(L, lib->name, lib->func, 1);
lua_pop(L, 1); /* remove lib */
}
}
在这里只要知道如果需要在lua里边用到print等基础函数,就需要在C++程序里面导入相应的库就可以了。具体对luaL_requiref的理解可以等我们讲到自己写的C++函数,并需要在lua程序里边调用时就有更深入的理解了。
3. luaL_dofile
luaL_dofile就是运行脚本文件,追踪源码可以知道它就是一个宏定义。
#define luaL_dofile(L, fn) \
(luaL_loadfile(L, fn) || lua_pcall(L, 0, LUA_MULTRET, 0))
所以我们也可以自己写成
error = luaL_loadfile(lua_state, "hello.lua") || lua_pcall(lua_state, 0, LUA_MULTRET, 0);
我们可以看到它分为两个部分luaL_loadfile和lua_pcall。
1) luaL_loadfile
这部分是用于导入文件,同时有一些错误检查,例如找不到文件等会引发错误。如果没有错误,调用返回0,并向栈(栈的概念后面详细讲)中压入编译后的代码块。
2) lua_pcall
这个函数将代码块从栈中弹出,并运行,如果有错误,则会向栈中压入一条错误信息。比如,如果我们在程序中没有导入基本库(没有luaL_openlibs相关的库),则我们可以从栈中得到一条错误信息(lua默认不打印到标准输出,而只是压到栈中),将其读取出来并打印可以得到。
hello.lua:1: attempt to call a nil value (global 'print')Program ended with exit code: 0
如果把上述两部分分开写,大家可以尝试一下分别打印错误信息。
std::string scriptPath = "hello.lua";
int status = luaL_loadfile(lua_state, scriptPath.c_str());
std::cout << "return: " << status << std::endl;
int result = 0;
if(status == LUA_OK)
{
result = lua_pcall(lua_state, 0, LUA_MULTRET, 0);
if (result == LUA_OK)
{
std::cout << "Nothing wrong" << std::endl;
}
else
{
std::cout << lua_tostring(lua_state, -1);
}
}
else
{
std::cout << " Could not load the script." << std::endl;
}
二、栈
C++和lua的交互全部通过一个栈来完成。lua的类型广泛来讲只有一种就是table,那么lua和c++之间交互的时候又互相不知道类型,所有不能直接使用,栈这种结构就比较好地解决了这个问题。栈用来保存lua的数据,当c++想向lua传入数据的时候,就需要先将数据通过AP
I转换成lua的形式,再压入栈;当c++想从lua读取数据的时候,可以从栈中获取数据,然后转换成c++的数据形式。反之,如果是从lua的角度来看,数据也都是通过栈来传递的。
1. 栈的基本结构
| n | 栈顶 | -1 |
| … | ----- | -2 |
| 2 | ------ | … |
| 1 | 栈底 | -n |
上述图是栈的序号示意图,如果从栈底往栈顶,则序号一次为1,2,…,n; 如果从栈顶往栈底往下数,则序号为-1,-2,…, -n。
2. 栈的基本操作
栈的基本操作包括压入弹出操作、访问查询操作。
1)压入弹出元素
举例:lua_pushnumber(lua_State *L, lua_Number n)
lua_pop(lua_State *L, int n)
注意,这里的lua_pop的第二个参数是个数,而不是栈的序号
2) 访问查询操作
1、检查元素类型lua_isnumber(lua_State *L, int idx)
或者 int i = lua_type(lua_State *L, int idx)
2、遍历元素lua_tonumber(lua_State *L, int idx)
,可以直接将这个结果在C++程序中打印出来
3) 其他
lua_gettop(lua_State *L)
用于获得栈中元素的个数
举例
//1. 初始化Lua虚拟机
lua_State *lua_state;
lua_state = luaL_newstate();
//2. 压数据入栈
lua_pushnumber(lua_state, 5);
lua_pushstring(lua_state, "hello~ lua lua");
//3. 判断数据类型并用相应的接口转换数据
if(lua_isnumber(lua_state, 1))
{
std::cout << lua_tonumber(lua_state, 1) << std::endl;
}
if(lua_type(lua_state, 2) == LUA_TSTRING)
std::cout << lua_tostring(lua_state, 2) << std::endl;
// 4. 获取栈里边数据的数目
std::cout << "There are " << lua_gettop(lua_state) << " elements in the stack!" << std::endl;
lua_close(lua_state);
输出结果
5
hello~ lua lua
There are 2 elements in the stack!