C11 _通用用法

时间:2016-10-17 22:20:31

标签: c generics macros default c11

我试图学习如何使用" new" 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;
 }

这一切都完美无缺,但我还是不明白为什么这些默认情况在&#34; _Generic((Y),...&#34;是必要的,而我可以在结束时省略它&#34; _Generic((X),...&#34;没有后果。

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

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

4 个答案:

答案 0 :(得分:5)

遗憾的是,

_Generic在标准中没有明确规定。通常的解释似乎是非选定案例中的表达式不得包含任何约束违规。

一个更简单的案例:

int main(void)
{
    int x;

    _Generic(0, int: x = 5, float: x = (void)0);
}

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

在没有默认情况下将此原则应用于您的代码,我们发现问题是当使用Yint *s实例化为声明为_Generic(s, double * : test_double)的变量时,其中一个关联表达式为{ {1}},这是一种约束违规,因为没有案例匹配。

答案 1 :(得分:4)

TL; DR

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

  

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

(Source)

这是一个令人惊讶的:

没有“first 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)

我收到了这个错误:

  

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

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

    int a2 = test(i, s, 1);

X类型为intY类型为int *

现在是重要的部分:扩展发生无条件。因此,即使X属于int类型,X的第一个关联(当类型为double时)也必须是格式良好的程序。因此,如果Yint *,则必须确保以下内容:

_Generic((Y), \
             double * :  test_double \
              ), \

由于int *不是double *,因此事情会在这里破裂。

我只是在查看标准(实际上是N1570)并且找不到实际上正确指定此行为的任何内容。我想在这种情况下可以报告一个缺陷,标准对此太过模糊。我现在正试图这样做。

答案 2 :(得分:1)

这显然是因为嵌套的_Generic选择。

问题是,使用_Generic时,必须在编译时满足所有可能的泛型关联的类型兼容性。即使只选择了一个关联,但未选择的关联仍必须具有一些兼容的关联。

关联默认值处理与该_Generic选择的其余关联不兼容的每个关联。

假设您删除int: _Generic((Y),上的默认关联。如果这样做,则选择双关联,但int关联仍然必须处理double *类型,这通常是默认情况下完成的。在这种情况下,只有int *关联,它输出错误。

答案 3 :(得分:1)

是优先顺序。在您的代码中,两个_Generic((Y)_Generic((X)之前运行。它们在括号中。编译器非常渴望并为test(3.4, t, 1)test(i, s, 1)运行。

我会稍微改写一下代码。

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

static void test_error(void)
{
    /* This function should be optimised away by compiler. */
    assert(0);
}