This version of the double-bounded array correctly
implements copying of objects.
#ifndef _BOUNDARR_H_
#define _BOUNDARR_H_
/*
* This class implements an array with user-specified bounds. Subscripting
* is done with a at() function, and subscripts are bounds-checked.
*/
#include <iostream>
#include <string>
#include <stdexcept>
#include <memory>
#include <algorithm>
template <typename T>
class BoundArr {
public:
// Create a new array of the given dimensions
BoundArr(int low, int high):
m_space(new T[high-low+1]), m_low(low), m_high(high) { }
// Note: I think m_space(std::make_shared<T[]>(high-low+1)) should
// work also, but it blows up with a farily indecipherable memory
// corruption error. Seems to be a 2020 standard behavior, so
// compiler may not have it right yet, or maybe 2020 just breaks
// everything it touches.
// Convenience for conventional creation.
BoundArr(int size): m_space(new T[size]), m_low(0), m_high(size-1) { }
// Create an array as a copy of another.
BoundArr(const BoundArr &other) {
copyin(other);
}
// Perform an array assignment. Delete our space, then make us a
// copy of other.
BoundArr & operator=(const BoundArr &other)
{
copyin(other);
return *this;
}
// Find out how large this is.
int low() const { return m_low; }
int high() const { return m_high; }
int size() const { return m_high - m_low + 1; }
// Subscript the array. The two versions are allowed, and will
// be chosen based on the context of use.
T &at(int subs) { return m_space[offset(subs)]; }
T const &at(int subs) const { return m_space[offset(subs)]; }
// Print the array to the stream.
void print(std::ostream &out) const;
private:
std::shared_ptr<T[]> m_space; // Actual storage space for the array.
int m_low, m_high; // Bounds
// Make us a copy of the other array. Allocates m_space of
// the same size, then copies the contents of other.m_size into it,
// and copies m_size.
void copyin(const BoundArr<T> &other);
// Check the bounds, throw if out, then
int offset(int sub) const;
};
// Make us a copy of the other array. Allocates m_space of the same size, then
// copies the contents of other.m_size into it, and copies m_size.
template <typename T>
void BoundArr<T>::copyin(const BoundArr<T> &other)
{
// The proper way to set m_space is the following, which is C++ 17
// standard. It won't compile, even with the relevant compiler option.
// I believe this is some error in the compiler installation on
// Fedora, but I don't really know. The reset and new gets the
// job done.
// m_space = std::make_shared<T[]>(other.size());
// Replace any existing space with a new block
// having the same size and content as other.
m_space.reset(new T[other.size()]);
m_low = other.m_low;
m_high = other.m_high;
for(int i = 0; i < size(); ++i) m_space[i] = other.m_space[i];
}
// Check that the sub is in range, and throw if it is not. Otherwise,
// return the sub adjusted to an offset for subscripting the base array.
template <typename T>
int BoundArr<T>::offset(int sub) const
{
if(sub < m_low || m_high < sub)
throw std::range_error("Bounds: sub " + std::to_string(sub) +
" outside " + std::to_string(m_low) +
".." + std::to_string(m_high));
return sub - m_low;
}
// Array printer
template <typename T>
void BoundArr<T>::print(std::ostream &out) const
{
out << "<" << m_low << "|";
for(int n = 0; n < size(); ++n) {
out << " " << m_space[n];
}
out << " |" << m_high << ">";
}
// This exports the printing function to standard streams by defining
// the meaning of << on a stream and a BoundArr to be running the print
// method on the array to the stream.
template <typename T>
inline std::ostream & operator<<(std::ostream &strm, const BoundArr<T> &arr)
{
arr.print(strm);
return strm;
}
#endif