自定义调试信息的输出

  调试信息的输出方法有很多种,  例如直接用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
#include
//开启下面的宏表示程序运行在调试版本, 否则为发行版本, 这里假设只有调试版本才输出调试信息
#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__ );\
            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;
}
 
 
  上面也说了,只有支持C99规范的gcc编译器才有__VA_ARGS__这个宏,如果不是gcc编译器,或者所用的gcc编译器版本不支持__VA_ARGS__宏怎么办? 可参考下面的代码片段,我们换一种做法,可先将可变参数转换成字符串后,再进行输出即可:
void printDebugMsg( const char* format, ...)
{
    char buffer[DEBUG_BUFFER_MAX_LENGTH + 1]={0};
    va_list arg;
    va_start (arg, format);
    vsnprintf(buffer, DEBUG_BUFFER_MAX_LENGTH, format, arg);
    va_end (arg);

printf( "%s", buffer );
}

__VA_ARGS__用法(转)的更多相关文章

  1. __VA_ARGS__用法

    转载 自定义调试信息的输出 调试信息的输出方法有很多种,  例如直接用printf,  或者出错时使用perror, fprintf等将信息直接打印到终端上, 在Qt上面一般使用qDebug,而守护进 ...

  2. 关于调试日志Log

    __VA_ARGS__  是一个可变参数的宏,这个可变参数的宏是新的C99规范中新增的,目前似乎只有gcc支持(VC6.0的编译器不支持).宏前面加上##的作用在于,当可变参数的个数为0时,这里的## ...

  3. VC 宏与预处理使用方法总结

    目录(?) C/C++ 预定义宏^ C/C++ 预定义宏用途:诊断与调试输出^ CRT 和 C 标准库中的宏^ NULL 空指针^ limits.h 整数类型常量^ float.h 浮点类型常量^ m ...

  4. C陷阱与缺陷读书笔记

    2.1理解函数声明 这一章仔细分析了(*(void(*)())0)();这条语句的含义,并且提到了typedef的一种函数指针类型定义的用法. 我们经常用到的typedef用法是用于指定结构体的类型, ...

  5. Linux驱动开发——pr_fmt的用法

    作者:彭东林 邮箱:pengdonglin137@163.com 在阅读kernel代码的时候,总是看到有很多驱动都在第一行定义pr_fmt,闲来没事,分析了一下, 发现,确实挺方便的.下面记录分享一 ...

  6. ios开发 <AppName>-Prefix.pch文件的用法详解

    我们知道,每新建立一个工程,比如说HelloWord,在分类SupportingFiles里都会有一个以工程名开头-Prefix.pch结尾的文件,如HelloWord-Prefix.pch.对于这个 ...

  7. Objective-C中#define的常见用法

    参考博客 http://blog.csdn.net/kindazrael/article/details/8108868 在C语言中,预处理代码是非常强大的工具,能让代码变得可读性和可维护性更强.预处 ...

  8. C 语言 define 变参__VA_ARGS__使用

    在C语言的标准库中,printf.scanf.sscanf.sprintf.sscanf这些标准库的输入输出函数,参数都是可变的.在调试程序时,我们可能希望定义一个参数可变的输出函数来记录日志,那么用 ...

  9. C语言宏高级用法

    1.前言  今天看代码时候,遇到一些宏,之前没有见过,感觉挺新鲜.如是上网google一下,顺便总结一下,方便以后学习和运用.C语言程序中广泛的使用宏定义,采用关键字define进行定义,宏只是一种简 ...

随机推荐

  1. leetcode 144. Binary Tree Preorder Traversal ----- java

    Given a binary tree, return the preorder traversal of its nodes' values. For example:Given binary tr ...

  2. 三国游戏 2010年NOIP全国联赛普及组

    题目描述 Description 小涵很喜欢电脑游戏,这些天他正在玩一个叫做<三国>的游戏. 在游戏中,小涵和计算机各执一方,组建各自的军队进行对战.游戏中共有N 位武将(N 为偶数且不小 ...

  3. hdu1003 dp(最大子段和)

    题意:给出一列数,求其中的最大子段和以及该子段的开头和结尾位置. 因为刚学过DP没几天,所以还会这题,我开了一个 dp[100002][2],其中 dp[i][0] 记录以 i 为结尾的最大子段的和, ...

  4. exceptions-in-java

    http://www.javaworld.com/article/2076721/core-java/designing-with-exceptions.html http://www.javawor ...

  5. tomcat集群待整理

    对于tomcat的集群有两种方式,这个主要是针对 session而言的.一种就是sticky模式,即黏性会话模式:另外一种就是session复制模式了.所谓sticky模式就是说同一个用户的访问 请求 ...

  6. redis模块

    http://www.cnblogs.com/melonjiang/p/5342505.html http://www.django-china.cn/topic/1054/ 1.连接方式 redis ...

  7. 反向代理代理百度、google

    <VirtualHost _default_:443> # ServerAdmin mail@localhost # DocumentRoot "/var/www/html&qu ...

  8. Intellij IDEA 的使用(创建项目、导入项目、同时部署多个项目、JRebel)等常见eclipse、myeclipse换idea必看

    第一篇:Intellij IDEA 的使用 1.黑色主题 中文乱码修改 2.WEB项目的部署 以及自动编译 3.多项目的同时部署 4.相关插件提高工作效率 1.JRebel插件 实现热部署 2.Tas ...

  9. Redis 利用锁机制来防止缓存过期产生的惊群现象-转载自 http://my.oschina.net/u/1156660/blog/360552

    首先,所谓的缓存过期引起的“惊群”现象是指,在大并发情况下,我们通常会用缓存来给数据库分压,但是会有这么一种情况发生,那就是在一定时间 内生成大量的缓存,然后当缓存到期之后又有大量的缓存失效,导致后端 ...

  10. openstack(liberty): devstack中的iniset/iniget函数分析

    这个ini开头的函数在devstack的启动配置中用的非常多,他主要负责.ini文件的配置,这个过程包括对相关ini文件的添加,注释,删除,获取信息,多行信息获取等. 这里主要说的iniset和ini ...