Programmation orientée object en C

Dupliqués possibles:
Pouvez-vous écrire du code orienté object en C?
Motif orienté object en C?

Je me souviens avoir lu il y a quelque temps à propos de quelqu’un (je pense que c’était Linus Torvalds) parlant de la façon dont C ++ est un langage horrible et de la façon dont on peut écrire des programmes orientés object avec C. tous les concepts orientés object se retrouvent dans C. Certaines choses sont assez évidentes. Par exemple:

  1. Pour émuler des fonctions membres, vous pouvez placer des pointeurs de fonctions dans des structures.
  2. Pour émuler un polymorphism, vous pouvez écrire une fonction qui prend un nombre variable d’arguments et faire du vaudou en fonction, par exemple, de la sizeof des parameters.

Comment pouvez-vous émuler l’encapsulation et l’inheritance?

Je suppose que l’encapsulation pourrait en quelque sorte être émulée en ayant une structure nestede qui stocke des membres privés. Il serait assez facile de se déplacer, mais pourrait être nommé PRIVATE ou quelque chose de tout aussi évident pour signaler qu’il n’est pas destiné à être utilisé en dehors de la structure. Qu’en est-il de l’inheritance?

Vous pouvez implémenter un polymorphism avec des fonctions régulières et des tables virtuelles (vtables). Voici un joli système que j’ai inventé (basé sur C ++) pour un exercice de programmation: texte alt

Les constructeurs allouent de la mémoire puis appellent la fonction init de la classe où la mémoire est initialisée. Chaque fonction init doit également contenir une structure vtable statique contenant les pointeurs de fonction virtuelle (NULL pour pure virtual). Les fonctions init de classe dérivées appellent la fonction init de la superclasse avant de faire autre chose.

Une très belle API peut être créée en implémentant les wrappers de fonctions virtuelles (à ne pas confondre avec les fonctions pointées par les vtables) comme suit (ajoutez static inline devant, si vous le faites dans l’en-tête):

 int playerGuess(Player* this) { return this->vtable->guess(this); } 

L’inheritance simple peut être fait en abusant de la disposition binary d’une structure: texte alt

Notez que l’inheritance multiple est plus compliqué car vous devez souvent ajuster la valeur du pointeur lors de la conversion entre les types de la hiérarchie.

D’autres données spécifiques à un type peuvent également être ajoutées aux tables virtuelles. Les exemples incluent des informations de type à l’exécution (par exemple, le nom du type sous forme de chaîne), la liaison à la superclasse vtable et à la chaîne du destructeur. Vous voulez probablement des destructeurs virtuels où le destructeur de classe dérivé rétrograde l’object dans sa super classe, puis appelle récursivement le destructeur de celui-ci, jusqu’à ce que le destructeur de la classe de base soit atteint et que cela libère finalement la structure.

L’encapsulation a été effectuée en définissant les structures dans player_protected.h et en implémentant les fonctions (désignées par la vtable) dans player_protected.c, et de la même manière pour les classes dérivées, mais cela est assez maladroit et dégrade les performances (car les wrappers virtuels ne peuvent pas être mis à en-têtes), alors je le déconseille.

Avez-vous lu la “bible” sur le sujet? Voir Object Oriented C …

Comment pouvez-vous émuler l’encapsulation et l’inheritance?

En fait, l’encapsulation est la partie la plus facile. L’encapsulation est une philosophie de conception , elle n’a rien à voir avec le langage et avec tout ce que vous pensez des problèmes.

Par exemple, l’API Windows FILE est complètement encapsulé. Lorsque vous ouvrez un fichier, vous récupérez un object opaque contenant toutes les informations d’état du fichier “object”. Vous remettez cette poignée à chacun des fichiers io apis. L’encapsulation est en réalité bien meilleure que C ++ car il n’y a pas de fichier d’en-tête public que les gens peuvent regarder et voir les noms de vos variables privées.

L’inheritance est plus difficile, mais ce n’est pas du tout nécessaire pour que votre code soit orienté object. À certains égards, l’agrégation est préférable à l’inheritance, et l’agrégation est tout aussi simple en C qu’en C ++. voyez ceci par exemple.

En réponse à Neil, consultez Wikipedia pour une explication de la raison pour laquelle l’inheritance n’est pas nécessaire pour le polymorphism.

Nous, les anciens, avons écrit du code orienté object des années avant que les compilateurs C ++ soient disponibles, c’est un état d’esprit et non un ensemble d’outils.

Le framework CoreFoundation basé sur C d’Apple a en fait été écrit pour que ses “objects” puissent doubler en tant qu’objects dans Objective-C, un langage OO réel. Un sous-ensemble assez important du framework est open source sur le site d’Apple en tant que CF-Lite . Peut-être une étude de cas utile dans un cadre majeur au niveau du système d’exploitation réalisé de cette manière.

