【问题标题】:How to make G++ preprocessor output a newline in a macro?如何让 G++ 预处理器在宏中输出换行符?
【发布时间】:2011-01-17 06:48:26
【问题描述】:

gcc/g++ 4.* 中有没有办法编写一个扩展为多行的宏?

以下代码:

#define A X \ Y

扩展到

X Y

我需要一个宏扩展到

X
Y

【问题讨论】:

  • 有趣的问题 - 我不知道用 C/C++ 做你所描述的事情的方法(尽管 GCC 可能有一个我不熟悉的扩展)。您能否更详细地解释您正在尝试做什么(根据“什么”,而不是“如何”) - 可能还有另一种方法可以获得您最终想要的结果。
  • 我的动机是让调试更容易。
  • @JonathanLeffler 我看不到将 this 问题标记为重复的用途,因为它是唯一有可行答案的问题。
  • 这尤其令人遗憾,因为未登录的用户会自动被重定向到另一个用户,所以他们得到的问题没有好的答案。

标签: c++ g++ newline c-preprocessor


【解决方案1】:

知道了!

#define anlb /*
*/ A /*
*/ B

anlb anlb

gcc -E -CC nl.c

/*
*/ A /*
*/ B /*
*/ A /*
*/ B

【讨论】:

  • 不错的把戏。我用VC++试过了;虽然可以使用/C 指示其预处理器在预处理器输出中保留 cmets,但它不会在宏中保留 cmets,因此这一切都以一行结束。
  • 哪个版本适用于此?最新的g++ 似乎没有。 (考虑到我目前正在使用的宏,这很遗憾。)
  • @underscore_d 我刚刚用我最新的 GCC 5.1 和 Clang 3.6 进行了测试。您确定 -CC 参数通过编译器吗?
  • @Potatoswatter:我已经用向下箭头的图像替换了“V”(为这个场合制作的)。随时恢复。
【解决方案2】:

使宏生成一个特殊的标记,比如__CR__,然后将 CPP 的结果通过管道传输到一个脚本中,该脚本将宏转换为真正的换行符,例如,sed 's/__CR__/\n/g'

我刚刚发现这对于生成要手动填充的代码模式很有用。当代码可读时,就容易多了。

