[✔️]c++和lua相互调用

465 阅读2分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 2 月更文挑战」的第 5 天,点击查看活动详情

lua源码

lua官网下载lua源码,自己学习lua和c++相互调用的例子

image.png

以下2个文件不需要参与编译,原因是他们是bin文件的源代码,都有Main入口

lua.c

image.png

luac.c

image.png

加载lua文件

lua_State* L = luaL_newstate();
luaL_loadfile(L, "main.lua");
lua_pcall(L,0,0,0);
// luaL_dofile(L, "main.lua");
lua_close(L);
  • dofile和loadfile的区别:
#define luaL_dofile  (L, fn) 	(luaL_loadfile (L, fn    ) || lua_pcall(L, 0, LUA_MULTRET, 0))
#define luaL_loadfile(L, f )	 luaL_loadfilex(L, f,NULL)

lua栈

  • 特点:先进后出
  • 正数索引1永远表示栈底,负数索引-1永远表示栈顶**

image.png

lua_gettop(L); // 获取当前lua栈的大小
lua_pop(L, -1);// 弹出栈顶元素,-1弹出所有,>1弹出指定数量的元素

CPP栈操作

void test1(){
    lua_State* L = luaL_newstate();
    lua_pushstring(L, "i am string");
    lua_pushnumber(L, 100);

    if (lua_isstring(L, 1))
    {
        cout << lua_tostring(L, 1) << endl;
    }
    if (lua_isnumber(L, 2))
    {
        cout << lua_tonumber(L, 2) << endl;
    }
    lua_close(L);
}

堆栈情况如下:

image.png

image.png

lua调用c++函数(注册全局函数)

注册函数类型:

typedef int (*lua_CFunction) (lua_State *L);

没有返回值的

  • lua
print("cpp call me");
  • cpp
int print(lua_State* L){
    if (lua_isstring(L, -1))
    {
        cout << lua_tostring(L, -1) << endl;
    }
    return 0;
}
void main(){
    lua_State* L = luaL_newstate();
    lua_register(L, "print", print); 
    luaL_dofile(L, "test.lua");
    lua_close(L);
}

返回int,string基础类型

  • lua
local sum, mul = add(100, 20);
print("sum: " .. sum);
print("mul:" .. mul)
  • cpp
int add(lua_State* L){
    // 参数是按照顺序依次压入栈里面
    double a = lua_tonumber(L, -1); // 20
    double b = lua_tonumber(L, -2); // 100

    lua_pushnumber(L, a + b); // sum
    lua_pushnumber(L, a * b); // mul
    return 2;// 返回的参数个数
}
void main(){
    lua_State* L = luaL_newstate();
    lua_register(L, "add", add); 
    luaL_dofile(L, "test.lua");
    lua_close(L);
}

返回table类型

  • lua
local t = returnTable();
for i, v in pairs(t) do
    print("key:" .. i .. ",value:" .. v)
end
  • cpp
void returnTable(){
    lua_newtable(L);// len1
    
    lua_pushstring(L, "100cm"); //len+1
    lua_pushstring(L, "200cm"); //len+1,只会使用这个值
    lua_setfield(L, -3, "height");// len-1, -3表示table在栈中的位置
    lua_pop(L,1); // 把100cm pop出去
    
    lua_pushstring(L, "age");// len2
    lua_pushstring(L, "120");// len3
    lua_rawset(L, -3);// 设置到栈顶的表中,并将其从栈上弹出。此时len1
    return 1; // 此时堆栈里面就剩下newtable了
    // 1表示该C函数将返回给Lua代码的返回值数量
}
void main(){
    lua_State* L = luaL_newstate();
    luaopen_base(L); // 注册了一些内置的基础函数:ipairs、pairs、print
    lua_register(L, "returnTable", returnTable); 
    luaL_dofile(L, "test.lua");
    lua_close(L);
}

遇到的一个报错

attempt to call a nil value (global 'pairs')

很明显,提示找不到pairs=nil,难道pairs不是lua的关键字?

是的,这是lua的内置基础函数

cpp调用lua的变量

一般我们不需要调用lua的函数,大部分情况都是lua调用c++的实现

  • lua
str="string"
int=100
  • cpp
