Я наткнулся на информативную статью: 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.
Как вы думаете?