Использование std::list в программировании Arduino

При попытке компиляции

#include <list>
std::list<byte> l = { 7, 5, 16, 8 };

в Arduino IDE (с ATtinyCore для ATtiny4313) я получаю следующее сообщение:

ошибка: список: нет такого файла или каталога
#include <список>

Как использовать стандартный C++ std::list с Arduino/ATtiny?

, 👍4


2 ответа


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

3

Я поддерживаю предложение Мишеля Кейзерса использовать простой массив, особенно учитывая крошечный объем ОЗУ в вашем MCU (всего 256 байт).

Относительно удаления элементов из списка и с учетом того, что ваш список будет максимум 16 байт, вам не нужно придумывать: просто вернитесь назад элементы, которые прошли мимо того, который вы удалили. Это не займет много времени.

Вот упрощенная реализация списка, который может содержать не более 16 байт и поддерживает удаление:

class List {
public:
    byte length;
    byte data[16];
    void append(byte item) {
        if (length < 16) data[length++] = item;
    }
    void remove(byte index) {
        if (index >= length) return;
        memmove(&data[index], &data[index+1], length - index - 1);
        length--;
    }
};

Обратите внимание, что для этого требуется всего 17 байт ОЗУ (максимум 16 элементов плюс счет длины). Нет vtable, так как у него нет виртуальных методов. Ты Если хотите, можете сделать его более привлекательным, сделайте этот шаблон параметризованным тип данных и максимальная длина, добавьте средства доступа и т. д., но если вы просто пишут простой скетч (в отличие от общей цели библиотеку), я бы рекомендовал сделать ее как можно более простой.

Пример использования на основе вашего псевдокода:

List l { .length = 3, .data = { 12, 82, 29 } };
l.remove(1);

Это действительно приведет к списку [12, 29].

,

Очень полезно, большое спасибо! Оба ответа теперь очень хороши, я не знаю, какой выбрать!, @Basj

PS: что это за синтаксис: List l { .length = 3 ... }; Это ярлык для чего-то еще, чтобы создать экземпляр класса?, @Basj

@Basj: это [совокупная инициализация](https://en.cppreference.com/w/cpp/language/aggregate_initialization): просто заполнение полей данных, [как в обычном C](https://en.cppreference. com/w/c/language/struct_initialization). Удобно, когда вам лень писать правильный конструктор. ;-), @Edgar Bonet

Спасибо @ЭдгарБонет. Вы видите умный/легкий способ найти индекс данного элемента? Пример: L = [12, 82, 29, 128]. L.indexof(29) вернет 2. Это последнее, что мне нужно в этих списках :), @Basj

@Basj: я не вижу умного пути. Но список настолько мал, что можно использовать грубую силу: просто перебирайте список, пока не найдете нужный элемент., @Edgar Bonet

Я сделал это, работает!, @Basj


1

Я не думаю, что эта библиотека поддерживается attiny (и вообще Arduino).

Однако помимо этого не рекомендуется его использовать, поскольку неизвестно, сколько памяти и флэш-накладных расходов он потребляет.

Особенно проблема с памятью по двум причинам:

  • Внутренне вы не знаете точных структур данных, используемых для списка. Наиболее эффективно он будет использовать только 4 байта (4 раза по 1 байту для массива), однако, поскольку это класс под капотом, добавляется виртуальная таблица, и кто знает, какая еще память (максимальная емкость, текущая емкость список и т. д.).
  • Также при добавлении/удалении элемента из списка либо копируется весь список, либо используются какие-то трюки с указателями. В первом случае вы получаете пробелы в памяти, во втором случае возможно (и используется дополнительная память для указателей).

Мой совет — использовать только простой массив и определить максимум массива во время компиляции (если это возможно). Не такой гибкий, но надежный, требует меньше памяти и более надежен с устройством с низким объемом SRAM, таким как Attiny (и Arduino в целом).

Удаление элементов

На основе вашего первого комментария:

Удаление элементов действительно является проблемой, однако обратите внимание, что если вы используете стандартную библиотеку, она может скопировать весь массив (исключая) новый элемент, поэтому временно у вас будет двойной массив И пробел в памяти.

Есть несколько способов реализовать это самостоятельно:

  • Если вы хотите только удалить элементы (и никогда не добавлять), добавьте новый массив с логическими значениями, которые обозначают, какие из них присутствуют, а какие удалены (например, delete[4] = 1 означает, что элемент 4 удален. Это стоит всего 1 бит на элемент.
  • Используйте два массива с элементами и каждый раз копируйте их туда и обратно. На самом деле это не приведет к разрыву памяти, но вам понадобится хранилище, необходимое для двух массивов. И сохраните переменную, которая из двух массивов является «текущей».
,

Спасибо за ваш ответ. Я был бы рад использовать простой массив (в любом случае мне нужен список максимум 16 байт). Единственное, что я не могу легко сделать с простым массивом, это это (псевдокод): L = [12, 82, 29] => L.remove(index=1) => L = [12, 29], т.е. удалить элементы в середине или в начале. Любая идея, как сделать это легко, не изобретая велосипед?, @Basj

Я обновил свой ответ (работает только для удаления элементов, добавление элементов может быть более сложным)., @Michel Keijzers