关键词:C/C++、模板、template、反射、Lua、API、绑定、自动化、开发、效率、成本
痛点:
C/C++没有完美的反射功能(如golang/delphi),对开发 Lua ⟷ C API 来说是比较繁琐和耗工时的,并且人工编码的出错率也非常高。
方案
C++模板编译期自动化推导。
优点
•性能高(相比于反射)
•提高开发效率(相比与人工)
•出错率极低(相比与人工)
示例
示例1:注册 C++ 函数到 Lua
示例2:从 C++ 调用 Lua 函数(无函数指针方式)
示例3:从 C++ 调用 Lua 函数(函数指针方式)
代码
/**********************************************************************************
*
* 自动化 Lua ⟷ C API功能模块
*
* author: ygluu
*
* 1、Lua->C: 自动推导和注册C-Func参数和返回值给Lua调用
* 函数 alua::reg_lua<c-func>(L, mod_name, func_name)
*
* 2、C->Lua: 自动推导C-Call语句的参数和返回值并调用Lua函数
*
* 无返回值调用(常用)
* alua::call_lua(L, mod_name, func_name, lua-args...);
*
* 显式返回类型调用(常用)
*
* int ret = alua::call_lua(L, mod_name, func_name, lua-args...);
*
* std::tuple<int, bool, string> ret = alua::call_lua(L, mod_name, func_name, lua-args...);
*
* std::map<string, int> ret = alua::call_lua(L, mod_name, func_name, lua-args...);
*
* 隐式返回类型调用(需声明与LUA函数一致的c-func声明)
* auto ret = alua::call_lua<c-func>(...);
*
* 参数和返回值类型支持:
* 1、基础类型:int/uint32/int64/uint64/string(const char*)/bool
* 2、数组:std::vector<基础类型>
* 3、元组:std::tuple<基础类型,基础类型,...>
* 4、字典:std::map/std::unordered_map<基础类型, 基础类型>
*
*
**********************************************************************************/
#pragma once
#include "lua.hpp"
#include <vector>
#include <string>
#include <unordered_map>
#include <tuple>
#include <type_traits>
#include <utility>
#include <source_location>
namespace alua
{
// 工具:把 __PRETTY_FUNCTION__ 里的裸函数名取出来
namespace
{
// 判断是否是 tuple
template<typename T> inline constexpr bool is_tuple_v = false;
template<typename... Ts> inline constexpr bool is_tuple_v<std::tuple<Ts...>> = true;
// 计算期望返回数量
template<typename R>
constexpr int expected_returns() noexcept
{
if constexpr (std::is_same_v<R, void>) return 0;
else if constexpr (is_tuple_v<R>) return std::tuple_size_v<R>;
else return 1;
}
constexpr std::string_view raw_name(std::string_view pretty) noexcept
{
std::size_t tail = pretty.rfind('(');
if (tail == std::string_view::npos) { return pretty; }
std::size_t head = pretty.rfind(' ', tail);
return (head == std::string_view::npos)
? pretty.substr(0, tail)
: pretty.substr(head + 1, tail - head - 1);
}
// 单值读取 / 压栈
template<typename T> struct popper;
template<typename T> struct pusher;
// 数值
template<> struct popper<int> { static int get(lua_State* l,int i){return static_cast<int>(luaL_checkinteger(l,i));}};
template<> struct popper<float> { static float get(lua_State* l,int i){return static_cast<float>(luaL_checknumber(l,i));}};
template<> struct popper<double> { static double get(lua_State* l,int i){return luaL_checknumber(l,i);}};
template<> struct popper<bool> { static bool get(lua_State* l,int i){return lua_toboolean(l,i)!=0;}};
template<> struct popper<std::uint32_t> { static std::uint32_t get(lua_State* l,int i){return static_cast<std::uint32_t>(luaL_checkinteger(l,i));}};
template<> struct popper<std::uint64_t> { static std::uint64_t get(lua_State* l,int i){return static_cast<std::uint64_t>(luaL_checkinteger(l,i));}};
template<> struct popper<std::int64_t> { static std::int64_t get(lua_State* l,int i){return static_cast<std::int64_t>(luaL_checkinteger(l,i));}};
// 字符串
template<> struct popper<std::string>
{
static std::string get(lua_State* l,int i)
{
std::size_t len = 0;
const char* s = luaL_checklstring(l,i,&len);
return std::string{s,len};
}
};
template<> struct popper<const char*> { static const char* get(lua_State* l,int i){return luaL_checkstring(l,i);}};
// 轻量 userdata
template<> struct popper<void*> { static void* get(lua_State* l,int i){return lua_touserdata(l,i);}};
// 压栈
template<> struct pusher<int> { static void put(lua_State* l,int v){lua_pushinteger(l,v);}};
template<> struct pusher<float> { static void put(lua_State* l,float v){lua_pushnumber(l,v);}};
template<> struct pusher<double> { static void put(lua_State* l,double v){lua_pushnumber(l,v);}};
template<> struct pusher<bool> { static void put(lua_State* l,bool v){lua_pushboolean(l,v);}};
template<> struct pusher<std::uint32_t> { static void put(lua_State* l,std::uint32_t v){lua_pushnumber(l,v);}};
template<> struct pusher<std::uint64_t> { static void put(lua_State* l,std::uint64_t v){lua_pushnumber(l,v);}};
template<> struct pusher<std::int64_t> { static void put(lua_State* l,std::int64_t v){lua_pushnumber(l,v);}};
template<> struct pusher<std::string> { static void put(lua_State* l,const std::string& v){lua_pushlstring(l,v.data(),v.size());}};
template<> struct pusher<const char*> { static void put(lua_State* l,const char* v){lua_pushstring(l,v);}};
template<> struct pusher<void*> { static void put(lua_State* l,void* v){lua_pushlightuserdata(l,v);}};
// vector
template<typename T> struct popper<std::vector<T>>
{
static std::vector<T> get(lua_State* l,int idx)
{
luaL_checktype(l,idx,LUA_TTABLE);
std::vector<T> v;
lua_pushnil(l);
while(lua_next(l,idx)!=0)
{
v.emplace_back(popper<T>::get(l,-1));
lua_pop(l,1);
}
return v;
}
};
template<typename T> struct pusher<std::vector<T>>
{
static void put(lua_State* l,const std::vector<T>& v)
{
lua_createtable(l,static_cast<int>(v.size()),0);
for(std::size_t i=0;i<v.size();++i)
{
pusher<T>::put(l,v[i]);
lua_rawseti(l,-2,static_cast<int>(i+1));
}
}
};
// unordered_map
template<typename K,typename V> struct popper<std::unordered_map<K,V>>
{
static std::unordered_map<K,V> get(lua_State* l,int idx)
{
luaL_checktype(l,idx,LUA_TTABLE);
std::unordered_map<K,V> m;
lua_pushnil(l);
while(lua_next(l,idx)!=0)
{
K k=popper<K>::get(l,-2);
V v=popper<V>::get(l,-1);
m.emplace(std::move(k),std::move(v));
lua_pop(l,1);
}
return m;
}
};
template<typename K,typename V> struct pusher<std::unordered_map<K,V>>
{
static void put(lua_State* l,const std::unordered_map<K,V>& m)
{
lua_createtable(l,0,static_cast<int>(m.size()));
for(const auto& [k,v]:m)
{
pusher<K>::put(l,k);
pusher<V>::put(l,v);
lua_settable(l,-3);
}
}
};
// tuple
template<typename...> struct tuple_popper;
template<> struct tuple_popper<> { static std::tuple<> get(lua_State*,int){return {};}; };
template<typename H,typename... T> struct tuple_popper<H,T...>
{
static auto get(lua_State* l,int idx)
{
return std::tuple_cat(std::make_tuple(popper<H>::get(l,idx)),
tuple_popper<T...>::get(l,idx+1));
}
};
// 返回值包装
template<typename R> struct ret_helper
{
static int push(lua_State* l,const R& v){pusher<R>::put(l,v);return 1;}
};
template<> struct ret_helper<void>{ static int push(lua_State*,...){return 0;} };
template<typename... Ts> struct ret_helper<std::tuple<Ts...>>
{
static int push(lua_State* l,const std::tuple<Ts...>& tup)
{
std::apply([&l](const Ts&... vs){(pusher<Ts>::put(l,vs),...);},tup);
return static_cast<int>(sizeof...(Ts));
}
};
// C 函数 → Lua 包装
template<auto f> struct lua_to_c_wrapper;
template<typename R,typename... A,R(*f)(A...)> struct lua_to_c_wrapper<f>
{
static int call(lua_State* l)
{
constexpr int k_expect=sizeof...(A);
const int argc=lua_gettop(l);
if(argc!=static_cast<int>(k_expect))
{
luaL_error(l,"expect %d args, got %d",k_expect,argc);
}
try
{
auto args=tuple_popper<A...>::get(l,1);
if constexpr(std::is_same_v<R,void>)
{
std::apply(f,args);
return 0;
}
else
{
R ret=std::apply(f,args);
return ret_helper<R>::push(l,ret);
}
}
catch(const std::exception& e)
{
luaL_where(l,1);
lua_pushfstring(l,"C++ exception: %s",e.what());
lua_concat(l,2);
lua_error(l);
}
catch(...)
{
luaL_where(l,1);
lua_pushstring(l,"unknown C++ exception");
lua_concat(l,2);
lua_error(l);
}
return 0;
}
};
// 已知函数指针的调用实现
template<auto f> struct lua_caller;
template<typename R,typename... A,R(*f)(A...)> struct lua_caller<f>
{
static R call(lua_State* L,const char* mod,const char* fn,const A&... args)
{
constexpr int k_argc=sizeof...(A);
constexpr int k_ret = expected_returns<R>();
constexpr bool k_void=std::is_same_v<R,void>;
const int base=lua_gettop(L);
lua_getglobal(L,"debug");
lua_getfield(L,-1,"traceback");
// 找函数
if(mod && *mod)
{
lua_getglobal(L,mod);
if(!lua_istable(L,-1)) luaL_error(L,"module '%s' not found",mod);
lua_getfield(L,-1,fn);
lua_remove(L,-2);
}
else
{
lua_getglobal(L,fn);
}
if(!lua_isfunction(L,-1))
{
auto full=mod?(std::string(mod)+'.'+fn):std::string(fn);
alog_print(get_log_file(),LogLevel::LEVEL_ERROR,full.c_str(),full.size());
}
(pusher<std::decay_t<A>>::put(L,args),...);
lua_inc_call_count();
if(lua_pcall(L,k_argc,k_ret,base+2)!=LUA_OK)
{
size_t sz; auto str=lua_tolstring(L,-1,&sz);
alog_print(get_log_file(),LogLevel::LEVEL_ERROR,str,sz);
}
if constexpr(std::is_same_v<R,void>)
{
lua_settop(L,base);
return;
}
else if constexpr(is_tuple_v<R>)
{
// 读取 tuple 返回值
R ret = [&L,k_ret] {
return popper<R>::get(L,-k_ret);
}();
lua_settop(L,base);
return ret;
}
else
{
R ret=popper<R>::get(L,-1);
lua_settop(L,base);
return ret;
}
}
};
// 无需函数指针的调用实现
template<typename R>
struct free_caller
{
template<typename... Args>
static R call(lua_State* L,const char* mod,const char* fn,Args&&... args)
{
constexpr int k_ret = expected_returns<R>();
constexpr bool k_void=std::is_same_v<R,void>;
const int base=lua_gettop(L);
lua_getglobal(L,"debug");
lua_getfield(L,-1,"traceback");
// 找函数
if(mod && *mod)
{
lua_getglobal(L,mod);
if(!lua_istable(L,-1)) luaL_error(L,"module '%s' not found",mod);
lua_getfield(L,-1,fn);
lua_remove(L,-2);
}
else
{
lua_getglobal(L,fn);
}
if(!lua_isfunction(L,-1))
{
auto full=mod?(std::string(mod)+'.'+fn):std::string(fn);
alog_print(get_log_file(),LogLevel::LEVEL_ERROR,full.c_str(),full.size());
}
(pusher<std::decay_t<Args>>::put(L,std::forward<Args>(args)),...);
lua_inc_call_count();
if(lua_pcall(L,sizeof...(Args),k_ret,base+2)!=LUA_OK)
{
size_t sz; auto str=lua_tolstring(L,-1,&sz);
alog_print(get_log_file(),LogLevel::LEVEL_ERROR,str,sz);
}
if constexpr(std::is_same_v<R,void>)
{
lua_settop(L,base);
return;
}
else if constexpr(is_tuple_v<R>)
{
R ret = popper<R>::get(L,-k_ret);
lua_settop(L,base);
return ret;
}
else
{
R ret=popper<R>::get(L,-1);
lua_settop(L,base);
return ret;
}
}
};
// tuple 读取器
template<typename... Ts>
struct popper<std::tuple<Ts...>>
{
static std::tuple<Ts...> get(lua_State* l,int idx)
{
constexpr std::size_t N=sizeof...(Ts);
if(lua_gettop(l)-idx+1<static_cast<int>(N))
luaL_error(l,"not enough return values for tuple");
return [&l,idx]<std::size_t... I>(std::index_sequence<I...>)
{
return std::make_tuple(popper<Ts>::get(l,idx+I)...);
}(std::make_index_sequence<N>{});
}
};
} // namespace
// 已知函数原型,两参
template<auto f,typename... Args>
auto call_lua(lua_State* L,const char* fn,Args&&... args)
{
return lua_caller<f>::call(L,nullptr,fn,std::forward<Args>(args)...);
}
// 已知函数原型,三参
template<auto f,typename... Args>
auto call_lua(lua_State* L,const char* mod,const char* fn,Args&&... args)
{
return lua_caller<f>::call(L,mod,fn,std::forward<Args>(args)...);
}
// 仅指定返回值,两参(默认 void)
template<typename R=void,typename... Args>
auto call_lua(lua_State* L,const char* fn,Args&&... args)
{
return free_caller<R>::call(L,nullptr,fn,std::forward<Args>(args)...);
}
// 仅指定返回值,三参
template<typename R,typename... Args>
auto call_lua(lua_State* L,const char* mod,const char* fn,Args&&... args)
{
return free_caller<R>::call(L,mod,fn,std::forward<Args>(args)...);
}
// 注册函数
template<auto f>
inline void reg_lua(lua_State* L,const char* module_name,const char* lua_name=nullptr)
{
lua_getglobal(L,module_name);
if(!lua_istable(L,-1))
{
lua_pop(L,1);
luaL_error(L,"module '%s' not found",module_name);
return;
}
const char* real_name=lua_name?lua_name:raw_name(std::source_location::current().function_name()).data();
lua_pushcfunction(L,lua_to_c_wrapper<f>::call);
lua_setfield(L,-2,real_name);
lua_pop(L,1);
}
} // namespace alua