Donc tu vois qu'il y a des vérifications à faire (même si t'a pas du beaucoup utiliser C++ dans ta vie…)
On va faire simple :
template<typenameT>voidfaire_quelquechose(Tt){t.
Montre moi comment tu autocomplête à cet endroit. Tu pourrais regarder les usages de faire_quelquechose(), même si là plupart du temps, ils ne sont même pas encore écrits. Tu pourrais me sortir l'intersection de tout les membres des T utilisés, ce qui n'est pas toujours pertinent, puisque tu ne sait peut-être pas que j'ai envie de spécialiser faire_quelquechose juste après. Et pourtant, ça n'est qu'une bête fonction template, alors imagine ce que c'est avec du vrai code qui fait de l'injection de dépendance (chouette, un buzzword) avec des templates
Oui, il faudrait pouvoir vérifier les usages. Quand on à du vrai code ou il y a des templates qui appellent d'autres templates, tu ne peux vérifier l'usage que lorsque du code non-template commencera à utiliser ta chaîne d'appel. Et si jamais il y a une erreur d'instantiation, tu ne sais jamais la faute de qui c'est. Autrement dit, ton IDE n'est pas plus avancé que ton compilateur, et un bête :make sous vim fait très bien la même chose que ton IDE.
Comment tu fais avec sed pour vérifier que tu n'es pas entrain de renommer une autre méthode d'une autre classe, mais qui a le même nom ?
La question ce n'est pas de savoir comment ne pas renommer une autre méthode d'une autre classe, mais de savoir si c'est pertinent.
classA{voidfaire_quelquechose();};classB{voidfaire_quelquechose();voidfaire_autre_chose();}// peut être utilisé avec A et B.template<typenameT>classUser1{voidf(T&t){t.faire_quelquechose();}};// peut être utilisé avec A et Btemplate<typenameT>classUser2{voidf(T&t){for(unsignedi=0;i<5;++i)t.faire_quelquechose();}};voidusage_actuel(){User1<A>user1;User2<B>user2;Aa;Bb;user1.f(a);user2.f(b);}
Je veut renommer A::faire_quelquechose() en A::do_something() avec un outil automatique. Est ce que je modifie que son usage dans C, ou est ce que je modifie tout, comme sed ? Dans les deux cas, le code compile quand même. Mais les commentaires donnent plutôt raison à sed.
Et encore, on est dans un cas simple, vu qu'on peut faire la même chose en C avec des macros. Si j'ajoute un petit utilisateur un peu plus compliqué (version C++11, je vous épargne la version C++03):
template<typenameT>autofaire_la_meilleure_chose(T*t,T*bidon)->decltype(t->faire_quelquechose()){t->faire_quelquechose();}template<typenameT>voidfaire_la_meilleure_chose(T*t,void*bidon){t->faire_autre_chose();}// peut être utilisé avec A, B, et aussi C que j'ai la flemme de définir.template<typenameT>voiduser3(T&t){faire_la_meilleure_chose(&t,&t);// et autre chose au passage.}voidusage_actuel(){Bb;user3(b);}
Pour ceux qui ne connaissent pas le C++ autant que nous deux (parce que tu le comprend ce code, hein ?), la version python est plus courte :
defuser3(a):ifhasattr(a,"faire_quelquechose")andcallable(a.faire_quelquechose):a.faire_quelquechose()else:a.faire_autre_chose()# et autre chose au passage.
(et qu'on vienne pas me dire que C++ ne fait pas de duck-typing)
Sachant que B à aussi faire_autre_chose(), ne pas renommer l'usage par user3 va aussi produire un code qui compile. On est même pas dans un programme complet, mais on à déjà deux choix à faire, et donc 4 cas possibles. Seul le programmeur pourrai choisir ce qu'il faut faire pour ne pas se retrouver avec du code qui foire à l'exécution, et un simple sed amélioré qui demande si il faut remplacer ou pas est ce qu'il y a de mieux, un peu comme :%s/faire_quelquechose/faire_autrechose/gc dans un vim, quoi.
Et là ton programme il compilait et il connaissait les utilisateurs. Imagine si c'était une bibliothèque et que ce seront les utilisateurs de la bibliothèque qui écriront usage_actuel(). Tu ne peux pas vérifier tout les cas dans un vrai programme, parce qu'il y en a plusieurs milliers (si le nombre est fini), et avec tes tests, tu n'en couvrira qu'une toute petite partie.
Je n'es pas parlé de faire un calcul de tout les chemins (je vois pas pourquoi tu parle de ça). Je vois pas en quoi c'est non-pertinent (mais c'est bien reste avec tes aprioris et ton assurance que toi tu sais contrairement aux autres). grep va te ressortir un tas de faux-positif (si ça te conviens tant mieux, il m'arrive d'utiliser ack).
Simple : Parce que tout les chemins pertinents, tu ne les connais pas, pourtant tu en à besoin, rien que pour simplement renommer une méthode, et ce, même si ces chemins ne sont pas utilisés (ou alors pas encore). grep va sortir plein de faux positifs, tout comme Visual C++ 2010. Moi, personnellement, je préfère les faux positifs aux faux négatifs.
Ça n'a rien de compliqué de mettre en place ce genre de truc. En Python Tu dois pouvoir faire un truc qui marche pas superbien mais qui dépanne avec un grep sur le def (c'est plus technique en C, C++ et java qui n'ont pas de mot clef pour ça).
Ça n'a rien de très compliqué de rechercher les vrais usages dans un code qui compile, le compilo le fait très bien. Le problème, c'est que d'un point de vue du programmeur, du code peut être lié à d'autre code, même si le chemin n'est pas utilisé.
Aller, un exemple en C pour la route :
#ifdef ACTIVE_LE_DEBUG_STEPLAITvoiddebug(intseverity,constchar*format,...);// avec définition ailleurs.#elsestaticinlinevoiddebug(intseverity,constchar*format,...){}#endif
Si le debug est activé, la version sans debug n'est jamais utilisée. pourtant elle est importante, et doit suivre les évolutions de la fonction réellement utilisée. Cela peut rendre la fonction "sauter à la définition" non-pertinente, puisqu'il y a deux définitions.
Et ce cas là est facile, puisque si on ignore les macros, on peut voir deux déclarations.
Je présume que tu n'édite pas tout les modules de ton projet en même temps (si c'est le cas tu a peut être un problème d'architecture) de plus je présume que tu ne modifie pas les bibliothèques que tu utilise.
Une des bibliothèques que j'utilise, est boost. Je ne la modifie pas, mais je crée des chaînes de templates dedans qui peuvent potentiellement appeler mon code pas-fini (ou même pas écrit) en retour.
C'est une bonne pratique en C++ et en python. Quand tu renomme une classe ce qui prend le plus de temps c'est de le faire partout où elle est utilisée.
Lorsque ta classe est déjà utilisée, oui, c'est ce qui est le plus long, mais généralement, je modifie plus souvent les noms des classes que je n'ai pas fini de l'écrire qu'autre chose, surtout quand je me rend compte au dernier moment que la classe que j'écris devrai être coupée en deux.
Quand à faire du une classe = un fichier en C++, non merci, mes classes sont bien trop petites pour ça. Et j'ai trop de mauvaises expériences avec de gros projets java de 10000 fichiers de 100 lignes qui sont impossibles à naviguer, même avec un IDE.
C'est des langages où il est plus compliqué de créer des IDE aussi poussé que ce que l'on trouve ailleurs. Le C++ a une syntaxe très compliquée (y compris pour écrire un compilateur), python a le typage dynamique et le C est très laxiste.
C'est l'un des points qui fait le succès de Java d'avoir permis la création d'outil autour du langage. C'est quelque chose de recherché et non pas un effet de bord (ça simplifie la compilation, l'analyse statique (pour détecter d'éventuelle erreur et failles), etc).
Peut-être, mais tout le monde ne code pas en Java, et Java n'est pas la solution à tout. Les autres langages sont peut-être moins parsables, mais ils ne sont pas inintéressants pour autant. Certains font des choses que Java ne pourra jamais faire.
On trouve pourtant des IDE pour le C++ (qdevelop) qui font pas mal de choses déjà).
Si tu regarde qdevelop, c'est plutôt un IDE pour ceux qui font du C++ avec Qt, c'est à dire avec un framework orienté objet avec presque pas de templates. Mais pour le reste, il est rapidement perdu.
Par contre je ne comprends toujours pas t'a vraiment voulu dire que le C++ fais du duck-typing ???
On peut faire du mal aux mouches pour définir ce qu'est le duck-typing, mais de mon point de vue, c'est tout comme. Tu définit des fonctions qui prennent n'importe quoi en paramètre sur lequel tu peux utiliser n'importe quelle opérations du moment qu'elle éxiste. Tu peux aussi tester si une opération est possible et faire autre chose si elle ne l'est pas. Si ce n'est pas du "si ça cancane, ça nage et que ça marche comme un canard, alors c'est un canard", alors je ne vois pas ce que c'est.
[^] # Re: IDE
Posté par Batchyx . En réponse au journal opa-watch: compilation et lancement automatique à l'édition. Évalué à 4.
On va faire simple :
Montre moi comment tu autocomplête à cet endroit. Tu pourrais regarder les usages de faire_quelquechose(), même si là plupart du temps, ils ne sont même pas encore écrits. Tu pourrais me sortir l'intersection de tout les membres des T utilisés, ce qui n'est pas toujours pertinent, puisque tu ne sait peut-être pas que j'ai envie de spécialiser faire_quelquechose juste après. Et pourtant, ça n'est qu'une bête fonction template, alors imagine ce que c'est avec du vrai code qui fait de l'injection de dépendance (chouette, un buzzword) avec des templates
Oui, il faudrait pouvoir vérifier les usages. Quand on à du vrai code ou il y a des templates qui appellent d'autres templates, tu ne peux vérifier l'usage que lorsque du code non-template commencera à utiliser ta chaîne d'appel. Et si jamais il y a une erreur d'instantiation, tu ne sais jamais la faute de qui c'est. Autrement dit, ton IDE n'est pas plus avancé que ton compilateur, et un bête
:makesous vim fait très bien la même chose que ton IDE.La question ce n'est pas de savoir comment ne pas renommer une autre méthode d'une autre classe, mais de savoir si c'est pertinent.
Je veut renommer A::faire_quelquechose() en A::do_something() avec un outil automatique. Est ce que je modifie que son usage dans C, ou est ce que je modifie tout, comme sed ? Dans les deux cas, le code compile quand même. Mais les commentaires donnent plutôt raison à sed.
Et encore, on est dans un cas simple, vu qu'on peut faire la même chose en C avec des macros. Si j'ajoute un petit utilisateur un peu plus compliqué (version C++11, je vous épargne la version C++03):
Pour ceux qui ne connaissent pas le C++ autant que nous deux (parce que tu le comprend ce code, hein ?), la version python est plus courte :
(et qu'on vienne pas me dire que C++ ne fait pas de duck-typing)
Sachant que B à aussi faire_autre_chose(), ne pas renommer l'usage par user3 va aussi produire un code qui compile. On est même pas dans un programme complet, mais on à déjà deux choix à faire, et donc 4 cas possibles. Seul le programmeur pourrai choisir ce qu'il faut faire pour ne pas se retrouver avec du code qui foire à l'exécution, et un simple sed amélioré qui demande si il faut remplacer ou pas est ce qu'il y a de mieux, un peu comme
:%s/faire_quelquechose/faire_autrechose/gcdans un vim, quoi.Et là ton programme il compilait et il connaissait les utilisateurs. Imagine si c'était une bibliothèque et que ce seront les utilisateurs de la bibliothèque qui écriront
usage_actuel(). Tu ne peux pas vérifier tout les cas dans un vrai programme, parce qu'il y en a plusieurs milliers (si le nombre est fini), et avec tes tests, tu n'en couvrira qu'une toute petite partie.Simple : Parce que tout les chemins pertinents, tu ne les connais pas, pourtant tu en à besoin, rien que pour simplement renommer une méthode, et ce, même si ces chemins ne sont pas utilisés (ou alors pas encore). grep va sortir plein de faux positifs, tout comme Visual C++ 2010. Moi, personnellement, je préfère les faux positifs aux faux négatifs.
Ça n'a rien de très compliqué de rechercher les vrais usages dans un code qui compile, le compilo le fait très bien. Le problème, c'est que d'un point de vue du programmeur, du code peut être lié à d'autre code, même si le chemin n'est pas utilisé.
Aller, un exemple en C pour la route :
Si le debug est activé, la version sans debug n'est jamais utilisée. pourtant elle est importante, et doit suivre les évolutions de la fonction réellement utilisée. Cela peut rendre la fonction "sauter à la définition" non-pertinente, puisqu'il y a deux définitions.
Et ce cas là est facile, puisque si on ignore les macros, on peut voir deux déclarations.
Une des bibliothèques que j'utilise, est boost. Je ne la modifie pas, mais je crée des chaînes de templates dedans qui peuvent potentiellement appeler mon code pas-fini (ou même pas écrit) en retour.
Lorsque ta classe est déjà utilisée, oui, c'est ce qui est le plus long, mais généralement, je modifie plus souvent les noms des classes que je n'ai pas fini de l'écrire qu'autre chose, surtout quand je me rend compte au dernier moment que la classe que j'écris devrai être coupée en deux.
Quand à faire du une classe = un fichier en C++, non merci, mes classes sont bien trop petites pour ça. Et j'ai trop de mauvaises expériences avec de gros projets java de 10000 fichiers de 100 lignes qui sont impossibles à naviguer, même avec un IDE.
Peut-être, mais tout le monde ne code pas en Java, et Java n'est pas la solution à tout. Les autres langages sont peut-être moins parsables, mais ils ne sont pas inintéressants pour autant. Certains font des choses que Java ne pourra jamais faire.
Si tu regarde qdevelop, c'est plutôt un IDE pour ceux qui font du C++ avec Qt, c'est à dire avec un framework orienté objet avec presque pas de templates. Mais pour le reste, il est rapidement perdu.
On peut faire du mal aux mouches pour définir ce qu'est le duck-typing, mais de mon point de vue, c'est tout comme. Tu définit des fonctions qui prennent n'importe quoi en paramètre sur lequel tu peux utiliser n'importe quelle opérations du moment qu'elle éxiste. Tu peux aussi tester si une opération est possible et faire autre chose si elle ne l'est pas. Si ce n'est pas du "si ça cancane, ça nage et que ça marche comme un canard, alors c'est un canard", alors je ne vois pas ce que c'est.