Variable Dimension Array

Finally, after months of putting this project off, I figured out a simple algorithm that makes the resize function work! I tested it and was overjoyed to see it working as it should.

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
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
//array.hpp
//This header defines the array class which allows for the creation
//of dynamic arrays with any given dimension or type.
#pragma once
#ifndef MULTIDIM_ARRAY
#define MULTIDIM_ARRAY

#include <stdexcept>
#include <cstdarg>

template<typename T,typename SizeType=unsigned>
class array{
	protected:
		SizeType num_dimensions;//number of dimensions
		SizeType data_size;//size of data (in elements)
		SizeType* dimensions;//array of dimension sizes
		T* data;//the data
		static void memcpy(T* dst,T* src,SizeType len){
			if(dst==src){
				return;}
			SizeType x=0;
			while(x<len){
				*dst=*src;
				++dst;
				++src;
				++x;}}
		static void memmove(T* dst,T* src,SizeType len){
			if(dst==src){
				return;}
			SizeType x=0;
			if(dst<src){
				while(x<len){
					*dst=*src;
					++dst;
					++src;
					++x;}}
			else{
				dst+=len;
				src+=len;
				while(x<len){
					*dst=*src;
					--dst;
					--src;
					++x;}}}
	public:
		//accesor methods
		inline SizeType numdim() const{
			return num_dimensions;}
		inline SizeType dimsize(SizeType n) const{
			if(n>=num_dimensions){
				throw std::out_of_range("array::dimsize(SizeType) const");}
			return dimensions[n];}
		//use to get data directly from data array
		inline T& operator[](SizeType x){
			if(x>=datasize){
				throw std::out_of_range("array::operator[]");}
			return data[x];}
		inline const T& operator[](SizeType x) const{
			if(x>=datasize){
				throw std::out_of_range("array::operator[](SizeType) const");}
			return data[x];}
		//constructor
		//no arguments
		array(){
			num_dimensions=0;
			data_size=0;
			dimensions=NULL;
			data=NULL;}
		//array(number of dimensions,<dimension sizes>...)
		array(SizeType ndim,...){
			num_dimensions=ndim;
			if(ndim==0){
				throw dimensionless_array("array::array(SizeType,...)");}
			else{
				dimensions=new SizeType[ndim];
				va_list args;
				va_start(args,ndim);
				SizeType size=1;
				for(int a=0;a<ndim;++a){
					dimensions[a]=va_arg(args,T);
					if(dimensions[a]==0){
						--a;
						--ndim;
						continue;}
					size*=dimensions[a];}
				data=new T[size];
				data_size=size;
				va_end(args);}}
		//destrctor
		~array(){
			delete[] dimensions;
			delete[] data;}
		//returns a reference to the element at the given position
		//operator()(x,y,z,...)
		T& operator()(SizeType x,...){
			SizeType index,pos=x,dim=1,a=1;
			va_list args;
			va_start(args,x);
			for(;a<num_dimensions;++a){
				if((index=va_arg(args,SizeType))>=dimensions[a]){
					printf("num_dimensions: %inindex: %indimensions[a]: %in",num_dimensions,index,dimensions[a]);
					throw std::out_of_range("array::operator()(SizeType,...)");}
				dim*=dimensions[a];
				pos+=index*dim;}
			va_end(args);
			return data[pos];}
		const T& operator()(SizeType x,...) const{
			SizeType index,pos=x,dim=1,a=1;
			va_list args;
			va_start(args,x);
			for(;a<num_dimensions;++a){
				if((index=va_arg(args,SizeType))>=dimensions[a]){
					throw std::out_of_range("array::operator()(SizeType,...) const");}
				dim*=dimensions[a];
				pos+=index*dim;}
			va_end(args);
			return data[pos];}
		//resizes the array, even with different dimensions
		//resize(number of dimensions (0 for current),<dimension sizes>...)
		//TODO: fix this function so that it keeps old data
		SizeType resize(SizeType ndim,...){
			if(ndim==0){
				throw std::invalid_argument("Array resized to 0 dimensions in array::resize(SizeType,...)");}
			SizeType a,x,y,size=1,mwidth;
			SizeType* ndimen=new SizeType[ndim];
			T* ndata;
			va_list args;
			va_start(args,ndim);
			for(a=0;a<ndim;++a){
				ndimen[a]=va_arg(args,SizeType);
				//make sure that there isn't a size 0 dimension
				if(ndimen[a]==0){
					delete[] ndimen;
					printf("DEBUG");
					throw std::invalid_argument("Passed a 0-length dimension in array::resize(SizeType,...)");}
				size*=ndimen[a];}
			ndata=new T[size];
			mwidth=(dimensions[0]<ndimen[0]?dimensions[0]:ndimen[0]);
			x=0;
			y=0;
			//copy the data to the new buffer
			while(x<data_size && y<size){
				memcpy(ndata+y,data+x,mwidth);
				x+=dimensions[0];
				y+=ndimen[0];}
			//fill in the rest of the variables
			delete[] dimensions;
			dimensions=ndimen;
			num_dimensions=ndim;
			data_size=size;
			delete[] data;
			data=ndata;
			return data_size;}
		//returns the size in bytes of the contained data
		inline SizeType datasize() const{
			return data_size*sizeof(T);}
		//returns the number of elements contained
		inline SizeType size() const{
			return data_size;}
		//operators
		array<T,SizeType>& operator=(const array<T,SizeType>& rhs){
			delete[] data;
			data=new T[rhs.data_size];
			data_size=rhs.data_size;
			delete[] dimensions;
			dimensions=new T[rhs.num_dimensions];
			num_dimensions=rhs.num_dimensions;
			memcpy(data,rhs.data,data_size);
			return *this;}
		template<typename U>
		bool operator==(const array<U>& rhs) const{
			SizeType x;
			for(x=0;x<len;++x){
				if(data[x]=rhs.data[x]){
					return false;}}
			return true;}
		template<typename U>
		inline bool operator!=(const array<U>& rhs) const{
			return !(*this==rhs);}};

#endif 

What do you guys think?
closed account (zb0S216C)
Call me stupid, but, are we reviewing the validity of the code?
I've already tested it - I just wanted to get your opinions about design, naming, and maybe a few function suggestions.
It would appear the code requires that T is a POD based on the use of memmove/memcpy but it also appears that array can be specialized with a non-POD type (unless I'm missing this somewhere) which would result in undefined behavior. You can prevent that (that is, require T be a POD) with some minor template meta-programming. Nevermind. I see that you re-implemented std::copy() as a static member function and called it memcpy/memmove.

It also seems to me that smart pointers would simplify memory management a bit.

A few typedefs would simplify the code.

And the swap idiom would simplify assignment:

1
2
3
4
5
6
7
8
9
10
11
typedef array<T, SizeType> type;
typedef type& reference;
...
void swap(reference other) {
    // swap all member variables;
}

reference operator=(type rhs) { // take rhs by value, invoke copy ctor
    this->swap(rhs);
    return *this;
}


Also, member variables should be private. There is no reason for anything to be protected in this class.
Last edited on
Topic archived. No new replies allowed.