目录
lua_proxy模板类实现
调用lua函数实例
注意事项
c++调用lua要用到模拟栈,调用函数比较复杂。如下写了一个模板,用来以C++熟悉的方式进行lua函数调用,就很方便。目前只写了C++11版本的,如果要适配C++11之前的版本,需要进行一些改动。
#ifndef _LUA_PROXY_H_
#define _LUA_PROXY_H_
#include
#include
#include "lua.hpp"
class nil_t
{
};
nil_t nil;
class lua_proxy
{
private:
lua_State* m_status;
private:
template
void push_type(val_t i_data) const {}
template <>
void push_type(int i_data) const
{
lua_pushinteger(m_status, i_data);
}
template<>
void push_type(double d_data) const
{
lua_pushnumber(m_status, d_data);
}
template <>
void push_type(bool b_data) const
{
lua_pushboolean(m_status, b_data ? 1 : 0);
}
template <>
void push_type(nil_t nil_data) const
{
lua_pushnil(m_status);
}
template <>
void push_type(const char *cstr_data) const
{
lua_pushstring(m_status, cstr_data);
}
private:
class ret_type
{
public:
int m_top_idx; // 调用前栈位置
lua_State* m_status;
public:
ret_type() {}
ret_type(lua_State* ptr_status, const int& i_top_idx) : m_status(ptr_status), m_top_idx(i_top_idx)
{}
ret_type(const ret_type& other) = delete;
ret_type(ret_type&& other)
{
m_status = other.m_status;
m_top_idx = other.m_top_idx;
other.m_status = nullptr;
other.m_top_idx = 0;
}
virtual ~ret_type()
{
if (m_status)
{
lua_settop(m_status, m_top_idx);
}
}
template
operator std::tuple()
{
/* 对m_status操作,获取返回值 */
return gets<-(int)sizeof...(vals_t), vals_t...>();
}
template
void get_tie(std::tuple tp)
{
tp = std::tuple() = *this;
}
template
std::tuple gets() const
{
std::tuple t1{ get(m_status, idx) };
if constexpr (sizeof...(vals_t) == 0)
{
return t1;
}
else
{
return std::tuple_cat(t1, gets());
}
}
template
val_t get(lua_State* L, int idx) const {}
template<>
int get(lua_State* L, int idx)const
{
int itype = lua_type(L, idx);
auto ret = lua_tonumber(L, idx);
return ret;
}
template<>
std::string get(lua_State* L, int idx)const
{
return lua_tostring(L, idx);
}
template<>
double get(lua_State* L, int idx)const
{
int itype = lua_type(L, idx);
auto ret = lua_tonumber(L, idx);
return ret;
}
};
public:
lua_proxy() :m_status()
{
m_status = luaL_newstate();
luaL_openlibs(m_status);
}
virtual ~lua_proxy()
{
lua_close(m_status);
}
lua_State* get_status() const
{
return m_status;
}
void push_stack_value(const int& i_stack_idx) const
{
lua_pushvalue(m_status, i_stack_idx);
}
void set_top_to_stack_idx(const int& i_stack_idx) const
{
lua_replace(m_status, i_stack_idx);
}
void set_stack_idx_to_top(const int& i_stack_idx) const
{
lua_settop(m_status, i_stack_idx);
}
void remove_stack_idx(const int& i_stack_idx) const
{
lua_remove(m_status, i_stack_idx);
}
int stack_size() const
{
return lua_gettop(m_status);
}
template
void push(val_t val, vals_t... vals) const
{
push_type(val);
if constexpr (sizeof...(vals) != 0)
{
push(vals...);
}
}
template
ret_type pcall(const char* cstr_func_name, vals_t...vals)
{
int i_ret_pos = lua_gettop(m_status);
int i_ret = lua_getglobal(m_status, cstr_func_name);
if (i_ret != 0)
{
/* 函数查找失败 */
}
push(vals...);
/*压入数据并调用函数*/
i_ret = lua_pcall(m_status, sizeof...(vals), LUA_MULTRET, 0);
if (i_ret != 0)
{
/* 调用失败 */
}
return ret_type{ m_status, i_ret_pos };
}
int load(const char* cstr_file_path)
{
int i_ret = luaL_dofile(m_status, cstr_file_path);
return i_ret;
}
};
#endif
lua脚本,用于被调用,文件名“main.lua”。
function test(x,y)
print(x ,y)
return x,y
end
调用示例
#include
#include
#include "lua_proxy.h"
/* 函数生成器 */
template
std::function get_func(lua_proxy* plu, const char* cstr_func_name)
{
std::string str_func_name = cstr_func_name;
return [plu, str_func_name](paras_t...paras)->auto
{
return plu->pcall(str_func_name.c_str(), paras...);
};
}
int main()
{
std::tuple t1{1};
lua_proxy lu;
int iret = lu.load("main.lua");
int x = 0; double y = .0;
std::tuple tp;
lu.pcall("test", 1, 1.1).get_tie(std::tie(x, y)); // lua函数调用并转tie
tp = lu.pcall("test", 1, 1.1); // lua函数调用并转tuple
// auto k = lu.pcall("test", 1, 1.1); // 这种调用会导致无法回滚到栈顶,直到k析构
/* 推荐调用方法 */
auto lua_test = get_func,int,double>(&lu, "test");// 定义lua_test函数
std::tie(x,y) = lua_test(1, 1.2); // 调用lua_test函数
_getch();
}
上面的代码展示了三种调用的方法。第一种调用方法通过get_tie直接将返回的多个返回值赋值给x,y;第二种调用方式返回一个tuple;第三种使用了一个小技巧,通过get_func函数返回了一个闭包,其中保存有需要调用的函数名和lua_proxy;获得闭包后再调用lua_test就是调用了lua脚本中的test函数,并且能够正确的获得返回值。
回值会在返回的ret_type析构时自动回退,由于ret_type为内部类,所以在外部不能直接构建,但是由于auto的存在,依然能在外部构建出该对象,因此要注意pcall的返回值不要直接使用auto接,这样会导致栈无法回滚。