Переопределение typedef

Я, возможно, делаю это неправильно, и это вопрос о том, почему он работает в одном компиляторе, а не в другом.

У меня есть большое приложение C, и я стараюсь следовать стилю не включая заголовочные файлы из других файлов заголовков. Вместо этого, используя форвардные объявления; таким образом, я пытаюсь сделать следующее.

// in A.h
typedef struct A_ A;
typedef struct B_ B;
struct A_ {
    double a;
    B *b;
};

// in B.h
typedef struct B_ B;
struct B_ {
    int c;
};

// in C.h
typedef struct A_ A;
typedef struct B_ B;
void function_do_something(A*, B*);

// in C.c
#include "A.h"
#include "B.h"
#include "C.h"
void function_do_something(A* a, B* b) {
    ...
}

Эта парадигма компилируется и запускается в Ubuntu 11.10 gcc - но она дает компромиссные ошибки в OpenSUSE gcc, которые говорят "переопределить typedef".

Я делаю свое развитие в Ubunutu и поэтому не понял, что эта парадигма может быть неправильной. Разве это просто неправильно, и Ubuntu gcc слишком хорош?

Ответ 1

Я был удивлен этим, потому что я уверен, что повторное использование одинакового typedef в той же области действия является законным в С++, но, по-видимому, оно не является законным в C.

Во-первых, имена typedef не имеют привязки:

ISO/IEC 9899: 1999 + TC3 6.2.6/6:

Следующие идентификаторы не имеют связи: идентификатор, объявленный как нечто иное, чем объект или функция [...]

и 6.7/3:

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

Поэтому вам нужно убедиться, что каждое объявление typedef появляется только один раз в области файлов в каждой единицы перевода.

Ответ 2

Отсутствует одна часть идиомы. Передовые декларации не зависят от определений, поэтому они должны быть в отдельном файле заголовка.

// a_fwd.h

#ifndef A_FWD_H
#define A_FWD_H

typedef struct A_ A;

#endif

// a.h

#ifndef A_H
#define A_H

#include "a_fwd.h"

struct A_ {
};

#endif

Теперь всегда безопасно включать любые заголовки в любом порядке.


Нельзя иметь два определения чего-либо. Typedef - это определение, а не просто объявление, поэтому один компилятор был довольно слабым, чтобы позволить избыточность.

Ответ 3

Как раз в стиле, я бы поставил typedef после структуры. то есть:.

struct B_ {
    int c;
};
typedef struct B_ B;

Таким образом вы говорите: "вот B_, и теперь я хочу называть его" B ". Может быть, иначе обманывать что-то в компиляторе.

Ответ 4

Компилятор ubuntu слишком мягкий; вы не можете напечатать одно и то же дважды. В том стиле, о котором вы говорите, упорядочение включений имеет важное значение и обычно упоминается как комментарий в файле заголовка или в документации. В этом случае у вас будет:

//A.h
typedef struct A A;
struct A {
    double a;
    B* b;
};

// B.h
typedef struct B B;
struct B {
    int c;
};

// C.h
void function_do_something(A*, B*);

// C.c
#include "B.h"
#include "A.h"
#include "C.h"

void function_do_something(A* a, B* b){ ... }

Вы можете заметить, что это будет беспорядочно в случае циклических зависимостей.

Ответ 5

Вы переопределяете A и B, написав один и тот же оператор в нескольких файлах заголовков. Одним из решений было бы отказаться от typedef от A и B от A.h и B.h и использовать ваш C.h как есть.

Ответ 6

Как уже указывали другие, вы не можете переопределять типы в C. Эта ошибка в основном указывает на то, что могут быть зацикленные включения или другие логические недостатки. Чтобы этого избежать, наилучшей практикой является блокировка файлов, например,

#ifndef __HEADER_H__
#define __HEADER_H__

// Your code goes here

#endif

Таким образом ненужные включения будут исключены этой блокировкой.
В вашем примере вам нужно включить B в и A в C. Включение B в C не будет иметь эффекта и удовлетворит компилятор

Ответ 7

Вы определяете несколько раз одно и то же.

Вы можете распространять его по нескольким файлам заголовков, просто нужно убедиться, что есть некоторый B, видимый до того, как определена структура _A.

Этот код работает:

#include <stdio.h>

typedef struct _B B;
typedef struct _A A;

struct _A {
    double a;
    B *b;
};

struct _B {
    int c;
};

void function_do_something(A* a, B* b)
{
    printf("a->a (%f) b->c (%d)\n", a->a, b->c);
}

int main()
{
   A a;
   B b;

  a.a = 3.4;
  b.c = 34;

  function_do_something(&a, &b);

  return 0;
}

Вывод:

> ./x
a->a (3.400000) b->c (34)

EDIT: обновлено для C

EDIT 2: распространение в несколько файлов заголовков

b.h:

#ifndef B_H
#define B_H

struct _B {
    int c;
};

#endif

хиджры:

#ifndef A_H
#define A_H

typedef struct _B B;

struct _A {
    double a;
    B *b;
};

typedef struct _A A;

#endif

main.c:

#include <stdio.h>

#include "a.h"
#include "b.h"

void function_do_something(A* a, B* b)
{
    printf("a->a (%f) b->c (%d)\n", a->a, b->c);
}

int main()
{
   A a;
   B b;

  a.a = 3.4;
  b.c = 34; 

  function_do_something(&a, &b);

  return 0;
}