避免运行时到编译时数值参数转换的代码重复

Avoiding code duplication for runtime-to-compile-time numeric parameter translation

本文关键字:转换 参数 代码 值参 运行时 编译      更新时间:2023-10-16

假设我们有这样的函数

template <typename T, unsigned N> void foo();

为简单起见,假设我们知道只有(常量(值N_1N_2... N_kN有效。

现在,假设我想使该编译时参数成为运行时参数,使用 foo() 作为黑盒,即实现:

template <typename T> void foo(unsigned n);

通过拨打foo<,>()电话。我应该怎么做?显然,我可以写:

template <typename T> void foo(unsigned n) {
    switch(n) {
    case N_1 :  foo<T, N_1>(); break;
    case N_2 :  foo<T, N_2>(); break;
    // etc. etc.
    case N_k :  foo<T, N_k>(); break;
    }
 }

。但这让我觉得很脏。我想我可以使用 MAP(( 元宏来生成这些 k 行;但是我能做任何更好、更少宏观的事情来实现同样的目标吗?是否可以编写类似上述通用的东西,并且适用于每个可变参数模板和固定的常量值序列?

笔记:

  • C++11/14/17 的具体建议显然是受欢迎的。
  • N不一定是连续的,也不小,也不一定是排序的,例如假设N_2 = 123456789,N_5 = 1。

你可以做一个函数指针表:

using F = void(*)();
template <class T, class >
struct Table;
template <class T, size_t... Is>
struct Table<T, std::index_sequence<Is...> > {
    static constexpr F fns[] = {
        foo<T, Is>...
    };
};
template <class T, size_t... Is>
constexpr F Table<T, std::index_sequence<Is...> >::fns[sizeof...(Is)];

然后只需调用您想要的那个:

template <class T, size_t N>
struct MakeTable : Table<T, std::make_index_sequence<N>> { };
template <typename T>
void foo(unsigned n) {
    MakeTable<T, MaxN>::fns[n]();
}
<小时 />

如果N_k不是连续的,那么我们可以使用 lambda 进行内联参数解包:

 template <class T>
 void foo(unsigned n) {
     using seq = std::index_sequence<N_1, N_2, ..., N_k>;
     indexer(seq)([n](auto i){
         if (n == i) {
             f<T, i>();
         }
     });
}
<小时 />

如果以上太慢,那么我想只需手动构建std::unordered_map<unsigned, void(*)()>什么的。

在这种情况下,我喜欢构建一个函数指针的静态表,用一个动态参数决定调度到哪个。下面是在函数中实现此目的的实现 foo_dynamic .对于此函数,您可以指定要支持的N的最大值,并使用一些递归模板生成函数指针的静态表。然后,使用动态参数取消引用到此表中。

using ftype = void (*)();
template <typename T, unsigned N> void foo()
{
    std::cout << N << std::endl;
}
template <typename T, unsigned max>
struct TablePopulator
{
    static void populateFTable(ftype* table)
    {
        table[max] = foo<T,max>;
        TablePopulator<T,max-1>::populateFTable(table);
    }
};
template <typename T>
struct TablePopulator<T, 0>
{
    static void populateFTable(ftype* table)
    {
        table[0] = foo<T,0>;
    }
};
template<typename T, unsigned max_N>
std::array<ftype, max_N>& initTable()
{
    static std::array<ftype, max_N> table;
    TablePopulator<T, max_N-1>::populateFTable(table.data());
    return table;
}
template<typename T, unsigned max_N>
void foo_dynamic(unsigned actualN)
{
    static auto ftable = initTable<T, max_N>();
    if(actualN >= max_N)
        throw std::runtime_error("Max param exceeded");
    ftable[actualN]();
}

int main()
{
    foo_dynamic<int, 10>(1);
    foo_dynamic<int, 10>(5);
   return 0;
}
编辑

鉴于问题编辑中的约束,这里有一种方法是手动指定有效索引的,它使用unordered_map而不是数组:

using ftype = void (*)();
template <typename T, unsigned N> void foo()
{
    std::cout << N << std::endl;
}
template<typename T, size_t ... Indices>
void foo_dynamic_indices(size_t actual_index)
{
    static std::unordered_map<size_t, ftype> fmap = {{Indices, foo<T,Indices>}...};
    auto fIt = fmap.find(actual_index);
    if(fIt == fmap.end())
        throw std::runtime_error("Index not found");
    fIt->second();
}
int main()
{
    foo_dynamic_indices<int, 0, 3, 400, 1021, 10000000>(10000000);
    foo_dynamic_indices<int, 0, 3, 400, 1021, 10000000>(4); //Exception
   return 0;
}