GCC 7,-Wimplicit-fallthrough 警告,以及清除它们的便携方式?

Posted

技术标签:

【中文标题】GCC 7,-Wimplicit-fallthrough 警告,以及清除它们的便携方式?【英文标题】:GCC 7, -Wimplicit-fallthrough warnings, and portable way to clear them? 【发布时间】:2017-07-16 14:35:11 【问题描述】:

我们正在捕获来自 GCC 7 的警告,即在 switch 语句中隐式失败。以前,我们在 Clang 下清除了它们(这就是下面评论的原因):

g++ -DNDEBUG -g2 -O3 -std=c++17 -Wall -Wextra -fPIC -c authenc.cpp
asn.cpp: In member function ‘void EncodedObjectFilter::Put(const byte*, size_t)’:
asn.cpp:359:18: warning: this statement may fall through [-Wimplicit-fallthrough=]
    m_state = BODY;  // fall through
                  ^
asn.cpp:361:3: note: here
   case BODY:
   ^~~~

GCC manual 声明使用__attribute__ ((fallthrough)),但它不可移植。该手册还指出“......也可以添加一条贯穿注释以使警告静音”,但它只提供FALLTHRU(这真的是唯一的选择吗?):

switch (cond)
  
  case 1:
    bar (0);
    /* FALLTHRU */
  default:
    …
  

是否有一种可移植的方式通过 Clang 和 GCC 的警告清除跌倒?如果有,那是什么?

【问题讨论】:

当您使用 C++17 时,请参阅属性 [[fallthrough]];(注意 - 需要 ;)来源:en.cppreference.com/w/cpp/language/attributes 谢谢理查德。不幸的是,不,我们目前不是属性。我们仍然支持回到 C++03。 -std=c++17 只是在 Fedora 26 下测试一种配置,因为它提供 GCC 7。但我会接受一个宏来抽象它以实现可移植性。 也许BOOST_FALLTHROUGH 可以满足您的需求。 boost.org/doc/libs/master/libs/config/doc/html/boost_config/… 谢谢@BaummitAugen - 是的,这可能行得通。你会碰巧知道__has_feature(x) 是否适用于此?用__has_feature(fallthrough) 测试就够了吗?还是我们需要通过编译器版本测试来做到这一点?还是别的什么? @BaummitAugen - 糟糕,我们不使用 Boost。我必须从头开始构建一个等效的宏。这就是其他问题的内容。 【参考方案1】:

GCC 期望标记注释出现在自己的行中,如下所示:

  m_state = BODY;
  // fall through
case BODY:

标记也必须在case 标签之前;不能有中间的右大括号

fall through 是 GCC 识别的标记之一。不仅仅是FALLTHRU。如需完整列表,请参阅-Wimplicit-fallthrough option 的文档。另见posting on the Red Hat Developer blog。

C++17 添加了一个[[fallthrough]] 属性,可用于抑制此类警告。注意结尾的分号:

  m_state = BODY;
  [[fallthrough]];
case BODY:

Clang 支持 -Wimplicit-fallthrough 警告,但不将它们作为 -Wall-Wextra 的一部分启用。 Clang 不识别注释标记,因此必须使用基于属性的抑制(这目前意味着 C 前端的非标准 __attribute__((fallthrough)) 构造)。

请注意,仅当编译器实际看到注释时,使用标记 cmets 抑制警告才有效。如果预处理器单独运行,则需要指示它保留 cmets,与 -C option of GCC 一样。例如,为避免ccache 出现虚假警告,您需要在编译时指定-C 标志,或者,对于最新版本的ccache,使用keep_comments_cpp 选项。

【讨论】:

谢谢弗洛里安。我在 Fedora 26 (gcc (GCC) 7.1.1 20170622 (Red Hat 7.1.1-3)) 上尝试了 GCC 7。如您的示例所示,将// fall through 移动到底部仍然会产生警告。如果需要,这是真正的源文件:asn.cpp。如果重要的话,我正在使用-std=c++17 你把评论放在哪里了?在 之前还是之后?你需要把它放在case关键字之前的那一行。 再次感谢弗洛里安。似乎其他人对 GCC 的实施有一些问题。请参阅 GCC 帮助列表中的 -Wimplicit-fallthrough broken?。 仅供参考:-save-temps 选项破坏了评论识别启发式。在这种情况下不会抑制警告 虽然 fallthrough cmets 可能是可移植的,但代码中的 cmets 被设计为不通过预处理器存在,并且使用 ccache 之类的工具利用预处理器输出,任何此类 cmets 都将在预处理后的输出被发送到 gcc 进行编译。因此,如果您使用 ccache,您仍然会看到这些警告。【参考方案2】:

C++17 [[fallthrough]]

例子:

