Я наткнулся на информативную статью: http://cnicholson.net/2009/02/stupid-c-tricks-adventures-in-assert/, который указал на большое количество проблем, существующих в моем текущем наборе отладочных макросов.
Полный код финальной версии макроса приведен ближе к концу статьи, если вы перейдете по ссылке.
Общая форма в представленном виде выглядит так (кто-нибудь, пожалуйста, поправьте меня, если я ошибаюсь в переносе):
#ifdef DEBUG
#define ASSERT(cond) \
do \
{ \
if (!(cond)) \
{ \
ReportFailure(#cond, __FILE__, __LINE__, 0); \
HALT(); \
} \
} while(0)
#else
#define ASSERT(cond) \
do { (void)sizeof(cond); } while(0)
Думая о том, чтобы изменить свой код с учетом того, что я узнал, я заметил пару интересных вариантов, опубликованных в комментариях к этой статье:
Во-первых, вы не можете использовать этот макрос с тернарным оператором (то есть cond?ASSERT(x):func()
), и было предложено заменить if()
тернарным оператором и некоторыми круглыми скобками, а также оператором запятой. Позже другой комментатор сообщил следующее:
#ifdef DEBUG
#define ASSERT(x) ((void)(!(x) && assert_handler(#x, __FILE__, __LINE__) && (HALT(), 1)))
#else
#define ASSERT(x) ((void)sizeof(x))
#endif
Я подумал, что использование логического и &&
в этом случае особенно разумно, и мне кажется, что эта версия более гибкая, чем версия, использующая if
или даже троичную ?:
. Еще лучше то, что возвращаемое значение assert_handler
можно использовать для определения необходимости остановки программы. Хотя я не уверен, почему это (HALT(), 1)
, а не просто HALT()
.
Есть ли какие-то особые недостатки второй версии, которые я упустил из виду? Это избавляет от do{ } while(0)
, обернутых вокруг макросов, но здесь это кажется ненужным, потому что нам не нужно иметь дело с if
s.
Что вы думаете?
sizeof
не применимо к функциям и элементам битовых полей. Как предлагается здесь stackoverflow.com/questions/4030959/, вместо этого вы можете использовать((void)(true ? 0 : ((x), void(), 0)));
. - person anxieux   schedule 25.08.2018