Комментарии 87
Да, необходимость отказываться от динамических аллокаций довольно сильно мешает писать прошивки на С++; но на мой взгляд это все равно лучше, чем писать на С — да, стандартными контейнерами не попользуешься (но можно написать свои или взять https://www.etlcpp.com/), да, std::function не поюзаешь (но можно написать свою реализацию без динамических аллокаций).
К счастью, все эти вещи можно отключить буквально тремя опциями компилятора, и форсировать это на уровне соглашения о стиле кода.
Зато шаблоны, лямбды, RAII и ООП без ручных манипуляций с указателями.
Кстати, почему тремя?
Но если отключаешь динамику, то начинается адище — либо как в статье, либо ETL (который имхо в обозримом будущем подавится и упадет под диван), в общем широкий набор способов создать новый язык. Это плохо само по себе, к тому же туго ложится на индустрию.
Я как-то без контейнеров стандартных и исключений вполне себе обхожусь, на самом деле; больше не хватает асинхронщины какой-нибудь, но она на плюсах пока что выглядит довольно мерзко в любом случае.
(Правда велосипеды вместо std::function и std::span таки изобрелись)
Кстати, почему тремя?
Ну, обычно что-то в духе -fno-exceptions
-fno-rtti
и еще какой-нибудь финт, чтобы динамику отключить. В опциях gcc я не так хорошо ориентируюсь, вполне может быть достаточно сделать --specs=nano.specs
вместо трех отдельных опций.
больше не хватает асинхронщины какой-нибудь, но она на плюсах пока что выглядит довольно мерзко в любом случае
Ммм, вы находите? Вроде норм… Но вообще, как по мне — уж лучше в потоки, я к асинхронщине в принципе отношусь с сомнением.
Я как-то без контейнеров стандартных и исключений вполне себе обхожусь
Да, но не использовать контейнеры — это же почти идиоматическое преступление, могут и к высшей мере приговорить в интернетах!!!111
Впрочем это может быть интересной концепцией — запретить вообще пространство имен STL. Только сишные либы (выпилив и из них динамику) и сам стандарт языка. То есть все эти вот извращения вокруг STL это примерно как мамкино веганство («курицы глупые, их можно есть»), а вот такой подход — чистое, настоящее веганство.
Правда велосипеды вместо std::function и std::span таки изобрелись
М, да, кстати, спан бывает полезен, впрочем я и к нему с сомнением отношусь.
Ну, обычно что-то в духе -fno-exceptions -fno-rtti и еще какой-нибудь финт, чтобы динамику отключить
А, ну вы про РТТИ еще вспомнили, точно. Главное же кучу задать нулевой!
Ммм, вы находите? Вроде норм… Но вообще, как по мне — уж лучше в потоки, я к асинхронщине в принципе отношусь с сомнением.
Можно и потоки, лишь бы не городить полотнища свитчей для конечных автоматов. С другой стороны, зато все просто и понятно :)
Да, но не использовать контейнеры — это же почти идиоматическое преступление, могут и к высшей мере приговорить в интернетах!!!111
В embedded — скорее наоборот, насколько я могу судить; многие придерживаются мнения, что С++ в принципе — это недопустимо.
Впрочем это может быть интересной концепцией — запретить вообще пространство имен STL. Только сишные либы (выпилив и из них динамику) и сам стандарт языка. То есть все эти вот извращения вокруг STL это примерно как мамкино веганство («курицы глупые, их можно есть»), а вот такой подход — чистое, настоящее веганство.
Но в std есть куча полезных вещей, которые с контейнерами не связаны — скажем, std:nth_element
или std::atomic
; контейнеры в отдельный неймспейс не выделили.
Можно, конечно, просто несколько контейнерных хедеров "запретить", но зачем — если запретить динамическую аллокацию, то с ними все равно линковаться не будет.
А, ну вы про РТТИ еще вспомнили, точно. Главное же кучу задать нулевой!
Просто именно куча опцией-то как раз не выключается, по крайней мере в Кейле для этого прагму надо писать, в gcc или ставить нулевой размер кучи или какой-то костыль делать типа --wrap=malloc
. Мб в IAR опция, не знаю.
М, да, кстати, спан бывает полезен, впрочем я и к нему с сомнением отношусь.
Меня лично спан (как и все остальные контейнеры STL) огорчает в основном тем, что он не проверяет выход за границы в операторе [] — а легко везде заменить [] на at — не особо получается.
Но спан все равно лучше, чем отдельно указатель и размер.
Можно и потоки, лишь бы не городить полотнища свитчей для конечных автоматов
А вы про какую ситуацию? Просто какой-нибудь парсер со сложным автоматом на кучу потоков не заменишь. Или вы про другое?
В embedded — скорее наоборот, насколько я могу судить; многие придерживаются мнения, что С++ в принципе — это недопустимо.
Да, и оно ведь во многом растет от библиотеки шаблонов. Так что тут так — и этим своим не станешь, и эти врагами объявятся)))
Но в std есть куча полезных вещей, которые с контейнерами не связаны — скажем, std:nth_element или std::atomic; контейнеры в отдельный неймспейс не выделили.
Про std:nth_element даже не знал, спасибо, неплохая вещь для поиска медиан.
Атомики жалко, да.
Меня лично спан (как и все остальные контейнеры STL) огорчает в основном тем, что он не проверяет выход за границы в операторе [] — а легко везде заменить [] на at — не особо получается.
Требования совместимости. Впрочем, выход за границы массива — вещь нечастая. А при наличии foreach… Который еще и оптимизируется лучше.
Но спан все равно лучше, чем отдельно указатель и размер
Имхо норм указатель и размер, я даже в шарпе этого не стесняюсь при работе со строками (иначе получается нечитаемая хтонь), просто дело же не только в этом, спан типа вообще лучше.
Вы Аду не пробовали? Тут параллельно идет обсуждение. Там все есть, и все красиво. :)
А вы про какую ситуацию? Просто какой-нибудь парсер со сложным автоматом на кучу потоков не заменишь. Или вы про другое?
Парсер не особо, но просто какую-то логику "событийную" типа "послать такой запрос — подождать ответ (или таймаут) — послать следующий — подождать" — иногда очень хочется.
Да, и оно ведь во многом растет от библиотеки шаблонов. Так что тут так — и этим своим не станешь, и эти врагами объявятся)))
На мой взгляд, оно растет чаще просто от незнания; многие думают, что в С++ память как-то "сама по себе тратится" и не пытаются вникать дальше, просто сходу отметают.
Требования совместимости. Впрочем, выход за границы массива — вещь нечастая. А при наличии foreach… Который еще и оптимизируется лучше.
Имхо норм указатель и размер, я даже в шарпе этого не стесняюсь при работе со строками (иначе получается нечитаемая хтонь), просто дело же не только в этом, спан типа вообще лучше.
foreach по указателю как раз не сделать, например.
Я лично регулярно за границы вылезал, прям фобия развилась. Особенно учитывая, что никаких санитайзеров-то нема, можно ведь вылезти и не заметить вообще.
Вы Аду не пробовали? Тут параллельно идет обсуждение. Там все есть, и все красиво. :)
Слышать — слышал, пробовать — не пробовал. В бывшем СНГ вроде на ней вакансий около нуля, так что… В Rust лично у меня веры больше.
Парсер не особо, но просто какую-то логику «событийную» типа «послать такой запрос — подождать ответ (или таймаут) — послать следующий — подождать» — иногда очень хочется.
Да, тут бы асинк, но и на потоках можно.
На мой взгляд, оно растет чаще просто от незнания; многие думают, что в С++ память как-то «сама по себе тратится» и не пытаются вникать дальше, просто сходу отметают.
Людей можно понять, в плюсах и функции «сами собой вызываются». Причем ведь накалываешься на мелочах — упустил буквально один символ при наборе, и если бы у тебя не был удален конструктор копирования, то ты бы не увидел ошибку, и возможно нескоро узнал бы, что делаешь что-то не то.
Я лично регулярно за границы вылезал, прям фобия развилась. Особенно учитывая, что никаких санитайзеров-то нема, можно ведь вылезти и не заметить вообще.
Хм, а я ни разу. А что с санитайзерами кстати, почему нет?
Людей можно понять, в плюсах и функции «сами собой вызываются». Причем ведь накалываешься на мелочах — упустил буквально один символ при наборе, и если бы у тебя не был удален конструктор копирования, то ты бы не увидел ошибку, и возможно нескоро узнал бы, что делаешь что-то не то.
Да я их прекрасно понимаю, че уж. С по крайней мере можно целиком в голове удержать, а С++ уже кажется нельзя :)
Хм, а я ни разу. А что с санитайзерами кстати, почему нет?
Почему — не знаю, просто нету их и все.
Ну, точнее, у Кейла они в альфе сейчас https://www.keil.com/support/man/docs/armclang_ref/armclang_ref_lnk1549304794624.htm, про другие тулчейны не в курсе
gcc.gnu.org/onlinedocs/gcc/Instrumentation-Options.html
github.com/google/sanitizers/wiki/AddressSanitizer
Как раз чтобы бороться с выходом за границы массива.
Да я их прекрасно понимаю, че уж. С по крайней мере можно целиком в голове удержать, а С++ уже кажется нельзя
Да-да-да… Там вон ниже гражданин возмущается, но я воздержусь от ответа, так как в общих чертах его уже знаю — «вам надо было написать программу совершенно иначе, и тогда не было бы проблем». Этим плюсы и «прекрасны» для любого человека на планете, кроме, вероятно, khim-а, что как бы вы ни написали ваш код на плюсах, где-то в последнем стандарте есть способ написать его более правильно.
Причем ведь накалываешься на мелочах — упустил буквально один символ при наборе, и если бы у тебя не был удален конструктор копирования, то ты бы не увидел ошибку, и возможно нескоро узнал бы, что делаешь что-то не то.
А можно подробнее и про удаленный конструктор копирования и пример одной забытой буквы? Спасибо.
Отказаться от динамической аллокации ради надежности, потом искать сбои в рантайме, так как stdlib пересобрать нет возможности без исключений, а размер массива «эмпирически» вычисляется под shared_ptr. Потом ищи в какой фазе луны их стало мало.
Вы любите жить в кредит? Писать ПО в кредит точно у вас хорошо получается…
Можно отказаться от исключений, но это не значит, что прошивка никогда не упадет. Но мы ни разу не видели сбоя в рантайме по причине проскочившего шального исключения. Мы соблюдали технику безопасности. Аналогично с умным указателем: разница в размере дополнительной информации образуется из-за разницы в реализации под конкретный компилятор. Эмпирический подбор состоял в том, что прошивка не собиралась, если памяти было мало. Хоть китайское электричество до конца не изучено, но тут от фазы луны мало что зависело. (upd: в настоящее время все shared_ptr удалены)
Мы никогда не полагаемся на удачу или UB.
Интересно было бы в сравнение добавить Keil.
Давно уже есть готовая реализация std::function без аллокаций https://github.com/WG21-SG14/SG14/blob/master/SG14/inplace_function.h
и в частности www.etlcpp.com/function.html
всяко лучше своего моноцикла
Ну… я даже не знаю что и сказать. Т.е. сами придумали себе проблем и сами их героически решаете — путь Дон Кихота не зарастет никогда. Где-то рядом промчался хлебный троллейбус.
А все потому, что думать — лень, что хочется все делать готовым микроскопом, даже если он не предназначен для забивания гвоздей. И иногда не стоит бездумно использовать ООП, обертки, исключения и другие достаточно тяжелые вещи там, где без этого вполне можно обойтись. И дело тут не в языке — ибо C++ отлично работает и без всего этого, а конкретно в том, кто пишет.
Мне страшно у вас спросить — у меня множество проектов на ATTINY85 как раз на C++ — а там всего 512 БАЙТ ОЗУ — как бы вы там std::array или std::vector-ом то пользовались и исключениями?
Мораль простая — не нужно использовать инструмент там, где он абсолютно не подходит, даже, если инструмент — стандартный и привычный. Нужно чуть потрудится и либо найти и использовать подходящий инструмент, или (О, БОЖЕ!) написать свой.
srd::array оверхед имеет только без оптимизации совсем. А так это то же самое что и голый массив.
Inline по оптимизации ставите и никаких отличий.
Зато пользоваться значительно безопаснее.
Вектор согласен, если заранее известен размер, непонятен смысл его использования.
Эксепшены, имхо, одни минусы. Мало того, что оверхед гигантский, так еще и SIL не рекомендует их и с ними пройти сертификацию практически невозможно.
К сожалению стандарт не требует размер std::array чтобы был равен размеру массива.
sizeof(array<int,10>) >= sizeof(int[10])
А зачем sizeof ом выяснять размер массива? Это не безопасно, указатель передадите и вот вам уже размер указателя.
Для этого есть std::size. Там все будет равно.
sizeof — возвращает размер в char-ах
std::size — возвращает количество элементов
Для этого есть std::size. Там всё будет равно.Метод std::size для «голых» указателей вообще не определён.
Да, поэтому непонятно зачем sizeof использовать для массивов.
Вы же знаете тип, знаете количество элементов, можете получить размер в байтах.
Имхо, sizeof имеет смысл использовать только для типов.
Метод std::size для «голых» указателей вообще не определён
Для "голых" массивов вполне даже определен.
Да, поэтому непонятно зачем sizeof использовать для массивов.А затем, что вам таким образом хотят сказать что есть накладные расходы у std::array по сравнению с голым массивом:
sizeof(array<int,10>) >= sizeof(int[10])
А количество элементов и там и там будет равным:std::size(array<int,10>) == std::size(int[10])
Для «голых» массивов вполне даже определенА при чем тут голые массивы, вы же рассматривали указатели.
sizeof(array<int,10>) >= sizeof(int[10])Это классическая перестраховочное допущение. На практике я не вижу причины, почему не должно выполняться равенство. Или может у Вас они есть?
Я понял, вы имеете ввиду, что std::array хранит в себе size. Но там же все constexpr
constexpr size_type size() const _NOEXCEPT
{ // return length of sequence
return _Size;
}
т.е. компилятор фактически все обсчитает на этапе компиляции и ничего хранить не будет. Возможно стандарт разрешает делать размер std::array больше размера голого массива, но эта такая еще реализация должны быть. Мне такие не встречались.
Даже проверил специально на IAR 8.40.2
Как говориться найди отличия.
Поэтому не пойму, где там оверхед должен быть?
Однако дело даже не в этом, дело в том, что в 90% случаев вполне можно обойтись обычным массивом, не прибегая к обертке. Это не значит, что так надо делать везде и всегда, все зависит от задачи. Но в случае ограниченности памяти это вполне уместно.
Насчет безопасности — да, безопасней, однако, если программист понимает, как оно устроено в памяти и понимает зачем ему нужен именно массив (а разработчик встройки просто обязан это знать и понимать), то большинство проблем с безопасностью также отпадает — достаточно понимать граничные случаи. Если же они достаточно специфичны — можно обернуть все это в класс, где все их учесть — и по оверхэду это все равно будет меньше, т.к. системные обертки написаны со всеми возможными и невозможными допущениями, которые в данном окружении сильно избыточны.
https://m.habr.com/ru/company/auriga/blog/539760/#comment_22898618
Вполне возможно, что в простых случаях компилятор всё оптимизирует, но нет требований, чтобы это было всегда.
В остальном — вообще на много чего нет гарантий. На то что компилятор не вставит Sleep(1000) после каждой инструкции — тоже гарантии нет. Но вообще он не вставляет.
А так его можно использовать только там где это не критично.
Кстати, у std::array есть ещё одна неприятная особенность.
std::array::size имеет тип size_t, а sizeof(a)/sizeof(a[0]) это константа, которую компилятор легко превращает в нужный тип.
Итого в MSVC получаем предуреждение об усечении типа:
std::array<int, 1> a;
short size_a = a.size(); // warning C4267: 'initializing': conversion from 'size_t' to 'short', possible loss of data
int b[1];
short size_b = sizeof(b)/sizeo(b[0]); // OK
На счет size_t — ну так это правильный тип для размера в памяти, надо приведение — просто пару букв дописать. А вот sizeof(a)/sizeof(a[0]) выглядит архаично, так и не скажешь, дружит ли оно с выравниванием, ну и есть это:
template<typename T, uptr N>
inline constexpr uptr array_size( T ( & )[N] )
{
return N;
}
sizeof(a)/sizeof(a[0]
Тоже тип будет size_t, так как sizeof — возращает size_t. И ОК вам выдает, только встроенная диагностика, а любой анализатор, типа PCLint, так же руганется там и по правильному в обоих случаях к short привести явно. Если вообще этот short тут нужен.
А в строчке с warning все верно компилятор написал, сделайте
constexpr auto size_a = a.size()
Какая идея у преобразования size в short? Либо явно тогда преобразуйте, либо оставьте тип size_t. Память сэкономить? Но тогда у вас доступ может быть медленнее.
Тогда нужно в любом случае явно кастить, иначе у вас чисто теоретически в другом проекте может быть косяк.
Дело в том, что short size_a = a.size() — это рантйм проверка, и поэтому вам анализатор выдает, что чисто теоретически, если вдруг вы когда-то зададите массив размеров больше длины типа short, тут будет обрезка.
А sizeof(b)/sizeo(b[0]) — это не в реальном мире — это во время компиляции, и анализатор точно значет, что для вашего b, это не выйдет за размер short.
чтобы убрать ворнинг, думаю можете сделать так:
constexpr short size_a = a.size();
Тогда компилятор будет высчитывать это на этапе компиляции и поймет, что вы не вышли за границы типа short.
но правильно делать кончено так:
constexpr short size_a = static_cast<short>(a.size());
А с массивом С эти приседания не нужны.
Ок, не нужны, если использовать sizeof.
Зато прикольно будет, если кто-то потом сделает так:
https://gcc.godbolt.org/z/YEMd6hfa5
В первом случае вас предупредили, что возможно потенциальная ошибка, и обратили ваше внимание, на то, а точно ли вы хотите, чтобы так было? Ну если точно, то вы должны сделать cast. А если не точно, то значит вы так не хотели, и просто ошиблись.
(5): warning C4305: 'initializing': truncation from 'unsigned __int64' to 'short'
<source>(5): warning C4309: 'initializing': truncation of constant value
</code>
Мы ведь не игнорируем предупреждения компилятора, не так ли ? ;)
Вот бы было указано в стандарте, что std::array должен быть двоично идентичен C-array тогда можно было бы использовать std::array не боясь неправильного размещения в памяти.Я тебе одну умную вещь скажу, только ты не одижайся. Но для С-array тоже не гарантируется «неправильное размещение в памяти» если под «неправильным» понимается выравнивание. Только в случае с массивом выравнивание сделает менеджер памяти и вы это ни когда не увидите. Вы реально считаете, что запрашивая new char[3] будет выделен блок памяти в три байта?
Кстати, у std::array есть ещё одна неприятная особенность.И это как раз правильно.
std::array::size имеет тип size_t
sizeof(a)/sizeof(a[0]) это константа, которую компилятор легко превращает в нужный типТут компилятор подставляет неявное преобразование, это действительно правильное поведение?
ага я не понял, откуда там оверхед?
вот я на IAR проверил
Советую посмотреть следующее видео с 8 по 23 минуты, там как раз как можно эффективно применять С++ во встройке для валидации очень многих вещей на этапе компиляции и получить ровно такой же бинарник, как и на С в конце, только написание кода на порядок безопаснее.
youtu.be/TYqbgvHfxjM
если программист понимает, как оно устроено в памяти и понимает зачем ему нужен именно массив, то большинство проблем с безопасностью также отпадает
Да-да — сначала программист умный и всё понимает, а потом внезапно heartbleed.
Ещё раз, а при чём тут С++42 если проблема "от недостатка опыта на железе"
При правильном на подходе С++ можно накрутить намного больше чем на чистом Си, с кучей валидаций на этапе компиляции и синтаксического сахара, но итоговый бинарник будет ровно таким же как и на Си.
тут на 22:30 есть сравнение итогового ассемблерного кода
Кресты в любительский эмбед активно привнесло ардуино, насколько помню.
Да и вообще. Вот скажем, есть задача: кучка шаговиков которыми нужно рулить.
С:
Упаковываем пины в структуры, пишем процедуы управления, но когда нужно сделать шаг(И) — как передать экземпляр? Понятно что указателем на структуру конкретного ШД. Т.е. нечто вроде steps(*leftStepper, 10)
при этом все руками описывать и ни дай бог чтонить изменить в сигнатурах — придеццо перелопатить весь исходник.
плюсы:
LeftStepper.(тут выпадаете автодополнение на методы)steps(10);
при изменении структуры данных или методов — правки минимальны обычно…
плюс таки сильнее контроль типов, шаблоны, constexpr, вот это вот все…
На самом деле ваше сравнение неудачное имхо, принципиальных отличий нет, вызываете ли вы foo(*bar, baz...) или bar.foo(baz...). Как по мне, кратно более крутое отличие, понятное широкой аудитории — наличие неймспейсов в плюсах.
а принципиальное отличие… left[автодополнение].s[автодополнение]([выпадает нужный список параметров, заполняем]); гораздо быстрее.
В принципе да, встречается такое автодополнение что лучше бы его не было, но ведь всегда можно взять внешний редактор который вот прям как надо.
Как насчёт написания мелкого аллигатора и использования placement new
на статически выделенном массиве?
*аллокатора
Переопределить глобальные операторы new и delete?
Кто-нибудь сейчас читает и думает — ага, норм, шаблоны, классы, операторы можно переопределять, просто мякотка. Только сперва надо язык обрезать садовыми ножницами, которых притом нет… Ой, да ну его…
Наконец-то появилось время внимательно прочитать.
Это самое неприятное, поскольку нет никаких подсказок, по какой причине все так неприятно обернулось
На самом деле есть, все описано в руководстве на С++, в разделе "Overview—Standard C++", а баг с биндингом вынесен мной еще года 2 или 3 назад и так и висит у них тут:
[EWARM-7305, TPB-3270]
В принципе даже со сложными темплейтными шаблона он справляется на ура. Есть пару небольших косяков еще, например невозможно использовать if constexpr в конструкторе, но это они поправили в 8.50 версии. Ну и по мелочам, но в целом все неплохо.
std::exception. Только вот стандартную библиотеку нужно будет использовать вдвойне осторожнее, поскольку пересобрать ее с нужными опциями под IAR возможности нет.
Там почти на всей библиотеки стоит noexcept, так что правильно, что не используете exception — офигенный оверхед, недопустимо использовать для SIL и вообще можно обойтись из них, преимущества их использования для встроенного ПО вообще мне непонятны.
std::vector
Про вектор, я уже написал, если вы задаете ограниченный размер, под вектор, который все равно вам известен на этапе компиляции и никогда не поменяется. Смысл использовать вектор вообще не понятен.
std::shared_ptr
Тоже самое, что и с вектором — стреляем из пушки по воробьям. Самое интересное, что для сертификации по SIL вам придется описывать все это в архитетктуре, писать юнит тесты, исправлять замечания статического анализатора, и все для того, чтобы использовать то, что использовать не нужно. Т.е. вы просто добалвяете ненужных еффортов на разработку и сертификацию. Странно, только если уже так был написан легаси код, и в не хотите его менять.
В этом случае, когда мы присваиваем переменной callback экземпляр нашей лямбда-функции, будет использована динамическая аллокация.
Вот тут не понял, какая еще динамическая эллокация? Тут же все известно на этапе компиляции, максимум memset c известным размером на стеке. Heap тут точно не будет. Надо её в настройках проекта в 0 ставить, чтобы проверить, что у вас нигде нет динамической эллокации.
std::function явно вызываются исключения,
Это явно не про IAR, там все вот такое:
function() _NOEXCEPT
{ // construct empty function wrapper
}
function(_Unutterable) _NOEXCEPT
{ // construct empty function wrapper from null pointer
}
Поэтому IAR и является стандартом для SIL ембеддед разработки, что все эти ненужные вещи типа rti, exception можно отключить и ваша библиотека будет работать без всех этих ненужных вещей. А вот GCC универсальная штука, которая на ебмед болт положила, отсюда и запрет на его использование для SIL.
static auto global_handlers = std::pair<Handler, Handler> {};
Да такая же ситуация случается с constexpr константами, когда кто-то хочет обратиться по их адресам, каждый в разных юнитсах компиляции получает разные адреса. В итоге код быстро разрастается, поэтому inline необходим!
Вообще у IAR есть 8.40.3 Functional Safety — на основе 8.40.2 — работает хорошо, а исключением известных багов, которые хорошо описаны.
правильно, что не используете exception — офигенный оверхед, недопустимо использовать для SIL и вообще можно обойтись из них, преимущества их использования для встроенного ПО вообще мне непонятны
Несмотря на бесспорность утверждения, я как-то в разговоре с одним коллегой пришел к обратному выводу. Представьте себе, что при вычислениях, при каком-нибудь банальном сложении, что-то пошло не так и (если вам повезло и у вас локстепнутое ядро) вывалилось исключение процессора. Вы можете внутри него просто передать управление на резервный комплект, а тут уйти в перезагрузку и ресинхронизацию. А можете в его коде применить разнообразную эвристику (типа пройтись по стеку например) и вернуться обратно в кодец и например заново пройти данный цикл вычислений, пересинхронизировавшись с другим комплектом без перезагрузки (соответственно коэффициент готовности растет). А если его нет, или если потеря точности в один цикл несущественна (например вы там сидите и интегрируете дифур) — так и пойти дальше. И вот когда есть исключения и код обернут верно, то вы получите инфу об этом в нужном месте основного кода, без сложных способов возврата из обработчика, дропнете этот цикл и все.
Недопустимость оных для SIL sensitive приложений вообще веселая штука. Вот есть в Аде обработчики исключительных ситуаций процедур, по сути те же самые, но их не только можно, а нужно использовать…
Смысл использовать вектор вообще не понятен
Положим вам нужен массив переменной длины с постоянным размером в памяти. Для std::array потребуется отдельная переменная длины, а range based for вообще не взлетит. Вот отсюда и рождается вектор на некоем заранее заданном хранилище.
Тоже самое, что и с вектором — стреляем из пушки по воробьям
А почему? И это, разве есть способ никогда-никогда не использовать shared_ptr?
Разработка firmware на С++ словно игра в бисер. Как перестать динамически выделять память и начать жить