Je travaille donc sur une conception de logiciel utilisant C pour un processeur donné. La trousse à outils inclut la possibilité de compiler C ainsi que C ++. Pour ce que je fais, il n'y a pas d'allocation de mémoire dynamique disponible dans cet environnement et le programme est globalement assez simple. Sans parler du fait que le périphérique n'a presque pas de puissance de processeur ni de ressources. Il n'est vraiment pas nécessaire d'utiliser le C ++.
Cela étant dit, il y a quelques endroits où je surcharges (une fonctionnalité de C ++). Je dois envoyer quelques types de données différents et je n'ai pas envie d'utiliser le printf
formatage de style avec une sorte d' %s
argument (ou autre). J'ai vu des personnes qui n'avaient pas accès à un compilateur C ++ printf
, mais dans mon cas, le support C ++ est disponible.
Maintenant, je suis sûr que je pourrais avoir la question de savoir pourquoi je dois commencer par surcharger une fonction. Je vais donc essayer de répondre à cela maintenant. J'ai besoin de transmettre différents types de données via un port série. J'ai donc quelques surcharges qui transmettent les types de données suivants:
unsigned char*
const char*
unsigned char
const char
Je préférerais simplement ne pas avoir une méthode qui gère toutes ces choses. Quand je demande à la fonction que je veux juste de transmettre le port série, je n'ai pas beaucoup de ressources , donc je ne veux pas faire à peine RIEN mais ma transmission.
Quelqu'un d'autre a vu mon programme et m'a demandé: "Pourquoi utilisez-vous les fichiers du RPC?" Donc, c'est ma seule raison. Est-ce une mauvaise pratique?
Mise à jour
J'aimerais répondre à quelques questions posées:
Une réponse objective à votre dilemme dépendra de:
Si la taille de l'exécutable augmente considérablement si vous utilisez C ++.
À l'heure actuelle, la taille de l'exécutable consomme 4,0% de la mémoire du programme (sur 5248 octets) et 8,3% de la mémoire de données (sur 342 octets). Autrement dit, compiler pour C ++ ... Je ne sais pas à quoi cela ressemblerait pour C parce que je n’utilisais pas le compilateur C. Je sais que ce programme ne se développera plus, alors pour ce qui est des ressources limitées, je dirais que ça va aller là-bas ...
Indique s'il y a un impact négatif notable sur les performances si vous utilisez C ++.
Eh bien, s’il y en a, je n’ai rien remarqué ...
Si le code peut être réutilisé sur une plate-forme différente où seul un compilateur C est disponible.
Je sais que la réponse à cette question est définitivement non . Nous envisageons actuellement de passer à un processeur différent, mais uniquement à des processeurs plus puissants basés sur ARM (tous, je le sais, possèdent des chaînes d'outils du compilateur C ++).
//
commenter. Si cela fonctionne, pourquoi pas?//
commentaires sont dans la norme C depuis C99.Réponses:
Je n'irais pas jusqu'à qualifier cela de "mauvaise pratique" en soi , mais je ne suis pas non plus convaincu que ce soit vraiment la bonne solution à votre problème. Si vous ne voulez que quatre fonctions distinctes pour faire vos quatre types de données, pourquoi ne pas ce que les programmeurs C ont fait depuis des temps immémoriaux:
C’est effectivement ce que le compilateur C ++ fait en coulisse de toute façon et ce n’est pas une lourde charge pour le programmeur. Évite tous les problèmes de "pourquoi n'écris-tu pas assez C avec un compilateur C ++", et signifie que personne d'autre sur ton projet ne va confondre quels bits de C ++ sont "autorisés" et quels bits ne le sont pas.
la source
#define
transmettre () en utilisant C11_Generic
Utiliser seulement certaines fonctionnalités de C ++ tout en le traitant comme C n'est pas vraiment commun, mais ce n'est pas non plus inconnu. En fait, certaines personnes n'utilisent même aucune fonctionnalité du C ++, à l'exception de la vérification de type plus stricte et plus puissante. Ils écrivent simplement C (en prenant soin de n'écrire que dans l'intersection commune de C ++ et C), puis compilent avec un compilateur C ++ pour la vérification de type et un compilateur C pour la génération de code (ou collent simplement avec un compilateur C ++).
Linux est un exemple de base de code où les utilisateurs demandent régulièrement que des identificateurs tels
class
que renommésklass
oukclass
, afin de pouvoir compiler Linux avec un compilateur C ++. Évidemment, étant donné l'opinion de Linus sur le C ++ , ils sont toujours abattus :-D GCC est un exemple de base de code qui a d'abord été transformée en "C ++ - clean C", puis progressivement modifiée pour utiliser davantage de fonctionnalités C ++.Il n'y a rien de mal à ce que vous faites. Si vous êtes vraiment paranoïaque à propos de la qualité de génération de code de votre compilateur C ++, vous pouvez utiliser un compilateur tel que Comeau C ++, qui compile en C comme langage cible, puis utilisez le compilateur C. De cette façon, vous pouvez également effectuer des inspections ponctuelles du code pour voir si l'utilisation de C ++ injecte un code imprévu, sensible aux performances. Cela ne devrait cependant pas être le cas pour la simple surcharge, qui génère littéralement automatiquement des fonctions nommées différemment - IOW, exactement ce que vous feriez de toute façon en C.
la source
Une réponse objective à votre dilemme dépendra de:
Si la réponse à l'une des questions est "oui", il vaudrait peut-être mieux créer des fonctions nommées différemment pour différents types de données et rester en C.
Si la réponse à toutes les questions est "non", je ne vois aucune raison pour laquelle vous ne devriez pas utiliser le C ++.
la source
Vous posez la question comme si la compilation avec C ++ vous donnait simplement accès à plus de fonctionnalités. Ce n'est pas le cas. Compiler avec un compilateur C ++ signifie que votre code source est interprété comme une source C ++, et que C ++ est un langage différent de celui du C. Les deux ont un sous-ensemble commun suffisamment grand pour pouvoir être utilement programmé. possible d'écrire du code acceptable dans les deux langues mais interprété différemment.
Si vraiment tout ce que vous voulez, c'est une surcharge de fonctions, je ne vois vraiment pas l'intérêt de confondre le problème en y intégrant du C ++. Au lieu de fonctions différentes portant le même nom, distinguant leurs listes de paramètres, écrivez simplement des fonctions portant des noms différents.
Quant à vos critères objectifs,
L'exécutable peut être légèrement plus grand lorsqu'il est compilé en C ++, par rapport à un code C similaire construit en tant que tel. Au minimum, l'exécutable C ++ aura l'avantage douteux de la gestion des noms C ++ pour tous les noms de fonctions, dans la mesure où tous les symboles sont conservés dans l'exécutable. (Et c'est en fait ce qui préserve vos surcharges en premier lieu.) Il vous faudrait déterminer expérimentalement si la différence est suffisamment grande pour être importante pour vous.
Je doute que vous voyiez une différence de performance notable entre le code que vous décrivez et un analogue hypothétique au pur-C.
Je jetterai un éclairage légèrement différent à ce sujet: si vous souhaitez lier le code que vous construisez avec C ++ à un autre code, cet autre code devra également être écrit en C ++ et construit avec C ++, ou vous aurez besoin de faire une disposition spéciale dans votre propre code (déclarant une liaison "C"), ce que vous ne pouvez pas faire en plus pour vos fonctions surchargées. D'autre part, si votre code est écrit en C et compilé en C, les autres peuvent le lier avec les modules C et C ++. Ce genre de problème peut généralement être surmonté en inversant les rôles, mais comme vous ne semblez vraiment pas avoir besoin de C ++, pourquoi accepter un tel problème en premier lieu?
la source
De retour dans la journée, l'utilisation d'un compilateur C ++ en tant que «meilleur C» a été promue comme cas d'utilisation. En fait, les premiers C ++ étaient exactement cela. Le principe de conception sous-jacent était que vous ne pouviez utiliser que les fonctionnalités que vous souhaitiez et que vous ne supporteriez pas le coût de fonctionnalités que vous n'utilisiez pas. Donc, vous pouvez surcharger des fonctions (en déclarant l’intention via le
overload
mot clé!) Et le reste de votre projet non seulement compilerait parfaitement mais générerait un code pas pire que ce que le compilateur C produirait.Depuis lors, les langues ont quelque peu divergé.
Chaque
malloc
code C sera une erreur d’appariement de types - pas un problème dans votre cas sans mémoire dynamique! De même, tous vosvoid
pointeurs en C vous trébucheront, car vous devrez ajouter des conversions explicites. Mais… pourquoi le faites-vous de cette façon … vous serez amené de plus en plus à utiliser les fonctionnalités C ++.Donc, cela pourrait être possible avec du travail supplémentaire. Mais cela servira de passerelle pour l'adoption du C ++ à plus grande échelle. Dans quelques années, les gens vont se plaindre de votre code hérité qui ressemble à celui de 1989, car ils remplacent vos
malloc
appels parnew
des blocs de code de corps de boucle pour appeler simplement un algorithme, et dénoncer le faux polymorphisme dangereux qui ont été triviales si le compilateur avait été autorisé à le faire.D'autre part, vous savez que ce serait pareil si vous l'aviez écrit en C, est-il donc faux de l'écrire en C au lieu de C ++ étant donné son existence? Si la réponse est «non», alors utiliser des fonctionnalités choisies avec soin de C ++ ne peut pas être faux non plus .
la source
De nombreux langages de programmation ont une ou plusieurs cultures qui se développent autour d'eux et ont des idées particulières sur ce qu'un langage est censé "être". Bien qu'il devrait être possible, pratique et utile, de disposer d'un langage de bas niveau adapté à la programmation système, complétant ainsi un dialecte de C adapté à cet usage, avec certaines fonctionnalités du C ++ qui conviendraient également, les cultures entourant les deux langages ne le sont pas. t particulièrement en faveur d’une telle fusion.
J'ai lu des compilateurs C incorporés intégrant certaines fonctionnalités du C ++, notamment la possibilité d'avoir
interprété comme, essentiellement
ainsi que la possibilité de surcharger des fonctions statiques (les problèmes de nom ne surviennent que lorsqu’ils sont exportésles fonctions sont surchargées). Il n’ya aucune raison que les compilateurs C ne soient pas en mesure de prendre en charge une telle fonctionnalité sans imposer d’exigences supplémentaires à l’environnement de liaison et d’exécution, mais le rejet réflexif de nombreux compilateurs C de la quasi-totalité du C ++ dans l’intérêt d’éviter les contraintes environnementales les provoque rejeter même les fonctionnalités qui n'imposeraient pas un tel coût. Pendant ce temps, la culture du C ++ n'a guère d'intérêt dans les environnements qui ne peuvent pas prendre en charge toutes les fonctionnalités de ce langage. Sauf si ou jusqu'à ce que la culture de C change pour accepter de telles fonctionnalités, ou la culture de C ++ pour encourager les implémentations de sous-ensembles légers, le code "hybride" est susceptible de souffrir de nombreuses limitations des deux langages tout en étant limité dans sa capacité à exploiter les avantages d'opérer dans un super ensemble combiné.
Si une plate-forme prend en charge à la fois C et C ++, le code de bibliothèque supplémentaire requis pour prendre en charge C ++ rendra probablement l'exécutable un peu plus volumineux, bien que l'effet ne soit pas particulièrement efficace. L'exécution de "fonctionnalités C" dans C ++ ne sera probablement pas particulièrement affectée. Un problème plus important pourrait être la façon dont les optimiseurs C et C ++ gèrent divers cas. Le comportement des types de données dans un C est principalement défini par le contenu des bits qui y sont stockés, et C ++ a une catégorie reconnue de structures (PODS - Plain Old Data Structures) dont la sémantique est également définie par les bits stockés. Cependant, les normes C et C ++ autorisent parfois des comportements contraires à ceux qu'impliquent les bits stockés, et les exceptions au comportement "bits stockés" diffèrent entre les deux langages.
la source
Un autre facteur important à prendre en compte: quiconque héritera de votre code.
Est-ce que cette personne va toujours être un programmeur C avec un compilateur C? Je suppose.
Cette personne sera-t-elle également un programmeur C ++ avec un compilateur C ++? Je voudrais être raisonnablement sûr de cela avant de faire dépendre mon code de choses spécifiques à C ++.
la source
Le polymorphisme est une très bonne fonctionnalité que C ++ fournit gratuitement. Cependant, vous devrez peut-être prendre en compte d'autres aspects lors du choix d'un compilateur. Il existe une alternative au polymorphisme en C mais son utilisation peut provoquer des sourcils surélevés. C'est la fonction variadique, veuillez vous référer au tutoriel de la fonction variadique .
Dans votre cas, ce serait quelque chose comme
J'aime l’approche de Phillips, mais elle jette beaucoup d’appels dans votre bibliothèque. Avec ce qui précède, l'interface est propre. Il a ses inconvénients et c'est finalement une question de choix.
la source
Arg_type_t
et unvoid *
pointant sur les données. Cela dit, oui, attribuer à la fonction (unique) un argument indiquant que le type de données est une alternative viable.Pour votre cas particulier de surcharge statique d'une "fonction" pour quelques types différents, vous pouvez envisager d'utiliser simplement C11 avec sa
_Generic
macro- machine. Je pense que cela pourrait suffire à vos besoins limités.En utilisant la réponse de Philip Kendall, vous pouvez définir:
et code
transmit(foo)
quel que soit le type defoo
(parmi les quatre types énumérés ci-dessus).Si vous vous souciez uniquement des compilateurs GCC (et compatibles, par exemple Clang ), vous pouvez envisager
__builtin_types_compatible_p
sontypeof
extension.la source
Mon point de vue à mon humble avis, oui, et je devrai devenir schizophrène pour pouvoir répondre à cette question, car j’aime les deux langues, mais cela n’a rien à voir avec l’efficacité, mais plutôt avec la sécurité et l’utilisation idiomatique des langues.
Côté C
D'un point de vue C, je trouve cela tellement inutile de faire en sorte que votre code nécessite C ++ uniquement pour utiliser la surcharge de fonctions. À moins que vous ne l'utilisiez pour le polymorphisme statique avec des modèles C ++, il s'agit d'un sucre syntaxique trivial acquis en échange du passage à un langage totalement différent. En outre, si vous souhaitez exporter vos fonctions vers un dylib (qu’il s’agisse ou non d’une préoccupation pratique), vous ne pouvez plus le faire de manière très pratique pour une consommation généralisée avec tous les symboles portant le nom déchiqueté.
Côté C ++
Du point de vue de C ++, vous ne devriez pas utiliser C ++ comme le C avec surcharge de fonctions. Ce n'est pas un dogmatisme stylistique, mais un dogme lié à l'utilisation pratique du C ++ quotidien.
Votre type de code C normal n’est raisonnablement sain d’esprit et «sûr» d’écrire que si vous travaillez contre le système de type C qui interdit des choses comme les copieurs
structs
. Une fois que vous travaillez dans une grande partie du système de type plus riche de C ++, les fonctions quotidiennes qui sont d' une valeur inestimable commememset
etmemcpy
ne deviennent pas des fonctions vous devez appuyer sur tout le temps. Ce sont plutôt des fonctions que vous souhaitez généralement éviter, comme la peste, car avec les types C ++, vous ne devriez pas les traiter comme des bits bruts et des octets à copier, à mélanger et à libérer. Même si votre code utilise uniquement des éléments tels quememset
les primitives et les UDT POD, à l'instant où quelqu'un ajoute un ctor à n'importe quel UDT que vous utilisez (y compris le simple ajout d'un membre qui en nécessite un, commestd::unique_ptr
membre) contre de telles fonctions ou une fonction virtuelle ou quoi que ce soit de ce genre, il rend tout votre codage C-style normal sujet à un comportement indéfini. Prenez Herb Sutter lui-même:Tant de développeurs C seraient en désaccord avec cela et à juste titre, puisque la philosophie ne s'applique que si vous écrivez du code en C ++. Vous avez probablement sont en train d' écrire un code très problématique si vous utilisez fonctionne comme
memcpy
tous les temps dans le code qui construit que C ++ , mais il est parfaitement bien si vous le faites en C . Les deux langues sont très différentes à cet égard en raison des différences dans le système de types. Il est très tentant d'examiner le sous-ensemble de fonctionnalités que ces deux-là ont en commun et de penser que l'une peut être utilisée comme une autre, en particulier du côté C ++, mais le code C + (ou C-- code) est généralement beaucoup plus problématique que C et C. Code C ++.De même, vous ne devriez pas utiliser, par exemple,
malloc
dans un contexte de style C (ce qui n’implique aucune EH) s’il peut appeler directement des fonctions C ++ pouvant émettre, car vous avez alors un point de sortie implicite dans votre fonction à la suite de la exception que vous ne pouvez pas attraper efficacement l'écriture de code de style C, avant d'être capable defree
cette mémoire. Donc , chaque fois que vous avez un fichier qui construit en C ++ avec une.cpp
extension ou de tout et fait tout ce genre de choses commemalloc
,memcpy
,memset
,qsort
, etc., alors il demande des problèmes plus loin sur la ligne, si ce n'est déjà le cas, sauf s'il s'agit du détail d'implémentation d'une classe qui fonctionne uniquement avec des types primitifs. À ce stade, la gestion des exceptions doit toujours être protégée contre les exceptions. Si vous écrivez du code C ++ vous voulez plutôt compter généralement sur RAII et utiliser des choses commevector
,unique_ptr
,shared_ptr
, etc, et éviter tout codage normal style C lorsque cela est possible.Sécurité d'exception
Encore une fois, dès que vous serez en C ++, les gens s'attendent à ce que votre code soit protégé contre les exceptions. Les gens pourraient maintenir votre code à l'avenir, étant donné qu'il est déjà écrit et compilé en C ++, et simplement utilisé
std::vector, dynamic_cast, unique_ptr, shared_ptr
, etc. dans du code appelé directement ou indirectement par votre code, le jugeant inoffensif puisque votre code est déjà "supposé" C ++. code. À ce stade, nous devons faire face à la chance que les choses vont se produire, et ensuite, lorsque vous prenez un code C parfaitement parfait et charmant, comme ceci:... il est maintenant cassé. Il doit être réécrit pour être exceptionnellement sûr:
Brut! C’est pourquoi la plupart des développeurs C ++ exigent plutôt ceci:
Le code ci-dessus est conforme à la norme RAII et est du type que les développeurs C ++ approuveraient généralement, car la fonction ne perd pas la ligne de code qui déclenche une sortie implicite à la suite d'un
throw
.Choisissez une langue
Vous devez soit adopter le système de types et la philosophie de C ++ avec RAII, exception-safe, templates, OOP, etc. ou adopter C, qui tourne principalement autour de bits et d'octets bruts. Vous ne devez pas former un mariage impie entre ces deux langues, mais plutôt les séparer en langues distinctes pour les traiter de manière très différente au lieu de les brouiller.
Ces langues veulent vous épouser. En général, vous devez en choisir un au lieu de sortir ensemble et de vous amuser avec les deux. Vous pouvez aussi être un polygame comme moi et épouser les deux, mais vous devez changer complètement de pensée lorsque vous passez du temps avec l'un sur l'autre et les maintenir bien séparés les uns des autres afin qu'ils ne se combattent pas.
Taille binaire
Juste par curiosité, j'ai essayé de prendre tout à l'heure mon implémentation et mon benchmark libres et de les porter en C ++ depuis que je suis vraiment curieux de savoir ceci:
... et je voulais savoir si la taille binaire gonflerait du tout en construisant en C ++. Cela m'a obligé à saupoudrer des jets explicites un peu partout (une raison pour laquelle j'aime écrire des choses de bas niveau comme les allocateurs et les structures de données en C mieux), mais cela n'a pris qu'une minute.
Cela consistait simplement à comparer une version publiée 64 bits de MSVC pour une application de console simple et un code qui n’utilisait aucune fonctionnalité C ++, pas même une surcharge d’opérateur - juste la différence entre le construire avec C et utiliser, disons, à la
<cstdlib>
place de<stdlib.h>
et Des choses comme ça, mais j'ai été surpris de constater que cela ne faisait aucune différence pour la taille binaire!Le binaire était constitué d'
9,728
octets lors de sa construction en C, et également d'9,278
octets lorsqu'il était compilé en tant que code C ++. En fait, je ne m'y attendais pas. Je pensais que des choses comme EH en ajouteraient au moins un peu (pensaient que ce serait au moins une centaine d'octets différents), bien qu'il ait probablement pu comprendre qu'il n'était pas nécessaire d'ajouter des instructions relatives à EH puisque je ne faisais que en utilisant la bibliothèque standard C et rien ne jette. J'ai pensé quelque choseajouterait un peu à la taille binaire de toute façon, comme RTTI. Quoi qu'il en soit, c'était plutôt cool de voir ça. Bien sûr, je ne pense pas que vous devriez généraliser à partir de ce résultat, mais au moins cela m'a un peu impressionné. Cela n'a également eu aucun impact sur les points de repère, et naturellement, puisque j'imagine que la taille binaire résultante identique signifiait également des instructions machine résultantes identiques.Cela dit, qui se soucie de la taille binaire avec les problèmes de sécurité et d’ingénierie mentionnés ci-dessus? Encore une fois, choisissez une langue et adhérez à sa philosophie au lieu d'essayer de la bâtardiser; c'est ce que je recommande.
la source