J'ai une solide expérience Java / Groovy et j'ai été affecté à une équipe qui maintient une base de code C assez importante pour un logiciel d'administration.
Certains problèmes, comme le traitement des blob dans la base de données ou la génération de rapports au format PDF et Excel, ont été externalisés vers le service Web Java.
Cependant, en tant que développeur Java, je suis un peu confus par certains aspects du code:
- il est verbeux (surtout lorsqu'il s'agit d'une «exception»)
- il y a beaucoup de méthodes énormes (beaucoup de méthodes 2000+ lignes)
- il n'y a pas de structures de données avancées (il me manque beaucoup List, Set et Map)
- aucune séparation des préoccupations (SQL est joyeusement mélangé tout autour du code)
En conséquence, je sens que l'entreprise est cachée dans des tonnes de code technique et mon cerveau, façonné avec Orienté Objet et une pincée de programmation fonctionnelle, n'est pas à l'aise.
Le bon côté du projet est que le code est simple: il n'y a pas de framework, pas de manipulation de code d'octet à l'exécution, pas d'AOP. Et le serveur peut répondre simultanément à plus de 10000 utilisateurs avec une seule machine en utilisant moins de mémoire que java a besoin pour cracher "hello world".
Je veux apprendre à écrire du code C conformément aux principes modernes généralement acceptés. Y a-t-il des principes communément acceptés sur la façon dont le C moderne devrait être écrit et structuré?
Quelque chose d'un peu comme l'équivalent du livre "Effective Java", mais pour C.
Modifier à la lumière des réponses et des commentaires:
- Je vais essayer d'adapter mon état d'esprit au code C et ne pas essayer de le refléter dans la POO.
- J'ai commencé à lire les guides de style de codage recommandés dans le commentaire (Les normes de codage GNU et Le style de codage du noyau Linux).
- Je vais ensuite essayer de proposer ce style de code à mes collègues. La partie la plus difficile pourrait être de convaincre des collègues qu'une méthode énorme pourrait être divisée en parties plus petites et que répéter les mêmes 4 lignes de code de gestion des erreurs pourrait être évité à l'aide d'une méthode.
la source
Réponses:
Je peux lire à partir de votre question que le problème n'est pas que le code est du vieux C mais juste une mauvaise programmation. La plupart des problèmes que vous avez mentionnés, comme la verbosité, les énormes fonctions de 2000+ lignes ou l'absence de séparation des préoccupations, sont applicables à n'importe quel langage, C ou Java.
La verbosité a été mentionnée dans le contexte de la gestion des erreurs. Vous n'avez pas fourni d'exemple, je ne peux que rappeler que le code de gestion des erreurs est également du code . Il n'y a aucune excuse pour les sections répétitives du code passe-partout. Factorisez-le; soit vers une fonction ou (si cela ne vaut pas la peine de créer une fonction distincte) faites le
goto Error;
modèle et déplacez la gestion des erreurs et le nettoyage des ressources vers uneError:
section en bas de la fonction.Si passer l'erreur dans la chaîne d'appels semble être le problème, demandez-vous: la fonction là-haut a-t-elle vraiment besoin de savoir qu'un petit gars ici avait un problème? Les mécanismes d'exception intégrés dans un langage facilitent la tâche, mais en général, il est préférable de gérer les exceptions tôt (dans n'importe quel langage) afin que la condition d'erreur ne pollue pas la logique du code de haut niveau. Et si la fonction là-haut a vraiment besoin de savoir, il existe des moyens d' émuler des exceptions avec
setjmp
etlongjmp
.Je pense que le seul problème vraiment lié à C mentionné est le manque de conteneurs standard. Alors que
Set
peut généralement être remplacé par un tableau trié etMap
(pour la plupart) par un tableau de paires ou unstruct
(si vous connaissez le jeu de clés avant,map[key] = value
se transforme ens.key = value
), mais le fait est qu'il n'y a pas de conteneur de tableau dynamique dans la norme bibliothèque. En C99, vous pouvez au moins déclarer un tableau de longueur variable sur la pile (int array[len]
) mais vous devez calculerlen
au préalable (généralement pas dur) et bien sûr, vous ne pouvez pas le renvoyer en tant qu'objet alloué à la pile. La plupart des projets finissent par écrire leur propre conteneur de tableau dynamique ou en adopter un open-source.Pour terminer, j'aimerais souligner que j'y suis allé. J'ai été le programmeur Java qui est passé au C ++ et au C. pur. Je voudrais conseiller "lire le livre X pour apprendre le bon C" mais il n'y en a pas comme il n'y en a pas pour Java. La voie à suivre consiste à s'imprégner de toutes les subtilités du langage et de la bibliothèque standard; google beaucoup, lire beaucoup et coder beaucoup jusqu'à ce que vous commenciez à penser en C. Essayer d'écrire des choses en C comme vous le feriez en Java est aussi frustrant que d'essayer d'écrire une phrase dans une langue étrangère avec des mots directement traduits de votre mère langue; vous et le lecteur grincerez des dents. La bonne nouvelle est que l'apprentissage d'une bonne programmation est lent mais l'apprentissage d'une autre langue est rapide. donc si vous écrivez du code décent en Java,
la source
setjmp()
/longjmp()
comme un outil valide: il n'essaie même pas d'effectuer un nettoyage. Toute allocation sera divulguée, aucun verrou maintenu ne sera libéré, aucun fichier ouvert ne sera fermé et toute incohérence transitoire des données deviendra permanente. À mon humble avis, cette paire de fonctions est fondamentalement le pire hack jamais inventé, avec la seule justification qu'il était possible de le mettre en œuvre. En fin de compte, il n'y a vraiment qu'une seule façon valide de gérer les erreurs dans C: les codes d'erreur explicites.setjmp/longjmp
dirait un poisson hors de l'eau en C et je ne les ai jamais utilisés. Je me sentais obligé de les inclure uniquement en raison des nombreux tutoriels / bibliothèques sur Internet pour imiter les exceptions, alors j'ai pensé qu'il y avait des gens qui l'utilisaient réellement.Je vous recommanderais d'être prudent quant à savoir si cela vaut votre temps et l'argent de l'entreprise pour dépenser des ressources pour "moderniser" un logiciel qui fonctionne avec une faible complexité de code et qui fonctionne bien. Il y a un risque élevé que vous introduisiez vous-même de nouveaux bogues, d'autant plus qu'il semble s'agir d'un système que vous ne connaissez pas.
Si vous voulez toujours emprunter cette voie, je suggère ce qui suit:
À ce stade, vous déciderez si cela vaut la peine d'être exploré. Si la culture de votre entreprise ne récompense pas l'échec, obtenez le feu vert d'un supérieur ou d'un gestionnaire.
Je pense que c'est une bonne feuille de route et vous emmener où vous en avez besoin. Sans connaître les spécificités de ce projet, il est difficile de vous aider beaucoup. Veuillez ne pas rejeter ma clause de non-responsabilité car je suis trop alarmiste. Des tonnes d'excellents programmeurs ont battu la poussière en essayant de réécrire un projet existant dans leur langue préférée ou en utilisant des outils "modernes". C'est une décision qui doit être mûrement réfléchie et je vous exhorte à ne pas vous laisser aller et à le faire vous-même sans le soutien de la direction ou l'aide de vos collègues.
la source
Si vous préférez un langage de niveau supérieur, il existe certains langages comme C ++ ou Objective-C qui peuvent être mélangés avec du code C assez facilement.
Alternativement, C et C ++ sont raisonnablement compatibles. Vous pourrez peut-être simplement compiler la base de code entière en C ++ avec quelques modifications - vous aurez la variable occasionnelle nommée "classe" ou "modèle" que vous devez renommer, mais en pratique, ce sera tout. (sizeof ('a') est différent en C et C ++, mais je ne pense pas l'avoir jamais utilisé).
Si vous optez pour cette voie, considérez que le responsable suivant ne maîtrisera peut-être pas trop C ++. Ne vous laissez pas emporter. Tirez parti de C ++, mais seulement dans la mesure où un programmeur C peut facilement le comprendre.
la source
malloc
) est considéré comme une mauvaise pratique en C. La signification deconst
etinline
est également très différente entre C et C ++, et bien sûr C ++ ne comprend pas__restrict
. Ne traitez pas les langues comme interchangeables, même dans le sous-ensemble de sources qui se compilent dans les deux.Fondamentalement, écrire un bon code C est identique à écrire un bon code C ++ ou Java: vous voulez une classe, utilisez a
struct
. Vous voulez l'héritage, incluez la base enstruct
tant que premier membre sans nom. Vous voulez des fonctions virtuelles, ajoutez un pointeur à une statiquestruct
de pointeurs de fonction. Et ainsi de suite, etc. C'est exactement ce que fait C ++ sous le capot, la seule différence est qu'il est explicite en C. Ainsi, vous pouvez faire une programmation parfaitement orientée objet en C, il semble juste un peu différent et plus chaud que ce que vous être habitué.Le fait est qu'une bonne programmation concerne les paradigmes, pas les fonctionnalités du langage. Certes, il est toujours agréable que vos fonctionnalités linguistiques fournissent un bon support pour les paradigmes que vous souhaitez utiliser, mais les fonctionnalités linguistiques ne sont pas obligatoires. Une fois que vous vous en rendez compte, vous pouvez écrire du bon code dans à peu près n'importe quel langage (à l'exception de certains langages ésotériques comme brainfuck ou INTERCAL, c'est-à-dire).
Bien sûr, le problème demeure que la bibliothèque C standard ne contient aucune de ces classes de conteneurs astucieuses auxquelles vous êtes habitué. Malheureusement, cela signifie que vous devrez soit utiliser le vôtre, soit contourner ce manque en utilisant des tableaux alloués dynamiquement. Mais je parie que vous découvrirez bientôt que tout ce dont vous avez vraiment besoin est de tableaux dynamiques (
malloc()
) et de listes / arborescences liées qui sont implémentées via des membres de pointeur dans vos classes.la source