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.
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));
}
|