Статическое распределение памяти в микроконтроллерах

    Холмс: Любезнейший, не подскажите где мы находимся?
    Пастух: Вы находитесь на воздушном шаре!!!
    Холмс: Вы должно быть программист.
    Пастух: Да, но как вы догадались?
    Холмс: Только программист мог дать столь точный и
    при этом столь бесполезный ответ.

    … отрывок из известного анекдота


    Если Вы когда нибудь программировали под микроконтроллер, неважно, с помощью Arduino IDE или напрямую работали с компилятором для AVR, ARM, или ESP, Вы наверняка видели отчеты о завершении сборки вроде

    Sketch uses 1,090 bytes (3%) of program storage space. Maximum is 30,720 bytes.
    Global variables use 21 bytes (1%) of dynamic memory, leaving 2,027 bytes for local variables. Maximum is 2,048 bytes.
    

    Или

     text data   bss   dec  hex filename
    52136 1148 12076 65360 ff50 MyProject

    Такие отчеты действительно являются абсолютно точными… Вот только неполными, а потому не такими уж полезными. Проблема в том, что тут учитываются только те данные, которые были распределены статически. А вот все что выделяется через new или malloc в статистику не попадает. Как результат гораздо сложнее отследить моменты когда вдруг перестает хватать памяти и прошивка начинает работать неверно. А ведь памяти в микроконтроллерах обычно не очень много, и за этим параметром стОит тщательно следить.

    На вскидку я не вспомнил ни одного примера для младших и средних микроконтроллеров, где бы применение динамического выделения памяти было бы действительно оправданно. Как правило это выделение некоторого буфера или создание каких нибудь объектов в самом начале работы прошивки, после чего эти объекты так и висят в памяти до следующего ресета. А это повод аллоцировать такую память статически – сегодня этим и займемся.

    Статья рассчитана на новичков (хотя совсем уж базовые вещи рассказывать не буду – ожидаю, что читатель проштудировал хоть какую нибудь книгу по C++). Поехали.

    Статическое распределение переменных


    Если не вдаваться глубоко в детали и описания различных сегментов данных, то оперативную память в микроконтроллере можно поделить на 3 вида:

    • статически распределенная память – сюда пападает все о чем знает компилятор на этапе сборки проекта: глобальные переменные и объекты, статические переменные в функциях и объектах
    • куча (heap) – большая область памяти, из которой система (аллокатор памяти) нарезает кусочки кому сколько нужно. Функции вроде malloc и оператор new берут память как раз отсюда
    • стек – место где процессор сохраняет регистры и адреса возвратов из функций, но также на стеке размещаются локальные переменные в функциях.

    Давайте исследуем это на практике. Чтобы не парится с компилятором и системой сборки я воспользуюсь Arduino IDE под целевую платформу Arduino Nano.

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

    Global variables use 21 bytes (1%) of dynamic memory, leaving 2,027 bytes for local variables. Maximum is 2,048 bytes.

    Судя по сообщению свободной памяти у нас просто завались. Но давайте посмотрим на код

    int * buf;
    void setup()
    {
      buf = new int[3000];
    }
    void loop() {}
    

    Прошивка пытается выделить памяти больше чем есть в самом микроконтроллере. Разумеется, такой код работать не будет. Но, на мой взгляд. самая большая проблема тут в том, что код компилируется успешно, а компилятор вовремя не надавал по рукам и не предупредил, что память может закончится. И хорошо если есть внятный обработчик ошибки. А если нет?

    Конечно, Вы сейчас скажете, мол сам себе злобный Буратино! Тебе отчет говорит, что есть всего 2кб, а ты пытаешься выделить 3000 элементов по 2 байта каждый. Но давайте подумаем как бы это выглядело в реальном проекте. Скорее всего часть была бы уже занята какими нибудь переменными, некоторым образом бы использовался стек, сколько-то памяти было бы уже распределено динамически. И тут вдруг наступает какое нибудь редкое событие, которое потребует еще кусочек памяти и… ой все, приехали.

    Что я имею в виду под статическим распределением и чего вообще хочу добиться? Я ничего не имею против динамического выделения памяти. Но мне не очень понятно зачем использовать динамическое выделение для объектов, буферов, и массивов заранее известного размера. Почему бы просто не объявить это как глобальную переменную?

    Я пока временно уменьшил размер массива до более вменяемого размера.

    int buf[300];
     
    void setup()
    {
      buf[0] = 1; //Avoid optimizing this array
    }
     
    void loop()
    {
    }
    

    Теперь компилятор и линковщик заранее знают про некий массив размером в 300*2=600 байт, который должен быть размещен в оперативной памяти. Более того, линковщик может выделить этому массиву фиксированный адрес, который при желании можно посмотреть утилитой objdump (если, конечно, найдете куда Arduino IDE кладет бинарь)

    cd C:\Users\GrafAlex\AppData\Local\Temp\arduino_build_55567
    "C:\Program Files (x86)\Arduino\hardware\tools\avr\bin\avr-objdump.exe" -x -D -S -s StaticMem.ino.elf
    …
    00800100 l 	O .bss      	00000258 buf
    …

    Тут 0x00800100 это адрес, который линковщик присвоил нашему буферу, 0x258 это его размер (600 байт).

    Попробуем теперь вернуть неадекватный размер в 3000 элементов и посмотреть что получится. Мы закономерно получаем «фе» от системы сборки

    Sketch uses 456 bytes (1%) of program storage space. Maximum is 30,720 bytes.
    Global variables use 6,009 bytes (293%) of dynamic memory, leaving -3,961 bytes for local variables. Maximum is 2,048 bytes.
    ...
    Not enough memory; see http://www.arduino.cc/en/Guide/Troubleshooting#size for tips on reducing your footprint.
    

    Собственно, это и была цель упражнения – компилятор и линкер еще на этапе сборки надавали по рукам и сказали, что выделять такой большой буфер это плохая идея.

    Сейчас придут знатоки хороших практик кодирования и скажут, что глобальные переменные это зло. Предлагаю полемику на этот счет оставить за кадром, а кого глобальные переменные смущают, то я могу предложить несколько альтернатив. В сущности, все нижеописанные подходы служат одной и той же цели – подсказать компилятору, что определенный объект можно распределить в памяти еще на этапе компиляции/линковки, дать ему заранее известный адрес и место в оперативной памяти. Как бонус получаем еще отсутствие фрагментации памяти, а также у нас нет оверхеда на динамическую аллокацию (во всяком случае для таких объектов).

    Статическая глобальная переменная


    Можно объявить наш массив/объект/переменную используя слово static. Это ограничит область видимости переменной – к ней можно будет доступиться только из этого cpp файла.

    static int buf[300];
     
    void setup()
    {
      buf[0] = 1; //Avoid optimizing this array
    }
    

    Теперь даже если создать другой cpp файл и попробовать обратиться к нашему массиву, например через extern, то ничего не получится.

    extern int buf[300];
    
    void foo()
    {
        buf[0]++;
    }

    Линковщик выругается, что не может найти переменную buf

    C:\Program Files (x86)\Arduino\hardware\arduino\avr\cores\arduino/main.cpp:47: undefined reference to `buf'

    Если же в обоих файлах переменную buf объявить как static, то у каждого cpp файла эта переменная будет своя!

    static int buf[300];
    
    void foo()
    {
        buf[0]++;
    }

    Вывод objdump в этом случае будет выглядеть так

    0080036a l     O .bss	00000258 _ZL3buf.lto_priv.12
    00800112 l     O .bss	00000258 _ZL3buf.lto_priv.13
    

    Безымянный namespace


    Можно положить нашу глобальную переменную в безымянный namespace и получить те же плюшки, что и в предыдущем пункте. По сути это глобальная переменная с ограниченной областью видимости.

    namespace
    {
    int buf[300];
    }
    

    Статические члены классов


    Статические члены классов также распределяются на этапе компиляции.

    class A
    {
        static int buf[300];
    public:
        int * getBuf()
        {
            return buf;
        }
    };
    
    int A::buf[300];
     
    void setup() {}
    
    void loop()
    {
        A a;
        a.getBuf()[0] += 1;
    }

    Несмотря на то, что экземпляров класса A может быть много (для примера я создаю по экземпляру каждый раз, когда попадаю в функцию loop()), буфер объявлен как статический член класса. А это значит, что он будет существовать в единственном экземпляре и распределен в памяти еще на этапе линковки.

    00800100 l     O .bss	00000258 A::buf

    Статические переменные в функциях


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

    int getCounter()
    {
        static int counter = 0;
        return ++counter;
    }
    
    void setup() 
    {
        Serial.begin(9600);
    }
    
    void loop()
    {
        Serial.println(getCounter());
    }
    

    Переменная counter распределена статически. Она не будет создаваться каждый раз при вызове функции getCounter(), а будет иметь фиксированный адрес в памяти

    00800116 l     O .bss	00000002 getCounter()::counter
    

    Побочным эффектом (а заодно и полезной особенностью) такого распределения является то, что значение переменных «выживают» между вызовами функции — при каждом следующем вызове getCounter() значение переменной будет увеличено на единицу.

    Тем не менее с этим способом связано несколько нюансов. Например статическое распределение массивов будет работать как нужно

    int * getBuf()
    {
      static int buf[300];
      return buf;
    }
    

    Стандарт языка C++ гласит, что переменная buf будет инициализирована при первом вызове getBuf(), ну а поскольку для инициализации тут ничего делать не нужно, то этот массив просто будет выделен где нибудь в секции .bss (область памяти, которая заполняется нулями на старте прошивки).

    int * getBuf()
    {
      static int buf[300] = {1, 2, 3};
      return buf;
    }

    В таком варианте компилятор также распределит эту переменную статически в оперативной памяти, но в памяти программ добавится еще 600 байт, которые будут хранить начальные значения для этого массива.

    class Buf
    {
    public:
      int buf[300];
     
      Buf(int v)
      {
    	buf[1] = v;
      }
    };
     
    int * getBuf()
    {
      static Buf buf(1234);
      return buf.buf;
    }

    В таком варианте у нас появляется нетривиальный конструктор, который что-то делает при первом вызове функции getBuf(). Для платформы AVR (ATMega) компилятор сгенерирует специальный флажок, который будет регулировать нужно ли запускать конструктор или он уже был запущен до этого. Это нужно иметь в виду, т.к. под этот флажок расходуется немножко оперативной памяти, а также будет неявная проверка флажка при каждом вызове getBuf(), что может сказаться на производительности.

    А вот на платформе ARM (например, STM32) получается весьма неожиданная штуковина. Как только появляется нетривиальный конструктор прошивка сразу вырастает примерно на 60кб и уже может не поместиться в микроконтроллер. Это связано с тем, что компилятор под платформу ARM более строго следует стандарту С++ и реализует потокобезопасную инициализацию статических переменных (на случай если несколько потоков вдруг одновременно зайдут в функцию getBuf()).

    Более того, этот код также проверяет, а не вызывается ли эта функция рекурсивно во время инициализации нашей переменной? И хотя по стандарту это undefined behavior, реализация имени g++ кидает исключение recursive_init_error. А раз есть исключение, то есть и код, который эти исключения обслуживает. По меркам больших процессоров там не очень много (те самые 60кб), но вот для микроконтроллера это очень дофига.

    Решение – добавить ключик компилятора -fno-threadsafe-statics, он как раз и предназначен для того, чтобы отключить всю эту лабуду если у нас нет многопоточности и мы уверенны, что этот код будет вызываться строго из одного потока.

    Синглтон


    Наконец, можно использовать синглтон — объект, который существует в памяти в единственном экземпляре. В общем случае объект может создаваться динамически, но в случае микроконтроллеров его есть смысл распределять на этапе компиляции.

    class Singleton
    {
        int buf[300];
    
    public:
        static Singleton & getInstance()
        {
            static Singleton instance;
            return instance;
        }
        
        int * getBuf()
        {
            return buf;    
        }
    };
    
    void setup() 
    {
        Serial.begin(9600);
        Singleton::getInstance().getBuf()[42] = 10;
    }
    
    void loop()
    {
        Serial.println(Singleton::getInstance().getBuf()[42]);
    }
    

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

    00800116 l     O .bss	00000258 Singleton::getInstance()::instance

    Обратите внимание, что сам массив buf объявлен как обычный член данных класса. Статическим является экземпляр instance, который внутри содержит наш массив. К сожалению objdump описывает данные внутри объекта не очень детально. Если бы внутри класса Singleton помимо buf были бы и другие поля, objdump все равно бы их слепил вместе и показывал одной строкой.

    Кому хочется хардкорности и шаблонной магии, предлагаю ознакомиться этой статьей. Как раз вышла вчера и более полно раскрывает тему синглтонов применительно к микроконтроллерам.

    Заключение


    Оперативной памяти в небольших микроконтроллерах, как правило, не очень много. Если вам повезло и вы работаете с ARM (STM32, nRF, NXP) или ESP8266 то у Вас в распоряжении до нескольких десятков килобайт (в зависимости от «толстости» микроконтроллера). Если у Вас AVR (в составе Arduino или сам по себе), то это всего 1-4кб. Если же вам не повезло и у вас архитектура MCS51, то у вам доступно всего пара сотен байт.

    И хотя стандартная библиотека позволяет использовать функции динамического выделения памяти (new, malloc), и эти функции используются во множестве библиотек (например которые входят в состав STM Cube, или доступны в мире Arduino), как мне кажется, динамическое выделение памяти в микроконтроллерах создает больше проблем чем приносит пользы.

    В этой статье я предлагаю долгоживущие объекты распределять в памяти статически, на этапе компиляции. Это позволит более детально отслеживать распределение памяти и минимизировать риск ситуации, когда память внезапно закончилась.

    Я ни в коем случае не призываю бросаться и переделывать все объекты в программе на глобальные переменные. Описанный способ годиться в первую очередь для объектов и буферов которые создаются вначале работы прошивки и живут все время работы программы. Или же для организации некоторого пула объектов, откуда специальный менеджер будет раздавать объекты кому нужно.

    Не стОит этот способ использовать для короткоживущих объектов, возможно размещение на стеке подойдет куда лучше. Также этот способ не подойдет, если для создания объектов нужна некоторая информация не доступная на этапе компиляции.

    Всем спасибо кто дочитал эту статью до конца. Я буду рад конструктивной критике. Мне также будет интересно обсудить нюансы в комментариях.

    UPD: Вторая часть про статическое распределение объектов FreeRTOS
    AdBlock похитил этот баннер, но баннеры не зубы — отрастут

    Подробнее
    Реклама

    Комментарии 209

      –2
      … а компилятор вовремя не надавал по рукам и не предупредил, что память может закончится.
      А откуда компилятору знать сколько памяти в вашей Arduino?
        +3
        Во имя упрощения под словом компилятор я в этой статье имел в виду компилятор+линковщик. Даже больше — по хорошему львиная часть нюансов в этой статье относится именно к линковщику. А уж он то точно знает сколько памяти имеется в устройстве, т.к. на вход получает .ld скрипт под целевое устройство, где сказано сколько памяти имеется, по каким адресам размещается, что это за память (ПЗУ/ОЗУ) и много чего другого.

        И да, в статье я продемонстрировал как именно линковщик будет давать по рукам
          0
          Именно Arduino IDE не даёт компоновщику никаких .ld скриптов.
            0
            Хм… Вы правы, в случае компиляции под AVR ардуино ИДЕ действительно никак не говорит под какую платформу компилировать и линкер скриптов явно не указывает. Тем не менее в статье я продемонстрировал что кто-то все таки по рукам дает. Видимо сама Arduino IDE.

            При компиляции под STM32 линкер скрипт указывается явно (сорри, не получилось выделить)
            C:\Users\GrafAlex\AppData\Local\Arduino15\packages\STM32\tools\arm-none-eabi-gcc\8.2.1-1.7/bin/arm-none-eabi-gcc -mcpu=cortex-m3 -mthumb -Os --specs=nano.specs -Wl,--defsym=LD_FLASH_OFFSET=0 -Wl,--defsym=LD_MAX_SIZE=131072 -Wl,--defsym=LD_MAX_DATA_SIZE=20480 -Wl,--cref -Wl,--check-sections -Wl,--gc-sections -Wl,--entry=Reset_Handler -Wl,--unresolved-symbols=report-all -Wl,--warn-common -TC:\Users\GrafAlex\AppData\Local\Arduino15\packages\STM32\hardware\stm32\1.6.1\variants\PILL_F103XX/ldscript.ld -Wl,-Map,C:\Users\GrafAlex\AppData\Local\Temp\arduino_build_728324/Blink.ino.map -LC:\Users\GrafAlex\AppData\Local\Arduino15\packages\STM32\tools\CMSIS\5.5.1/CMSIS/DSP/Lib/GCC/ -larm_cortexM3l_math -o C:\Users\GrafAlex\AppData\Local\Temp\arduino_build_728324/Blink.ino.elf -LC:\Users\GrafAlex\AppData\Local\Temp\arduino_build_728324 -Wl,--start-group C:\Users\GrafAlex\AppData\Local\Temp\arduino_build_728324\sketch\Blink.ino.cpp.o C:\Users\GrafAlex\AppData\Local\Temp\arduino_build_728324\core\PeripheralPins.c.o C:\Users\GrafAlex\AppData\Local\Temp\arduino_build_728324\core\variant.cpp.o -Wl,--whole-archive C:\Users\GrafAlex\AppData\Local\Temp\arduino_build_728324\core\core.a -Wl,--no-whole-archive -lc -Wl,--end-group -lm -lgcc -lstdc++
              0
              Ха, так под AVR же указывается ключик -mmcu=atmega328p.
              Хотя, признаться, как именно он работает я не знаю.

              "C:\Program Files (x86)\Arduino\hardware\tools\avr/bin/avr-gcc" -Wall -Os -g -flto -fuse-linker-plugin -Wl,--gc-sections -mmcu=atmega328p -o C:\Users\GrafAlex\AppData\Local\Temp\arduino_build_728324/Blink.ino.elf C:\Users\GrafAlex\AppData\Local\Temp\arduino_build_728324\sketch\Blink.ino.cpp.o C:\Users\GrafAlex\AppData\Local\Temp\arduino_build_728324/core\core.a -LC:\Users\GrafAlex\AppData\Local\Temp\arduino_build_728324 -lm
              0
              никак не говорит под какую платформу компилировать
              А вот платформа там как раз задаётся параметром '-mmcu'. Например: '-mmcu=atmega328p'. Теоретически, компилятор всё-таки может знать про количество памяти, хотя этот параметр ему нужен только для определения набора инструкций.
                0
                Компилятору чтобы понять какой набор инструкций генерировать, линкеру для распределения памяти — все в плюсе :)
                  0

                  Возможно. Не спорю)

        0
        Прямо неделя Синглтона какая-то :). Хорошая статья, понятно и по по полочкам все расписано, а принцип разработки встроенного софта, описанный вами совпадает с моим видением. А то я уже решил, что никто не использует Синглтон и С++ в микроконтроллерах, как мне пишут.
        А вот на платформе ARM (например, STM32) получается весьма неожиданная штуковина. Как только появляется нетривиальный конструктор прошивка сразу вырастает примерно на 60кб

        Да так и есть, но в IAR, например, она по умолчанию отключена, включается специальным ключем. Поэтому он не добавляет захват и освобождения ресурса на время инициализации, и исключения тоже отключены по умолчанию. Но если это все дело включить, да мало не покажется.
        Я бы еще рекомендовал использовать std::array вместо обычного массива. К нему можно по ссылке обращаться, в нем размер есть, да и вообще он безопаснее.
          +1
          Спасибо, что оценили. Завтра будет вторая часть про статическое распределение объектов FreeRTOS :)

          За наводку на std::array спасибо, нужно будет попробовать.
          0
          А то я уже решил, что никто не использует Синглтон и С++ в микроконтроллерах, как мне пишут.

          Почему же? Сам сейчас активно пилю на плюсах. github.com/andreili/STM32FX_FM
          Пока — перебиваю на чистые плюсы, с минимумом define'ов и прочего, все регистры прячу в классы-обёртки и т.п. В крайних коммитах это как раз заметно.
          +1
          STM32, gcc 8.2 -O0, пример из статьи с и без -fno-threadsafe-statics отличаются на 64 байта, не знаю что там должно быть чтобы получить разницу 60кб…
            0
            В Ардуино похоже по умолчанию threasafe стоит, как того стандарт требует, и возможно ардуиновский компилятор делает защиту инициализации через std::thread еще и exception подключает, а на раскрутку стека и поиск обработчика много ресурсов требуется…
              0
              Какие эксепшены? -fno-exceptions и -fno-rtti — это стандартные ключи для эмбедда, по идее в любой IDE должны быть выставлены по умолчанию, разве что автор в блокноте компилит…
                0
                Да, но автор писал:
                Чтобы не парится с компилятором и системой сборки я воспользуюсь Arduino IDE

                А что там за компилятор и какие у него настройки, я предполагаю, что раз так много откушал, то эти ключи не стоят.
                  +1
                  они там таки не стоят. Но вот с g++ 8.2 действительно все норм.
                    0
                    Ключ '-fno-exceptions' там присутствует, а вот '-fno-rtti' там нет.
                    Включите «Show verbose output during compilation» («Показать подробный вывод») и вам будут видны все флаги.
                      0
                      Спасибо, так значительно удобнее чем выковыривать коммандлайны через ProcessMonitor

                      По делу, ситуация начинает проясняться. Как я уже упоминал у меня установлены 2 реализации ардуино под STM32.

                      Реализация github.com/rogerclarkmelbourne/Arduino_STM32 использует компилятор для Atmel SAM, там по умолчанию установлен g++ 4.8. Ключи -fno-exceptions и -fno-rtti присутствуют, ключа -fno-threadsafe-statics. Как результат на тестовом примере имеем прошивку 70кб. Добавление ключа -fno-threadsafe-statics решает проблему.

                      Фреймворк github.com/stm32duino/Arduino_Core_STM32 тянет за собой g++ 8.2, ключи -fno-exceptions и -fno-rtti присутствуют, ключ -fno-threadsafe-statics тоже есть. Результат компиляции — 8.5кб.

                      Но! Если ключ -fno-threadsafe-statics убрать, то действительно прошивка вырастает не намного. Если дизассемблировать, то видим следующее:
                      • оба компилятора при включенном ключике генерят простой код, который проверяет флажок. Если флажка нет — инициализируется статическая переменная, если есть — проходим мимо.
                      • при отсутствии ключа -fno-threadsafe-statics оба компилятора генерируют вызов __cxa_guard_acquire/__cxa_guard_release. И тут уже разница в том какую стандартную библиотеку подсунули линкеру. В случае g++ 4.8, похоже, подключается полная реализация с поддержкой исключений, а в случае 8.2 с отключенной.


                      Как видим это не зависит (не сильно зависит) от ключиков с которыми мы компилируем наш код, а зависит от настроек линкера.
                        0
                        Я бы не стал отключать -fno-threadsafe-statics. Так как кто вам гарантирует что у вас вдруг инстанс синглтона не будет использован в прерывании.
                          0
                          В народной плате BluePill всего 64к флеша (по факту, правда 128к, но дела это не меняет). Как по мне +60кб прошивки это слишком дорогая цена за потокобезопасность синглтона. Проще явно их посоздавать вначале работы программы.
                          +1
                          Все дело в реалиазации стандартной библиотеки. По умолчанию для тулчейна GNU ARM, линкер использует реализации библиотек: libstdc++ для С++ и libc для С, что подразумевает наличие дополнительного thread safe кода.
                          Существует реализация стандартной библиотеки без поддержки многопоточности — newlib nano. Для использования этой версии библиотеки в GNU ARM тулчейне в опциях линкера нужно добавить спецификацию: --specs=nano.specs
                            0
                            Спасибо, стало чуть более понятно как это работает
                0
                Вполне возможно влияют еще какие-то флаги.

                Сейчас к моей ArduinoIDE подключено 2 реализации Arduino под STM32. В одно используется gcc 4.8 и там это стреляет. В другой используется 8.2 и действительно никаких +60кб нет.

                Завтра попробую поиграться без ардуино.
                –3
                Все понятно, в статье все правильно… за исключением С++… Вот зачем писать на этом языке, если есть С, на котором никто не мешает писать в ООП-стиле? Практически все виденные мною программы для микроконтроллеров, написанные на С++ были в духе — а вот определим класс, а затем будем работать только с одним экземпляром…
                Но использование конструкторов (особенно по умолчанию и копирования) будет приводить к неявному вызову оператора new — а именно этого и хочется обычно избежать при программировании микроконтроллеров.
                  +3
                  Конструктор в неявном виде никаких new не вызывает. Это вызов new инициирует вызов конструктора. Конструктор сам по себе просто метод… такой же как и все, который вызывается при инициализации объекта, не более…
                    +3
                    Еще добавлю С++ развивается быстро, вот скоро контракты введут для С++ и будет надежный как Ada. Глядишь и его будут рекомендовать для разработки встроенного ПО, а не только Ada. Сейчас ни Си ни С++ в эту категорию не входят…
                      0

                      Ну неправда же. Входят, но с подмножеством.


                      К слову, контракты в будущих плюсах — не то же самое, что ограничение параметров в Аде. Зато синтаксически первый показанный вариант контрактов офигенен, хотел бы такое в Си.

                        0
                        О каких категориях и о каком подмножество идёт речь? Главное — кто и как энфорсит использование именно и исключительно подмножества?

                        Без явного определения можно спорить о том входят ли «в эту категорию» те или иные языки «до бесконца».

                        Как C, так и C++, к примеру, рассчитывают на уникального «суперпрограммиста», не допускающего ошибок. И этим кардинально отличаются от Ada и, внезапно, Java. Но при этом у Ada и Java очень отличающиеся подходы к переносимости…
                          0
                          Комментатор выше написал о том, что в категорию рекомендованных для разработки встраиваемого ПО языков не входя С и С++, в отличие от Ады. Но это на самом деле не так, поскольку при использовании подмножества, правил разработки и статического анализа они переходят из категории NR/R в категорию R/HR. Хотя конечно же это говорит об их недостатках, ага.
                            0
                            Комментатор выше написал о том, что в категорию рекомендованных для разработки встраиваемого ПО языков не входя С и С++, в отличие от Ады.
                            Извините, но он этого не писал. Он чушь написал:
                            Еще добавлю С++ развивается быстро, вот скоро контракты введут для С++ и будет надежный как Ada.
                            Обсуждать это бессмысленно: в комитете по разработке C++ вопрос безопасности не рассматривается в принципе. Считается, что программист пишет без ошибок. Контракты те же — это про скорость, не надёжность.

                            Но это на самом деле не так, поскольку при использовании подмножества, правил разработки и статического анализа они переходят из категории NR/R в категорию R/HR
                            Вопрос не в том «в какую категорию они попадают», а «насколько всем этим удобно пользоваться».

                            Поскольку вы язык изначально не затрачиваете под безопасность, то вы попадаете в состояние «гонки за дырами». Ну примерно как «антивирусы против прав доступа»: можно поставить сколько угодно антивирусов на Windows 9X (на FAT), но это всё равно будет менее надёжно, чем Linux или Windows NT.

                            У меня ощущение, что популярность C/C++ в эмбеде — сродни популярности JavaScript на серверах и десктопе: язык не то, что «неподходящий», язык, скорее, «категорически не годящийся»… но… «каких разработчиков можем нанять — таких и используем»… а дыры — затыкаем костылями.
                              0
                              Контракты те же — это про скорость


                              Но не только про скорость.

                              Для надежности они важны потому в первую очередь, что у них внятный синтаксис и их наличие относительно просто требовать от программистов методами технофашизма.

                              Вопрос не в том «в какую категорию они попадают», а «насколько всем этим удобно пользоваться»


                              Совершенно верно. Так же верно, как и вопрос «а насколько вообще удобно писать на Аде» и «что это вообще за норвежская поэзия, ах нет, это был Раст, вротмненоги».

                              У меня ощущение, что популярность C/C++ в эмбеде — сродни популярности JavaScript на серверах и десктопе: язык не то, что «неподходящий», язык, скорее, «категорически не годящийся»… но… «каких разработчиков можем нанять — таких и используем»… а дыры — затыкаем костылями.


                              Мы уже на эту тему сломали много копий. Вы мой подход знаете: практика — критерий истины.
                                0
                                Для надежности они важны потому в первую очередь, что у них внятный синтаксис и их наличие относительно просто требовать от программистов методами технофашизма.
                                Тем не менее это их свойство в комитете по стандартизации не рассматривается и не обсуждается. Программист объявляется умеющим написать корректную программу априори.

                                Мы уже на эту тему сломали много копий. Вы мой подход знаете: практика — критерий истины
                                Что очевидным образом не так — просто потому, что в течении столетий Земля считалась плоской и никто это даже не обсуждал.

                                Да, неограниченном временном участке практика, возможно, приходит к истине — но и мы все приходим к могиле — так что это малоинтересно.
                                  0
                                  Что очевидным образом не так — просто потому, что в течении столетий Земля считалась плоской и никто это даже не обсуждал


                                  Как только эта концепция перестала удовлетворять требованиям общества — ее изменили. Но какое-то время она вполне всех устраивала.

                                  Впрочем все это лирика.
                                    0
                                    Впрочем все это лирика.
                                    Нет. Возьмите ту же плоскую Землю: от неё отказались не в результате открытия Америки. Наоборот, отказ от концепции проской Замли, создание качественных звёздных альманахов и изобретение хронометра «запустили» Эпоху Великих географических открытий.

                                    То же самое с языками программирования: пока микроконтроллеры программируют на C++ рассказы про «надёжность» и «безопасность» — это всего лишь рассказы. В этом нет ничего страшного (в конце-концов сами железки, управляемые микроконтроллерами тоже не обладают какой-то свернадёжностью), но нужно чётко отдавать себе в этом отчёт.
                                      0
                                      Нет. Возьмите ту же плоскую Землю: от неё отказались не в результате открытия Америки. Наоборот, отказ от концепции проской Замли, создание качественных звёздных альманахов и изобретение хронометра «запустили» Эпоху Великих географических открытий.


                                      Гм, это спорный вопрос, что за чем шло, и находилось ли в причинно-следственной связи.

                                      пока микроконтроллеры программируют на C++ рассказы про «надёжность» и «безопасность» — это всего лишь рассказы


                                      Да почему же, елки-палки? Если программа на С++ не содержит ошибок, то чем она плоха? Чем она хуже содержащей ошибку программы на… на чем?
                                        0
                                        Гм, это спорный вопрос, что за чем шло, и находилось ли в причинно-следственной связи.
                                        Ни разу не спорный. Не учитывая шарообразность Земли нет возможности плавать в открытом море. Просто тупо вероятность хотя бы вернуться назад (не говоря уже о том, чтобы куда-то там доплыть) слишком мала, если вы теряете из виду землю.

                                        Максимум, чего вы сможете достичь — это приплыть на какой-нибудь остров Пасхи… но об этом достижении «на родине» никто никогда не узнает.

                                        Если программа на С++ не содержит ошибок, то чем она плоха?
                                        Тем что у вас нет никаких возможностей, без помощи со стороны компилятора, «выловить» всё ошибки для программы хоть сколько-нибудь осмысленного размера. Для программ на тысячу-другую строк — можно и на ассемблере всё написать, язык ВУ тут не нужен, а для программы большего размера языки C/C++ практически гарантируют наличие ошибок ибо постулируют непогрешимость программиста — а он, увы, человек. Ошибки делает.

                                        Чем она хуже содержащей ошибку программы на… на чем?
                                        Да хоть Бейсик, хоть Ада. Любой язык, который не допускает такого явления, когда переход с одной версии компилятора на другую — это лотерея.
                                          0
                                          Не учитывая шарообразность Земли нет возможности плавать в открытом море


                                          Вот я как раз про это и говорю — начали с плавания в открытом море, а потом начали задумываться о географических следствиях этого плавания.

                                          Тем что у вас нет никаких возможностей, без помощи со стороны компилятора, «выловить» всё ошибки


                                          А почему обязательно надо что-то делать без помощи?

                                          C/C++ практически гарантируют наличие ошибок ибо постулируют непогрешимость программиста — а он, увы, человек. Ошибки делает.


                                          В любом языке. В любом языке программист делает ошибки и посылает данные не по тому адресу, например. Например потому, что спецификациями немного пренебрег. А вы как-то все концентрируетесь именно на UB и прочих неприятностях, которые конечно очень важны и особенно плохи своей неявностью, но все же не составляют 100% ошибок.

                                          Любой язык, который не допускает такого явления, когда переход с одной версии компилятора на другую — это лотерея


                                          Пардон, а сколь многие языки вообще доживают до смены версии компилятора?
                                            0
                                            Вот я как раз про это и говорю — начали с плавания в открытом море, а потом начали задумываться о географических следствиях этого плавания.
                                            В такой последовательности — оно бы не сработало.

                                            А почему обязательно надо что-то делать без помощи?
                                            Потому что так C/C++ устроен: в нём есть многие десятки действий, которые разработчик совершать не должен — но которые, при этом, ошибками компиляции не являются.

                                            В любом языке программист делает ошибки и посылает данные не по тому адресу, например. Например потому, что спецификациями немного пренебрег.
                                            И часто при этом у вас программа работает, год, два, три… а потом бац — и перестаёт? В результате небольших изменений в совсем другом месте?

                                            но все же не составляют 100% ошибок.
                                            Они составляют почти 100% «внезапно появляющихся» (и также «внезапно исчезающих») ошибок. Главное — во многих языках подобные ошибки проводят к пусть ошибочному, но, тем не менее, детерминированному поведению.

                                            Пардон, а сколь многие языки вообще доживают до смены версии компилятора?
                                            В процентном отношении — не скажу (тот вопрос встаёт «что такое вообще „язык программирования“ „). Но по количеству… тысячи их. И простеньких (типа Форта или PL/M) и больших и сложных (Ada, C#, Oberon и многие другие).
                                              0
                                              Потому что так C/C++ устроен: в нём есть многие десятки действий, которые разработчик совершать не должен — но которые, при этом, ошибками компиляции не являются.


                                              Для этого есть сторонние (помимо компилятора) средства анализа. Помимо безыдейности такого подхода, чем он плох собственно?

                                              И часто при этом у вас программа работает, год, два, три… а потом бац — и перестаёт? В результате небольших изменений в совсем другом месте?


                                              А что ей мешает? Если она так написана, что изменения в одном месте ломают что-то в другом.

                                              Они составляют почти 100% «внезапно появляющихся» (и также «внезапно исчезающих») ошибок


                                              Но не 100% всех ошибок. И тут дело не только в их детерминированности. Если у вас при нажатии кнопки «сохранить файл» появляется диалог открытия файла, то при чем тут язык?

                                              И простеньких (типа Форта или PL/M) и больших и сложных (Ada, C#, Oberon и многие другие)


                                              Отличные примеры. И как, много у Ады и Оберона поменялось версий компиляторов?

                                              Насчет Сишарпа — ну да, у моих соседей тут как раз со сменой версии дотнета кое-что стало работать не так. Деталей не помню, помню только, что доооолго они с «платиновой техподдержкой от Майкрософт» переписывались.
                                                –1
                                                С такими бесполезно спорить, они вбили что-либо себе в голову и прут заданным курсом.
                                                Выпейте валерьянки, отдохните ;)

                                                PS: Я вот пишу программу для STM32F407 на чистом С++, даже заголовочники переписал. Ради эксперимента оставил девайс на неделю работающим, читать рандомный сектор с USB-флешки. И раз в пару минут выдавало содержимое флешки (без кеширования, ест — памяти не много свободной после основной прошивки). И всё вполне нормально живёт, никаких сбоев. И там ещё общение с FPGA живёт, прорисовка GUI по таймеру, обработка USB-HID клавиатуры и мыши для интерфейса — ничего не отваливалось. Хотя от девайса требуется от силы день непрерывной работы — специфика такая, включается только по необходимости.
                                                  0
                                                  STM32F407 на чистом С++

                                                  Как вы думаете, почему freertos и lwip на Cи написаны?
                                                    0
                                                    Потому что их авторам так было проще.
                                                      0
                                                      А чем проще то?
                                                        0
                                                        Они знали этот язык. Да и если бы не знали — на Си писать проще, чем на плюсах.
                                                          0
                                                          Они знали этот язык.

                                                          Ну тут не поспоришь))

                                                          Да и если бы не знали — на Си писать проще, чем на плюсах.

                                                          А как же шаблоны, строгая типизация, RAII и много еще чего полезного??
                                                            0
                                                            А как же шаблоны, строгая типизация, RAII и много еще чего полезного??


                                                            Оно полезно, но сложновато для понимания. Даже я бы сказал «для комплексного осознания».

                                                            Не всякий программист, пишущий в резюме «знание С++» в состоянии оператор переопределить…
                                                              0
                                                              Честно говоря я сомневаюсь что создатель freertos не знает плюсы. Хотя конечно утверждать не берусь
                                                                0
                                                                Честно говоря я сомневаюсь что создатель freertos не знает плюсы


                                                                Незнание плюсов отнюдь не синоним тупости. Для написания ОС в конце концов не знание языка нужно. Вот например создатель Линукса по некоторым данным довольно плохо знает плюсы (хотя знает).

                                                                И, повторюсь в который раз, зачем изучать Фотошоп, если у тебя и в Пэйнте неплохо получается монохромные комиксы рисовать?
                                                                  0
                                                                  Незнание плюсов отнюдь не синоним тупости.

                                                                  Это точно. Но скорее всего создатель freertos знал плюсы. И (скорее всего) выбор Си был осознанным

                                                                  И, повторюсь в который раз, зачем изучать Фотошоп, если у тебя и в Пэйнте неплохо получается монохромные комиксы рисовать?


                                                                  Если писать программы для МК, то действительно, я считаю С++ избыточным (лично моё мнение). Ну и вообще когда у тебя и ось и TCP стек и библиотеки периферии на Си написаны, зачем тащить плюсы?
                                                                    0
                                                                    И (скорее всего) выбор Си был осознанным


                                                                    Конечно. Зачем что-то другое, если и так все хорошо?

                                                                    Если писать программы для МК, то действительно, я считаю С++ избыточным (лично моё мнение)


                                                                    С этим не согласен, поскольку «программы для МК» — это что-то слишком общее. Вот если говорить конкретно про FreeRTOS и lwIP — вполне возможно. что для них С++ не дал бы чего-то особенного. Пример — Линукс.
                                                                      0
                                                                      Пример — Линукс.

                                                                      Не совсем понял пример про линукс.
                                                                      и что в линукс?
                                                                        0
                                                                        Ну, там вроде все нормально работает, и при этом на Си (чудеса).
                                                                          0
                                                                          И?
                                                                          Я всего лишь говорил что (опять, по моему мнению) не стоит тащить плюсы в микроконтроллеры. (хотя люди и питон туда прикручикают)
                                                                          Не понимаю при чём тут линукс?
                                                                            0
                                                                            Ну вы еще раз мое сообщение перечитайте. Я там написал, что утверждение «С++ избыточен для МК» я считаю неверным. Но при этом для FreeRTOS и lwIP, для решаемых ими задач, скорее всего достаточно и чистого С. И пример тому — Линукс.
                                                                              0
                                                                              Вы имеете ввиду ядро линукс или линукс полностью?

                                                                              Вообще Си, как и ассемблера для всего достаточно.
                                                                                0
                                                                                Очевидно ядро.

                                                                                Вообще Си, как и ассемблера для всего достаточно


                                                                                А вот это спорное утверждение.
                                                                                  +1
                                                                                  Нет! Еще необходимо 640 килобайт!
                                                                                    0
                                                                                    Да, а то распухли аппетиты сейчас, процы по 4ГГц, памяти 32 Гб, а хром всё равно тормозит(
                                                      +1
                                                      Ну, я бы не был так категоричен. Каждый имеет право высказаться, даже если лично я с его аргументами не согласен.
                                                        0
                                                        Тоже мне «достижение». Вы всего лишь проверили, что ваш код не вызывает очевидных ошибок если его кормить очевидно правильными данными. Я понимаю, если бы вы USB-fuzzer на свою железяку натравили, который бы «кормил» её всеми мыслимыми и немыслимыми ошибками. В Linux с его помощью накопали не один десяток багов. Причём часть репродьюсеров успешно заваливала и Windows тоже. Причём в основном — это банальные переполнения. В Ada или Java таких ошибок бы просто не было.

                                                        P.S. Собственно как я и сказал ранее:
                                                        рассказы про «надёжность» и «безопасность» — это всего лишь рассказы. В этом нет ничего страшного (в конце-концов сами железки, управляемые микроконтроллерами тоже не обладают какой-то свернадёжностью), но нужно чётко отдавать себе в этом отчёт.
                                                        Разработчикам ПО для микроконтроллеров не нужна, на самом деле, сверхнадёжность и супербезопасность если «проверка» в «тепличных условиях» их устраивает. Но при этом почему-то многие возражения против C++ начинаются с обратного утверждения… странно всё это.
                                                          0
                                                          Разработчикам ПО для микроконтроллеров не нужна, на самом деле, сверхнадёжность и супербезопасность если «проверка» в «тепличных условиях» их устраивает


                                                          Не говорите за всех. Огромное количество написанных на С и С++ решений проходит отнюдь не тепличные проверки. Причем упомянутое ПО не только со своими ошибками как-то справляется, но и участвует в исправлении сбоев оборудования. И многие люди несут за это юридическую ответственность, и даже как-то без Оберона обходятся, вот же чудеса.
                                                            0
                                                            И многие люди несут за это юридическую ответственность, и даже как-то без Оберона обходятся, вот же чудеса.
                                                            «Нести юридическую ответственность» и «сделать так, чтобы оно работало без сбоев» — это разные вещи.

                                                            Вас не удивляет, что Windows имеет кучу разнообразных сертификаций, в том числе военных, количество «дыр» в ней измеряется тысячами — но сертификаты, тем не менее, не отозваны?

                                                            Вот тут-то и разница между «юридической ответственностью» и «необходимостью сделать без ошибок».

                                                            Да вспомните про тот самый, пресловутый, 373 MAX: если бы второй самолёт, за относительно короткое время, не грохнулся — всё так бы и кончилось рассказами про то, что лётчики плохо инструкцию прочитали.

                                                            А уж сколько в авиастроении «юридической ответственности»… мало какие микроконтроллеры так сертифицируются.
                                                              0
                                                              «Нести юридическую ответственность» и «сделать так, чтобы оно работало без сбоев» — это разные вещи.


                                                              Я говорил не о том, что юридическая ответственность сама по себе исправляет ошибки (это очевидно не так, и по этому поводу есть что сказать), а о том, что ваша фраза "да никому не надо, поверхностные проверки, хаха" чересчур обобщающая. Много кому надо, и проверки делают весьма серьезные.

                                                              Вас не удивляет, что Windows имеет кучу разнообразных сертификаций, в том числе военных, количество «дыр» в ней измеряется тысячами — но сертификаты, тем не менее, не отозваны?


                                                              Конечно не удивляет, поскольку вы опять обобщаете без меры. Кто-то выдал какой-то сертификат какой-то версии Винды, возможно он был выдан с какими-то ограничениями и ограниченной областью применения. Внезапно в каких-то других тоже Виндах в каких-то еще областях применения «что-то случилось». Без конкретики получаются общие слова.

                                                              Да вспомните про тот самый, пресловутый, 373 MAX: если бы второй самолёт, за относительно короткое время, не грохнулся — всё так бы и кончилось рассказами про то, что лётчики плохо инструкцию прочитали.


                                                              Отличный пример. Надеюсь вы не будете утверждать, что в этих случаях дело таки в UB и плохих языках программирования? И наверное не возьмете на себя смелость утверждать, что Боингу не интересна надежность их самолетов?

                                                              А уж сколько в авиастроении «юридической ответственности»… мало какие микроконтроллеры так сертифицируются.


                                                              Эта фраза плохо согласована по смыслу. Впрочем неважно.
                                                                0
                                                                Надеюсь вы не будете утверждать, что в этих случаях дело таки в UB и плохих языках программирования?
                                                                Дело таки в подходе. А языки программирования с UB — его часть.

                                                                И наверное не возьмете на себя смелость утверждать, что Боингу не интересна надежность их самолетов?
                                                                Надёжность сама по себе? Нет, разумеется. Вот то, как проблемы безопасности отражаются на курсе акций — да, это его руководителей волнует. Но не безопасность сама по себе.

                                                                Откуда вообще взялась эта подсистема? Из желания не менять в сертификации диапазон допустимых углов атаки. Ситуация, когда такие вещи, для гражданских самолётов, запрещены — не является черезвычайной. Какой-нибудь Ту-154 в таких случаях попадает в плоской штопор с гарантированной гибелью всех пассажиров — и ничего, сертифицирован, летает.

                                                                Первое решение когда вопросы безопасности были «отодвинуты», так как курс акций был важнее. Когда выяснилось, что нужно менять параметры сертификата ради реальной безопасности или сделать «затычку» — выбрали «сделать затычку».

                                                                Дальше, об этой «затычке» никому ничего не сообщили (до первой катастрофы) — опять-таки: в выборе между «курс акций» и «безопасность» победил… понятно кто.

                                                                Саму «затычку» сделали так, что она работает от одного датчика без всякого дублирования чего-бы-то-ни-было (и даже «платная опция» этого не меняет — она лишь добавляет индикацию… от которой мало толку, потому что инструкция даже не объясняет — что нужно делать, когда индикация сработает).

                                                                Почему так сделали — понятно (дешевле и проще), но хочу заметить, что в прошлом, когда Бортом руководили инженеры такой халтуры не допускали.

                                                                Далее, когда выяснилось, что «система» может отправить самолёт в землю… Написали инструкцию для лётчиков — и всё. Никаких дополнительных сертификатов, ни проверки того, что лётчики вообще получили эту информацию… ничего. Похоже это на людей, которых волнует безопасность?

                                                                В том-то и дело, что вся эта история просто вопит «да плевать нам на безопасность, главное, чтобы в газетах про проблемы с безопасностью не писали».

                                                                И вот в этот подход и C++ с его UB и вот эти вот проверки только на исправных флешках и прочее — вписываются прекрасно.

                                                                А вот в желание что-то реально исправить — нет, не вписывается. У руководства Боинга была масса способов решить проблему другими, менее «стрёмными» способами… но он были отвергнуты ибо могли плохо повлиять на квартальные отчёты.

                                                                А уж сколько в авиастроении «юридической ответственности»… мало какие микроконтроллеры так сертифицируются.
                                                                Эта фраза плохо согласована по смыслу. Впрочем неважно.
                                                                Важно то, что тот факт, что возможные проблемы с безопасностью, кучи сертификатов, и возможные потери в случае если бы мухлёж выплыл нарувжу никого не остановили: квартальные отчёты важнее.
                                                                  0
                                                                  Вот то, как проблемы безопасности отражаются на курсе акций — да, это его руководителей волнует. Но не безопасность сама по себе.


                                                                  Гм, а это по-вашему ненормально? Где-то в мире на ваш взгляд бизнес сознательно идет на усложнение систем ради безопасности, не будучи заставляемым стандартами, сертификацией и репутационными рисками? Никогда такого не было и не будет. В том числе в странах с жестким регулированием экономики.

                                                                  И учтите еще вот что — «надежность» сама по себе непонятно чем является. Что это такое, откуда оно берется? Оно берется только из действующих стандартов компетентных регуляторов, и только. Никакой абстрактной «надежности вообще» нет в помине.

                                                                  Когда выяснилось, что нужно менять параметры сертификата ради реальной безопасности или сделать «затычку» — выбрали «сделать затычку»


                                                                  Нет, не так. В силу определенных причин появился дополнительный поворачивающий момент и изменился диапазон допустимых углов. Для устранения связанных с этим опасностей была добавлена система MCAS (устранение любых проблем с работой которой в свою очередь известно любому пилоту!).

                                                                  Вас просто корежит от того, что первопричиной указанных изменений являются бизнес-цели. Хотели поставить движок помощнее… Хотели не ломать имеющийся сертификат… Нехорошие люди. Вот если бы ради детей Африки!

                                                                  Дальше, об этой «затычке» никому ничего не сообщили


                                                                  Это утверждение уже было опровергнуто со ссылкой на документы.

                                                                  Саму «затычку» сделали так, что она работает от одного датчика без всякого дублирования чего-бы-то-ни-было


                                                                  Вот это уже кажется более серьезной проблемой, но… Только если бы от дублирования зависело ВСЕ. Между тем на Боингах это самое ВСЕ зависит от пилота. Увидел странную перекладку руля высоты — выключай систему и выравнивай самолет. Как и в любом другом случае внезапного несанкционированного поведения.

                                                                  Почему так сделали — понятно (дешевле и проще), но хочу заметить, что в прошлом, когда Бортом руководили инженеры такой халтуры не допускали


                                                                  Щито? В каком-таком прошлом Боингом руководили инженеры? И если даже вдруг руководили, то чего ж самолеты-то падали, причем куда чаще, чем сейчас?

                                                                  Далее, когда выяснилось, что «система» может отправить самолёт в землю… Написали инструкцию для лётчиков — и всё. Никаких дополнительных сертификатов, ни проверки того, что лётчики вообще получили эту информацию… ничего. Похоже это на людей, которых волнует безопасность?


                                                                  Ненене, обождите. А где все это время были те самые авиакомпании, которые собственно обязаны обеспечивать знание инструкций своими летчиками?

                                                                  А вот в желание что-то реально исправить — нет, не вписывается. У руководства Боинга была масса способов решить проблему другими, менее «стрёмными» способами… но он были отвергнуты ибо могли плохо повлиять на квартальные отчёты.


                                                                  Например не ставить новые движки… Нуачо, прогресс — враг человека.
                                                        0
                                                        Для этого есть сторонние (помимо компилятора) средства анализа. Помимо безыдейности такого подхода, чем он плох собственно?
                                                        Он плох тех, что в любой программе хоть сколько-нибудь осмысленного размера масса вещей, которые потенциально могут вызвать UB — что собственно и является проблемой для таких анализаторов. Уже какая-нибудь «страшная» конструкция типа x = x + 1; может вызвать UB, потому все эти «сторонние средства анализа» занимаются не доказательством того, что в решете нет дыр, но автоматическим затыканием тех дыр, через которые, скорее всего, просочится вода. Дыр в решете, после их применения остаётся более, чем достаточно для того, чтобы рассчитывать на какую-то осмысленную надёжность было нельзя.

                                                        А что ей мешает? Если она так написана, что изменения в одном месте ломают что-то в другом.
                                                        То, что UB — это не свойство программы. Это комбинация входных данных и программы. При этом любая, сколько-нибудь нетривиальная, программа на C++ содержит тысячи, если не миллионы потенциальных UB (грубо — примерно столько же, сколько в программе строк), которые, вдруг, неожиданно, могут превратиться в реальные из-за небольшого «шевеления» входных данных. Причём место, которое вызвало появление UB может быть отделено от места, где оное UB проявится многими строчками кода и многими же слоями абстракций.

                                                        Если у вас при нажатии кнопки «сохранить файл» появляется диалог открытия файла, то при чем тут язык?
                                                        Притом, что в какой-нибудь Java у вас не может это произойти из-за того, что кто-то не тот конструктор для передачи имени файла использовал, пользователь хитрый иероглиф ввёл (программа либо упадёт, либо таки откроет правильный диалог), в Ada или Modula-2 — вы знаете, что ошибка точно проистекает из «небезопасного» блока (хотя проявиться может, конечно, и в «безопасном»), а в C++ — ошибка может быть где угодно и проявиться она может так же где угодно.

                                                        И как, много у Ады и Оберона поменялось версий компиляторов?
                                                        Достаточно. В случае с Ада — есть с десяток версий компиляторов, у Оберона — три штуки точно есть, если все версии считать — то их тоже с десяток.

                                                        Деталей не помню, помню только, что доооолго они с «платиновой техподдержкой от Майкрософт» переписывались.
                                                        А вы поинтересуйтесь. Случаи, когда переход с одной версии компилятора на другую оказываются проблемными — да, наблюдаются. Но они потому и запоминаются, что случаются нечасто. А вот в случае с C++ сама идея обновить компилятор в каком-то проекте зачастую вызывает такой взгляд, как будто у тебя не одна голова, а по меньшей мере три…
                                                          0
                                                          Дыр в решете, после их применения остаётся более, чем достаточно


                                                          И есть надежные средства анализа их наличия и поведения? Или просто «они должны быть»?

                                                          Причём место, которое вызвало появление UB может быть отделено от места, где оное UB проявится многими строчками кода и многими же слоями абстракций


                                                          Вот вот вот, отличная фраза. Кто-то наворотил невесть что, и не может в этом разобраться. Наверное язык плохой…

                                                          Извините уж за сарказм.

                                                          Притом, что в какой-нибудь Java у вас не может это произойти из-за того, что кто-то не тот конструктор для передачи имени файла использовал, пользователь хитрый иероглиф ввёл (программа либо упадёт, либо таки откроет правильный диалог), в Ada или Modula-2 — вы знаете, что ошибка точно проистекает из «небезопасного» блока (хотя проявиться может, конечно, и в «безопасном»), а в C++ — ошибка может быть где угодно и проявиться она может так же где угодно.


                                                          Неправда. Во всех случаях причина в банальной описке программиста. И таких описок — охуиллиард.

                                                          Достаточно. В случае с Ада — есть с десяток версий компиляторов, у Оберона — три штуки точно есть, если все версии считать — то их тоже с десяток.


                                                          К слову, я вчера вечером ради интереса зашел на форум русскоязычного сообщества Оберона. Где во многих ключевых темах и разделах форума последние комментарии прошлым годом датируются. Сразу видно, что язык очень надежный, даже обсуждать почти нечего! Ой, стоп, а может просто… Ну нет, конечно же нет.

                                                          И снова извините за злой сарказм.

                                                          А вот в случае с C++ сама идея обновить компилятор в каком-то проекте зачастую вызывает такой взгляд, как будто у тебя не одна голова, а по меньшей мере три


                                                          Все нормально, не ошибается только тот, кто ничего не делает. И разработчики как компиляторов С++, и разработчики программ на С++, все они люди, и все они платят жестокую цену за то, чтобы не писать на Сишарпе.
                                                            0
                                                            И есть надежные средства анализа их наличия и поведения? Или просто «они должны быть»?
                                                            Для того, чтобы они могли появиться — язык должен быть устроен по другому. И статистический анализ должен не обнаруживать проблемы типа «ой, а у вас тут проверка на переполнение неправильно написана», а, наоборот, обнаруживать места, где проверки на переполнение можно опустить — а по умолчанию они должны быть всегда и везде.

                                                            Кто-то наворотил невесть что, и не может в этом разобраться. Наверное язык плохой…
                                                            Язык, собственно, так устроен что наличие или отсутствие UB — нелокально. И да, это проблема. Она есть во многих языках (скажем в любом языке с «ручным» распределением памяти обращение к указателю после вызова free — это потенциальный UB), однако C и C++ отличаются тем, что принципиально отказываются считать это проблемой языка и говорят, что всё это — только и исключительно проблемы программиста.

                                                            Сразу видно, что язык очень надежный, даже обсуждать почти нечего! Ой, стоп, а может просто… Ну нет, конечно же нет.
                                                            Не знаю уж чего вы там хотели найти. Проблема ошибок и надёжности, в действительности, сводится к одному простому тезису: «борьба за надёжность» — это хорошо и прибыльно. «Создание надёжным систем» — никак нет!

                                                            Ну потому что в первом случае вы создаёте и продаёт вещи, которые приносят кучу денег, осваиваете бюджеты, подписываете контракты… а во втором случае — что? Написали библиотеку один раз и пользуетесь? Где Деньги, Зин?
                                                              0
                                                              Для того, чтобы они могли появиться — язык должен быть устроен по другому


                                                              То бишь нынче это лишь предположение? Основанное на опыте конечно, я не спорю.

                                                              однако C и C++ отличаются тем, что принципиально отказываются считать это проблемой языка и говорят, что всё это — только и исключительно проблемы программиста


                                                              Ну а фиг ли он такую чепушню пишет, этот программист. Грубовато, но в чем-то верно.

                                                              Не знаю уж чего вы там хотели найти.


                                                              Я не хотел там ничего найти, просто стало интересно, чем живет сообщество. Оказалось, что ничем. Не чувствуют своей выгоды!

                                                              Ну потому что в первом случае вы создаёте и продаёт вещи, которые приносят кучу денег, осваиваете бюджеты, подписываете контракты… а во втором случае — что? Написали библиотеку один раз и пользуетесь? Где Деньги, Зин?


                                                              Ээээ, чо? Как это понимать? Вы серьезно утверждаете, что кто-то из реальных производителей реального оборудования с реальными требованиями к safety или reliability типа как бы специально все делает так, чтобы потом переделывать? И кто-то еще якобы им за это платит? Ппц, вы сами к подобному бизнесу имели отношение вообще, или сугубо предполагаете?
                                                                0
                                                                Ну а фиг ли он такую чепушню пишет, этот программист. Грубовато, но в чем-то верно.
                                                                В том-то и дело, что неверно. Когда у вас программист — полубог и ошибок не делает никогда, в принципе, ни при каких условиях, то вы практически не можете делать ничего.

                                                                Знаменитое «Если бы строители строили здания так же, как программисты пишут программы, первый залетевший дятел разрушил бы цивилизацию» — это как раз про C/C++. Если в программе есть хотя бы одна ошибка, сколь угодно незначительная, то вы не можете про неё сказать ничего.

                                                                Не чувствуют своей выгоды!
                                                                Так а откуда у них выгода? Надёжные программы они сделать могут, но хайповые — нет. Откуда выгода?

                                                                Ппц, вы сами к подобному бизнесу имели отношение вообще, или сугубо предполагаете?
                                                                Имел, конечно. Из личного опыта: попытка перенести криптотокен в телефон (ещё не в смартфон, речь шла о Java ME). Ну может помните… там всякие мидлеты, разделение прав, всё такое прочее…

                                                                Так вот: после изучения «предложений на рынке» обнаружилось что хотя все телефоны обеспечивают прохождение всех тестов Sun… мало какие из них реально обеспечивали хоть какую-то защиту хоть от чего-то. Внушительный процент вообще предоставлял «фирменные» интерфейсы, позволявшие залезть в данные «чужого» приложения без проблем. Ну там типа известной истории с Samsung'ом. Прям как ключик над дверью в известном мультфильме. И? Где вы тут видите заботу о безопасности?

                                                                Да, все успешные компании весьма и весьма «озабочены безопасностью»… вот только для огромного процента из них это «то, что может появиться в прессе и обрушить курс наших акций». Остальное — это вообще не безопасность, это им не интересно и не нужно.
                                                                  0
                                                                  Так а откуда у них выгода? Надёжные программы они сделать могут, но хайповые — нет. Откуда выгода?


                                                                  Ну хз, по-моему вполне почетно сделать хорошо работающую программу, разве нет?

                                                                  Где вы тут видите заботу о безопасности?


                                                                  Стоп, вы тут про другую безопасность. Мы чуть ранее говорили про safety, а не security. Тем не менее если говорить о security, тем не менее… Тесты Sun это хорошо, только о чем тут вообще говорить? Какой-то регулятор этот процесс регулировал? Кто определял, что такое «безопасность»?

                                                                  Да, все успешные компании весьма и весьма «озабочены безопасностью»… вот только для огромного процента из них это «то, что может появиться в прессе и обрушить курс наших акций».


                                                                  Пф, это так не для огромного процента, а абсолютно для всех. Вы просто так вот прикрываетесь «курсом акций», а ведь IRL помимо курсы акций там еще штрафы, уголовные преследования, отзывы лицензий… Помимо курса акций очень много санкций. И они, только они держат всех в тонусе. Это касается и софта, и железа, и документации.
                              +5
                              Но использование конструкторов (особенно по умолчанию и копирования) будет приводить к неявному вызову оператора new
                              С чего это?
                                +2
                                Но зачем С, если можно на плюсах? На них я могу писать красиво.
                                Там есть шаблоны, строгая типизация, RAII и много еще чего полезного.
                                И я умею этим пользоваться. Даже если это работа с одним экземпляром — это все равно удобно.

                                С я в какой-то момент даже вычеркнул из своего резюме — я не умею на нем эффективно и красиво писать.

                                Кстати, конструкторы никак не подразумевают вызов new. Как пример — создайте объект на стеке. Память будет выделяться, но не через new.

                                ЗЫ: а new и malloc из своего pet проекта я вообще выкосил. Так что неявно что нибудь утечь там в принципе не может.
                                  0
                                  Потому что синтаксический сахар.
                                  Потому что дает написать более понятный код, и ничего за это не заплатить.
                                  Потому что это современный язык программирования.
                                  Потому что писать на С в ООП стиле — это придумывать велосипеды. Каждый программист придумает свой велосипед. А на С++ даже в режиме «С с классами» все будет более однообразно.
                                    0
                                    Что за ООП-стиль такой?
                                    Я пишу на Arduino на С++, при том что часть классов в итоге превращаются в синглтоны.
                                    Почему? Уберем в сторону всякие удобности и озвучим одну: НАСЛЕДОВАНИЕ, ЧЕРТ ВОЗЬМИ.
                                    Как вы наследование будете делать в «ООП-Стиле» на С?
                                      0
                                      Как вы наследование будете делать в «ООП-Стиле» на С?

                                      Легко, через указатели на функции. Хоть и смысла в этом никакого, так как в плюсах все реализовано на уровне компилятора.
                                        0
                                        проблема в том что делая указатели на функции вы скорее эмулируете виртуальные функции и их наследование и огребаете их проблемы(использование их в конструкторе(init-методе) явная стоимость в размере класса на sizeof vtable и пр.)
                                      0

                                      На С действительно можно писать в ООП-стиле — только работу, которую в С++ делает компилятор, вам придется делать руками.
                                      Вспомогательного кода будет больше, статических проверок будет меньше, читать это будет менее приятно. И все это ради чего? Просто чтобы "не писать на С++"?


                                      Но почему бы просто не писать на "С с классами", если просто хочется ООП?


                                      И это я молчу про богатую стандартную библиотеку, RAII, шаблоны и constexpr-функции.
                                      Например, можете ли вы на чистом С генерировать таблицу для расчета синуса на этапе компиляции?

                                        0
                                        Разве использование чистого С мешает придерживаться RAII?
                                        Или имеется ввиду, что раз нет деструктора — мы не можем гарантировать освобождение ресурсов? Это не проблема, есть же cleanup
                                          0

                                          Эм. А как сделать RAII без конструктора и деструктора?
                                          Вся суть идиомы ведь в том, что создание объекта — это и есть инициализация (а исчезновение объекта — деинициализация); невозможно создать объект в невалидном состоянии и невозможно забыть за ним прибрать.


                                          Это не проблема, есть же cleanup

                                          Что вы имеете в виду под cleanup'ом?

                                            0
                                            Что вы имеете в виду под cleanup'ом?
                                            Я так понимаю cleanup variable attribute. Полный аналог RAII в C.

                                            Если у вас нет динамической памяти — то его достаточно. Но мне кажется, что это всё — попытки перетащить «полезные кусочки C++» «себе в норку».

                                            Сегодня пользоваться всем этим и, тем самым, привязывать себя намертво к GCC — по меньшей мере глупо. C++ — это вещь более стандартная и ничуть не менее эффективная (в последние лет 10, не будем говорить про прошлый век), так что… зачем?
                                              0
                                              Ну речь была не про то что надо это использовать.
                                              Речь про то, что инструмент есть.
                                                0
                                                В языке C — его нет. А если закладываться на конкретный компилятор — то неясно почему не заложиться на конкретный GNU C++, если уж ограничить себя GNU C вы готовы…
                                                  0
                                                  Во первых и без клинапа есть способы реализации.
                                                  Во вторых, GCC это стандарт для Arduino. Мы же не абстрактный компилятор обсуждаем.
                                                    0
                                                    В целом с выкладками на счет С++ согласен. Но хочу напомнить, что ардуино у меня в статье чисто для того, чтобы было удобно и быстро создать проект и не парится с системой сборки и компилятором. Сама суть статьи не ограничивается ардуино и конкретным компилятором.

                                                    Более того, арудиноИДЕ позволяет делать расширения для любых процессоров и устанавливать любые компиляторы. Пример: STM32duino привозит свой компилятор, пускай тот же gcc, но свой. На его месте мог бы быть и другой.
                                              0
                                              RAII — очень полезная идиома. Но появилась она в первую очередь из-за исключений. И только во вторую очередь как синтаксический сахар для замены не слишком любимого, не слишком красивого, но вполне рабочего кода C:
                                              X x = aquire_resource_x();
                                              if (!x) goto exit_point;
                                              Y y = aquire_resorce_y();
                                              if (!y) goto cleanup_x;
                                              // doing something real smart
                                              release_resource_y(y);
                                              cleanup_x:
                                              release_resource_x(x);
                                              exit_point:
                                              return something_very_smart;
                                              

                                              Вы будете смеяться, но в отсутствии исключений C++-компилятор создаст что-то именно такое. При наличие исключений — что-то наподобие гошного defer…
                                                0

                                                Понятно, что это просто сахар. Но это удобный сахар, который избавляет от глупых ошибок, которые можно искать очень долго и мучительно, из-за которых существуют инструменты типа valgrind'a и asan'a.


                                                Разумеется, все, что в рантайме можно сделать на С++, можно сделать на С, полнота по Тьюрингу это обеспечивает.
                                                Вопрос исключительно в удобстве.


                                                Вот во время компиляции на С++ можно сделать больше — благодаря шаблонам и constexpr-функциям. На это тоже можно возразить, мол, есть кодогенераторы.


                                                Неясно только, зачем возражать, откуда эта пресуппозиция, что С++ — это плохо?

                                                  +1
                                                  А где я говорил, что C++ — обязательно плохо? Мне не нравятся недостаточно обоснованные. а часто — просто неверные высказывания как про C, так и про C++. Какие-то мантры, мифы и заклинания.
                                                  У C есть много недостатков и, в сравнении с C++ — немало ограничений. С++, в сравнении с C, намного больше возможностей, но сильно не хватает ограничений: программист должен сам следить за тем, какие возможности применимы в каждом конкретном случае, а какие нет. Не у всех и не всегда хорошо получается такое самоограничение. А в C многих возможностей нет, даже думать не надо. Поэтому мне кажется, что там, где выразительных возможностей C достаточно — код на С обычно понятнее и его легче анализировать. Он, что ли, более детерминированный. На C++ можно написать код не хуже и даже лучше, но в «общем случае» это сделать труднее, надо более тщательно все продумывать и сдерживать «излишне творческие» порывы. У кого-то получается, а кто-то не справляется. Юношеский энтузиазм неофита при написании кода на C++ может сыграть плохую шутку, особенно, если сохранятся в более зрелом возрасте.
                                                  Ваш Кэп)))
                                                    0

                                                    Просто вы отвечаете в треде, который начал человек с такой пресуппозицией :) Прошу прощения, что неверно спроецировал.

                                        0

                                        Вот только (если не изменяет память) всякие либы типа lwip используют кучу.

                                          0
                                          Вполне возможно. И не только lwip.

                                          Кстати, статья навеяна библиотекой Adafruit SSD1306 — они там буфер кадра дисплея через malloc выделяют. Раньше было статически — дефайнами задаешь размер дисплея, а библиотека буфер распределяет. Но теперь они решили размер дисплея через параметры принимать и в конструкторе malloc'ом буфер выделять. Зачем они так сделали, в принципе, понятно, но мне оно мешало.
                                            +2
                                            Затем, что так правильно. Буфер выделяется один раз, при инициализации, никогда не удаляется и если памяти не хватит — это очень легко отследить. А вот конфигурация дефайнами в куче файлов кучи библиотек, вместо аккуратной и красивой передачи через параметры в конструктор — это зло.
                                            Проблемы возникают не тогда, когда память выделяется один раз при старте, проблемы возникают тогда, когда это происходит постоянно в процессе работы(особенно в многопоточных системах) — вот тогда отследить что же произошло гораздо труднее.
                                              0
                                              Я ж не спорю, что так правильно. Я полностью понимаю зачем они так сделали.

                                              Я лишь сказал, что оно мне мешало. В своем проекте я выкосил динамическую аллокацию памяти и когда обновился на новую версию библиотеки у меня все отломалось. Теперь вот сижу и думаю как это обрулить, чтобы с одной стороны иметь возможность гибко задавать параметры библиотеки, но память под буфер размещалась статически…
                                            +3
                                            В LwIP несколько стратегий управления памятью. Во встраиваемых системах обычно используют пред-аллокацию статических пулов.
                                            0
                                            Пишу реальный проект на ESP32. functional, algorithm, {vector, map, etc}, умные указатели, полиморфизм, шаблоны и исключения. Брат жив.
                                              +2
                                              Умные указатели нужны только при динамическом создании объектов. А этого как раз избегать надо, а без динамически созданных объектов — это оверхед ненужный…
                                              +1
                                              Статическое выделение памяти — довольно логичный ход для МК, и не только для маленьких. В больших камнях нередко есть участки быстрой памяти, где вручную размещают часто используемые данные. Или, например, участок памяти, общий для разных ядер.
                                              В своих проектах я не использую динамику совсем, за исключением библиотек типа lwip, которые используют свой аллокатор и живут в той области памяти, которую я им выделю. Под спойлером пример кода из одного проекта.
                                              Код
                                              namespace buffers
                                              {
                                                uint8_t modbus[100];
                                                // Other buffers...
                                              }
                                              int main()
                                              {
                                                // Буферы часто используются в прерываниях, так что указатель пригодится
                                                Singleton::instance().modbus_buffer = &buffers::modbus;
                                              
                                                // Настраиваем периферию
                                                /* Для настройки части периферии используются шаблоны
                                                 * Нужно быть уверенным в корректности параметров ещё на этапе компиляции
                                                */
                                                mcu::clk::Init<mcu::pwr::VoltageSupply::_2v7_3v6,
                                                                      8,                      // xtal
                                                                      true, true, true,  // Using hse, pll and art
                                                                      8, 336, 2, 4,      // Коэффициенты для PLL
                                                                     mcu::clk::AHB_Prescaler::_1,
                                                                     mcu::clk::APB_Prescaler::_4,
                                                                     mcu::clk::APB_Prescaler::_2,
                                                                     mcu::clk::SystickPrescaler::_1>();
                                                mcu::systick::Init();
                                                // Other configs...
                                              
                                                // Создаём в стеке класс для управления сервоусилителем через структуру-инициализатор
                                                mrj2a::InitStruct imot;
                                                imot.TIM       = TIM4;
                                                imot.direct    = mcu::pwm::channel::_1;
                                                imot.reverse   = mcu::pwm::channel::_2;
                                                imot.SON       = {GPIOG, mcu::gpio::pin::_2};
                                                imot.EMG       = {GPIOG, mcu::gpio::pin::_3}; 
                                                imot.RES       = {GPIOG, mcu::gpio::pin::_4}; 
                                                imot.ALM       = {GPIOF, mcu::gpio::pin::_11};  
                                                imot.sensor    = &sensorAZT;  // Был проинициализирован выше, выкинут из примера
                                                imot.regulator = &regulatorAZT; // Аналогично
                                                imot.Fbus      = mcu::clk::get_apb1tim_freq();
                                                imot.Kf        = 2800.0f;
                                                imot.maxSpeed  = 6.00f;	
                                                imot.minSpeed  = 0.01f;
                                                mrj2a motorAZT(&imot);
                                              
                                                // bla bla bla
                                              
                                                while(1)
                                               {
                                                  // Тут просто дергаем конечные автоматы
                                               }
                                              

                                              Страшненько, но зато работает как часы. Из минусов данного примера, структура — инициализатор останется в стеке, хотя это оборимо.
                                                0
                                                Прикольно.

                                                Инициализаторы типа mcu::clk::Init сами писали? или уже есть библиотека готовая?
                                                  +1

                                                  Моя писанина. hal меня не устроил, поэтому я написал свой.

                                                0
                                                Добавлю ещё как вариант:
                                                volatile uint8_t *hw_buf = (uint8_t *)(0x24000000)
                                                
                                                void foo()
                                                {
                                                    hw_buf[0]++;
                                                }

                                                Это как бы и не совсем выделение памяти, просто нагло создаём указатель на память и работаем с ним. Такое актуально для SoC на базе FPGA, когда есть железо и проц, работающие с одной памятью. Или же, например у 32-битных Atmel есть шина EBI (External Bus Interface) к которой можно подключить память или что-то ещё. В адресном пространстве EBI имеет фиксированный адрес. Таким образом обращение к EBI так или иначе будет сведено к этой конструкции.
                                                  0
                                                  На вскидку я не вспомнил ни одного примера для младших и средних микроконтроллеров, где бы применение динамического выделения памяти было бы действительно оправданно.

                                                  То, что на микроконтроллерах лучше не использовать динамическое выделение памяти это понятно, но как быть в случае, когда количество (физических) объектов, с которыми предстоит работать, заранее неизвестно? Например, к контроллеру могут подключаться 2, 5, 10 или 254 (беспроводных) датчика.

                                                  Как тут обойтись статическими методами, когда количество объектов заранее неизвестно? А такие ситуации в реальных задачах на микроконтроллерах встречаются сплошь и рядом.
                                                    0
                                                    ИМХО тут все зависит от конкретной задачи и сопутствующих ограничений. Из того что приходит в голову:
                                                    • Использовать динамическое выделение, может быть тут ему самое место
                                                    • Заранее выделить пулл/массив под максимально возможное количество датчиков
                                                      0
                                                      В том-то всё и дело, что максимальное количество (в данном случае датчиков) может быть очень большим и никакой памяти в микроконтроллере не хватит, если сразу зарезервировать её по максимуму, да и бесполезно «простаивать» она будет 99% времени. А если учесть, что и тип (беспроводного) датчика нам заранее неизвестен, то это точно путь в никуда.

                                                      Оптимальной стратегией, вероятно, будет выделение памяти по мере подключения новых датчиков (с соответствующими процедурами типа проверки текущего её объёма и т. п.). Но на этом все разговоры о «статике» заканчиваются — хочешь — не хочешь — а без динамики не обойтись.
                                                        0
                                                        По всей видимости да…
                                                        • НЛО прилетело и опубликовало эту надпись здесь
                                                            –1
                                                            То есть вы предлагаете статически резервировать память на максимальное количество датчиков? Так, давайте посчитаем (гипотетический пример):

                                                            Максимально 254 датчика каждого типа, типов 20 штук, итого 5120 объектов, каждый, допустим, по 30 байт, итого 153600 байт. И при этом пользователь может подключить в реале 1-2 датчика.

                                                            И какой контроллер такое безобразие выдержит? :)
                                                              +6
                                                              Ерунда какая-то, если заявлена поддержка 254*20 датчиков, то устройство должно с таким количество датчиков нормально работать, а тут даже памяти на них не хватает. Очевидно, что в таком случае должно быть ограничение на общее количество, например, если будет 1000 датчиков, то памяти потребуется 30'000 байт. Опять же тогда и тестить нужно с этой тысячей, а не с 1-2, следовательно нужны все 30'000 байт и это для статики, если выделять память динамически, то ее потребуется еще больше, даже без учета возможной дефрагментации.
                                                                +1
                                                                Я так понимаю, автор комментария имеет в виду, что на одной конфигурации у него может быть 4 датчика типа A, и 30 датчиков типа B, а на другой конфигурации может быть 100 датчиков типа A, и 5 датчиков типа B. А размеры структур, необходимых для разных типов датчиков тоже разные, поэтому «общее максимальное количество» заранее не обозначить, и заранее статически память под все возможные варианты (комбинации) не выделить.

                                                                Поэтому выделять динамически при старте в зависимости от конфигурации, проверять корректность конфигурации запуске, и описать в документации сколько есть всего доступной для датчиков памяти и сколько требует каждый датчик определенного типа — вариант вполне ок.
                                                                  +1
                                                                  Вообще-то было конкретно написано о 5120 объектах по 30 байт, которые не влезают в имеющуюся память. Не влезают, пиши в документации такое количество, которое гарантированно влезет… Если объекты могут быть по 30 и 50 байт, а памяти под них в наличии 50'000 байт, значит максимальное количество датчиков — 1000. Может быть и больше, но я акцентирую внимание на том, что устройство должно работать если у него минимум эти 50'000 байт отобрать, а автор комментария думает, что заявит поддержку 1000 датчиков, но т.к. обычно их используется несколько, то он экономит память. Нет, в смысле память то экономится, только 1000 датчиков наверняка работать не будут.
                                                                    +3
                                                                    Можно выделить для каждого датчика область памяти, равную размеру максимальной структуры среди разных типов, чтобы туда поместился и А, и В. Будет оверхед, но зато гарантированно нельзя будет упустить проверку, чтобы память начала течь.
                                                                      +2
                                                                      Именно так и надо делать во встраиваемых системах. Если в документации написано, что устройство должно поддерживать 100500 датчиков одновременно, то и память сразу надо выделить на этот обьём. В конце концов микроконтроллер подбирается под конкретную задачу. Поэтому вопрос нехватки ресурсов должен решаться на этапе проектирования.
                                                                        0
                                                                        Мне тоже нравится такой подход. Но, теоретически, можно придумать ситуацию когда мы подключаем, в крайних случаях, дофига простых датчиков с маленьким буфером, либо десяток «жирных» с большими требованиями к памяти + промежуточные варианты.
                                                                        Разумеется, если есть возможность, лучше так не делать — ибо тестировать все промежуточные варианты будет очень весело.
                                                                      –2
                                                                      Хочу обратить ваше внимание, что это гипотетический пример, но тем не менее: в примере идёт речь не о поддержке «254*20 датчиков», а о поддержке 254 датчиков, но, если мы заранее не знаем сколько и каких (какого типа) датчиков подключит пользователь, то мы вынуждены в статике резервировать полную матрицу 254*20 (ведь объекты, соответствующие каждому типу — разные).

                                                                      Что касается фрагментации, то, очевидно, удалять инициализированные объекты не стоит, их нужно просто помечать, как свободные для повторного использования. Всё это конечно мудрёно, но статическое выделение памяти в таких задачах выглядит ещё менее приемлемо.

                                                                      Но всё это только моё скромное мнение — с удовольствием послушаю как на практике решают такие проблемы профессионалы.
                                                                        0
                                                                        На самом деле у Вас все исходные данные есть в вашем же посте. И практически готовое решение. Только собрать пазл нужно.

                                                                        И так, самое-самое важное то, что контроллер это константные ресурсы. У него есть четкое количество памяти, частота, пины. Его миссия неизменна. Т.е. функционал всегда имеет предел.

                                                                        Да, на шине может быть некоторое количество датчиков. И разного типа. Например такие шины как 1w или I2C. Но в любом случае, есть предел общего количества датчиков. Если вы раздробите их на несколько массивов это будет неэффективно. Т.е. будут выделены ресурсы и с высокой вероятностью не использованы. Таким образом, обычно, устанавливает предельное количество датчиков. Ну к примеру 16 всего, всех типов.

                                                                        Вы очень близки по фрагментации к решению. В массиве каждый элемент должен иметь признак использования. Он может быть косвенным. Например ID датчика. Если ID есть, значит датчик подключен. Если 0 то нет.

                                                                        У каждого элемента массива есть тип. Именно он определяет что же это за датчик. И от него зависит обработчик — handler функция.

                                                                        Все становится сложнее, если каждый датчик имеет специфичные параметры окружения. Ну например буфер. У одного он есть, у другого нет. На ум приходит поинтер (payload) и преобразование к нужному типу. Оттуда достаем уже все что нужно. Но… это уже динамическое выделение памяти. А мы хотим его избежать.

                                                                        Так вот, для решения как раз таких задач есть прекрасное решение — union. И жить становится просто ;)

                                                                        Это решение позволяет гарантированно выделять четкий объем памяти на этапе компиляции и избегать возможных проблем с фрагментирование памяти, что не позволит в очередной момент добавить очередной датчик и потребует ребута контроллера.
                                                                          +1
                                                                          enum sensor_type {
                                                                              SENSOR_TYPE_A,
                                                                              SENSOR_TYPE_B
                                                                          };
                                                                          
                                                                          struct sensor {
                                                                              enum sensor_type type;
                                                                              union {
                                                                                  struct type_a type_a;
                                                                                  struct type_b type_b;
                                                                              } data;
                                                                          };
                                                                          
                                                                          struct sensor sensors[254];
                                                                            0
                                                                            Спасибо, что код кинули. С телефона очень сложно было его накидать. Все очень наглядно.
                                                                              +2
                                                                              union'ы очень спасают и при обращении к регистрам.
                                                                              Вот пример:
                                                                                      union PLLCFGR_t
                                                                                      {
                                                                                          struct
                                                                                          {
                                                                                              uint32_t PLLM       : 6;
                                                                                              uint32_t PLLN       : 9;
                                                                                              bool     res0       : 1;
                                                                                              uint32_t PLLP       : 2;
                                                                                              uint32_t res1       : 4;
                                                                                              bool     PLLSRC     : 1;
                                                                                              bool     res2       : 1;
                                                                                              uint32_t PLLQ       : 4;
                                                                                              uint32_t res3       : 4;
                                                                                          };
                                                                                          uint32_t RAW;
                                                                                      };

                                                                              static inline void set_PLL_N(uint32_t val) { reinterpret_cast<PLLCFGR_t*>(RCC_PLLCFGR_BASE)->PLLN= val; }

                                                                              Вызов данной функции разложится в 4 операции — загрузка адреса регистра, загрузка значения из регистра, модификация битовых полей и запись нового значения в регистр. Данную оптимизацию GCC умеет «из коробки», что избавляет от излишка операций с битовыми масками и сдвигами, если это значение — не константа. Всё выполнит операция BFI.
                                                                                0

                                                                                Вот только если у вас регистр volatile (а он таким будет в 100% случаев), а поменять вам нужно больше одного флага, то union вам делает медвежью услугу, когда на каждый флаг значение будет загружаться заново.

                                                                                  0
                                                                                  на запись убрал volatile — смотрите внимательнее ;)
                                                                                  В чувствительных к этому местах (пины GPIO, например), оставил. Чтение же везде 100% volatile
                                                                +5
                                                                В С++ можно написать свою реализацию аллокатора, например, используя концепцию memory pools и потом использовать обобщенные контейнеры/алгоритмы стандартной библиотеки, что довольно сильно повысит гибкость и переносимость кода. В книге «Real-Time C++» Christopher Kormanyos (2018) есть примеры реализации таких аллокаторов (и еще много чего для embedded, рекомендую к прочтению).
                                                                  –1
                                                                  Порадовало то, что обсуждается malloc как фи… и избыточность. И тут же пример на С++ Т.е. писать на С++ для 3кб ОЗУ это норм, а malloc это зло. Мне изменяет память или объект, даже со статическими свойствами, при создании получит свой поинтер в памяти?

                                                                  Ну положим, что и объекты мы будем создавать не динамически. Кто-то может пояснить, сколько кучи будет использовать подкапотное пространство объектного движка при создании объектов?

                                                                  Тут или штаны нужно надеть или в баню зайти.

                                                                  Если мы берем чип с такими ограниченными ресурсами, то естественно каждый байт у него на счету. И во-первых, инструментом разработки должен стать С. С его полной ясностью.

                                                                  В динамическом выделении памяти нет ничего злостного. Все приведенные примеры можно экстраполировать и на ОЗУ в гигабайты. Просто выделять куски по гигу. Утечки как в 3кб так и в 16gb плохо.

                                                                  Вопрос небольшого объема памяти и динамического выделения связан с ее фрагментированием. Было 10 байт. Выделили 2 байта, потом, 6, потом еще 2. Затем освоболили 2 и 2 байта. Захотели выделить 3 (ведь у нас 4 свободно), а не тут то было… потому, что у нас нет 3х байт памяти подряд. У нас есть 2 по 2. Естественно, это тем менее актуально чем больше ОЗУ.

                                                                  Чип это не компьютер. У него четкое количество ресурсов и четкая миссия на всю жизнь.

                                                                  Ровно по этой причине, с учетом ограниченности самих функций чипа и его ресурсов используется статическое выделение памяти.
                                                                    +4
                                                                    Давайте отделим язык, от распределения памяти.

                                                                    Статья про то, что заранее известные и большие куски памяти можно распределить статически. Это можно сделать на любом языке. Я предпочитаю C++.

                                                                    Теперь о С++.

                                                                    Т.е. писать на С++ для 3кб ОЗУ это норм, а malloc это зло. Мне изменяет память или объект, даже со статическими свойствами, при создании получит свой поинтер в памяти?


                                                                    Да, я думаю это норм писать на С++ для 3кб ОЗУ, если мои данные размещаются правильно и ОЗУ не расходуются где не нужно.

                                                                    И да, я считаю, что если моя прошивка занимает килобайт флеша, то реализация malloc на 2кб для AVR и 10кб на ARM это многовато.

                                                                    На С++ можно писать и без динамической памяти вообще, кучу можно выкинуть, как и new/malloc. При этом многие вещи от C++ по прежнему будут полезны — строгая типизация, RAII, шаблоны, некоторая часть стандартной библиотеки и алгоритмов.

                                                                    Мне изменяет память или объект, даже со статическими свойствами, при создании получит свой поинтер в памяти?

                                                                    Поинтер в памяти получит любой объект. Но поинтер не занимает места :)
                                                                    И вопрос в какой памяти. Если это .readonly, то и пускай себе там аллоцируется. Если это .data или .bss — тут вопрос насколько больше этот объект будет занимать. Если речь идет о некоторых данных (скажем какой нибудь буфер), то что на C, что на C++ они будут занимать одинаково. Не вижу разницы.

                                                                    Кстати, в примерах из статьи С++ объекты не занимают ни байтом больше, чем аналогичные структуры данных на С

                                                                    И во-первых, инструментом разработки должен стать С. С его полной ясностью.

                                                                    Полная ясность определяется стандартом. И у С и у С++ он есть. Все остальное от умения или не умения этим пользоваться.

                                                                    Мне кажется у Вас устаревшее мнение о С++ как о прожорливом языке, где постоянно течет память. Это мнение не соответствует действительности. Хочу напомнить, что язык С++ как и компиляторы для него развиваются не как попало. Стандарт обновляется каждые 3 года, и над ним работает несколько сотен человек. Вы всерьез думаете, что никто там не подумал про embedded программирование?

                                                                    С вопросами о фрагментации памяти согласен, но это, опять же, не зависит от языка.
                                                                      –1
                                                                      Давайте отделим язык, от распределения памяти.

                                                                      Нет не давайте:)) Как раз это порочная практика. Язык это инструмент разработки, который должен быть релевантен задаче и условиям.

                                                                      Да, я думаю это норм писать на С++ для 3кб

                                                                      Это лишь говорит об ограниченности практики в этом вопросе. Не упрекаю, а лишь отмечаю. Все дело в том, что не только в распределении памяти проблема. А в наследованиях, полиморфизме тоже. Как не странно, но при разработке софта для контроллеров есть коренная проблема — тайминг. Она крепко связана с кодом и тактами. Как только в ваш код попадает «темная зона», а при наследованиях таких зон куча, вы получаете неуправляемые ситуации. И поиск их займет месяцы на боевых проектах. Вам в любом случае придется всю иерархию классов изучить от корки до корки. Все профиты ооп будут, по сути, утеряны.

                                                                      Но поинтер не занимает места :)

                                                                      Это что-то новое:) поинтер занимает место равное адресному слову. Вопрос что вы с ним потом сделаете — к вам. Можете освободить, можете хранить и сами решать где. Но вопрос в другом — куда смотрит этот поинтер? И что там лежит?

                                                                      Мне кажется у Вас устаревшее мнение о С++

                                                                      Нормальное у меня мнение. Боевое :)) Знаете есть такая ОС как freertos? Как вы думаете на чем она? И прошу пояснить — почему?

                                                                      Что же касается С++ в мире контроллеров, то это зло пришедшие с arduino ИМХО. И суть его проста — маркетинговое продвижение и понижение порога вхождения.

                                                                      Казалось бы как так? Ведь C++ сложнее С! Но если посмотреть на скетчи, все станет ясно. Для того, чтобы вывести строку в UART нужно буквально пару строк. Это позволяет сделать именно С++. И каждый себе, понимаешь, эмембер. И продавать такой код куда как проще.

                                                                      Во всем важна первопричина. Остальное на нее навешивается блястящей герляндой, за которой уже и елечку не рассмотреть ©

                                                                      P.S. Но корнем была фрагментация памяти как причина отказа от динамики. В моем посте. И это как раз зависит от языка. Точнее у кого как. У C и C++ для этого либы. Вы же понимаете, что контроллер этим вопросом не занимается?
                                                                        +4
                                                                        Само по себе использование компилятора С++ не вызывает дополнительного оверхеда ни по памяти, ни по тактам. Методы класса — это обычные функции, у которых просто есть доступ к указателю this. Виртуальные функции добавят всего лишь 1 уровень косвенного вызова через таблицу виртуальных функции. Объявленный на стеке или в глобальных переменных объект не использует динамическое выделение памяти.
                                                                        С++ и классы — это прежде всего способ разделить сущности для программиста, человека.
                                                                        Uart uart;
                                                                        Button button;
                                                                        main()
                                                                        {
                                                                          if (button.pressed())
                                                                             uart.send("hello C++");
                                                                        }
                                                                        

                                                                        Код эквивалентен коду на С, как по RAM, так и по Flash.
                                                                        bool buttonPressed();
                                                                        void uartSend(const char*);
                                                                        main()
                                                                        {
                                                                          if (buttonPressed())
                                                                            uartSend("hello C");
                                                                        }
                                                                        

                                                                        Объект С++ без внутренних данных занимает 0 байт. sizeof его будет равен 1, но это только для совместимости.

                                                                        На С++ норм писать и с 64 байтами памяти. Просто нужно понимать язык и типы оперативной памяти, где что выделяется и как долго живет.

                                                                        Что же касается С++ в мире контроллеров, то это зло пришедшие с arduino ИМХО. И суть его проста — маркетинговое продвижение и понижение порога вхождения.

                                                                        C++ был задолго до появления Arduino, в том числе в микроконтроллерах. И это не зло, а благо, позволяющее писать и поддерживать большие проекты.
                                                                          0
                                                                          Никто не спорти, что С++ был задолго. Но вот популяризация для контроллеров пришла, подчеркиваю ИМХО, с arduino. Причины я описал. Ну не было по моей памяти «отчаянных» кто писал на С++ под контроллеры «до».

                                                                          Теперь про спецификации языков. Спецификации это одно, а реализация — другое. Не новость, что многое в linux сначала парсится в С, а затем уже компилится в бинарники. Как думаете почему? Это не совсем про память, сразу замечу. Просто в этом есть коренная истина использования С. Которую многие упускают. Реализация выделения памяти имет прямую связь с этой стратегии.

                                                                          Я Вам уже привел пример по неучтенному использованию ОЗУ при использовании С++. Оно понятно для конкретного компилятора+платформа. Только так. Это напрямую связано с накладными расходами на ООП.

                                                                          И последний гвоздик в С++ для контроллеров. ООП имеет совершенно конкретные цели. И как не странно, одна из них — нормализация данных в памяти. Т.е. устранение избыточности. И динамическое создание объектов на основе классов, как бы… ну это его миссия:)) В то время, как статика = избыточность. Т.е. вы граблями телегу грузите. Да, ручка удобная, да цвет красивый… но это грабли;)
                                                                            0
                                                                            Никто не спорти, что С++ был задолго. Но вот популяризация для контроллеров пришла, подчеркиваю ИМХО, с arduino. Причины я описал. Ну не было по моей памяти «отчаянных» кто писал на С++ под контроллеры «до».

                                                                            Вы экстаполируете свой личный опыт на других людей. Прекрасно писали embedded на С++ до ардуино, который кстати, удел больше хоббийщиков, не умеющих программировать. Вообще не понимаю зачем вы приплетаете сюда ардуино.
                                                                            Мне кажется реальная причина появления С++ в embedded — это контроллеры стали мощнее, появилась возможность писать более крупные программы, которые очень тяжело писать на ассемблере или C.
                                                                            Не новость, что многое в linux сначала парсится в С, а затем уже компилится в бинарники. Как думаете почему? Это не совсем про память, сразу замечу. Просто в этом есть коренная истина использования С. Которую многие упускают. Реализация выделения памяти имет прямую связь с этой стратегии.
                                                                            При чем тут вообще Linux и что там куда парсится?
                                                                            Я Вам уже привел пример по неучтенному использованию ОЗУ при использовании С++. Оно понятно для конкретного компилятора+платформа. Только так. Это напрямую связано с накладными расходами на ООП.

                                                                            Где оно неучтено? Все прекрасно известно, пустой объект памяти занимает 0, объект с данными — по размеру этих данных, объект с виртуальными функциями — плюс таблица виртуальных функций (просто набор указателей по числу виртуальных функций).
                                                                            И последний гвоздик в С++ для контроллеров.

                                                                            Ваши метафоры не могу понять. Какие-то телеги, миссии…
                                                                            И если ставите минусы, то приводите, пожалуйста, конкретные факты и обосновывайте свои доводы примерами. А то это как-то не по-тимлидски.

                                                                              –1
                                                                              Я не минусую. Это во-первых. Во-вторых, я дал Вам совершенно четкие причины нерелевантности С++ для контроллеров с небольшими ресурсами.

                                                                              Пример, я Вам пишу, что по моему личному мнению arduino стало причиной и приводу основания. Вы это пропускаете и пишите — причем тут ардуино?

                                                                              Я вам пишу, чтосмысла С++ нет именно для ограниченных ресурсов. Вы мне сейчас пишите, что в больших ресурсах он имеет смысл. Т.е. контроллеры начали расти. Так и я вам проэто ж… только одновременно с ростом ресурсов автоматически есть и профиты в динамическом выделени.

                                                                              Мне кажется, что Вам стоит перечитать мои комментарии без эмоций. Вы что-то пишите на С++, у Вас это получается. Это ж здорово и очевидно, что технических ограничений в этом нет. Вопрос только в адекватности трудозатрат и результата. Если Вас все устраиват — отлично!

                                                                              P.S. Ой… в треде 2 автора уже. Простите, если что-то перепутал по цитатам. Думаю мысль ясна.
                                                                                0
                                                                                Я вам пишу, чтосмысла С++ нет именно для ограниченных ресурсов. Вы мне сейчас пишите, что в больших ресурсах он имеет смысл. Т.е. контроллеры начали расти. Так и я вам проэто ж… только одновременно с ростом ресурсов автоматически есть и профиты в динамическом выделени.
                                                                                Смысл в С++ есть в любых ресурсах. Все эти namespace, шаблоны, auto, const, ссылки — это потребляет 0 байт ресурсов.
                                                                                Способ выражения программы как набора объектов кому-то нравится, кому-то нет, но обычно он позволяет сделать программу более понятной. Для больших же проектов он просто обязателен. И нет никакого смысла отделять малые проекты от больших по стилю написания. Использование языка С++ несет только плюсы, которые я описал, и ни одного минуса.
                                                                                  0
                                                                                  Перечитайте плз мои аргументы. Вы их настойчиво упускаете. Предлагаю продолжить дебаты только в формате того, что вы цитируете мой аргумент и контраргументируете. Сначала опровергнув мой аргумент. Это будет конструктивно и полезно всем.
                                                                                    –1
                                                                                    Вы их настойчиво упускаете.
                                                                                    Он их не «упускает». Он их не понимает.

                                                                                    Вот вы пишите:
                                                                                    Мне изменяет память или объект, даже со статическими свойствами, при создании получит свой поинтер в памяти?
                                                                                    Да, конечно, если объект не содержит внутри себя совсем ничего — он займёт, всё-таки, свой один байт. Но если у вас в нём совсем ничего нет — то вы можете сделать все функции статическими и компилятор его у вас из кода изведёт. Примерно так. Это же очевидно!

                                                                                    Того факта, что когда-то, давным-давно, славный gcc 2.7.2.3 этого сделать не мог — он просто не знает и потому вашего вопроса не понимает.

                                                                                    Ну положим, что и объекты мы будем создавать не динамически. Кто-то может пояснить, сколько кучи будет использовать подкапотное пространство объектного движка при создании объектов?
                                                                                    Та же самая история: какая куча? Какое «подкапотное пространство»? Вы, эта, вообще о чём? Если вы объекты в куче не размещаете — то на что вообще может потратится куча???

                                                                                    Опять-таки: времёна libg++ (не путать с libstdc++ !) он не застал и вопрос просто пролетает «мимо ушей».

                                                                                    То есть вы обсуждаете какие-то «фантомные боли» из «детства и отрочества» C++ (про которые и-то я знаю только потому что мне прикольно возиться со всяким софтом прошлого века) — а ваш оппонент тогда, возможно, ещё и не родился… откуда ему про них знать?

                                                                                    В современном мире при грамотном написании кода компилятор C++ с соотвествующим линкером не будут порождать ни байта ненужного кода — хотя иногда нужно будет ему помочь.

                                                                                    Например если в моём примере сделать foo не статической, а глобальное — то да, таки один лишний байт под неё будет выделен. Однако если вы используете опции -fdata-sections и --gc-sections (а кто вообще без них программы на C++ пишет?) — то и этот разнесчастный байт будет удалён во время линковски.

                                                                                    То же самое с исключениемя и RTTI — если их отключить, то соотвествующего кода и не будет нигде.
                                                                                      –2
                                                                                      Мне всегда нравится, что комментарий первый перестает быть актуальным уже на третьем посте. И о чем спорили всем пофиг. Ну так я напомню:

                                                                                      1. Основная причина использования статики в контроллерах — фрагментация памяти. Алес. Ничего плохого в maloc как и в статике нет. Есть физика процесса. ее просто нужно знать и учитывать.
                                                                                      2. Я не говорил и не говорю, что С++ плох. Я говорю — он нерелевантен для маломощных контроллеров по причинам:
                                                                                      2.1. Создан для решения вопросов не связанных с низким уровнем программирования. Просто НЕ СОЗДАН.
                                                                                      2.2. При реализации ООП скрывает многие вещи, которые становятся критическими в реальности. Та же перегрузка операторов. Такие баги на реальных проектах можно искать месяцами!
                                                                                      2.3. Нет хорошего или плохого языка, есть их миссия. И есть их реализация. Берем С и С++ смотрим. И С более прозрачен в том, что ты понимаешь, что будет в реальности в кодах процессора. То, что есть оптимизированный компилятор это ничего не значит. А где-то это СТРАШНЫЙ ВРЕД. Важна частная реализация. Один будет лучше,, другой будет хуже. И не ясно сколько займет тактов твоя конструкция из кучи классов. И все… выкидывай свой код на помойку. ВСЕ

                                                                                      Есть что-то сказать по выше перечисленным пунктам?

                                                                                      P.S. Что за выдумки о том, что кто и что видел. Или не понимает. Это просто недопустимо в части программирования под контроллеры. Ты должен знать все. Каждый байт своего кода. Каждый. Повторяю, я не говорю о микроконтроллерах с ОЗУ и тактовыми частотами сравнимыми с моим пентиумом 2003г. Это НЕ О ТОМ.
                                                                                        0
                                                                                        Есть,
                                                                                        Я так и не понял, какая связь между С++ и ООП? Приплюснутый — это язык, ООП — парадигма программирования, которая прекрасно работает и в обычном С.
                                                                                        Заказчик указал в ТЗ восьмибитник с 4 Кбайтами ОЗУ, ну и ладно. Я просто буду использовать функциональное программирование и шаблоны, namespace и constexpr, типобезопасный enum class, а если понадобится, воткну ассемблерную вставку. Никакого overhead не будет.

                                                                                          –1
                                                                                          Потому, что Вы уперлись в одно — доказать что-то, а не слышать аргументы. Прочтите статью. Посмотрите пример. Он реализован на ООП.

                                                                                          Никакого overhead не будет.


                                                                                          Я кажется четко изложил свои аргументы. Более мне сказать нечего.
                                                                                          –1
                                                                                          И не ясно сколько займет тактов твоя конструкция из кучи классов.
                                                                                          А как вы это понимаете в C, я извиняюсь?

                                                                                          Есть что-то сказать по выше перечисленным пунктам?
                                                                                          Да, конечно. Расскажите — как вы вычисляете скорость работы в тактах для программы на языке C, потом можно будет о чём-то говорить.

                                                                                          Пока что я вижу утвеждения, которые одинаковы либо либо C и C++ одновременно, либо просто неверны. Какой-нибудь нашумевший пример прекрасно вызовет никогда не вызываемую функцию с на C и на C++ — никакой принципиальной разницы.

                                                                                          А constexpr-выражения (и, вообще, любые вычисления во время компиляции) — могут быть гарантированы в C++, но не в C.

                                                                                          P.S. Что за выдумки о том, что кто и что видел. Или не понимает. Это просто недопустимо в части программирования под контроллеры. Ты должен знать все. Каждый байт своего кода. Каждый.
                                                                                          Ой сколько гонору-то.

                                                                                          Повторяю, я не говорю о микроконтроллерах с ОЗУ и тактовыми частотами сравнимыми с моим пентиумом 2003г. Это НЕ О ТОМ.
                                                                                          А о чём, извините? Всё ведь просто: если цена дополнительной памяти и более быстрого процессора в микроконтроллере меньше, чем экономия на зарпалате программиста, пишущего код под эти контроллеры — то, стало быть, использовать более удобные инструменты использовать выгоднее.

                                                                                          В современном мире чтобы экономить и учитывать каждый байт — нужно только если выпускать что-то какими-то просто миллиардными тиражами. А такими тиражами мало что, всё-таки, выпускается.
                                                                                            –1
                                                                                            Да, конечно. Расскажите — как вы вычисляете скорость работы в тактах для программы на языке C, потом можно будет о чём-то говорить.


                                                                                            Отнюдь. Говорить УЖЕ не о чем. Рекомендую реализовать функцию delay и ручками написать протокол… ну к примеру 1w. Вот тогда все встанет на место. Пока одна теоретика.
                                                                                              0
                                                                                              delay реализуется через системный таймер. Или вы такты считаете? Так на arm с их конвеерным ядром и арбитражем на шинах это работать не будет. Даже разработчики контроллеров стыдливо умалчивают о количестве тактов на операцию.
                                                                                              C 1-wire можно работать через обычный spi или usart. О аппаратных i2c-1w мостах я уж молчу.
                                                                                              Даже если делать ногодрыгом на 8-ми битниках решение на С и С++ одинаково.
                                                                                            –1
                                                                                            Та же перегрузка операторов. Такие баги на реальных проектах можно искать месяцами!

                                                                                            С 2006 года работаю с С++ на реальных проектах.
                                                                                            Ни разу не сталкивался с проблемами из-за перегрузки операторов.
                                                                                            Это, простите, какой дегенерат должен писать перегрузку, чтобы она вызвала проблемы?
                                                                                            В С++ полно способов себе ноги отстрелить с корнем, но писать так чтобы ноги не отстреливались не представляет собой проблемы. Теже оператор — не перегружай без необходимости, а если перегружаешь — оператор должен на 100% соответствовать аналогичным операторам. И… ВСЁ. Нет проблем с перегрузкой.
                                                                                              –1
                                                                                              Очень здорово. Ведь именно эту мысль я доносил. Разговор шел о перегрузки оператора. А не был приведен частный пример. Давайте теперь эту полемику развернем.

                                                                                              Можно продолжить — писать нужно без ошибок, тогда ошибок не будет. Вы против этого утверждения?
                                                                                                0
                                                                                                Не совсем про микроконтроллеры, и вообще оффтоп…

                                                                                                Это, простите, какой дегенерат должен писать перегрузку, чтобы она вызвала проблемы?

                                                                                                К сожалению такие люди порой работают в именитых корпорациях.

                                                                                                github.com/Microsoft/clr-samples/blob/master/ProfilingAPI/ELTProfiler/CComPtr.h#L57

                                                                                                Операция взятия адреса возвращает другой тип, не указатель на объект, а указатель на указатель, хранящийся внутри объекта. Изза этого CComPtr (и многочисленные его клоны в различных фреймворках) нельзя положить в std::list банально из-за того, что некоторые реализации list'а удаляют элементы примерно так: delete &*elem. Компилятор сходит с ума при такой замене типа.

                                                                                                Хотя это известный баг. Если один раз столкнулся, то за часик разберешься и будешь понимать что к чему. Про «годами искать проблему» тут, конечно же, речи не идет
                                                                                                  0
                                                                                                  Тут вы указали, что компилятор ругнётся — то есть это вообще не проблема. Проблема, обнаруженная компилятором — по сути вообще не проблема, так как в железку она никогда не попадёт.
                                                                                              0
                                                                                              вы можете сделать все функции статическими и компилятор его у вас из кода изведёт

                                                                                              Ну это уже не будет объектом...)))))
                                                                                            –1
                                                                                            Все эти namespace, шаблоны, auto, const, ссылки — это потребляет 0 байт ресурсов.
                                                                                            Но так было не всегда. Просто вы это время не застали — а ваш собеседник застал.

                                                                                            P.S. Всё-таки Планк был 1000 раз прав, когда говорил «Новая научная истина торжествует не потому, что ее противники признают свою неправоту, просто ее оппоненты со временем вымирают, а подрастающее поколение знакомо с нею с самого начала». Вот вы просто не можете себе представить что C++ может вас «обжечь» на ровном месте (потому что компиляторы поумнели — я не могу ни одной «подставы» ни от GCC 4.4+, ни от Clang 4.0+ вспомнить), а вашего собеседника он, возможно, попалил не раз — и уже выработался рефлекс.
                                                                                          +1
                                                                                          Мне кажется реальная причина появления С++ в embedded — это контроллеры стали мощнее, появилась возможность писать более крупные программы, которые очень тяжело писать на ассемблере или C.
                                                                                          На самом деле вы с вашим собеседником говорите о разных вещах: вы говорите, о том, что C++ может быть полезен, а ваш собественик — о том, что он может быть вреден.

                                                                                          Так вот второе утверждение — действительно было верно долгое время. Но это время — закончилось.

                                                                                          C++ с момента создания «продавался» под лозунгом «вы не платите за то, что не используете» — однако для ранних компиляторов C++ это было не так.

                                                                                          Получить +10%-15% у объёму и -10%-15% к скорости, просто собрав код C++ компилятором! Легко!

                                                                                          А вот уже примерно к GCC 4.2-4.4 эти «детские болезни» были вылечены и соответствующий бенчмарк стал выдавать одинаковые числа.

                                                                                          Вот после этого лешитимным ответом на вопрос «а почему C++?» стал «а почему бы и нет?». А то, что по времени этот момент, примерно, совпал с созданием Arduino… ну тут можно разные гипотезы разводить.
                                                                                            +1
                                                                                            Про глюки компиляторов никто вроде бы речи не вел, по крайней мере явно это товарищ rpiontik не назвал. А назвал такие аргументы как тайминги наследования, поинтер, который куда-то не туда указывает и парсинг чего-то там в Linux в С программы.
                                                                                            Глюки компиляторов все-таки глюки, а значит рано или поздно их исправят и приведут поведению, описанному в стандарте языка. К тому же глюки gcc и глюки IAR отличаются. Поэтому так в общем говорить что С++ плохой язык, подразумевая плохой компилятор gcc не стоит. Тем более, как вы говорите, глюки исправили еще в 2005 году.
                                                                                            Кстати, 10% к объему и скорости — это легко можно получить, просто играясь с опциями оптимизации.
                                                                                              –1
                                                                                              Глюки компиляторов все-таки глюки, а значит рано или поздно их исправят и приведут поведению, описанному в стандарте языка.
                                                                                              И вот тут-то и начинаешь вспоминать Планка. GCC 2.7.2.3 (самое раннье, с чем я игрался) — это 1995й год. А GCC 3.3.6 — это 2005й. Но первое, извините, это — десять лет после появления «C с классами», а второе — двадцать.

                                                                                              Для вас, я так подозреваю, это всё «преданья старины глубокой». Как и для меня, на самом деле. А вот для человека, который застал появление «C с классами» в 1985м? Извините — но для такого человека, который в течении двадцати лет слышит мантру «это глюки, а значит рано или поздно их исправят» и видит, что это «рано или поздно» всё никак не настаёт — это не руководство к действию, а повод назвать того, кто так заявляет идиотом…

                                                                                              Тем более, как вы говорите, глюки исправили еще в 2005 году.
                                                                                              Так вот тут и разница в поколениях: для вас 2005й год — это «когда-то там очень давно», а для вашего оппонента это может быть «когда-то недавно, после того, как я за обманщиками C++-оводами следить перестал».
                                                                                                0
                                                                                                1. Мы здесь (в статье и комментариях) обсуждаем 2019 год, в котором живем, в котором имеем ATmega328 и STM32 c приличными объемами памяти.
                                                                                                2. Если так интересно, писать под микроконтроллеры я начал в 2002г на ассемблере и С под AT90S2313. На С++ под контроллеры перешел после 2010 наверное. Но это больше из-за инерционности мышления, отсутствия С++ компилятора под тот микроконтроллер, что я прогал и, самое главное, такого же навязанного мнения, что на С++ «все тормозит».
                                                                                                3. В целом на С++ я программирую года с 2003, под Windows в основном. Ни разу не встречал глюков компилятора, чтобы прям написан if, а компилятор его не сделал. То что там где-то внутри у пустого объекта выделяется указатель или нет, меня не особо волновало. И сейчас не волнует. Память давно не на ферритовых колечках.

                                                                                                Извините — но для такого человека, который в течении двадцати лет слышит мантру «это глюки, а значит рано или поздно их исправят» и видит, что это «рано или поздно» всё никак не настаёт — это не руководство к действию, а повод назвать того, кто так заявляет идиотом…

                                                                                                4. Вы можете считать меня идиотом, но те глюки исправили 15 лет назад. Вы можете либо также использовать gcc 2.7.2.3, либо принять новую реальность, смириться с тем что сейчас компилятор работает нормально.
                                                                                              –1
                                                                                              Вы правы. О глюках речь не шла. Я с трепетом отношусь к С++ и использую его в некоторых серверных решениях, где требуется надежное управление процессами. Но это другая песня.
                                                                                      +1

                                                                                      Для примера, есть у меня класс списка пинов:


                                                                                      PinList<PA1, PA12, PA3, PA10, PA5, PA8> pins;
                                                                                      pins.write(val);

                                                                                      Этот код компилируется в 7 инструкций, при том что тут две группы пинов идущие не подряд и растущие в разных направлениях… И похожий класс, только под весь порт сразу, есть в mbed, там в конструктор передаются 16 пинов, запоминаются в массиве, потом в цикле все обходятся и модифицированные пины сохраняются в другой массив, наконец write() опять обходит в цикле весь массив в выводит все данные побитно. Есть разница? Так что не нужно сравнивать то, что можно сделать на современном С++ с тем, как это сделано во всяких ардуинах…
                                                                                      Другой пример, делал я как-то менюшку, первоначально она задается в виде удобном для человека, потом трансформируется в совершенно другую структуру, в которой уже нет уровня вложенности, зато есть двусвязный список и некоторые дополнительные флаги. И этот массив структур лежит по флеше, вся трансформация выполняется на этапе компиляции достаточно тривиальным способом, без никакой шаблонной магии.

                                                                                        0
                                                                                        А если еще обьект pins не создавать, то еще меньше будет.
                                                                                        template <typename... Types>
                                                                                        class Container {};
                                                                                        
                                                                                        using Led1 = Pin<PortA, 5> ;
                                                                                        using Led2 = Pin<PortC, 5> ;
                                                                                        using Led3 = Pin<PortC, 8> ;
                                                                                        
                                                                                        class LedsController {
                                                                                          public:
                                                                                            __forceinline template<typename... args>
                                                                                             inline constexpr static void ToggleAll()    {
                                                                                              toggleAll(tLedsController()) ;
                                                                                            }
                                                                                          
                                                                                          private:       
                                                                                            using tLedsController = Container<Led1, Led2, Led3> ;    // вот тут делаем шаблонный тип с разными классами на входе
                                                                                          
                                                                                          __forceinline template<typename ...Args> 
                                                                                           constexpr inline void static toggleAll(Container<Args...> obj)  {
                                                                                              pass((Args::Toggle(), true)...) ;  // проходим по каждому типу в списке и вызываем у него Toggle()
                                                                                          }    
                                                                                          
                                                                                          __forceinline  template<typename... Args> 
                                                                                          inline constexpr static void pass(Args&&...) {}
                                                                                        } ;
                                                                                        
                                                                                        int main() { 
                                                                                          LedsController::ToggleAll() ;
                                                                                          return 0;
                                                                                        }    


                                                                                        Вот я там в статье про светодиды это и описывал, что все это выраждается в то как если бы на ассемблере писать
                                                                                          0

                                                                                          В статье про светодиоды было все достаточно просто… У меня в списке могут быть пины, другие списки и еще один класс в котором пины задаются маской, все это на начальном этапе превращается в список пинов и потом он проверяется на дубликаты. Затем из получившегося списка создается список недублирующихся портов, для каждого порта считается маска всех пинов к нему относящихся, она потом пойдет в старшую часть BSRR для обнуления, далее список пинов превращается в индексированный список, для каждого пина из списка берется разница между позицией бита порта и данных, получается сдвиг при котором данные попадут в правильную позицию, и ищутся другие пины в таким же сдвигом, тогда их можно объединить в группу и отфильтровать по маске в один заход, причем то же самое делается для реверсного сдвига, потом из двух вариантов выбирается лучший, для реверса добавляется RBIT. И в самом конце еще проверяется пишем ли мы в порт целиком или его половинки, тогда можно писать в ODR, что чуть эффективнее. Естественно работает чтение, задание режимов и т.д., большинство операций выполняются на этапе компиляции, на ассме написать лучше не факт, что получится…

                                                                                            0

                                                                                            Ок, понял, т. Е, вы по пину определяете порт, потом строите список портов, и находите маски по пинам этих портов… и собственно устанавливаете эти маски. Правильно понял?

                                                                                              +1

                                                                                              Если находить одни маски, то можно делать тот же toggle(), т.к. там порядок не важен, но запись и чтение(не для bool) сложнее, там порядок имеет значение, потому помимо маски вычисляется еще и сдвиг. Итого будет одна маска на каждый порт которая идет в старшую половину BSRR и еще по одной маске со сдвигом, но уже для входных данных, для каждой последовательности пинов которая будет обнаружена. В частности эквивалентный код для моего первоначального примера со списком состоящим из пинов PA1, PA12, PA3, PA10, PA5, PA8(порядок как у бит в байте) будет следующим:


                                                                                              GPIOA->BSRR = 0x152A'0000 | __RBIT(val & 0x2A) >> 25 | (val & 0x15) << 8;
                                                                                                +1

                                                                                                А для pins.toggle() будет просто:


                                                                                                GPIOA->BSRR = 0x152A'0000 | ~GPIOA->ODR & 0x152A;

                                                                                                Это правильный подход, в отличии от GPIOA->ODR ^= 0x152A можно тоглить разные пины находящиеся на одном порту, например, в основном цикле и прерывании не боясь повредить соседние биты.

                                                                                                  0
                                                                                                  Ага, я понял, спасибо, попробую на вариадиках сделать через ODR пока…
                                                                                              0
                                                                                              Скажите, насколько ваша реализация обобщена? Наблюдал несколько элегантных реализаций — преимущественно с использованием typelists c метапрограммированием. Все эти реализации были неплохо оптимизированны под конкретную платформу. Но все потуги обобщить, скажем, понятие Pin (учитывая все альтернативные режимы работы пина) или любого другого периферийного модуля приводили к серьезным оверхедам.
                                                                                                0
                                                                                                Да это для примера, там же адреса прямые используются, по сути это класс Pin<addr, 5>… единственное тип надо addr надо сделать псевдонимом, чтобы на 8-16-32 битные контроллеры, потому что сейчас он для uint32_t в примере…
                                                                                            –1
                                                                                            Знаете есть такая ОС как freertos? Как вы думаете на чем она? И прошу пояснить — почему?


                                                                                            На мой взгляд, FreeRTOS написана на С по нескольким причинам:
                                                                                            • Она работает на 30+ архитектурах, и не факт что для всех из них есть компилятор С++
                                                                                            • ОС разрабатывается 15+ лет, как уже обсудили в других комментариях в то время компиляторы С++ были еще недостаточно хорошими.
                                                                                            • С хорошо стыкуется с другими языками, ABI четко описан, сущностей языка которые нужно выставлять наружу не очень много (по сути только функции и данные). Так что не проблема завернуть FreeRTOS в С++ или другой язык.
                                                                                            • Как результат целевой охват у библиотеки/ОС написанной на С будет выше


                                                                                            Но это никак не умаляет С++ в эмбеддеде. И уж тем более не может служить аргументом превосходства одного языка над другим. Написать подобную ОС на плюсах не сложно, на хабре даже недавно кто-то занимался подобным.
                                                                                              0

                                                                                              Превосходстве? Откуда это слово? Я где-то его использовал? Я использовал слово — релевантность.


                                                                                              Да, прекрасный анализ по freertos. Есть еще пара мелочей, но не суть. То, что вы описали достаточно, чтобы выбрать С для продакшен проектов. В них становится важным партирование кода. И C тут вне конкуренции. И по этой же причине, очень часто, прочие языки лишь транслируют свой код в С.


                                                                                              В части freertos я, в целом, вижу ряд профитов от С++. И не считаю это оверхедом. Особенно при крупных прикладных задачах.


                                                                                              Но есть и один ключевой недостаток — эти обертки только ваши. И понятны они только вам. В реальных проектах важна приемственность кода. И поэтому, опять… С.


                                                                                              И никакого превосходства. Лишь — релевантность.

                                                                                                +1
                                                                                                Да, согласен, релевантность более правильное слово.

                                                                                                Хотя с самой нерелевантностью С++ для embedded по прежнему не согласен :)
                                                                                                Ну и что, библиотека на С, если она легким движением заворачивается и дальше можно использовать плюшки плюсов?

                                                                                                Ну разве код планируется портировать на платформу где нет плюсового компилятора, или он непригоден для продакшена. Ну тогда да, однозначно С.

                                                                                                Кстати, зачем писать свою обертку над FreeRTOS если есть готовые? берем как обычную библиотеку и используем.

                                                                                                  +1
                                                                                                  Обратное тоже верно: оборачивая плюсами сишную библиотеку — это вы в плюсах пользуетесь плюшками C.
                                                                                                    –1
                                                                                                    Лично я исключительно за плюшки. Исключительно! Главное, чтобы при этом «попа в дверь проходила» :))

                                                                                                    А почему не использовать готовые… ну потому, что это очередная прослойка глюков.

                                                                                                    Мне сложно объяснить, с каким уважением я отношусь к ребятам из freertos. Они заслужили доверие надежностью кода, которой может позавидовать ооооочень много кто. Если бы не их суровая работоспособность, они бы просто не взлетели.

                                                                                                    А также, их документация. Подробная. Четкая.

                                                                                                    Пытаться это лучшее, «завернуть» в что-то похуже… ну лично я всеми конечностями против такой стратегии.

                                                                                                    Да, повторюсь, я буду использовать С++, если встнет вопрос о достаточно сложной, но не критичной логике. Ну к примеру нужно будет сделать WEB сервер. Хотят и тут у мангуста есть щикарные решения на С. Но… положим это будет иметь смысл, т.к. этот сервер будет ну очень кастомный. Но вот обертки над freertos я не буду делать. Это базовая вещь. Почти святая :)

                                                                                                    Просто как спойлер. Я сейчас пилю платформу для IoT с JavaScript как внутрисистемного языка. Платформу для широких масс. В сентябре надеюсь зарелизить ее. Это просто для понимания широты моих взглядов на возможное плюшкование :))) Но везде нужна разумность.
                                                                                                      –1
                                                                                                      Кстати, зачем писать свою обертку над FreeRTOS если есть готовые? берем как обычную библиотеку и используем.
                                                                                                      Например, мою: github.com/nickshl/DevCore
                                                                                                0
                                                                                                Кстати, нашел, что Вы собираетесь по freertos статью делать. Значит Вы с ней работали. Интересно будет взглянуть представление о ней.