Pointeurs de fonctions – compléments

Quelques compléments d'information quant aux pointeurs de fonctions et quant aux pointeurs de méthodes d'instance.

Il est possible, mes étudiant(e)s le voient en classe, d'harmoniser « côté serveur » les différences entre foncteurs et fonctions avec des traits. Il est aussi possible de faire aisément quelque chose de semblable côté client, en permettant au code client de transformer aisément un pointeur de fonction en un foncteur correctement documenté. Le foncteur résultant invoquera la fonction à travers un pointeur, et ne sera sans doute pas assujetti à des optimisations par inlining, mais il pourra être instancié et manipulé comme un objet à part entière (bien que très simple).

Le standard du langage, ayant été ratifié en 1998, utilise une technique semblable avec la fonction ptr_fun(), qui créera, selon les types impliqués, des pointer_to_unary_function ou des pointer_to_binary_function.

Notez que les types unary_function et binary_function sont dépréciés depuis C++ 11. Je mettrai ces exemples à jour quand j'en aurai le temps.

Le code proposé à droite est un exemple d'une telle implémentation.

#include <functional>
#include <algorithm>
#include <iostream>
#include <iterator>
#include <string>
using namespace std;

template <class A, class R>
   class unary_function_wrapper
      : public unary_function<A,R>
   {
      result_type (*p_)(argument_type);
   public:
      unary_function_wrapper(result_type (*p)(argument_type))
         : p_{p}
      {
      }
      result_type operator()(argument_type arg) const
         { return p_(arg); }
   };
template <class A0, class A1, class R>
   class binary_function_wrapper
      : public binary_function<A0, A1,R>
   {
      result_type (*p_)(first_argument_type, second_argument_type);
   public:
      binary_function_wrapper(result_type (*p)(first_argument_type, second_argument_type))
         : p_{p}
      {
      }
      result_type operator()(first_argument_type a0, second_argument_type a1) const
         { return p_(a0, a1); }
   };
template <class A, class R>
   unary_function_wrapper<A,R> pointeur_fonction(R (*p)(A))
      { return unary_function_wrapper<A,R>(p); }
template <class A0, class A1, class R>
   binary_function_wrapper<A0, A1, R> pointeur_fonction(R (*p)(A0,A1))
      { return binary_function_wrapper<A0,A1,R>(p); }

int arrondir(double d)
   { return static_cast<int>(d > 0? d + 0.5 : d - 0.5); }


template <class Itt, class C>
   void afficher_sequence(Itt debut, Itt fin, basic_ostream<C> &os,
                          const basic_string<C> &msg = {},
                          const basic_string<C> &sep = {})
   {
      using value_type = typename iterator_traits<Itt>::value_type;
      os << msg;
      copy(debut, fin, ostream_iterator<value_type>(os, sep.c_str()));
      os << endl;
   }
template <class Itt, class C>
   void afficher_sequence(Itt debut, Itt fin, basic_ostream<C> &os,
                          const C *msg)
   {
      afficher_sequence(debut, fin, os, basic_string<C>(msg));
   }
template <class Itt, class C>
   void afficher_sequence(Itt debut, Itt fin, basic_ostream<C> &os,
                          const C *msg, const C *sep)
   {
      afficher_sequence(debut, fin, os, basic_string<C>(msg),
                                        basic_string<C>(sep));
   }
int main()
{
   double vals[] = { 1.3, -2.4, 3.5, -4.6 };
   enum { N = sizeof(vals) / sizeof(vals[0]) };
   int res[N];
   transform(begin(vals), end(vals), res, pointeur_fonction(arrondir));
   afficher_sequence(begin(vals), end(vals), cout, "Valeurs originales : ", " ");
   afficher_sequence(begin(res), end(res),   cout, "Valeurs arrondies :  ", " ");
}

Pour ce qui est des méthodes d'instance, le code proposé à droite invoquera la méthode sans paramètres afficher() de plusieurs instances de X.

Le type method_wrapper (à droite) montre comment il est possible d'encapsuler une telle méthode, et le programme de test illustre une application légale du concept.

Pour expérimenter, vous pouvez aussi examiner les fonctions génératrices mem_fun() et mem_fun_ref() de la bibliothèque <functional>, qui jouent un rôle semblable dans le standard, ou encore examiner boost::function, qui sera intégré dans C++ 11 en remplacement de ces deux mécanismes (boost::function est plus raffiné que ses prédécesseurs, ayant bénéficié de l'expérience développée au fil des ans avec la POO et la programmation générique en C++).

#include <functional>
#include <ostream>
#include <algorithm>
#include <iostream>

template <class R, class T>
   class method_wrapper
      : public std::unary_function<T*, R>
   {
      result_type (T::*p_)() const;
   public:
      method_wrapper(result_type (T::*p)() const)
         : p_{p}
      {
      }
      result_type operator()(argument_type p) const
         { return (p->*p_)(); }
   };
template <class R, class T>
   method_wrapper<R, T>
      pointeur_methode(R (T::*p)() const)
         { return method_wrapper<R, T>(p); }

class X
{
   std::ostream &os_;
   int val_;
public:
   X (std::ostream &os, int val)
      : os_{os}, val_{val}
   {
   }
   void afficher() const
      { os_ << "X de valeur " << val_ << std::endl; }
};

int main()
{
   using namespace std;
   X x0{cout, 3},
     x1{cout, 4};
   X *x[] =
   {
      &x0, &x1
   };
   for_each(begin(x), end(x), pointeur_methode(&X::afficher));
}

En espérant que le tout vous soit utile...


Valid XHTML 1.0 Transitional

CSS Valide !