GCC Форвардные декларации и __may__alias - PullRequest
0 голосов
/ 05 октября 2010

Я пытаюсь переслать объявление класса с атрибутом _ may _alias, но GCC выдает ошибку при попытке сделать это:

struct __attribute__((__may_alias__)) MyType;
MyType* foo();

typedef struct  __attribute__((__may_alias__)) MyType { ... } MyType;
MyType* foo() {}

выдает ошибку: testc.c: 4: ошибка: переопределение typedef ‘A’
testc.c: 1: примечание: предыдущая декларация «A» была здесь
testc.c: 5: ошибка: конфликтующие типы для 'foo' '
testc.c: 2: note: предыдущее объявление «foo» было здесь

Есть ли способ сделать это?

Ответы [ 2 ]

2 голосов
/ 05 октября 2010

C не позволяет сделать typedef дважды. Кроме того, вы должны различать предварительное объявление struct и typedef. Самый простой способ получить это - использовать тот же токен, что и тег struct и идентификатор typedef. Без атрибута вещи в стандарте C это будет выглядеть так:

/* this is a forward declaration of struct and typedef */
typedef struct MyType MyType;
MyType* foo(void);

/* declare the struct as a struct */
struct MyType { };
MyType* foo(void) { return NULL; }

Теперь идет игра с атрибутами. Вы должны выяснить, к чему это относится к декларации struct или typedef. Я предполагаю, что struct, но быстрый просмотр информации gcc должен показать вам это.

/* this is a forward declaration of struct and typedef */
typedef __attribute__((__may_alias__)) struct MyType MyType;

/* declare the struct as a struct */
__attribute__((__may_alias__)) struct MyType { };
0 голосов
/ 06 августа 2018

После нескольких часов экспериментов кажется, что ограничение ошибка в GCC. Более новые GCC не имеют этой проблемы.

1. Предварительное объявление, нет may_alias (неправильное поведение)

Вот минимальная демонстрация проблемы строгого алиасинга:

#include <stdio.h>

struct MyType;  // Forward declaration here, without may_alias.

void foo(struct MyType *, int *b);

struct MyType {
    short a;
};  // Full definition here, without may_alias.

void f(struct MyType *my_type, int *b)
{
    *b = 1;
    my_type->a = 0;

    if (*b == 1) {
        printf("Strict aliasing problem\n");
    }
}

int main(void) {
    int b;
    f((struct MyType *)&b, &b);

    return 0;
}

Скомпилируйте его с помощью GCC 5.4.0:

$ gcc -O2 -o main main.c
$ ./main
Strict aliasing problem

2. Нет предварительного объявления, нет may_alias (неправильное поведение)

То же, что и выше.

3. Предварительное объявление, may_alias (не компилируется)

struct __attribute__((may_alias)) MyType;  // Forward declaration here, with may_alias.

struct __attribute__((may_alias)) MyType {
    short a;
};  // Full definition here, with may_alias.

Скомпилируйте его с помощью GCC 5.4.0:

$ gcc -O2 -o main main.c
main.c:11:10: error: conflicting types for ‘foo’
     void foo(struct MyType *my_type, int *b)
          ^
main.c:5:10: note: previous declaration of ‘foo’ was here
     void foo(struct MyType *, int *b);

Похоже, что GCC думает, что struct MyType; - это другой тип. Однако я не нашел способа добавить атрибут may_alias в предварительную декларацию. В соответствии с заказом документов :

Это игнорируется, если содержимое структуры, объединения или перечислимого типа не определено в спецификаторе, в котором используется список спецификаторов атрибута, то есть в таких случаях, как struct attribute ((foo )) панель без открывающей скобки.

Потенциальный обходной путь может быть объявлен foo следующим образом:

void foo(struct MyType __attribute__((may_alias)) *, int *b);

Однако это не очень хорошее решение, похоже, синтаксис может еще не поддерживаться :

Обратите внимание, что это не работает с большинством атрибутов; например, использование указанных выше атрибутов «align» и «noreturn» пока не поддерживается.

И хотя он компилируется, атрибут may_alias не работает:

$ gcc -O2 -o main main.c
$ ./main
Strict aliasing problem

4. Нет предварительной декларации, may_alias (хорошо)

Это единственный способ, который работает.

$ gcc -O2 -o main main.c
$ ./main
$

Однако в моем случае требуется предварительное объявление. Мой обходной путь должен использовать void * вместо struct MyType *:

// In the .h file, no definition of MyType yet.
void foo(void *my_type, int *b);

// In the .c file, has the definition of MyType.
void foo(void *t, int *b)
{
    struct MyType *my_type = t;

    // ...
}

Это совсем не элегантно, но единственный способ, который работает на данный момент.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...