我有一个模板化函数
template <int N>
void myfunc(int a) {
// contents compile-time optimised using N
}
我在编译时实例化,为不同的
N
做出独特的定义。在运行时,我需要使用用户给定变量通知的模板参数来调用 myfunc
。
一个详细的方法是:
void callOptimisedMyFunc(int n, int a) {
if (n == 0) myfunc<0>(a);
if (n == 1) myfunc<1>(a);
if (n == 2) myfunc<2>(a);
// ...
if (n == MAX) myfunc<MAX>(a);
}
在我的例子中是非常长的,因此
MAX
可以和64
一样大。更糟糕的是,我需要为大约 ten 具有独特签名和返回类型的不同函数创建这样的包装器!例如
template <int N>
long otherFunc(string x, double y) {
// ...
}
void callOptimisedOtherFunc(int n, string x, double y) {
if (n == 0) return otherfunc<0>(x, y);
if (n == 1) return otherfunc<1>(x, y);
if (n == 2) return otherfunc<2>(x, y);
// ...
if (n == MAX) return otherfunc<MAX>(x, y);
}
我希望以元编程方式执行运行时到编译时参数的映射。 不满意的解决方案
#define NORETURN
#define INNER1(usernum, compnum, func, args, retcmd) \
if (usernum == compnum) retcmd func<compnum> args;
#define INNER4(usernum, compbase, func, args, retcmd) \
INNER1( usernum, compbase+0, func, args, retcmd ); \
INNER1( usernum, compbase+1, func, args, retcmd ); \
INNER1( usernum, compbase+2, func, args, retcmd ); \
INNER1( usernum, compbase+3, func, args, retcmd );
#define INNER16(usernum, compbase, func, args, retcmd) \
INNER4( usernum, compbase+0, func, args, retcmd ); \
INNER4( usernum, compbase+4, func, args, retcmd ); \
INNER4( usernum, compbase+8, func, args, retcmd ); \
INNER4( usernum, compbase+12, func, args, retcmd );
#define INNER64(usernum, func, args, retcmd) \
INNER16( usernum, 0, func, args, retcmd ); \
INNER16( usernum, 16, func, args, retcmd ); \
INNER16( usernum, 32, func, args, retcmd ); \
INNER16( usernum, 48, func, args, retcmd );
#define CALL_OPTIMISED_FUNC(n, func, ...) \
INNER64( n, func, (__VA_ARGS__), NORETURN );
#define CALL_AND_RETURN_OPTIMISED_FUNC(n, func, ...) \
INNER64( n, func, (__VA_ARGS__), return );
这让我可以跑步:
void callOptimisedMyFunc(int N, int a) {
CALL_OPTIMISED_FUNC( N, myfunc, a );
}
long callOptimisedOtherFunc(int N, string x, double y) {
CALL_AND_RETURN_OPTIMISED_FUNC( N, otherfunc, x, y );
}
这仍然是令人不舒服的冗长,并且没有使用 C++17 中提供的所有漂亮的 C++ 元编程工具。在每次调用时不必要地评估 64 个
if
语句似乎也是一种耻辱。更令人满意的解决方案是准备一个函数指针的静态数组,由它们的模板参数索引
N
。部分解决方案
模板化函数要调用(即只有myfunc
),我可以像这样使用
index_sequence
:#include <array>
#include <utility>
template<size_t... N>
constexpr auto helper_myfunc(index_sequence<N...>) {
return array <void (*)(int), sizeof...(N)> { myfunc<N> ... };
}
constexpr auto getArrayOfMyFuncRefs() {
return helper_myfunc( make_index_sequence<MAX>() );
}
这可以让我简洁地写
void callOptimisedMyFunc(int n, int a) {
static auto funcs = getArrayOfMyFuncRefs();
funcs[n](a);
}
唉,我需要为其他我希望将运行时参数映射到编译时参数的函数复制此定义:
template<size_t... N>
constexpr auto helper_otherfunc(index_sequence<N...>) {
return array <long (*)(string, double), sizeof...(N)> { otherfunc<N> ... };
}
constexpr auto getArrayOfOtherFuncRefs() {
return helper_otherfunc( make_index_sequence<MAX>() );
}
long callOptimisedOtherFunc(int n, string x, double y) {
static auto funcs = getArrayOfOtherFuncRefs();
return funcs[n](x, y);
}
寻求解决方案
getArray*()
函数概括为一个接受
any单整数模板化函数的函数,即使具有不同的签名。这将使我能够简洁地定义:
void callOptimisedMyFunc(int n, int a) {
static auto funcs = getArrayOfFuncRefs(myfunc);
funcs[n](a);
}
void callOptimisedOtherFunc(int n, string x, double y) {
static auto funcs = getArrayOfFuncRefs(otherfunc);
return funcs[n](x, y);
}
我正在努力做到这一点,因为我不知道如何将像
myfunc
这样的引用传递给函数;它缺少模板语法(即
<0>
)。我最近的努力没有编译:template<size_t... N, typename RetType, typename... Args>
constexpr auto helper(index_sequence<N...>, ReturnType (*funcname)(Args...)) {
return array<RetType (*)(Args...), sizeof...(N)>{ funcname<N> ... };
}
template<typename Func>
constexpr auto getArrayOfFuncRefs(Func func) {
return helper(make_index_sequence<MAX>(), func);
}
有没有办法通过 C++ 元编程实现我想要的目标,或者我必须退回到宏的可怕用法?
// Need one of these for each function template
template <int N>
struct MyFuncWrapper {
static void call(int a) { myFunc<N>(a); }
};
template <template <int N> class Wrapper, typename... Args, size_t... Is>
void CallOptimizedHelper(std::index_sequence<Is...>, int n, Args&&... args) {
((n==Is ? (void)Wrapper<Is>::call(std::forward<Args>(args)...) : (void)0), ...);
}
template <template <int N> class Wrapper, typename... Args>
void CallOptimized(int n, Args&&... args) {
CallOptimizedHelper<Wrapper>(
std::make_index_sequence<MAX>{}, n,
std::forward<Args>(args)...);
}
然后你称其为
CallOptimized<MyFuncWrapper>(/*n=*/1, /*a=*/2);