#define 宏用于 C 中的调试打印?
尝试创建一个可在定义 DEBUG 时用于打印调试消息的宏,如以下伪代码:
#define DEBUG 1
#define debug_print(args ...) if (DEBUG) fprintf(stderr, args)
这是如何用宏完成的?
Trying to create a macro which can be used for print debug messages when DEBUG is defined, like the following pseudo code:
#define DEBUG 1
#define debug_print(args ...) if (DEBUG) fprintf(stderr, args)
How is this accomplished with a macro?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(14)
如果您使用 C99 或更高版本的编译器
它假定您使用的是 C99(早期版本不支持变量参数列表表示法)。
do { ... } while (0)
习惯用法确保代码的行为类似于语句(函数调用)。无条件使用代码可确保编译器始终检查您的调试代码是否有效 - 但优化器将在 DEBUG 为 0 时删除代码。如果您想使用 #ifdef DEBUG,请更改测试条件:
然后使用DEBUG_TEST 我使用 DEBUG 的地方。
如果您坚持使用字符串文字作为格式字符串(无论如何可能是个好主意),您还可以引入诸如
__FILE__
、__LINE__
和__func__
之类的内容> 到输出中,这可以改进诊断:这依赖于字符串连接来创建比程序员编写的更大的格式字符串。
如果您使用 C89 编译器
如果您坚持使用 C89 并且没有有用的编译器扩展,那么就没有特别干净的方法来处理它。我曾经使用的技术是:
然后,在代码中写:
双括号至关重要 - 这就是为什么你在宏扩展中使用有趣的符号。和以前一样,编译器始终检查代码的语法有效性(这很好),但优化器仅在 DEBUG 宏计算结果为非零时才调用打印函数。
这确实需要一个支持函数(示例中为 dbg_printf())来处理“stderr”等内容。它要求您知道如何编写可变参数函数,但这并不难:
当然,您也可以在 C99 中使用此技术,但
__VA_ARGS__
技术更简洁,因为它使用常规函数表示法,不是双括号黑客。为什么编译器始终看到调试代码至关重要?
[重新整理对另一个答案的评论。]
上述 C99 和 C89 实现背后的一个中心思想是编译器本身始终会看到类似 printf 的调试语句。这对于长期代码(将持续一两年的代码)非常重要。
假设一段代码多年来一直处于休眠(稳定)状态,但现在需要更改。您重新启用调试跟踪 - 但必须调试调试(跟踪)代码是令人沮丧的,因为它引用了在稳定维护期间已重命名或重新键入的变量。如果编译器(后预处理器)始终看到打印语句,则它可以确保任何周围的更改不会使诊断无效。如果编译器没有看到打印语句,它无法保护您免受自己的粗心(或您的同事或合作者的粗心)的影响。请参阅 Kernighan 和 Pike 的“编程实践”,尤其是章节8(另请参阅维基百科上的 TPOP)。
这是“去过那儿,做过那件事”的经历——我基本上使用了其他答案中描述的技术,其中非调试构建多年来(十多年)都没有看到类似 printf 的语句。但我在 TPOP 中遇到了建议(请参阅我之前的评论),然后在几年后启用了一些调试代码,并遇到了更改上下文破坏调试的问题。有几次,始终验证打印使我避免了以后出现的问题。
我仅使用 NDEBUG 来控制断言,并使用单独的宏(通常是 DEBUG)来控制是否将调试跟踪内置到程序中。即使内置了调试跟踪,我也经常不希望无条件地出现调试输出,因此我有机制来控制输出是否出现(调试级别,而不是直接调用
fprintf()
,我调用了一个调试打印函数,该函数仅有条件地打印,因此相同的代码版本可以根据程序选项打印或不打印)。我还有一个用于更大程序的“多子系统”版本的代码,这样我就可以在运行时控制下让程序的不同部分产生不同数量的跟踪。我主张对于所有构建,编译器都应该看到诊断语句;但是,除非启用了调试,否则编译器不会为调试跟踪语句生成任何代码。基本上,这意味着每次编译时编译器都会检查所有代码 - 无论是发布还是调试。这是一件好事!
debug.h - 版本 1.2 (1990-05-01)
debug.h - 版本 3.6 (2008-02-11)
C99 或更高版本的单参数变体
Kyle Brandt 问:
有一种简单的老式技巧:
下面显示的仅限 GCC 的解决方案也提供了对此的支持。
但是,您可以使用直接的 C99 系统来完成此操作:
与第一个版本相比,您失去了需要“fmt”参数的有限检查,这意味着有人可以尝试在不带参数的情况下调用“debug_print()”(但
fprintf()
的参数列表中的尾随逗号将无法编译)。支票丢失是否是一个问题是有争议的。针对单个参数的 GCC 特定技术
某些编译器可能会提供对宏中处理可变长度参数列表的其他方法的扩展。具体来说,正如 Hugo Ideler 的评论中首先指出的那样,GCC 允许您省略通常出现在后面的逗号宏的最后一个“固定”参数。它还允许您使用
##__VA_ARGS__
在宏替换文本中,当且仅当前一个标记是逗号时,它会删除符号前面的逗号:此解决方案保留了要求格式参数同时接受格式后面的可选参数的优点。
Clang 也支持此技术以实现 GCC 兼容性。
C23 和 __VA_OPT__
C++20(及更高版本)和 C23(及更高版本)都添加了 __VA_OPT__ 机制来处理 __VA_ARGS__ 的问题由 GCC 使用
, ## __VA_ARGS__
表示法处理。您只需使用:
如果
__VA_ARGS__
不为空,则将__VA_OPT__
的参数添加到输出中(如果__VA_ARGS__
为空,则不添加任何内容)。如果__STDC_VERSION__ >= 202311L
则应该可用 - 但请注意,在使用-std= 编译时,GCC 14.1.0(仍然)设置
或__STDC_VERSION__ == 202000
c23-std=iso9899:2024
。为什么要使用 do-while 循环?
您希望能够使用宏,使其看起来像函数调用,这意味着它后面会跟一个分号。因此,您必须封装宏体以适应。如果您使用
if
语句而不使用周围的do { ... } while (0)
,您将得到:现在,假设您写:
不幸的是,缩进不' t 反映流程的实际控制,因为预处理器生成与此等效的代码(添加缩进和大括号以强调实际含义):
宏的下一次尝试可能是:
现在生成相同的代码片段:
并且
else
现在是一个语法错误。do { ... } while(0)
循环避免了这两个问题。还有另一种可能有效的编写宏的方法:
这使得程序片段显示为有效。 (void) 强制转换阻止它在需要值的上下文中使用 - 但它可以用作逗号运算符的左操作数,其中
do { ... } while ( 0)
版本不能。如果您认为应该能够将调试代码嵌入到此类表达式中,您可能更喜欢这样做。如果您希望要求调试打印充当完整语句,那么do { ... } while (0)
版本更好。请注意,如果宏主体涉及任何分号(粗略地说),则只能使用do { ... } while(0)
表示法。它总是有效;表达式语句机制可能更难应用。您还可能会收到来自编译器的警告,其中包含您希望避免的表达式形式;这取决于编译器和您使用的标志。_TPOP was previously at http://plan9.bell-labs.com/cm/cs/tpop and http://cm.bell-labs.com/cm/cs/tpop but both are now (2015-08-10) broken._
GitHub 中的代码
如果您好奇,可以在我的 SOQ (Stack
溢出问题)存储库作为文件
debug.c
、debug.h
、mddebug.c
和mddebug.h
中这src/libsoq
子目录。
If you use a C99 or later compiler
It assumes you are using C99 (the variable argument list notation is not supported in earlier versions). The
do { ... } while (0)
idiom ensures that the code acts like a statement (function call). The unconditional use of the code ensures that the compiler always checks that your debug code is valid — but the optimizer will remove the code when DEBUG is 0.If you want to work with #ifdef DEBUG, then change the test condition:
And then use DEBUG_TEST where I used DEBUG.
If you insist on a string literal for the format string (probably a good idea anyway), you can also introduce things like
__FILE__
,__LINE__
and__func__
into the output, which can improve the diagnostics:This relies on string concatenation to create a bigger format string than the programmer writes.
If you use a C89 compiler
If you are stuck with C89 and no useful compiler extension, then there isn't a particularly clean way to handle it. The technique I used to use was:
And then, in the code, write:
The double-parentheses are crucial — and are why you have the funny notation in the macro expansion. As before, the compiler always checks the code for syntactic validity (which is good) but the optimizer only invokes the printing function if the DEBUG macro evaluates to non-zero.
This does require a support function — dbg_printf() in the example — to handle things like 'stderr'. It requires you to know how to write varargs functions, but that isn't hard:
You can also use this technique in C99, of course, but the
__VA_ARGS__
technique is neater because it uses regular function notation, not the double-parentheses hack.Why is it crucial that the compiler always see the debug code?
[Rehashing comments made to another answer.]
One central idea behind both the C99 and C89 implementations above is that the compiler proper always sees the debugging printf-like statements. This is important for long-term code — code that will last a decade or two.
Suppose a piece of code has been mostly dormant (stable) for a number of years, but now needs to be changed. You re-enable debugging trace - but it is frustrating to have to debug the debugging (tracing) code because it refers to variables that have been renamed or retyped, during the years of stable maintenance. If the compiler (post pre-processor) always sees the print statement, it ensures that any surrounding changes have not invalidated the diagnostics. If the compiler does not see the print statement, it cannot protect you against your own carelessness (or the carelessness of your colleagues or collaborators). See 'The Practice of Programming' by Kernighan and Pike, especially Chapter 8 (see also Wikipedia on TPOP).
This is 'been there, done that' experience — I used essentially the technique described in other answers where the non-debug build does not see the printf-like statements for a number of years (more than a decade). But I came across the advice in TPOP (see my previous comment), and then did enable some debugging code after a number of years, and ran into problems of changed context breaking the debugging. Several times, having the printing always validated has saved me from later problems.
I use NDEBUG to control assertions only, and a separate macro (usually DEBUG) to control whether debug tracing is built into the program. Even when the debug tracing is built in, I frequently do not want debug output to appear unconditionally, so I have mechanism to control whether the output appears (debug levels, and instead of calling
fprintf()
directly, I call a debug print function that only conditionally prints so the same build of the code can print or not print based on program options). I also have a 'multiple-subsystem' version of the code for bigger programs, so that I can have different sections of the program producing different amounts of trace - under runtime control.I am advocating that for all builds, the compiler should see the diagnostic statements; however, the compiler won't generate any code for the debugging trace statements unless debug is enabled. Basically, it means that all of your code is checked by the compiler every time you compile - whether for release or debugging. This is a good thing!
debug.h - version 1.2 (1990-05-01)
debug.h - version 3.6 (2008-02-11)
Single argument variant for C99 or later
Kyle Brandt asked:
There's one simple, old-fashioned hack:
The GCC-only solution shown below also provides support for that.
However, you can do it with the straight C99 system by using:
Compared to the first version, you lose the limited checking that requires the 'fmt' argument, which means that someone could try to call 'debug_print()' with no arguments (but the trailing comma in the argument list to
fprintf()
would fail to compile). Whether the loss of checking is a problem at all is debatable.GCC-specific technique for a single argument
Some compilers may offer extensions for other ways of handling variable-length argument lists in macros. Specifically, as first noted in the comments by Hugo Ideler, GCC allows you to omit the comma that would normally appear after the last 'fixed' argument to the macro. It also allows you to use
##__VA_ARGS__
in the macro replacement text, which deletes the comma preceding the notation if, but only if, the previous token is a comma:This solution retains the benefit of requiring the format argument while accepting optional arguments after the format.
This technique is also supported by Clang for GCC compatibility.
C23 and
__VA_OPT__
Both C++20 (and later) and C23 (and later) add the
__VA_OPT__
mechanism to handle the problem with__VA_ARGS__
that is handled by GCC using the, ## __VA_ARGS__
notation.You simply use:
The argument to
__VA_OPT__
is added to the output if__VA_ARGS__
is not empty (and nothing is added if__VA_ARGS__
is empty). This should be available if__STDC_VERSION__ >= 202311L
— but be aware that GCC 14.1.0 (still) sets__STDC_VERSION__ == 202000
when compiling with-std=c23
or-std=iso9899:2024
.Why the do-while loop?
You want to be able to use the macro so it looks like a function call, which means it will be followed by a semi-colon. Therefore, you have to package the macro body to suit. If you use an
if
statement without the surroundingdo { ... } while (0)
, you will have:Now, suppose you write:
Unfortunately, that indentation doesn't reflect the actual control of flow, because the preprocessor produces code equivalent to this (indented and braces added to emphasize the actual meaning):
The next attempt at the macro might be:
And the same code fragment now produces:
And the
else
is now a syntax error. Thedo { ... } while(0)
loop avoids both these problems.There's one other way of writing the macro which might work:
This leaves the program fragment shown as valid. The
(void)
cast prevents it being used in contexts where a value is required — but it could be used as the left operand of a comma operator where thedo { ... } while (0)
version cannot. If you think you should be able to embed debug code into such expressions, you might prefer this. If you prefer to require the debug print to act as a full statement, then thedo { ... } while (0)
version is better. Note that if the body of the macro involved any semi-colons (roughly speaking), then you can only use thedo { ... } while(0)
notation. It always works; the expression statement mechanism can be more difficult to apply. You might also get warnings from the compiler with the expression form that you'd prefer to avoid; it will depend on the compiler and the flags you use._TPOP was previously at http://plan9.bell-labs.com/cm/cs/tpop and http://cm.bell-labs.com/cm/cs/tpop but both are now (2015-08-10) broken._
Code in GitHub
If you're curious, you can look at this code in GitHub in my SOQ (Stack
Overflow Questions) repository as files
debug.c
,debug.h
,mddebug.c
andmddebug.h
in thesrc/libsoq
sub-directory.
我使用这样的东西:
比我只使用 D 作为前缀:
编译器看到调试代码,不存在逗号问题,并且它在任何地方都有效。当 printf 不够时,例如当您必须转储数组或计算一些对程序本身来说多余的诊断值时,它也可以工作。
编辑:好的,当附近有
else
可以被注入的if
拦截时,它可能会产生问题。这是一个覆盖它的版本:I use something like this:
Than I just use D as a prefix:
Compiler sees the debug code, there is no comma problem and it works everywhere. Also it works when
printf
is not enough, say when you must dump an array or calculate some diagnosing value that is redundant to the program itself.EDIT: Ok, it might generate a problem when there is
else
somewhere near that can be intercepted by this injectedif
. This is a version that goes over it:这是我使用的版本:
Here's the version I use:
对于可移植(ISO C90)实现,您可以使用双括号,如下所示;
或(黑客,不推荐)
For a portable (ISO C90) implementation, you could use double parentheses, like this;
or (hackish, wouldn't recommend it)
我会做一些
我认为这样更干净的事情。
I would do something like
I think this is cleaner.
根据 http://gcc.gnu.org/onlinedocs/cpp/Variadic-Macros .html,
__VA_ARGS__
之前应该有一个##
。否则,宏
#define dbg_print(format, ...) printf(format, __VA_ARGS__)
将无法编译以下示例:dbg_print("hello world");
。According to http://gcc.gnu.org/onlinedocs/cpp/Variadic-Macros.html,
there should be a
##
before__VA_ARGS__
.Otherwise, a macro
#define dbg_print(format, ...) printf(format, __VA_ARGS__)
will not compile the following example:dbg_print("hello world");
.这就是我使用的:
即使没有额外的参数,它也有正确处理 printf 的好处。如果 DBG ==0,即使是最愚蠢的编译器也没有什么可咀嚼的,因此不会生成任何代码。
This is what I use:
It has the nice benefit to handle printf properly, even without additional arguments. In case DBG ==0, even the dumbest compiler gets nothing to chew upon, so no code is generated.
所以,当使用gcc时,我喜欢:
因为它可以插入到代码中。
假设您正在尝试调试
然后您可以将其更改为:
您可以分析什么表达式被评估为什么。
它受到双重评估问题的保护,但 gensyms 的缺失确实使其容易发生名称冲突。
然而它确实嵌套:
所以我认为只要避免使用 g2rE3 作为变量名,就可以了。
当然,我发现它(以及字符串的联合版本,以及调试级别的版本等)非常宝贵。
So, when using gcc, I like:
Because it can be inserted into code.
Suppose you're trying to debug
Then you can change it to:
And you can get an analysis of what expression was evaluated to what.
It's protected against the double-evaluation problem, but the absence of gensyms does leave it open to name-collisions.
However it does nest:
So I think that as long as you avoid using g2rE3 as a variable name, you'll be OK.
Certainly I've found it (and allied versions for strings, and versions for debug levels etc) invaluable.
下面我最喜欢的是
var_dump
,当调用它时:var_dump("%d", count);
会产生如下输出:
patch.c:150: main(): count = 0
感谢@“Jonathan Leffler”。全部都是 C89 兼容的:
代码
My favourite of the below is
var_dump
, which when called as:var_dump("%d", count);
produces output like:
patch.c:150:main(): count = 0
Credit to @"Jonathan Leffler". All are C89-happy:
Code
我多年来一直在思考如何做到这一点,终于想出了一个解决方案。但是,我不知道这里已经有其他解决方案。首先,与 Leffler 的回答不同,我没有看到他认为应该始终编译调试打印的论点。我不想在不需要的时候在我的项目中执行大量不需要的代码,以防我需要测试并且它们可能没有得到优化。
不每次都编译可能听起来比实际情况更糟糕。有时您确实会得到无法编译的调试打印,但在完成项目之前编译和测试它们并不难。有了这个系统,如果您使用三个级别的调试,只需将其置于调试消息级别三,修复您的编译错误并在完成代码之前检查任何其他错误。 (当然,调试语句编译并不能保证它们仍然按预期工作。)
我的解决方案还提供了调试详细级别;如果你将其设置为最高级别,它们都会编译。如果您最近使用了高调试详细级别,那么它们当时都能够编译。最终更新应该非常容易。我从来不需要超过三个级别,但乔纳森说他用过九个。这种方法(如莱夫勒的方法)可以扩展到任意数量的级别。我的方法的使用可能比较简单;在代码中使用时只需要两个语句。然而,我也在编写 CLOSE 宏——尽管它没有做任何事情。如果我发送到一个文件,可能会这样。
相对于成本,测试它们以确保它们在交付之前能够编译的额外步骤是,
在现代预取处理器中,分支实际上相对昂贵。如果您的应用程序对时间要求不高,也许没什么大不了的;但如果性能是一个问题,那么,是的,这是一个足够大的问题,我更愿意选择执行速度更快的调试代码(以及可能更快的发布,在极少数情况下,如前所述)。
所以,我想要的是一个调试打印宏,如果不打印则不编译,但如果打印则编译。我还想要调试级别,因此,例如,如果我希望代码的性能关键部分在某些时候不打印,但在其他时候打印,我可以设置调试级别,并启动额外的调试打印。遇到了一种实现调试级别的方法,该级别确定打印是否已编译。我是这样实现的:
DebugLog.h:
DebugLog.cpp:
使用宏
要使用它,只需执行以下操作:
要写入日志文件,只需执行以下操作:
要关闭它,您需要执行以下操作:
尽管目前甚至没有必要,从技术上来说,它什么也没做。不过,我现在仍在使用 CLOSE,以防万一我改变了对它的工作方式的想法,并希望在日志记录语句之间保持文件打开。
然后,当您想要打开调试打印时,只需编辑头文件中的第一个 #define 即可,例如,
要使日志记录语句编译为空,请执行
如果您需要来自频繁执行的代码段(即高级代码)的信息,请执行以下操作: 如果您将 DEBUG 定义为 3,则
日志记录级别为 1、2 和 1。 3 编译。如果将其设置为 2,您将获得日志记录级别 1 和 2。 2. 如果将其设置为 1,则仅获得日志记录级别 1 语句。
至于 do-while 循环,由于它的计算结果要么是单个函数,要么什么也不计算,而不是 if 语句,因此不需要循环。好吧,谴责我使用 C 而不是 C++ IO(而且 Qt 的 QString::arg() 也是一种在 Qt 中格式化变量的更安全的方法 — 它相当流畅,但需要更多代码,而且格式化文档组织得不那么好)可能是这样 - 但我仍然发现了更可取的情况),但您可以将任何代码放入您想要的 .cpp 文件中。它也可能是一个类,但是您需要实例化它并跟上它,或者执行 new() 并存储它。这样,您只需将 #include、init 和可选的 close 语句放入源代码中,就可以开始使用它了。然而,如果你愿意的话,这将是一堂很好的课。
我之前见过很多解决方案,但没有一个比这个更适合我的标准。
不是很重要,但除此之外:
DEBUGLOG_LOG(3, "got here!");
);从而允许您使用 Qt 更安全的 .arg() 格式。它适用于 MSVC,因此也可能适用于 gcc。正如 Leffler 指出的那样,它在#define
中使用##
,这是非标准的,但得到了广泛支持。 (如有必要,您可以重新编码以不使用##
,但您必须使用他提供的 hack。)警告:如果您忘记提供日志记录级别参数,MSVC 将毫无帮助地声明标识符未定义。
您可能想要使用除 DEBUG 之外的预处理器符号名称,因为某些源也定义了该符号(例如,使用
./configure
命令准备构建的 progs)。当我开发它时,这对我来说似乎很自然。我在一个应用程序中开发了它,其中 DLL 被其他东西使用,并且将日志打印发送到文件更方便;但将其更改为 vprintf() 也可以正常工作。我希望这可以减轻你们中的许多人在找出进行调试日志记录的最佳方法时的烦恼;或者向您展示您可能更喜欢的一个。几十年来我一直半心半意地试图解决这个问题。工作于MSVC 2012 & 2012 2015 年,因此可能在 gcc 上;以及可能对许多其他人进行的工作,但我还没有在他们身上进行过测试。
我也打算有一天制作一个流媒体版本。
注意:感谢 Leffler,他热诚地帮助我更好地为 StackOverflow 格式化我的消息。
I've been stewing on how to do this for years, and finally come up with a solution. However, I didn't know that there were other solutions here already. First, at difference with Leffler's answer, I don't see his argument that debug prints should always be compiled. I'd rather not have tons of unneeded code executing in my project, when not needed, in cases where I need to test and they might not be getting optimized out.
Not compiling every time might sound worse than it is in actual practice. You do wind up with debug prints that don't compile sometimes, but it's not so hard to compile and test them before finalizing a project. With this system, if you are using three levels of debugs, just put it on debug message level three, fix your compile errors and check for any others before you finalize yer code. (Since of course, debug statements compiling are no guarantee that they are still working as intended.)
My solution provides for levels of debug detail also; and if you set it to the highest level, they all compile. If you've been using a high debug detail level recently, they all were able to compile at that time. Final updates should be pretty easy. I've never needed more than three levels, but Jonathan says he's used nine. This method (like Leffler's) can be extended to any number of levels. The usage of my method may be simpler; requiring just two statements when used in your code. I am, however, coding the CLOSE macro too - although it doesn't do anything. It might if I were sending to a file.
Against the cost the extra step of testing them to see that they will compile before delivery, is that
Branches are actually relatively pretty costly in modern pre-fetching processors. Maybe not a big deal if your app is not a time-critical one; but if performance is an issue, then, yes, a big enough deal that I'd prefer to opt for somewhat faster-executing debug code (and possibly faster release, in rare cases, as noted).
So, what I wanted is a debug print macro that does not compile if it is not to be printed, but does if it is. I also wanted levels of debugging, so that, e.g. if I wanted performance-crucial parts of the code not to print at some times, but to print at others, I could set a debug level, and have extra debug prints kick in. I came across a way to implement debug levels that determined if the print was even compiled or not. I achieved it this way:
DebugLog.h:
DebugLog.cpp:
Using the macros
To use it, just do:
To write to the log file, just do:
To close it, you do:
although currently this isn't even necessary, technically speaking, as it does nothing. I'm still using the CLOSE right now, however, in case I change my mind about how it works, and want to leave the file open between logging statements.
Then, when you want to turn on debug printing, just edit the first #define in the header file to say, e.g.
To have logging statements compile to nothing, do
If you need info from a frequently executed piece of code (i.e. a high level of detail), you may want to write:
If you define DEBUG to be 3, logging levels 1, 2 & 3 compile. If you set it to 2, you get logging levels 1 & 2. If you set it to 1, you only get logging level 1 statements.
As to the do-while loop, since this evaluates to either a single function or nothing, instead of an if statement, the loop is not needed. OK, castigate me for using C instead of C++ IO (and Qt's QString::arg() is a safer way of formatting variables when in Qt, too — it's pretty slick, but takes more code and the formatting documentation isn't as organized as it might be - but still I've found cases where its preferable), but you can put whatever code in the .cpp file you want. It also might be a class, but then you would need to instantiate it and keep up with it, or do a new() and store it. This way, you just drop the #include, init and optionally close statements into your source, and you are ready to begin using it. It would make a fine class, however, if you are so inclined.
I'd previously seen a lot of solutions, but none suited my criteria as well as this one.
Not terribly significant, but in addition:
DEBUGLOG_LOG(3, "got here!");
); thus allowing you to use, e.g. Qt's safer .arg() formatting. It works on MSVC, and thus, probably gcc. It uses##
in the#define
s, which is non-standard, as Leffler points out, but is widely supported. (You can recode it not to use##
if necessary, but you will have to use a hack such as he provides.)Warning: If you forget to provide the logging level argument, MSVC unhelpfully claims the identifier is not defined.
You might want to use a preprocessor symbol name other than DEBUG, as some source also defines that symbol (eg. progs using
./configure
commands to prepare for building). It seemed natural to me when I developed it. I developed it in an application where the DLL is being used by something else, and it's more convent to send log prints to a file; but changing it to vprintf() would work fine, too.I hope this saves many of you grief about figuring out the best way to do debug logging; or shows you one you might prefer. I've half-heartedly been trying to figure this one out for decades. Works in MSVC 2012 & 2015, and thus probably on gcc; as well as probably working on many others, but I haven't tested it on them.
I mean to make a streaming version of this one day, too.
Note: Thanks go to Leffler, who has cordially helped me format my message better for StackOverflow.
我相信主题的这种变体提供了调试类别,而无需每个类别都有单独的宏名称。
我在 Arduino 项目中使用了这种变体,其中程序空间限制为 32K,动态内存限制为 2K。添加调试语句和跟踪调试字符串很快就会耗尽空间。因此,每次构建代码时,必须能够将编译时包含的调试跟踪限制为所需的最低限度。
debug.h
调用.cpp文件
I believe this variation of the theme gives debug categories without the need to have a separate macro name per category.
I used this variation in an Arduino project where program space is limited to 32K and dynamic memory is limited to 2K. The addition of debug statements and trace debug strings quickly uses up space. So it is essential to be able to limit the debug trace that is included at compile time to the minimum necessary each time the code is built.
debug.h
calling .cpp file
如果你不关心输出到标准输出,你可以使用这个:
If you don't care that the output goes to stdout, you can use this: