初始化函数指针的constexpr数组

时间:2019-11-07 00:09:20

标签: c++ optimization c++17 template-meta-programming lookup-tables

我想将运行时值int v转换为对带有非类型模板参数v,例如template <int v> void hello()的相应函数的调用。

这是编写它的蛮力方式:

using fn_type = void();

template <int v>
void hello() {
    // of course ITRW this function requires v to be a
    // constexpr value
    printf("hello: %d\n", v);
}

static std::array<fn_type *, 3> lut = {
    hello<0>,
    hello<1>,
    hello<2>
};

void hello_dispatch(int v) {
    lut[v](); // we don't handle OOB values b/c we are naughty like that
}

我可以将其用于3个值,但是对于更多的值,或者当限制本身是由其他编译时值计算得出的,这是不切实际的。

如何在编译时初始化LUT 2 而不在初始化程序中明确列出各种实例hello<0>, hello<1>, ...

这是我想出的:

template <size_t I, size_t MAX>
constexpr void make_helper(std::array<fn_type *, MAX>& a) {
    if constexpr (I < MAX) {
        a[I] = hello<I>;
        make_helper<I + 1, MAX>(a);
    }
}

template <size_t MAX>
constexpr std::array<fn_type *, MAX> make_lut() {
    std::array<fn_type *, MAX> ret{};
    make_helper<0, MAX>(ret);
    return ret;
}


static constexpr std::array<fn_type *, 3> lut2 = make_lut<3>();

在C ++ 17中,必须有一些更简单,更好和更惯用的东西,尤其是不需要递归的东西。


2 或者,如果这是一个XY问题,如何在没有LUT(但至少具有LUT效率)的情况下实现hello_dispatch

3 个答案:

答案 0 :(得分:3)

您可以直接初始化std::array,即不需要递归地分配元素。

template<std::size_t... I>
constexpr auto make_helper(std::index_sequence<I...>) {
    return std::array<fn_type *, sizeof...(I)> { hello<I>... };
}
template <std::size_t MAX>
constexpr auto make_lut() {
    return make_helper(std::make_index_sequence<MAX>{});
}

LIVE

答案 1 :(得分:2)

使用std::integer_sequencefold expressions

GET http://127.0.0.1:8080/api/projects?filter[tasks.id]=1

{
  "data" : [ ]
}
  • template <int... Is> constexpr std::array<fn_type*, sizeof...(Is)> make_lut(std::integer_sequence<int, Is...>) { std::array<fn_type*, sizeof...(Is)> res{}; ((res[Is] = &hello<Is>), ...); return res; } template <int Max> constexpr auto make_lut() { return make_lut(std::make_integer_sequence<int, Max>{}); } static constexpr auto lut2 = make_lut<3>(); 创建一个std::make_integer_sequence<int, N>{}类型的对象。
  • 我们使用第一个函数模板捕获这些值,并使用折叠表达式std::integer_sequence<int, 0, 1, 2, ..., N - 1>将每个hello<N>分配给res[N]
  • 我们可以对res[Is] = &hello<Is>折痕左侧的值应用逗号运算符。

答案 2 :(得分:0)

现在,对于完全不同的东西...

如果可以使用C ++ 20,则可以使用template-lambdas,并且可以完全避免使用lut数组

#include <iostream>
#include <utility>

template <int v>
void hello()
 { std::cout << "hello: " << v << std::endl; }

void hello_dispatch (int v)
 {
   [&]<int ... Is>(std::integer_sequence<int, Is...> const &)
      { ((v == Is ? (hello<Is>(), 0) : 0), ...); }
         (std::make_integer_sequence<int, 100u>{}); // 100 is top limit (former lut size)
 }

int main ()
 {
   hello_dispatch(42);
 }

如果您只能使用C ++ 17 ...不是那么优雅,但是您可以使用递归通用lambda

#include <iostream>
#include <utility>

template <int v>
void hello()
 { std::cout << "hello: " << v << std::endl; }

template <int I>
using IC = std::integral_constant<int, I>;

void hello_dispatch (int v)
 {
   auto lf = [&](auto self, auto ic)
    { if constexpr ( ic < 100 )
         v == ic ? (hello<ic>(), 0)
                 : (self(self, IC<ic+1>{}), 0); };

   lf(lf, IC<0>{});
 }

int main ()
 {
   hello_dispatch(42);
 }