【问题标题】:Any utility to test expand C/C++ #define macros?任何用于测试扩展 C/C++ #define 宏的实用程序?
【发布时间】:2011-03-02 11:13:34
【问题描述】:

似乎我经常花费太多时间来尝试让#define 宏完全按照我的意愿行事。我将在下面发布我当前的困境,并感谢任何帮助。但实际上更大的问题是是否有人可以推荐任何实用程序来快速显示宏实际在做什么?如果我能看出问题所在,即使是缓慢的试错过程似乎也会变得更快。

目前,我正在从我制作的 DLL 中动态加载一长串函数。按照我的设置方式,函数指针与导出的函数具有相同的名称,用于原型化它们的 typedef(s) 具有相同的名称,但带有前置下划线。所以我想用一个define来简化一长串函数指针的赋值。

例如,在下面的代码语句中,'hexdump'是一个typedef的函数点的名称,也是函数的名称,而_hexdump是typedef的名称。如果 GetProcAddress() 失败,则失败计数器递增。

if (!(hexdump = (_hexdump)GetProcAddress(h, "hexdump"))) --iFail;

假设我想用一个宏替换上面的每一行,就像这样......

GETADDR_FOR(hexdump )

嗯,这是迄今为止我想出的最好的。它不起作用(我的 // 注释只是为了防止消息中的文本格式)...

// #define GETADDR_FOR(a) if (!(a = (#_#a)GetProcAddress(h, "/""#a"/""))) --iFail; 

再说一次,虽然我很高兴能深入了解我犯了什么愚蠢的错误,但如果我有一个实用程序,只需插入我的宏,就可以向我显示我的方式错误。

【问题讨论】:

  • 你用的是什么编辑器?
  • 赞成,但希望它是一个 IDE ;-)

标签: c++ macros c-preprocessor


【解决方案1】:

您似乎对在 C 预处理器宏中进行字符串化或标记粘贴的确切语法感到困惑。

您可能会发现有关 C preprocessor macros in general 的页面很有帮助。

特别是,我认为这个宏应该是这样的:

#define GETADDR_FOR(a) if (!(a = (_##a)GetProcAddress(h, #a))) --iFail

应该跳过尾随的;,因为您可能会将其输入为GETADDR_FOR(hexdump);,如果您不这样做,它会在您的C 代码中看起来很奇怪,并且会混淆许多语法高亮显示。

正如其他人提到的那样,gcc -E 将运行预处理器并跳过其他编译步骤。这对于调试预处理器问题很有用。

【讨论】:

  • 啊,谢谢!我当然对正确使用 stringizer # 的概念很差。但它的用法和连接用法!非常感激!并感谢文章链接。这看起来比我在 MSDN 中找到的简要说明要全面得多!
【解决方案2】:

转到https://godbolt.org/。在左窗格中输入您的代码并选择编译器作为 gcc 将参数作为 -E 在右窗格中。您的预处理代码将显示在右侧。

【讨论】:

  • 很棒的工具!应该是最佳答案,因为它需要的工作量最少。
  • 你拯救了我的一天!
  • 对于 MSVC (Visual C++),将 /E 设置为编译器选项,然后打开输出面板(底部的选项卡)。然后,您还可以将此“输出”面板拖动到页面中的任何位置。
  • 对于另一个选项,repl.it 可以使用。只需启动一个新的 repl 以选择语言并将代码粘贴到左侧编辑器中。按F1输入或找到“shell”打开shell,然后输入gcc -E main.c(假设main.c为文件名),就会显示预处理后的代码。
【解决方案3】:

【讨论】:

  • 用户在哪里提到了 Visual Studio?
  • @AndrewBarber 他在评论中提到他访问了 MSDN
【解决方案4】:

您可能想看看Boost Wave。像大多数 Boost 一样,它实际上更像是一个库而不是实用程序,但它确实有一个驱动程序来充当一个完整的预处理器。

【讨论】:

  • 我不知道为什么这被否决了... Wave 确实有一个预处理器实现(尽管它并不完整;它不支持宏替换的几个晦涩的方面)。
  • @James:是的,“完整”可能是错误的词——我的意思是它有驱动程序代码,所以你可以编译和链接它作为一个完整的可执行程序。如果(例如)你想要宏替换没有文件包含,这是我所知道的最好的起点(你经常不想要文件包含,因为它可以/将产生大量的内容来扫描以找到您关心的内容)。
  • 也许您可以提一下wave driver应用程序的tracing ability?因为我不知道有任何其他工具可以逐步显示宏是如何扩展的。 gdb 将来可能会获得逐步扩展的功能(这里提到:stackoverflow.com/questions/57517469/…)...
【解决方案5】:

您可以通过预处理器运行您的代码,它会向您显示它将被扩展成什么(或根据需要吐出错误):

$猫交流 #define GETADDR_FOR(a) if (!(a = (#_#a)GetProcAddress(h, "/""#a"/""))) GETADDR_FOR(十六进制转储) $ gcc -E a.c # 1 “交流电” # 1 "" # 1 "" # 1 “交流电” a.c:1:36:错误:“#”后面没有宏参数 GETADDR_FOR(十六进制转储)

在 GCC 中,gcc -E foo.c 只对文件进行预处理。

Visual Studio uses /P 参数。

【讨论】:

  • 谢谢。我想我会调查 GCC。我想我希望能在我的宏上做一个“尽可能扩展”的东西,这样我就可以看到它在视觉上做了什么。但我想就像错误的 C 代码一样,如果我的宏完全是垃圾,那么预处理器可能根本无法扩展它。 ;-)。我想我只需要阅读有关该主题的更好的文档。但是,有一些东西可以转储成功扩展的结果仍然会很好,因为比错误消息更糟糕的是一个有效的扩展,但不会生成预期的代码! GCC /P 会这样做吗?
  • @Randy:实际上,Jerry Coffin 推荐的 Boost Wave 具有“替换跟踪”功能,它会输出一个文件,显示宏替换期间所采取的步骤。它非常冗长,因为步骤通常比我们通常想象的要多得多,但它可能非常有用,尤其是对于复杂或长的宏定义。
  • @Randy:我的观点是,如果您的宏复杂到需要复杂的调试工具,那么您做错了什么。 :-)
  • Omni,再说一遍,作为程序员,我们所有的人类努力都是从一个简单的前提开始的,但最终却把奥卡姆剃刀的所有可能的解释都打到了地狱。 ;-)
  • 有什么办法可以省略 std 包含的内容吗?得到很多不必要的噪音。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2010-12-11
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多