随笔-54  评论-11  文章-0 

宏定义中的#,##操作符和... and _ _VA_ARGS_ _与自定义调试信息的输出

自定义调试信息的输出
  调试信息的输出方法有很多种, 例如直接用printf, 或者出错时使用perror, fprintf等将信息直接打印到终端上, 在Qt上面一般使用qDebug,而守护进程则一般是使用syslog将调试信息输出到日志文件中等等...
  使用标准的方法打印调试信息有时候不是很方便, 例如Qt编程, 在调试已有的代码时, 我想在打印调试信息的地方, 把代码位置也打印出来以方便定位错误, 或者需要在调试信息前面加一个前辍, 好方便在调试信息太多的时候可以用grep过滤一下, 仅显示本模块的调试信息, 这时就需要一个一个地修改已有的qDebug, 使其成为以下形式:
  qDebug( "[模块名称] 调试信息 File:%s, Line:%d", __FILE__, __LINE__ );
  这样的修改比较烦人, 而且一不小心会遗漏某个没改的...
  为了能方便地管理调试信息的输出,一个比较简单的方法就是自已定义一个打印调试信息的宏, 然后替换原来的,废话就不多说了,直接给出一个现成的,下面是一个例子, 我用WiFi表示当前代码的模块名称,我要求在模块中的所有调试信息前面均带有[WiFi]前辍,这样我就能方便地只需使用命令行 | grep "\[WiFi\]"来过滤掉来自其它模块的调试信息了:
#define qWiFiDebug(format, ...) qDebug("[WiFi] "format" File:%s, Line:%d, Function:%s", ##__VA_ARGS__, __FILE__, __LINE__ , __FUNCTION__);
  上面的宏是使用qDebug输出调试信息,在非Qt的程序中也可以改为printf,守护进程则可以改为syslog等等... 其中,决窍其实就是这几个宏 ##__VA_ARGS__, __FILE__, __LINE__ 和__FUNCTION__,下面介绍一下这几个宏:
  1) __VA_ARGS__ 是一个可变参数的宏,很少人知道这个宏,这个可变参数的宏是新的C99规范中新增的,目前似乎只有gcc支持(VC6.0的编译器不支持)。宏前面加上##的作用在于,当可变参数的个数为0时,这里的##起到把前面多余的","去掉的作用,否则会编译出错, 你可以试试。
  2) __FILE__ 宏在预编译时会替换成当前的源文件名
  3) __LINE__宏在预编译时会替换成当前的行号
  4) __FUNCTION__宏在预编译时会替换成当前的函数名称
  有了以上这几个宏,特别是有了__VA_ARGS__ ,调试信息的输出就变得灵活多了。


  有时,我们想把调试信息输出到屏幕上,而有时则又想把它输出到一个文件中,可参考下面的例子:
//debug.c
#include <stdio.h>
#include <string.h>
//开启下面的宏表示程序运行在调试版本, 否则为发行版本, 这里假设只有调试版本才输出调试信息
#define _DEBUG
#ifdef _DEBUG
    //开启下面的宏就把调试信息输出到文件,注释即输出到终端
    #define DEBUG_TO_FILE
    #ifdef DEBUG_TO_FILE
        //调试信息输出到以下文件
        #define DEBUG_FILE "/tmp/debugmsg"
        //调试信息的缓冲长度
        #define DEBUG_BUFFER_MAX 4096
        //将调试信息输出到文件中
        #define printDebugMsg(moduleName, format, ...) {\
            char buffer[DEBUG_BUFFER_MAX+1]={0};\
            snprintf( buffer, DEBUG_BUFFER_MAX \
                    , "[%s] "format" File:%s, Line:%d\n", moduleName, ##__VA_ARGS__, __FILE__, __LINE__ );\
       &nbsp;     FILE* fd = fopen(DEBUG_FILE, "a");\
            if ( fd != NULL ) {\
                fwrite( buffer, strlen(buffer), 1, fd );\
                fflush( fd );\
                fclose( fd );\
            }\
        }
    #else
        //将调试信息输出到终端
        #define printDebugMsg(moduleName, format, ...) \
                  printf( "[%s] "format" File:%s, Line:%d\n", moduleName, ##__VA_ARGS__, __FILE__, __LINE__ );
    #endif //end for #ifdef DEBUG_TO_FILE
#else
    //发行版本,什么也不做
    #define printDebugMsg(moduleName, format, ...)
#endif //end for #ifdef _DEBUG
int main(int argc, char** argv)
{
    int data = 999;
    printDebugMsg( "TestProgram", "data = %d", data );
    return 0;
}

=========================================================================

1.#
假如希望在字符串中包含宏参数,ANSI C允许这样作,在类函数宏的替换部分,#符号用作一个预处理运算符,它可以把语言符号转化程字符串。例如,如果x是一个宏参量,那么#x可以把参数名转化成相应的字符串。该过程称为字符串化(stringizing).
#incldue <stdio.h>
#define PSQR(x) printf("the square of" #x "is %d.\n",(x)*(x))
int main(void)
{
    int y =4;
    PSQR(y);
    PSQR(2+4);
    return 0;
}
输出结果:
the square of y is 16.
the square of 2+4 is 36.
第一次调用宏时使用“y”代替#x;第二次调用时用“2+4"代#x。
2.##
##运算符可以使用类函数宏的替换部分。另外,##还可以用于类对象宏的替换部分。这个运算符把两个语言符号组合成单个语言符号。例如:
#define XNAME(n) x##n
这样宏调用:
XNAME(4)
展开后:
x4
程序:
#include <stdio.h>
#define XNAME(n) x##n
#define PXN(n) printf("x"#n" = %d\n",x##n)
int main(void)
{
    int XNAME(1)=12;//int x1=12;
    PXN(1);//printf("x1 = %d\n", x1);
    return 0;
}
3.可变宏 ...和_ _VA_ARGS_ _
实现思想就是宏定义中参数列表的最后一个参数为省略号(也就是三个点)。这样预定义宏_ _VA_ARGS_ _就可以被用在替换部分中,以表示省略号代表什么。比如:
#define PR(...) printf(_ _VA_ARGS_ _)
PR("hello");-->printf("hello");
PR("weight = %d, shipping = $.2f",wt,sp);
    -->printf("weight = %d, shipping = $.2f",wt,sp);
省略号只能代替最后面的宏参数。
#define W(x,...,y)错误!

 

==============================================================

 

1.Preprocessor Glue: The ## Operator

预处理连接符:##操作符


Like the # operator, the ## operator can be used in the replacement section of a function-like macro.Additionally, it can be used in the replacement section of an object-like macro. The ## operator combines two tokens into a single token.


##将两个符号连接成一个。


For example, you could do this:

#define XNAME(n) x ## n

Then the macro

XNAME(4)

would expand to the following:

x4

Listing 1 uses this and another macro using ## to do a bit of token gluing.

 


// glue.c -- use the ## operator

#include <stdio.h>

#define XNAME(n) x ## n

#define PRINT_XN(n) printf("x" #n " = %d\n", x ## n);

int main(void)

{

int XNAME(1) = 14; // becomes int x1 = 14;

int XNAME(2) = 20; // becomes int x2 = 20;

PRINT_XN(1);        // becomes printf("x1 = %d\n", x1);

PRINT_XN(2);        // becomes printf("x2 = %d\n", x2);

    return 0;

}

Here's the output:

x1 = 14

x2 = 20

Note how the PRINT_XN() macro uses the # operator to combine strings and the ## operator to combine tokens into a new identifier.

 


2.Variadic Macros: ... and _ _VA_ARGS_ _

Some functions, such as printf(), accept a variable number of arguments. The stdvar.h header file,provides tools for creating user-defined functions with a variable number of arguments. And C99 does the same thing for macros.Although not used in the standard, the word variadic has come into currency to label this facility. (However, the process that has added stringizing and variadic to the C vocabulary has not yet led to labeling functions or macros with a fixed number of arguments as fixadic functions and normadic macros.)

The idea is that the final argument in an argument list for a macro definition can be ellipses (that is, three periods)(省略号). If so, the predefined macro _ _VA_ARGS_ _ can be used in the substitution part to indicate what will be substituted for the ellipses. For example, consider this definition:

#define PR(...) printf(_ _VA_ARGS_ _)

Suppose you later invoke the macro like this:

PR("Howdy");

PR("weight = %d, shipping = $%.2f\n", wt, sp);

For the first invocation, _ _VA_ARGS_ _ expands to one argument:

"Howdy"

For the second invocation, it expands to three arguments:

"weight = %d, shipping = $%.2f\n", wt, sp

Thus, the resulting code is this:

printf("Howdy");

printf("weight = %d, shipping = $%.2f\n", wt, sp);

Listing 2 shows a slightly more ambitious example that uses string concatenation and the # operator:

 


// variadic.c -- variadic macros

#include <stdio.h>

#include <math.h>


#define PR(X, ...) printf("Message" #X ": " _ _VA_ARGS_ _)

int main(void)

{

    double x = 48;

    double y;

    y = sqrt(x);

    PR(1, "x = %g\n", x);

    PR(2, "x = %.2f, y = %.4f\n", x, y);

    return 0;

}

In the first macro call, X has the value 1, so #X becomes "1". That makes the expansion look like this:

(#为参数加双引号。)


print("Message " "1" ": " "x = %g\n", x);

Then the four strings are concatenated, reducing the call to this:

print("Message 1: x = %g\n", x);

Here's the output:

Message 1: x = 48

Message 2: x = 48.00, y = 6.9282

Don't forget, the ellipses have to be the last macro argument:

#define WRONG(X, ..., Y) #X #_ _VA_ARGS_ _ #y(这个是错误的例子。)

posted on 2011-01-24 22:39 cnmaizi 阅读(...) 评论(...) 编辑 收藏