M2103-TP7-Exo-2

Le passage d’une fonction (ou plutôt d’un pointeur de fonction) en paramètre d’une autre fonction est une technique très ancienne, puisqu’elle date de l’apparition du langage C.

Il est indispensable de la connaître pour cette raison (c’est la seule possibilité de “généricité” de traitement en C), et elle est encore utilisée par certains langages comme Ada95.

Cependant, le C++ offre un moyen beaucoup plus élégant, et surtout plus performant : les functors (voir l’amphi correspondant).

Au lieu de passer une fonction f1() en paramètre d’une autre fonction f2(), il suffit de créer une classe contenant cette fonction f1() comme méthode (fonction-membre, opération), d’instancier cette classe en un objet obj, de passer cet objet à la fonction f2(), qui appellera la méthode f1() de son paramètre : param.f1 (...).

class Obj
{
    // ...
  public :
    ... f1 (...) 
    { 
        ... 

    } // f1

    // ...

}; // Obj

void f2 (Obj & param)
{
    // ...
    ... param.f1 (...);
    // ...

} // F2()

Le dernier obstacle est l’obligation qu’a la fonction f2() de connaître l’identificateur de la fonction f1().

Il est levé en surchargeant l’opérateur () dans la classe et en donnant à cet opérateur le même profil et le même corps que la fonction f1().

Un objet de la classe ainsi modifiée est appelé un “objet-fonction” ou plus simplement un functor

Ainsi, la fonction f2() se contente d’appeler param (...) au lieu de param.f1 (...).

Travail à effectuer

Créer le projet Functor.

remplacer le contenu du fichier main.cpp par celui du fichier TestFunctor.cpp

/**
 *
 * @file   TestFunctor.cpp
 *
 * @authors M. Laporte, D. Mathieu
 *
 * @date    27/04/2010
 *
 * @version V1.0
 *
 * @brief   Premier functor 
 *
 **/
#include <iostream>
#include <cassert>
#include <cctype>  // tolower(), toupper()
#include <string>

using namespace std;

namespace 
{
    class ToLower
    {
        // ToDo

    }; // ToLower

    typedef int (* fctInt2Int_t) (int);
    string & moulinette (string & str, fctInt2Int_t transf)
    {
        for (string::size_type i (str.size ()); i--; )
            str [i] = transf (str [i]);

        return str;

    } // moulinette()

    void testFunctor (void)
    {
        cout << "Functor : ";

        string ligne  ("AZECv qrgWSg wrV  wfdgWFDHG  wdfGWXCV");
        string minusc ("azecv qrgwsg wrv  wfdgwfdhg  wdfgwxcv");

        // ToDo 
        assert (minusc == moulinette (ligne, ...));

        cout << "OK\n";

    } // testFunctor()

} // namespace anonyme

int main (void)
{
    /*      */    testFunctor ();     /*           */

    return 0;

} // main()

Complétez la classe ToLower possédant :

  • un destructeur virtuel,
  • une surcharge (virtuelle) de l’opérateur (), ayant le même profil que la fonction C tolower() :
    int tolower (int); 
    

    qui se contente d’appeler cette dernière et d’en renvoyer le résultat.

Modifiez la fonction moulinette() en remplaçant le second paramètre par un functor de la classe ToLower, et complétez l’appel de la fonction assert() dans la fonction testFunctor().

Remarque : la lourdeur de mise en œuvre est à peu près du même ordre que celle de l’utilisation des pointeurs de fonction, et, pour le moment, la fonction moulinette() ne peut faire qu’une seule opération : mettre en
minuscules.

Nous verrons ultérieurement les avantages décisifs des functors.