C调试宏(具有不同的调试“源”)

时间:2010-03-11 23:07:11

标签: c debugging macros

我打算让自己得到一个整洁的C调试宏,不确定我真正想要的是什么(并且在涉及到宏时无能为力)我转向谷歌。一段时间后,我现在认为我知道我想要什么,但不知道它是如何工作的。我没有太多运气获得有关宏和调试技术的正确信息。

过去我一直在使用的是这样的:

#ifdef DEBUG
 #define DBG(x) printf x
#else
 #define DBG(x) /* nothing */
#endif

问题是它可能会变得非常混乱,最终你最终会评论出旧的调试消息,尽管你以后可能会需要它们。

我找到的最好的例子来自高级c课程的一些幻灯片,可以在这里找到: http://www.mpi-inf.mpg.de/departments/rg1/teaching/advancedc-ws08/script/lecture07.pdf (相关部分是幻灯片19-23,但大部分内容包括在下面)

作为演讲幻灯片,他们不幸需要一些解释。但他们提到的东西看起来非常有用:

DBG((MOD_PARSER , "z = %d\n", z));

其中MOD_PARSER是调试模块/类别,其余参数应该给printf。

DBG的实施:

#ifdef PRGDEBUG
 #define DBG(x) dbg_printer x
#else
 #define DBG(x) /* nothing */
#endif
void dbg_printer(int module , const char *fmt, ...);

问题#1是编写dbg_printer函数,我不知道如何将可变数量的参数传递给printf语句。

幻灯片继续讨论如何优雅地添加新模块,我相当确定我根本不理解这一点,但无论如何...

*How to add new modules elegantly
*Add a file debug_modules.def 
ADD_MOD(0, PARSER) 
ADD_MOD(1, SOLVER) 
ADD_MOD(2, PRINTER)

...

*“Generate” an enum with debug modules: debug.h
...
#define ADD_MOD(num, id) MOD_ ## id = 1 << num,
enum _debug_modules_t {
#include "debug_modules.def"
};
#undef ADD_MOD
...

...

*Preprocessor yields enum _debug_modules_t {
 MOD_PARSER = 1 << 0,
 MOD_SOLVER = 1 << 1,
 MOD_PRINTER = 1 << 2,
};

我不明白你为什么要左移这些枚举元素的值,我缺少一些漂亮的技巧?

除了上面的幻灯片,我还没有看到一个例子或文章/帖子/甚至提到这个,所以这甚至不适合我的目的。这听起来合理吗?实际使用的技术类似吗?

截至目前,问题是如何实现dbg_printer以及调试模块枚举应该如何工作,但看看我可能误解了可能发生变化的一切:(

2 个答案:

答案 0 :(得分:6)

  

我不明白你为什么要左移这些枚举元素的值,我缺少一些漂亮的技巧?

这很可能是通过按位或连接它们来指示多个元素。

我首选的调试日志宏没有模块特异性,但是将文件名和行号添加到输出中并且相对简单:

    #ifdef DEBUG
        #define DLOG(fmt, args...) printf("%s:%d "fmt,__FILE__,__LINE__,args)
    #else
        #define DLOG(fmt, args...)
    #endif

编辑: 这更像我现在使用的内容,基于以下评论(__PRETTY_FUNCTION__##__VA_ARGS__)和{{3} }(do {} while (0)):

    #ifdef DEBUG
        #define DLOG(fmt, ...) printf("%s:%d "fmt, __PRETTY_FUNCTION__, __LINE__, ##__VA_ARGS__)
    #else
        #define DLOG(fmt, ...) do {} while (0)
    #endif

答案 1 :(得分:2)

要从dbg_printer传递可变数量的参数,请拨打vfprintf而不是fprintf。像这样:

#include <stdarg.h>

extern int dbg_mod_stderr;
extern int dbg_mod_file;
extern FILE *dbg_file;

void dbg_printer(int module , const char *fmt, ...)
{
    if (dbg_mod_stderr & module)
    {
        va_list ap;

        va_start(ap, fmt);
        vfprintf(stderr, fmt, ap);
        va_end(ap);
    }

    if (dbg_file != NULL && (dbg_mod_file & module))
    {
        va_list ap;

        va_start(ap, fmt);
        vfprintf(dbg_file, fmt, ap);
        va_end(ap);
    }
}

(此示例显示如何允许一组调试消息转到stderr,以及另一组设置为调试文件)

常量的左移允许您选择任何模块子集,而不仅仅是单个模块。例如,使用上面的代码,您可以将解析器和解算器的调试打开到stderr,如下所示:

dbg_mod_stderr |= MOD_PARSER;
dbg_mod_stderr |= MOD_SOLVER;

您可以稍后关闭解算器的调试:

dbg_mod_stderr &= ~MOD_SOLVER;

(这是有效的,因为每个MOD_常量在二进制表示中只有一个唯一的位集。)