最近由于项目需要,需要在客户端中使用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…