【问题标题】:How to require a semicolon after a macro如何在宏之后要求分号
【发布时间】:2016-06-02 12:49:03
【问题描述】:

所以我正在编写一个库,它必须使用-pedantic -ansi -std=c++98 -Werror-Weverything 构建clang 和-Wall -Wextra 构建gcc,我有这个宏TESTSUITE(X),它打算在全局范围内使用,如下所示:

TESTSUITE(current testsuite);

它的作用是用字符串调用一个函数(在程序启动时通过初始化一个虚拟变量):

#define TESTSUITE(name) \
static int ANONYMOUS_VARIABLE(SOME_PREFIX) = setTestSuiteName(#name)

问题是这会在clang 下为-Wglobal-constructors 生成警告。

如果我像这样用_Pragma 包围它:

#define TESTSUITE(name)                                              \
_Pragma("clang diagnostic push");                                    \
_Pragma("clang diagnostic ignored \"-Wglobal-constructors\"");       \
static int ANONYMOUS_VARIABLE(SOME_PREFIX) = setTestSuiteName(#name) \
_Pragma("clang diagnostic pop")

编译时不需要使用宏后的分号(如果缺少它,-pedantic 会报错)。

如果我在宏的末尾添加这个

static int ANONYMOUS_VARIABLE(SOME_PREFIX) = 5

分号是必需的,但我会收到一个未使用变量的警告,我无法将其静音(因为如果我用 _Pragma 语句包围它,我将回到不需要分号的方块 1)。

那么有谁知道我如何要求分号并且还有 0 个警告?

【问题讨论】:

  • 要在启动时调用一个函数,你可以给它非标准的__attribute((constructor))__
  • @chris 很高兴知道 - 谢谢。但我更喜欢我已经拥有的解决方案,因为我还必须处理 Windows 编译器,并且拥有相同的宏集对我来说更好
  • 是的,looks complicated。如果有什么安慰的话,Visual Studio 现在可以使用 Clang。

标签: c++ c-preprocessor compiler-warnings suppress-warnings


【解决方案1】:

聚会有点晚了,但我想我会在稍后为寻找答案的人插话。实现此目的的推荐方法是将宏包装在 do {....} while (0) 中。这只会执行一次,编译器通常会为您优化它,不会生成额外的代码,但仍然可以实现抽象。

来源:https://gcc.gnu.org/onlinedocs/cpp/Swallowing-the-Semicolon.html

【讨论】:

  • 这不能在函数之外使用......我相信它会执行一次 - 而不是两次。
  • @onqtam 我的错,我想说一次
【解决方案2】:

类似于@thomas-eding 的解决方案,您可以将static_assert(true, "") 放在宏的末尾以要求分号。

这适用于内部和外部的类和函数。

而且它不会污染任何命名空间,也不会生成任何代码。

【讨论】:

  • 这是一个完美的答案,因为:(a) 它是一种适用于任何地方的技巧(与我之前一直使用的 using namespace requireSemicolon 不同)(b) 它没有副作用并且不生成代码, (c) 注释字符串可以记录其用途,例如static_assert( true, "semi-colon required after this macro, per zwhconst's excellent suggestion at https://stackoverflow.com/a/59153563/1261599" )
  • 这将在没有块的 if-else 中失败。
  • 这个答案应该占据更高的位置
  • @MartinBa 抱歉不清楚。 if (y == 7) SOME_MACRO(); else { return; } 扩展为 if (y == 7) { /* ... */ } static_assert(true, ""); else { return; },因为 SOME_MACRO() 扩展为 { /* ... */ } static_assert(true, "")
  • @EricRoller 在其他地方,我在宏周围看到过do { ... } while (0),它没有这个问题。
【解决方案3】:

我在宏末尾使用enum {} 来强制使用分号。

这适用于内部和外部的类和函数。

这种方法不会污染任何命名空间,也不会生成任何代码。

【讨论】:

  • 我从没想过枚举,而且一定会尝试的!今天!非常感谢这个想法!
  • 不幸的是,这会产生:“警告:ISO C++ 禁止使用 gcc-9.1 为空的未命名枚举 [-Wpedantic]”
【解决方案4】:

您可以在宏的末尾添加一个函数声明:

#define TESTSUITE(name)  \
//...                    \
void ANONYMOUS_FUNCTION()

Demo

函数名称在不同的TESTSUITE 宏中甚至不必不同。如果它没有在其他任何地方使用就足够了,因此它不会参与任何重载。

【讨论】:

  • 看起来 GCC 可以使用(void)0(void)"TESTSUITE requires a semicolon" 提供更好的错误,如果字符串文字曾经出现在错误中。 Clang 的错误几乎相同。
  • @chris (void)0; 是函数外的错误
猜你喜欢
  • 2011-09-15
  • 2020-05-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-04-10
  • 1970-01-01
  • 1970-01-01
  • 2012-01-16
相关资源
最近更新 更多