Initializing n-dimensional std::array with a list of components

 
InitializeNArray<4, bool, NARRAY>()(narray, {1,2,3,2}, true);

is supposed to initialize a 4-dimensional std::array a with a[1][2][3][2] = true.
The commented-out line, which is the desired recursion, does not compile for some reason, and the problem third parameter cannot be deduced. So I placed some temporary lines to work in the special case only. Can anyone make that recursion work?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
#include <iostream>
#include <array>
#include <vector>
#include <memory>

template <typename, int...> struct NArray;

template <typename T, int N>
struct NArray<T, N> {
    using type = std::array<T, N>;
};

template <typename T, int FIRST, int... REST>
struct NArray<T, FIRST, REST...> {
    using type = std::array<typename NArray<T, REST...>::type, FIRST>;
};

template <typename T, typename NARRAY>
auto getSubNArray (std::reference_wrapper<NARRAY> narray, int component) -> decltype(std::ref(narray.get()[component])) {
	return std::ref(narray.get()[component]);
}

template <int SIZE, typename T, typename NARRAY>
struct InitializeNArray {
	void operator() (std::reference_wrapper<NARRAY> narray, const std::vector<int>& v, const T& value, int n = 0) {
//		InitializeNArray<SIZE - 1, T, decltype(getSubNArray<T>(narray, v[n]).get())> (getSubNArray<T>(narray, v[n]), v, value, n+1);  // Won't compile.
		auto a = getSubNArray<T>(narray, v[0]);  // temporary lines to fulfill the above line's job
		auto b = getSubNArray<T>(a, v[1]);
		auto c = getSubNArray<T>(b, v[2]);
		auto d = getSubNArray<T>(c, v[3]);
		d.get() = value;
	}
};

template <typename T, typename NARRAY>
struct InitializeNArray<0, T, NARRAY> {
	void operator() (std::reference_wrapper<NARRAY> narray, const std::vector<int>&, const T& value, int = 0) {
		narray.get() = value;
	}
};

int main() {
    using NARRAY = NArray<bool,2,4,5,3>::type;
    NARRAY narray{};
    InitializeNArray<4, bool, NARRAY>()(narray, {1,2,3,2}, true);
    std::cout << std::boolalpha << narray[1][2][3][2] << std::endl;  // true
}


Incidentally, this is a sub-problem within the task of initializing more generally with
1
2
template <typename T, int... N, typename... ARGS>
typename NArray<T, N...>::type NDimensionalArray (const T& value, ARGS&&... args)

Once this subproblem is fixed, then the bigger task is fixed too.
Last edited on
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#include <array>
#include <iostream>

template < typename E, typename T > void fill( E& elem, const T& v ) { elem = v ; }

template < typename E, std::size_t N, typename T >
void fill( std::array<E,N>& array, const T& v ) { for( auto& e : array ) fill( e, v ) ; }

template < typename, std::size_t... > struct NArray ;

template < typename T, std::size_t N > struct NArray<T,N> { using type = std::array<T,N> ; };

template < typename T, std::size_t FIRST, std::size_t... REST >
struct NArray<T,FIRST,REST...> { using type = std::array< typename NArray<T,REST...>::type, FIRST >; };

int main()
{
    NArray< int, 5, 6, 7, 8, 9 >::type a ;
    fill( a, 99 ) ;
    std::cout << a[3][4][2][1][5] << '\n' ;
}

http://coliru.stacked-crooked.com/a/e3f067eb3c456943
But only one element is supposed to be initialized.
 
InitializeNArray<4, bool, NARRAY>()(narray, {1,2,3,2}, true);

where narray = NArray<bool,2,4,5,3>::type, is supposed to result in narray[1][2][3][2] = true, while all other elements of narray are the default value--false.
Last edited on
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#include <array>
#include <iostream>
#include <vector>

template < typename E, typename T > void assign( E& elem, std::vector<std::size_t>, const T& v ) { elem = v ; }

template < typename E, std::size_t N, typename T >
void assign( std::array<E,N>& array, std::vector<std::size_t> pos, const T& v )
{ assign( array[ pos.front() ], { pos.begin()+1, pos.end() }, v ) ; }

template < typename, std::size_t... > struct NArray ;

