template<class A>
struct std_array_helper {
using type=A;
};
template<class A>
using array_t = typename std_array_helper<A>::type;
template<class T, std::size_t N0>
struct std_array_helper<T[N0]> {
using type=std::array<array_t<T>, N0>;
};
now
array_t<Foo[XSIZE][YSIZE]>
is
std::array< std::array<Foo, XSIZE>, YSIZE>
an alternative solutions is:
template<class T, std::size_t...Sz>
struct array_helper {
using type=T;
};
template<class T0, std::size_t...Ns>
using array_t = typename array_helper<T0, Ns...>::type;
template<class T, std::size_t N0, std::size_t...Ns>
struct array_helper<T, N0, Ns...>
{
using type=std::array<array_t<T, Ns...>, N0>;
};
this uses the syntax:
array_t<Foo, XSIZE, YSIZE>
if you prefer it.
We can even combine the two, allowing either syntax.
template<class T, std::size_t...Sz>
struct array_helper {
using type=T;
};
template<class T0, std::size_t...Ns>
using array_t = typename array_helper<T0, Ns...>::type;
template<class T, std::size_t N0, std::size_t...Ns>
requires (!std::is_array_v<T>)
struct array_helper<T, N0, Ns...>
{
using type = std::array<array_t<T, Ns...>, N0>;
};
template<class T, std::size_t N0, std::size_t...Ns>
struct array_helper<T[N0], Ns...>:
array_helper<array_t<T, Ns...>, N0>
{};
and now
array_t< Foo[XSIZE], YSIZE >
works.
But be careful - the order is tricky!
int[3][2] is an array of 3 elements of arrays of 2 elements.
To keep this the same we want
array_t<int, 3, 2>
to be
std::array< std::array< int, 2 >, 3>
not
std::array< std::array< int, 3 >, 2>
here are test cases to determine if you got the order right:
static_assert( std::is_same_v< std::array<int, 3>, array_t<int, 3> > );
static_assert( std::is_same_v< std::array< std::array<int, 2>, 3>, array_t<int, 3, 2> > );
static_assert( std::is_same_v< std::array< std::array<int, 2>, 3>, array_t<int[3], 2> > );
static_assert( std::is_same_v< std::array< std::array<int, 2>, 3>, array_t<int[3][2]> > );
remove whichever have the wrong syntax for your chosen array_t
.
Live example
Now, even this might be wrong. It feels incorrect that
array_t<int[3], 2>
doesn't have sub-arrays of size 3, yet
array_t<int[3][2]>
feels like it should also be the same array, and the layout of int[3][2]
should agree with array_t<int[3][2]>
and agree with array_t<int, 3, 2>
.
Also, array_t< array_t<int, 3>, 2>
should be the same as array_t<int[3], 2>
.
These requirements disagree with each other. I mean, all over the place they disagree.
Probably the simplest way to resolve this is to require only [][][]
syntax, or don't permit mixed []
and ,
syntax.
Having array_t<int[3][2]>
with the same layout as int[3][2]
is high value. Similarly, having array_t< int, 3, 2 >
syntax is high value. Probably we want array_t<int, 3, 2>
to mean the same as int[3][2]
? Throw away this being equal to array_t< array_t<int, 3>, 2>
- instead it equals array_t<array_t<int,2>,3>
. Finally, block array_t<int[3], 2>
syntax as confusing.
Then, split the array_t< T, 1,2,3,...>
from array_t<T[1][2][3]...>
templates to minimize confusion.