Home » Algorithms » C++ wrapper for legacy C arrays

C++ wrapper for legacy C arrays

If you are writing in C++ you will sometimes be presented with an array defined by a section of C code. Using C++ you will have got used to having member functions to allow access to a container in a more error free fashion than raw C arrays. Using this template we can create a zero cost wrapper around a C array which will give it an STL style interface. The template has no member variables and has functions that return a value return fixed values that will certainly be optimised away at compile time. The template may be used for access to both const and non-const arrays.

Template parameters may not only be types, but literal integral values too! In this way we can create the object with the array data at compile time.

The signature for the template is as follows. (Using STL naming conventions)

template <typename T, std::size_t SIZE_, T(&ARRAY_)[SIZE_]>
class array_wrapper

The template is given the type of the array, the size of the array (in elements) and the array itself.

An example would be this.

// Mutable array.
int data[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };

typedef array_wrapper<int, sizeof(data) / sizeof(int), data> WrappedData;

WrappedData wrappedData;

// Immutable array.
extern const int cdata[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };

typedef array_wrapper<const int, sizeof(cdata) / sizeof(const int), cdata> WrappedCData;

WrappedCData wrappedCData;

Elements would now be able to be accessed in the same way as other STL containers.

WrappedData::const_reverse_iterator itr = wrappedData.crbegin();

while (itr != wrappedData.crend())
{
    Print(*itr++); // Prints the array in reverse order.
}

size_t size = wrappedData.size();

int lastItem = wrappedData.back();

wrappedData.fill(0);

And now the code.

First we have the template declaration, internal types and constants.

template <typename T, std::size_t SIZE_, T(&ARRAY_)[SIZE_]>
class array_wrapper
{
public:

  typedef T                                     value_type;
  typedef std::size_t                           size_type;
  typedef T&                                    reference;
  typedef const T&                              const_reference;
  typedef T*                                    pointer;
  typedef const T*                              const_pointer;
  typedef T*                                    iterator;
  typedef const T*                              const_iterator;
  typedef std::reverse_iterator<iterator>       reverse_iterator;
  typedef std::reverse_iterator<const_iterator> const_reverse_iterator;

  // Indexes for positions in the array.
  enum
  {
    SIZE     = SIZE_,
    MAX_SIZE = SIZE_,
    FRONT    = 0,
    BACK     = SIZE - 1,
    BEGIN    = 0,
    END      = SIZE,
    RBEGIN   = SIZE - 1,
    REND     = -1
  };

Next, the ability to get references to the front and back elements of the array.

  //*************************************************************************
  /// Returns a reference to the first element.
  //*************************************************************************
  reference front()
  {
    return *&ARRAY_[FRONT];
  }

  //*************************************************************************
  /// Returns a const reference to the first element.
  //*************************************************************************
  constexpr const_reference front() const
  {
    return *&ARRAY_[FRONT];
  }

  //*************************************************************************
  /// Returns a reference to the last element.
  //*************************************************************************
  reference back()
  {
    return *&ARRAY_[BACK];
  }

  //*************************************************************************
  /// Returns a const reference to the last element.
  //*************************************************************************
  constexpr const_reference back() const
  {
    return *&ARRAY_[BACK];
  }

Now the abiliy to get the start address of the array.

  //*************************************************************************
  /// Returns a pointer to the first element of the internal storage.
  //*************************************************************************
  pointer data()
  {
    return &ARRAY_[BEGIN];
  }

  //*************************************************************************
  /// Returns a const pointer to the first element of the internal storage.
  //*************************************************************************
  constexpr const_pointer data() const
  {
    return &ARRAY_[BEGIN];
  }

We now define the set of member functions that enable iterators to traverse forwards and backwards through the array.

  //*************************************************************************
  /// Returns an iterator to the beginning of the array.
  //*************************************************************************
  iterator begin()
  {
    return &ARRAY_[BEGIN];
  }

  //*************************************************************************
  /// Returns a const iterator to the beginning of the array.
  //*************************************************************************
  constexpr const_iterator begin() const
  {
    return &ARRAY_[BEGIN];
  }