int main(int argc, char **argv) 
    switch (argc) 
        case 0:
            argc = 1;
            [[fallthrough]];
        case 1:
            argc = 2;
    ;

编译:

g++ -std=c++17 -Wimplicit-fallthrough main.cpp

如果您删除 [[fallthrough]];,GCC 会发出警告:

main.cpp: In function ‘int main()’:
main.cpp:5:15: warning: this statement may fall through [-Wimplicit-fallthrough=]
             argc = 1;
             ~~^~~
main.cpp:6:9: note: here
         case 1:
         ^~~~

还请注意,该示例仅在遇到两种情况时才会出现警告:最后一个 case 语句(此处为 case 1)即使没有 break 也不会生成警告。

以下构造也不会生成警告:

#include <cstdlib>

[[noreturn]] void my_noreturn_func() 
    exit(1);


int main(int argc, char **argv) 
    // Erm, an actual break
    switch (argc) 
        case 0:
            argc = 1;
            break;
        case 1:
            argc = 2;
    

    // Return also works.
    switch (argc) 
        case 0:
            argc = 1;
            return 0;
        case 1:
            argc = 2;
    

    // noreturn functions are also work.
    // https://***.com/questions/10538291/what-is-the-point-of-noreturn/47444782#47444782
    switch (argc) 
        case 0:
            argc = 1;
            my_noreturn_func();
        case 1:
            argc = 2;
    

    // Empty case synonyms are fine.
    switch (argc) 
        case 0:
        case 1:
            argc = 2;
    

    // Magic comment mentioned at:
    // https://***.com/a/45137452/895245
    switch (argc) 
        case 0:
            argc = 1;
            // fall through
        case 1:
            argc = 2;
    

    switch (argc) 
        // GCC extension for pre C++17.
        case 0:
            argc = 1;
            __attribute__ ((fallthrough));
        case 1:
            argc = 2;
    

    switch (argc) 
        // GCC examines all braches.
        case 0:
            if (argv[0][0] == 'm') 
                [[fallthrough]];
             else 
                return 0;
            
        case 1:
            argc = 2;
    

从最后一个我们可以看出,GCC 会检查所有可能的分支,如果其中任何一个分支没有[[fallthrough]];breakreturn,则会发出警告。

您可能还想使用this GEM5 inspired snippet 中的宏检查功能可用性:

#if defined __has_cpp_attribute
    #if __has_cpp_attribute(fallthrough)
        #define MY_FALLTHROUGH [[fallthrough]]
    #else
        #define MY_FALLTHROUGH
    #endif
#else
    #define MY_FALLTHROUGH
#endif

另见:https://en.cppreference.com/w/cpp/language/attributes/fallthrough

在 GCC 7.4.0、Ubuntu 18.04 上测试。

另请参阅

本题C版:How to do an explicit fall-through in C

【讨论】:

【参考方案3】:

另一个例子:Linux 内核提供了一个fallthrough 伪关键字宏。可以用作:

switch (cond) 
case 1:
    foo();
    fallthrough;
case 2:
    bar();
    break;
default:
    baz();

在内核v5.10 中实现如下:

#if __has_attribute(__fallthrough__)
# define fallthrough                    __attribute__((__fallthrough__))
#else
# define fallthrough                    do  while (0)  /* fallthrough */
#endif

【讨论】:

【参考方案4】:

清洁 C 解决方案:

int r(int a) 
    switch(a) 
    case 0:
        a += 3;
    case 1:
        a += 2;
    default:
        a += a;
    
    return a;

变成:

int h(int a) 
    switch(a) 
    case 0:
        a += 3;
        goto one;
    case 1:
    one:
        a += 2;
        goto others;
    default:
    others:
        a += a;
    
    return a;

编辑:按照 cmets 中的 Stéphane Gourichon 的建议,在 case 语句之后移动标签,以便更轻松地查看失败。

【讨论】:

我喜欢这个想法,因为它是完全可移植的。在case 之前添加额外的标签 before 真的有效吗?从技术上讲,仍然存在跌幅。我会考虑这样的事情:goto one 然后case 1: 然后one: 按此顺序。 Gcc 接受这两种变体。【参考方案5】:

没有人提到完全禁用警告,这可能不是 OP 正在寻找的答案,但我认为它应该包括在内以保持完整性,因为它也适用于两个编译器:

-Wno-implicit-fallthrough

【讨论】:

以上是关于GCC 7,-Wimplicit-fallthrough 警告,以及清除它们的便携方式?的主要内容,如果未能解决你的问题,请参考以下文章

Centos 7/linux 安装gcc 教程

[ubuntu][原创]ubuntu22.04更换gcc版本为gcc-7

Ubuntu 20编译安装GCC 7.3.0

Ubuntu 20编译安装GCC 7.3.0

std::atomic 库依赖 (gcc 4.7.3)

环境搭建——安装gcc 7.3.1版本