【讨论】:

    【解决方案3】:

    你可以在宏中放一个魔术字符序列,例如

    #define X(y,z) y nl z
    

    运行gcc -E infile | sed g/s/nl/\n/ > whatever

    (也许不完全正确,但你明白了,对吗?通过sedtr 传递它或在输出上运行Emacs。)

    我有时这样做是为了使用 C 宏来生成源代码,不,我不想在 PerlM4 或其他工具中学习如何做到这一点。

    【讨论】:

      【解决方案4】:

      撇开无法在宏中添加换行符这一事实不谈,这会产生不可读的代码,从而使调试预处理器输出变得更加困难。的确,C 和 C++ 可能不关心换行符,但 C 预处理器会。

      我真的很想制作一个宏 ConditionalDefine(x,y) 来输出以下内容。

          #ifdef x
          #define y
          #endif
      

      以下定义做一些接近的事情:

          #define hash #
          #define nl
          #define _def_p8(A,B) A ifdef _P8_K60_BOARD_ A define B  A endif
          #define X_def_p8(A,B) _def_p8(A,B)
          #define def_p8(A) X_def_p8(nl hash,A)
      

      扩展以下内容:

          def_p8(PTPD_DBGA 0)
      

      结果:

          # ifdef _P8_K60_BOARD_ # define PTPD_DBGA 0    # endif
      

      但是如果不能在哈希之前添加新行,它将无法按预期工作。这也很烦人,你必须跳过去才能接近。

      【讨论】:

      • 即使您使用宏形成预处理器指令的文本,它也不会被执行。您可以做的是将要复制的行放入标题中并使用“计算”​​#include STRINGIZE( blah( xyz ) ) 指令。您仍然无法计算宏名称,但这是唯一的选择。也许检查 Boost Preprocessor 库(但我从未涉足过)。
      【解决方案5】:

      我很确定 CPP 是为不关心换行符的 C 设计的,而且所有这些都无法处理这种工作。你仍然可以用一些特殊的标记字符串标记想要的换行符,然后通过sedawk 传递结果来得到你想要的。

      【讨论】:

      • C 也不关心标签,但您可以定义一个宏,其扩展包含一个或多个标签。缺少换行符是 C 宏功能中一个非常特殊的漏洞。
      • @Steve Jessop:如果您需要通用宏预处理器,请使用“m4”。 C 预处理器旨在满足 C 编译器的需要;尽管它被广泛使用,但它并不是一个通用的宏预处理器。
      • 标签保留是由标准保证还是仅仅允许实现?预处理器对标记序列进行操作,而 cmets、制表符和换行符被计为空白,而不是预处理标记。我在标准中没有看到任何表明需要在替换列表中保留空白的内容,只需要预处理标记。
      • @jw013:我不确定它是否是一般标准,但我绝对确定它必须在字符串文字中保留制表符和其他空格。我严重怀疑是否有人会头疼地识别替换是否包含字符串,并仅删除/替换这些字符串之外的选项卡。想想:#define tab_or_spaces(x) ( (x)? " " : " " ) - 为什么预处理器会尝试去确定允许剥离制表符或空格的位置?
      • “我严重怀疑是否有人会头疼地识别替换是否包含字符串并仅删除/替换这些字符串之外的选项卡。” ——真是太无知了。
      【解决方案6】:

      来自docs

      在目前的实现中, 整个扩展出现在一条线上

      【讨论】:

        【解决方案7】:

        为什么间距很重要?

        在 X11 的(旧的?)构建中使用的 imake 程序使用 C 预处理器来生成 makefile,但是 imake 程序使用了一种特殊的技术,即在行尾用 @@ 符号指示行尾,然后后置处理预处理器的输出以用换行符替换 @@ 符号。

        从这个设计中,我得出结论,没有可靠的方法从 C(或 C++)中的扩展宏中获取换行符。事实上,对于 C,没有必要,因为:

        • 在 C 预处理器运行后,C 不关心换行符和空格,并且
        • 您不能从宏等生成预处理器指令。

        【讨论】:

        • 只要您使用 CPP 来生成 C/C++/其他与换行无关的语言结果,那就太好了。最近我不得不使用 C 预处理器生成 shell 文件。我向你保证换行很重要。
        • @SF:在这种情况下,您使用了错误的工具来完成这项工作。 CPP 是,嗯,C 预处理器。因此,它遵循 C 标准中的一些严格规则,并且可能针对预处理 C 进行了优化。为自己准备一个真正的宏预处理器,或者使用脚本语言编写一些东西。请不要坚持将附加功能固定在我按预期使用的软件上。同样,我喜欢能够购买侧面没有锤头的月牙扳手。
        • “为什么间距很重要?”有时人类必须对输出进行故障排除:)
        • CPP 是 gfortran 使用的预处理器。 Fortran 对换行符和行长很敏感。
        【解决方案8】:

        这是你想要的吗:

        #define A "X \nY"
        

        【讨论】:

        • 不,我不需要字符串常量。我需要 A 扩展为由换行符分隔的两个运算符。例如#define A x++; 这里有一些特别的东西 y++; A 我希望它扩展到: x++; y++;而不是 x++; y++;
        • x++; y++; 有什么问题?编译器不关心空格。
        • @Michael,那你想达到什么目的
        猜你喜欢
        • 1970-01-01
        • 2020-04-06
        • 1970-01-01
        • 1970-01-01
        • 2017-09-18
        • 2012-05-12
        • 1970-01-01
        • 1970-01-01
        • 2015-04-15
        相关资源
        最近更新 更多