Использование assert () для производства не является предпочтительным по сравнению с блоками if..else ..? - PullRequest
7 голосов
/ 30 января 2012

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

Ответы [ 9 ]

4 голосов
/ 30 января 2012

Если это ошибка программирования (возможно, вызывающая сторона), используйте assert.

Если это не программированиеошибка, затем используйте if / else и обработайте ситуацию соответствующим образом.

4 голосов
/ 30 января 2012

Прочитав эту статью Я поделюсь своими убеждениями о assert:

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

  2. Многие языки позволяют выдавать пользовательские ошибки при утверждении, C без «Исключений» может привести к ошибкам, которые немного сложнее диагностировать, не обращаясь непосредственно к рассматриваемому источнику.

3 голосов
/ 31 марта 2014

IMO ни один из ответов здесь не говорит самой важной части: Утверждение утверждает предположения программистов.assert(x) говорит что-то вроде "x всегда верен в этой точке, вы можете проверить это, если хотите" .Если пользователь когда-либо видит ошибку подтверждения, это означает, что вы сделали что-то не так.

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

3 голосов
/ 30 января 2012

Утверждения хорошие. Утверждения времени компиляции еще лучше. Примечание:

Если в вашей среде еще нет статического утверждения, вот предложение.

Макрос ASSERT(), приведенный ниже, может быть размещен в любом месте кода, кроме:

  • В заголовочном файле, включенном дважды, без оболочки #ifndef...#endif.
  • В середине определения структуры (или определения enum).
  • В строгом C89 или C90, после заявления. (Но вы можете завернуть его в фигурные скобки!)

Если вы хотите вставить что-то в середину определения структуры, вам нужно использовать длинную, некрасивую трехстрочную конструкцию #if...#error...#endif. И если вы сделаете это, препроцессор будет иметь гораздо более ограниченное представление о том, что такое "константное выражение".

Это уточнение идей из Интернета, в основном из http://www.pixelbeat.org/programming/gcc/static_assert.html. Это определение короче BOOST_STATIC_ASSERT(). И, я полагаю, лучше, чем предложение Линуса об улучшении BUILD_BUG_ON(). А оболочка do{...}while(0), которую вы обычно видите, здесь совершенно неприменима, поскольку ограничивает допустимые местоположения.

Это также проще, чем Google COMPILE_ASSERT / CompileAssert. Трюк «sizeof bitfield» также кажется хорошим, из Linux BUILD_BUG_ON_ZERO(), но не его бесполезный брат BUILD_BUG_ON().

Существует множество предложений по использованию массивов с отрицательным индексом. Но с GCC большинство из них не обнаруживают непостоянную arg (что достаточно легко сделать по ошибке), за исключением extern int foo [выражение], которое также дает «неиспользованный» переменная 'предупреждение. Но typedef int array[expression] кажется также хорошим: см. Ниже.

Определение:

#define CONCAT_TOKENS(a, b)     a ## b
#define EXPAND_THEN_CONCAT(a,b) CONCAT_TOKENS(a, b)
#define ASSERT(e) enum{EXPAND_THEN_CONCAT(ASSERT_line_,__LINE__) = 1/!!(e)}

Не менее хорош, как мне кажется, следующий вариант, но он длиннее на пять символов:

#define ASSERT(e) typedef int EXPAND_THEN_CONCAT(ASSERT_line_,__LINE__)[1-2*!(e)]

Существует также конструкция do{switch(0){case 0:case(e):;}}while(0), которую я не исследовал.

Иногда нужен вариант для обработки случая, когда два разных заголовочных файла случайно имеют два ASSERT () в одной строке, или аналогично для исходного файла и заголовочного файла. Вы можете справиться с этим с помощью __COUNTER__, но это не поддерживается некоторыми компиляторами (и это уродливее). И мы не можем использовать __FILE__, потому что он обычно не расширяется до действительного токена C (например, у него есть точка c или точка h). Версия Mozilla http://mxr.mozilla.org/mozilla-central/source/mfbt/Assertions.h гласит, что такие конфликты "должны быть редкими", но они будут сильно раздражать ваших товарищей по команде, когда это произойдет. Это также можно использовать для обработки нескольких ASSERTS в многострочном макросе, где __LINE__ не меняется.

#define ASSERTM(e,m) enum{EXPAND_THEN_CONCAT(m##_ASSERT_line_,__LINE__)=1/!!(e)}

Следующий вариант ASSERT_zero(), аналогичен BUILD_BUG_ON_ZERO(), с использованием трюка «sizeof bitfield». Это дает либо:

  • ошибка компиляции, когда e имеет значение false или
  • значение ноль.

Так что его можно использовать в местах, где оператор не может, например, в середине выражения.

#ifndef __cplusplus
#define ASSERT_zero(e) (!sizeof(struct{int:!!(e);})) // cf. BUILD_BUG_ON_ZERO(), !C++
#else
#define ASSERT_zero(e) (!sizeof(char[(e) ? 1 : -1])) // careful: g++ has VLAs
#endif
2 голосов
/ 30 января 2012

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

  • assert обычно компилируется из неотладочных сборок.Теперь вы можете выбрать компиляцию всех ваших сборок с ожиданием того, что assert всегда включен, но любой, кто наследует ваш код позже, может этого не понять.Будет еще хуже, если вы напишите код, в котором ваши assert s имеют побочные эффекты (например, assert((fp = fopen(filename, "r")) != NULL)).

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

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

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

#define FAIL_IF(cond, action) do { if (cond) { action; } } while (0)

FAIL_IF((fp = fopen(filename, "r")) == NULL, goto exit);
2 голосов
/ 30 января 2012

Хотя в других ответах содержится некоторая полезная информация, ни один из них (на мой взгляд) не имеет прямого отношения к заданному вами вопросу.

IMO, да, есть довольно существенный недостаток в том, чтобы оставлять assert s в (большинстве) производственном коде: вы не можете контролировать отображаемое сообщение об ошибке, которое часто выглядит довольно страшно.

Вместо чего-то вроде:

Fatal error: Assertion failed! x != NULL in xxx.c, line 107

Я бы лучше дал клиенту что-то вроде:

Please contact customer support and give them code xxx:107
2 голосов
/ 30 января 2012

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

if - else предназначен для вещей, которые иногда могут быть ложными при нормальных обстоятельствах, потому что *Часть 1006 * предназначена для изящной обработки таких ситуаций.

Короче говоря, операторы if предназначены для предотвращения сбоев;assert() предназначен для причин их.

1 голос
/ 30 января 2012

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

#ifdef DEBUG
#  define q_assert(...) ...
#endif

, и я писал что-то вроде

q_assert(some_ptr_type a = get_new_ptr());

Когда я выполнил это вРежим выпуска, я был совершенно озадачен тем, что a никогда не инициализировался.

0 голосов
/ 30 января 2012

Как исключения Java assert дает fail-fast .Это очень хорошая практика, экономящая много времени и усилий на разработку.Но код доставки должен иметь быстрые выпуски как веб-приложение, а не как дистрибутив CD.Кроме того, в Java исключения могут быть перехвачены, зарегистрированы и красиво представлены.

...