如何在 C++ 中模板化接受模板化参数并在其上应用模板化函数的函数?
How do I template a function that takes templated args and applies a templated function on them in c++?
我有一堆静态 class 函数,它们接受不同数量的 {string, int, float} 参数和一个输出参数。根据所调用的函数,相同的参数可能会有不同的行为。例如:
static void ChangeOutput1(const string& foo, int bar, Output* output);
static void ChangeOutput2(int bar, Output* output);
static void ChangeOutput3(float foo, Output* output);
static void ChangeOutput4(float foo, Output* output); // behaves differently from ChangeOutput3
我想要一种简单、安全的方式来编写模板以对每个函数执行类似的行为,基本上是使用 Output 参数调用并将其转换为字符串 return。理想情况下无需再次指定参数类型。它可能看起来像这样:
template<typename... Args, int (*ChangeOutputFn)(Args...)>
string OutputString(Args... args) {
Output output;
ChangeOutputFn(args..., &output);
return ConvertToString(output);
}
// Is there a way to alias static templated functions?
using StringOutput1 = OutputString<ChangeOutput1>;
using StringOutput2 = OutputString<ChangeOutput2>;
using StringOutput3 = OutputString<ChangeOutput3>;
我不确定如何实现。我不确定如何编写 OutputString 以及我将如何别名或定义静态函数。有不太优雅的解决方案,但它们需要我想避免的重复样板。
对于 class,您可以这样做:
template <typename T, T f> struct OutputString;
template<typename... Args, void (*ChangeOutputFn)(Args...)>
struct OutputString<void (*)(Args...), ChangeOutputFn>
{
template <typename ... Ts>
auto operator()(Ts... args)
-> decltype(ChangeOutputFn(std::forward<Ts>(args)..., std::declval<Output *>()),
std::string{})
{
Output output;
ChangeOutputFn(std::forward<Ts>(args)..., &output);
return ConvertToString(output);
}
};
然后
using StringOutput1 = OutputString<decltype(&ChangeOutput1), &ChangeOutput1>;
using StringOutput2 = OutputString<decltype(&ChangeOutput2), &ChangeOutput2>;
using StringOutput3 = OutputString<decltype(&ChangeOutput3), &ChangeOutput3>;
并将其用作
std::string s2 = StringOutput2{}(42);
std::string s3 = StringOutput3{}(4.2f);
如果将 Output 参数移到前面,就可以做到这一点。
static void ChangeOutput1(Output*, const std::string& foo, int bar);
static void ChangeOutput2(Output*, int bar);
static void ChangeOutput3(Output*, float foo);
static void ChangeOutput4(Output*, float foo);
现在你可以拥有这个模板了:
template<typename... Args>
std::function<std::string(Args...)>
mkOutput (void (*ChangeOutputFn)(Output*, Args...))
{
return [ChangeOutputFn](Args... args)->std::string{
Output output;
ChangeOutputFn(&output, args...);
return ConvertToString(output);
};
}
和"function aliases"看起来像这样:
auto a1 = mkOutput(ChangeOutput1);
auto a2 = mkOutput(ChangeOutput2);
auto a3 = mkOutput(ChangeOutput3);
auto a4 = mkOutput(ChangeOutput4);
注意 1. 你不能有这个语法
OutputString<ChangeOutput1>
因为 ChangeOutput1
是一个值,而 OutputString
必须事先知道它的类型或将其作为另一个模板参数接收。
有这样的可能
OutputString<decltype(ChangeOutput1), ChangeOutput1>
然后用宏去掉重复的,但是那太丑了。
我选择在运行时而不是在编译时传递函数,这样更容易。
我有一堆静态 class 函数,它们接受不同数量的 {string, int, float} 参数和一个输出参数。根据所调用的函数,相同的参数可能会有不同的行为。例如:
static void ChangeOutput1(const string& foo, int bar, Output* output);
static void ChangeOutput2(int bar, Output* output);
static void ChangeOutput3(float foo, Output* output);
static void ChangeOutput4(float foo, Output* output); // behaves differently from ChangeOutput3
我想要一种简单、安全的方式来编写模板以对每个函数执行类似的行为,基本上是使用 Output 参数调用并将其转换为字符串 return。理想情况下无需再次指定参数类型。它可能看起来像这样:
template<typename... Args, int (*ChangeOutputFn)(Args...)>
string OutputString(Args... args) {
Output output;
ChangeOutputFn(args..., &output);
return ConvertToString(output);
}
// Is there a way to alias static templated functions?
using StringOutput1 = OutputString<ChangeOutput1>;
using StringOutput2 = OutputString<ChangeOutput2>;
using StringOutput3 = OutputString<ChangeOutput3>;
我不确定如何实现。我不确定如何编写 OutputString 以及我将如何别名或定义静态函数。有不太优雅的解决方案,但它们需要我想避免的重复样板。
对于 class,您可以这样做:
template <typename T, T f> struct OutputString;
template<typename... Args, void (*ChangeOutputFn)(Args...)>
struct OutputString<void (*)(Args...), ChangeOutputFn>
{
template <typename ... Ts>
auto operator()(Ts... args)
-> decltype(ChangeOutputFn(std::forward<Ts>(args)..., std::declval<Output *>()),
std::string{})
{
Output output;
ChangeOutputFn(std::forward<Ts>(args)..., &output);
return ConvertToString(output);
}
};
然后
using StringOutput1 = OutputString<decltype(&ChangeOutput1), &ChangeOutput1>;
using StringOutput2 = OutputString<decltype(&ChangeOutput2), &ChangeOutput2>;
using StringOutput3 = OutputString<decltype(&ChangeOutput3), &ChangeOutput3>;
并将其用作
std::string s2 = StringOutput2{}(42);
std::string s3 = StringOutput3{}(4.2f);
如果将 Output 参数移到前面,就可以做到这一点。
static void ChangeOutput1(Output*, const std::string& foo, int bar);
static void ChangeOutput2(Output*, int bar);
static void ChangeOutput3(Output*, float foo);
static void ChangeOutput4(Output*, float foo);
现在你可以拥有这个模板了:
template<typename... Args>
std::function<std::string(Args...)>
mkOutput (void (*ChangeOutputFn)(Output*, Args...))
{
return [ChangeOutputFn](Args... args)->std::string{
Output output;
ChangeOutputFn(&output, args...);
return ConvertToString(output);
};
}
和"function aliases"看起来像这样:
auto a1 = mkOutput(ChangeOutput1);
auto a2 = mkOutput(ChangeOutput2);
auto a3 = mkOutput(ChangeOutput3);
auto a4 = mkOutput(ChangeOutput4);
注意 1. 你不能有这个语法
OutputString<ChangeOutput1>
因为 ChangeOutput1
是一个值,而 OutputString
必须事先知道它的类型或将其作为另一个模板参数接收。
有这样的可能
OutputString<decltype(ChangeOutput1), ChangeOutput1>
然后用宏去掉重复的,但是那太丑了。
我选择在运行时而不是在编译时传递函数,这样更容易。