Populate An Array Using Constexpr at Compile-time

后端 未结 4 1775
[愿得一人]
[愿得一人] 2020-11-29 03:32

I would like to populate an array of enum using constexpr. The content of the array follows a certain pattern.

I have an enum separating ASCII character set into fou

相关标签:
4条回答
  • 2020-11-29 04:05

    IMHO the best way to do this is simply write a tiny setup program that will generate table for you. And then you can either throw out the setup program, or check it in alongside the generated source code.

    The tricky part of this question is just a duplicate of this other one: Is it possible to create and initialize an array of values using template metaprogramming?

    The trick is, it's impossible to write anything like

    Type table[256] = some_expression();
    

    at file scope, because global arrays can be initialized only with literal (source-level) initializer-lists. You can't initialize a global array with the result of a constexpr function, even if you could somehow get that function to return a std::initializer_list, which you can't because its constructor isn't declared constexpr.

    So what you have to do is get the compiler to generate the array for you, by making it a static const data member of a template class. After one or two levels of metaprogramming that I'm too confused to write out, you'll bottom out in a line that looks something like

    template <int... Indices>
    Type DummyStruct<Indices...>::table[] = { whichCategory(Indices)... };
    

    where Indices is a parameter-pack that looks like 0,1,2,... 254,255. You construct that parameter-pack using a recursive helper template, or maybe just using something out of Boost. And then you can write

    constexpr Type (&table)[] = IndexHelperTemplate<256>::table;
    

    ...But why would you do all that, when the table is only 256 entries that will never change unless ASCII itself changes? The right way is the simplest way: precompute all 256 entries and write out the table explicitly, with no templates, constexpr, or any other magic.

    0 讨论(0)
  • 2020-11-29 04:10

    In C++17 ::std::array has been updated to be more constexpr friendly and you can do the same as in C++14, but without some of the scary looking hacks to get around the lack of constexpr in crucial places. Here is what the code would look like there:

    #include <array>
    
    enum Type {
        Alphabet,
        Number,
        Symbol,
        Other,
    };
    
    constexpr ::std::array<Type, 128> MagicFunction()
    {
       using result_t = ::std::array<Type, 128>;
       result_t result = {Other};
       result[65] = Alphabet;
       //....
       return result;
    }
    
    const ::std::array<Type, 128> table = MagicFunction();
    

    Again MagicFunction still needs to obey the rather loose constexpr rules. Mainly, it may not modify any global variables or use new (which implies modifying global state, namely the heap) or other such things.

    0 讨论(0)
  • 2020-11-29 04:10

    The way to do this in C++14 looks like this:

    #include <array>
    
    enum Type {
        Alphabet,
        Number,
        Symbol,
        Other,
    };
    
    constexpr ::std::array<Type, 128> MagicFunction()
    {
       using result_t = ::std::array<Type, 128>;
       result_t result = {Other};
       const result_t &fake_const_result = result;
       const_cast<result_t::reference>(fake_const_result[65]) = Alphabet;
       //....
       return result;
    }
    
    const ::std::array<Type, 128> table = MagicFunction();
    

    No clever template hackery required any longer. Though, because C++14 didn't really undergo a thorough enough review of what did and didn't have to be constexpr in the standard library, a horrible hack involving const_cast has to be used.

    And, of course, MagicFunction had better not modify any global variables or otherwise violate the constexpr rules. But those rules are pretty liberal nowadays. You can, for example, modify all the local variables you want, though passing them by reference or taking their addresses may not work out so well.

    See my other answer for C++17, which allows you to drop some of the ugly-looking hacks.

    0 讨论(0)
  • 2020-11-29 04:19

    Ignoring ALL the issues, indices to the rescue:

    template<unsigned... Is> struct seq{};
    template<unsigned N, unsigned... Is>
    struct gen_seq : gen_seq<N-1, N-1, Is...>{};
    template<unsigned... Is>
    struct gen_seq<0, Is...> : seq<Is...>{};
    
    template<unsigned... Is>
    constexpr Table MagicFunction(seq<Is...>){
      return {{ whichCategory(Is)... }};
    }
    
    constexpr Table MagicFunction(){
      return MagicFunction(gen_seq<128>{});
    }
    

    Live example.

    0 讨论(0)
提交回复
热议问题