lua_State* L = luaL_newstate();
luaL_dofile(L, "cppCallLuaVar.lua");
// get后,立刻读取,所以使用-1没问题
{
    // 读取全局变量,并将其放入栈顶
    lua_getglobal(L, "str");// stack len = 1
    // 从栈顶取出字符串数据
    cout << "str=" << luaL_checkstring(L,-1)  << endl;
    lua_getglobal(L, "int"); // stack len = 2
    cout << "int=" << luaL_checkinteger(L, -1) << endl;

    cout << "len=" << lua_gettop(L) << endl; // 此时栈的大小为2
}
lua_close(L);
lua_State* L = luaL_newstate();
luaL_dofile(L, "cppCallLuaVar.lua");
{
    lua_getglobal(L, "str");
    lua_getglobal(L, "int");
    // 和上边的代码不同的是,读取堆栈的方式不同,先get,后读取,所以必须使用1、2
    cout << "str=" << luaL_checkstring(L, 1) << endl;
    cout << "int=" << luaL_checkinteger(L, 2) << endl;
    cout << "len=" << lua_gettop(L) << endl; // 此时栈的大小为2
}
lua_close(L);

从以上2个例子中可以看出,lua_getglobal会依次将查找到的值放入栈顶,使用的时候需要栈的顺序

关于luaL_check*的api:

LUALIB_API lua_Integer luaL_checkinteger (lua_State *L, int arg) {
  int isnum;
  lua_Integer d = lua_tointegerx(L, arg, &isnum);
  if (l_unlikely(!isnum)) {
    interror(L, arg);
  }
  return d;
}
static void interror (lua_State *L, int arg) {
  if (lua_isnumber(L, arg))
    luaL_argerror(L, arg, "number has no integer representation");
  else
    tag_error(L, arg, LUA_TNUMBER);
}

lua_is*这类的函数可以判断栈中的数据类型,但是需要注意int->string的隐形转换。

cpp调用lua的table

  • lua
tbl = {
    name = "lua",
    100,
    {
        id = 10,
    }
}
  • cpp通过key获取值
lua_State* L = luaL_newstate();
luaL_dofile(L, "cppCallLuaTable.lua");

lua_getglobal(L, "tbl"); // stack len = 1
// 获取指定表中指定字段的值并将其推入 Lua 栈顶
lua_getfield(L, -1, "name");// stack len = 2, 此时tbl在栈顶,所以第二个参数是-1
cout << "tbl.name=" << luaL_checkstring(L, -1) << endl;

lua_close(L);
lua_State* L = luaL_newstate();
luaL_dofile(L, "cppCallLuaTable.lua");
lua_getglobal(L, "tbl"); // len=1

lua_pushstring(L, "11");// 这个是干扰项,len=2
lua_pushstring(L, "name");// len=3
// 从栈顶取出一个元素(name)并且返回把查找到的值压入栈顶
lua_gettable(L, 1); // len=3
cout << lua_tostring(L, -1);
    
lua_close(L);
  • cpp获取无序的值
lua_State* L = luaL_newstate();
luaL_dofile(L, "cppCallLuaTable.lua");
lua_getglobal(L, "tbl"); // len=1

// -1表示tbl在栈顶, 1表示取tbl[1]
lua_rawgeti(L, -1, 1);// len=2
// 将tbl[1]的值放在了栈顶,因为tbl[1]=100
cout << "tbl[1]=" << lua_tointeger(L, -1) << endl;

lua_pop(L,1); // 弹出tbl[1]的值,len=1

// 获取嵌套的table
lua_rawgeti(L,-1,2);// 此时栈顶是tbl,直接获取tbl[2]元素,len=2
lua_getfield(L,-1,"id");//将id的值放在栈顶,len=3
cout << lua_tointeger(L, -1);

lua_close(L);

cpp调用lua的函数

  • lua
function add(a, b)
    return a + b;
end
  • cpp
lua_State* L = luaL_newstate();
luaL_dofile(L, "cppCallLuaFunction.lua");

lua_getglobal(L, "add");// len=1
lua_pushnumber(L, 10);// len=2
lua_pushnumber(L, 20);// len=3

// 2表示参数数量,1表示返回值个数,0表示错误处理函数在栈中的索引值,压入结果前会弹出函数和参数
// 弹出函数地址和所有参数,并将返回值压入栈顶
int ret = lua_pcall(L, 2, 1, 0); // len=1
cout << "add result is: " << luaL_checkinteger(L, -1) << endl;

lua_close(L);