Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C11 _Generic usage

I was trying to learn how to use the "new" C11 Generic expressions but I ran into a wall.

Consider the following code:

#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;
 }

This is all perfectly working, still I don't see why those default cases in "_Generic((Y), ..." are necessary while I can omit it at the end of "_Generic((X), ..." without consequences.

In fact, if I remove those two defaults I get an error (gcc 5.4.0) saying "selector of type ‘double *’ is not compatible with any association" while macro-expanding " int a1 = test(3.4, t, 1);" and the same thing with "int *" while macro-expanding test(i, s, 1)

Is "default" really necessary or am I missing something? In the first case, why the hell should it be? If I have only test_double and test_int that can be called why should I put a default case for something that should never even compile?

like image 429
Manuel Avatar asked Oct 17 '16 22:10

Manuel


4 Answers

_Generic is underspecified in the standard unfortunately. The usual interpretation seems to be that the expressions in the non-selected cases must not contain any constraint violations.

A simpler case:

int main(void)
{
    int x;

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

This code gives a constraint violation in gcc because it performs constraint checking on all of the associated expressions (not just the selected one), and x = (void)0 contains a constraint violation.


Applying this principle to your code without the default case, we see the problem is that when the macro is instantiated with Y as a variable declared as int *s, then one of the associated expressions is _Generic(s, double * : test_double), which is a constraint violation because no case is matched.

like image 160
M.M Avatar answered Oct 11 '22 09:10

M.M


TL;DR

The selection happens at compile time, but this does not mean that the other (not selected) code is discarded. It still has to be valid, and this means that ...

If default is not used and none of the type-names are compatible with the type of the controlling expression, the program will not compile.

(Source)


That was a surprising one:

Without the default case for the "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)

I get that error:

prog.c:6:30: error: '_Generic' selector of type 'int *' is not compatible with any association

Note that it complains about an int * that is not compatible! Why? Well let's look at the reported line:

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

X is of type int, and Y of type int *.

Now comes the important part: Expansions happens unconditionally. So even though X is of type int, the first association for X (when it's of type double) has to be a well formed program. So with Y being an int * the following must be well formed:

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

And since an int * is not a double *, things break here.


I was just looking through the standard (N1570 actually) and could not find anything that actually specifies exactly this behavior, though. I guess one could report a defect in this case, the standard is too vague about this. I'm trying to do this now.

like image 35
Daniel Jour Avatar answered Oct 11 '22 10:10

Daniel Jour


This happens because of nested _Generic selections, obviously.

The problem is that with _Generic, type compatibility must be satisfied at compile time for all possible generic associations. Even though only one association is selected, the ones that are not selected must still have some compatible association.

The association default handles every association that isn't compatible with the rest of associations for that _Generic selection.

Let's say you remove the default association on int: _Generic((Y),. If you do then the double association is selected, but the int association still must handle the double* type, which is usually done by default. In this case there is only the int* association and it outputs an error.

like image 1
2501 Avatar answered Oct 11 '22 09:10

2501


It is order of precedence. In Your code the two _Generic((Y) are run before _Generic((X). They are in parentheses. The compiler is eager and run both for test(3.4, t, 1) and test(i, s, 1).

I would rewrite the code a little.

#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);
}
like image 1
Michas Avatar answered Oct 11 '22 11:10

Michas