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

时间:2016-07-27 14:50:40

标签: c++ templates template-meta-programming template-specialization idiomatic

假设我们有

等功能
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行;但我可以做更好的事情,而不是那么做宏观的事情吗?是否可以编写类似上面 general 的内容,并且适用于每个可变参数模板和固定值的常量值?

备注:

  • C ++ 11/14/17特别欢迎具体的建议。
  • N不一定是连续的,也不是小的,也不是分类的。例如假设N_2 = 123456789且N_5 = 1。

2 个答案:

答案 0 :(得分:6)

你可以创建一个函数指针表:

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(*)()>或其他东西。

答案 1 :(得分:3)

在这种情况下,我喜欢构建一个函数指针的静态表,其中一个动态参数决定调用哪一个。以下是在函数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;
}