1

I'd like to use std::array on a project that will need to be very stable, and have all memory pre-allocated to avoid heap fragmentation.

I am reading in characters from the serial port, following a known protocol. Each received byte will be written to a char array of ample size to hold the longest possible string I will receive. I will then perform some minor on the fly operations, (i.e. skipping certain characters, replacing one with another, etc.) and write those out another serial port.

This device needs to function indefinitely. I will not be calling malloc or new anywhere in my code. All of my control objects are singletons, (audible gasp from the high-level crowd!) created during setup, and never being destroyed.

Is the STL array template safe to use in this context?

After reading this post, and this post, the only potential issues I can see are exception handling. As far as I'm concerned, an exception is as fatal as an access violation. I would not expect to ever attempt recovery from an exception, so I don't care that Arduinos don't support them.

But what will happen if the program tries to throw one? Will the arduino compiler even allow the use std::array if it supports exceptions? Is there an Arduino-friendly array template that uses fixed size arrays and supports iterators?

Thanks!

3
  • I don't put it as answer, because it's not directly answering your question. It seems it is dynamic memory allocation safe to use std::array, however, you still don't know what's under the hood, maybe memory consumption is an issue (stack size of local variables of nested functions called by the public functions for replacing and iterating). If you need some minor functions, I would write them myself and stay in full control of both memory and performance decisions. Commented Oct 7, 2019 at 19:11
  • "I would write them myself and stay in full control of both memory and performance decisions" That's the eternal question. My time spent writing it myself, or my potential time spent figuring out where my memory went. I'm working on other parts of the code in the mean time, but I hope someone who has already made this mistake can chime in before I commit to one or the other. Commented Oct 7, 2019 at 19:14
  • @Michel Keijzers Also, FYI, I am running this on a Mega, so it does have more memory. But still it isn't a lot, in the grand scheme of things. Commented Oct 7, 2019 at 19:24

1 Answer 1

4

std::array does not use dynamic memory allocations to store its elements. If you use static or stack-allocated std::arrays, you don't have to worry about memory fragmentation (contrary to std::vector or String, for example).
Of course, if you allocate arrays on the stack, you can have a stack overflow if you call too many functions, either by design, or because of a bug.

That brings us to the problem of exceptions. If you look in the standard, you'll see that most of the member functions of std::array are marked noexcept, with the notable exception of the element accessors. Obviously, the at() accessor can throw, but you'd expect that operator[] doesn't.
In practice, operator[] is marked noexcept in the version of the STL (GCC9) that I have on my computer, so if you check it in the STL implementation you're using, you can be pretty confident that it won't throw. Just keep in mind that this is not mandated by the standard.

/usr/include/c++/9/array

      // Element access.
      _GLIBCXX17_CONSTEXPR reference
      operator[](size_type __n) noexcept
      { return _AT_Type::_S_ref(_M_elems, __n); }

      constexpr const_reference
      operator[](size_type __n) const noexcept
      { return _AT_Type::_S_ref(_M_elems, __n); }

The debug version is also marked noexcept, but it can throw/abort if the index is out of bounds:

/usr/include/c++/9/debug/array

      // Element access.
      _GLIBCXX17_CONSTEXPR reference
      operator[](size_type __n) noexcept
      {
    __glibcxx_check_subscript(__n);
    return _AT_Type::_S_ref(_M_elems, __n);
      }

      constexpr const_reference
      operator[](size_type __n) const noexcept
      {
    return __n < _Nm ? _AT_Type::_S_ref(_M_elems, __n)
     : (_GLIBCXX_THROW_OR_ABORT(_Array_check_subscript<_Nm>(__n)),
        _AT_Type::_S_ref(_M_elems, 0));
      }

The STL implementations for Arduino I found online don't seem to have the <array> header.

Luckily, you can easily create your own array container, for example:

template <class T, size_t N>
struct Array {
    // Storage
    T data[N];

    static size_t length() { return N; }
    using type = T;

    // Item access
    T &operator[](size_t index) { return data[index]; }
    const T &operator[](size_t index) const { return data[index]; }

    // Iterators
    T *begin() { return &data[0]; }
    const T *begin() const { return &data[0]; }
    T *end() { return &data[N]; }
    const T *end() const { return &data[N]; }

    // Comparisons
    bool operator==(const Array<T, N> &rhs) const {
        if (this == &rhs)
            return true;
        for (size_t i = 0; i < N; i++)
            if ((*this)[i] != rhs[i])
                return false;
        return true;
    }
    bool operator!=(const Array<T, N> &rhs) const {
        return !(*this == rhs);
    }
};

void setup() {
  Serial.begin(115200);
  while (!Serial);

  // Create an array
  Array<int, 5> myArray = {1, 2, 3, 4, 5};
  // On older compilers, use double braces:
  //    Array<int, 5> myArray = {{1, 2, 3, 4, 5}};

  // Iterate over the array
  for (int element : myArray)
    Serial.println(element);

  // Access elements of the array
  myArray[2] = 30;
  Serial.println(myArray[2]);
}

void loop() {}

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.