HiveBrain v1.2.0
Get Started
← Back to all entries
patterncppMinor

Allocate N-dimensional array in C++

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
dimensionalarrayallocate

Problem

I need to allocate n-dimensional arrays of type T, currently I am using the following functions. Is there a better way to do this? Also, ideally I would like to allocate a 1-D array and access that as a N-D array but I could not figure out a better way of doing that. Using existing matrix library is not an option as I want access to raw pointers.

template 
T*** allocate_3d_array(Ti nx, Ti ny, Ti nz){
    T*** A = new T**[nx];
    for(Ti i(0); i 
void release_3d_array(T*** A, Ti nx, Ti ny, Ti nz){
    for (Ti i = 0; i 
T** allocate_2d_array(Ti nx, Ti ny){
    T** A = new T*[nx];
    for(Ti i(0); i 
void release_2d_array(T** A, Ti nx, Ti ny){
    for (Ti i = 0; i 
T* allocate_1d_array(Ti nx){
    T *A = new T[nx];
    return A;
}

template 
    void release_1d_array(T* A, Ti nx){
    delete[] A;
}

Solution

I will add some more on top of Loki's answer. The main thing you left out there is type safety. By making those indirections you're basically stripping off everything the standard library templated part can offer to you. On top of that, decltype(), sizeof() and many other functions that rely on compile time type information are rendered useless. It is crucial thing to be fixed.

Suggested implementation

So, first of all we need to find out how can we make convenient type/type alias to generate for us array type with specified dimensions?

The word type suggests that it needs to be compile time and the best (probably the only) way is template metaprogramming.

The way it is done is as follows: we take a type and a sequence of dimensions (e.g. int, 3, 4, 5) and starting from the end, append every dimension to the type.

#ifndef GENERATE_DIMENSIONS_H
#define GENERATE_DIMENSIONS_H
#include 

template 
struct generate_dimensions
{
    using type = typename generate_dimensions::type[first];
};

template 
struct generate_dimensions
{
    using type = T[first];
};

template 
using generate_dimensions_t = typename generate_dimensions::type;

#endif


The recursion is stopped when it reaches single dimension case, and appends other dimensions on the way back. The allocation of the array is trivial:

new generate_dimensions_t;


In case you want to defer construction, we can write this:

operator new(sizeof(generate_dimensions_t));


The size of the array type is calculated correctly, which is one of our goals.

This is pretty much it. Stay calm, and embrace C++.

Code Snippets

#ifndef GENERATE_DIMENSIONS_H
#define GENERATE_DIMENSIONS_H
#include <cstddef>

template <typename T, std::size_t first, std::size_t ... rest>
struct generate_dimensions
{
    using type = typename generate_dimensions<T, rest...>::type[first];
};

template <typename T, std::size_t first>
struct generate_dimensions<T, first>
{
    using type = T[first];
};

template <typename T, std::size_t first, std::size_t ... rest>
using generate_dimensions_t = typename generate_dimensions<T, first, rest...>::type;

#endif
new generate_dimensions_t<int, 3, 4, 5>;
operator new(sizeof(generate_dimensions_t<int, 3, 4, 5>));

Context

StackExchange Code Review Q#139156, answer score: 3

Revisions (0)

No revisions yet.