// // nd_array.hpp // // Created by Joshua Moerman on 10/25/11. // Copyright 2011 Vadovas. All rights reserved. // /* Dynamic multi-dimensional array. With the usual c-syntax: v[x][y][z]. Memory is allocated as one big block, instead of multiple smaller blocks (which is the case with std::vector>). Showed no difference in speed compared to std::vector> (in release build), so you better can use std::vector. This class can be used te reinterpret a big chunk of memory as a n-dimensional array. (Could be useful if your legacy-code gives you somthing like that). It is templated in number of dimensions (and of course type). With extra effort you could make the dimension dynamic (ie not compile-time, but run-time), I leave that as an excercise to the reader. */ #ifndef ND_ARRAY_HPP #define ND_ARRAY_HPP #include #include #include #include // C++11, right here :D (if this fails, try using tr1) template class nd_array{ public: typedef T & reference; typedef T const & const_reference; typedef T * iterator; typedef T const * const_iterator; typedef size_t size_type; typedef ptrdiff_t difference_type; typedef T value_type; typedef T * pointer; typedef T const * const_pointer; typedef std::reverse_iterator reverse_iterator; typedef std::reverse_iterator const_reverse_iterator; /* NOTE: I actually wanted to make a specialisation for N = 0, but this is not possible in class scope, so it had to be in namespace-scope. Problem with the specialisation then is that the outer-class isn't specialised, while the inner is, this is also not possible. So either I had to make this class completly out of the nd_array class (which would require a lot of templating), OR I check things in an non-templated way. I chose for the latter, since it was easier to make. The first one is actually beter (it would have better types, which make dimensionality-errors a type-error, instead of an exception). */ template struct proxy { nd_array * const data; size_t const offset; proxy(nd_array * const c, size_t o) : data(c) , offset(o) {} proxy operator[](size_t y){ if (N == 0) throw std::logic_error("called operator[] on a value"); return proxy(data, data->sizes[dimension - N]*offset + y); } operator reference(){ if (N != 0) throw std::logic_error("using a non-value"); return *(data->data + offset); } reference operator=(T const & n){ if (N != 0) throw std::logic_error("assignment to a non-value"); return *(data->data + offset) = n; } }; template struct const_proxy { nd_array const * const data; size_t const offset; const_proxy(nd_array const * const c, size_t o) : data(c) , offset(o) {} const_proxy operator[](size_t y) const { if (N == 0) throw std::logic_error("called operator[] on a value"); return const_proxy(data, data->sizes[dimension - N]*offset + y); } operator const_reference() const { if (N != 0) throw std::logic_error("using a non-value"); return *(data->data + offset); } }; nd_array(size_type width, size_type height) : data(0) , sizes() { if (dimension != 2) throw std::logic_error("wrong constructor"); sizes[0] = width; sizes[1] = height; data = new T[width*height]; } nd_array(size_type width, size_type height, size_type depth) : data(0) , sizes() { if (dimension != 3) throw std::logic_error("wrong constructor"); sizes[0] = width; sizes[1] = height; sizes[2] = depth; data = new T[width*height*depth]; } ~nd_array(){ delete[] data; } size_type get_size(size_type d) const { return sizes[d]; } size_type size() const { return std::accumulate(sizes.begin(), sizes.end(), 1, std::multiplies()); } proxy operator[](size_t x){ return proxy(this, x); } const_proxy operator[](size_t x) const { return const_proxy(this, x); } iterator begin(){ return data; } iterator end(){ size_type length = size(); return data + length; } const_iterator cbegin() const { return data; } const_iterator cend() const { size_type length = size(); return data + length; } private: T * data; std::array sizes; }; #endif // ND_ARRAY_HPP