如何将ygluu自动化Lua-C API模块为?

摘要:关键词:CC++、模板、template、反射、Lua、API、绑定、自动化、开发、效率、成本 痛点: CC++没有完美的反射功能(如golangdelphi),
关键词: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