Est-il possible de faire l’inheritance d’une structure abstract / base ou de simuler quelque chose le long de ces lignes en C?

Je travaille actuellement avec un programme C qui utilise des structures composées de coordonnées xyz, mais parfois ces coordonnées peuvent faire référence à des vecteurs (type Force / Vélocité, pas la structure de données), alors que d’autres fois, il peut s’agir de la position. Je sais qu’il est possible d’utiliser simplement une structure pour toutes ces conditions différentes puisqu’elles utilisent toutes principalement le même type de données (float), mais tout simplement pour que mes calculs soient mieux organisés (plus les noms de variables et de structures) et pour éviter toute confusion. , y a-t-il un moyen de définir une structure de base qui se définit comme ayant trois flottants, d’être héritée par une autre structure qui définit plus précisément ce que la structure est supposée être (telle que la position au lieu de la vitesse, etc.)? Je sais que C n’est pas une programmation orientée object, mais il semble possible de faire ceci. Voici à quoi ressemblerait la structure de base:

struct ThreeDCartesianData { float x; float y; float z; }; 

Une structure plus spécifique hériterait de celle-ci et définirait peut-être des variables supplémentaires ou utiliserait des noms différents pour les variables. Plusieurs structures de position seront utilisées, mais je pense une seule structure de vélocité pour chaque dataset. J’ai vu des questions similaires à cela, mais elles semblent toutes faire référence à un langage de niveau supérieur (C ++, C #, etc.).

Vous pouvez utiliser un union pour cela. Votre struct principale contiendrait une union des structures “dérivées” ainsi qu’un champ “drapeau” vous indiquant quel membre de l’union est valide:

 enum { DERIVED11, DERIVED2, DERIVED3 }; struct derived1 { int x1; }; struct derived2 { char x2; }; struct derived3 { float x3; }; struct ThreeDCartesianData { float x; float y; float z; int derivedType; union { struct derived1 d1; struct derived2 d2; struct derived3 d3; } derived; }; 

Ensuite, vous pouvez les utiliser comme ceci:

 struct ThreeDCartesianData data1; data1.x=0; data1.y=0; data1.z=0; data1.derivedType = DERIVED1; data1.derived.d1.x1 = 4; 

Vous pouvez également les définir comme ceci:

 struct common { int type; float x; float y; float z; }; struct derived1 { int type; float x; float y; float z; int x1; }; struct derived2 { int type; float x; float y; float z; char x2; }; struct derived3 { int type; float x; float y; float z; float x3; }; union ThreeDCartesianData { struct common c; struct derived1 d1; struct derived2 d2; struct derived3 d3; }; 

Et utilisez-les comme ceci:

 union ThreeDCartesianData data1; data1.c.type=DERIVED1; data1.d1.x=0; data1.d1.y=0; data1.d1.z=0; data1.d1.x1 = 4; 

Si toutes les structures d’une union ont des éléments de liste initiaux du même type dans le même ordre, la norme vous permet d’accéder à ces champs à partir de n’importe quelle sous-structure en toute sécurité.

Qu’en est-il de l’utilisation de typedef s?

 typedef struct general3d { float x; float y; float z; } general3d_t; typedef general3d position; typedef general3d velocity; 

Ainsi, lorsque vous rencontrez quelque chose qui est un type de velocity , vous l’encodez dans le type de variable, mais sous le capot, il ne rest que 3 points, x , y , z . Ensuite, les lecteurs du code sauront que vous parlez d’une velocity et non d’une position . Si vous voulez devenir vraiment fou avec cela, vous masquez general3d dans un fichier d’implémentation, de sorte qu’un utilisateur ne puisse jamais instancier lui-même un general3d , puisqu’il devrait utiliser la position ou la velocity selon les besoins; cela peut être ou ne pas être raisonnable pour votre tâche / mérite l’effort supplémentaire.

EDIT: Je ne suis pas favorable au changement de nom de variable ni à l’ajout de variables directement à la même structure, mais je commencerais par aller vers une conception différente à ce stade.

D’une part, si vous avez deux struct qui ont les mêmes types sous-jacents mais nécessitent des noms différents, vous n’avez probablement que deux struct distinctes. Par exemple:

 struct point3d { float x; float y; float z; }; struct person { float age; float weight; float salary; }; 

Oui, ce sont deux flotteurs, mais leur compréhension est très différente et ils devraient pouvoir varier d’eux-mêmes si l’un ou l’autre change. Peut-être que je veux append un champ de name à la person , mais il n’y a pas d’analogue raisonnable pour un caractère char * sur point3d . Il suffit de les définir séparément s’ils veulent dire des choses différentes.

En ce qui concerne l’ajout de variables, cela ressemble à des struct contenant d’autres struct :

 struct point3d { float x; float y; float z; }; struct person { point3d position; float age; float weight; float salary; }; // access like: person.position.x; 

J’ai déjà fait ce genre de chose. J’incorpore une copie du type de base à l’avant de la structure de type dérivé. Cela imite de plus près ce que c++ pourrait faire. Voici deux méthodes que j’ai utilisées.


Utiliser un type simple:

 #define XYZDEF \ int type; \ float x; \ float y; \ float z // base type struct xyzdata { XYZDEF; }; // derived type 1 struct vector { XYZDEF; int vector_info; ... }; // derived type 2 struct position { XYZDEF; int position_info; ... }; #define BASEOF(_ptr) \ ((struct xyzdata *) (_ptr)) // vector_rotate -- rotate a vector void vector_rotate(vector *ptr) { } // position_rotate -- rotate a position void position_rotate(position *ptr) { } // xyzrotate -- rotate void xyzrotate(xyzdata *ptr) { switch (ptr->type) { case TYPE_POSITION: vector_rotate((vector *) ptr); break; case TYPE_VECTOR: position_rotate((position *) ptr); break; } } 

Utilisation d’un pointeur de table de fonction virtuelle:

 #define XYZDEF \ int type; \ vtbl *vtbl; \ float x; \ float y; \ float z // forward definitions struct xyzdata; struct vector; struct position; // virtual function table struct vtbl { void (*rotate)(struct xyzdata *); }; // base type struct xyzdata { XYZDEF; }; // derived type 1 struct vector { XYZDEF; int vector_info; ... }; // derived type 2 struct position { XYZDEF; int position_info; ... }; #define BASEOF(_ptr) \ ((struct xyzdata *) (_ptr)) // vector_rotate -- rotate a vector void vector_rotate(struct xyzdata *ptr) { struct vector *vec = (void *) ptr; ... } // position_rotate -- rotate a position void position_rotate(struct xyzdata *ptr) { struct position *pos = (void *) ptr; ... } // xyzrotate -- rotate void xyzrotate(xyzdata *ptr) { ptr->vtbl->rotate(ptr); }