如何创建一个“C单行注释”宏。

12

我正在尝试在C中创建一个“单行注释”宏,用于根据某些全局宏定义有条件地注释掉代码行。这是此文章中表达的相同思想。

尝试了许多这个代码的排列组合,但是我一直从编译器得到错误信息。

例如,直接按照该页面上的代码示例:

#define COMMENT SLASH(/)
#define SLASH(s) /##s
#define DEBUG_ONLY COMMENT
DEBUG_ONLY a = b;   // <-- line #83

GCC报以下错误:

prog.c:83:1: 错误:粘贴“/”和“/”不能产生有效的预处理记号
prog.c:83: 错误:在‘/’标记之前期望表达式

如前所述,我已尝试多种变化,但都未能通过编译并出现了类似的诊断。

我做错了什么,为什么文章中的代码不能很好地编译?


1
编译器会告诉你哪里出了问题。这些技巧有时可以在有缺陷的编译器上使用,但它们本不应该起作用,你也不应该使用它们。 - Alexey Frunze
@AlexeyFrunze - 你是说这段代码基本上是非法的吗?为什么会这样呢?我为什么不能创建一个注释宏? - ysap
是的,不合法。Steve的答案提到了标准中相关的部分。 - Alexey Frunze
5个回答

21

它无法工作是因为语言规范不允许。实际上,在宏替换之前,注释的移除已经发生了。//不再是一个有效的记号(如错误消息所述)。它不能被宏替换生成,也不再表示“注释”。

这是标准中的“翻译阶段”。虽然部分编号不同,但C89、C99和C11都在第3阶段定义:

每个注释将被替换为一个空格字符。

然后在第4阶段:

宏调用将被扩展


我不确定我理解了。"注释删除发生在宏替换之前" - 所以至少 DEBUG_ONLY 宏应该变成一个空宏,是吗(我的意思是,现在注释已经被删除了)? - ysap
@ysap:不行。您的代码中仅有的注释是// <-- line #83,在第三阶段被删除。在第四阶段,DEBUG_ONLY尝试使用标记粘贴来创建一个无效的标记 //。因此,该程序是非法的。 - Steve Jessop
1
好的,那么,在符合标准的编译器中没有生成“注释”宏的方法吗? - ysap
很遗憾...不过还是谢谢。 - ysap

10

一个调试宏:

#define DEBUG(x) x

可以在生产环境中关闭,方法如下:

#define DEBUG(x)

或者我记得的是 #undef(抱歉,我的C语言已经生疏了)。

感谢您的提问。这个问题是情况的简化。实际的代码更加复杂,所以我不能使用这种风格。 - ysap
1
如果可以的话,你能否发布一些代码来展示为什么不能使用这种风格? - fuz
@ysap,我真的不太明白你想做什么。你介意给一些代码吗?当涉及到参数列表时,CPP并不愚蠢。 - fuz
@ysap,应用最少的代码更改是Joachim Pileborg建议的方法。我不明白你想做的与我的说法相比如何更多或更少地影响代码。 - fuz
2
@ysap,只需执行#define d(x) if((x > 0) && 0),任何(半聪明的)编译器都会消除整个d(something) { ... }结构。 - vonbrand
显示剩余5条评论

7
为什么不直接使用例如的方法呢?
#ifdef DEBUG
a = b;
#endif  /* DEBUG */

更简单,同样易读。

3
如果某个元素只在几个地方出现,你可能是正确的。但如果它在多个模块中出现了数十次,且嵌套层级很深,那么这肯定会使你的代码难以阅读。 - ysap
1
@ysap 或许这就是你不应该在项目中到处堆积垃圾调试代码的原因?没人会阻止你将某个代码片段复制到一个单独的测试项目中。 - Lundin
1
@ysap,这里的问题在于你的代码已经难以阅读和维护了。你需要一个真正的日志框架,而不是更多的宏。 - djechlin
注意:是的,我的宏观相关声明有例外。我知道这一点。但这不是其中之一。 - djechlin
2
@ysap 代码不会进化,只会腐烂。抛弃它并重新开始。如果你的管理层认为通过原型演化可以实现更具有攻击性的目标,那么找到新的管理层,因为他们是错误的。 - djechlin
显示剩余5条评论

1

使用#define宏时,你不能完全注释掉一行代码,但你可以注释掉分号之前的所有内容。我发现这种方法非常有效。

#define LOG_LVL 111100011
//              987654321
#if(LOG_LVL%10   >= 1  )
    #define LOG1 if(1)
#else
    #define LOG1 if(0)
#endif//End LOG1 if-block

#if(LOG_LVL%1000 >= 100)
    #define LOG3 if(1)
#else
    #define LOG3 if(0)
#endif//End LOG3 if-block

只要你在分号上小心,这个应该能正常工作。默认情况下,非大括号的if语句只会执行下一行代码。
像这样记录日志的另一个好处是可以精细调整所需的日志级别。在此示例中,启用了LOG1,禁用了LOG3。如果我想使我的日志更详细,我可以快速更改LOG_LVL 111100011,以便其3位数字中有一个1(或更高),以启用LOG3。

1

我在我的应用程序中使用了#define cout(x) //cout<<x;。你可能想要修改它,比如:

#ifdef DEBUG
#define cout(x) cout<<x;
#else
#define cout(x)

并将其用作

cout(arg0<<arg1<<arg2);

在这里,您不需要注释该行,因此也不需要单独的行来避免打印。 此外,无论何时必须无条件打印,都可以使用cout。
cout("Print this when debugging");
cout<<"Always print this";

谢谢,欢迎来到SO。请注意,您的答案(假设它有效)仅针对打印输出情况。问题更加普遍。它要求通过将其转换为注释来消除任意语句。无论如何,欢迎+1。 - ysap

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接