Initialize a simple 2D array with the given function at compile time

I want to create a 2D array populated with some well-known function without any investment in execution time.

To have an example, suppose that the function f(x, y) = 10 * y + x , let x be in {1, 2, 3} and y in {4, 5, 6} . I want to create a 2D array with content

 41 42 43 51 52 53 61 62 63 

Now the easiest way is to simply adjust the values โ€‹โ€‹directly in my sources. And it really suits my task, so the question is just out of curiosity.

I would like to create a metafunction and a structure that has some kind of black magic, which allows me to define an array of given sets of values โ€‹โ€‹for x and y . Like this:

 template<int X> struct Func { template<int Y> struct over { static const int value = 10 * Y + X; // f(x, y) }; }; template<int... args1> struct Rows { template<int... args2> struct Cols { static const int data[sizeof...(args1)][sizeof...(args2)]; }; }; template<int... args1> template<int... args2> const int Rows<args1...>::Cols<args2...>::data[sizeof...(args1)][sizeof...(args2)] = { { Func<args1>::over<args2>::value... } // This does not do what I want :( // Need some black magic here }; // Here is my precious table const auto& table = Rows<1, 2, 3>::Cols<4, 5, 6>::data; 

If I print the values โ€‹โ€‹from a table, I have this:

 41 52 63 0 0 0 0 0 0 

I understand what is happening, the term Func<args1>::over<args2>::value has two parameter packages in it, args1 and args2 , so applying ... on it expands them at the same time, and I have only 3 members instead nine.

If you have reached so far, you already understand what I want. So the question is, how do I do this? How to apply an ellipsis separately to both packages of parameters so that I can have a Cartesian combination in the initializer? Or maybe there are other ways to do this?

I know this answer and which answers . They use std::array instead of a simple array, so they first build 1D arrays and then initialize a 2D array with the number of 1D array. But if I understood correctly, this initialization should be done at runtime. I want to avoid this. However, I have no objection to std::array . I believe that with the right compiler, they are as fast as simple arrays.

By the way, here is my possible solution using a generic constexpr from C ++ 14 and a question about it. Any ideas on how to solve the problem using constexpr from C ++ 11 are also welcome.

+6
source share
1 answer

The only thing I found was to separate the parameter packages with commas and expand one of them, and then expand the other from the outside:

 #include <array> #include <utility> using namespace std; template<class T, TY, T... Xs> constexpr array<T, sizeof...(Xs)> a1{10*Y+Xs...}; template<class T, T... Xs, T... Ys> constexpr auto a2(integer_sequence<T, Xs...>, integer_sequence<T, Ys...>) { return array<array<T, sizeof...(Xs)>, sizeof...(Ys)>{a1<T, Ys, Xs...>...}; } array<array<int, 3>, 3> table(a2( integer_sequence<int, 1, 2, 3>(), integer_sequence<int, 4, 5, 6>() )); 

Asm result:

 table: .long 41 .long 42 .long 43 .long 51 .long 52 .long 53 .long 61 .long 62 .long 63 

Code in the compiler

+1
source

All Articles