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.
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).
typedef struct Object Object_t; typedef struct Klass Klass_t;
#include "typedefs.h" struct Klass { void (*_initialize)(Object_t *object, Klass_t *klass); };
#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.
struct Object
en prototipo 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);
#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.
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
.
#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 */
#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 */
#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.