暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

C++与Lua交互 使用模板特化封装

林元皓 2021-10-18
1014

当前 Lua 的版本已经是 Lua 5.4.3 了。创建工程使用的 Lua 版本是:Lua 5.1.5。

版本比较老,原因比较复杂,因为后续文章使用的 tolua++ 和 luaJIT 对 lua 的支持都停留在 5.1 的版本。

但是不用担心,在此文章使用了一些比较新而且好用的函数,并且将这些函数手动移植到了 Lua 5.1.5 。

VS 2017 中搭建 Lua 5.1

创建静态链接库的空工程、添加lua代码文件。

修改工程配置:


lua_project_lua_setting


lua_project_lua_setting


编译工程,生成 lua.lib。

C++ 对原生 Lua 的处理

在封装代码之前,我们先简单的从原生的代码开始。先对 Lua 有个简单的理解。

hello.lua

str = "I am so cool"  
tbl = {name = "shun", id = 20114442}
function add(a,b)
return a + b + math.pi
end

main.cpp

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




// 下面的代码 是处理 原生的 lua脚本的
using namespace std;


void main()
{
//1.创建Lua状态
lua_State *L = luaL_newstate();
if (L == NULL)
{
return;
}


//2.加载Lua文件
int bRet = luaL_loadfile(L, "E:\\hello.lua");
if (bRet)
{
cout << "load file error" << endl;
return;
}


//3.运行Lua文件
bRet = lua_pcall(L, 0, 0, 0);
if (bRet)
{
cout << "pcall error" << endl;
return;
}


//4.读取变量
lua_getglobal(L, "str");
string str = lua_tostring(L, -1);
cout << "str = " << str.c_str() << endl; //str = I am so cool~


//5.读取table
lua_getglobal(L, "tbl");
lua_getfield(L, -1, "name");
str = lua_tostring(L, -1);
cout << "tbl:name = " << str.c_str() << endl; //tbl:name = shun


//6.读取函数
lua_getglobal(L, "add"); // 获取函数,压入栈中
lua_pushnumber(L, 10); // 压入第一个参数
lua_pushnumber(L, 20); // 压入第二个参数
int iRet = lua_pcall(L, 2, 1, 0);// 调用函数,调用完成以后,会将返回值压入栈中,2表示参数个数,1表示返回结果个数。
if (iRet) // 调用出错
{
const char *pErrorMsg = lua_tostring(L, -1);
cout << pErrorMsg << endl;
lua_close(L);
return;
}
if (lua_isnumber(L, -1)) //取值输出
{
double fValue = lua_tonumber(L, -1);
cout << "Result is " << fValue << endl;
}


system("pause");
//至此,栈中的情况是:
//=================== 栈顶 ===================
// 索引 类型 值
// 4 int:30
// 3 string:shun
// 2 table: tbl
// 1 string: I am so cool~
//=================== 栈底 ===================


//7.关闭state
lua_close(L);
return;
}

使用 C++ 模板封装 Lua

配置 Lua 环境、植入新版本的 Lua 库函数

将 Lua 的头文件和编译的库文件,添加到工程文件中。

我们需要植入一些先版本的 Lua 库函数。因为它们比较好用~ 可以更方便的让 C++ 和 Lua 的相互交互。

创建一个 LuaEx.hpp 文件,包含头文件,并植入函数:

/***************************************************************************************************************************************************
vic.MINg 2021/10/11
        lua 函数扩展,由于使用 lua 版本 5.1.5,一些比较有用的函数 没有,这里将这些函数 扩展到这里,也可以了解 lua 的实现
**************************************************************************************************************************************************/


#pragma once


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


#include "lstate.h"
}




#define LUAI_UACINT LUA_INTEGER








inline const char *luaL_tolstring(lua_State *L, int idx, size_t *len) {
if (luaL_callmeta(L, idx, "__tostring")) { /* metafield? */
if (!lua_isstring(L, -1))
luaL_error(L, "'__tostring' must return a string");
}
else {
switch (lua_type(L, idx)) {
case LUA_TNUMBER: {
// if (lua_isinteger(L, idx))
// lua_pushfstring(L, "%I", (LUAI_UACINT)lua_tointeger(L, idx));
// else
lua_pushfstring(L, "%f", (LUAI_UACNUMBER)lua_tonumber(L, idx));
break;
}
case LUA_TSTRING:
lua_pushvalue(L, idx);
break;
case LUA_TBOOLEAN:
lua_pushstring(L, (lua_toboolean(L, idx) ? "true" : "false"));
break;
case LUA_TNIL:
lua_pushliteral(L, "nil");
break;
default: {
int tt = luaL_getmetafield(L, idx, "__name"); /* try name */
const char *kind = (tt == LUA_TSTRING) ? lua_tostring(L, -1) :
luaL_typename(L, idx);
lua_pushfstring(L, "%s: %p", kind, lua_topointer(L, idx));
if (tt != LUA_TNIL)
lua_remove(L, -2); /* remove '__name' */
break;
}
}
}
return lua_tolstring(L, -1, len);
}


