如何处理纯C中的严格枚举?

3

看一下下面的代码:

typedef enum {
    A, B, C
} Foo;

int foo(Foo x) {
    switch(x) {
        case A: return 1;
        case B: return 2;
        case C: return 3;
    }
}

GCC 10.2 输出

<source>:11:1: warning: control reaches end of non-void function [-Wreturn-type]
   11 | }
      | ^

这是因为我可以向 foo 传递像 42 这样的东西,而不仅仅是 ABC。所以问题是:如何告诉 GCC 只有 ABC 可以被 switch 语句处理,否则行为是未定义的?接受编译器特定的功能。
让我指出一些不满足我的解决方案。首先,我可以插入 default: __builtin_unreachable();,但这会穿透 case 分析:想象一下,显然我正在添加 D 变量,而编译器不会告诉我 D 是未处理的。
其次,我可以在 switch 语句之前插入 if (x > C) { __builtin_unreachable(); },但这太不可能了,因为 switch(x) 实际上是由一个宏生成的,这个宏不知道 Foo 的变量,它只知道一些变量 x

第三,我可以插入#pragma GCC diagnostic ignored "-Wreturn-type",但是由于switch(x)是由宏生成的,这就是为什么我不能通过#pragma GCC diagnostic pop将诊断恢复到先前状态的原因。

第四,我可以使用数组代替switch,但返回的表达式不是常量,而是由生成switch(x)的宏的用户提供的。

最后一个:在switch语句之后,我可以编写return 42;,但是我再次想自动禁用生成switch(x)的宏内部的警告,因为它在我的代码库中被广泛使用。

Godbolt


3
可能需要添加一个default:情况吗? - Code-Apprentice
1
@Code-Apprentice:楼主已经提出了那个想法。 - Robert Harvey
2
你能否更改宏以分配变量而不是返回吗?然后在 switch 之后返回该变量。 - Barmar
1
@JonathanLeffler,正如我在问题中所指出的那样,表达式可以是任意复杂的,并且可能具有副作用。 - Hirrolot
2
不幸的是,C语言没有严格枚举的概念。你必须做出一些让步。 - Barmar
显示剩余17条评论
1个回答

0

如果你想要进行一些轻度的自虐,你可以使用 setjmplongjmp 来粗略地实现类似异常的行为:

#include <stdlib.h>
#include <setjmp.h>
#include <stdio.h>

typedef enum Foo {
  A, B, C
} Foo;

#define ENUM_OUT_OF_RANGE -1

jmp_buf env;

int foo( Foo x )
{
  switch ( x )
  {
    case A: return 1;
    case B: return 2;
    case C: return 3;
    default: longjmp( env, ENUM_OUT_OF_RANGE ); // "throw" ENUM_OUT_OF_RANGE
  }
}

int main( void )
{
  int ex;

  if ( (ex = setjmp( env )) == 0 ) // "try" block
  {
    Foo arr[] = {A, B, C, 42};

    for ( size_t i = 0; i < 4; i++ )
    {
      printf( "value of %d = %d\n", arr[i], foo( arr[i] ) );
    }
  }
  else // "catch" block
  {
    if ( ex == ENUM_OUT_OF_RANGE )
    {
      fprintf( stderr, "foo was called with a value outside the range of Foo\n" );
    }
  }

  return 0;
}

在我的系统上,以下代码构建没有警告:

gcc -o exceptions -std=c11 -pedantic -Wall -Werror exceptions.c

输出:

$ ./exceptions 
value of 0 = 1
value of 1 = 2
value of 2 = 3
foo was called with a value outside the range of Foo

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接