前言
c++访问lua中的全局函数,网上资料较多,这里不再赘述。但是,c++访问lua中类的成员函数,网上有参考价值的资料很少。访问类的成员函数,有2个关键点:
- 导出函数前,要先导出全局table;
- 调用函数时,第一个参数要传入self(也就是全局table);
接口封装
要封装一个C++调用Lua类函数的通用接口,需要考虑不定参的问题,所以压入参数部分的代码在调用接口时以匿名函数的形式实现。
- 定义一个动态参数入栈的接口
// 定义lua接口封装的传参
struct lua_State;
typedef void (*Action)(lua_State* pLua, va_list ap);
- 实现函数主体
/**
* @brife c++调用Lua类函数通用接口
* @param strFuncName lua类的成员函数名
* @param action 执行参数入栈的函数指针
* @param nArg 入栈参数的个数
* @param ... 入栈参数(可变)
*/
void CAudioVideoDemoDlg::CallLuaFunc(const std::string strFuncName, Action action, int nArg, ...)
{
// 先置空一下栈
lua_settop(m_luaState, 0);
// 获取全局的table,这里zegoSdk为已创建好的全局对象
lua_getglobal(m_luaState, "zegoSdk");
if (!lua_istable(m_luaState, -1))
{
MessageBox(L"lua文件出错,导出sdk失败", L"错误");
return;
}
// 压入方法名,并获取对于的方法(弹出zegoSdk.strFuncName)
lua_pushstring(m_luaState, strFuncName.c_str());
lua_gettable(m_luaState, -2);
lua_pushvalue(m_luaState, -2);
// 压入参数
if (action != nullptr || action != NULL)
{
va_list ap;
va_start(ap, nArg);
action(m_luaState, ap);
va_end(ap);
}
// 此处要特别注意,需要多传一个self,参数个数要+1
int iRet = lua_pcall(m_luaState, nArg + 1, 0, 0);
if (iRet != 0)
{
// 打印一下调用出错的信息
std::string strError = strFuncName + ":" + lua_tostring(m_luaState, -1);
MessageBoxA(nullptr, strError.data(), "Error", MB_OK);
}
}
注意:要调用变参相关的函数,需要引用#include <stdarg.h>
。
接口调用
- Lua接口原型
这里enumScenario为默认参数,调用时可传2个参数。
function M:InitSdk(iAppID, strAppSign, enumScenario)
-- 获取引擎对象
M.expressSdk = ZEGO_NS.ZegoExpressLuaSdk:GetInstance()
-- 构造参数
local zep = ZEGO_NS.ZegoEngineProfile:new()
zep.appID = iAppID
zep.appSign = strAppSign
zep.scenario = enumScenario or Enum_ZegoScenario.ZEGO_SCENARIO_GENERAL
-- 初始化SDK
M.expressSdk:InitSdk(zep)
end
- 函数调用
调用时根据传入参数的类型,做对应解析即可。
CallLuaFunc("InitSdk", [](lua_State* lua, va_list ap){
// 获取参数
unsigned int uAppId = va_arg(ap, unsigned int);
std::string strAppSign = va_arg(ap, char*);
// 将参数压缩到栈中
lua_pushnumber(lua, uAppId);
lua_pushstring(lua, strAppSign.data());
}, 2, ZEGO_APPID, ZEGO_APPSIGN);