Tout cela me parait fort subjectif.
Pour en pas violer le dogme relatif aux goto, on viole le dogme relatif aux indentations...
Version imprimable
Tout cela me parait fort subjectif.
Pour en pas violer le dogme relatif aux goto, on viole le dogme relatif aux indentations...
Quoi ?
Le elif est parfaitement indenté.
Salut
Allons oodini, le choix d'indentation n'a aucun impact sur le sens du code.
Globalement, ce qui ressort des discussions est que goto n'est utile que dans de très rares cas. Dans le tien, il ne t'apporte rien : le code n'est pas plus lisible, pas plus efficace, pas plus sécurisé. Pire : tu ne peux pas utiliser les one liners comme tu l'as fait, tu es obligé de mettre des accolades, ce qui n'est pas le cas dans le elif.
Je ne vois ce qu'il y a de subjectif là dedans, ne sont-ce pas des faits ?
Salut,
Pour le problème de base ayant provoqué la question, je crois que je commencerais déjà par essayer de voir s'il n'y a vraiment pas moyen d'abandonner les chaines de caractères au profit de valeurs numériques et cela pour les deux raisons classiques qui sont
S'il n'y a vraiment pas moyen d'éviter la comparaison de chaines de caractères, je me demande si je ne m'orienterais pas carrément vers une map dont la clé serait la chaine de caractères et la valeur un pointeur de fonction (ou mieux: un pointeur sur foncteur :D) appelant les fonctions nécessaires, permettant d'avoir un code proche de
- le gain de performances (ou plutot la perte de performance qu'occasion la comparaison entre chaines de caractères :D)
- la possibilité d'utiliser le test à choix multiple, rendant de facto le goto inutile :D
Cela aurait, en plus, l'avantage d'éviter de comparer la chaine à rechercher avec l'ensemble des chaines existantes, l'alogrithme de recherche dans une map étant dichotomique ;)Code:
1
2
3 std::map<std::string, Functor *>::const_iterator it = lamap.find(lachaine); if( it!= lamap.end()) it->second();
Mais pour répondre à l'interrogation initiale concernant le goto (quand meme :D)
J'avoue sans honte ne vraiment pas être un ardent défenseur du goto, parce que j'estime vraiment qu'il cause énormément de tord à la programmation structurée.
Il faut dire que, quand un prof qui fait une fois et demie à deux fois ton poids te menace de te jeter par la fenêtre si tu utilises goto, ca aide à prendre l'habitude de s'en passer :D
Mais, plus sérieusement, je n'ai jamais eu besoin d'utiliser goto, meme quand je programmais en C ou en Cobol, et au dela du simple dogme, je trouve que c'est comme beaucoup de chose: il se peut qu'il soit utile dans certaines circonstances très particulières, et à condition qu'il soit utilisé avec parcimonie, mais, à moins que ce ne soit vraiment la meilleure (ou plutot la moins mauvaise) des solutions envisageables, il est quand meme beaucoup plus sur de l'éviter.
Ne serait-ce que parce que, s'il est utilisé de manière impropre ou inadaptée, on a tôt fait de se retrouver avec un "code spagetti" dans lequel il devient de se retrouver ;).
J'y vais de mon avis personnel :
1/ goto en C++ est inutile.
Dans le cas de l'OP, une simple gestion à base de if (...) else if (...) fonctionne très bien, donc je ne vais pas revenir dessus.
C++ offre la possibilité d'utiliser RAII pour contrôler la gestion des ressources. En cas d'erreur d'exécution, les ressources seront libérées automatiquement si on sort de la fonction. RAII, auquel on ajoute l'écriture de code résistant aux exceptions permet de s'assurer que l'état en sortie d'une fonction qui a échoué est le même qu'en entrant dans cette fonction.
Au cas où l'on souhaite sortir de boucle imbriquées, et étant donné que l'algorithme en O(n^2) est nécessairement assez important (en tout cas, plus qu'un appel de fonction), autant écrire l'algorithme dans une fonction séparée et provoquer la sortie de la fonction. Ca permet en outre d'éviter de réécrire le même code 40 lignes plus loin (parce que les algos nécessitant des boucles imbriquées ont tendance à se répéter) et de profiter de RAII pour gérer les cas d'erreur.
Enfin, la remontée d'erreur peut se faire vie l'utilisation d'exceptions.
C++ fournit tous les outils permettant de se passer de goto, sans impact notable sur le code lui-même. Autant les utiliser.
2/ goto en C peut être utile, à condition de savoir ce qu'on fait.
En fait, C offre des outils limités. Dans le cas ou l'on souhaite sortir d'une boucle imbriquée, je répéterais ce que j'ai dis plus haut. Mais on a un problème lorsqu'on souhaite gérer efficacement les erreurs.
C n'offre pas la possibilité d'implémenter RAII. Du coup, il peut être utile d'utiliser goto avec parcimonie lorsque le besoin s'en fait sentir. Ceux qui se sont lancé dans l'étude du code source du kernel linux peuvent se rendre compte de la fréquence d'utilisation de goto pour traiter les cas d'erreur.
Sans passer par des goto, le code devient vite difficilement lisible - car à chaque étape, il faut répéter le code de librération des ressource en cas d'échec. La duplication mène à la possibilité de bug dans ce cas, et ne facilite pas la maintenance. Une autre solution serait d'écrire un code ressemblant à :Code:
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
26 static struct my_driver driver_obj; void my_driver_init() { if (my_driver_probe() < 0) return -ENOENT; driver_obj.mem = kzalloc(...); if (!driver_obj) return -ENOMEM; if (my_driver_fetch_pci_registers(&driver_obj) < 0) goto err_fetch_registers; if (my_driver_send_reset(driver_obj) < 0) goto err_reset; /* encore plein de code qui, à chaque étape, peut échouer */ return 0; err_reset: my_driver_restore_pci_registers(driver_obj); err_fetch_pci_registers: kfree(driver_obj.mem); return -1; }
Mais dans ce cas, on diminue sensiblement la lisibilité (d'autant plus que le style de code nécessite une tabulation de 8 espaces). Quelques uns auront pensé à une autre solution : l'utilisation de fonctions plus simples, qui sont appelées en cascade. Sauf qu'il ne faut quand même pas oublier qu'on est dans un kernel d'OS, et que tout appel de fonction a un coût (et qu'il y a d'autres considérations à ne pas perdre de vue, comme le linker interne du kernel qui va libérer la mémoire prise pas les fonctions du segment __init une fois ce code exécuté, ou ne pas charge le code du segment __exit si le driver est intégré au kernel ; il y a plein de subtilité, mais le gros point noir des appels de fonctions reste quand même le cout de l'appel en lui même).Code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14 if (my_driver_probe() == 0) { driver_obj.mem = kzalloc(...); if (driver_obj.mem) { if (my_driver_fetch_pci_registers(&driver_obj) == 0) { if (my_driver_send_reset(driver_obj) == 0) { ... return 0; } my_driver_restore_pci_registers(driver_obj); } kfree(driver_obj.mem); } }