如何移除可变参数模板方法调用时的尾部多余参数?
问题描述
我正在用C++11实现一个简易反射器,通过std::unordered_map存储成员函数指针,核心代码如下:
static std::unordered_map<std::string, std::pair<void(EmptyClass::*)(void), int>>* methods; template<typename ClassType, typename returnType, typename... Args> static void RegistFunction(std::string name, returnType(ClassType::* func)(Args... args)) { (*methods)[name] = std::make_pair((void(EmptyClass::*)())func, sizeof...(Args)); } template<typename ReturnType, typename ClassType, typename... Args> static ReturnType ExecuteFunction(ClassType* object, std::string name, Args... args) { if (object == NULL) return; ReturnType(ClassType:: * func)(Args...) = (ReturnType(ClassType::*)(Args...))(*methods)[name].first; return (object->*func)(std::forward<Args>(args)...); }
现在遇到的问题是:调用ExecuteFunction时传入的参数数量可能多于目标函数实际接受的数量,目前只能实现移除参数列表头部的多余参数,现有尝试的代码如下:
template<typename ReturnType, typename ClassType, typename Arg, typename... Args> static ReturnType ExecuteFunction(ClassType* object, std::string name, Arg arg, Args... args) { if (sizeof...(Args) + 1 > (*methods)[name].second) { return ExecuteFunction<ReturnType>(std::forward<ClassType*>(object), std::forward<std::string>(name), std::forward<Args>(args)...); } if (object == NULL) return; ReturnType(ClassType:: * func)( Arg, Args...) = (ReturnType(ClassType::*)(Arg, Args...))(*methods)[name].first; return (object->*func)(std::forward<Arg>(arg), std::forward<Args>(args)...); }
需要找到移除可变参数模板方法尾部多余参数的解决方案。
解决方案
要实现移除尾部多余参数,核心思路是提取输入参数列表的前N个参数(N为目标函数的参数数量),再将这个参数前缀传递给目标函数。在C++11中可以通过模板元编程实现这一点,具体步骤如下:
1. 实现参数前缀提取工具
首先定义模板结构,用于从可变参数列表中提取前N个参数:
// 基础递归模板 template<std::size_t N, typename... Args> struct take_first; // 递归终止条件:N=0时返回空元组 template<typename... Args> struct take_first<0, Args...> { using type = std::tuple<>; }; // 递归提取:保留第一个参数,继续提取剩余的N-1个 template<std::size_t N, typename Arg, typename... Args> struct take_first<N, Arg, Args...> { using type = decltype(std::tuple_cat( std::make_tuple(std::declval<Arg>()), std::declval<typename take_first<N-1, Args...>::type>() )); }; // 辅助别名简化使用 template<std::size_t N, typename... Args> using take_first_t = typename take_first<N, Args...>::type;
2. 实现元组参数展开执行函数
需要一个辅助函数将提取到的参数元组展开,调用目标成员函数:
// C++11兼容的索引序列实现(C++14可直接用std::index_sequence) template<std::size_t... Indices> struct index_sequence {}; template<std::size_t N, std::size_t... Indices> struct make_index_sequence_impl : make_index_sequence_impl<N-1, N-1, Indices...> {}; template<std::size_t... Indices> struct make_index_sequence_impl<0, Indices...> { using type = index_sequence<Indices...>; }; template<std::size_t N> using make_index_sequence = typename make_index_sequence_impl<N>::type; // 递归展开元组参数 template<std::size_t... Indices, typename ReturnType, typename ClassType, typename... Args> static ReturnType execute_with_tuple(ClassType* object, ReturnType(ClassType::* func)(Args...), const std::tuple<Args...>& args, index_sequence<Indices...>) { return (object->*func)(std::get<Indices>(args)...); } // 对外接口:自动生成索引序列 template<typename ReturnType, typename ClassType, typename... Args> static ReturnType execute_with_tuple(ClassType* object, ReturnType(ClassType::* func)(Args...), const std::tuple<Args...>& args) { return execute_with_tuple< typename make_index_sequence<sizeof...(Args)>::type{} >(object, func, args); }
3. 修改ExecuteFunction核心逻辑
更新ExecuteFunction,先获取目标函数的参数数量,提取输入参数的前N个组成元组,再调用辅助函数执行:
template<typename ReturnType, typename ClassType, typename... Args> static ReturnType ExecuteFunction(ClassType* object, std::string name, Args&&... args) { if (!object) return ReturnType{}; // 处理空指针,需确保ReturnType可默认构造 auto& method_entry = (*methods)[name]; const std::size_t target_arg_count = method_entry.second; // 参数不足时的容错处理,可根据需求调整(如抛出异常) if (target_arg_count > sizeof...(Args)) { return ReturnType{}; } // 提取前target_arg_count个参数 using ArgsTuple = take_first_t<target_arg_count, Args...>; ArgsTuple args_tuple(std::forward<Args>(args)...); // 转换函数指针并执行 using FuncType = ReturnType(ClassType::*)(typename std::tuple_element<0, ArgsTuple>::type...); FuncType func = reinterpret_cast<FuncType>(method_entry.first); return execute_with_tuple(object, func, args_tuple); }
关键说明
take_first模板通过递归拼接元组实现参数前缀提取,是截断尾部参数的核心。- 原代码中
void(EmptyClass::*)()的强制转换存在类型安全风险,实际使用时建议确保ClassType与EmptyClass的类型兼容性,或改用void*存储函数指针并配合类型信息恢复。
内容的提问来源于stack exchange,提问作者Lyar
相关产品推荐
相关产品推荐

