c++调用Lua函数

3,037 阅读4分钟

  最近由于项目需要,需要在客户端中使用C++调用lua函数,所以简单写了篇关于c++和lua调用的文档,其中也有一些暂时没搞懂的东西,先留做记录。

实现原理

  比较简单,实际上就是通过lua栈为中介实现的,这里的栈对应的是lua_State对象。lua栈和普通堆栈一样,遵循先进后出原则,涉及操作简单来说就是出栈和入栈,这些操作支持的数据格式是多样的,eg:int,string等。

前期准备

使用时在windows系统下,用到lua51+vs2017。使用前请确保你具备以下内容:
1.lua源码及lua相关的库文件
  我这边用的是lua51.lib,在进行调用时会打开这个lib(lua的静态库、动态库生成方法自行搜索)。
2.调用用lua文件test.lua
3.测试用cpp文件addon_test.cpp
4.设置好的vs和目录结构
  项目建好之后,在vs界面中选择项目->项目属性->VC++目录->库目录,把lua的lib所在目录添加进去,应用即可。目录结构方面是为了让使用同样代码进行测试的朋友方便测试,我的目录结构大致如下:
├─addTest
│ │ addTest.vcxproj
│ │ addTest.vcxproj.filters
│ │ addTest.vcxproj.user
│ ├─cluaAddon
│ │ │ addon_test.cpp
│ │ │ test.lua
│ │ └─lua(下载的lua源文件,除去luac.c和lua.c)
│ │   ├─doc
│ │   └─src
│ └─Debug
└─Debug

调用示例

test.lua文件内容如下:

str = "it's str test."  
tb = {name = "shun", id = 123}  
function add (a, b)  
    return a + b
end
function print_test()
	print("print test.")
end

主要试验了几块内容:
1.变量内容传递
2.无参函数的调用
3.有参函数的调用

#include <iostream>  
#include <string.h>  
using namespace std;
// 包含头文件
extern "C"
{
#include "lua/src/lua.h"  
#include "lua/src/lualib.h"  
#include "lua/src/lauxlib.h"  
}
//#pragma comment(lib,"lua51.lib")
int main() {

	// 创建lua堆栈
	lua_State *L = luaL_newstate();
	if (L == NULL)
	{
		cout << "Creat Lua State Error !" << endl;
		return -1;
	}
	// 如需在终端输出打印信息,库是必须加载的,否则看不到lua的print信息
	luaL_openlibs(L);
	// 加载lua脚本
	int ret = luaL_dofile(L, "D:\\vsProject\\addTest\\addTest\\cluaAddon\\test.lua");
	if (ret)
	{
		cout << "Lua doFile Error !" << endl;
		return -1;
	}
	// 调用.lua中写好的内容
	// 读取变量内容
	lua_getglobal(L, "str");	//变量名为str
	cout << "str is " << lua_tostring(L, -1) << endl;
	lua_getglobal(L, "tb");		//获取table中对应name下的value
	lua_getfield(L, -1, "name");
	cout << "name in tb is" << lua_tostring(L, -1) << endl;
	// 无参函数
	lua_getglobal(L, "print_test");   //函数名为ruler
	lua_pcall(L, 0, 0, 0);          //用保护模式调用lua函数,入参个数为0、出参个数为0、无自定义错误处理

	// 有参函数
	lua_getglobal(L, "add");     //函数名为add
	lua_pushnumber(L, 1);  //第一个入参
	lua_pushnumber(L, 2);  //第二个入参  
	lua_pcall(L, 2, 1, 0);          //函数有两个入参,一个出参,所以函数形式为add(a,b)
	//5、获得返回值,单回值情况下调用完成后lua会把结果放到栈顶,多返回值时,按照规则存放,具体查API
	
	if (lua_isnumber(L, -1))
	{
		cout << "the result is :" << lua_tonumber(L, -1) << endl;
	}
	//6、销毁lua环境
	lua_close(L);
	return 0;
}

可以直接看上面代码,注释里提到了流程,也可以看下面的分步分析。
  C++调用lua文件中的函数主要分为以下几个部分:
1.包含头文件
  主要是包含三个文件lua.h、lualib.h、lauxlib.h,当然你也可以直接#include lua.hpp,实际上是等价的,因为lua.hpp的内容是这样的:

// lua.hpp
// Lua header files for C++
// <<extern "C">> not supplied automatically because Lua also compiles as C++

extern "C" {
#include "lua.h"
#include "lualib.h"
#include "lauxlib.h"
}

哈哈
2.新建lua堆栈

lua_State *L = luaL_newstate();  

3.加载必要库和lua文件

luaL_openlibs(L);//这个是为了使得lua的打印等输出可以在cpp代码调试时显示出来  
int ret = luaL_dofile(L, "D:\\vsProject\\addTest\\addTest\\cluaAddon\\test.lua");
	if (ret)
	{
		cout << "Lua doFile Error !" << endl;
		return -1;
	}

  luaL_dofile调用的时候用的是绝对路径,直接使用test.lua会加载失败,暂时没有找到问题所在,后续解决了的话再来添加解决方案。
4.调用.lua中的函数
  分为有参和无参两种,主要通过lua_pcall等关键函数  

// 无参函数
	lua_getglobal(L, "print_test");   //函数名为ruler
	lua_pcall(L, 0, 0, 0);          //用保护模式调用lua函数,入参个数为0、出参个数为0、无自定义错误处理

	// 有参函数
	lua_getglobal(L, "add");     //函数名为add
	lua_pushnumber(L, 1);  //第一个入参
	lua_pushnumber(L, 2);  //第二个入参  
	lua_pcall(L, 2, 1, 0); //函数有两个入参,一个出参,所以函数形式为add(a,b)
	cout << "the result is :" << lua_tonumber(L, -1) << endl; //有返回值的函数的结果被存在栈中,通过lua_tonumbner等取出

  主要做的就是,先通过getglobal把加载相关内容,然后向lua堆栈中推函数的参数(当然没有的话就不用push)。完事后调用lua_pcall指明函数的入参数目,出参数目和错误处理(错误处理暂未涉及,需要的可以自行查看源码),最后将结果取出。
5.获取栈中数据
  对函数结果的取出在4.中已经写明了,当然也可以直接获取.lua中的变量内容,例如获取string类型的str和table类型的tb中的某个值:

// 读取变量内容
	lua_getglobal(L, "str");	//变量名为str
	cout << "str is " << lua_tostring(L, -1) << endl;
	lua_getglobal(L, "tb");		//获取table中对应name下的value
	lua_getfield(L, -1, "name");
	cout << "name in tb is" << lua_tostring(L, -1) << endl;

6.调用完毕后,销毁lua堆栈

lua_close(L);

  至此,所有流程走完。

参考链接

gamedevgeek.com/2006/05/04/…  Lua Tutorials
blog.csdn.net/yxli_2018/a…
blog.csdn.net/weixin_3388…