在我们的C代码库中,我们有断言宏,例如:
ASSERT3(x, ==, y) // x=0, y=1 results in "main.c:45: 'x == y' (0 == 1) is untrue"
ASSERT(x == y) // x=0, y=1 results in "main.c:45: 'x == y' is untrue"
显然,ASSERT3
表单在您尝试在失败后调试某些内容时会更有帮助,因为它会告诉您变量的值是什么。
但是,每当您需要执行更复杂的断言(尤其是那些包含||
的断言,因为您可以将具有&&
的断言分成多个断言时),例如ASSERT(x == y || y != 0 || x == 2)
,您可以不再利用令人敬畏的ASSERT3
格式。很明显,我可以构建一个像ASSERT11(x, ==, y, ||, y, !=, 0, ||, x, ==, 2)
这样的宏,但理想情况下我想创建一个宏,它可以处理可变数量的参数并找出要在其上打印的内容拥有。要做到这一点,我认为我需要宏来过滤出只是逻辑运算符的参数,这样它就不会尝试打印它们的值 - 有没有办法做到这一点?
答案 0 :(得分:0)
我已经有了这样的感觉和想法,但事实证明,断言是为了永远不会发生的事情,所以为它们提供奇特的输出并不是真的有意义。您正在寻找的是在不满足这些条件时打印的调试消息,因此您应该以这样的方式编写代码:
if (!condition) {
DEBUG(whatever); /* or however your ASSERT() macro prints */
PANIC(); /* or however your ASSERT() macro aborts */
}
在这种情况下,我实际上认为代码更容易编写,更容易阅读,并且比您最终会遇到的复杂ASSERT语句更加可靠。更不用说一旦断言变得复杂,无论如何,肯定还有其他重要的相关信息实际上在断言声明中。
答案 1 :(得分:0)
这可能不值得你付出努力,但我会给你一般大纲,你可以决定。
理论上可以使用可变参数宏
#define UBER_ASSERT(...) uberAssert( __FILE__, __LINE__, __VA_ARGS__ )
和一个可变函数
void uberAssert( char *filename, int linenumber, char *format, ... )
典型用法如下所示
UBER_ASSERT( "%1d == %2d || %2d != 0 || %1d == 2", x, y );
并且最小的例子看起来像这样
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#define UBER_ASSERT(...) uberAssert( __FILE__, __LINE__, __VA_ARGS__ )
int evaluateExpression( char *format, va_list args )
{
int x = va_arg( args, int );
int y = va_arg( args, int );
printf( "evaluating \"%s\" with x=%d y=%d\n", format, x, y );
return 0;
}
void displayExpression( char *format, va_list args )
{
int x = va_arg( args, int );
int y = va_arg( args, int );
printf( "%s with x=%d y=%d\n", format, x, y );
}
void uberAssert( char *filename, int linenumber, char *format, ... )
{
va_list args, args2;
va_start( args, format );
va_copy( args2, args );
if ( !evaluateExpression( format, args ) )
{
printf( "%s:%d: ", filename, linenumber );
displayExpression( format, args2 );
abort();
}
va_end( args );
va_end( args2 );
}
int main( void )
{
int x = 3;
int y = 5;
UBER_ASSERT( "%1d==%2d || %2d!=0 || %1d==2", x, y );
}
困难的部分是编写表达式赋值器和表达式显示函数,并将其作为练习留给读者。