Безопасно ли использовать std::array (из C++ STL) на Arduino? Использует ли он динамическое выделение памяти?

c++ array stl

Я бы хотел использовать std::array в проекте, который должен быть очень стабильным, и вся память должна быть предварительно выделена, чтобы избежать фрагментации кучи.

Я считываю символы из последовательного порта по известному протоколу. Каждый полученный байт будет записан в массив символов достаточного размера, чтобы содержать максимально длинную строку, которую я получу. Затем я выполняю некоторые второстепенные операции «на лету» (т. е. пропускаю определенные символы, заменяю один другим и т. д.) и записываю их в другой последовательный порт.

Это устройство должно работать неограниченное время. Я не буду вызывать malloc или new в своем коде. Все мои объекты управления — это синглтоны (слышен вздох высокопоставленной толпы!), созданные во время установки и никогда не уничтожаемые.

Безопасно ли использование шаблона массива STL в этом контексте?

После прочтения этого сообщения и этот пост, единственные потенциальные проблемы, которые я вижу, это обработка исключений. Насколько я понимаю, исключение так же фатально, как и нарушение прав доступа. Я бы не стал пытаться восстановиться после исключения, поэтому меня не волнует, что Arduino их не поддерживает.

Но что произойдет, если программа попытается выбросить его? Разрешит ли компилятор arduino использовать std::array, если он поддерживает исключения? Существует ли удобный для Arduino шаблон массива, который использует массивы фиксированного размера и поддерживает итераторы?

Спасибо!

, 👍1

Обсуждение

Я не ставлю это как ответ, потому что это не является прямым ответом на ваш вопрос. Кажется, что динамическое выделение памяти безопасно использовать std::array, однако вы все еще не знаете, что под капотом, возможно, проблема в потреблении памяти (размер стека локальных переменных вложенных функций, вызываемых публичными функциями для замены и повторение). Если вам нужны какие-то второстепенные функции, я бы написал их сам и полностью контролировал решения как по памяти, так и по производительности., @Michel Keijzers

«Я бы написал их сам и полностью контролировал как память, так и производительность». Это вечный вопрос. Мое время, потраченное на то, чтобы написать это самому, или мое потенциальное время, потраченное на выяснение того, куда ушла моя память. Тем временем я работаю над другими частями кода, но я надеюсь, что кто-то, кто уже совершил эту ошибку, может вмешаться, прежде чем я совершу ту или другую., @Mustard Tiger

@Michel Keijzers Кроме того, к вашему сведению, я использую это на Mega, поэтому у него больше памяти. Но все же это не так много, по большому счету., @Mustard Tiger


1 ответ


Лучший ответ:

4

std::array не использует динамическое выделение памяти для хранения своих элементов. Если вы используете статические или выделенные в стеке объекты std::array, вам не нужно беспокоиться о фрагментации памяти (в отличие от std::vector или String , например).
Конечно, если вы размещаете массивы в стеке, у вас может возникнуть переполнение стека, если вы вызовете слишком много функций либо по дизайну, либо из-за ошибки.

Это подводит нас к проблеме исключений. Если вы заглянете в стандарт, то увидите, что большинство функций-членов std::array помечены noexcept, за заметным исключением методов доступа к элементам. Очевидно, что метод доступа at() может генерировать ошибки, но вы ожидаете, что operator[] этого не сделает.
На практике operator[] помечен как noexcept в той версии STL (GCC9), которая установлена на моем компьютере, поэтому, если вы проверите его в реализации STL, повторно используя, вы можете быть уверены, что он не будет бросать. Просто имейте в виду, что это не предусмотрено стандартом.

/usr/include/c++/9/массив

      // Доступ к элементу.
      _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); }

Отладочная версия также помечена как noexcept, но она может сбросить/прервать работу, если индекс выходит за пределы:

/usr/include/c++/9/отладка/массив

      // Доступ к элементу.
      _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));
      }

Реализации STL для Arduino, которые я нашел в Интернете, похоже, не имеют заголовка <array>.

К счастью, вы можете легко создать свой собственный контейнер массива, например:

template <class T, size_t N>
struct Array {
    // Хранилище
    T data[N];

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

    // Доступ к элементу
    T &operator[](size_t index) { return data[index]; }
    const T &operator[](size_t index) const { return data[index]; }

    // Итераторы
    T *begin() { return &data[0]; }
    const T *begin() const { return &data[0]; }
    T *end() { return &data[N]; }
    const T *end() const { return &data[N]; }

    // Сравнения
    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);

  // Создаем массив
  Array<int, 5> myArray = {1, 2, 3, 4, 5};
  // В старых компиляторах используйте двойные фигурные скобки:
  // Массив<int, 5> мой массив = {{1, 2, 3, 4, 5}};

  // Перебираем массив
  for (int element : myArray)
    Serial.println(element);

  // Доступ к элементам массива
  myArray[2] = 30;
  Serial.println(myArray[2]);
}

void loop() {}
,