C11 _Generic() 表达式在嵌套时失败

C11 _Generic() expression failing if being nested

我试图找出嵌套 _Generic() 表达式对我来说失败的原因。如果我不嵌套它们,它们工作正常。

当然,我在一个较长的程序中遇到过这种情况,但我将其总结为一个最小的工作示例,它触发的错误与在较大的程序中完全相同。

我试着反复检查代码,但没有找到确凿的证据...问题的原因是什么?

#include <stdio.h>

typedef struct sta
    {
    char a;
    }ta;

typedef struct stb
    {
    short b;
    }tb;

typedef struct stc
    {
    int c;
    }tc;

#define MAGICNUMBER(X) _Generic((X), \
ta: 1, \
tb: 2, \
tc: 3 \
)

#define NESTEDMAGICNUMBER3(X) _Generic((X), \
ta: 1 \
)

#define NESTEDMAGICNUMBER2(X) _Generic((X), \
tb: 2, \
default: NESTEDMAGICNUMBER3(X) \
)

#define NESTEDMAGICNUMBER(X) _Generic((X), \
tc: 3, \
default: NESTEDMAGICNUMBER2(X) \
)

int     main(int argc, char *argv[])
    {
    ta a;
    tb b;
    tc c;
    
    printf("direct macro: ta has # %d\n",MAGICNUMBER(a));
    printf("direct macro: tb has # %d\n",MAGICNUMBER(b));
    printf("direct macro: tc has # %d\n",MAGICNUMBER(c));
    printf("nested macro: ta has # %d\n",NESTEDMAGICNUMBER(a)); /* Compile-time error why? */
    printf("nested macro: tb has # %d\n",NESTEDMAGICNUMBER(b)); /* Compile-time error why? */
    printf("nested macro: tc has # %d\n",NESTEDMAGICNUMBER(c)); /* Compile-time error why? */
    
    return 0;
    }

编译时,我得到这个:

cc -o testgeneric testgeneric.c
testgeneric.c:49:39: error: controlling expression type 'tb' (aka 'struct stb') not compatible with any generic association type
        printf("nested macro: tb has # %d\n",NESTEDMAGICNUMBER(b)); /* Compile-time error why? */
                                             ^~~~~~~~~~~~~~~~~~~~
testgeneric.c:36:10: note: expanded from macro 'NESTEDMAGICNUMBER'
default: NESTEDMAGICNUMBER2(X) \
         ^~~~~~~~~~~~~~~~~~~~~
testgeneric.c:31:10: note: expanded from macro 'NESTEDMAGICNUMBER2'
default: NESTEDMAGICNUMBER3(X) \
         ^~~~~~~~~~~~~~~~~~~~~
testgeneric.c:25:40: note: expanded from macro 'NESTEDMAGICNUMBER3'
#define NESTEDMAGICNUMBER3(X) _Generic((X), \
                                       ^~~
testgeneric.c:50:39: error: controlling expression type 'tc' (aka 'struct stc') not compatible with any generic association type
        printf("nested macro: tc has # %d\n",NESTEDMAGICNUMBER(c)); /* Compile-time error why? */
                                             ^~~~~~~~~~~~~~~~~~~~
testgeneric.c:36:10: note: expanded from macro 'NESTEDMAGICNUMBER'
default: NESTEDMAGICNUMBER2(X) \
         ^~~~~~~~~~~~~~~~~~~~~
testgeneric.c:31:10: note: expanded from macro 'NESTEDMAGICNUMBER2'
default: NESTEDMAGICNUMBER3(X) \
         ^~~~~~~~~~~~~~~~~~~~~
testgeneric.c:25:40: note: expanded from macro 'NESTEDMAGICNUMBER3'
#define NESTEDMAGICNUMBER3(X) _Generic((X), \
                                       ^~~
2 errors generated.

为什么嵌套版本失败,而非嵌套版本正常?

谢谢!

_Generic 表达式中,传入类型 必须 匹配给定类型之一或具有 default 部分。

让我们看一下预处理器输出,看看发生了什么:

printf("direct macro: ta has # %d\n",_Generic((a), ta: 1, tb: 2, tc: 3 ));
printf("direct macro: tb has # %d\n",_Generic((b), ta: 1, tb: 2, tc: 3 ));
printf("direct macro: tc has # %d\n",_Generic((c), ta: 1, tb: 2, tc: 3 ));
printf("nested macro: ta has # %d\n",_Generic((a), tc: 3, default: _Generic((a), tb: 2, default: _Generic((a), ta: 1 ) ) ));
printf("nested macro: tb has # %d\n",_Generic((b), tc: 3, default: _Generic((b), tb: 2, default: _Generic((b), ta: 1 ) ) ));
printf("nested macro: tc has # %d\n",_Generic((c), tc: 3, default: _Generic((c), tb: 2, default: _Generic((c), ta: 1 ) ) ));

前三行使用非嵌套宏。在每一种情况下,传入的类型都匹配 3 种情况之一。

现在让我们看一下嵌套的情况,特别是第二个:

_Generic((b), tc: 3, default: _Generic((b), tb: 2, default: _Generic((b), ta: 1 ) ) ))

在最后一个案例中:_Generic((b), ta: 1 )b 的类型不是 ta 并且没有 default 案例,所以这是编译器抱怨的地方。请注意,所有 _Generic 表达式必须有效,即使它们最终不是被评估的情况。

您可以通过给最里面的宏一个 default 大小写来解决这个问题:

#define NESTEDMAGICNUMBER3(X) _Generic((X), \
ta: 1, \
default: 0 \
)

Why does the nested version fail, while the non-nested version works fine?

考虑NESTEDMAGICNUMBER(b))的扩展:

_Generic((b), tc: 3, default: _Generic((b), tb: 2, default: _Generic((b), ta: 1 ) ) )

编译器拒绝此表达式,因为子表达式 _Generic((b), ta: 1 ) 由于没有 b 类型的大小写(tb、a.k.a)而无效。 struct stb)。这不是将要选择的通用替代项的一部分这一事实无关紧要——程序源代码中的每个表达式和子表达式都必须有效,无论它是否有任何效果。您可以通过添加 default 替代 NESTEDMAGICNUMBER3:

来解决问题
#define NESTEDMAGICNUMBER3(X) _Generic((X), \
ta: 1, \
default: 0 \
)