"Si vous voulez vraiment du sucre OO - allez utiliser C ++" - a été la réponse immédiate d'un de mes amis quand j'ai demandé cela. Je sais que deux choses sont complètement fausses ici. Le premier OO n'est PAS du «sucre» et le second, le C ++ n'a PAS absorbé le C.
Nous devons écrire un serveur en C (dont le front-end sera en Python), et j'explore donc de meilleures façons de gérer de gros programmes C.
La modélisation d'un grand système en termes d'objets et d'interactions d'objets le rend plus gérable, maintenable et extensible. Mais lorsque vous essayez de traduire ce modèle en C qui ne porte pas d'objets (et tout le reste), vous êtes confronté à des décisions importantes.
Créez-vous une bibliothèque personnalisée pour fournir les abstractions OO dont votre système a besoin? Des choses comme les objets, l'encapsulation, l'héritage, le polymorphisme, les exceptions, pub / sub (événements / signaux), les espaces de noms, l'introspection, etc. (par exemple GObject ou COS ).
Ou, vous utilisez simplement les constructions C de base ( struct
et les fonctions) pour approximer toutes vos classes d'objets (et autres abstractions) de manière ad hoc. (par exemple, certaines des réponses à cette question sur SO )
La première approche vous donne un moyen structuré d'implémenter l'intégralité de votre modèle en C. Mais cela ajoute également une couche de complexité que vous devez maintenir. (Rappelez-vous, la complexité était ce que nous voulions réduire en utilisant des objets en premier lieu).
Je ne connais pas la deuxième approche et son efficacité pour approximer toutes les abstractions dont vous pourriez avoir besoin.
Donc, mes questions simples sont: Quelles sont les meilleures pratiques pour réaliser une conception orientée objet en C. N'oubliez pas que je ne demande pas COMMENT le faire. Ceci et ces questions en parlent, et il y a même un livre à ce sujet. Ce qui m'intéresse le plus, ce sont des conseils / exemples réalistes qui abordent les vrais problèmes qui se présentent lorsque dong ceci.
Remarque: veuillez ne pas conseiller pourquoi C ne devrait pas être utilisé en faveur de C ++. Nous avons bien dépassé cette étape.
la source
extern "C"
et puisse être utilisée à partir de python. Vous pouvez le faire manuellement ou demander à SWIG de vous aider. Donc, le désir de frontend python n'est pas une raison pour ne pas utiliser C ++. Cela ne veut pas dire qu'il n'y a aucune raison valable de vouloir rester avec C.Réponses:
De ma réponse à Comment structurer des projets complexes en C (pas OO mais sur la gestion de la complexité en C):
De ma réponse à Quelles sont les conventions de dénomination typiques pour les fonctions publiques et privées OO C (je pense que c'est une meilleure pratique):
De plus, de nombreux outils UML sont capables de générer du code C à partir de diagrammes UML. Un open source est Topcased .
la source
Je pense que vous devez différencier OO et C ++ dans cette discussion.
L'implémentation d'objets est possible en C, et c'est assez simple - il suffit de créer des structures avec des pointeurs de fonction. C'est votre "deuxième approche", et j'irais avec elle. Une autre option consisterait à ne pas utiliser de pointeurs de fonction dans la structure, mais plutôt à transmettre la structure de données aux fonctions lors d'appels directs, en tant que pointeur "contextuel". C'est mieux, à mon humble avis, car il est plus lisible, plus facilement traçable et permet d'ajouter des fonctions sans changer la structure (facile sur l'héritage si aucune donnée n'est ajoutée). C'est en fait ainsi que C ++ implémente généralement le
this
pointeur.Le polymorphisme devient plus compliqué car il n'y a pas de prise en charge intégrée de l'héritage et de l'abstraction, vous devrez donc soit inclure la structure parent dans votre classe enfant, soit faire beaucoup de copier-coller, l'une ou l'autre de ces options est franchement horrible, même si techniquement Facile. Énorme quantité de bugs attendant de se produire.
Les fonctions virtuelles peuvent facilement être obtenues grâce à des pointeurs de fonction pointant vers différentes fonctions selon les besoins, encore une fois - très sujets aux bogues lorsqu'ils sont effectués manuellement, beaucoup de travail fastidieux pour initialiser correctement ces pointeurs.
En ce qui concerne les espaces de noms, les exceptions, les modèles, etc. - je pense que si vous êtes limité à C - vous devriez simplement y renoncer. J'ai travaillé sur l'écriture d'OO en C, je ne le ferais pas si j'avais le choix (sur ce lieu de travail, je me suis littéralement battu pour introduire le C ++, et les gestionnaires ont été "surpris" de la facilité avec laquelle il était possible de l'intégrer). avec le reste des modules C à la fin.).
Cela va sans dire que si vous pouvez utiliser C ++ - utilisez C ++. Il n'y a aucune vraie raison de ne pas le faire.
la source
Voici les bases de la création d'une orientation d'objet en C
1. Création d'objets et encapsulation
Habituellement - on crée un objet comme
object_instance = create_object_typex(parameter);
Les méthodes peuvent être définies de l'une des deux manières ici.
Notez que dans la plupart des cas,
object_instance (or object_instance_private_data)
est retourné est de typevoid *.
L'application ne peut pas référencer des membres individuels ou des fonctions de celui-ci.De plus, chaque méthode utilise ces object_instance pour la méthode suivante.
2. Polymorphisme
Nous pouvons utiliser de nombreuses fonctions et pointeurs de fonction pour remplacer certaines fonctionnalités lors de l'exécution.
par exemple - toutes les méthodes object_methods sont définies comme un pointeur de fonction qui peut être étendu aux méthodes publiques et privées.
Nous pouvons également appliquer une surcharge de fonctions dans un sens limité, en utilisant
var_args
ce qui est très similaire à la façon dont le nombre variable d'arguments est défini dans printf. oui, ce n'est pas aussi flexible en C ++ - mais c'est la manière la plus proche.3. Définition de l'héritage
La définition de l'héritage est un peu délicate mais on peut faire ce qui suit avec les structures.
ici, le
doctor
etengineer
dérive de la personne et il est possible de le transcrire à un niveau supérieur, par exempleperson
.Le meilleur exemple de ceci est utilisé dans GObject et ses objets dérivés.
4. Création de classes virtuelles Je cite un exemple concret d'une bibliothèque appelée libjpeg utilisée par tous les navigateurs pour le décodage jpeg. Il crée une classe virtuelle appelée error_manager que l'application peut créer une instance concrète et fournir en retour -
Notez ici que JMETHOD se développe dans un pointeur de fonction via une macro qui doit être chargée avec les bonnes méthodes respectivement.
J'ai essayé de dire tant de choses sans trop d'explications individuelles. Mais j'espère que les gens pourront essayer leurs propres choses. Cependant, mon intention est juste de montrer comment les choses évoluent.
De plus, il y aura de nombreux arguments selon lesquels ce ne sera pas exactement la vraie propriété de l'équivalent C ++. Je sais que OO en C ne sera pas aussi strict à sa définition. Mais en travaillant comme ça, on comprendrait certains des principes fondamentaux.
L'important n'est pas que OO soit aussi strict qu'en C ++ et JAVA. C'est que l'on peut organiser structurellement le code en pensant à OO et le faire fonctionner de cette façon.
Je recommanderais fortement aux gens de voir la vraie conception de libjpeg et les ressources suivantes
une. Programmation orientée objet en C
b. c'est un bon endroit où les gens échangent des idées
c. et voici le livre complet
la source
L'orientation des objets se résume à trois choses:
1) Conception de programme modulaire avec classes autonomes.
2) Protection des données avec encapsulation privée.
3) Héritage / polymorphisme et diverses autres syntaxes utiles comme les constructeurs / destructeurs, les modèles, etc.
1 est de loin le plus important, et il est également complètement indépendant de la langue, il s'agit de conception de programme. En C, vous faites cela en créant des "modules de code" autonomes composés d'un fichier .h et d'un fichier .c. Considérez cela comme l'équivalent d'une classe OO. Vous pouvez décider de ce qui doit être placé à l'intérieur de ce module par le bon sens, UML ou toute autre méthode de conception OO que vous utilisez pour les programmes C ++.
2 est également très important, non seulement pour protéger un accès intentionnel aux données privées, mais aussi pour se protéger contre un accès involontaire, c'est-à-dire "l'encombrement de l'espace de noms". C ++ le fait de manière plus élégante que C, mais cela peut toujours être réalisé en C en utilisant le mot-clé statique. Toutes les variables que vous auriez déclarées privées dans une classe C ++ devraient être déclarées statiques en C et placées à la portée du fichier. Ils ne sont accessibles qu'à partir de leur propre module de code (classe). Vous pouvez écrire "setters / getters" comme vous le feriez en C ++.
3 est utile mais pas nécessaire. Vous pouvez écrire des programmes OO sans héritage ou sans constructeurs / destructeurs. Ces choses sont agréables à avoir, elles peuvent certainement rendre les programmes plus élégants et peut-être aussi plus sûrs (ou l'inverse s'ils sont utilisés avec négligence). Mais ils ne sont pas nécessaires. Étant donné que C ne prend en charge aucune de ces fonctionnalités utiles, vous devrez simplement vous en passer. Les constructeurs peuvent être remplacés par des fonctions init / destruct.
L'héritage peut être fait à travers diverses astuces de structure, mais je le déconseillerais, car cela rendra probablement votre programme plus complexe sans aucun gain (l'héritage en général devrait être soigneusement appliqué, non seulement en C mais dans n'importe quelle langue).
Enfin, chaque astuce OO peut être effectuée dans le livre de C. Axel-Tobias Schreiner "Programmation orientée objet avec ANSI C" du début des années 90 le prouve. Cependant, je ne recommanderais ce livre à personne: il ajoute à vos programmes C une complexité désagréable et étrange qui ne vaut tout simplement pas la peine. (Le livre est disponible gratuitement ici pour ceux qui sont toujours intéressés malgré mon avertissement.)
Donc, mon conseil est d'implémenter 1) et 2) ci-dessus et de sauter le reste. C'est une façon d'écrire des programmes C qui a fait ses preuves depuis plus de 20 ans.
la source
Emprunter une certaine expérience des différents temps d'exécution d'Objective-C, écrire une capacité OO polymorphe dynamique en C n'est pas trop difficile (la rendre rapide et facile à utiliser, en revanche, semble toujours en cours après 25 ans). Cependant, si vous implémentez une capacité d'objet de style Objective-C sans étendre la syntaxe du langage, le code avec lequel vous vous retrouvez est assez compliqué:
objc_msgSend(object, selector, …)
. En sachant de quelle classe l'objet est une instance, il peut trouver l'implémentation correspondant au sélecteur et ainsi exécuter la fonction correcte.Cela fait partie d'une bibliothèque OO à usage général conçue pour permettre à plusieurs développeurs d'utiliser et d'étendre les classes les uns des autres, donc pourrait être exagéré pour votre propre projet. J'ai souvent conçu des projets C comme des projets orientés classes "statiques" utilisant des structures et des fonctions: - chaque classe est la définition d'une structure C spécifiant la disposition ivar - chaque instance est juste une instance de la structure correspondante - les objets ne peuvent pas être "messaged", mais des fonctions de type méthode ressemblant
MyClass_doSomething(struct MyClass *object, …)
sont définies. Cela rend les choses plus claires dans le code que l'approche ObjC mais a moins de flexibilité.Le choix des compromis dépend de votre propre projet: il semble que les autres programmeurs n'utilisent pas vos interfaces C, le choix se résume donc aux préférences internes. Bien sûr, si vous décidez de vouloir quelque chose comme la bibliothèque d'exécution objc, il existe des bibliothèques d'exécution objc multiplateforme qui fonctionneront.
la source
GObject ne cache pas vraiment de complexité et introduit sa propre complexité. Je dirais que la chose ad-hoc est plus facile que GObject à moins que vous n'ayez besoin des choses avancées de GObject comme les signaux ou la machinerie d'interface.
La chose est légèrement différente avec COS car cela vient avec un préprocesseur qui étend la syntaxe C avec certaines constructions OO. Il existe un préprocesseur similaire pour GObject, le G Object Builder .
Vous pouvez également essayer le langage de programmation Vala , qui est un langage complet de haut niveau qui se compile en C et d'une manière qui permet d'utiliser les bibliothèques Vala à partir de code C simple. Il peut utiliser soit GObject, son propre cadre d'objet ou la manière ad-hoc (avec des fonctionnalités limitées).
la source
Tout d'abord, à moins qu'il ne s'agisse de devoirs ou qu'il n'y ait pas de compilateur C ++ pour le périphérique cible, je ne pense pas que vous ayez à utiliser C, vous pouvez fournir une interface C avec une liaison C à partir de C ++ assez facilement.
Deuxièmement, j'examinerais à quel point vous vous reposerez sur le polymorphisme et les exceptions et les autres fonctionnalités que les cadres peuvent fournir, si ce n'est pas beaucoup de structures simples avec des fonctions connexes seront beaucoup plus faciles qu'un cadre complet, si une partie importante de votre le design en a besoin, puis mordez la balle et utilisez un cadre pour que vous n'ayez pas à implémenter les fonctionnalités vous-même.
Si vous n'avez pas encore vraiment de conception pour prendre la décision, faites un pic et voyez ce que le code vous dit.
enfin, il ne doit pas nécessairement être un choix ou un autre (bien que si vous utilisez le framework depuis le début, vous pouvez aussi bien le conserver), il devrait être possible de commencer avec des structures simples pour les parties simples et d'ajouter uniquement des bibliothèques comme requis.
EDIT: C'est donc une décision prise par la direction de droite permet d'ignorer le premier point.
la source