• Listen to a special audio message from Bill Roper to the Hive Workshop community (Bill is a former Vice President of Blizzard Entertainment, Producer, Designer, Musician, Voice Actor) 🔗Click here to hear his message!
  • Read Evilhog's interview with Gregory Alper, the original composer of the music for WarCraft: Orcs & Humans 🔗Click here to read the full interview.

c++ nd-array functions

Status
Not open for further replies.
So, have you ever gotten tired of creating and initializing dynamic multi-dimensional arrays? Destroying them is a pain too.

Well, I created some functions that'll do it for you ^)^. They will take any number of dimensions with a size for each dimension.

Warning: mad c++ abuse

C++:
#pragma once

/*
*	void* nd_array(const std::initializer_list<int>& dimensions)
*		-	auto i = nd_array<int, int***>({ 1, 2, 3 });
*
*	void nd_array_free(void* arr, const std::initializer_list<int>& dimensions)
*		-	nd_array_free<int>(i, { 1, 2, 3 });
*
*	void nd_array_set(void* arr, const std::initializer_list<int>& dimensions, const T val)
*		-	nd_array_set<int>(i, { 1, 2, 3 }, 1)
*/

#include <stdint.h>
#include <initializer_list>

using namespace std;

template <class T>
void nd_array(uintptr_t* arr, const int* dim, const int* len) {
	if (dim < len - 1) {
		for (int i = 0; i < *(dim - 1); ++i) {
			*(uintptr_t**)(arr + i) = new uintptr_t[*dim];
			nd_array<T>((uintptr_t*) *(arr + i), dim + 1, len);
		}
	}
	else {
		for (int i = 0; i < *(dim - 1); ++i) {
			*(T**) (arr + i) = new T[*dim];
		}
	}
}

template <class T, class T2>
T2 nd_array(const std::initializer_list<int>& dimensions) {
	int* dim = new int[dimensions.size()];

	int i = 0;
	for (auto it = dimensions.begin(); it != dimensions.end(); ++it) {
		dim[i++] = *it;
	}

	auto arr = new uintptr_t[*dim];

	nd_array<T>(arr, dim + 1, dimensions.size() + dim);

	delete [] dim;

	return (T2)arr;
}

template <class T>
void nd_array_free(uintptr_t* arr, const int* dim, const int* len) {
	if (dim < len - 1) {
		for (int i = 0; i < *(dim - 1); ++i) {
			nd_array_free<T>((uintptr_t*) *(arr + i), dim + 1, len);
			delete [] * (uintptr_t**) (arr + i);
		}
	}
	else {
		for (int i = 0; i < *(dim - 1); ++i) {
			delete [] * (T**) (arr + i);
		}
	}
}

template <class T>
void nd_array_free(void* arr, const std::initializer_list<int>& dimensions) {
	int* dim = new int[dimensions.size()];

	int i = 0;
	for (auto it = dimensions.begin(); it != dimensions.end(); ++it) {
		dim[i++] = *it;
	}

	nd_array_free<T>((uintptr_t*)arr, dim + 1, dimensions.size() + dim);

	delete [] arr;
	delete [] dim;
}

template <class T>
void nd_array_set(uintptr_t* arr, const int* dim, const int* len, const T val) {
	if (dim < len) {
		for (int i = 0; i < *(dim - 1); ++i) {
			nd_array_set<T>((uintptr_t*) *(arr + i), dim + 1, len, val);
		}
	}
	else {
		for (int i = 0; i < *(dim - 1); ++i) {
			*(T*) (arr + i) = val;
		}
	}
}

template <class T>
void nd_array_set(void* arr, const std::initializer_list<int>& dimensions, const T val) {
	int* dim = new int[dimensions.size()];

	int i = 0;
	for (auto it = dimensions.begin(); it != dimensions.end(); ++it) {
		dim[i++] = *it;
	}

	nd_array_set<T>((uintptr_t*) arr, dim + 1, dimensions.size() + dim, val);

	delete [] dim;
}
 

Dr Super Good

Spell Reviewer
Level 64
Joined
Jan 18, 2005
Messages
27,258
It appears you are using the pointer method of multi dimensional arrays.

There are two disadvantages to this.
1. Chain pipeline dependencies. Since a read depends on the value of another read you end up having a lookup time dependant entirely on how fast you can read the pointers multiplied by the number of dimensions. Worse is that the data is often sparse (might not be allocated physically nearby) so caching is very poor.
2. Memory fragmentation overhead. Since the memory is not allocated in a single unit yet the entire array acts as a single unit, you have unnecessary overhead to store the fragmentation details that should logically not be present.

The main advantage of this approach is the support for sparse arrays where dimensions may be variable sized and only used dimensions are allocated, however your script does not really allow for that as far as I can tell.

In any case, an optimization could be to allocate each dimension as a separate array of continuous space. This would give you a fragmentation level equal to the number of dimensions of the array. It would also reduce the amount of dynamic memory allocation required considerably as each dimension would only use 1 memory allocation as opposed to the iterative approach currently which uses more allocations the deeper it has to go. The disadvantage is that it will allocate larger chunks (which could end up as full pages) so it may cause virtual address space depending on use.
 
Level 5
Joined
May 6, 2013
Messages
125
edit: see what you mean. nevermind.

@Kanadaj
JASS:
delete [] * (uintptr_t**) (arr + i);
uintptr_t is a typedef ensured to have the size of void*, while being treated as an integer type (so you can calculate and stuff). (usually typedef over unsigned long or unsigned int i guess).
so, let arr+i be the address of the i'th element of the array arr; Interpret this address as the value of a uintptr_t pointer pointer; dereference it once (so you get the uintptr_t pointer that is stored in the i'th index of the array arr); delete the array that this pointer points to (invoking the destructors of the objects if it had any)
or in short: delete the array pointed to by the pointer in arr
 
Last edited:
I know I'm necroing, but for the sake of future readers, please, don't do this shit.

Use vector<T> or vector<vector<T>> or vector<vector<vector<T>>>.
If you're nesting too many vectors, consider Boost.MultiArray.

And if you're using naked new and delete in your code, refactor. Look into using objects directly or std::unique_ptr<T> if needed.
 
Status
Not open for further replies.
Top