Bonjour,
J’ai récemment découvert les algorithmes de la STL et les foncteurs en particulier. Grâce à la F.A.Q et à ce forum j’y commence à y voir un peu plus clair. En revanche, je suis toujours dans le noir complet sur la manière dont on les utilise dans du vrai code de production (ou même au-delà d’un exemple de quelques lignes) et sur la manière dont on organise son code quand il y a des foncteurs.
Actuellement je suis sur un projet contenant des centaines et des centaines de .cpp et .h, Si j’introduis quelques foncteurs, il est primordial qu’ils restent très localisés et soient faciles à trouver, à comprendre et à utiliser par d’autres programmeurs.
Pour mieux comprendre j’ai cherché à modifier le projet donné en exemple ici:
http://r0d.developpez.com/articles/algos-stl/#LI-E-3
Il est très simple mais soulève déjà pas mal de question. On a une classe album contenant un titre, un chanteur, une année, un genre etc. et une classe AlbumManager contenant un vecteur d’Album. On cherche à trouver grâce à un prédicat si le titre d’un album quelqueconque passé en paramètre est présent dans le vecteur.
Première solution : le prédicat est une méthode de la classe
Avantage :
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4 // Dans le fichier album.h // HaveSameTitle est un prédicat qui retourne true si le titre passé en paramètre est le même que celui de lalbum bool HaveSameTitle(std::string title) const { return title_ == title;)
. Court
. Simple à comprendre, simple à trouver (dans la classe).
. Le prédicat peut accéder aux données privées de la classe.
Inconvénient :
L’utilisation : Il semble que le seul moyen d’utiliser ce genre de prédicat soit cette horreur :
Et là, non. C’est juste trop lourd et trop confus. La plupart de mes collègues utilisent la STL pour les conteneurs et quelques algos de base, je ne peux donc pas introduire ce genre de subtilité ésotérique au beau milieu de code.
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5 std::string title = "Led Zeppelin"; AlbumList::iterator searched = std::find_if(main_list_.begin(), main_list_.end(), std::bind2nd (std::mem_fun_ref<bool, Album, std::string>(&Album::HaveSameTitle), title) );
Deuxième solution : Le prédicat est une structure dont on redéfinit l’opérateur()
On a alors dans le .h de la classe Album
Et dans le .cpp
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7 struct AlbumHaveSameTitle : public std::unary_function<Album, bool> { AlbumHaveSameTitle(const std::string& title); bool operator() (const Album& album) const; std::string title_; };
Avantage :
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9 AlbumHaveSameTitle::AlbumHaveSameTitle(const std::string& title) : title_(title) {} bool AlbumHaveSameTitle::operator() ( const Album& album ) const { return album.GetTitle() == title_; }
On a enin une utilisation simple et logique :
Inconvénient :
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4 std::string title = "Led Zeppelin"; AlbumList::iterator searched = std::find_if ( main_list_.begin(), main_list_.end(), AlbumHaveSameTitle(title) );
. On ne peut pas accéder aux variables privées de la classe Album. Il faut donc mettre des accesseurs partout.
. Ca reste tout de même un peu confus, quand on le rencontre pour la première fois (une sructure dont on redéfinit l’operateur() !? Pas banal...)
. Assez lourd. Au final il faut 13 lignes pour écrire un béte == . Sachant qu’il y peut y avoir pas mal de foncteur (après tout on peut chercher les albums par titre, par chanteur, par genre, par année, mais on peut ausssi imaginer plein d’autre prédicat). Le fichier album.cpp et album.h va vite être saturé de déclaration de structure.
. Les structures sont globales. Je n’ai pas trop envie de polluer le namespace (qui contient déjà de nombreuses classe) par plein de nouvelles structures volantes.
Voilà où j’en suis actuellement. J’avoue que cette première évaluation des algos de la STL m’a un peu découragé. La méthode basique, sans algo et foncteur, semble bien plus simple au final.
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14 // dans le fichier album.h Bool Album::EqualTitel(const std ::string title) const { return _title == title); // quelques parts dans le code std::string title = "Led Zeppelin"; Std::vector<Album>::iterator it; For(it = catalogue.begin(), catalogue.end(), ++it) { if(it->EqualTitle(title) == true) { // do something } }
D’où ma question. Si vous utilisez des foncteurs dans du code de production, comment organisez-vous votre code, quelle présentation adoptez-vous ? Des bind2nd, bind1st et autres mem_ref_fun illisibles ? Pleins de struct globales qui se baladent partout ? Une autre solution ?
Merci d’avance !
Partager