Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Declaring 2 (or even multi-) dimensional std::arrays elegantly

Tags:

c++

stdarray

I'm using 2-dimensional arrays based on std::array.

Basically instead of:

MyType myarray[X_SIZE][Y_SIZE];

I have:

std::array<std::array<MyType, Y_SIZE>, X_SIZE> myarray;

This works perfectly fine but IMO the declaration is not very readable.

Is there a way to declare this using some clever C++ template mecanism, so the declaration could look something like this?

My2DArray<Mytype, X_SIZE, Y_SIZE> myarray;
like image 278
Jabberwocky Avatar asked Sep 02 '19 14:09

Jabberwocky


People also ask

What is a multi-dimensional array?

An array with more than one dimension is known as a multi-dimensional array. The most commonly used multi-dimensional arrays are 2-D and 3-D arrays. We can say that any higher dimensional array is basically an array of arrays.

What is an example of a 2D array?

A very common example of a 2D Array is Chess Board. A chessboard is a grid containing 64 1×1 square boxes. You can similarly visualize a 2D array.

What are multidimensional arrays in PHP?

For this, we have multidimensional arrays. A multidimensional array is an array containing one or more arrays. PHP supports multidimensional arrays that are two, three, four, five, or more levels deep. However, arrays more than three levels deep are hard to manage for most people.

What are the elements in a three dimensional array called?

Accessing Elements of Three-Dimensional Arrays Elements in three-dimensional arrays are commonly referred by x [i] [j] [k] where ‘i’ is the array number, ‘j’ is the row number and ‘k’ is the column number.


2 Answers

If you want just 2D arrays, it's fairly straightforward:

template <class T, std::size_t X, std::size_t Y>
using My2DArray = std::array<std::array<T, Y>, X>;

If you want a generic mechanism not limited to 2D arrays, it can be done too:

template <class T, std::size_t N, std::size_t... Ns>
struct AddArray {
    using type = std::array<typename AddArray<T, Ns...>::type, N>;
};

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

template <class T, std::size_t... N>
using MyNDArray = typename AddArray<T, N...>::type;

[Live example]

like image 145
Angew is no longer proud of SO Avatar answered Oct 21 '22 07:10

Angew is no longer proud of SO


We can wrap one of the existing MyNDArray / md_array_t answers to arrive at an alternative interface:

template <typename Arr, std::size_t... Is>
constexpr auto make_array_impl(std::index_sequence<Is...>)
    -> md_array_t<std::remove_all_extents_t<Arr>,
        std::extent_v<Arr, Is>...>;

template <typename Arr>
using make_array = decltype(make_array_impl<Arr>(
    std::make_index_sequence<std::rank_v<Arr>>{}));

Compiler Explorer

This allows us to write make_array<int[4][5][6]> to mean array<array<array<int, 6>, 5, 4>.


Explanation:

  1. std:rank gives the number of dimensions of an array type. Thus, for int[4][5][6], it returns 3.
  2. We hand this to make_index_sequence to end up with a pack of indices. (0, 1, 2)
  3. std::remove_all_extents gives us the underlying type of the array; T[a][b]...[n] -> T (int)
  4. std::extent gives us the extent of the given dimension. We call this for each index. (4, 5, 6).

By passing these to our previously-implemented md_array_t, we end up with md_array_t<int, 4, 5, 6>, which produces what we want.

like image 34
Justin Avatar answered Oct 21 '22 08:10

Justin