I want to create a 2D-array populated by some known function with no runtime overhead.
To have an example, suppose a 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 to go is just hard-code the values directly in my sources. And it is indeed appropriate for my task, so the question is just out of curiosity.
I would like to create a metafunc and a struct possessed with some kind of black magic, which allows me to define an array out 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 values from the table, I have this:
41 52 63
0 0 0
0 0 0
I understand what's happening, the term Func<args1>::over<args2>::value
has two parameter packs in it, args1
and args2
, so applying ...
on it expand them simultaneously, and I only have 3 members instead of 9.
If you've reached so far, you've already understood what I want. So the question is, how do I do it? How do I apply ellipsis separately to both parameter packs so I can have cartesian combination in the initializer? Or maybe there are some other ways to do it?
I am aware of this answer and that answer. They use std::array
instead of plain array, so they first construct 1D-arrays, and then initialize 2D-array with a number of 1D-array. But if I understood correctly, this initialization has to be done in runtime. I want to avoid that. However, I have no objections against std::array
. I suppose that with a proper compiler they are just as fast as plain arrays.
By the way, here is my possible solution using generalized constexpr
from C++14 and a question about it. Any ideas on how to solve the task with constexpr
from C++11 are also welcomed.
The only way I found it is to separate the parameter packs by commas and expand one of them, and then to expand the other from the outside:
#include <array>
#include <utility>
using namespace std;
template<class T, T Y, 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>()
));
The asm result is this:
table:
.long 41
.long 42
.long 43
.long 51
.long 52
.long 53
.long 61
.long 62
.long 63
Code in Compiler Explorer
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With