Помощь - Поиск - Пользователи - Календарь
Полная версия этой страницы: использование библиотеки STL на stm32
Форум разработчиков электроники ELECTRONIX.ru > Микроконтроллеры (MCs) > ARM
klen
Здравствуйте

С появление дешевых армов появилась возможность эффективно использовать С++ для разработки более сложных проектов чем ранее. Вопрос о применимости С++ для меня лично не стоит. С другими мнениями разного уравня осознания материала рекомендую ознакомится в соседней ветке http://electronix.ru/forum/index.php?showtopic=91629
покуда в ней коллеги ломают копья, мы не ломаем а строим.
Почему это выгодно.
1. даже 32К ОЗУ это на мой взгляд колосальное пространство. (я не имею ввиду буфера и тд - тесть то от чего не избавишся, я имею ввиду только то что С++ не жрет память просто так и грамотно написанные алгоритмы на С и С++ на выходи дадут идентичный асм).
2. Наличие фенечек ООП дает возможность сократить сами исходниуи, сделать их прозрачными и тд.
3. Мы не зачто не платим.
4. куча другого что Вы и сами знаете без меня.

Нюансы применительно к программам для stm32
1 если вы не пользуетесь динамическим выделение памяти но все работает сразу (особо отмечу что crt код должен вызывать _init_array для вызова конструкторов глобальных и статических объектов, конструкторы объектов на стеке компиллер вызоват сам)
2, если всетаки нужно new и delete, а я подразумеваю здесь что это так, то ничего страшного нет - достаточно написать свой менеджер памяти - он может быть очень простой. и переопределить operator new, operator delete с вызовами функций этого менеджера.
3. выключить нахрен поддержку эксепшенов и RTTI

вот в этом месте Вы получите возможности С++. я на этой стадии уже 2 год - полет нормальный, проекты строю по своему шаблону
FreeRTOS
C++ обертка которая реализует классы задача,очередь,шедуллер,семафор и тд для FreeRTOS
менеджер памяти взятый из примеров FreeRTOS со соими костыликами которыми я его расширил.
написанные ране классы и алгоритмы.

Всебы хорошо но грызла меня мысль а что если с С++ прокатило (да еще как замечательно!) может еще и STL забобахать! поскольку я програмирую и для больших систем в программах лдя которых применение STL жизненно необходимо, пришлось поизучать концепции положенные в STL. неделю назад я пришел к осознанию что люди которые придумали С++ и расширили его шаблонами и поверх еще накрутили STL - потому что им удалось реализовать подход - какой бы сложный инструмент не былбы - платить только за тоо что используеш. с другой стороны и в микроконтроллерных программах приходится реализовавть алгоритмы и методы которые закодированы в STL. мой опыт показал чтьо то что я дедад руками - всегда хуже sad.gif, я надеюсь что это не я такой безграмотный - это авторы того о чем мы говорим - таланты.

итак следующая ступень развития!
STL на микроконтроллерах быть!

сначала результаты и потом как этого добится.
вот рабочий код на котором я тестировал возможность применить STL
Код
#include "supc++.h" //  inline переопределение operator new и оператор delete
#include "supstl.h"  // inline определение аллокатора пригодного для нас

#include <string.h>

#include <map> //  шаблон std::map


using namespace std;

char heap[4*1024];  // кучка для менеджера памяти

void *HeapMalloc( size_t WantedSize )  // наипростейшая реализация
{
    static char* ptr = heap;
    char* tmp = ptr;
    ptr += WantedSize;
    return (void*)tmp;
}

void  HeapFree( void* pv )  наипростейшая реализация
{
     // заглушка
}

class TTestObject   // класс тестовых объектов котрые будут засовыватся в мапу
{
    public:
        TTestObject()
            {
                vals = new char[4];
                strcpy( name , "noname" );
            }
        TTestObject(const TTestObject& src)
            {
                vals = new char[4];
                strcpy( name , src.name );
                memcpy( vals , src.vals , 4 );
                val = src.val;
            }
        TTestObject(const char* name)
            {
                vals = new char[4];
                strcpy( this->name , name );
            }
        ~TTestObject()
            {
                delete vals;
            }

        TTestObject&  operator= (const char* rhs) { strcpy ( name , rhs ); return *this; }
        operator char*() { return name; }


    char name[16];
    char* vals;
    char  val;
};

typedef pair<const char*,TTestObject> TMyMapPair;
typedef map<const char*, TTestObject, compareT , KgpAllocator<TMyMapPair> > TMyMap;
typedef TMyMap::iterator TMayMapIterator;


