I am trying to create a constexpr std::array
with precompiled handler functions for my emulator. The code below works just fine for smaller numbers like 0x250
, but everything above causes a 'C1026 parser overflow, program too complex' when used with the recent version of MSVC.
#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;
}
I need an std::array
with the size of 0x1000
. I can achieve that by using 4
smaller static_for()
loops from 0
to 0x250
, but I feel like that's an ugly solution.
Does anybody know a proper way to fill an constexpr std::array
with template functions?
Have you tried the solution based over 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>{}); }
I can't test it with MSVC but, in my Linux platform, g++ and clang++ compile also (with long, long time)
constexpr auto lut = makeLut<0x10000u>();
I've modified the code max66 posted a little to allow constexpr array creation using a custom lamdba function. I am just pasting it here in case anybody needs it.
#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;
}
User contributions licensed under CC BY-SA 3.0