  //*************************************************************************
  /// Returns a const iterator to the beginning of the array.
  //*************************************************************************
  constexpr const_iterator cbegin() const
  {
    return &ARRAY_[BEGIN];
  }

  //*************************************************************************
  /// Returns an iterator to the end of the array.
  //*************************************************************************
  iterator end()
  {
    return &ARRAY_[END];
  }

  //*************************************************************************
  /// Returns a const iterator to the end of the array.
  //*************************************************************************
  constexpr const_iterator end() const
  {
    return &ARRAY_[END];
  }

  //*************************************************************************
  // Returns a const iterator to the end of the array.
  //*************************************************************************
  constexpr const_iterator cend() const
  {
    return &ARRAY_[END];
  }

  //*************************************************************************
  // Returns an reverse iterator to the reverse beginning of the array.
  //*************************************************************************
  reverse_iterator rbegin()
  {
    return reverse_iterator(&ARRAY_[END]);
  }

  //*************************************************************************
  /// Returns a const reverse iterator to the reverse beginning of the array.
  //*************************************************************************
  constexpr const_reverse_iterator rbegin() const
  {
    return const_reverse_iterator(&ARRAY_[END]);
  }

  //*************************************************************************
  /// Returns a const reverse iterator to the reverse beginning of the array.
  //*************************************************************************
  constexpr const_reverse_iterator crbegin() const
  {
    return const_reverse_iterator(&ARRAY_[END]);
  }

  //*************************************************************************
  /// Returns a reverse iterator to the end of the array.
  //*************************************************************************
  reverse_iterator rend()
  {
    return reverse_iterator(&ARRAY_[BEGIN]);
  }

  //*************************************************************************
  /// Returns a const reverse iterator to the end of the array.
  //*************************************************************************
  constexpr const_reverse_iterator rend() const
  {
    return const_reverse_iterator(&ARRAY_[BEGIN]);
  }

  //*************************************************************************
  /// Returns a const reverse iterator to the end of the array.
  //*************************************************************************
  constexpr const_reverse_iterator crend() const
  {
    return const_reverse_iterator(&ARRAY_[BEGIN]);
  }

We also define operator [] members, plus at() member functions for STL compatibilty.

  //*************************************************************************
  /// Returns a reference to the indexed value.
  //*************************************************************************
  reference operator[](size_t i)
  {
    return ARRAY_[i];
  }

  //*************************************************************************
  /// Returns a const reference to the indexed value.
  //*************************************************************************
  constexpr const_reference operator[](size_t i) const
  {
    return ARRAY_[i];
  }

  //*************************************************************************
  /// Returns a reference to the indexed value.
  //*************************************************************************
  reference at(size_t i)
  {
    return ARRAY_[i];
  }

  //*************************************************************************
  /// Returns a const reference to the indexed value.
  //*************************************************************************
  constexpr const_reference at(size_t i) const
  {
    return ARRAY_[i];
  }

We also have a safe way of interogating the size of the array.

  //*************************************************************************
  /// Returns the size of the array.
  //*************************************************************************
  constexpr size_t size() const
  {
    return SIZE;
  }

  //*************************************************************************
  /// Returns the maximum possible size of the array.
  //*************************************************************************
  constexpr size_t max_size() const
  {
    return MAX_SIZE;
  }

Finally we define a couple of useful modifier member functions.

  //*************************************************************************
  /// Fills the array.
  //*************************************************************************
  void fill(const T& value)
  {
    std::fill(begin(), end(), value);
  }

  //*************************************************************************
  /// Swaps the contents of arrays.
  //*************************************************************************
  template <typename U, U(&ARRAYOTHER)[SIZE_]>
  typename std::enable_if<std::is_same<T, U>::value, void>::type
    swap(etl::array_wrapper<U, SIZE_, ARRAYOTHER>& other)
  {
    for (size_t i = 0; i < SIZE; ++i)
    {
      std::swap(ARRAY_[i], other.begin()[i]);
    }
  }

To complete the set of functionality we define a set of comparison free functions and a swap().

