Je ne maîtrise pas l'entièreté du français. Par exemple, jusqu'à il y a 10 secondes, je ne savais pas ce que « décize » voulait dire. Mais je me débrouille quand même pour comprendre ce que racontes les autres.
Je ne maîtrise pas l'entièreté du français. Par exemple, jusqu'à il y a 10 secondes, je ne savais pas ce que « décize » voulait dire. Mais je me débrouille quand même pour comprendre ce que racontes les autres.
Très drôle :-) Mais plaisanterie mise á part, l'exemple est très mauvais. Il peut être impossible de comprendre le sens de certaines phrases si on ne maitrise pas le vocabulaire qui y est employé. Mais il est également vrai que des phrases composés de mots très simples peuvent être difficiles á comprendre.
Ceci est du code écrit dans un langage
- sans classes ni constructeur pour enforcer les invariants,
- ni références, pour également enforcer d'autres invariants.
Vu qu'il est difficile d'avoir des garanties offertes par des syntaxes alternatives trop simplistes, le code est farci jusqu'à l'os de programmation défensive car il est difficile de maitriser où l'on va et qui pourrait faire quoi comme bêtise. Au final, on a un code bien plus difficile à maintenir qu'un équivalent C++.
Avec de la syntaxe supplémentaire, on se met dans des conditions qui simplifie la logique du code. La contre-partie, perdre un petit peu de temps au début pour apprendre des notions de conception, et des nouvelles syntaxes, pour gagner beaucoup de temps en maintenance.
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25 struct Image : boost::noncopyable { explicit Image(std::size_t nb) : pixels(new double[nb]), nb_pixels(nb) {} ~Image() { delete[] pixels; } std::size_t get_size() const { return nb_pixels;} double const& operator[](std::size_t idx) const { assert(idx < get_size()); return pixels[idx]; } double & operator[](std::size_t idx) { assert(idx < get_size()); return pixels[idx]; } private: double * pixels; std::size_t nb_pixels; };
Blog|FAQ C++|FAQ fclc++|FAQ Comeau|FAQ C++lite|FAQ BS|Bons livres sur le C++
Les MP ne sont pas une hotline. Je ne réponds à aucune question technique par le biais de ce média. Et de toutes façons, ma BAL sur dvpz est pleine...
Tout le monde a du mal avec le niveau de templates qui augmente. Et pourtant, bien utilisés, il permettent d'écrire des trucs comme
L'utilisateur ne sait pas (/ne veut pas savoir) comment c'est fichu, et cela lui est pourtant très utile.
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5 using non_null_double = my::non_null<double>; boost::unit::length f(non_null_double factor, boost::unit::length distance) { return distance / factor; // pas de question à me poser ici, je sais que factor est non nul. }
Blog|FAQ C++|FAQ fclc++|FAQ Comeau|FAQ C++lite|FAQ BS|Bons livres sur le C++
Les MP ne sont pas une hotline. Je ne réponds à aucune question technique par le biais de ce média. Et de toutes façons, ma BAL sur dvpz est pleine...
Je partirais sur cette base pour implémenter une telle fonctionnalité. Je renverais une exception plutot que la valeure 1. Mais c'est surement très naïf.
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24 template <typename Type_> struct NonNull { Type_ value; NonNull() : value(Type_()) {} NonNull(Type_ val) : value(val) {} operator Type_() { if(!value) return 1; else return value; } }; int main(int argc, char** argv) { NonNull<double> non_null_double; double d = 10.0 / non_null_double; return 0; }
C'est plus subtil.
- Le constructeur par défaut doit être retiré (= delete)
- Le constructeur mono-paramètre doit être explicite, et faire un assert.
- L'opérateur de conversion doit également être explicite -- il faut le C++11 pour ça.
Sans ça, la classe ne présente pas grand intérêt, sinon, l'utilisateur pourrait écrire sans soucis des choses comme "double d = 0; f(d);". Or on désire une erreur de compilation qui force l'utilisateur à être conscient que la fonction attend un nombre non null, ou à défaut qu'il en soit conscient, qu'il passe un nombre non null. "auto d = mon_rationnel_valide.as_real(); f(d);"
Blog|FAQ C++|FAQ fclc++|FAQ Comeau|FAQ C++lite|FAQ BS|Bons livres sur le C++
Les MP ne sont pas une hotline. Je ne réponds à aucune question technique par le biais de ce média. Et de toutes façons, ma BAL sur dvpz est pleine...
J'ai bien dit que je renverais une exception meme si dans l'exemple je mets 1.
Pas pris encore l'habitude. Des fois j'y pense des fois non.
En quoi le constructeur par défaut gène t-il ? Initialiser la valeur, mème á zéro, ne pose pas de problemes. Il ne faut juste pas l'utiliser telle quellle ,mais en mème temps ça n'aurait pas beaucoup de sens que de l'utiliser telle quelle.
a- Exception que tu auras le jour où les données entrantes ne seront pas bonnes et qui ne te permettra pas de corriger le code pour lui permettre de renvoyer un message d'erreur pertinent : http://luc-hermitte.developpez.com/t...heorie/#LIII-A
Avec une erreur de compilation, on renvoie la responsabilité de la vérification au bon endroit.
b- Si la variable est initialisée par défaut à 0, c'est totalement en contradiction avec l'objectif : faire en sorte que la donnée ne puisse jamais être dans un état non utilisable afin de n'avoir jamais à se poser la moindre question.
Blog|FAQ C++|FAQ fclc++|FAQ Comeau|FAQ C++lite|FAQ BS|Bons livres sur le C++
Les MP ne sont pas une hotline. Je ne réponds à aucune question technique par le biais de ce média. Et de toutes façons, ma BAL sur dvpz est pleine...
Oui en effet. Je viens d'aller faire des courses et sur la route j'ai réfléchi au problème, á comment je l'implémenterai. Et effectivement je n'avais pas bien compris le principe. Le but n'étant pas de ne pas pouvoir utiliser la valeur si elle est égale á zéro, mais tout simplement faire en sorte qu'elle ne soit jamais égale á zéro. C'est pas la mème chose.
De mème que j'aurais du déclaré la valeur en privée et non en public et tout un tas d'autres subtilités auxquelles je n'avais pas spécialement pensées sur le moment. Mais enfin bon, on est un peu hors sujet la
C'est une question d'habitude. Pour ma part, je comprends ce code du premier coup d'oeil. D'ailleurs, du code comme ça, ça a été la norme pendant TRES longtemps. Les nouvelles constructions syntaxiques sont utiles, ce n'est pas le problème, mais on n'est pas non plus obligé de mettre toutes les constructions syntaxiques existantes d'un langage dans un programme.
C'est même qu'une question de langage. Ce code avec des pointeurs est du code C (en changeant enum class par enum) alors que la version avec RAII est du C++. Un code correctement écrit en C est aussi compréhensible qu'un code correctement écrit en C++. Tout dépend des besoins. Pour programmer un MCU dans l'aviation par exemple on choisira le C pour obtenir un exécutable plus léger, moins groumant en RAM, plus performant et surtout complètement prédictible dans un contexte critique car le C++ fait des choses derrière le dos du programmeurs qui ne sont pas toujours souhaitables.
A propos de C plus léger et plus rapide, je renvoie aux présentations que j'avais évoquées ici: http://www.developpez.net/forums/d32...r/#post8505022
Sinon, AMA, trouver ce type de code plus simple à comprendre est une illusion dont on se berce.
Ceci n'est que le code "bas-niveau". Comme je disais, imaginez maintenant un filtre qui va extraire un bandeau de l'image (et donc construire un objet sous-image), et parser tous les pixels pour calculer des statistiques ou faire autre chose.
Il faut prévoir une nouvelle ressource allouée : le bandeau (et donc sa libération en cas d'erreur sur get_pixel), et une boucle qui va itérer sur chaque pixel. Ce cas est encore simple. Cela devient encore plus compliqué sur des calculs de corrélation où l'on extrait 2 bandeaux qui sont parcourus simultanément).
Déjà qu'un code C correct est complexe: cf le code en bas à gauche dans http://alexandre-laurent.developpez....ou-exceptions/, si en plus on rajoute de la programmation défensive (pour résister aux erreurs de programmation), le code devient encore plus complexe.
En C, il aurait été plus propre de rendre la classe/structure image complètement cachée (comme FILE), histoire de retirer le code qui vérifie à chaque fois si tout ce qui est reçu est dans un état correct. C'est à dire: simuler des invariants assurés par encapsulation+constructeur.
Mon point, est que le C++ offre de "nouveaux" (pour le coup, ceux là vieux) moyen pour simplifier notre code. On ne va quand même pas refuser des sucres syntaxiques sous prétexte que c'est une syntaxe alternative à une que nous avons déjà.
Blog|FAQ C++|FAQ fclc++|FAQ Comeau|FAQ C++lite|FAQ BS|Bons livres sur le C++
Les MP ne sont pas une hotline. Je ne réponds à aucune question technique par le biais de ce média. Et de toutes façons, ma BAL sur dvpz est pleine...
J'utilise à la fois C et C++ dans l'embarqué et je peux confirmer que dans l'immense majorité des cas, et c'est logique, C est plus performant que C++ et surtout moins gourmand en mémoire. Le fais que l'orateur ne sache pas pourquoi certains programmes écrit en C++ donnent un exécutable de taille moindre est pour moi révélateur du problème que pose C++ dans l'embarqué. Il y a bien eu des tentatives de porter certains projets de l'embarqué écrit en C vers du C++ (le plus connu étant le kernel Linux) mais la plupart se sont avérées êtres des échecs où ont été rapidement abandonnées après étude. Dans le cas des MCU, la question est souvent vite réglée par le fait que l'ensemble des outils de la STL (à l'exception de std::array) et la plupart des mécanismes du langage comme les exceptions ne peuvent tout simplement pas être mis en oeuvre.A propos de C plus léger et plus rapide, je renvoie aux présentations que j'avais évoquées ici: http://www.developpez.net/forums/d32...r/#post8505022
[Ca devient de plus en plus HS, relativement aux concepts ^^']
Justement, ce n'est pas si logique.
Le C++ te permet de choisir quelle fonctionnalité tu prends du C d'origine, ou des apports qui viennent du C++. A partir de là, il n'y a aucune raison que l'on ne soit pas capable d'avoir un code au moins aussi efficace/petit -- quitte à retirer les exceptions pour garder un petit binaire.
Là, où ça devient intéressant, c'est que l'on peut se donner de nouveaux outils, et dans le lot, on peut en trouver qui ne vont pas dégrader performances et empreinte. Quelques un ont été listés dans la présentation : algos standards qui permettent de nouvelles optims (je ne parle pas des conteneurs -- autres que std::array, ou les array_view/string_view), not_null qui évite de payer pour les branchements de la programmation défensive, etc.
Blog|FAQ C++|FAQ fclc++|FAQ Comeau|FAQ C++lite|FAQ BS|Bons livres sur le C++
Les MP ne sont pas une hotline. Je ne réponds à aucune question technique par le biais de ce média. Et de toutes façons, ma BAL sur dvpz est pleine...
Les mécanismes de C++ sont à la fois plus complexes et plus lourds, c'est donc logique. Les cas présentés à charge du C sont sélectionnés et hors contexte. Dans la majorité des cas, les performances de C sont meilleures comme le montre ce benckmark. La plupart des lib bas niveau et des applications temps-réel sont écrites en C en partie pour cette raison. N'importe quel ingénieur sérieux dans l'embarqué le dira. J'aime beaucoup C++ mais il n'est pas à même de tenir la barre dans les applications critiques ou nécessitant des performances.
Je pense qu'il y a un souci dans la façon dont tu t'exprimes.
Le fait que du code soit simple a comprendre n'implique pas qu'il soit approprié pour une situation donnée.
Tu semble confondre simplicité de compréhension et réutilisabilité. A partir de là, ça devient un dialogue de sourd. Mais sinon, dans le fond, sur l'exemple donné, je te rejoins. Il serait plus générique d'encapsuler le code. Mais ça n'en fait pas un programme plus simple à comprendre. D'ailleurs, l'encapsulation en cascade peut elle aussi poser des problèmes de compréhension du code.
Si tu prends fasta, et regarde les codes, tu verras une énorme différence: "#pragma OMP". On ne compare pas les mêmes choses. Pour les arbres binaires, on n'utilise pas le même COTS. Pareil pour la lib de regex dans le suivant. Il est difficile de comparer quand autant d'éléments changent, et qu'il ne s'agit pas juste de remplacer des structures par des classes (pour mieux contrôler les invariants et remplacer de nombreux cas d'utilisation du débuggeur par le compilateur dans l'assistance à la maintenance de l'application), et prendre un peu de template par-ci par-là.
La part du C dans l'embarqué est du à deux raisons : la tradition et les aprioris (je ne sais plus si c'est le sujet de la 1ère ou de la 2nd présentation de Dan Saks lors du dernier code::dive), et au problème de support par des compilateurs maintenus par les fournisseurs de matos (qui n'ont pas forcément envie, ni les moyens, de fournir des compilateurs plus complexes comprenant le C++). Et aussi on a facilement des problèmes d'ABI. Si tu veux faire dans l'ad hominem, je pense pouvoir dire que Dan Saks est un développeur sérieux qui bosse dans l'embarqué. Et pourtant il se fait l'avocat du C++ depuis très longtemps.
Qui plus est, si une construction propre au C++ n'est pas acceptable, il suffit d'utiliser l'équivalente du C. Un code C compilé avec un compilateur C++ n'a pas à être plus lent -- cf la video de l'autre intervenant dont le nom m'échappe. A partir de là, tu utilises juste ce que tu veux du C++. Et même en embarqué (moyennant la disponibilité d'un compilateur), tu ne dois pas observer de dégradations.
En effet, je sens un problème de communication. Désolé.
La simplicité de compréhension ne se borne pas au cas local. Il est vrai que la simplicité que je critique ne se limite pas à ces deux fonctions. Elle englobe tout le produit de traitement d'image. Quand je vois un tel code (en général, je commence par la couche haute avant de descendre pour savoir si on n'aurait pas des fuites de ressources), je dois accorder plus d'attention pour comprendre ce qu'il fait. Cela vaut aussi bien pour la maintenance (a-t-on oublié des cas?), que pour son utilisation.
Et justement, son utilisation est à 100% embarquée dans du code qui continue à faire la même chose. Ce code que j'ai montré est le plus simple car tout au fond des appels. Quand on maintiens les couches au dessus, (qui suivent la même politique, détail très important), c'est vite l'enfer car il faut vérifier les cas remontés par cette partie, plus ceux remontés par les autres parties employées. Là, on a constitué une opération plus ou moins simple à appréhender, et on recommence en l'utilisant à un niveau supérieur. Mon expérience est que la complexité explose et qu'il y a des potentiels de fuites assez importants si un erreur de programmation est bel et bien détectée.
Le problème de ce type de code est le parasitage par la programmation défensive qui rend vite tout plus compliqué.
get_size() fait du défensif. Il est localement assez simple. get_pixel fait du défensif, il serait presque simple, sauf qu'il appelle une fonction qui elle-même fait du défensif, ce qui implique une complexification du code de get_pixel. get_region fait du défensif à son niveau et appelle d'autres fonctions qui font du défensif (get_size(), get_pixel()).
S'il y a des raccourcis que je prends, c'est lié au fait que j'affirme que grâce à class+private+constructeur, j'ai moyen d'éliminer une part non négligeable du défensif, ce qui permet d'avoir des fonctions plus simples à chaque niveau. Avec les exceptions, on peut encore aller plus loin : si on a un objet bandeau, alors il ne peut que être valide, et alors nul besoin de tester des choses assurées par construction du programme.
Blog|FAQ C++|FAQ fclc++|FAQ Comeau|FAQ C++lite|FAQ BS|Bons livres sur le C++
Les MP ne sont pas une hotline. Je ne réponds à aucune question technique par le biais de ce média. Et de toutes façons, ma BAL sur dvpz est pleine...
Ce que montre Dan Saks dans sa vidéo n'est tout simplement pas possible dans bon nombre de systèmes embarqués ou induit clairement d'importants surcoûts de mémoire par rapport à une implémentation en C. De plus il ne parle pas des performances et des questions temps-réel importantes dans l'embarqué et pour lesquelles il n'est pas possible d'obtenir des résultats satisfaisants en C++. Il est du reste compliqué de prédire le code machine qui résultera d'une compilation en C++ lorsque ça n'est pas tout simplement impossible. On n'est donc pas en mesure de pouvoir utiliser C++ dans des applications exigeant des preuves solides ou nécessitant une très faible empreinte mémoire.La part du C dans l'embarqué est du à deux raisons : la tradition et les aprioris (je ne sais plus si c'est le sujet de la 1ère ou de la 2nd présentation de Dan Saks lors du dernier code::dive), et au problème de support par des compilateurs maintenus par les fournisseurs de matos (qui n'ont pas forcément envie, ni les moyens, de fournir des compilateurs plus complexes comprenant le C++). Et aussi on a facilement des problèmes d'ABI. Si tu veux faire dans l'ad hominem, je pense pouvoir dire que Dan Saks est un développeur sérieux qui bosse dans l'embarqué. Et pourtant il se fait l'avocat du C++ depuis très longtemps.
En reprenant le code dans l'autre vidéo de Bartosz Szurgot mais en remplaçant la boucle for par un simple memset, l'exécutable en C est 30% plus petit et 40% plus rapide. La question à se poser est, "quel est le problème?". Chacun de ces 2 langages a une solution a apporter et l'idée est de prendre la meilleure.
Je ne dis pas qu'il n'est pas possible que la majorité des personnes dans l'embarqué fassent erreur mais il y a quand même toujours matière à réfléchir sur le sérieux d'une démarche lorsqu'elle est minoritaire. Ici c'est justifié.
Vous avez un bloqueur de publicités installé.
Le Club Developpez.com n'affiche que des publicités IT, discrètes et non intrusives.
Afin que nous puissions continuer à vous fournir gratuitement du contenu de qualité, merci de nous soutenir en désactivant votre bloqueur de publicités sur Developpez.com.
Partager