Может ли ключевое слово c11 'Generic' использоваться в gcc _Static_assert - PullRequest
4 голосов
/ 19 сентября 2019

Я понимаю, для чего нужно использовать 'Generic' в C11, и я хотел бы использовать его в статическом утверждении, чтобы гарантировать, что два пользовательских типа (typedefs) - это один и тот же первичный тип.

Я создал макрос, который отображает каждый тип примитива на перечисляемое значение, и проверил, что он работает как нужно.Однако, когда я пытаюсь сравнить равенство двух результирующих макросов из двух типов в статическом утверждении, я получаю ошибку компилятора.Когда вы закомментируете статическое утверждение, код работает как положено.

Похоже, что статическое утверждение оценивается компилятором ПЕРЕД оценкой общего расширения.Может ли это быть так?И куда я могу обратиться, чтобы проверить поведение этого?

Пример кода :

#include <stdbool.h>
#include <stdio.h>
#include <stdint.h>


typedef enum
{
    UTIL_TYPE_ENUM_BOOL,
    UTIL_TYPE_ENUM_CHAR,
    UTIL_TYPE_ENUM_SCHAR,
    UTIL_TYPE_ENUM_UCHAR,
    UTIL_TYPE_ENUM_SHORT,
    UTIL_TYPE_ENUM_USHORT,
    UTIL_TYPE_ENUM_INT,
    UTIL_TYPE_ENUM_UINT,
    UTIL_TYPE_ENUM_LONG,
    UTIL_TYPE_ENUM_ULONG,
    UTIL_TYPE_ENUM_LONG_LONG,
    UTIL_TYPE_ENUM_ULONG_LONG,
    UTIL_TYPE_ENUM_FLOAT,
    UTIL_TYPE_ENUM_DOUBLE,
    UTIL_TYPE_ENUM_LONG_DOUBLE,
    UTIL_TYPE_ENUM_OTHER,
} UtilTypeEnum_t;


// returns the enumerated value representing a primitive type
#define UTIL_TYPE_GET_TYPE_ENUM(x) _Generic((x), \
    _Bool: UTIL_TYPE_ENUM_BOOL, \
    char: UTIL_TYPE_ENUM_CHAR, \
    signed char: UTIL_TYPE_ENUM_SCHAR, \
    unsigned char: UTIL_TYPE_ENUM_UCHAR, \
    short int: UTIL_TYPE_ENUM_SHORT, \
    unsigned short int: UTIL_TYPE_ENUM_USHORT, \
    int: UTIL_TYPE_ENUM_INT, \
    unsigned int: UTIL_TYPE_ENUM_UINT, \
    long int: UTIL_TYPE_ENUM_LONG, \
    unsigned long int: UTIL_TYPE_ENUM_ULONG, \
    long long int: UTIL_TYPE_ENUM_LONG_LONG, \
    unsigned long long int: UTIL_TYPE_ENUM_ULONG_LONG, \
    float: UTIL_TYPE_ENUM_FLOAT, \
    double: UTIL_TYPE_ENUM_DOUBLE, \
    long double: UTIL_TYPE_ENUM_LONG_DOUBLE, \
    default: UTIL_TYPE_ENUM_OTHER)


typedef int32_t foo_t;
typedef float bar_t;

// IF YOU COMMENT OUT THE STATIC ASSERT, THE CODE WILL COMPILE AND WORKS AS EXPECTED
_Static_assert((UTIL_TYPE_GET_TYPE_ENUM(foo_t)==UTIL_TYPE_GET_TYPE_ENUM(bar_t)),"ERROR");

int main(void)
{
    foo_t foo;
    bar_t bar;

    printf("foo's type = %d\n", UTIL_TYPE_GET_TYPE_ENUM(foo));    
    printf("bar's type = %d\n", UTIL_TYPE_GET_TYPE_ENUM(bar));

    if (UTIL_TYPE_GET_TYPE_ENUM(foo) != UTIL_TYPE_GET_TYPE_ENUM(bar))
    {
        printf("Not the same type!\n");
    }
    else
    {
        printf("Same type!\n");
    }
    return 0;
}

#endif 

Ошибка компилятора:

