Estructura C typedef con Declaraciones Adelante

Estaba usando typedef para estructuras en todas partes en mi aplicación. Entonces empecé a refactorizar en varios archivos de encabezado cuando comenzó a volverse torpe. Noté que necesitaba reenviar declarar Object, y Klass. Bueno, para mi sorpresa, no pude reenviar Declarar Objeto, o Klass. Esto se debe a que, como se puede ver en la estructura de Object y Klass, estoy usando un typedef de Object y Klass.

//Klass.h typedef struct Klass Klass_t; struct Klass { void (*_initialize)(Object_t* object, Klass_t* klass); }; //Object.h typedef struct Object Object_t; struct Object { Klass_t* _klass; }; 

Al principio, usar typedef fue genial. Pero tratando de reenviar declarar objeto:

 struct Object_t; 

No funciona, ya que tendría que volver a escribir por declaraciones de funciones como:

 void (*_initialize)(struct Object_t* object, Klass_t* klass); 

Así que decidí tipearf Object dentro del archivo de encabezado Klass.h:

 typedef struct Object Object_t; 

Bueno, cuando todos los archivos de encabezado están incluidos en mi archivo Main.c, cumple:

 Object.h:5: error: redefinition of typedef 'Object_t' 

Entonces, decidí simplemente eliminar todas las definiciones de estructura y declarar explícitamente mis estructuras.

¿Hay alguna manera de tipear una estructura y reenviar la statement en otro archivo sin usar explícitamente el objeto struct?

Quiero mantener la estructura typedefs dentro del archivo de encabezado donde se declara la estructura. Si tengo que agrupar todos los typedefs dentro de un archivo de encabezado, entonces prefiero no usar typedef en absoluto. De todos modos, gracias por tu tiempo.

Recuerde que typedef es solo un nombre alternativo para un tipo. Hay una razón por la que el kernel de Linux no usa typedef s para los tipos de estructura, y lo está demostrando.

Hay un par de maneras de solucionar su problema. Observo que C11 permite varias apariciones del mismo typedef , pero supongo que está atascado con un comstackdor más antiguo que no lo admite.

TL; DR

A pesar de que el kernel de Linux no usa typedef s, por lo general uso typedef s, pero principalmente evito los tipos de estructuras de referencia mutua (no puedo pensar en ningún código donde utilicé ese tipo). Y, como Jens Gustedt señala en su respuesta , casi invariablemente utilizo las notaciones:

 typedef struct SomeTag SomeTag; 

de modo que el nombre de tipo y la etiqueta de estructura son los mismos (están en diferentes espacios de nombres). Esta operación no es necesaria en C ++; cuando define la struct SomeTag o la class SomeTag , el nombre SomeTag convierte en un nombre de tipo sin la necesidad de un typedef explícito (aunque un typedef no hace más que revelar que el autor tiene más experiencia en C que en C ++, o el código se originó como C código).

También observo que los nombres que comienzan con un guión bajo se tratan mejor como ‘reservados para la implementación’. Las reglas son un poco más complejas que eso, pero corre el riesgo de que la implementación usurpe sus nombres (y esté dentro de sus derechos para usurpar sus nombres) cuando use nombres que comiencen con un guión bajo, así que no lo haga. Del mismo modo, POSIX reserva los nombres de tipo que terminan _t para la implementación si incluye encabezados POSIX (como cuando no está comstackndo en el modo estricto Estándar C solamente). Evite crear tales nombres; Te lastimarán tarde o temprano. (No he corregido el código de abajo para solucionar ninguno de estos problemas: caveat emptor !).

En los fragmentos de código a continuación, la mayoría de las veces ignoro el código que evita las inclusiones múltiples (pero debería tenerlo en su código).

Cabecera extra

typedefs.h:

 typedef struct Object Object_t; typedef struct Klass Klass_t; 

klass.h

 #include "typedefs.h" struct Klass { void (*_initialize)(Object_t *object, Klass_t *klass); }; 

objeto.h

 #include "typedefs.h" struct Object { Klass_t *_klass; }; 

Esto funciona porque los dos nombres de tipo Klass_t y Object_t se declaran antes de ser utilizados.

Usa struct Object en prototipo

klass.h

 typedef struct Klass Klass_t; struct Object; struct Klass { void (*_initialize)(struct Object *object, Klass_t *klass); }; 

O, por consistencia, incluso podría usar:

  void (*_initialize)(struct Object *object, struct Klass *klass); 

objeto.h

 #include "klass.h" struct Object { Klass_t *_klass; }; 

Esto funciona porque (dentro de amplios límites, básicamente, si los tipos están definidos en el ámbito del archivo, no dentro de una función), la struct Object siempre se refiere al mismo tipo, independientemente de si todos los detalles están completamente definidos todavía.

GCC 4.8.2

Bajo todos de -std=c89 , -std=c99 y -std=c11 , GCC 4.8.2 acepta -std=c11 replicados, como en el código a continuación. Requiere -std=c89 -pedantic o -std=c99 -pedantic para obtener errores sobre los typedef repetidos.

Incluso sin la opción -pedantic , GCC 4.5.2 rechaza este código; sin embargo, GCC 4.6.0 y versiones posteriores lo aceptan sin la opción -pedantic .

klass.h

 #ifndef KLASS_H_INCLUDED #define KLASS_H_INCLUDED typedef struct Klass Klass_t; typedef struct Object Object_t; struct Klass { void (*_initialize)(Object_t *object, Klass_t *klass); }; #endif /* KLASS_H_INCLUDED */ 

objeto.h

 #ifndef OBJECT_H_INCLUDED #define OBJECT_H_INCLUDED typedef struct Klass Klass_t; typedef struct Object Object_t; struct Object { Klass_t *klass; }; #endif /* OBJECT_H_INCLUDED */ 

consumidor.c

 #include "klass.h" #include "object.h" Klass_t k; Object_t o; 

Tendrá que decidir si ese es un riesgo que está dispuesto a asumir para su código: cuán importante es la portabilidad y a qué versiones de C (y qué comstackdores de C) debe ser portátil.

Te estás perdiendo la palabra clave struct . Debería ser

 typedef struct Object Object_t; 

La statement hacia adelante de esa manera (pero ver más abajo) siempre debería ser posible. Este reenvío declara el identificador typedef y la etiqueta struct al mismo tiempo.

Simplemente coloque dichas declaraciones de toda su struct antes de las declaraciones reales. Siempre que use punteros para estas struct dentro de las declaraciones, todo debería estar bien, entonces.

nitpick: los nombres con un _t están reservados por POSIX. Esto significa que debe evitarlo, porque algún día en alguna plataforma podría haber un Object_t que esté predefinido y entrará en conflicto con su tipo.

Personalmente prefiero la siguiente convención

 typedef struct Object Object; 

así que la palabra Objeto, con o sin struct , siempre se refiere a la misma.