Contexte / scénario
J'ai commencé à écrire une application CLI uniquement en C (mon premier programme C ou C ++ approprié qui n'était pas "Hello World" ou une variante de celui-ci). Vers la mi-parcours, je travaillais avec des "chaînes" d'entrée utilisateur (tableaux de caractères) et j'ai découvert l'objet streamer de chaîne C ++. J'ai vu que je pouvais enregistrer du code en les utilisant, alors je les ai utilisés via l'application. Cela signifie que j'ai changé l'extension de fichier en .cpp et maintenant compiler l'application avec g++
au lieu de gcc
. Donc, sur cette base, je dirais que l'application est maintenant techniquement une application C ++ (bien que 90% + du code soit écrit dans ce que j'appellerais C, car il y a beaucoup de croisement entre les deux langues étant donné mon expérience limitée de les deux). Il s'agit d'un seul fichier .cpp d'environ 900 lignes.
Facteurs importants
Je veux que le programme soit gratuit (comme en argent) et librement distribuable et utilisable par tous. Ma préoccupation est que quelqu'un regarde le code et réfléchisse à quelque chose comme:
Oh regardez le codage, c'est affreux, ce programme ne peut pas m'aider
Quand c'est potentiellement possible! Une autre question est que le code est efficace (c'est un programme pour tester la connectivité Ethernet). Aucune partie du code ne doit être si inefficace qu'elle peut gravement nuire aux performances de l'application ou de sa sortie. Cependant, je pense que c'est une question pour Stack Overflow lorsque vous demandez de l'aide avec des fonctions, des méthodes, des appels d'objet spécifiques, etc.
Ma question
Ayant (à mon avis) mélangé C et C ++ où je ne devrais peut-être pas. Dois-je chercher à tout réécrire en C ++ (par cela, je veux dire implémenter plus d'objets et de méthodes C ++ où j'ai peut-être codé quelque chose dans un style C qui peut être condensé en utilisant des techniques C ++ plus récentes), ou supprimer l'utilisation d'objets streamer de chaîne et tout ramener "en arrière" au code C? Y a-t-il une approche correcte ici? Je suis perdu et j'ai besoin de conseils sur la façon de garder cette application «bonne» aux yeux des masses, donc ils vont l'utiliser et en bénéficier.
Le code - mise à jour
Voici un lien vers le code. Il s'agit d'environ 40% de commentaires, je commente presque toutes les lignes jusqu'à ce que je me sente plus fluide. Dans la copie à laquelle j'ai lié, j'ai supprimé à peu près tous les commentaires. J'espère que cela ne rend pas la lecture trop difficile. J'espère cependant que personne ne devrait avoir besoin de bien le comprendre. Si j'ai fait des défauts de conception fatals, j'espère qu'ils devraient être facilement identifiables. Je dois également mentionner que j'écris quelques ordinateurs de bureau et ordinateurs portables Ubuntu. Je n'ai pas l'intention de porter le code sur d'autres systèmes d'exploitation.
LICENSE
fichier. Vous pourriez obtenir des commentaires intéressants.Réponses:
Commençons par le début: le code mixte C et C ++ est assez courant. Vous êtes donc dans un grand club pour commencer. Nous avons d'énormes bases de code C à l'état sauvage. Mais pour des raisons évidentes, de nombreux programmeurs refusent d'écrire au moins de nouvelles choses en C, ayant accès à C ++ dans le même compilateur, de nouveaux modules commencent à être écrits de cette façon - tout d'abord en laissant les parties existantes seules.
Ensuite, certains fichiers existants sont recompilés en C ++, et certains ponts peuvent être supprimés ... Mais cela peut prendre très longtemps.
Vous êtes un peu en avance, votre système complet est maintenant en C ++, juste la plupart est écrit en "C-style". Et vous voyez que la mixité des styles pose un problème, ce que vous ne devriez pas: C ++ est un langage multi-paradigmes supportant de nombreux styles, et leur permettant de coexister pour de bon. En fait, c'est la principale force, que vous n'êtes pas obligé de choisir un seul style. Celui qui serait ici et là sous-optimal, avec un peu de chance pas partout.
Retravailler la base de code est une bonne idée, SI elle est cassée. Ou si c'est en voie de développement. Mais si cela fonctionne (dans le sens d'origine du terme), veuillez suivre le principe d'ingénierie le plus élémentaire: s'il n'est pas cassé, ne le réparez pas. Laissez les parties froides tranquilles, mettez vos efforts là où ça compte. Sur les pièces qui sont mauvaises, dangereuses - ou dans de nouvelles fonctionnalités, et juste refaçonner les pièces pour en faire un lit.
Si vous cherchez des choses générales à aborder, voici ce qui vaut la peine d'être expulsé d'une base de code C:
Lorsque vous avez terminé, vous approchez du point où la base de code peut être raisonnablement protégée contre les exceptions, vous pouvez donc supprimer le football de code retour en utilisant des exceptions et des tentatives / captures rares dans les fonctions de haut niveau uniquement.
Au-delà de cela, il suffit d'écrire du nouveau code dans du C ++ sain, et si certaines classes naissent qui remplacent bien le code existant, prenez-les.
Je n'ai pas mentionné de choses liées à la syntaxe, utilisez évidemment des références au lieu de pointeurs dans tout le nouveau code, mais remplacer les anciennes parties C juste pour ce changement n'est pas une bonne valeur. Les conversions que vous devez adresser, éliminer tout ce que vous pouvez et utiliser des variantes C ++ dans les fonctions d'encapsulation pour le reste. Et très important, ajoutez const chaque fois que cela s'applique. Ces entrelacement avec les balles précédentes. Et consolidez vos macros et remplacez ce que vous pouvez transformer en énumération, fonction en ligne ou modèle.
Je suggère de lire les normes de codage C ++ de Sutter / Alexandrescu si ce n'est pas encore fait et de les suivre de près.
la source
En bref: vous n'allez pas en enfer, il ne se passera rien de mal, mais vous ne gagnerez pas non plus de concours de beauté.
Bien qu'il soit parfaitement possible d'utiliser C ++ comme un "meilleur C", vous perdez de nombreux avantages de C ++, mais parce que vous ne vous limitez pas à la vanille C, vous n'obtenez aucun des avantages de C (simplicité, portabilité , transparence, interopérabilité). En d'autres termes: C ++ sacrifie certaines des qualités de C pour en gagner d'autres - par exemple, la transparence de C où vous pouvez toujours voir clairement où et quand les allocations de mémoire se produisent est échangée contre les abstractions plus puissantes de C ++.
Étant donné que votre code semble fonctionner correctement maintenant, ce n'est probablement pas une bonne idée de le réécrire juste pour le plaisir: conservez-le tel qu'il est pour l'instant et changez-le en C ++ plus idiomatique au fur et à mesure, une pièce à la fois, chaque fois que vous travaillez sur une pièce donnée. Et gardez à l'esprit les leçons ainsi apprises pour votre prochain projet, surtout celui-ci: C et C ++ ne sont pas le même langage, et vous feriez mieux de faire un choix à l'avance que de décider de passer au C ++ à mi-chemin de votre projet .
la source
Le C ++ est un langage très complexe, dans le sens où il possède de nombreuses fonctionnalités. C'est également un langage qui prend en charge plusieurs paradigmes, ce qui signifie qu'il vous permet d'écrire du code entièrement procédural sans utiliser aucun objet.
Donc, parce que C ++ est si complexe, vous voyez souvent des gens utiliser un sous-ensemble limité de ses fonctionnalités dans leur code. Les débutants utilisent souvent les E / S de flux, les objets chaîne et new / delete au lieu de malloc / free, et peut-être des références au lieu de pointeurs. À mesure que vous vous familiarisez avec les fonctionnalités orientées objet, vous pouvez commencer à écrire dans un style appelé "C avec classes". Finalement, à mesure que vous en apprenez davantage sur C ++, vous commencez à utiliser des modèles, RAII , STL , des pointeurs intelligents, etc.
Le point que j'essaie de faire est que l'apprentissage du C ++ est un processus qui prend du temps. Oui, en ce moment, votre code semble avoir été écrit par un programmeur C essayant d'écrire C ++. Et puisque vous apprenez, c'est tout à fait correct. Cependant, cela me fait grincer des dents quand je vois ce genre de chose dans le code de production écrit par des programmeurs chevronnés qui devraient mieux savoir.
N'oubliez pas qu'un bon programmeur Fortran peut écrire du bon code Fortran dans n'importe quel langage. :)
la source
Il semble que vous récapituliez exactement le chemin emprunté par de nombreux anciens programmeurs C en allant en C ++. Vous l'utilisez comme un "meilleur C". Il y a vingt ans, cela avait un certain sens, mais à ce stade, il y a tellement de constructions plus puissantes en C ++ (comme la bibliothèque de modèles standard) que cela n'a plus de sens maintenant. Au minimum, vous devriez probablement faire ce qui suit pour éviter de donner des anévrismes aux programmeurs C ++ lorsque vous regardez votre code:
printf
famille.new
au lieu demalloc
.std::string
plutôtchar*
que possibleSi votre programme est court (et ~ 900 lignes semblent courtes), personnellement, je ne pense pas qu'essayer de créer un ensemble robuste de classes soit nécessaire ou même utile.
la source
La réponse acceptée ne mentionne que les avantages de la conversion de C en C ++ idiomatique, comme si le code C ++ serait dans un sens absolu meilleur que le code C. Je suis d'accord avec d'autres réponses qu'il n'est probablement pas nécessaire d'apporter des modifications drastiques au code mixte s'il n'est pas cassé.
Que le mélange de C et C ++ soit durable dépend de la façon dont le mélange est effectué. Des bogues subtils peuvent être introduits, par exemple, lorsque des exceptions sont déclenchées en code C (ce qui n'est pas sûr pour les exceptions), provoquant des fuites de mémoire ou des altérations de données. La manière la plus sûre et la plus courante consiste à envelopper des bibliothèques C ou des interfaces dans des classes, ou à les utiliser dans un autre type d'isolement dans un projet C ++.
Avant de décider de réécrire tout votre projet en C ++ idiomatique (ou C), vous devez savoir que la plupart des modifications présentées dans d'autres réponses peuvent ralentir votre programme ou provoquer d'autres effets indésirables. Par exemple:
Pour conclure, la conversion d'un code C et C ++ mixte en C ++ idiomatique devrait être considérée comme un compromis qui a beaucoup plus à faire que le simple temps de mise en œuvre et l'élargissement de votre boîte à outils avec des fonctionnalités apparemment pratiques. Il est donc très difficile de donner une réponse pour le cas général autre que "ça dépend".
la source
C'est une mauvaise forme de mélanger C et C ++. Ce sont des langues distinctes et doivent vraiment être traitées comme telles. Choisissez celui avec lequel vous êtes le plus à l'aise et essayez d'écrire du code idiomatique dans cette langue.
Si C ++ vous fait économiser beaucoup de code, restez avec C ++ et réécrivez les parties C. Je doute qu'une différence de performance soit même perceptible, si c'est ce qui vous préoccupe.
la source
Je fais des allers-retours entre C et C ++ tout le temps et je suis le type bizarre qui préfère travailler de cette façon. Je préfère le C ++ pour les choses de haut niveau tandis que j'utilise C comme un instrument contondant qui me permet de radiographier des types de données et de les traiter comme des bits et des octets avec, disons,
memcpy
que je trouve pratique pour implémenter des structures de données de bas niveau et des allocateurs de mémoire . Si vous vous retrouvez à travailler au niveau des bits et octets bruts, alors le système de type très riche de C ++ n'aide en rien du tout, et je trouve souvent plus facile d'écrire un tel code en C où je peux supposer en toute sécurité que je peux traiter tout type de données C comme de simples bits et octets.La principale chose à garder à l'esprit est le cas où ces deux langues ne s'articulent pas bien.
1. La fonction AC ne doit jamais appeler une fonction C ++ qui le peut
throw
. Étant donné le nombre d'emplacements que votre type quotidien de code C ++ peut implicitement exécuter dans une exception, cela signifie généralement que vos fonctions C ne devraient pas appeler des fonctions C ++ en général. Sinon, votre code C ne pourra pas libérer les ressources qu'il alloue pendant le déroulement de la pile, car il n'a aucun moyen pratique de détecter l'exception C ++. Si vous finissez par exiger que votre code C appelle une fonction C ++ en tant que rappel, par exemple, le côté C ++ doit s'assurer que toute exception qu'il rencontre est interceptée avant de revenir au code C.2. Si vous écrivez du code C qui traite les types de données comme de simples bits et octets bruts, en passant au bulldozer sur le système de type (c'est en fait ma principale raison d'utiliser C en premier lieu), alors vous ne voulez jamais utiliser ce code contre des données C ++ types qui pourraient avoir des constructeurs et des destructeurs de copie et des pointeurs virtuels et des choses de ce genre qui doivent être respectées. Donc, généralement, ce devrait être du code C utilisant du code C de ce type, et non du code C ++ utilisant du code C de ce type.
Si vous souhaitez que votre code C ++ utilise un tel code C, vous devez généralement l'utiliser comme détail d'implémentation d'une classe qui garantit que les données qu'il stocke dans la structure de données C générique sont un type de données qui est trivial à construire et détruire. Heureusement, il existe des traits de type comme celui-ci que vous pouvez utiliser pour vérifier cela dans une assertion statique, en vous assurant que les types que vous stockez dans la structure de données C générique ont des destructeurs et des constructeurs triviaux et resteront ainsi.
À mon avis, vous n'avez pas besoin de vous embêter si vous respectez les règles ci-dessus et assurez-vous que votre code est bien testé par rapport aux tests que vous avez écrits. La partie qui mérite d'être améliorée est le code que vous avez écrit en C ++ jusqu'à présent, mais cela ne signifie pas que vous devez porter le code strictement basé sur C vers C ++.
Avec le code que vous avez écrit en C ++ et compilé en code C ++, vous devez être prudent. L'utilisation d'un codage de type C en C ++ pose problème. Par exemple, si vous allouez et libérez des ressources manuellement, il est probable que votre code ne soit pas protégé contre les exceptions, car votre code peut rencontrer une exception à quel point vous quittez implicitement de la fonction avant de pouvoir utiliser
free
la ressource. En C ++, RAII et des choses comme les pointeurs intelligents ne sont pas une commodité moelleuse car ils peuvent apparaître si vous ne regardez que les chemins d'exécution normaux sans tenir compte des chemins exceptionnels. Ils sont souvent une nécessité fondamentale pour écrire du code correct et sûr contre les exceptions de manière simple et efficace qui ne commencera pas simplement à fuir partout lors de la rencontre d'une exception.la source