Salut 'Nal (ah non, ça marche pas)
Lors d'une discussion avec un collègue, ça chambrait 1 gentillement sur l'absence de switch sur des chaînes constantes en C++. Ça a déclenché quelques recherches de mon côté, et voilà ce que j'ai pu trouver (les codes qui suivent ne sont que le reflet de ce que j'ai pu lire en ligne, rien de nouveau sous le soleil brestois)
Approche LLVM : StringSwitch
Source : http://llvm.org/docs/doxygen/html/StringSwitch_8h_source.html
L'idée est de simuler une cascade de
if(str == "str0")
return value0;
else if(str == "str1")
return value1;
else
return value2;
La solution est plutôt amusante, je vous mets une version simplifiée ci-dessous
template<typename Holder>
class StringSwitch {
Holder const * holder_;
std::string const value_;
public:
StringSwitch(std::string value) : holder_(nullptr), value_(value) {}
template<size_t N>
StringSwitch& Case(char const (&v)[N], Holder const& h) {
if(!holder_ && value_.size() == N- 1 && value_ == v)
holder_ = &h;
return *this;
}
Holder const& Default(Holder const& h) {
if(holder_) return *holder_;
return h;
}
};
int main(int argc, char **argv) {
auto && msg =
StringSwitch<std::string>(argc == 1 ? "--help" : argv[1])
.Case("hello", "world")
.Case("--help", "no help can be found in this world")
.Default("");
if(msg.empty())
return 1;
else {
std::cout << msg << std::endl;
return 0;
}
}
Ça coûte à peine plus que la cascade de if (quelques comparaison à un pointeur nul en plus) et c'est plutôt élégant. Bien sûr ce n'est qu'un petit exemple, il faut se référer au code complet pour saisir les subtilités !
Pour réduire un peu le coût de comparaison (on en dans le pire des cas, où n est le nombre de case et m la taille moyenne d'une chaîne, on pourrait utiliser une std::map
que l'on marquerait static const
. Malheureusement, le constructeur de std::map
n'est pas constexpr
, donc on aurait une initialisation à l'exécution. On pourrait, pour un compromis différent, utiliser une std::unordered_map
aussi.
Et pour les chagrins qui veulent du code dans leur case:
on peut utiliser une std::function
(mais il y a un coût caché). Un truc du genre :
#include <map>
#include <functional>
int main(int argc, char **argv) {
static const
std::map<std::string, std::function<int()>> Switch = {
{"hello",
[]() {
std::cout << "world" << std::endl;
return 1;
}
},
{"--help",
[]() {
std::cout << "no help can be found in this world" << std::endl;
return 1;
}
}
};
auto where = Switch.find(argc == 1 ? "--help" : argv[1]);
return where == Switch.end() ? 1 : where->second();
}
Approche C++14
Cette approche est fortement inspirée de ce post https://dev.krzaq.cc/post/switch-on-strings-with-c11/
L'idée est de reposer sur les constexpr
pour calculer un hash à compile time, et donc traduire une chaîne en un entier, valide pour un switch. Un peu de sucre syntaxique avec les user defined literals et on obtient ça :
constexpr unsigned long hash(char const* str) {
unsigned long hash = 5381;
int c = 0;
while (c = *str++)
hash = hash * 33 + c;
return hash;
}
constexpr auto operator ""_h(char const str[], size_t) {
return hash(str);
}
int main(int argc, char **argv) {
switch(hash(argc == 1 ? "--help" : argv[1])) {
case "hello"_h:
std::cout << "world" << std::endl;
return 0;
case "--help"_h:
std::cout << "no help can be found in this world" << std::endl;
return 0;
default:
return 1;
};
}
Malheureusement l'implémentation de std::hash
n'est pas constexpr, sinon on aurait pu éviter de fournir une implem miteuse2 mais respectant la constexpritude.
On est pas loin du switch
sur des litéraux, et ça coûte un calcul de hash + le saut.
Conclusion
Comme souvent, on a le choix en C++, et c'est ce que j'aime dans ce langage (et ce qui en fait un langage difficile à utiliser en production). Mais la monotonie n'est pas prête de venir toquer à la porte !
-
petit conseil : si ça chambre, aère ! ↩
# Commentaire supprimé
Posté par Anonyme . Évalué à 1. Dernière modification le 31 août 2016 à 11:41.
Ce commentaire a été supprimé par l’équipe de modération.
[^] # Re: Plusieurs remarques
Posté par serge_sans_paille (site web personnel) . Évalué à 9.
Tout à fait d'accord avec toi; C'est même écrit dans le journal :-/
[…] on aurait pu éviter de fournir une implem miteuse
Tout à fait d'accord avec toi; C'est même écrit dans le journal :-/
Et pour les chagrins qui veulent du code dans leur case: on peut utiliser une std::function
Avec un code qui illustre ça
# Vérification de l'égalité?
Posté par ®om (site web personnel) . Évalué à 10. Dernière modification le 31 août 2016 à 12:19.
Étant donné qu'une fonction de hash (ici: String → int) n'est pas injective, il faut vérifier l'égalité de chaînes même dans le cas où elles ont le même hash. Où est-elle effectuée dans ton dernier exemple?
Le switch sur les Strings en Java 7 calcule aussi le hash, mais c'est juste une question d'optimisation (random link) ; évidemment cela ne permet pas de se passer de la vérification de l'égalité.
blog.rom1v.com
# Map à la place du swicth/case
Posté par barmic . Évalué à 7.
C'est ce que je fais de plus en plus (alors que mon langage a un switch sur les java.lang.String ;) ). Je vois de plus en plus de problèmes aux
switch/case
:case
leak son contexte avec le reste du codebreak
?)case
Du coup pour toutes ces raisons j'ai une lourde préférence pour utiliser une Map. Je la déclare et construit en statique à la classe, donc le coût de construction n'est payé d'une seule fois par exécution et tant que je n'ai pas de vrai besoin de performance (et que l'on a pas encore établi que les problème venait de là) je garde le tout sous cette forme.
Tous les contenus que j'écris ici sont sous licence CC0 (j'abandonne autant que possible mes droits d'auteur sur mes écrits)
[^] # Re: Map à la place du swicth/case
Posté par ®om (site web personnel) . Évalué à 4. Dernière modification le 31 août 2016 à 14:05.
Éventuellement tu peux scoper :
Ce pattern n'est pas si mal adapté aux switches :
En java en plus dans une
Map
, tu vas devoir wrapper ces valeurs dans des objets.blog.rom1v.com
[^] # Re: Map à la place du swicth/case
Posté par barmic . Évalué à 3.
Ça résous que partiellement, tu n'a pas vraiment de lisibilité sur quels sont les données d'entrée et de sortie de chaque cas.
Oui c'est une option (c'est aussi une solution pour ce dont je parle au dessus). Mais vraiment je préfère une construction comme ici (avec guava) :
On peut plus tard imaginer charger ces valeurs depuis un fichier.
J'avoue ne pas connaître le coût de l'auo(un)boxing. Tu as plus d'info là dessus ? (je sais qu'il y a des systèmes de cache dans la JVM pour certaines valeurs par exemple)
Tous les contenus que j'écris ici sont sous licence CC0 (j'abandonne autant que possible mes droits d'auteur sur mes écrits)
[^] # Re: Map à la place du swicth/case
Posté par ®om (site web personnel) . Évalué à 3. Dernière modification le 31 août 2016 à 14:59.
Les objets, ça prend de la place. Les
HashMap
s en particulier :)Cf Android Memories à partir de la slide 24.
Oui, par exemple l'autoboxing des
int
utilise un cache pour les valeurs sur 8 bits. Cf l'implémentation deInteger.valueOf(int)
(qui est appelé automatiquement pour l'autoboxing).Perso je trouve ça très verbeux.
En C++ c'est plus joli :
blog.rom1v.com
# Lisibilité du code
Posté par Meku (site web personnel) . Évalué à 10.
Ben heureusement que c'était la version simplifiée ;-)
Bon j'imagine bien qu'ici il s'agit plus d'un exercice de style que la recherche d'une réelle solution à mettre en production.
D'un point de vue lisibilité du code, je trouve personnellement que :
Finalement, je crois que je préfère la version if/else, qui même moche, me semble plus expressive car moins polluée par des détails bas niveau du langage (le concept de switch/case c'est quand même du niveau très bas, et là ça donne une impression de vouloir inventer une roue de manière compliquée).
J'aimais beaucoup le C++ quand j'étais jeune, mais depuis j'accorde de plus en plus d'importance à la lisibilité et le C++ me paraît de plus en plus compliqué de ce point de vue là. Parfois, j'aurais plus envie de le classer comme un langage d'artiste que comme un langage industriel. Bien sûr, on peu se restreindre à faire du code assez simple, mais j'ai toujours peur de devoir repasser derrière un artiste x_x
[^] # Re: Lisibilité du code
Posté par barmic . Évalué à 4.
Personnellement il m'en faut bien plus (quelque soit le langage) pour vérifier que l'on fait un test d'égalité sur la même variable et qu'on a pas des doublons… Le
if/else
laisse toute l'amplitude a faire tout et n'importe quoi ce que je trouve très gênant. Personnellement je considère la multiplication deif
dans un code comme un code smell donc je préfère éviter dans ajouter.Question d'habitude. Quand tu commence à jouer avec des API fluent, ça se lit vite.
Là AMHA le problème vient des lambdas du C++ qui sont trop complexes.
Tous les contenus que j'écris ici sont sous licence CC0 (j'abandonne autant que possible mes droits d'auteur sur mes écrits)
[^] # Re: Lisibilité du code
Posté par Guillaum (site web personnel) . Évalué à 4.
En fait l’implémentation est complexe, mais l'usage est relativement simple et lisible. De mon point de vue il n'est pas forcement nécessaire de comprendre l’implémentation d'une abstraction pour l'utiliser correctement.
Ainsi j'aime bien l'approche
StringSwitch
qui est très claire (à mon sens). Je trouve que l'approche à base destd::map
ne devrait pas exposer lastd::map
directement ni lefind
. Je ferais un truc du genre : (Api non testé).# Pas uniquement string
Posté par Firwen (site web personnel) . Évalué à 3. Dernière modification le 31 août 2016 à 17:15.
Ça a quelques années déja, mais Bjarne Stroustrup avait publié à l'époque une petite lib permettant de faire du pattern matching "à l'OCaml" en C++.
Donc j'imagine également du "switch/Case" avec des string. ( j'imagine car je n'ai jamais joué avec )
https://github.com/solodon4/Mach7
Si tu veux t'amuser avec.
[^] # Re: Pas uniquement string
Posté par Guillaum (site web personnel) . Évalué à 2.
C'est pas clair à quel point c'est du C++ ou à quel point c'est une extension au standard actuel. La syntaxe est tout de même vraiment répugnante.
Avec d'autres langages, le pattern matching c'est extrêmement puissant, concis et clair et cela fait partie des outils qu'il me manque dramatiquement quand je fais du C++.
[^] # Re: Pas uniquement string
Posté par Firwen (site web personnel) . Évalué à 1.
C'est du pure C++ sauf erreur de ma part.
Question de goût. Je trouve la syntaxe au contraire très clair pour du code templaté.
Par contre comme tout comme templaté assez poussé, je suis curieux de la taille des messages d'erreurs à la compilations
Je ne suis on ne peut plus d'accord. C'est au programme du comité de normalisation C++, donc espérons.
[^] # Re: Pas uniquement string
Posté par Guillaum (site web personnel) . Évalué à 2.
La dessus, rien à dire ;) Mais bon, j'ai un peu pris l'habitude d'Haskell (ou autre). Pour le troll, comparons :
Vachement plus compact, lisible et polymorphique ;) (mais bon, je troll ;)
[^] # Re: Pas uniquement string
Posté par Guillaum (site web personnel) . Évalué à 2.
Je m'auto répond. Un point qui sent vraiment mauvais dans cette approche c'est que pour stocker le résultat d'un match, il faut déclarer une variable non initialisée, exemple :
Ce qui veut dire que on peut se servir d'une variable mal initialisée dans une branche. J'ai tendance à ne plus supporter çà et à refuser les revues de code qui en contiennent.
[^] # Re: Pas uniquement string
Posté par ®om (site web personnel) . Évalué à 2.
En C++,
var<int> m;
est default-initialized.Pour les types primitifs, ça veut dire non initialisé, mais pour un type complexe pas forcément.
blog.rom1v.com
[^] # Re: Pas uniquement string
Posté par Guillaum (site web personnel) . Évalué à 2.
C'est comme dire non initialisé, mais en pire ;)
Autant cela à un sens plus ou moins arbitraire pour certains types (genre
std::vector
), mais pourquoi unbool
serait plusfalse
quetrue
ou unint
mérite-t-il plus d'être0
que30
. Tant qu'on est dans le code-smell, j'ai tendance à faire des bonds sur les classes qui proposent un constructeur par défaut vide avec des valeurs par défaut. Il faut vraiment justifier que le choix par défaut à du sens.Généralement je préfère une fonction static ou free avec un nom explicite pour les cas par défaut.
[^] # Re: Pas uniquement string
Posté par pasBill pasGates . Évalué à 0. Dernière modification le 01 septembre 2016 à 21:17.
Parce que dans le cas géneral une variable non initialisée a de fortes chances de se retrouver avec tous les bits à zero car l'endroit en mémoire ou elle est allouée est à zero (tant que c'est pas sur la stack) ?
[^] # Re: Pas uniquement string
Posté par Guillaum (site web personnel) . Évalué à 0.
Ma question n'était pas la raison technique qui fait que c'est plutôt des bits à zéro qu'autre chose. Je voulais plutôt discuter du coté totalement arbitraire des initialisation par défaut.
Les valeur par défaut sont, à mon sens, plus dangereuses que les valeur non-initialisée. Parce que non-initialisée, cela fait souvent n'importe quoi, et dans le meilleur des cas, cela plante, et tu trouves vite le bug et au moins tu obtient un comportement instable qui t'invites à chercher une erreur. Mais lorsque c'est initialisé par défaut, tu vas obtenir un comportement stable, souvent cohérent, mais qui est totalement faux.
De plus, les valeurs par défaut des types primitifs sont assez évidentes, mais celles de types plus élaborés sont totalement dépendants des choix des auteurs de la classe, et cela rend le code bien moins lisible. Il faut aussi voir que les valeurs par défaut peuvent changer au bon vouloir du l'auteur de la classe, et celui-ci peut penser que son changement est indolore alors qu'en fait il détruit ton code.
[^] # Re: Pas uniquement string
Posté par pasBill pasGates . Évalué à 4.
Ca peut être ton sens, mais en pratique ce n'est absolument pas le cas.
L'exemple flagrant est les pointeurs, notamment pointeurs de fonction. Si tu le garde non-initialisé tu as un énorme trou de sécurité potentiel.
Ensuite, il n'y a absolument aucun besoin d'avoir un code instable pour aller chercher ces erreurs, n'importe quel compilateur décent te détectera une variable non-initialisée sur demande
Gni ? Tu demandes quoi là ? Evidemment que l'auteur de la classe va mettre ce qu'il désire dans le constructeur par défaut, c'est son code ! Tu veux faire quoi à la place ?
[^] # Re: Pas uniquement string
Posté par Guillaum (site web personnel) . Évalué à 1.
C'est quoi la valeur par défaut d'un pointeur de fonction qui as du sens ?
nullptr
, ou une fonction au hasard ? Les deux cas n'ont pas de sens et sont dangereux, bref, avoir un pointeur "default-initialised" n'as pas de sens.Justement ;) : les valeurs non-initialisées sont moins dangereuses que les valeurs initialisée par défaut, car souvent les valeurs non initialisée vont te générer une erreur, alors que les valeurs par défaut ne vont pas t'en générer et tu continues sur un bout de code qui n'a plus de sens.
On parle bien du cas tu as un bug dans ton code et alors que tu pensais gérer tous les cas de figure, il en manque certains et au lieu de mettre une valeur qui a du sens dans ton code, tu finis avec une valeur par défaut qui n'en a pas.
Je ne veux pas de constructeur par défaut ! Exemple, si tu croises le bout de code suivant
Color c;
, quel est la valeur d'uneColor
? Maintenant tu croisesColor c = Color::Black()
. Cette seconde solution est :a) Bien plus facile à lire / comprendre. Il est plus explicite. Il demande aussi moins de surcharge intellectuel au développeur qui n'a pas à retenir la liste des cas par défaut pour toutes les classes.
b) Il est moins sujet aux changements des comportements par défaut, je détail ce point qui te faisait bondir.
Imagine une interface de la classe
Color
comme suit :Ici mon seul contrat concernant le constructeur par défaut c'est le petit commentaire qui dit "black". C'est faible, et demain ce constructeur par défaut peut changer très facilement pour construire une autre couleur, sans doute le blanc, car pour les besoins de ce projet on fait plus du multiplicatif sur les couleurs que de l'additif, donc le blanc semble un meilleur choix par défaut. Tu peux aussi imaginer un refactoring dans lequel on change la classe de gestion de couleur, la plupart des opérations ont la même sémantique, sauf ? Les choix arbitraire des constructeurs par défaut.
Une autre conception pourrait donner :
Dans ce cas là, pas de constructeur par défaut, et des constructeurs aux noms bien plus explicites et plus plus robustes, ce n'est pas une valeur par défaut arbitraire, c'est
Black
ouWhite
, c'est écrit en dur dans le nom de la fonction, donc il faudrait vraiment être pervers pour faire un refactoring dans lequel la nouvelle classeColor
renvoie du vert dans sa fonctionWhite
.En résumé, je préfère l'explicite et j'évite les les valeurs par défauts, les constructeurs par défauts, les arguments par défauts sur les fonction. Tout cela parce que de mon expérience, c'est trop facile de se tromper, et cela coûte trop cher. J'évite aussi les variables non initialisées (ou initialisées par défaut) qui servent de valeur de sortie d'une fonction en passage par référence, parce que c'est encore un coup à se planter. Je veux qu'à tout moment dans mon code, si j'ai accès à une variable, elle ai un sens.
[^] # Re: Pas uniquement string
Posté par Firwen (site web personnel) . Évalué à 4.
L'initialisation par défaut a du sens et c'est évident. Je sais que les programmeurs Haskell adore rappeler au autres à quel point leur langage est supérieur à la plèbe de ce monde car il n'autorise pas l'initialisation, mais la tu fais preuve d'un poil de mauvaise fois :)
Un dangling pointer va te causer des effets de bords alétoire, dépendant de la platforme et de l'initialisation de la mémoire. Dans le cas de l'utilisation d'un pointeur non initialisé, tu peux trés bien obtenir un programme qui marchera pendant 10 ans car ton pointeur utilise une région de mémoire qui est aléatoirement remplit de zero, et qui va segfaulter aleatoirement aprés dix ans car pour une raison X, ce segment de mémoire était différent ce jour là.
Dans le cas d'une initialisation forcée à null_ptr, tu auras un comportement déterministique ( qui segfaultera ) ce qui est déja bien plus "safe".
L'initialisation par défaut doit TOUJOURS être fait, sauf si trés bonne raison, point barre.
Dans ce cas, ton problème est ton développeur qui trouve bon de changer le comportement par défaut d'une fonction sans changer l'API. Ce qui est idiotique et inacceptable dans tout code partagé. Le problème ce n'est pas l'initialisation par défaut, encore une fois.
[^] # Re: Pas uniquement string
Posté par foobarbazz . Évalué à 2.
C'est un comportement non défini en C, occasionnellement ça peut faire des choses super étranges…
http://stackoverflow.com/questions/22847539/does-dereference-a-null-pointer-guarantee-to-crash-a-program-in-c-c
[^] # Re: Pas uniquement string
Posté par barmic . Évalué à 4.
Vous n'avez pas l'air de vous comprendre…
Il ne veut pas d'initialisation automatique silencieuse, il veut que celui qui crée l'objet se pose la question de la valeur que cet objet doit avoir.
Tous les contenus que j'écris ici sont sous licence CC0 (j'abandonne autant que possible mes droits d'auteur sur mes écrits)
[^] # Re: Pas uniquement string
Posté par Anthony Jaguenaud . Évalué à 2.
Pour ça, il suffit de déclencher une exception dans le constructeur par défaut, non ?
[^] # Re: Pas uniquement string
Posté par barmic . Évalué à 3.
Personne n'a dit que c'était impossible :)
Si c'est pour empêcher ce genre de choses, je préfère par contre déclarer le constructeurs comme privé comme ça c'est vérifié systématiquement à la compilation.
Tous les contenus que j'écris ici sont sous licence CC0 (j'abandonne autant que possible mes droits d'auteur sur mes écrits)
[^] # Re: Pas uniquement string
Posté par groumly . Évalué à 3.
Non. Le but c'est que le code ne compile pas si une variable peut ne pas être initialisée au moment où elle utilisée.
Swift fait ca notamment. Pas de valeurs par défaut, tu doit faire un choix explicite avant d'utiliser la variable (que ca soit une locale, un field ou quoi que ce soit d'autre).
Pour null, les optionals, c'est bon, mangez en :)
Linuxfr, le portail francais du logiciel libre et du neo nazisme.
[^] # Re: Pas uniquement string
Posté par Guillaum (site web personnel) . Évalué à 4.
Désolé, mon commentaire était sans doute puant à la mode élitiste Haskell, ce n'était pas mon but et je me suis mal exprimé.
Je communique mon intérêt pour Haskell, et que ce langage influence beaucoup ma façon de travailler, mais au final le C++ reste mon outil de travail et d'enseignement. C'est un langage que j'apprécie, malgré ses défauts. Il me permet de faire mon travail alors que je ne pourrais pas le faire en Haskell pour des raisons de performance.
Ai-je tort d'essayer d'appliquer des principes inspirés d'autres langages pour rendre mon code C++ plus robuste ? Ai-je tort de critiquer dans un but d'amélioration, un langage que j'utilise tous les jours? Par exemple, je suis très heureux de l'arrivée des
std::optional
en C++17, mais je trouve que l'api proposée est un échec total puisqu'elle permet beaucoup d'erreurs qu'une api différente aurait évitée et à ce niveau ils auraient pu s'inspirer d'Haskell (ou de Rust, ou de OCaml, …). Au final, je ne me servirais des std::optional qu'une fois enrobés dans une API plus sécurisée.Pour le reste, d'autres ont déjà montrés qu'un pointeur initialisé à null par défaut est potentiellement un comportement indéfini, et dans ce cas tout aussi dangereux qu'un pointeur non initialisé. Mais mon point n'était pas que je conseil de privilégier la non initialisation à l'initialisation par défaut. Je veux juste bannir les deux autant que possible et je pense qu'il est possible de construire des API qui apportent cette sécurité.
Pour finir, je préfère les valeurs explicites car je pense que cela augmente la lisibilité du code. Et à ce niveau, les constructeurs par défaut ne sont pas explicite dans leur intentions. Encore une fois, quel doit être le constructeur par défaut d'une Matrice 4x4 ? d'une couleur ? d'un répertoire ? d'une image ? Je ne sais pas répondre à ces questions, et c'est pour cela que je préfère des fonctions / méthodes statiques avec des noms explicites que des constructeurs dont le comportement est arbitraire et implicite.
[^] # Re: Pas uniquement string
Posté par pasBill pasGates . Évalué à 3.
Qu'on se comprenne.
Le fait que les variables non-initialisées sont bcp plus dangereuses que les variables initialisées à une valeur par défaut est un fait établi. Ce n'est pas un cas d'opinion.
nullptr est la bonne valeur, et la raison est super simple :
Bref tu peux penser ce que tu veux, mais si tu oses aller donner ton explication à n'importe quelle personne en sécurité (moi par exemple, qui a passé les 12 dernières années dans le milieu) tu vas leur filer un arrèt cardiaque tellement ton explication ne tient absolument pas la route et démontre un manque de compréhension des enjeux.
[^] # Re: Pas uniquement string
Posté par ®om (site web personnel) . Évalué à 3. Dernière modification le 02 septembre 2016 à 21:33.
En fait, pour être précis, déréférencer un pointeur null ne garantit pas que le soft crashe : c'est un comportement indéfini (undefined behavior).
Par exemple, compile ce code avec
gcc -O2
et exécute-le :(tiré d'un billet que j'ai écrit il y a quelque temps)
blog.rom1v.com
[^] # Re: Pas uniquement string
Posté par ®om (site web personnel) . Évalué à 2.
(si ça segfault avec
gcc -02
, essayer avecgcc -O -ftree-vrp
)blog.rom1v.com
[^] # Re: Pas uniquement string
Posté par pasBill pasGates . Évalué à 1.
Non qu'on se comprenne bien.
Déreferencer un pointeur NULL va causer une exception.
mov [eax],0x1 avec eax=0x0000000000000000 va causer un access violation
Ensuite que le compilo optimise ta représentation en C et enlève les instructions qui déreferencent NULL, c'est une autre histoire.
[^] # Re: Pas uniquement string
Posté par ®om (site web personnel) . Évalué à 3. Dernière modification le 02 septembre 2016 à 22:11.
+1
Par contre, vu qu'on parlait de l'initialisation des constructeurs en C++, on parlait bien de déréférencer "en C/C++", pas au niveau du binaire généré.
blog.rom1v.com
[^] # Re: Pas uniquement string
Posté par Guillaum (site web personnel) . Évalué à 4.
Je lis avec intérêt tes commentaires sur linuxfr, il en ressort que tu es compétant et surtout, tu as un avis différent de l'avis général, ce qui est souvent source de discussions intéressantes, mais qu'est ce que tu peux être impoli, c'est dingue ;)
Tu te permets de me traiter d’incompétent alors que tu n'as simplement pas compris mon discours, ou alors je me suis mal exprimé, on cherchera qui est le moins compétant en communication plus tard… Et tu te sers d'un argument d'autorité ce qui est petit.
Je ne veux absolument pas dire que le non initialisé est mieux ou moins bien que le initialisé par défaut, je veux dire que les deux sont dangereux. Mon argument est que les trucs par défaut arbitraire c'est dangereux. Si en tant que développeur tu veux le même comportement que celui par défaut, très bien, mais cela pourrait valoir le coup d'être explicite (çà c'est mon avis et c'est subjectif). Mais si tu ne veux pas le comportement par défaut mais qu'il apparaît parce que tu as fais une erreur et que tu n'est pas prévenu, et bien c'est faux et dangereux, et çà c'est factuel (enfin je pense, j'ai tort ?).
Oublions le cas des pointeurs qui est une particularité du fait de la valeur par défaut qui est nullptr et cela a plein d'implication marrantes que nous débattons. Mais qu'en est il d'autres types comme les entiers, les booléans où les char ? le \0 par défaut sur un char a certainement autant de chances de ne pas faire planter ton programme qu'un 42 ou un 12 par défaut, mais il est tout aussi faux si ce zéro n'était pas ton intention.
Sauf que le truc par défaut, le compilateur ne vas pas râler (son boulot c'est de mettre une valeur par défaut) et ton programme aura un résultat stable. Alors qu'une valeur non initialisé, le compilateur risque de râler et le programme n'aura pas un résultat stable, ce qui donne plus de chance de ce rendre compte du problème avant que le truc ne parte en production. J'ai eu un enseignant qui disant que le pire qui pouvait arriver dans un bug c'est que le programme ne plante pas, et le mieux c'est que cela plante, parce que cela permet de réaliser qu'il y a un bug. Et à ce niveau les valeur non initialisée augmentent les chances que cela plante comparée aux valeur par défaut arbitraire, d'où ma remarque initiale qui a lancée le "débat".
Bref, au final on est d'accord, non initialisé cela pue d'un point de vu sécurité, cela pue vraiment ! Mais par défaut, si ce par défaut n'est pas la valeur que tu veux, je trouve que cela pue aussi.
[^] # Re: Pas uniquement string
Posté par pasBill pasGates . Évalué à 1.
On va dire que des années dans la jungle de linuxfr m'ont endurci :)
Mai justement, moi ce que je te dis est que non-initialisé est PIRE que initialisé par défaut, et c'est factuel.
Ensuite cela ne veut pas dire que initialisé par défaut est super et idéal hein, mais à choisir entre les 2 un est bien plus sûr que l'autre.
Cela revient de nouveau au fait que la solution est meilleure que non-initialisé tout en n'étant pas idéale. Les valeurs par défaut choisies vont le plus souvent sauver le développeur qui n'initialise pas ses valeurs. Pas toujours, mais le plus souvent.
Idéalement le développeur choisit des options du compilo l'avertissant qu'il utilise une variable non initialisée. Mais sans cela, l'initialisation par défaut aide à sauver beaucoup de gens.
[^] # Re: Pas uniquement string
Posté par serge_sans_paille (site web personnel) . Évalué à 1.
Passionnant ! Merci !
# Duff's device
Posté par barmic . Évalué à 3.
Tiens puisqu'on parle de
switch
, que pensez-vous de « Duff's device » ?Personnellement je trouve ça immonde et je ne vois que des cas rares où il faut vraiment produire le code plus performant possible au détriment de tout le reste qui peuvent rendre ce truc acceptable…
Tous les contenus que j'écris ici sont sous licence CC0 (j'abandonne autant que possible mes droits d'auteur sur mes écrits)
[^] # Re: Duff's device
Posté par fearan . Évalué à 2.
je suis pour laisser le compilo gérer ce genre d'optimisation comme un grand; Ensuite ça illustre assez bien le fonctionnement d'un switch/case en C/C++.
Il ne faut pas décorner les boeufs avant d'avoir semé le vent
Suivre le flux des commentaires
Note : les commentaires appartiennent à celles et ceux qui les ont postés. Nous n’en sommes pas responsables.