本文介绍如何使用 Lua 的原生 C API 与 C++ 进行数据交互。Lua 和 C/C++ 之间的通信主要依赖 lua_State 这个虚拟栈:所有数据交换,包括调用函数、读取变量、传递参数和取回返回值,都是通过向这个栈压入和弹出数据来完成的。

Lua 栈的基本规则

在开始写代码前,先弄清楚 Lua 栈几个容易踩坑的特性。

  • Lua 栈的 index 是自下而上递增的,例如栈中依次有 6 个元素时,从底到顶的 index 为 1 2 3 4 5 6
  • Lua 栈的 index 是"双向循环"的:从栈顶往下看,可以用正数也可以用负数。例如某次状态下,从上到下的 index 依次是 3 2 1 0 -1 -2 -3,对应的栈中的值可能是:
    1
    2
    3
    x
    1
    2
    3
    
  • 当 Lua 函数返回多个值时,这些返回值会按顺序依次入栈。比如函数 return a, b, c,调用方取栈顶及往下依次得到的关系是 a = 3, b = 2, c = 1,也就是第一个返回值最先入栈、最靠近栈底。

栈的清理:lua_pop

函数调用完成后,通常需要手动清理返回值占用的栈空间,使用 lua_pop(x),其中 x 表示要弹出的元素个数。一般情况下函数只有一个返回值时使用 lua_pop(1)

需要注意:调用 Lua 函数时,参数入栈过程并不会自动保持堆栈平衡,函数执行结束后参数槽位已经由 Lua 内部清理完毕,调用方需要处理的只是返回值占用的那部分栈空间。

C++ 读取 Lua 数据的完整示例

下面是一段 C++ 调用 Lua 函数并读取全局变量的示例代码。它展示了 lua_getgloballua_pushnumberlua_calllua_tonumberlua_gettop 以及 lua_pop 这些核心 API 的典型用法。

extern "C"{
#include "src/lualib.h"
#include "src/lauxlib.h"
#include "src/lua.h"

}

#include "iostream"
using namespace std;

lua_State*l;


int get_sum(int x, int y)
{
	int sum=0;

	lua_getglobal(l, "get_sum");/*调用函数*/

	lua_pushnumber(l, x);
	lua_pushnumber(l, y);

	lua_call(l, 2, 3);/*参数2个,返回值3个*/

	cout << "top is  " << lua_gettop(l) << endl;
	cout << lua_tonumber(l, lua_gettop(l) - 0) << endl;
	cout << lua_tonumber(l, lua_gettop(l) - 1) << endl;
	cout << lua_tonumber(l, lua_gettop(l) - 2) << endl;
	cout << lua_tonumber(l, lua_gettop(l) - 2) << endl;
	cout << lua_tonumber(l, lua_gettop(l) - 3) << endl;
	cout << lua_tonumber(l, lua_gettop(l) - 4) << endl;
	cout << lua_tonumber(l, lua_gettop(l) - 5) << endl;
	cout << lua_tonumber(l, lua_gettop(l) - 6) << endl;
	cout << lua_tonumber(l, lua_gettop(l) - 7) << endl;
	cout << lua_tonumber(l, lua_gettop(l) - 8) << endl;
	cout << lua_tonumber(l, lua_gettop(l) - 9) << endl;


	lua_pop(l, 3);/*function返回了3个值*/


	cout << "\n\n" << endl;

	lua_getglobal(l, "b");/*获取变量 压入栈中*/
	cout <<"b=" <<lua_tonumber(l, lua_gettop(l)/*1*/ ) << endl;

	lua_getglobal(l, "a");/*获取变量 压入栈中*/
	cout << "a=" << lua_tonumber(l, lua_gettop(l)/*1*/) << endl;

	lua_getglobal(l, "c");/*获取变量 压入栈中*/
	cout << "c=" << lua_tonumber(l, lua_gettop(l)/*1*/) << endl;

	lua_pop(l, 3);/*清除栈*/
	cout << "top is" << lua_gettop(l) << endl;


	return sum;
}





int main()
{
	l = lua_open();

	luaL_openlibs(l);


	luaL_dofile(l, "a.lua");

	//cout << get_sum(1, 2) << endl;

	get_sum(1, 2);

	lua_close(l);

	system("pause");
	return 0;
}

配套的 Lua 脚本 a.lua

C++ 这端通过 luaL_dofile(l, "a.lua") 加载的 Lua 文件内容如下,里面定义了三个全局变量和一个函数 get_sum,返回三个值。

a=10;
b=11;
c=12;
function get_sum(arg_1,arg_2)
return arg_1+arg_2,"100","200";
end