$ gcc foo.c
foo.c:35:49: error: expected expression before ‘,’ token
 #define UTIL_TYPE_GET_TYPE_ENUM(x) _Generic((x), \
                                                 ^
foo.c:77:17: note: in expansion of macro ‘UTIL_TYPE_GET_TYPE_ENUM’
 _Static_assert((UTIL_TYPE_GET_TYPE_ENUM(foo_t)==UTIL_TYPE_GET_TYPE_ENUM(bar_t)),"ERROR");
                 ^~~~~~~~~~~~~~~~~~~~~~~
foo.c:35:49: error: expected expression before ‘,’ token
 #define UTIL_TYPE_GET_TYPE_ENUM(x) (_Generic((x), \
                                                 ^
foo.c:77:49: note: in expansion of macro ‘UTIL_TYPE_GET_TYPE_ENUM’
 _Static_assert((UTIL_TYPE_GET_TYPE_ENUM(foo_t)==UTIL_TYPE_GET_TYPE_ENUM(bar_t)),"ERROR");
                                                 ^~~~~~~~~~~~~~~~~~~~~~~
foo.c:77:16: error: expression in static assertion is not an integer
 _Static_assert((UTIL_TYPE_GET_TYPE_ENUM(foo_t)==UTIL_TYPE_GET_TYPE_ENUM(bar_t)),"ERROR");

Ответы [ 3 ]

4 голосов
/ 19 сентября 2019

Аргументом для выбора _Generic должно быть допустимое выражение C, тип которого затем проверяется.Вы предоставляете имя типа, которое просто не является выражением.

Чтобы получить выражение нужных вам типов, вы можете использовать составной литерал:

_Static_assert((UTIL_TYPE_GET_TYPE_ENUM((foo_t){0})==UTIL_TYPE_GET_TYPE_ENUM((bar_t){0})),"ERROR");

(foo_t){0} и (bar_t){0} теперь являются выражениями типов, которые вы хотите сравнитьи так может использоваться в общем выборе.

3 голосов
/ 20 сентября 2019

Вы можете достичь того, что вы хотите, или, по крайней мере, чего-то близкого, с помощью

#define SAME_TYPE(t1,t2) _Generic((t1){0}, t2: 1, default: 0)
_Static_assert(SAME_TYPE(foo_t, bar_t));

Это не включает перечисления, которые предполагают конечный набор типов (не поддерживающий структурные типы и т. Д.) И независит от расширения "GNU C" typeof (которое не является частью языка C).

Это работает для типов , а не для выражений.Его легко распространить на случай, когда один аргумент является типом, а другой - выражением.Если вам нужно утверждать, что два выражения имеют один и тот же тип, это, по крайней мере, несколько сложнее сделать чисто в C, и это может быть невозможно.Если вы находитесь в особом случае, когда они являются переменными, выражение

1 ? &var1 : &var2

является нарушением ограничения, если var1 и var2 имеют разные типы, но, к сожалению, GCC воспринимает это как предупреждениепо умолчанию, а не ошибка.Я не знаю, как превратить это в ошибку без полного -Werror, потому что, похоже, он не входит в свою собственную группу предупреждений, а только в анонимные предупреждения по умолчанию ...

0 голосов
/ 19 сентября 2019

Как уже отмечалось в другом ответе, вы не можете передать тип в качестве первого аргумента _Generic.Вы должны предоставить выражение, и компилятор определит, совпадает ли тип выражения со списком.

Возможно упростить ваш код.Вам не нужно использовать эти перечисления. поддерживает расширение typeof, которое позволяет вам делать что-то вроде:

#define EXPR_HAVE_SAME_TYPE(E1, E2) \
        _Generic((E1), typeof(E2): 1, default: 0)

_Static_assert(EXPR_HAVE_SAME_TYPE(foo, foo), "...");
_Static_assert(!EXPR_HAVE_SAME_TYPE(foo, bar), "...");

Это можно использовать для типов, если вы создаете выражения:

#define SAME_TYPE(T1, T2) \
        EXPR_HAVE_SAME_TYPE(*(T1 *)0, *(T2 *)0)

_Static_assert(SAME_TYPE(foo_t, foo_t), "...");
_Static_assert(!SAME_TYPE(foo_t, bar_t), "...");

Подобное понятие упоминалось в комментариях Р. и ответ .

...