Как стать автором
Обновить

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

При чем тут С++? При чем здесь девайсы?

Чтобы успешно разрабатывать большой проект на языке C++, необходимо хорошо настроить процесс разработки в команде (а у нас это несколько десятков инженеров). Также можно значительно осовременить разработку на C++ за счет использования подходящих инструментов статического и динамического анализа и правильной интеграции их в процесс разработки.

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

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

Извините, а для разработки на каком языке не надо

хорошо настроить процесс разработки в команде (а у нас это несколько десятков инженеров)

и

значительно осовременить разработку на ХХХХХ за счет использования подходящих инструментов статического и динамического анализа и правильной интеграции их в процесс разработки.

-?-

Безусловно процесс разработки важен при разработке на любом языке, но в мире С++ эта проблема стоит особенно остро, потому сам язык очень сложный, многие вещи можно делать разными способами.
Вот например проверки clang-tidy группы modernize-* будут требовать использования более современных вариантов решить одну и ту же задачу на C++:
modernize-avoid-bindmodernize-avoid-bind
modernize-avoid-c-arrays
modernize-use-auto
В других языках скорее всего просто не будет возможности написать код 5 разными вариантами.
Плюс в С++ многих полезных инструментов нет "из коробки", как например в Rust или Go.

Безусловно процесс разработки важен при разработке

Мне кажется, тут вы кратко выразили главную мысль всей статьи. Вряд ли тут можно что-то возразить. Я с вами совершенно согласен.

modernize-avoid-c-arrays сработает в структуре? К примеру принял пакет и привожу его к указателю на структуру: struct BBB_t { int a; int b[10]; inc c; };
тогда приходится отключать?

struct BBB_t
{
    int a;
    int b[10]; // NOLINT(modernize-avoid-c-arrays)
    int c;
};
extern void* rawData;
auto* structData = static_cast<BBB_t*>(rawData); // OK

Предупреждение будет при объявлении структуры, его нужно будет отключить. При приведении указателя к типу структуры предупреждения не будет.

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

поясните пжлст про "Форматирование кода". У вас не проходит пулл-реквест если формат неверный? Или осуществляется автоформатирование после коммита?

про юнит-тесты и valgrind, вы парсите вывод валгринда? насколько стабильным является отлов ошибок?

рассматривали ли другие варианты тестовых фреймворков? Рекомендую CppUTest, мемлик-детектор из коробки и можно гугл-тесты запускать, https://cpputest.github.io/manual.html#gtest

 У вас не проходит пулл-реквест если формат неверный? 

Да, если формат не верный, Pull Request не пройдет.
Обычно ошибки форматирования находятся еще раньше, так как также настроена интеграция clang-format в используемых IDE и при сохранения файла он будет сразу отформатирован и дополнительно еще есть локальный git-hook который обнаружит проблему до того, как будет создан PR. Тогда можно вызывать команду, которая исправит форматирования в измененных файлах автоматически.
Автоформатирование после коммита мы не делаем, потому что не нужно и рискованно менять код после коммита

Спасибо за статью, очень интересная, но вы больше в ней про процесс сборки рассказали, интересно было бы почитать продолжение про то, какие именно решения для улучшения кода с++ вы используете, например, почему вы не запретили исключения в формате, как это сделано в google-style

По поводу использования исключений ответ есть в Google C++ Code style: On their face, the benefits of using exceptions outweigh the costs, especially in new projects.  However, for existing code, the introduction of exceptions has implications on all dependent code
Наш проект относительно новый (по меркам других C++ проектов с многолетней историей), и мы используем исключения, потому что это удобно.
Какие еще темы были бы интересны?

Хорошая статья и подходы верные, только вот с выбором C++ все равно согласиться не могу, потому что вы тратите кучу ресурсов на борьбу с его недостатками, и при этом никаких его преимуществ, кроме, возможно, большего количества профессиональных разработчиков на локальном рынке, и "проверенности временем", не используете.

У вас нет интеропа с непонятным легаси-кодом на С, у вас нет существующей кодовой базы на миллионы строк, у вас нет экзотических архитектур, сумасшедших требований к производительности, отказоустойчивости, безопасности (security), безопасности (safety) и т.д.

Посмотрите на TamaGo, на Embedded Rust, на SPARK и прочие альтернативы. Я понимаю, что вы не стартап, а корпорация, но подход "мы обвесим C или C++ достаточным количеством тулинга, и он станет нормальным" - не работает. Он не работает у Microsoft, он не работает у Google, он не работает у Apple, и у вас он тоже не заработает. Чем раньше вы (и ваш менеджмент) это поймете, тем меньше у меня, инженера по обеспечению безопасности прошивок, будет новой работы. Жаль только жить в эту пору прекрасную...

Из перечисленных альтернатив мы интересовались только Rust, но действительно большое количество профессиональных разработчиков на локальном рынке - это может и не единственный, но главный фактор, второй фактор - это максимальная переносимость кода, хотя у Rust в этом плане все неплохо, но нам важно чтобы мы могли интегрироваться буквально в любую кофеварку.
Про bounds-safety, кстати, в последнем clang-tidy 18 есть новая проверкаbugprone-unsafe-functions, но мы ее пока не пробовали, у нас пока версия 15.

Стоило, я думаю, написать об этом в предисловии, вместо "выбор языка программирования для нашего приложения был очевиден". Ну и в действительно любую кофеварку лучше получилось бы внедрить подмножество C99, а не C++17, если уж выбирать и по этому критерию тоже. И разработчики были бы еще дешевле, и тулинг весь или тот же самый, или лучше.

Я искренне желаю вам удачи, и надеюсь, что C++ не станет для вас "чемоданом без ручки" в тот момент, когда ваша кодовая база перерастет 10М строк, из проекта уволятся его оригинальные разработчики, и прошивка накопит достаточно мертвого кода, чтобы перестать влезать на кофеварки. Пока что мой собственный опыт показывает, что писать хороший поддерживаемый низкоуровневый продолжительно работающий код на С++ не умеет примерно никто. Язык банально слишком сложный для и так уже весьма непростой предметной области, вариантов выстрелить себе в лицо слишком много, и оптимизирующий компилятор слишком много на себя берет.

Не могу сказать, что более современные альтернативы лишены всех этих недостатков, или не имеют собственных новых, но не замечать того, что практически все вокруг люди, команды и компании уже начали перевод своих прошивок и ОС с С-подобных языков на более современные и безопасные, я не могу. Nvidia переходит на SPARK, Microsoft переходит на Rust, Ядро Linux добавляет экспериментальную Rust-подсистему, Google начинает использование Rust в Android, имя им легион.

Overall, despite these challenges and limitations, we’ve still found Rust to be a significant improvement over C (or C++), both in terms of safety and productivity, in all the bare-metal use cases where we’ve tried it so far. We plan to use it wherever practical. (Google Security Blog)

 подмножество C99, а не C++17, если уж выбирать и по этому критерию тоже. И разработчики были бы еще дешевле, и тулинг весь или тот же самый, или лучше.

Типичный растер, который уравнивает в возможностях С99 и С++17

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

аахахах, живёте в собственном мире каком-то. Если бы в новостях писали о каждом проекте начатом на С++, то интернет бы рухнул все эти новости смотреть. А вам рекламную компанию раста в лицо дают и вы думаете, что теперь уже все на нём пишут

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

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

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

И надо понимать, что помимо проблем в виде страшных UB, сущесвует масса других вещей: архитектура приложения, документирование, процессы на проекте, проблемы с производительностью, гавнокод, баги (в том числе баги, связанные с безопасностью), дурные дедлайны и т.д. и т.п. И рамках действительного большого и сложного проекта все эти факторы играют существенно большую роль, чем ужасные недостатки C++ в сравнении с Rust.

И рамках действительного большого и сложного проекта все эти факторы играют существенно большую роль, чем ужасные недостатки C++ в сравнении с Rust.

То есть все эти факторы будут и в кодовой базе на Rust, и в кодовой базе на C++. Но в C++ будут ещё и грабли с недиагностируемым UB. По моему, выбор очевиден.

То есть все эти факторы будут и в кодовой базе на Rust, и в кодовой базе на C++. Но в C++ будут ещё и грабли с недиагностируемым UB.

И в Rust они тоже будут. We write unsafe so you don't have to, да-да. Двоечник, пишущий на расте, ничем не лучше двоечника, пишущего на C++ - а может, даже и хуже по ряду причин.

Но в C++ будут ещё и грабли с недиагностируемым UB.

Для тех у кого UB может быть недиагностируемым, уже ничего не поможет.

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

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

Плохому танцору всегда <что-то> мешает.

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