volatile char* obj_name;
TTestObject extern_obj("extr obj");  // глобальный
//----------------------------------------------------------------------------
int main(void)
{
    (void)obj_name;

    // пустой main 696 байт ( таблица векторов, ctr код)

        //мапа на стеке
    TMyMap mymap; // +104 байта

    // добавляем в мапу запись 0 с неименованным объектом
    mymap.insert ( TMyMapPair ("ключ_0", TTestObject() ) );   // + 720 байт (код STL std::map::insert и внутренняя реализация дерева )
    // добавляем в мапу запись 1 с неименованным объектом другим способом
    mymap["ключ_1"] = extern_obj;  // + 392 байт (код STL std::map::operator[] и внутренняя реализация дерева )
    // добавляем в мапу запись 2 с именованным объектом
    mymap["ключ_2"] = TTestObject("object #2"); // + 128 байт ( конструктор TTestObject(char*) )


    // доступ к объекту записей (чтение имени объекта)
    obj_name = mymap["ключ_0"];  // + 24 байт

    // доступ к объекту записей (чтение имени объекта)
    obj_name = mymap["ключ_1"];  // + 8 байт

    // доступ к объекту записей (запись имени объекта)
    mymap["ключ_1"] = "object #1"; // + 40 байт
    // доступ к объекту записей (запись самого объекта)
    mymap["ключ_0"] = extern_obj;
    // доступ к объекту записей (чтение самого объекта присваивание его другому)
    extern_obj = mymap["ключ_1"]; // + 16 байт

    // проход по записям
    char index = 0;
    for ( TMayMapIterator it = mymap.begin(); it != mymap.end(); it++  )  // +24 байт
        {
            it->second.val = index++;
        }

    mymap.erase("ключ_0"); // +680 байт
    mymap.clear(); // +24 байт

    return 0;
}


тут не приведено сожржение хидеров с аллокатором и операторами new и delete

результаты по жирности кода:
пустой main 696 байт
манипуляции с мапой (добавление чтение и тд) - добавилось 1446 байт
удаление и очистка еще 704 байт

тоесть на круг я заплатил приметно 2300 байт за стандартную реализацию map и получил возможность рулить объектами с помощью строковых ключей. Скорость я однако еще не мерял - пожже.

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

теперь подведем итоги.
1.компилятор GCC, сборка моя
2. необходимо выключить ключами генерацию кода поддержки исключений и RTTI
3. необходимо поправить скрипт линкера чтобы он выкидывал нафег скции с структурами данный опятьже поддержки исключений
4. написать свой аллокатор для шаблонов STL, стандартный совсем не подходит для эмбеддед. я даже не знаю будет ли он работать - не пробывал
5. переопределить операторы new delete
6. подсунуть операторам new delete функции выдедения и освобождения из подходящего менеджера памяти
8. Вы можете убедится что такой код замкнутый - не пользуется ничем извне - тоесть никаких загадочных 'unresolved symbol _sbrk_r_' быть не может - мы все сами определили, если не так то чтото неправильно линкуется или еще чтото.
7. наслаждатся красивым кодом .

скорее всего гденибудь чтонибудm вылезет - например std::string использовать "из коробки" не получилось - он уже есть специфицированный параметром типа char шаблон basic_string<charT, traits, Alloc> с заданным по умолчанию аллокатором который нам никак не подходит, а вот сделать свой my_string как специфисированый basic_string<charT, traits, Alloc> со своим аллокатором можно - получим такой же функционал. но это мелочи. грабли наверно в переди sm.gif

Хотелось бы обсудить предмет. Как дела обстоят у коллег пользующих проприетарные пакеты типа IAR и тд?
прошу прощения за длинность - но короше ужать не смог.

ps. люди которые выдумали С++ и шаблоны очень любили тех для кого они это делали (тоесть нас), проблемв в том что не всем дано познать эту любовь!
shreck
ИМХО.
При использовании контейнеров STL, что делать с фрагментацией кучи?
Меня, например, только это останавливает.
P.S. Речь идет только о контейнерах. Все остальное из STL уже давно успешно использую.
klen
Цитата(shreck @ Oct 7 2011, 17:11) *
ИМХО.
При использовании контейнеров STL, что делать с фрагментацией кучи?
Меня, например, только это останавливает.
P.S. Речь идет только о контейнерах. Все остальное из STL уже давно успешно использую.

sm.gif ничего. а что еще сделаеш если нет виртуальной памяти (ну если повезет - склеивать при освобождении сегментики). я на это смотрю с другой стороны - создать все контейнеры с объектами и их использовать. в случае выделения для них резерва памяти с помощью ''пустых объектов". конечно это не очень удобно но выкрутится можно. к томуже фрагментация памяти у вас будет и без STL - дело то не вней а в принципиальной необходимости выделять и освобождать блоки в программе, а кому - вашему коду или STLлевскому это придется делать - не важнго. можно прокрутить еще вот какой трюк - несколько куч для спецефических контейнеров - это можно поручить аллокатору, у контейнера всегда куски кратны размеру объекта - проще решить проблему склейти и поиска свободного блока.

