【问题标题】:How to create a "C single-line comment" macro如何创建“C 单行注释”宏
【发布时间】:2013-03-10 20:16:00
【问题描述】:

我正在尝试在 C 中创建一个“单行注释”宏,根据一些全局宏定义,它用于有条件地注释掉代码行。与this article中表达的想法相同。

尝试了此代码的许多排列,我不断收到来自编译器的错误消息。

例如,直接跟随该页面的代码示例:

#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:错误:“/”标记之前的预期表达式

如前所述,我使用该主题并尝试了许多变体,但都未能给出类似的诊断结果。

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

【问题讨论】:

  • 编译器告诉你你做错了什么。这些技巧有时适用于损坏的编译器,但它们根本不应该工作,你不应该使用它们。
  • @AlexeyFrunze - 你是说这段代码基本上是非法的吗?为什么会这样?为什么我不能创建评论宏?
  • 是的,非法的。史蒂夫的回答提到了标准的相关部分。

标签: c gcc macros c-preprocessor


【解决方案1】:

为什么不简单地使用例如

#ifdef DEBUG
a = b;
#endif  /* DEBUG */

麻烦少,可读性强。

【讨论】:

  • 当它出现在几个地方时,你可能是对的。当它出现在许多模块的几十个地方,嵌套层次很深时,这肯定会让你的代码不可读。
  • @ysap 也许这就是为什么您不应该首先在整个项目中保留无用的调试代码的原因?没有人阻止您将某段代码复制到单独的测试项目中。
  • @Lundin - 没有人,除了测试是在特定的上下文中,在特定的场景中可能涉及大量的初步操作,这意味着你的测试用例本身变得很复杂。
  • @ysap 这里的问题是您的代码已经不可读且不可维护。你需要一个实际的日志框架,而不是更多的宏。
  • @ysap 代码不会进化,它会腐烂。把它扔掉,重新开始。如果您的管理层认为改进原型可以让他们达到更激进的目标,那么请寻找新的管理层,因为他们错了。
【解决方案2】:

一个调试宏:

#define DEBUG(x) x

在生产中可以关闭如下:

#define DEBUG(x)

或 IIRC #undef(对不起,我的 C 生锈了)。

【讨论】:

  • 坦克。问题是情况的简化。实际代码比较复杂,所以不能用这种风格。
  • @ysap 如果是这样,您能否发布一些代码来说明您为什么不能使用这种样式?
  • @FUZxxl - 我基本上是在尝试将该功能叠加在现有代码结构上,其中宏只是一个测试,实际代码紧随其后。我想要做的是完全注释掉后面的代码,而不改变整个代码体(到处替换那个宏)。另外,后面的条件代码本身就包含了函数调用,所以我认为它们的参数列表不会被预处理器很好地处理。
  • @ysap 我真的不明白你想做什么。你介意给一些代码吗?当涉及到参数列表时,CPP 并不是那么愚蠢。
  • @ysap,只需执行#define d(x) if((x &gt; 0) &amp;&amp; 0),任何(中途)智能编译器都会摆脱整个d(something) { ... } 构造。
【解决方案3】:

它不起作用,因为语言规范不允许这样做。实际上,注释删除发生在 宏替换之前。删除 cmets 后,// 不是有效令牌(如错误消息所述)。无法通过宏替换生成,不再是“注释”的意思。

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

每条评论被一个空格字符替换。

然后在第 4 阶段:

宏调用被扩展

【讨论】:

  • 我不确定我是否理解。 “注释删除发生在宏替换之前” - 所以至少 DEBUG_ONLY 宏应该变成一个空宏,不是吗(我的意思是,现在删除了注释)?
  • @ysap:没有。您的代码中唯一的注释是// &lt;-- line #83,它在阶段 3 中被删除。在阶段 4,DEBUG_ONLY 尝试使用令牌粘贴来创建无效令牌 //。因此该程序是不正确的。
  • 好的,那么,没有办法在符合标准的编译器中生成“注释”宏吗?
  • 这太糟糕了......还是谢谢。
【解决方案4】:

使用#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

【讨论】:

    【解决方案5】:

    我在我的应用程序中使用了#define cout(x) //cout&lt;&lt;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。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-10-09
    • 2012-07-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-10-27
    • 1970-01-01
    相关资源
    最近更新 更多