Сильно в этом сомневаюсь. Если прочитать, например, ваш же цикл статей про проблемы безопасности UEFI, то перечисленные там проблемы главным образом относятся к категории ошибок в логике (отсутствие защиты от записи в некие области NVRAM в том или ином виде, отсутствие проверок подписи в определенных случаях, беспорядочный запуск кода из ROM'ов PCI-устройств, и т.д., и т.п.) Если честно, я не припомню ни одной проблемы из ряда перечисленных вами в этом цикле статей, которая была бы напрямую связана с использованием C/C++. Ровно те же самые проблемы были бы, если бы UEFI был написан на расте, на Ada, на Java, да на чем угодно. Так что не волнуйтесь, без работы вы не останетесь :)

У меня с тех пор много воды утекло, и я насмотрелся на нынешнем месте работы уже именно на проблемы, вызванные непосредственно недостатками С и C++, а точнее сочетанием необходимости сохранять нечеловеческий уровень концентрации для написания на них безопасного работающего кода с крупномасштабной потоковой разработкой, 500 пулл-реквестами в сутки, очень разным уровнем разработчиков и т.п. Не могу сказать, что у меня есть примеры кодовых баз похожего размера и активности на более новых "безопасных" языках, но пока что решительно все попытки замены C и C++ на FireBloom и embedded Swift, в которых я участвовал и с которыми имел дело, показывают однозначное улучшение по большинству метрик, особенно по количеству эксплуатируемых проблем с памятью, состояний гонок, и самодеятельностей компилятора. При этом внедрение это - крайне недешевое во всех смыслах мероприятие, идущее тем не менее полным ходом.

Мне, честно сказать, давно уже ультрафиолетовы конкретные инструменты, потому что мне необходимо уметь пользоваться любыми. В данном случае я вижу, что ничего, буквально ничего, не спасает большие проекты на С и С++ от "решета", и потому я уже давно пришел к тому, чтобы не начинать новых проектов на ЯП, доказавших практикой собственную ограниченность у всех участников индустрии. Лошадь сдохла - слезь.

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

Ну вот в том-то и дело. Есть подозрение, что при "крупномасштабной потоковой разработке разработчиками очень разного уровня с 500 пулл реквестами в сутки" в решето превратится все что угодно вне зависимости от используемых ЯП. Баланс типов ошибок, возможно, будет несколько другой, но не факт, что наступит какое-то прямо кардинальное улучшение. Впрочем, если есть лишние ресурсы и политическая воля, можно и попробовать попереставлять кровати, почему нет. Вдруг поможет.

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

никогда не слышал про такую аналогию, буду применять в работе :)

Есть подозрение, что при "крупномасштабной потоковой разработке разработчиками очень разного уровня с 500 пулл реквестами в сутки" в решето превратится все что угодно вне зависимости от

вот тут я совершенно согласен! Это уже не крупномасштабный проект, это какая-то раковая опухоль судя по симптомам. Скорее всего он работает (если можно саказать что он работает), через 5 раз на 15-тый, и далеко не по всей декларированной функциональности, иначе зачем его так интенсивно дорабатывать на 500 пулреквестов в сутки.

500 пулл-реквестами в сутки

Это сколько должно разработчиков работать над проектом? Десятки тысяч? И насколько плохо должен быть поделён проект на модули?

Такой проект был бы просто ошибкой и исправлять нужно в первую очередь её, а не язык на котором это пишется

Примерно 500 разработчиков? За последние 365 дней у меня было 304 CL, 100 дней - 139 CL. За месяц 51 CL, но я тут в последние дни много мелочёвки фигачил.

Ну и если уж говорить конкретно про UEFI и другие популярные продукты похожего уровня, то большая часть серьезных эксплуатируемых проблем с ними за последние 5 лет - это именно "детские" проблемы C-подобных языков:
- Intel ME 11.x INTEL-SA-00086 - переполнение стека при чтении файла с MFS.
- UEFI BootHole - переполнение кучи в GRUB при чтении конфигурационного файла, потому что в результате ручной проверки на это переполнение было только сообщение в лог.
- UEFI LogoFail - переполнение кучи и чтение за пределами буфера в парсерах изображений.
- Apple iBoot checkm8 - use-after-free в драйвере USB DFU.

Ошибок в логике тоже было море, и я ни в коем случае не пытаюсь тут сказать, что от использования более современных "безопасных" ЯП они все исчезнут, как по волшебству, и разработчики на них неожиданно перестанут быть людьми и делать ошибки. Не перестанут, конечно, но удаление целых классов ошибок из списка вероятных (а с ростом кодовой базы эта вероятность быстро приближается к 100%) - это несомненное благо, с какой стороны не смотри.

Не увидел ни одной штуки в статье, которая борется именно с мифическими "проблемами С++", форматирование, статический и динамический анализ, это всё должно проводиться в любом языке. Только не везде есть такие инструменты.

посмотрите на Rust

посмотрел, он убог и решает несуществующие проблемы (более того - у него не получается их решить)

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

Спасибо за статью, супер! Тоже использую с++17, очень нравится. Вопрос по поводу исключений: смотрели как влияет на производительность/размер кода с и без него? Я как-то отбросил исключения в своё время, но не разбирался в этом вопросе.

П. С. Пошёл внедрять)

Вопрос по поводу исключений: смотрели как влияет на производительность/размер кода с и без него?

Смотрели как исключения влияют на размер бинарного файла с помощью bloaty, размер секции .gcc_except_table составлял около 3%:

Clang-Tidy продолжает развиваться, в новых версиях появляются новые полезные проверки, такие как misc-include-cleaner для проверки неиспользуемых или отсутствующих #include директив.

К слову, в хобби-проекте fheroes2, в который я периодически контрибучу, уже довольно давно используется IWYU для отслеживания необходимости включения тех или иных заголовочных файлов. Ну и разумеется практически все остальное перечисленное - принудительный clang-format, clang-tidy (я для него даже поконтрибутил в один сторонний action, превращающий fixes.yml в PR review, фактически полностью его переписав), SonarQube, контроль дат в копирайт-заголовках, и т.д., и т.п.

Зарегистрируйтесь на Хабре, чтобы оставить комментарий