用模板函数初始化constexpr数组

时间:2019-07-18 06:00:43

标签: c++ variadic-templates template-meta-programming constexpr stdarray

我正在尝试为模拟器创建带有预编译处理函数的constexpr std::array。下面的代码适用于较小的数字,例如0x250,但与最新版本的MSVC一起使用时,上面的所有内容都会导致“ C1026解析器溢出,程序太复杂”。

#include <array>
#include <iostream>

template<typename T>
using Executor = void(*)(T);

using IntExecutor = Executor<int>;

template<int arg>
void func(int value)
{
    std::cout << (arg * value) << std::endl;
}

// Static for https://codereview.stackexchange.com/a/173570/160845

template<typename T, T Begin, class Func, T ...Is>
constexpr void static_for_impl(Func&& f, std::integer_sequence<T, Is...>)
{
    (f(std::integral_constant<T, Begin + Is>{ }), ...);
}

template<typename T, T Begin, T End, class Func>
constexpr void static_for(Func&& f)
{
    static_for_impl<T, Begin>(std::forward<Func>(f), std::make_integer_sequence<T, End - Begin>{ });
}

template<int N>
constexpr std::array<IntExecutor, N> makeLut()
{
    std::array<IntExecutor, N> lut = { };
    static_for<size_t, 0, N>([&](auto x) {
        lut[x] = func<x>;
    });
    return lut;
}

// 0x250 works just fine
// 0x300 causes a "C1026 parser overflow, program too complex" error
constexpr auto lut = makeLut<0x250>();

int main(int argc, char* argv[])
{
    int instruction = 0xDEADBEEF;

    int instructionHash = instruction & 0x24F;

    lut[instructionHash](instruction);

    return 0;
}

我需要一个std::array大小的0x1000。我可以使用从4static_for()的{​​{1}}个较小的0循环来实现这一点,但是我觉得这是一个丑陋的解决方案。

有人知道用模板函数填充0x250的正确方法吗?

2 个答案:

答案 0 :(得分:1)

您是否尝试过基于std::make_index_sequence / std::index_sequence的解决方案?

template <std::size_t ... Is>
constexpr std::array<IntExecutor, sizeof...(Is)> 
   makeLutHelper (std::index_sequence<Is...>)
 { return { func<int(Is)>... }; }

template <std::size_t N>
constexpr auto makeLut ()
 { return makeLutHelper(std::make_index_sequence<N>{}); }

我无法使用MSVC对其进行测试,但是,在我的Linux平台中,g ++和clang ++也可以编译(很长时间)

constexpr auto lut = makeLut<0x10000u>();

答案 1 :(得分:0)

我对代码max66进行了一些修改,以允许使用自定义lamdba函数创建constexpr数组。我只是将其粘贴在这里,以防有人需要。

#include <array>
#include <iostream>

template<typename T>
using Executor = void(*)(T);

using IntExecutor = Executor<int>;

template<int arg>
void exec(int value)
{
    std::cout << (arg * value) << std::endl;
}

template<int value>
constexpr IntExecutor emitExecutor()
{
    return exec<value>;
}

template<typename T, class Func, std::size_t ...Is>
constexpr std::array<T, sizeof...(Is)> makeArrayImpl(Func&& func, std::index_sequence<Is...>)
{
    return { func(std::integral_constant<std::size_t, Is>{})... };
}

template<typename T, std::size_t N, class Func>
constexpr std::array<T, N> makeArray(Func&& func)
{
    return makeArrayImpl<T>(std::forward<Func>(func), std::make_index_sequence<N>{});
}

constexpr auto executors = makeArray<IntExecutor, 0x1000>([&](auto x) {
    return emitExecutor<static_cast<int>(x)>();
});

int main(int argc, char* argv[])
{
    for (const auto& executor : executors)
    {
        executor(10);
    }
    return 0;
}