inline void luaL_setfuncs(lua_State *L, const luaL_Reg *l, int nup)
{
luaL_checkstack(L, nup, "too many upvalues");
for (; l->name != NULL; l++) { /* fill the table with given functions */
int i;
for (i = 0; i < nup; i++) /* copy upvalues to the top */
lua_pushvalue(L, -nup);
lua_pushcclosure(L, l->func, nup); /* closure with those upvalues */
lua_setfield(L, -(nup + 2), l->name);
}
lua_pop(L, nup); /* remove upvalues */


}


#define lua_newtable(L) lua_createtable(L, 0, 0)


#define luaL_newlibtable(L,l) \
lua_createtable(L, 0, sizeof(l)/sizeof((l)[0]) - 1)


#define luaL_newlib(L,l) \
(luaL_newlibtable(L,l), luaL_setfuncs(L,l,0))




#define ispseudo(i) ((i) <= LUA_REGISTRYINDEX)
#ifndef cast
#define cast(t, exp) ((t)(exp))
#endif


#define cast_int(i) cast(int, (i))
#define LUA_LOADED_TABLE "_LOADED"
struct lua_State;


inline int lua_absindex(lua_State *L, int idx) {
return (idx > 0 || ispseudo(idx))
? idx
: cast_int(L->top - L->ci->func) + idx;
}




inline int luaL_getsubtable(lua_State *L, int idx, const char *fname)
{
lua_getfield(L, idx, fname);
lua_pop(L, 1); /* remove previous result */
idx = lua_absindex(L, idx);
lua_newtable(L);
lua_pushvalue(L, -1); /* copy to be left at top */
lua_setfield(L, idx, fname); /* assign new table to field */
return 0; /* false, because did not find table there */
}


inline void luaL_requiref(lua_State *L, const char *modname,
lua_CFunction openf, int glb) {
luaL_getsubtable(L, LUA_REGISTRYINDEX, LUA_LOADED_TABLE);
lua_getfield(L, -1, modname); /* LOADED[modname] */
if (!lua_toboolean(L, -1)) { /* package not already loaded? */
lua_pop(L, 1); /* remove field */
lua_pushcfunction(L, openf);
lua_pushstring(L, modname); /* argument to open function */
lua_call(L, 1, 1); /* call 'openf' to open module */
lua_pushvalue(L, -1); /* make copy of module (call result) */
lua_setfield(L, -3, modname); /* LOADED[modname] = module */
}
lua_remove(L, -2); /* remove LOADED table */
if (glb) {
lua_pushvalue(L, -1); /* copy of module */
lua_setglobal(L, modname); /* _G[modname] = module */
}
}

模板封装,C++ 调用 Lua 函数

通过封装实现,C++ 调用 Lua 函数。存在 无返回值、一个返回值、多个返回值情况。

LuaHelp.h,面向过程 模板封装。

/*************************************************
vic.MINg 2021/10/11
使用 模板 lua 的封装
************************************************/


#pragma once




#include <iostream>
#include <stdexcept>
#include <vector>
#include "LuaEx.hpp"


