Skip to content

Instantly share code, notes, and snippets.

@rmartinho
Created October 26, 2012 17:05
Show Gist options
  • Star 4 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save rmartinho/3959961 to your computer and use it in GitHub Desktop.
Save rmartinho/3959961 to your computer and use it in GitHub Desktop.
Two-dimensional array with dimensions determined at runtime
// Two-dimensional array with dimensions determined at runtime
//
// Written in 2012 by Martinho Fernandes
//
// To the extent possible under law, the author(s) have dedicated all copyright and related
// and neighboring rights to this software to the public domain worldwide. This software is
// distributed without any warranty.
//
// You should have received a copy of the CC0 Public Domain Dedication along with this software.
// If not, see <http://creativecommons.org/publicdomain/zero/1.0/>.
// Installation:
// Place this into a header file and #include it in your code. Done.
//
// Usage:
// so::darray2d<int> x(10, 10); // create a 10x10 array
// int x12 = x[1][2]; // access an element
// x[1][2] = 42; // modify an element
// int* all = x.data(); // get a pointer to the whole array
// int* row1p = x[1].data(); // get a pointer to a row
// std::sort(x.begin(), x.end()); // use the whole array in standard library algorithms
// std::sort(x[1].begin(), x[1].end()); // use a row in standard library algorithms
// so::darray2d<int> y = x; // copiable
// so::darray2d<int> z = std::move(x); // movable (C++11)
//
// This can also be easily and safely returned from functions by value, no need to use pointers or dynamic allocation:
// so::darray2d<int> get_an_array(int x) {
// so::darray2d<int> result(x, x);
// return result;
// }
//
// Only two-dimensional arrays are supported.
// If you think you need a three-dimensional array, you're probably doing
// it wrong. But if you really want to take a stab at doing so, you may use
// this code as a starting point.
//
// If you don't want any C++11 features #define SO_ARRAY2D_NO_CPP11 before #including.
//
// Now, go forth and stop newing up multidimensional arrays
#ifndef DARRAY2D_HPP_INCLUDED
#define DARRAY2D_HPP_INCLUDED
#include <cassert> // assert
#include <cstddef> // size_t, ptrdiff_t
#include <algorithm> // copy
#ifndef SO_ARRAY2D_NO_CPP11
#define SO_ARRAY2D_CPP11_DELETE = delete
#else
#define SO_ARRAY2D_CPP11_DELETE
#endif
#ifndef SO_ARRAY2D_NO_CPP11
# include <memory> // unique_ptr
#else
# include <utility> // swap
namespace so {
namespace detail {
template <typename T>
class array_ptr {
public:
array_ptr(T* ptr) : ptr(ptr) { assert(ptr); }
~array_ptr() { delete[] ptr; }
T* get() const { return ptr; }
T& operator[](std::ptrdiff_t index) const { return ptr[index]; }
void swap(array_ptr<T>& that) {
using std::swap;
swap(ptr, that.ptr);
}
private:
T* ptr;
array_ptr(array_ptr const&);
array_ptr& operator=(array_ptr const&);
};
template <typename T>
void swap(array_ptr<T>& x, array_ptr<T>& y) { x.swap(y); }
}
}
#endif
namespace so {
template <typename T>
class darray2d {
#ifndef SO_ARRAY2D_NO_CPP11
typedef std::unique_ptr<T[]> smart_ptr;
#else
typedef detail::array_ptr<T> smart_ptr;
#endif
public:
darray2d(std::size_t n, std::size_t m)
: storage(new T[n*m]), n(n), m(m) {}
darray2d(darray2d const& that)
: storage(new T[that.n*that.m]), n(that.n), m(that.m) {
std::copy(that.begin(), that.end(), this->begin());
}
#ifndef SO_ARRAY2D_NO_CPP11
darray2d(darray2d&& that)
: storage(std::move(that.storage)), n(that.n), m(that.m) {
that.n = that.m = 0;
}
#endif
darray2d& operator=(darray2d that) {
swap(*this, that);
return *this;
}
class proxy {
public:
T& operator[](std::ptrdiff_t index) {
assert(index >= 0 && index < m);
return storage[row * m + index];
}
T* begin() { return &storage[row * m]; }
T* end() { return &storage[row * m] + m; }
T* data() { return &storage[row * m]; }
private:
template <typename X>
void operator=(X const&) SO_ARRAY2D_CPP11_DELETE;
friend class darray2d<T>;
proxy(T* storage, std::ptrdiff_t row, std::size_t m)
: storage(storage), row(row), m(m) {}
T* storage;
std::ptrdiff_t row;
std::size_t m;
};
class const_proxy {
public:
T const& operator[](std::ptrdiff_t index) {
assert(index >= 0 && index < m);
return storage[row * m + index];
}
T const* begin() const { return &storage[row * m]; }
T const* end() const { return &storage[row * m] + m; }
T const* data() const { return &storage[row * m]; }
private:
template <typename X>
void operator=(X const&) SO_ARRAY2D_CPP11_DELETE;
friend class darray2d<T>;
const_proxy(T const* storage, std::ptrdiff_t row, std::size_t m)
: storage(storage), row(row), m(m) {}
T const* storage;
std::ptrdiff_t row;
std::size_t m;
};
proxy operator[](std::ptrdiff_t index) {
assert(index >= 0 && index < n);
return proxy(storage.get(), index, m);
}
const_proxy operator[](std::ptrdiff_t index) const {
assert(index >= 0 && index < n);
return const_proxy(storage.get(), index, m);
}
std::size_t rows() const { return n; }
std::size_t columns() const { return m; }
T* begin() { return &storage[0]; }
T* end() { return &storage[0] + n*m; }
T* data() { return &storage[0]; }
T const* begin() const { return &storage[0]; }
T const* end() const { return &storage[0] + n*m; }
T const* data() const { return &storage[0]; }
friend void swap(darray2d& x, darray2d& y) {
using std::swap;
swap(x.storage, y.storage);
swap(x.n, y.n);
swap(x.m, y.m);
}
private:
smart_ptr storage;
std::size_t n;
std::size_t m;
};
}
#undef SO_ARRAY2D_CPP11_DELETE
#endif
@rmartinho
Copy link
Author

I wrote this as a quick snippet I can drop on StackOverflow questions from people having trouble getting their multi-dimensional dynamic arrays to work in some way or another.

I'd like to point that solutions to this problem already exist:

  • std::vector<std::vector<T>>: it's a very good solution if you don't need contiguous storage. It's also your best shot if you want jagged arrays.
  • boost::multiarray<T, 2>: another very good solution that works just as well with arrays of even more dimensions.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment