Wum
Wum

Reputation: 306

C++14 compile time std::array with variadic templates

I'd like to build a compile time lookup table using c++14 variadic templates. At the moment I'm there:

static const unsigned kCount = 5;

template<unsigned Index>
constexpr auto getRow(void)
{
    return std::array<unsigned, 2> { Index, Index * Index };
}

template<unsigned... Indices>
constexpr auto generateTable(std::index_sequence<Indices...>)
{
    return std::array<std::array<unsigned, 2>, sizeof...(Indices)>
    {
        // This is were I'm stuck. How to build a std::array using Indices as template parameter in getRow()?
    };
}

constexpr auto generate(void)
{
    return generateTable(std::make_index_sequence<kCount>{});
}

I want the table to be in a std::array. Each row consists of a std::array with 2 columns. I'm stuck in generateTable() where I need to somehow pass my Indices to getRow() as a template parameter.

Is this achievable using std::integer_sequence and template parameter pack expansion or do I need to implement the recursion on my own?

(getRow() is simplified - the value types are actually coming from templated types. Index * Index is just a placeholder. I need to know the way how to call getRow() using parameter pack expansion.)

Upvotes: 3

Views: 951

Answers (2)

max66
max66

Reputation: 66200

+1 for the solution of KyleKnoepfel but I have problems compiling your code in my amd64 linux because "error: no matching function for call to 'generateTable'" and "candidate template ignored: substitution failure : deduced non-type template argument does not have the same type as the its corresponding template parameter ('unsigned long' vs 'unsigned int')"

The problem is that std::make_index_sequence<kCount>{} generate a sequence of std::size_t. If std::size_t is defined as unsigned int, all goes well; if (like in my platform) std::size_t is defined as unsigned long, the following declaration didn't work

template<unsigned... Indices>
constexpr auto generateTable(std::index_sequence<Indices...>)

Suggestion: use ever std::size_t instead unsigned; particularly

template<std::size_t ... Indices>
constexpr auto generateTable(std::index_sequence<Indices...>)

En passant, initialize a std::array with { val1, val2 } (only one level of braces) it's perfectly legal in C++14 but (IMHO) I think it's better to use the old (C++11) syntax with a double level of braces ({ { val1, val2 } }); this for backwards compatibility (as pointed by Wum) and to avoid annoying warnings with some compilers (like clang++ 3.5). So I suggest to use a second level of braces in array declaration/initialization, so

return std::array<unsigned, 2> { { Index, Index * Index } };

and

return std::array<std::array<unsigned, 2>, sizeof...(Indices)>
 { { getRow<Indices>() ... } };

p.s.: sorry for my bad English.

Upvotes: 4

Kyle Knoepfel
Kyle Knoepfel

Reputation: 1698

Looks like you're almost there. Just rely on parameter-pack expansion:

return std::array<std::array<unsigned, 2>, sizeof...(Indices)>
{
   getRow<Indices>()...
};

where the getRow<Indices>()... line will expand to:

getRow<0>(), getRow<1>(), ..... , getRow<sizeof...(Indices)-1>()

Upvotes: 9

Related Questions