// 面向过程 封装
namespace LuaHelp {


union LH_LuaData {
bool lh_bool_data;
int lh_int_data;
double lh_number_data;
char* lh_string_data;
};


enum LH_LuaType {
LH_Lua_Bool = 0,
LH_Lua_Int,
LH_Lua_Number,
LH_Lua_String
};


///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// 全局变量 设置
inline bool GetGlobal(lua_State* lua_s, const char* g_name)
{
//if (lua_gettop(lua_s) < 1)
// return false;
lua_getglobal(lua_s, g_name);
return true;
}
inline bool SetGlobal(lua_State* lua_s, const char* g_name)
{
if (lua_gettop(lua_s) < 1)
return false;
lua_setglobal(lua_s, g_name);
return true;
}


///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// 打印 堆 信息
inline void PrintStack_Info(lua_State* lua_s,const char* tag = NULL)
{
int lua_size = lua_gettop(lua_s);


if (tag)
std::cout << "***************************** " << tag << " *****************************" << std::endl;
printf("lua_State:0x%x ! \n", lua_s);
printf("lua_Size:%d ! \n", lua_size);
for (int i = 1; i <= lua_size; i++)
{
printf("[%d](%s):%s\n", i, luaL_typename(lua_s, i), luaL_tolstring(lua_s, i, NULL));
lua_pop(lua_s, 1);
}
}


inline void PrintStack_Size(lua_State* lua_s, const char* tag = NULL)
{
int lua_size = lua_gettop(lua_s);


if (tag)
std::cout << "***************************** " << tag << " *****************************" << std::endl;


std::cout << "lua_Size:" << lua_size << std::endl;
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// 特化 LuaCheck
template<typename _Ty>
bool LuaCheck(lua_State* lua_s, int index = -1);
/// 检查是否是字符串
template<> inline bool LuaCheck<char*>(lua_State* lua_s, int index)
{
return lua_isstring(lua_s, index) == 0 ? false : true;
}
template<> inline bool LuaCheck<std::string>(lua_State* lua_s, int index)
{
return lua_isstring(lua_s, index) == 0 ? false : true;
}
/// 检查是否是整数
template<> inline bool LuaCheck<int>(lua_State* lua_s, int index)
{
return lua_isnumber(lua_s, index) == 0 ? false : true;
}
/// 检查是否是浮点数
template<> inline bool LuaCheck<double>(lua_State* lua_s, int index)
{
return lua_isnumber(lua_s, index) == 0 ? false : true;
}
template<> inline bool LuaCheck<float>(lua_State* lua_s, int index)
{
return lua_isnumber(lua_s, index) == 0 ? false : true;
}
/// 检查是否是布尔值
template<> inline bool LuaCheck<bool>(lua_State* lua_s, int index)
{
return lua_isboolean(lua_s, index) == 0 ? false : true;
}


///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// 特化 LuaGet
template<typename _Ty>
_Ty LuaGet(lua_State* lua_s, int index = -1, bool _pop = false); // _pop 表示 是否弹出
/// 获取字符串
template<> inline char* LuaGet<char*>(lua_State* lua_s, int index, bool _pop)
{
if (!LuaCheck<char*>(lua_s, index))
{
throw std::logic_error("LuaHelp::LuaCheck<char*> Error!");
}
char* tmp = (char*)lua_tostring(lua_s, index);
if (_pop)
{
lua_remove(lua_s, index);
}
return tmp;
}


template<> inline std::string LuaGet<std::string>(lua_State*lua_s, int index, bool _pop)
{
if (!LuaCheck<char*>(lua_s, index))
{
throw std::logic_error("LuaHelp::LuaCheck<std::string> Error!");
}


std::string tmpstr = (char*)lua_tostring(lua_s, index);
if (_pop)
{
lua_remove(lua_s, index);
}
return std::move(tmpstr);
}
/// 获取整型
template<> inline int LuaGet<int>(lua_State* lua_s, int index, bool _pop)
{
if (!LuaCheck<int>(lua_s, index))
{
throw std::logic_error("LuaHelp::LuaCheck<int> Error!");
}
int tmp = lua_tointeger(lua_s, index);
if (_pop)
{
lua_remove(lua_s, index);
}
return tmp;
}
/// 获取浮点数
template<> inline double LuaGet<double>(lua_State* lua_s, int index, bool _pop)
{
if (!LuaCheck<double>(lua_s, index))
{
throw std::logic_error("LuaHelp::LuaCheck<double> Error!");
}


double tmp = lua_tonumber(lua_s, index);
if (_pop)
{
lua_remove(lua_s, index);
}
return tmp;
}
template<> inline float LuaGet<float>(lua_State*lua_s, int index, bool _pop)
{
if (!LuaCheck<float>(lua_s, index))
{
throw std::logic_error("LuaHelp::LuaCheck<float> Error!");
}


float tmp = (float)lua_tonumber(lua_s, index);
if (_pop)
{
lua_remove(lua_s, index);
}
return tmp;
}
/// 获取布尔值
template<> inline bool LuaGet<bool>(lua_State*lua_s, int index, bool _pop)
{
if (!LuaCheck<bool>(lua_s, index))
{
throw std::logic_error("LuaHelp::LuaCheck<bool> Error!");
}


bool tmp = (bool)lua_toboolean(lua_s, index);
if (_pop)
{
lua_remove(lua_s, index);
}
return tmp;
}


///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// 特化 LuaPush
template<typename _TY>
void LuaPush(lua_State* lua_s, _TY);


/// 压栈字符串
template<> inline void LuaPush<char*>(lua_State* lua_s, char* value)
{
lua_pushstring(lua_s, value);
}
template<> inline void LuaPush<const char*>(lua_State* lua_s, const char* value)
{
lua_pushstring(lua_s, value);
}
/// 压栈整型
template<> inline void LuaPush<int>(lua_State* lua_s, int value)
{
lua_pushnumber(lua_s, value);
}
/// 压栈浮点数
template<> inline void LuaPush<double>(lua_State* lua_s, double value)
{
lua_pushnumber(lua_s, value);
}
template<> inline void LuaPush<float>(lua_State* lua_s, float value)
{
lua_pushnumber(lua_s, value);
}
/// 压栈布尔值
template<> inline void LuaPush<bool>(lua_State* lua_s, bool value)
{
lua_pushboolean(lua_s, value);
}
/// 压栈函数
template<> inline void LuaPush<lua_CFunction>(lua_State*lua_g, lua_CFunction fun)
{
lua_pushcfunction(lua_g, fun);
}


///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// 特化 全局变量
/// 特化 获取全局变量
template<typename _Ty>
_Ty LuaGetGlobal(lua_State* lua_s, char*global, bool _pop = true)
{
if (GetGlobal(lua_s, global) == false)
{
throw std::logic_error("LuaHelp::LuaGetGlobal Error!");
}
_Ty tmp = LuaGet<_Ty>(lua_s, -1, _pop);
return tmp;
}


/// 特化 设置全局变量
template<typename _Ty>
bool LuaSetGlobal(lua_State* lua_s, char* global, _Ty value)
{
LuaPush<_Ty>(lua_s, value);
if (SetGlobal(lua_s, global) == false)
{
throw std::logic_error("LuaHelp::LuaSetGlobal Error!");
return false;
}
return true;
}


///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// 调用 lua 函数模板
// __CallLua 压栈 无参数
inline void __CallLua(lua_State* lua_g, int& argSize) {


}


template<typename _FirstArg, typename... _Args>
void __CallLua(lua_State* lua_g, int& argSize, _FirstArg arg1, _Args...args) //压入参数
{
argSize++;
LuaPush<_FirstArg>(lua_g, arg1);
__CallLua(lua_g, argSize, args...); // 递归 压栈调用
}


template<typename _Ret, typename... _Args>
_Ret CallLuaFuction_return_oneValue(lua_State* lua_g, const char* FunctionName, _Args...args) // 调用lua函数, 一个返回值
{
if (GetGlobal(lua_g, FunctionName))
{
int argSize = 0; // 参数个数
__CallLua(lua_g, argSize, args...);
PrintStack_Info(lua_g, "CallLuaFuction_return_oneValue");
if (lua_pcall(lua_g, argSize, 1, 0) != 0)
{
lua_pop(lua_g, 1); // 弹出错误值
throw std::logic_error("LuaHelp::CallLuaFuction_return_oneValue Error!");
}
_Ret value = LuaGet<_Ret>(lua_g);
lua_pop(lua_g, 1);
return value;
}


throw std::logic_error("LuaHelp::CallLuaFuction_return_oneValue No Function!");
}


template< typename... _Args>
void CallLuaFuction_return_void(lua_State* lua_g,const char* FunctionName, _Args...args)
{
if (GetGlobal(lua_g, FunctionName))
{
int argSize = 0; // 参数个数
__CallLua(lua_g, argSize, args...);
PrintStack_Info(lua_g, "CallLuaFuction_return_void");
if (lua_pcall(lua_g, sizeof...(args), 0, 0) != 0)
{
lua_pop(lua_g, 1); // 弹出错误值
throw std::logic_error("LuaHelp::CallLuaFuction_return_void Error!");
}
return;
}


throw std::logic_error("LuaHelp::CallLuaFuction_return_void No Function!");
}


template< typename... _Args>
void CallLuaFuction_return_multValues(lua_State* lua_g, const char* FunctionName, _Args...args)
{
if (GetGlobal(lua_g, FunctionName))
{
int argSize = 0; // 参数个数
__CallLua(lua_g, argSize, args...);
PrintStack_Info(lua_g, "CallLuaFuction_return_multValues");
if (lua_pcall(lua_g, sizeof...(args), -1, 0) != 0)
{
lua_pop(lua_g, 1); //弹出错误值
throw std::logic_error("LuaHelp::CallLuaFuction_return_multValues Error!");
}
return;
}


throw std::logic_error("LuaHelp::CallLuaFuction_return_multValues No Function!");
}


template< typename... _Args>
std::vector<LH_LuaData> GetResult(lua_State* lua_g, _Args...args)
{
int _argsize = sizeof...(args);
int argarray[sizeof...(args)] = { args... };
std::cout << _argsize << std::endl;
std::vector<LH_LuaData> __array = {};
int __index = -_argsize + 1;
for (int v : argarray)
{
LH_LuaData tmp = { 0 };
std::cout << "v:" << v << std::endl;
switch (v)
{
case LH_LuaType::LH_Lua_Bool:
tmp.lh_bool_data = LuaGet<bool>(lua_g, -1 + __index);
break;
case LH_LuaType::LH_Lua_Int:
tmp.lh_int_data = LuaGet<int>(lua_g, -1 + __index);
__array.push_back(tmp);
break;
case LH_LuaType::LH_Lua_Number:
tmp.lh_number_data = LuaGet<double>(lua_g, -1 + __index);
__array.push_back(tmp);
break;
case LH_LuaType::LH_Lua_String:
tmp.lh_string_data = LuaGet<char*>(lua_g, -1 + __index);
__array.push_back(tmp);
break;
default:
throw std::logic_error("LuaHelp::GetResult LH_LuaType Error!");
break;
}
__index++;
}


lua_pop(lua_g, _argsize);
return __array;
}




};

LuaScript.h,面向对象 模板封装。

/*************************************************
vic.MINg 2021/10/11
使用 模板 lua 的封装
************************************************/


#pragma once


#include <iostream>
#include <stdexcept>
#include <vector>
#include "LuaHelper.h"


// 面向对象 封装
class LuaScript
{
public:
LuaScript():m_lua(NULL) {}
~LuaScript() {
if (m_lua) lua_close(m_lua);
}


bool CreateLuaState()
{
if (m_lua) return true;
m_lua = luaL_newstate();
if (m_lua == NULL)
return false;
luaL_openlibs(m_lua);
return true;
}


void PrintStack_Info(const char* tag = NULL)
{
LuaHelp::PrintStack_Info(m_lua, tag);
}


void PrintStack_Size(const char* tag = NULL)
{
LuaHelp::PrintStack_Size(m_lua, tag);
}


bool DoFile(const char* file_name) {
if (file_name == NULL) return false;
if (luaL_dofile(m_lua, file_name) != 0)
{
char* error_string = (char*)lua_tostring(m_lua, -1);
throw std::logic_error(error_string);
lua_pop(m_lua, 1);
return false;
}


return true;
}


bool DoString(char* lua_string) {
if (lua_string == NULL)
{
return false;
}
if (luaL_dostring(m_lua, lua_string) != 0)
{
char* error_string = (char*)lua_tostring(m_lua, -1);
throw std::logic_error(error_string);
lua_pop(m_lua, 1);
return false;
}


return true;
}


inline bool GetGlobal(const char* name)
{
return LuaHelp::GetGlobal(m_lua, name);
}
inline bool SetGlobal(const char* name)
{
return LuaHelp::SetGlobal(m_lua, name);
}


inline lua_State* GetLuaState()
{
return m_lua;
}


template<typename _Ty>
inline bool LuaCheck(int index = -1)
{
return LuaHelp::LuaCheck<_Ty>(index);
}


template<typename _Ty>
inline _Ty LuaGet(int index = -1, bool _pop = false)
{
return LuaHelp::LuaGet<_Ty>(GetLuaState(), index, _pop);
}


template<typename _Type>
inline void LuaPush(_Type data)
{
LuaHelp::LuaPush<_Type>(GetLuaState(), data);
}


template<typename _Ty>
inline _Ty LuaGetGlobal(char*global, bool _pop = true)
{
return LuaHelp::LuaGetGlobal<_Ty>(GetLuaState(), global, _pop);
}


template<typename _Ty>
inline bool LuaSetGlobal(_Ty data, char*global)
{
return LuaHelp::LuaSetGlobal<_Ty>(m_lua, data, global);
}


template<typename _Ret, typename... _Args>
inline _Ret CallLuaFuction_return_oneValue(const char* FunctionName, _Args...args)
{
return LuaHelp::CallLuaFuction_return_oneValue<_Ret>(GetLuaState(), FunctionName, args...);
}


template< typename... _Args>
inline void CallLuaFuction_return_void(const char* FunctionName, _Args...args)
{
LuaHelp::CallLuaFuction_return_void(GetLuaState(), FunctionName, args...);
}


template< typename... _Args>
inline LuaScript& CallLuaFuction_return_multValues(const char* FunctionName, _Args...args)
{
LuaHelp::CallLuaFuction_return_multValues(GetLuaState(), FunctionName, args...);
return *this;
}


template< typename... _Args>
std::vector<LuaHelp::LH_LuaData> GetResult(_Args...args)
{
auto tmp = std::move(LuaHelp::GetResult(m_lua, args...));
return tmp;
}


private:
lua_State * m_lua;
};

函数封装, Lua 调用 C++ 函数

Lua 调用 C++ 函数时,C++注册函数的五种方法。

LuaRegFuncs.h

#pragma once


#include "../include/LuaScript.h"
#include "../include/LuaEx.hpp"


class LuaRegFuncs : public LuaScript
{
public:
void RegFuncs();
};

LuaRegFuncs.cpp

#include "LuaRegFuncs.h"
#include <functional>


int show_luaproject_info1(lua_State *L)
{
std::cout << __FUNCTION__ << std::endl;
printf("Show LuaProject C++ Info 1.\n");
return 0;
}


int show_luaproject_info2(lua_State *L)
{
std::cout << __FUNCTION__ << std::endl;
printf("Show LuaProject C++ Info 2.\n");
return 0;
}


int show_luaproject_openlib(lua_State *L)
{
std::cout << __FUNCTION__ << std::endl;
struct luaL_Reg regfuc[]{
{ "show_luaproject_info1", show_luaproject_info1 },
{ "show_luaproject_info2", show_luaproject_info2 },
{ NULL,NULL }
};
luaL_newlib(L, regfuc);
return 1;
}


void LuaRegFuncs::RegFuncs()
{
struct luaL_Reg regfuc[]{
{ "show_luaproject_info1", show_luaproject_info1 },
{ "show_luaproject_info2", show_luaproject_info2 },
{ NULL,NULL }
};


std::function<void()> lua_pushcfunction_RegFuncs = [&]()
{
lua_pushcfunction(GetLuaState(), &show_luaproject_info1);
SetGlobal("show_luaproject_info1");


lua_pushcfunction(GetLuaState(), &show_luaproject_info2);
SetGlobal("show_luaproject_info2");
};


std::function<void()> luaL_newlib_RegFuncs = [&]()
{
luaL_newlib(GetLuaState(), regfuc);
SetGlobal("luaL_newlib_RegFuncs");
};


std::function<void()> lua_newtable_luaL_setfuncs_RegFuncs = [&]()
{
lua_newtable(GetLuaState());
luaL_setfuncs(GetLuaState(), regfuc, 0);
SetGlobal("lua_newtable_luaL_setfuncs_RegFuncs");
};


std::function<void()> lua_table_reg_RegFuncs = [&]()
{
// 创建一个Table 压入键值对
lua_newtable(GetLuaState());


for (auto& v : regfuc)
{
if (v.func == NULL || v.name == NULL)
{
break;
}
LuaPush<const char*>(v.name);
LuaPush<lua_CFunction>(v.func);
lua_rawset(GetLuaState(), -3);
}
SetGlobal("lua_table_reg_RegFuncs");
};




std::function<void()> luaL_requiref_RegFuncs = [&]()
{
luaL_requiref(GetLuaState(), "luaL_requiref_RegFuncs", show_luaproject_openlib, 1);
lua_pop(GetLuaState(), 1);
};


if (CreateLuaState())
{
lua_pushcfunction_RegFuncs();
luaL_newlib_RegFuncs();
lua_newtable_luaL_setfuncs_RegFuncs();
lua_table_reg_RegFuncs();
luaL_requiref_RegFuncs();
}
}

类封装,Lua 调用 C++ 类

这里有个 Teacher 类,这里实现对该类的封装,看看如何 在 Lua 中调用 C++ 类。

Teacher.h

#pragma once


#include <iostream>
#include <string>


class Teacher
{
public:
//构造/析构函数
Teacher();
~Teacher();


//get/set函数
std::string get_name();
void set_name(std::string name);
unsigned get_age();
void set_age(unsigned age);


//打印函数
void print();


private:
std::string _name;
unsigned _age;
};

Teacher.cpp

#include "Teacher.h"


Teacher::Teacher()
:_name("Empty"),
_age(0)
{
std::cout << "Teacher Constructor" << std::endl;
}


Teacher::~Teacher()
{
std::cout << "Teacher Destructor" << std::endl;
}


std::string Teacher::get_name()
{
return _name;
}


void Teacher::set_name(std::string name)
{
_name = name;
}


unsigned Teacher::get_age()
{
return _age;
}


void Teacher::set_age(unsigned age)
{
_age = age;
}


void Teacher::print()
{
std::cout << "name :" << _name << " age : " << _age << std::endl;
}

编写对 Teacher 的封装代码:

TeacherRegFuncs.h

#pragma once


#include "LuaEx.hpp"
#include "Teacher.h"




//------定义相关的全局函数------
//创建对象
int lua_create_new_teacher(lua_State* L);


//get/set函数
int lua_get_name(lua_State* L);
int lua_set_name(lua_State* L);
int lua_get_age(lua_State* L);
int lua_set_age(lua_State* L);


//打印函数
int lua_print(lua_State* L);


//转换为字符串函数
int lua_teacher2string(lua_State* L);


//自动GC
int lua_auto_gc(lua_State* L);


//------注册全局函数供Lua使用------
//构造函数
static const luaL_Reg lua_reg_teacher_constructor_funcs[] = {
{ "create", lua_create_new_teacher },
{ NULL, NULL }
};


//成员操作函数
static const luaL_Reg lua_reg_teacher_member_funcs[] = {
{ "get_name", lua_get_name },
{ "set_name", lua_set_name },
{ "get_age", lua_get_age },
{ "set_age", lua_set_age },
{ "print", lua_print },
{ "__gc", lua_auto_gc }, //注册Lua内部函数__gc
{ "__tostring", lua_teacher2string },
{ NULL, NULL },c++
};


int luaopen_teacher_libs(lua_State* L);

TeacherRegFuncs.cpp

#include "TeacherRegFuncs.h"


int lua_create_new_teacher(lua_State* L)
{
//创建一个对象指针放到stack里,返回给Lua中使用,userdata的位置-1
Teacher** t = (Teacher**)lua_newuserdata(L, sizeof(Teacher*));
*t = new Teacher();


//Lua->stack,得到全局元表位置-1,userdata位置-2
luaL_getmetatable(L, "TeacherClass");


//将元表赋值给位置-2的userdata,并弹出-1的元表
lua_setmetatable(L, -2);


return 1;
}


int lua_get_name(lua_State* L)
{
//得到第一个传入的对象参数(在stack最底部)
Teacher** t = (Teacher**)luaL_checkudata(L, 1, "TeacherClass");
luaL_argcheck(L, t != NULL, 1, "invalid user data");


//清空stack
lua_settop(L, 0);


//将数据放入stack中,供Lua使用
lua_pushstring(L, (*t)->get_name().c_str());


return 1;
}


int lua_set_name(lua_State* L)
{
//得到第一个传入的对象参数
Teacher** t = (Teacher**)luaL_checkudata(L, 1, "TeacherClass");
luaL_argcheck(L, t != NULL, 1, "invalid user data");


luaL_checktype(L, -1, LUA_TSTRING);


std::string name = lua_tostring(L, -1);
(*t)->set_name(name);


return 0;
}


int lua_get_age(lua_State* L)
{
Teacher** t = (Teacher**)luaL_checkudata(L, 1, "TeacherClass");
luaL_argcheck(L, t != NULL, 1, "invalid user data");


lua_pushinteger(L, (*t)->get_age());


return 1;
}


int lua_set_age(lua_State* L)
{
Teacher** t = (Teacher**)luaL_checkudata(L, 1, "TeacherClass");
luaL_argcheck(L, t != NULL, 1, "invalid user data");


luaL_checktype(L, -1, LUA_TNUMBER);


(*t)->set_age((unsigned)lua_tointeger(L, -1));


return 0;
}


int lua_print(lua_State* L)
{
Teacher** t = (Teacher**)luaL_checkudata(L, 1, "TeacherClass");
luaL_argcheck(L, t != NULL, 1, "invalid user data");


(*t)->print();


return 0;
}


int lua_teacher2string(lua_State* L)
{
Teacher** t = (Teacher**)luaL_checkudata(L, 1, "TeacherClass");
luaL_argcheck(L, t != NULL, 1, "invalid user data");


lua_pushfstring(L, "This is teacher name : %s age : %d !", (*t)->get_name().c_str(), (*t)->get_age());


return 1;
}


int lua_auto_gc(lua_State* L)
{
Teacher** t = (Teacher**)luaL_checkudata(L, 1, "TeacherClass");
luaL_argcheck(L, t != NULL, 1, "invalid user data");


if (t) {
delete *t;
}


return 0;
}




int luaopen_teacher_libs(lua_State* L)
{
//创建全局元表(里面包含了对LUA_REGISTRYINDEX的操作),元表的位置为-1
luaL_newmetatable(L, "TeacherClass");


//将元表作为一个副本压栈到位置-1,原元表位置-2
lua_pushvalue(L, -1);


//设置-2位置元表的__index索引的值为位置-1的元表,并弹出位置-1的元表,原元表的位置为-1
lua_setfield(L, -2, "__index");


//将成员函数注册到元表中(到这里,全局元表的设置就全部完成了)
luaL_setfuncs(L, lua_reg_teacher_member_funcs, 0);


//注册构造函数到新表中,并返回给Lua
luaL_newlib(L, lua_reg_teacher_constructor_funcs);


return 1;
}

为了方便,我们把注册的代码添加到之前的 LuaRegFuncs 类中,相关代码如下:

#include "Teacher.h"
#include "TeacherRegFuncs.h"
...
static const luaL_Reg regclass[] = {
{ "base", luaopen_base }, // 系统模块
{ "Teacher", luaopen_teacher_libs }, // 模块名字 Teacher,注册函数 luaopen_teacher_libs
{ NULL, NULL }
};


...
const luaL_Reg* lua_reg = regclass;
for (; lua_reg->func; ++lua_reg) {
luaL_requiref(GetLuaState(), lua_reg->name, lua_reg->func, 1);
lua_pop(GetLuaState(), 1);
}
...

C++ 和 Lua 相互调用测试

我们完成了相关代码的封装,现在我们来做一些必要的测试:

先写一份 Lua 脚本,需求如下:

编写无返回值、一个返回值、多个返回值函数。编写函数,调用 C++ 中的函数和类。最终,我们会在 C++ 中调用 这些 Lua 函数。

luascript_test.lua

function show_luascript_test1(a,b)
print("Test1",a,b)
return a+b
end




function show_luascript_test2(A,B,C)
print("Test3 Lua",A,B,C)
return 111,"LUA",333.6
end


function teacher_print()
local teacher_obj = Teacher.create()
teacher_obj:set_name("Jack")
teacher_obj:print()


--使用内部的__tostring函数进行打印
print(teacher_obj)


--下面的代码也是可行的
--teacher_obj.set_name(teacher_obj,"Jack")
--teacher_obj.print(teacher_obj)


--让其进行自动gc
teacher_obj = nil


--手动强制gc
--collectgarbage("collect")
end


function reg_funcs()
print("-----------------lua_pushcfunction_RegFuncs-----------------")
show_luaproject_info1()
show_luaproject_info2()


print("-----------------luaL_newlib_RegFuncs-----------------")
luaL_newlib_RegFuncs.show_luaproject_info1()
luaL_newlib_RegFuncs.show_luaproject_info2()


print("-----------------lua_newtable_luaL_setfuncs_RegFuncs-----------------")
lua_newtable_luaL_setfuncs_RegFuncs.show_luaproject_info1()
lua_newtable_luaL_setfuncs_RegFuncs.show_luaproject_info2()


print("-----------------lua_table_reg_RegFuncs-----------------")
lua_table_reg_RegFuncs.show_luaproject_info1()
lua_table_reg_RegFuncs.show_luaproject_info2()


print("-----------------luaL_requiref_RegFuncs-----------------")
--local luaL_requiref_RegFuncs = require "luaL_requiref_RegFuncs"
luaL_requiref_RegFuncs.show_luaproject_info1()
luaL_requiref_RegFuncs.show_luaproject_info2()
end

编写代码,加载 这个 Lua 文件,并且实现调用上面的 Lua 函数。

main.cpp

#include <stdio.h>
#include <string>
#include <assert.h>
#include <iostream>


#include "DirectoryManager.h"


#include "LuaRegFuncs.h"


int main(int argc, char** argv)
{
LuaRegFuncs* lua_reg_funcs = new LuaRegFuncs();
lua_reg_funcs->RegFuncs();


std::string strLuaScript;
#ifdef _WIN32
strLuaScript = DirectoryManager::GetExeDirectory() + "scripts/luascript_test.lua";
#else
strLuaScript = "scripts/luascript_test.lua";
#endif
// 加载一个lua脚本
if (!lua_reg_funcs->DoFile(strLuaScript.c_str()))
{
delete lua_reg_funcs;
return -1;
}


// 一返回值函数的调用
auto sum_ab = lua_reg_funcs->CallLuaFuction_return_oneValue<int>("show_luascript_test1", 1, 10);
std::cout << "show_luascript_test1 Result:" << sum_ab << std::endl;
// 无返回值函数的调用
// Lua 函数中,调用 C++ 函数的应用
lua_reg_funcs->CallLuaFuction_return_void("reg_funcs");


// 多返回值函数的调用
lua_reg_funcs->CallLuaFuction_return_multValues<int, const char*, float>("show_luascript_test2", 2, "vic.MINg", 3.14f);
auto arrResult = lua_reg_funcs->GetResult(LuaHelp::LH_LuaType::LH_Lua_Int, LuaHelp::LH_LuaType::LH_Lua_String, LuaHelp::LH_LuaType::LH_Lua_Number);
std::cout << "show_luascript_test2 Result:" << arrResult[0].lh_int_data << std::endl;
std::cout << "show_luascript_test2 Result:" << arrResult[1].lh_string_data << std::endl;
std::cout << "show_luascript_test2 Result:" << arrResult[2].lh_number_data << std::endl;


// Lua 函数中,调用 C++ 类的应用
lua_reg_funcs->CallLuaFuction_return_void("teacher_print");


system("pause");
delete lua_reg_funcs;


return 0;
}

运行程序,看一下成果物:

test


文章转载自林元皓,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论