template < typename T, std::size_t N > struct NArray<T,N> { using type = std::array<T,N> ; };

template < typename T, std::size_t FIRST, std::size_t... REST >
struct NArray<T,FIRST,REST...> { using type = std::array< typename NArray<T,REST...>::type, FIRST >; };

int main()
{
    NArray< int, 5, 6, 7, 8, 9 >::type a ;
    assign( a, { 3, 4, 2, 1, 5 }, 99 ) ;
    std::cout << a[3][4][2][1][5] << '\n' ;
}

http://coliru.stacked-crooked.com/a/3f1be989a3b31f3d
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#include <array>
#include <iostream>

template < std::size_t... > struct pos {};

template < typename E, typename T > void assign( E& elem, pos<>, const T& v ) { elem = v ; }

template < typename E, std::size_t N, typename T, std::size_t FIRST, std::size_t... REST >
void assign( std::array<E,N>& array, pos<FIRST,REST...>, const T& v )
{ assign( array[FIRST], pos<REST...>{}, v ) ; }

template < typename, std::size_t... > struct NArray ;

template < typename T, std::size_t N > struct NArray<T,N> { using type = std::array<T,N> ; };

template < typename T, std::size_t FIRST, std::size_t... REST >
struct NArray<T,FIRST,REST...> { using type = std::array< typename NArray<T,REST...>::type, FIRST >; };

int main()
{
    NArray< int, 5, 6, 7, 8, 9 >::type a ;
    assign( a, pos< 3, 4, 2, 1, 5 >{}, 99 ) ;
    std::cout << a[3][4][2][1][5] << '\n' ;
}

http://coliru.stacked-crooked.com/a/10cf945525772cbb
Thanks. My attempted solution in my first post was trying to follow the same type of recursion as your solutions. I just don't get why my way required me to state the template parameters in the recursion (which got in the way of compiling), while in your recursion the template parameters were deducible. I still don't get when templates are deducible and when they are not.
Last edited on
Your InitializeNArray is a class template (with specialisation); there is no deduction for class templates.

My fill<>() and assign<>() are function templates (with overload); deduction is possible for function templates.
Thanks for your explanation. Here is the full solution to my original larger task of using the more general function of the form
 
NDimensionalArray<double, 5,6,7,8,9> (3.14,  1,2,3,2,4,  3,3,2,1,2,  0,1,3,1,2, ...);

for anyone interested:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
#include <array>
#include <iostream>
#include <vector>
#define show(variable) std::cout << #variable << " = " << variable << std::endl;

template <typename, std::size_t...> struct NArray;

template <typename T, std::size_t N>
struct NArray<T, N> {using type = std::array<T, N>;};

template <typename T, std::size_t FIRST, std::size_t... REST>
struct NArray<T, FIRST, REST...> {using type = std::array<typename NArray<T, REST...>::type, FIRST>;};

template <typename CONTAINER, typename E, typename T>
void assign (E& element, const CONTAINER&, const T& v) {element = v;}

template <typename CONTAINER, typename SUBARRAY, std::size_t N, typename T>
void assign (std::array<SUBARRAY, N>& narray, const CONTAINER& pos, const T& v) {
    assign<CONTAINER> (narray[*pos.begin()], {pos.begin() + 1, pos.end()}, v);  // Thanks to JLBorges for this.
}

template <typename T, int... N, typename... ARGS>
typename NArray<T, N...>::type NDimensionalArray (const T& value, ARGS&&... args) {
    typename NArray<T, N...>::type narray{};
    const auto initializer = {std::forward<ARGS>(args)...};
    const int size = sizeof...(N),  S = initializer.size() / size;
    for (int i = 0;  i < S;  i++)
    	assign (narray, std::vector<std::size_t>(initializer.begin() + i*size, initializer.begin() + (i+1)*size), value);
    return narray;
}

int main() {
    const auto multiArray = NDimensionalArray<double, 5,6,7,8,9> (3.14,  1,2,3,2,4,  3,3,2,1,2,  0,1,3,1,2);
    show (multiArray[1][2][3][2][4]);  // 3.14
    show (multiArray[3][3][2][1][2]);  // 3.14
    show (multiArray[0][1][3][1][2]);  // 3.14
}
Last edited on
Topic archived. No new replies allowed.