..использую кроме контейнеров...
а что там еще есть ? sm.gif алгоритмы? дык их мощща тока на контейнерах и проявляется....
у меня вовсяком случае настроение радужное, посмотрим как дальше будет.
shreck
Цитата(klen @ Oct 7 2011, 20:32) *
..использую кроме контейнеров...
а что там еще есть ? sm.gif алгоритмы? дык их мощща тока на контейнерах и проявляется....
у меня вовсяком случае настроение радужное, посмотрим как дальше будет.

Алгоритмы можно использовать не только с контейнерами STL, но и, например, со встроенными массивами. Также можно написать свой контейнер, имеющий, скажем, статически выделенную память. Вариантов много.
AHTOXA
Я в качестве new/delete прикрутил bget. STL-ем не пользуюсь, боязноsm.gif
Постоянное выделение/освобождение памяти у меня тоже вызывает чувство ненадёжности программы. Поработает месяц, поработает другой, а потом кридык - фрагментация, не могу выделить нужный кусок. Поэтому по возможности память распределяю статически, максимум - динамически выделяю при запуске. Кратковременно динамически - на стеке. Так надёжнее и быстрее. Пока удаётся обходиться.
Но за информацию спасибо, надо будет это дело покуритьsm.gif
andrewlekar
Недурно получилось. На очереди BOOST?

Да, вроде как по стандарту, нужно вызывать delete[] если new выделял массив в памяти. В чем разница, не знаю, но как бы у вас там память не подтекала.

Цитата
ps. люди которые выдумали С++ и шаблоны очень любили тех для кого они это делали (тоесть нас), проблемв в том что не всем дано познать эту любовь!

Иисус любит вас! sm.gif))
igor sychev
Добрый день!

По поводу первого сообщения от klen...
Я тоже так хочу ! :-) STL уже выручал меня в достаточно сложных проектах но на PC архитектуре.

Использую Keil , микроконтроллер STM32F105
На данный момент память настроена так: стек 0х400 куча 0х8000.

С моим аллокатором и моими опциями для компайлера/линкера всё компилируется, но не выполняется.

(после программирования выдаёт:
target state: halted
target halted due to breakpoint, current mode: Thread
xPSR: 0x01000000 pc: 0x080011ee msp: 0x2000f778
)
причины такого поведения понятны, а вот побороть пока не получилось.

Возможно, вы поделитесь файлами "supc++.h" и "supstl.h" чтобы облегчить мою участь?

Спасибо!
klen
Цитата(igor sychev @ Mar 14 2013, 12:42) *
Использую Keil , микроконтроллер STM32F105
На данный момент память настроена так: стек 0х400 куча 0х8000.

С моим аллокатором и моими опциями для компайлера/линкера всё компилируется, но не выполняется.

(после программирования выдаёт:
target state: halted
target halted due to breakpoint, current mode: Thread
xPSR: 0x01000000 pc: 0x080011ee msp: 0x2000f778
)
причины такого поведения понятны, а вот побороть пока не получилось.

Возможно, вы поделитесь файлами "supc++.h" и "supstl.h" чтобы облегчить мою участь?

Спасибо!


про кейл я ничего не знаю. файлы пожалуйста

igor sychev
Худо-бедно (испытав адские муки) скомпилировал. Похоже, не все ошибки обошел корректно, на mymap->insert (...); прога останавливается. Где это происходит точно, не могу сказать, под рукой нет дебаггера.

Насколько я понимаю, для копирования вашего результата мне нужно взять компилятор GCC, но в моём проекте это потянет большую головную боль т.к. не я один использую Каил.

Очень впечатлило, сколько сложностей для добавления STL в свой проект вы смогли преодолеть. К сожалению, мне потребуется достаточно длительное время для повтора вашего подвига. В моём случае STL (при всей столь желанной элегантности) не является краеугольным камнем проекта, более целесообразно написать самому интересующий меня эквивалент шаблону deque.

Большое спасибо что так быстро ответили!

С лучшими пожеланиями,
Игорь.
SSerge
Цитата(klen @ Oct 7 2011, 19:06) *
Хотелось бы обсудить предмет. Как дела обстоят у коллег пользующих проприетарные пакеты типа IAR и тд?

В IAR с версии 6 заявлено о полном С++ со всеми его фишками.
Попробовал list, работает.
Цитата
ps. люди которые выдумали С++ и шаблоны очень любили тех для кого они это делали (тоесть нас), проблемв в том что не всем дано познать эту любовь!