A partir d’une altitude un peu plus élevée et en considérant le problème de manière plus ouverte que ce que peut suggérer le POO, la programmation orientée object implique de penser aux objects comme aux données avec des fonctions associées. Cela ne signifie pas nécessairement qu’une fonction doit être physiquement attachée à un object comme c’est le cas dans les langages populaires qui supportent le paradigme de la POO, par exemple en C ++:

 struct T { int data; int get_data() const { return data; } }; 

Je suggère de regarder de plus près le système d’object et de type de GTK + . C’est un exemple shiny de POO réalisé en langage de programmation C:

GTK + implémente son propre système d’object personnalisé, qui offre des fonctionnalités standard orientées object telles que l’inheritance et la fonction virtuelle

L’association peut aussi être contractuelle et conventionnelle.

En ce qui concerne les techniques d’encapsulation et de masquage des données, les plus simples et les plus courantes sont Opaque Pointer (ou Opaque Data Type) – vous pouvez le faire circuler, mais pour charger ou stocker des informations, vous devez appeler la fonction object caché derrière ce pointeur opaque.

Un autre, similaire mais différent, est le type Shadow Data – cochez ce lien où Jon Jagger donne une excellente explication de cette technique peu connue.

Les bibliothèques gtk et glib utilisent des macros pour transtyper des objects en différents types.
add_widget (GTK_WIDGET (myButton));
Je ne peux pas dire comment ça se passe mais vous pouvez lire leur source pour savoir exactement comment cela se passe.

Jetez un coup d’œil à la façon dont la couche VFS fonctionne dans le kernel Linux pour un exemple de modèle d’inheritance. Les opérations de fichiers pour les différents systèmes de fichiers “héritent” d’un ensemble de fonctions d’opérations de fichiers génériques (par exemple generic_file_aio_read() , generic_file_llseek() …), mais peuvent également les remplacer par leurs propres implémentations ( ntfs_file_aio_write() ).

Vraiment regarder Objective-C.

 typedef struct objc_object { Class isa; } *id; typedef struct objc_class { struct objc_class *isa; struct objc_class *super_class const char *name; long version; long info long instance_size; struct objc_ivar_list *ivars; struct objc_method_list **methodLists; struct objc_cache *cache; struct objc_protocol_list *protocols; } *Class; 

Comme vous pouvez le voir, les informations d’inheritance, ainsi que d’autres détails, sont contenues dans une structure struct (la classe peut également être traitée comme un object).

Objective-C souffre de la même manière que C ++ avec l’encapsulation en ce sens que vous devez déclarer vos variables publiquement. Straight C est beaucoup plus flexible en ce sens que vous pouvez simplement renvoyer des pointeurs vides auxquels seul votre module a un access interne, de sorte que l’encapsulation est bien meilleure.

Une fois, j’ai écrit un programme de peinture C de style OO de base dans le cadre d’un cours graphique – je ne suis pas allé aussi loin que la déclaration de classe, j’ai simplement utilisé un pointeur vtable comme élément principal de la structure et La bonne chose à propos de jouer avec des vtables à un niveau si bas est que vous pouvez modifier le comportement de la classe à l’exécution en modifiant quelques pointeurs ou en modifiant dynamicment la classe des objects. Il était assez facile de créer toutes sortes d’objects hybrides, de faux inheritances multiples, etc.

Bel article et discussion concernant Objective-C ici:

http://cocoawithlove.com/2009/10/objective-c-niche-why-it-survives-in.html

Pour un bon exemple de programmation orientée object en langage C, regardez la source de POV-Ray il y a plusieurs années – la version 3.1g est particulièrement bonne. Bien sûr, les “objects” étaient structurés avec des pointeurs de fonction. Les macros étaient utilisées pour fournir les méthodes et les données de base pour un object abstrait, et les classes dérivées étaient des structures qui commençaient par cette macro. Il n’y avait aucune tentative de traiter avec privé / public, cependant. Les choses à voir étaient dans les fichiers .h et les détails d’implémentation étaient dans les fichiers .c, principalement, sauf pour de nombreuses exceptions.

Il y avait quelques astuces que je ne vois pas comment on pourrait les transférer en C ++ – comme convertir une classe en une autre mais similaire à la volée en réaffectant simplement des pointeurs de fonctions. Simple pour les langages dynamics d’aujourd’hui. J’ai oublié les détails; Je pense que cela aurait pu être des objects intersection et union CSG.

http://www.povray.org/

Un morceau d’histoire intéressant. Cfront , l’implémentation C ++ d’origine, génère le code C, puis requirejs un compilateur C pour générer le code final. Donc, tout ce qui pourrait être exprimé en C ++ pourrait être écrit en C.

Une façon de gérer l’inheritance est d’avoir des structures nestedes:

 struct base { ... }; void method_of_base(base *b, ...); struct child { struct base base_elements; ... }; 

Vous pouvez ensuite faire des appels comme ceci:

 struct child c; method_of_base(&c.b, ...); 

Vous pourriez vouloir regarder Objective-C, c’est à peu près ce que ça fait. C’est juste un frontal qui comstack le code Objective-C OO en C.