Comments 33
я знаю нескольких людей из автомобильного софта, которые и не переставали жить в таком С++, и там это, думаю, оправдано, ибо ценой ошибки вполне может стать чья‑то головушка.
В автопроме это по сути делается не ради безопасности, а чтобы формально снять ответственность с руководителей проекта.
Second, we observed a negative correlation between MISRA rule violations and observed faults. In addition, 29 out of 72 rules had a zero true positive rate. Taken together with Adams’ observation that all modifications have a non-zero probability of introducing a fault [1], this makes it possible that adherence to the MISRA standard as a whole would have made the software less reliable. This observation is consistent with Hatton’s earlier assessment of the MISRA C 2004 standard [10]
https://repository.tudelft.nl/record/uuid:646de5ba-eee8-4ec8-8bbc-2c188e1847ea
Я сам работал по MISRA C++, и меня не покидало ощущение, что все эти пляски с бубном только увеличивают риск допустить ошибку (как минимум из-за выросшего объёма кода).
Я сам работал по MISRA C++, и меня не покидало ощущение, что все эти пляски с бубном только увеличивают риск допустить ошибку (как минимум из-за выросшего объёма кода).
Так многие требования там вполне адекватные для повышения надежности и детерминированного поведения, что требует некоторого количества "лишнего" кода. Если бы надежный код было проще писать, то и "ширпотребное" ПО выглядело иначе.
Ну так и оставили бы адекватные требования. Зачем, например, требовать городить лесенку из if(...) { if(...) { if(...) { вместо раннего return? Это же только вредит читаемости кода, и, как следствие, увеличивает риск человеческой ошибки.
Для некоторых разработчиков лесенка из if-ов + единственный return из функции делает код более читаемым, чем множество if-ов с ранними return-ами. Так что это субъективно.
В реальном коде не обязательно жестко соблюдать 100% всех рекомендаций, да и таблица требований обязательно/желательно/... обычно описывается под конкретное применение, т.к. особенности области использования всегда есть и это нормально.
Единственную точку выхода можно организовать различными способами (не только "лесенку из
if"), если это является обязательным.Общий смысл в повышении детерминированности кода. В этом могли бы быть полезны "чистые функции" (в понимании встроенных систем), только на C/C++ их нормально не сделаешь, нужен внешний кодогенератор (что, обычно, не приветствуется) или нормальное метапрограммирование (а не обрубок на шаблонах).
не обязательно жестко соблюдать 100% всех рекомендаций
Конкретно это правило помечено как "required", и его нарушение означает, что код не соответствует стандарту: "C++ code that is claimed to conform to MISRA C++ shall comply with every “Required” rule". https://github.com/zaznov/MISRA/blob/main/MISRA%20C%2B%2B%202008.pdf
Единственную точку выхода можно организовать различными способами
Как? Наименее корявая известная мне альтернатива — вынести содержимое внутреннего if в отдельную функцию — нарушает локализацию логики (нужно больше бегать глазами, чтобы прочитать код) и на ровном месте создаёт лишнее зацепление (вторая функция существует только ради вызова из первой).
Общий смысл в повышении детерминированности кода.
Что вы имеете в виду под детерминированностью? В чём практическое преимущество?
Конкретно это правило помечено как "required"
В этом и дело, в MISRA C++ отсутствует категория Mandatory, большинство правил Required. Вы можете нарушить правило при достаточных основаниях. Если в конкретном проекте Required = Mandatory, то придется соблюдать.
Как? Наименее корявая известная мне альтернатива — вынести содержимое внутреннего if в отдельную функцию — нарушает локализацию логики (нужно больше бегать глазами, чтобы прочитать код) и на ровном месте создаёт лишнее зацепление (вторая функция существует только ради вызова из первой).
Это один из вариантов, скорее характерный для С и, как отмечено, не самый удачный. В С++ есть более развитая система типов, концепты, пред/пост-условия, перегрузка, сокрытие реализации, т.о. можно написать код, чтобы функции вообще не вызывались с неправильными параметрами, это уберет часть "if-return". Естественно это не покрывает 100% случаев, но мы же не из тех кому "всё или ничего".
Что вы имеете в виду под детерминированностью? В чём практическое преимущество?
Имеется в виду предопределенность, просто этот термин используется реже, чем детерминированность. Определение: Детерминированность (от лат. determinans — «определяющий») — это свойство процесса или системы, при котором результат однозначно определяется начальными условиями, входными данными и правилами функционирования.
На практике это дает возможность делать большее количество оптимизаций, а главное уменьшает количество "что за ..." при чтении кода, если нужно могу привести примеры из библиотеки на шаблонах С++ для микроконтроллеров, там как раз улучшение читаемости за счет декларативности и однозначности, оптимальный машинный код, почти как рукописный ассемблер, операционная система времени компиляции.
Вы можете нарушить правило при достаточных основаниях.
Я же вам процитировал кусок, в котором говорится, что хотя бы одно нарушение - это уже doesn't conform to MISRA.
более развитая система типов, концепты, пред/пост-условия, перегрузка, сокрытие реализации
Всё это как раз увеличивает количество "что за ...". В критичном к надёжности коде по умолчанию нужно использовать тупые как черенок лопаты решения, знакомые любому стажёру или индусу (на поддержку к которым с большой вероятностью перейдёт ваш код).
"чистые функции" (в понимании встроенных систем), только на C/C++ их нормально не сделаешь
[[reproducible]], [[unsequenced]]
Ну если под if-ами мы проверяем результат захвата ресурсов (не обязательно памяти, мы можем например переводить какой-то конечный автомат из состояния x в y, а потом должны вернуть взад или перевести в z), то либо так, либо goto exit3.
Жду, когда адепты единственного ретурна начнут требовать писать весь код в одну строчку. Ради повышения детерминированности :)
В автопроме это по сути делается не ради безопасности, а чтобы формально снять ответственность с руководителей проекта.
Бумажная безопасность увы всегда важнее реальной. Менеджеру проще прикрыть задницу отчетом анализатора, чем разбирать реальные корки после аварии..
В Остине построили свой маленький и злой ++C внутри большого и пушистого С++, и этот внутренний язык запрещал худшие по времени пути программы, но требовал шумных API, собственных контейнеров и постоянного обучения новых разработчиков, так что время кадра покупалось временем жизни программиста.
Неужели и вправду нельзя создать отдельный язык (вроде ++C), компилятор которого уже "из коробки" имеет все необходимые ограничения?
У меня такой же вопрос был по Delphi - чтобы часть инструкций (типа with и goto) были заблокирована на уровне компилятора (сторонние либы, чтобы не трогать, примем что будут pre-compiled). Но почему-то такого так и не изобрели.
Потому что, как ты сам и сказал, есть проблема со сторонними библиотеками, а себя (свой код) ограничивать ты можешь и сам. В итоге получается инструмент, который позволяет тебе ограничить только себя, при этом, случайно, ты сам, и так "запретные" конструкции вряд ли напишешь.
Я сам могу и опечатки не делать, и помнить все имена всех методов в своём коде, но всё же наличие спеллчекера и автодополнятора - удобная штука. То же касается и работы в команде - проще было бы запретить какие-то конуструкции на этапе написания и компиляции (например, как это сделано с флагами Syntax options (assignable typed constants, complete boolean evaluation, typed @ operator, etc.), чем на этапе коммит-хука или на код-ревью.
По большей части rust — ровно то, что вы описали. Он из коробки поддерживает нужную модель ошибок, закрывает почти все баги памяти (в safe коде) и много чего еще хорошего дает, например модель синхронизации через send/sync трейты.
Правда их требования к аллокации так легко не решились бы, нужно было бы так же кастомные контейнеры писать.
Да, да, rust, еще бы не заставлял писать то, что компилятор может написать сам.
Например?
Достаточно посмотреть на избыточность синтаксиса многопоточности, времен жизни, заимствований, обработки ошибок. А главное, при изменении кода, нужно будет исправлять вручную.
Так "избыточность" синтаксиса возникает ровно по причине того что компилятор не может угадать, что требуется дописать, о чем успешно рапортует. В частности расстановка времен жизни и прочих ограничений типов. Но да, рефакторинг боль.
Так "избыточность" синтаксиса возникает ровно по причине того что компилятор не может угадать, что требуется дописать, о чем успешно рапортует.
Вот и я об этом же, компилятор rust не может. Есть другие языки, компилятор которых это делает самостоятельно. Может и в rust, когда-нибудь, появится.
Это какие другие компиляторы осиливают самостоятельно выводить гарантированное время жизни и жить без GC при этом? ЕМНИП до раста не существовало в принципе ни одного языка, который бы избавлялся от многопоточных дедлоков на этапе компиляции без ручного добавления килобайтов кода синхронизациия на поток. GHC тащит свой рантайм и даже близко несопоставим с остальными компилируемыми языками, да и чтобы приблизится к уровню раста там столько бойлерплейта надо дописывать. vlang не так давно что-то там добавлял про заимствования и кажется у D было что-то там про управление временем жизни и заимствований, но опять же - до уровня Rust им определённо далеко и я сомневаюсь, что они достаточно самостоятельны в этом плане. В плюсах там на диких костылях что-то собирали, что потом пару часов компилируется, а больше кандидатов у меня чё-то и нет. Так про какие иные компиляторы речь?
Как-то так Jonathan Blow и написал себе Jai. Ну и odin как тёзка по синтаксису туда же.
Написание своего компилятора - это намного более сложная задача, чем разработка игры. Мало кто будет так заморачиваться.
Два десятка аллокаций на фрейм впечатляют. В типичном энтерпрайзе только на инициализацию логгера обычно улетает пара сотен вызовов)
Эти пчелы делают неправильный с++