文章目录- 运行方式
- 常用API
- 压入元素
- 类型相关
- 查询相关
- 执行lua代码
- 获取 Lua 代码执行结果
- 其他常用API
- 案例亲测
- 压入元素
- 类型相关
- 查询相关
- 执行lua代码
- 获取 Lua 代码执行结果
- 其他常用API
我也琢磨了好一会儿,这篇文章改了又改,主要是每一part的顺序问题。
运行方式
先看张图:
它的执行方式,有点熟悉,但是一时又实在想不起来那个案例。
波兰式,对,思考一下波兰式。
在Lua和C语言之间进行数据交换时,由于两种语言之间有着较大的差异,比如Lua是动态类型,C语言是静态类型,Lua是自动内存管理,而C语言则是手动内存管理。为了解决这些问题,Lua的设计者使用了虚拟栈作为二者之间数据交互的介质。在C/C 程序中,如果要获取Lua的值,只需调用Lua的C API函数,Lua就会将指定的值压入栈中。要将一个值传给Lua时,需要先将该值压入栈,然后调用Lua的C API,Lua就会获取该值并将其从栈中弹出。为了可以将不同类型的值压入栈,以及从栈中取出不同类型的值,Lua为每种类型均设定了一个特定函数。
常用API
压入元素
代码语言:javascript复制 Lua针对每种C类型,都有一个C API函数与之对应,如:
void lua_pushnil(lua_State* L); --nil值
void lua_pushboolean(lua_State* L, int b); --布尔值
void lua_pushnumber(lua_State* L, lua_Number n); --浮点数
void lua_pushinteger(lua_State* L, lua_Integer n); --整型
void lua_pushlstring(lua_State* L, const char* s, size_t len); --指定长度的内存数据
void lua_pushstring(lua_State* L, const char* s); --以零结尾的字符串,其长度可由strlen得出。
//对于字符串数据,Lua不会持有他们的指针,而是调用在API时生成一个内部副本,因此,即使在这些函
//数返回后立刻释放或修改这些字符串指针,也不会有任何问题。
//在向栈中压入数据时,可以通过调用下面的函数判断是否有足够的栈空间可用,一般而言,Lua会预留20
//个槽位,对于普通应用来说已经足够了,除非是遇到有很多参数的函数。
//int lua_checkstack(lua_State* L, int extra) --期望得到extra数量的空闲槽位,如果不
//能扩展并获得,返回false。
类型相关
API使用“索引”来引用栈中的元素,第一个压入栈的为1,第二个为2,依此类推。我们也可以使用为索引值,其中-1表示为栈顶元素,-2为栈顶下面的元素,同样依此类推。
代码语言:javascript复制 Lua提供了一组特定的函数用于检查返回元素的类型,如:
int lua_isboolean (lua_State *L, int index);
int lua_iscfunction (lua_State *L, int index);
int lua_isfunction (lua_State *L, int index);
int lua_isnil (lua_State *L, int index);
int lua_islightuserdata (lua_State *L, int index);
int lua_isnumber (lua_State *L, int index);
int lua_isstring (lua_State *L, int index);
int lua_istable (lua_State *L, int index);
int lua_isuserdata (lua_State *L, int index);
以上函数,成功返回1,否则返回0。需要特别指出的是,对于lua_isnumber而言,不会检查值是否为数字类型,而是检查值是否能转换为数字类型。 Lua还提供了一个函数lua_type,用于获取元素的类型,函数原型如下:
代码语言:javascript复制int lua_type (lua_State *L, int index);
除了上述函数之外,Lua还提供了一组转换函数,如:
代码语言:javascript复制 int lua_toboolean (lua_State *L, int index);
lua_CFunction lua_tocfunction (lua_State *L, int index);
lua_Integer lua_tointeger (lua_State *L, int index);
const char *lua_tolstring (lua_State *L, int index, size_t *len);
lua_Number lua_tonumber (lua_State *L, int index);
const void *lua_topointer (lua_State *L, int index);
const char *lua_tostring (lua_State *L, int index);
void *lua_touserdata (lua_State *L, int index);
对于上述函数,如果调用失败,lua_toboolean、lua_tonumber、lua_tointeger和lua_objlen均返回0,错误的通过判断返回值是否为NULL即可。 对于lua_tolstring函数返回的指向内部字符串的指针,在该索引指向的元素被弹出之后,将无法保证仍然有效。该函数返回的字符串末尾均会有一个尾部0。
查询相关
代码语言:javascript复制//除了上面给出的数据交换函数之外,Lua的C API还提供了一组用于操作虚拟栈的普通函数,如:
int lua_gettop(lua_State* L); //返回栈中元素的个数。
void lua_settop(lua_State* L, int index); //将栈顶设置为指定的索引值。
void lua_pushvalue(lua_State* L, int index); //将指定索引的元素副本压入栈。
void lua_remove(lua_State* L, int index); //删除指定索引上的元素,其上面的元素自动下移。
void lua_insert(lua_State* L, int index); //将栈顶元素插入到该索引值指向的位置。
void lua_replace(lua_State* L, int index); //弹出栈顶元素,并将该值设置到指定索引上。
//Lua还提供了一个宏用于弹出指定数量的元素:#define lua_pop(L,n) lua_settop(L, -(n) - 1)
执行lua代码
代码语言:javascript复制void lua_call (lua_State *L, int nargs, int nresults);
int lua_pcall (lua_State *L, int nargs, int nresults, int errfunc);
如上,lua_call 中指定了函数的传入参数个数 nargs 和函数返回结果个数 nresults
代码语言:javascript复制lua_getfield(L, LUA_GLOBALSINDEX, "f"); /* function to be called */
lua_pushstring(L, "how"); /* 1st argument */
lua_getfield(L, LUA_GLOBALSINDEX, "t"); /* table to be indexed */
lua_getfield(L, -1, "x"); /* push result of t.x (2nd arg) */
lua_remove(L, -2); /* remove 't' from the stack */
lua_pushinteger(L, 14); /* 3rd argument */
lua_call(L, 3, 1); /* call 'f' with 3 arguments and 1 result */
lua_setfield(L, LUA_GLOBALSINDEX, "a"); /* set global 'a' */
如果 Lua 代码执行过程中没有任何错误,lua_pcall 的行为与 lua_call 是相同的。如果在执行的过程中有错误发生,lua_pcall 会捕捉该错误,并将错误信息推送到 Lua 栈上,并返回一个错误码。
lua_pcall 最后一个参数 errfunc,指定错误处理函数在 Lua 栈中的位置
一般系统嵌入 Lua 代码,都是使用 lua_pcall,调用方法一般都是:
代码语言:javascript复制lua_pcall (l, 0, 0, 0)
获取 Lua 代码执行结果
使用 lua_call 或 lua_pcall 执行完一个函数后,会将执行结果放到栈顶,如果有两个返回值,栈索引 -1 和 -2 就是返回值,如果有三个值,栈索引 -1,-2,-3 就是返回值,以此类推
其他常用API
代码语言:javascript复制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_close(L); //释放lua资源
lua_getglobal(L, "val"); //获取全局变量的val的值,并将其放入栈顶
案例亲测
1、创建一个文件夹,里面放上 lua.h 文件。文件在哪里自行检索,find 命令不是很好用,可以检索lualib.h。
2、放上一个 test.lua
代码语言:javascript复制mystr = "I'm lua"
myTable = {name = "xiaomign", id = 123456}
function print_hello()
print("hello lua")
end
function _add(a, b)
return a b
end
3、创建 main.cpp
代码语言:javascript复制#include <iostream>
#include <string>
#include "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);
return 0;
}
4、编译
代码语言:javascript复制g *.cpp -o cpplua -llua -lm -ldl