А потом подумал что MISRA-C тоже не дураки выдумали, да и обошёлся без динамического выделения памяти вообще.
MALLOY2
Цитата
Хотелось бы обсудить предмет. Как дела обстоят у коллег пользующих проприетарные пакеты типа IAR и тд?
прошу прощения за длинность - но короше ужать не смог.


Пользуюсь, IAR + FREERTOS+ LWIP + FATFS + остальное свое, также сделал надстройку над FREERTOS (не нравится хочу переделать), драйвера перефирии шаблонные статичские, STL не ользуюсь, а вот динамической памятью очень даже часто. Для этого даже сделал свой менеджер который удовлетворяет моим требованиям.

Раскажу как я заменил IAR менагер памяти на свой и как инициализируется С++ приложение.

При переходе на С++ и использовании своего манагера пати возникло несколько порблем.
1 - Инициализация манагера до того как будут вызваны контструкторы глобальных обьектов
2 - Использование отладочных средств (у меня вывод в уарт) в конструктарах глобальных обьектов, следовательно инициализация должна быть выполнена до вызова глобальных конструкторов.
3 - у меня была задача в которой в глобальном конструкторе использовалась FatFS ее тоже надо инициализировать до вызова конструкторов.

Сначала я думал что решу эту проблему разместив код инициализации в lowlevel_init - но это не правельно!

IAR запускается следующим образом lowlevel_init -> инициализация секций -> вызов конструкторов глобальных обьектов -> main
Так как после lowlevel_init идет инициализация секций безсмесленно инициализировать модули м lowlevel_init так как все значения будут перетерты. Можно вернуть из функции lowlevel_init 0 это пропустит инициализацию секций, но блин конструкторы будут вызваны. Как это решается:

Заходим в настройки линкера -> Extra Options -> прописываем --skip_dynamic_initialization, после этого компилятор не вызовет инициализацию статических обьектов в программе пишем
Код

extern "C" void __iar_dynamic_initialization(void);

__task uint32_t main(void)
{
  static FATFS fsys;                    //File system object
  mk_heap_init();                       //Heap  initialization
  dev_debug_init();                    //Debug initialization
  f_mount(0, &fsys);                  //File system initialization
  EthernetInit();                         //Ethernet initialization
  __iar_dynamic_initialization();  //C++ Inicialization
  OS::Start();                            //Application start
}


Теперь о замене манагера памяти

Заходи м в настройки линкера --> Extra Options -> прописываем
Код
--redirect malloc= ваша функция
--redirect free=ваша функция
--redirect realloc=ваша функция
--redirect calloc=ваша функция


Можно конечно глобально переопределить оператор NEW но! по методу который я привел выше за мена произойдет не только в модулях использующих оператор new, но и в модулях использующих malloc.



Непомнящий Евгений
klen, я сейчас тоже развлекаюсь в этом направлении.

Правда я хочу заиметь еще и исключения с rtti, ну и кусочки из буста sm.gif

При использовании стандартной сборки от codesourcery в elf напихивается много всего ненужного из libstdc++ (к примеру деманглинг имен - хз хачем он, но 24 к весит и т.д.).
Как раз сейчас собираю gcc и пытаюсь порезать libstdc++. Собираю на базе скриптов от yagarto. А вы своими не поделитесь?

Еще вопрос - у вас менеджер памяти из freerots - вы сравнивали его со стандартным из newlib? Я делал свои тесты heap_4.c vs malloc/free - heap_4.c немного выиграл. А вы почему используете freertos-ный?

shreck, а что не так с фрагментацией кучи? Как я понимаю стандартный free склеивает освобожденные блоки (если есть такая возможность). Конечно, если вызывать malloc/free в хитрой последовательности, можно добиться нехватки памяти, хотя она еще есть - но в принципе можно использовать две кучи - для мелких объектов и для больших или другие алгоритмы оптимизации. Собственно на эти грабли можно и на винде напороться...
klen
Цитата(Непомнящий Евгений @ Mar 15 2013, 16:08) *
Еще вопрос - у вас менеджер памяти из freerots - вы сравнивали его со стандартным из newlib? Я делал свои тесты heap_4.c vs malloc/free - heap_4.c немного выиграл. А вы почему используете freertos-ный?

я использую только TLSF манагер памяти
igor sychev
Цитата(SSerge @ Mar 15 2013, 12:41) *
В IAR с версии 6 заявлено о полном С++ со всеми его фишками.
Попробовал list, работает.
...


Подтверждаю, IAR версии 5.4, STL при первых пробах работает. Поставил максимальную оптимизацию кода, попробовал выделить память и статически и динамически, всё работает, компилируется в бинарники разумных размеров. Keil v.4.53 ещё не победил.
Для просмотра полной версии этой страницы, пожалуйста, пройдите по ссылке.
Invision Power Board © 2001-2025 Invision Power Services, Inc.