Joshua Moerman
13 years ago
4 changed files with 177 additions and 0 deletions
@ -0,0 +1,156 @@ |
|||||
|
//
|
||||
|
// 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<std::vector<...>>). |
||||
|
Showed no difference in speed compared to std::vector<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 AwesomeAttractorND_nd_array_hpp |
||||
|
#define AwesomeAttractorND_nd_array_hpp |
||||
|
|
||||
|
#include <stdexcept> |
||||
|
#include <numeric> |
||||
|
#include <iterator> |
||||
|
#include <array> // C++11, right here :D (if this fails, try using tr1) |
||||
|
|
||||
|
template <typename T, size_t dimension> |
||||
|
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<iterator> reverse_iterator; |
||||
|
typedef std::reverse_iterator<const_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 <size_type N> |
||||
|
struct proxy { |
||||
|
nd_array * const data; |
||||
|
size_t const offset; |
||||
|
|
||||
|
proxy(nd_array * const c, size_t o) |
||||
|
: data(c) |
||||
|
, offset(o) |
||||
|
{} |
||||
|
|
||||
|
proxy<N-1> operator[](size_t y){ |
||||
|
if (N == 0) throw std::logic_error("called operator[] on a value"); |
||||
|
return proxy<N-1>(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 <size_type N> |
||||
|
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<N-1> operator[](size_t y) const { |
||||
|
if (N == 0) throw std::logic_error("called operator[] on a value"); |
||||
|
return const_proxy<N-1>(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<size_type>()); |
||||
|
} |
||||
|
|
||||
|
proxy<dimension-1> operator[](size_t x){ |
||||
|
return proxy<dimension-1>(this, x); |
||||
|
} |
||||
|
|
||||
|
const_proxy<dimension-1> operator[](size_t x) const { |
||||
|
return const_proxy<dimension-1>(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<size_type, dimension> sizes; |
||||
|
}; |
||||
|
|
||||
|
#endif |
Reference in new issue