  //*************************************************************************
  /// Equality for array wrappers.
  //*************************************************************************
  template <typename TL, typename TR, std::size_t SIZEL, std::size_t SIZER, TL(&ARRAYL)[SIZEL], TR(&ARRAYR)[SIZER]>
  bool operator == (const array_wrapper<TL, SIZEL, ARRAYL>& lhs,
                    const array_wrapper<TR, SIZER, ARRAYR>& rhs)
  {
    return (SIZEL == SIZER) && std::equal(lhs.begin(), lhs.end(), rhs.begin());
  }

  //*************************************************************************
  /// Inequality for array wrapper.
  //*************************************************************************
  template <typename TL, typename TR, std::size_t SIZEL, std::size_t SIZER, TL(&ARRAYL)[SIZEL], TR(&ARRAYR)[SIZER]>
  bool operator != (const array_wrapper<TL, SIZEL, ARRAYL>& lhs,
                    const array_wrapper<TR, SIZER, ARRAYR>& rhs)
  {
    return !(lhs == rhs);
  }

  //*************************************************************************
  /// Less-than for array wrapper.
  //*************************************************************************
  template <typename TL, typename TR, std::size_t SIZEL, std::size_t SIZER, TL(&ARRAYL)[SIZEL], TR(&ARRAYR)[SIZER]>
  bool operator < (const array_wrapper<TL, SIZEL, ARRAYL>& lhs,
                   const array_wrapper<TR, SIZER, ARRAYR>& rhs)
  {
    return std::lexicographical_compare(lhs.begin(), lhs.end(), rhs.begin(), rhs.end());
  }

  //*************************************************************************
  /// Greater-than for array wrapper.
  //*************************************************************************
  template <typename TL, typename TR, std::size_t SIZEL, std::size_t SIZER, TL(&ARRAYL)[SIZEL], TR(&ARRAYR)[SIZER]>
  bool operator > (const array_wrapper<TL, SIZEL, ARRAYL>& lhs,
                   const array_wrapper<TR, SIZER, ARRAYR>& rhs)
  {
    return rhs < lhs;
  }

  //*************************************************************************
  /// Less-than-equal for array wrapper.
  //*************************************************************************
  template <typename TL, typename TR, std::size_t SIZEL, std::size_t SIZER, TL(&ARRAYL)[SIZEL], TR(&ARRAYR)[SIZER]>
  bool operator <= (const array_wrapper<TL, SIZEL, ARRAYL>& lhs,
                    const array_wrapper<TR, SIZER, ARRAYR>& rhs)
  {
    return !(lhs > rhs);
  }

  //*************************************************************************
  /// Greater-than-equal for array wrapper.
  //*************************************************************************
  template <typename TL, typename TR, std::size_t SIZEL, std::size_t SIZER, TL(&ARRAYL)[SIZEL], TR(&ARRAYR)[SIZER]>
  bool operator >= (const array_wrapper<TL, SIZEL, ARRAYL>& lhs,
                    const array_wrapper<TR, SIZER, ARRAYR>& rhs)
  {
    return !(lhs < rhs);
  }

  //*************************************************************************
  /// Swap.
  //*************************************************************************
  template <typename T, std::size_t SIZE, T(&ARRAYL)[SIZE], T(&ARRAYR)[SIZE]>
  void swap(array_wrapper<T, SIZE, ARRAYL>& lhs,
            array_wrapper<T, SIZE, ARRAYR>& rhs)
  {
    lhs.swap(rhs);
  }
John Wellbelove

John Wellbelove

Director of Aster Consulting Ltd
United Kingdom
I have been involved in technology and computer systems for all of my working life and have amassed considerable knowledge of designing and implementing systems that are both performant and correct. My role normally encompasses the entire project life-cycle, from specification to maintenance phase. Most systems I have worked on have required high speed and deterministic performance, often within a highly constrained platform. I am experienced in designing and adapting algorithms to solutions that are both space and time efficient, avoiding the normal overheads of standard solutions. Acting as a mentor for colleagues has often been a significant, though unofficial, part of my role.

I administer an open source project on Github.
See http://www.etlcpp.com/

Leave a comment

Your email address will not be published. Required fields are marked *