C11 _通用用法

Man*_*uel 6 c generics macros default c11

我试图学习如何使用"新"C11通用表达式,但我碰到了一堵墙.

请考虑以下代码:

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

#define test(X, Y, c) \
    _Generic((X), \
        double:     _Generic((Y), \
                        double * :  test_double, \
                        default: test_double \
                    ), \
        int:        _Generic((Y), \
                        int * : test_int, \
                        default: test_int \
                    ) \
    ) (X, Y, c)


int test_double(double a, double *b, int c);
int test_int(int a, int *b, int c);

int test_double(double a, double *b, int c) { return 1; }
int test_int(int a, int *b, int c) { return 2; }

int main()
{
    double *t = malloc(sizeof(double));
    int *s = malloc(sizeof(int));
    int a1 = test(3.4, t, 1);
    int i = 3;
    int a2 = test(i, s, 1);
    printf("%d\t", a1);
    printf("%d\n", a2);
    return 0;
 }
Run Code Online (Sandbox Code Playgroud)

这一切都完美有效,但我不明白为什么"_Generic((Y),..."中的默认情况是必要的,而我可以在"_Generic((X),..."的末尾省略它)没有后果.

事实上,如果我删除这两个默认值,我会得到一个错误(gcc 5.4.0)说"类型'double*'的选择器与任何关联都不兼容"而宏扩展"int a1 = test(3.4,t,1 );" 宏扩展测试(i,s,1)与"int*"相同

"默认"真的有必要还是我错过了什么?在第一种情况下,为什么它应该是?如果我只有test_double和test_int可以调用,为什么我应该为一些永远不会编译的东西放置一个默认情况?

Dan*_*our 5

TL; 博士

选择发生在编译时,但这意味着其他(未选择的)代码被丢弃。它仍然必须有效,这意味着......

如果不使用 default 并且没有任何类型名称与控制表达式的类型兼容,则程序将无法编译。

(来源)


这是一个令人惊讶的:

没有“第一个 Y”的默认情况:

#define test(X, Y, c) \
    _Generic((X), \
        double:     _Generic((Y), \
                        double * :  test_double \
                    ), \
        int:        _Generic((Y), \
                        int * : test_int, \
                        default: test_default \
                    ) \
    ) (X, Y, c)
Run Code Online (Sandbox Code Playgroud)

我得到那个错误:

prog.c:6:30: 错误:“int *”类型的“_Generic”选择器与任何关联都不兼容

请注意,它抱怨int *不兼容!为什么?好吧,让我们看看报告的行:

    int a2 = test(i, s, 1);
Run Code Online (Sandbox Code Playgroud)

X是 类型intY类型int *

现在是重要的部分:扩展无条件发生。因此,即使X是 type intX(当它是 type 时double)的第一个关联也必须是一个格式良好的程序。因此,Y作为一个int *,必须很好地形成以下内容:

_Generic((Y), \
             double * :  test_double \
              ), \
Run Code Online (Sandbox Code Playgroud)

由于 anint *不是 a double *,事情就在这里破裂了。


我只是在浏览标准(实际上是 N1570),但找不到任何实际指定这种行为的内容。我想在这种情况下可以报告缺陷,标准对此过于模糊。我现在正在尝试这样做。


M.M*_*M.M 5

_Generic遗憾的是,标准中没有详细说明.通常的解释似乎是非选定案例中的表达式不得包含任何约束违规.

一个更简单的案例:

int main(void)
{
    int x;

    _Generic(0, int: x = 5, float: x = (void)0);
}
Run Code Online (Sandbox Code Playgroud)

此代码在gcc中提供约束违规,因为它对所有关联的表达式(不仅仅是选定的表达式)执行约束检查,并x = (void)0包含约束违规.


在没有默认情况下将此原则应用于您的代码,我们看到的问题是,当宏被实例Y化为声明为的变量时int *s,则关联表达式之一是_Generic(s, double * : test_double),因为没有匹配的情况,这是一个约束违规.