File array.hxx


template<typename T, typename Backing>
Array<T, Backing> copy(const Array<T, Backing> &other)

Create a copy of an Array, which does not share data

template<typename T>
struct ArrayData
#include <array.hxx>

ArrayData holds the actual data Handles the allocation and deletion of data

Public Functions

ArrayData(int size)
iterator<T> begin() const
iterator<T> end() const
int size() const
void operator=(ArrayData<T> &in)
T &operator[](int ind)

Private Members

int len

Size of the array.

T *data

Array of data.

template<typename T, typename Backing = ArrayData<T>>
class Array
#include <array.hxx>

Data array type with automatic memory management

This implements a container similar to std::vector but with reference counting like a smart pointer and custom memory management to minimise new and delete calls

This can be used as an alternative to static arrays

Array<dcomplex> vals(100); // 100 complex numbers

vals[10] = 1.0; // ok

When an Array goes out of scope or is deleted, the underlying memory (dataBlock/Backing) is put into a map, rather than being freed. If the same size arrays are used repeatedly then this avoids the need to use new and delete.

This behaviour can be disabled by calling the static function useStore:

Array<dcomplex>::useStore(false); // Disables memory store

The second template argument determines what type of container to use to store data. This defaults to a custom struct but can be std::valarray ( provided T is a compatible type), std::vector etc. Must provide the following : size, operator=, operator[], begin, end

Public Types

using data_type = T
using backing_type = Backing
using size_type = int

Public Functions


Create an empty array

Array a(); a.empty(); // True

Array(size_type len)

Create an array of given length


Destructor. Releases the underlying dataBlock

Array(const Array &other)

Copy constructor

Array &operator=(Array other)

Assignment operator After this both Arrays share the same dataBlock

Uses copy-and-swap idiom

Array(Array &&other)

Move constructor

void reallocate(size_type new_size)

Reallocate the array with size = new_size

Note that this invalidates the existing data!

void clear()

Release data. After this the Array is empty and any data access will be invalid

bool empty() const

Returns true if the Array is empty

size_type size() const

Return size of the array. Zero if the array is empty.

bool unique() const

Returns true if the data is unique to this Array.

void ensureUnique()

Ensures that this Array does not share data with another This should be called before performing any write operations on the data.

iterator<T> begin()
iterator<T> end()
const_iterator<T> begin() const
const_iterator<T> end() const
T &operator[](size_type ind)

Access a data element. This will fail if the Array is empty (so ptr is null), or if ind is out of bounds. For efficiency no checking is performed, so the user should perform checks.

const T &operator[](size_type ind) const

Public Static Functions

static bool useStore(bool keep_using = true)

Holds a static variable which controls whether memory blocks (dataBlock) are put into a store or new/deleted each time.

The variable is initialised to true on first use, but can be set to false by passing “false” as input. Once set to false it can’t be changed back to true.

static void cleanup()

Delete all data from the store and disable the store

Note: After this is called the store cannot be re-enabled

Private Types

using dataBlock = Backing
using dataPtrType = std::shared_ptr<dataBlock>
using storeType = std::map<size_type, std::vector<dataPtrType>>
using arenaType = std::vector<storeType>

Private Functions

dataPtrType get(size_type len)

Returns a pointer to a dataBlock object of size len with no references. This is either from the store, or newly allocated

Expects len >= 0

void release(dataPtrType &d)

Release an dataBlock object, reducing its reference count by one. If no more references, then put back into the store. It’s important to pass a reference to the pointer, otherwise we get a copy of the shared_ptr, which therefore increases the use count and doesn’t allow us to free the pass pointer directly

Note that this is noexcept only because we’ve ensure that both a) store()[<size>] already exists, and b) it has space for at least one data block. Of course, store() could throw in which case we’re doomed anyway, so the only thing we can do is abort

Private Members

dataPtrType ptr

Pointer to the data container object owned by this Array. May be null

Private Static Functions

static storeType &store(bool cleanup = false)

This maps from array size (size_type) to vectors of pointers to dataBlock objects

By putting the static store inside a function it is initialised on first use, and doesn’t need to be separately declared for each type T


  • cleanup: If set to true, deletes all dataBlock and clears the store


void swap(Array<T> &first, Array<T> &second)

Exchange contents with another Array of the same type. Sizes of the arrays may differ.