Вопрос

На C и C++ assert является очень тяжеловесная процедура, записывающая ошибку в stdout и завершение программы.В нашем приложении мы внедрили гораздо более надежную замену assert и предоставили ему собственный макрос.Были приложены все усилия для замены assert однако в нашем макросе все еще есть много способов assert может быть повторно введен (например, из внутренних сторонних библиотек, наивная инъекция и т.д.)

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

Это было полезно?

Решение

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

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

Другие советы

Это может быть удобно для улучшения встроенного средства утверждения (для обеспечения трассировки стека, дампов ядра, кто знает).В этом случае, если у вас возникли проблемы с тем, чтобы ваши разработчики следовали любым вашим стандартам (например, "вместо assert() использование SUPER_ASSERT()" или что-то еще), вы можете просто поставить свой собственный assert.h заголовок в пути включения перед каталогом заголовков среды выполнения компилятора.

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

Это будет зависеть (по крайней мере частично) от того, что вы меняете.Предполагая, что вы не возражаете, чтобы он распечатал свое обычное сообщение, и в основном хотите избавиться от его вызова abort(), ты мог бы подумать о том , чтобы уйти assert() в одиночку, и вместо этого определяете свою собственную версию abort().

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

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

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

Тем не менее, я не вижу ни одного решения, которое всегда работало бы.

  • Если вам повезет, сторонние библиотеки используют макросы ASSERT, которые вы можете переопределить самостоятельно, если файл, определяющий этот макрос, имеет какой-то #pragma once или #ifndef __HEADERFILE_H__ #define __HEADERFILE_H__ положение против многократного включения.Включите файл заголовка отдельно, переопределите ASSERT, и все в порядке.

  • Если они напрямую включают assert.h или cassert, вы можете только исправить код, я думаю.Внесите минимальные изменения в код, сохраните изменения в виде файлов исправлений и при обновлении библиотеки надейтесь, что исправления все еще работают.Добавьте исправления в систему управления версиями.

Если это не сработает, переосмыслите вопрос, действительно ли вам нужны внутренние утверждения в сторонних библиотеках.Отправляйте только сборки выпуска, это избавляет от утверждений и добавляет ваши утверждения для проверки правильности внутри вашего кода.Проверьте правильность возвращаемых значений.Если такое УТВЕРЖДЕНИЕ срабатывает, вы все равно можете погрузиться в сторонний код, чтобы увидеть, что вызвало проблему.

Я думаю, что этот вопрос справедлив.

Мое собственное утверждение расширяется до asm ("int3") при срабатывании, что эквивалентно точке останова.Я также обнаружил, что это гораздо полезнее для отладки, чем простое завершение.

Я просто назвал это "ASSERT()" вместо обычного "assert()" и вообще избегал использования assert().

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

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

Assert(3 == x);

превратится в

((void)0);

и точке с запятой есть куда пойти.

Кстати, однажды я работал над приложением с графическим интерфейсом, где assert представлял собой специальное всплывающее диалоговое окно с модальным графическим интерфейсом.У вас было три варианта:Игнорируй, игнорируй навсегда или сломайся.Игнорировать означало бы игнорировать утверждение и продолжать работать.Игнорировать навсегда установило бы флаг, и пока вы не перезапустите программу в отладчике, это утверждение больше не будет срабатывать.Break позволил бы assert проникнуть в отладчик.

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

assert() обычно это #define'd быть ((недействительный)0) для кода выпуска (#define NDEBUG), так что есть никаких накладных расходов вообще.

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

Похоже, вы упускаете из виду тот факт, что сторонний код, скорее всего, написан в предположении о "стандарте". assert поведение.То есть.код ожидает, что программа завершится при сбойном утверждении.Код, который следует за утверждением, обычно не может и не будет работать корректно, если утвержденное условие нарушено.В 99 случаях из 100 это вообще не сработает.В 99 случаях из 100 это просто приведет к сбою, т. е.программа завершится в любом случае.

Верить , что, переопределяя assert поведение в стороннем коде, которое вы каким-то образом заставите программу жить дольше, в лучшем случае наивно.

Если исходный код находится под вашим контролем:

#define NDEBUG
// Before
#include <assert.h>
// Or other header that includes assert.h

Или используя предварительно скомпилированный заголовок или параметры компиляции для определения NDEBUG.

Для двоичных файлов третьей части используйте их релизную версию.

Найти assert в заголовках библиотеки (при условии, что это реальные файлы в вашей файловой системе) и замените их недопустимым значением

// #define assert(condition) ... /* old definition */
#define assert(condition) ((condition) & "PLEASE DO NOT USE ASSERT" = 42)
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top