如何移除Wrapper间接层,实现C++模板参数包的直接转换?
移除Wrapper间接层的C++模板技巧
问题背景
现有代码通过Wrapper结构体间接实现了编译期绑定函数指针并转换参数的逻辑,核心代码如下:
现有可运行代码
template <typename ...Types> struct Wrapper { template <void (*F)(typename Types::ParameterType...)> static void toNativeFunction(Stuff stuff) { auto arguments = toConvertedArguments<Types...>(stuff); std::apply(F, arguments); } }; // toConvertedArguments 声明 template<typename... Types> using converted_tuple = std::tuple<typename Converter<Types>::ReturnType...>; template<typename... Types> converted_tuple<Types...> toConvertedArguments(Stuff stuff); // 调用示例 void implementation(int); template<typename T> class SomeClass { using ParameterType = T; }; // 使用Wrapper调用 { Wrapper<SomeClass<int>>::toNativeFunction<&implementation>(stuff); }
Wrapper的存在仅仅是为了绕开C++模板的语法限制:参数包Types...不能直接放在非类型模板参数(函数指针F)之前,而交换顺序后编译器又无法推导Types...。我们的目标是移除这个间接层,实现更直观的调用方式。
解决方案
方法1:C++20 auto非类型模板参数(最简洁)
利用C++20引入的auto非类型模板参数,让编译器自动推导函数指针的类型,再通过辅助模板反向映射出Types...:
// 辅助模板:从函数指针类型提取参数,映射为SomeClass<T>的列表 template<typename Func> struct ExtractTypes; template<typename... Args> struct ExtractTypes<void(*)(Args...)> { using type = std::tuple<SomeClass<Args>...>; }; // 目标函数模板:auto推导函数指针,requires约束确保Types匹配 template<auto F, typename... Types> requires (std::same_as<std::tuple<Types...>, typename ExtractTypes<decltype(F)>::type>) static void toNativeFunction(Stuff stuff) { auto arguments = toConvertedArguments<Types...>(stuff); std::apply(F, arguments); } // 调用方式(无需Wrapper) { toNativeFunction<&implementation>(stuff); }
编译器会自动推导函数指针&implementation的类型,通过ExtractTypes得到对应的SomeClass<int>,再通过requires约束匹配Types...,完全消除了Wrapper的间接层。
方法2:C++17及以上的模板特化推导
如果无法使用C++20,可以通过辅助结构体的模板特化来推导Types...:
// 辅助结构体:匹配函数指针和对应的SomeClass列表 template<void(*F)(typename Types::ParameterType...), typename... Types> struct Helper; // 特化版本:将函数参数Args...映射为SomeClass<Args> template<void(*F)(Args...), typename... Args> struct Helper<F, SomeClass<Args>...> { static void call(Stuff stuff) { auto arguments = toConvertedArguments<SomeClass<Args>...>(stuff); std::apply(F, arguments); } }; // 对外的函数模板:仅接受函数指针参数,委托给Helper template<void(*F)> static void toNativeFunction(Stuff stuff) { Helper<F>::call(stuff); } // 调用方式(无需Wrapper) { toNativeFunction<&implementation>(stuff); }
这里Helper的特化会自动匹配函数指针F的参数类型Args...,对应到SomeClass<Args>,从而完成Types...的推导,同样可以移除Wrapper。
总结
以上两种方法都能直接实现目标逻辑,无需额外的Wrapper结构体。其中C20的auto非类型模板参数写法最简洁直观,是首选方案;如果需要兼容更早的C标准,模板特化推导的方式同样可行。
内容的提问来源于stack exchange,提问作者Francisco Ryan Tolmasky I
相关产品推荐
相关产品推荐

