Search code examples
c++c++11constexpr

Lookup table with constexpr


I'm looking to create a lookup table of coordinates, something like:

int a[n][2] = {{0,1},{2,3}, ... }

For a given n, to be created at compile time. I started looking into constexpr, but is seems like a function returning a constexpr std::vector<std::array <int, 2> > isn't an option, as I get:

invalid return type 'std::vector<std::array<int, 2ul> >' of constexpr function

How can create such a compile time array?


Solution

  • I'll dump the code first, adding references and comments where necessary/appropriate later. Just leave a comment if the result is somewhat close to what you're looking for.

    Indices trick for pack expansion (required here to apply the generator), by Xeo, from this answer, modified to use std::size_t instead of unsigned.

    #include <cstddef>
    
    // by Xeo, from https://stackoverflow.com/a/13294458/420683
    template<std::size_t... Is> struct seq{};
    template<std::size_t N, std::size_t... Is>
    struct gen_seq : gen_seq<N-1, N-1, Is...>{};
    template<std::size_t... Is>
    struct gen_seq<0, Is...> : seq<Is...>{};
    

    Generator function:

    #include <array>
    
    template<class Generator, std::size_t... Is>
    constexpr auto generate_array_helper(Generator g, seq<Is...>)
    -> std::array<decltype(g(std::size_t{}, sizeof...(Is))), sizeof...(Is)>
    {
        return {{g(Is, sizeof...(Is))...}};
    }
    
    template<std::size_t tcount, class Generator>
    constexpr auto generate_array(Generator g)
    -> decltype( generate_array_helper(g, gen_seq<tcount>{}) )
    {
        return generate_array_helper(g, gen_seq<tcount>{});
    }
    

    Usage example:

    // some literal type
    struct point
    {
        float x;
        float y;
    };
    // output support for `std::ostream`
    #include <iostream>
    std::ostream& operator<<(std::ostream& o, point const& p)
    {  return o << p.x << ", " << p.y;  }
    
    // a user-defined generator
    constexpr point my_generator(std::size_t curr, std::size_t total)
    {
        return {curr*40.0f/(total-1), curr*20.0f/(total-1)};
    }
    
    int main()
    {
        constexpr auto first_array = generate_array<5>(my_generator);
        constexpr auto second_array = generate_array<10>(my_generator);
    
        std::cout << "first array: \n";
        for(auto p : first_array)
        {
            std::cout << p << '\n';
        }
        std::cout << "========================\n";
    
        std::cout << "second array: \n";
        for(auto p : second_array)
        {
            std::cout << p << '\n';
        }
    }