如何遍历C++参数包并按参数类型执行Lua栈操作?
问题描述
我有一个可接受任意数量混合参数的可变参数函数,当前代码仅能处理int类型(或可转换为int的类型):
template <typename ... Args> void call_snippet(lua_State *L, const std::string& name, Args... args) { lua_rawgeti(L, LUA_REGISTRYINDEX, snippets[name]); int nargs = 0; for (auto &&x : {args...}) { lua_pushinteger(L, x); nargs++; } lua_pcall(L, nargs, LUA_MULTRET, 0); }
现在需要扩展该函数,支持int、float、std::string、char*等不同类型的参数,类似如下伪代码的功能:
template <typename ... Args> void call_snippet(lua_State *L, const std::string& name, Args... args) { lua_rawgeti(L, LUA_REGISTRYINDEX, snippets[name]); int nargs = 0; for (auto &&x : {args...}) { switch (typeof(x)) { case int: lua_pushinteger(L, x); nargs++; break; case float: lua_pushnumber(L, x); nargs++; break; case std:string: lua_pushcstring(L, x.c_str()); nargs++; break; case char*: lua_pushcstring(L, x); nargs++; break; default: //raise error ; } lua_pcall(L, nargs, LUA_MULTRET, 0); }
请问该如何实际实现上述伪代码?
实现方案
C++是静态类型语言,无法像动态语言那样通过switch判断运行时类型,我们可以通过重载辅助函数或编译时类型判断来实现类型分发,以下是两种常用方案:
方法一:重载辅助函数(兼容C++11及以上)
先定义针对每种支持类型的重载函数,负责将参数推送到Lua栈:
#include <stdexcept> // 推送int类型 void push_to_lua(lua_State* L, int x) { lua_pushinteger(L, x); } // 推送float类型 void push_to_lua(lua_State* L, float x) { lua_pushnumber(L, x); } // 推送std::string类型 void push_to_lua(lua_State* L, const std::string& x) { lua_pushstring(L, x.c_str()); } // 推送char*类型 void push_to_lua(lua_State* L, const char* x) { lua_pushstring(L, x); } // 处理不支持的类型,运行时抛出异常 template <typename T> void push_to_lua(lua_State* L, const T& x) { throw std::invalid_argument("Unsupported argument type"); }
然后修改原函数,通过参数包展开逐个处理参数:
// C++17版本(使用折叠表达式) template <typename ... Args> void call_snippet(lua_State *L, const std::string& name, Args... args) { lua_rawgeti(L, LUA_REGISTRYINDEX, snippets[name]); // 逐个推送参数到Lua栈 (push_to_lua(L, args), ...); lua_pcall(L, sizeof...(args), LUA_MULTRET, 0); }
如果需要兼容C++11/14,可以用初始化列表实现参数包展开:
// C++11/14兼容版本 template <typename ... Args> void call_snippet(lua_State *L, const std::string& name, Args... args) { lua_rawgeti(L, LUA_REGISTRYINDEX, snippets[name]); int nargs = 0; // 利用初始化列表触发每个参数的推送操作 using dummy = int[]; (void)dummy{0, (push_to_lua(L, args), nargs++, 0)...}; lua_pcall(L, nargs, LUA_MULTRET, 0); }
方法二:if constexpr编译时判断(C++17及以上)
通过if constexpr在编译时直接判断参数类型,无需编写多个重载函数:
#include <type_traits> #include <stdexcept> template <typename T> void push_to_lua(lua_State* L, const T& x) { if constexpr (std::is_same_v<T, int>) { lua_pushinteger(L, x); } else if constexpr (std::is_same_v<T, float>) { lua_pushnumber(L, x); } else if constexpr (std::is_same_v<T, std::string> || std::is_same_v<T, const char*>) { lua_pushstring(L, x); } else { // 编译时直接报错,提前拦截不支持的类型 static_assert(false, "Unsupported argument type"); } } // 原函数实现与C++17版本一致 template <typename ... Args> void call_snippet(lua_State *L, const std::string& name, Args... args) { lua_rawgeti(L, LUA_REGISTRYINDEX, snippets[name]); (push_to_lua(L, args), ...); lua_pcall(L, sizeof...(args), LUA_MULTRET, 0); }
这种方式会在编译阶段就检查参数类型是否支持,比运行时抛异常更严格,能更早发现问题。
内容的提问来源于stack exchange,提问作者ZioByte
相关产品推荐
相关产品推荐

