Exemple simple de délégué

Un délégué est un objet capable de « contenir » une entité susceptible d'être appelée, qu'il s'agisse d'une fonction globale, d'une méthode de classe ou d'une méthode d'instance (pour une instance donnée). Je présume qu'on pourrait aussi y déposer une λ mais je n'y ai pas suffisamment réfléchi pour être en mesure (a) de l'affirmer ou (b) d'offrir un exemple opérationnel.

L'exemple proposé ci-dessus applique un variante de l'idiome pImpl avec une strate utilisable (delegue), une strate d'abstraction (invoquable, qui est elle-même Clonable) et une strate d'implémentation (fonction et methode_instance). La strate utilisable est une classe englobante et toutes les autres sont privées et internes.

Cette version utilise de l'allocation dynamique de mémoire. Ceci signifie qu'il nous faut implémenter la Sainte-Trinité, mais que la sémantique de mouvement y est banale à implémenter (ce que j'ai fait, évidemment).

Dans cette implémentation, le recours à new prend un temps indéterministe, mais que la sémantique de mouvement s'implémente en temps constant (complexité ).

#include <functional>
#include <utility>
#include <cassert>
#include <iostream>
struct Clonable
{
   virtual Clonable *cloner() const = 0;
   virtual ~Clonable() = default;
protected:
   Clonable() = default;
   Clonable(const Clonable &) = default;
};
template <class R, class A>
class delegue
{
   struct invoquable
      : Clonable
   {
      virtual R invoquer(A) = 0;
      virtual invoquable* cloner() const = 0;
      virtual ~invoquable() = default;
   };
   class fonction
      : public invoquable
   {
      R(*ptr_)(A);
   public:
      fonction(R(*ptr)(A))
         : ptr_{ ptr }
      {
      }
      R invoquer(A arg)
      {
         return ptr_(arg);
      }
      fonction* cloner() const
      {
         return new fonction{ ptr_ };
      }
   };
   template <class T>
   class methode_instance
      : public invoquable
   {
      T *inst_;
      R(T::*ptr_)(A);
   public:
      methode_instance(T *inst, R(T::*ptr)(A))
         : inst_{ inst }, ptr_{ ptr }
      {
      }
      R invoquer(A arg)
      {
         return (inst_->*ptr_)(arg);
      }
      methode_instance* cloner() const
      {
         return new methode_instance{ inst_, ptr_ };
      }
   };
   template <class T>
   class methode_instance_const
      : public invoquable
   {
      T *inst_;
      R(T::*ptr_)(A) const;
   public:
      methode_instance_const(T *inst, R(T::*ptr)(A) const)
         : inst_{ inst }, ptr_{ ptr }
      {
      }
      R invoquer(A arg)
      {
         return (inst_->*ptr_)(arg);
      }
      methode_instance_const* cloner() const
      {
         return new methode_instance_const{ inst_, ptr_ };
      }
   };
   invoquable *ptr_;
public:
   delegue()
      : ptr_{}
   {
   }
   delegue(R(*fct)(A))
      : ptr_{ new fonction{ fct } }
   {
   }
   template<class T>
   delegue(T *inst, R(T::*fct)(A))
      : ptr_{ new methode_instance<T>{ inst, fct } }
   {
   }
   template<class T>
   delegue(T *inst, R(T::*fct)(A) const)
      : ptr_{ new methode_instance_const<T>{ inst, fct } }
   {
   }
   void swap(delegue &d)
   {
      using std::swap;
      swap(ptr_, d.ptr_);
   }
   delegue(const delegue &d)
      : ptr_(d.ptr_ ? d.ptr_->cloner() : nullptr)
   {
   }
   delegue(delegue &&d)
      : ptr_{ std::move(d.ptr_) }
   {
      d.ptr_ = {};
   }
   delegue &operator=(const delegue &d)
   {
      delegue(d).swap(*this);
      return *this;
   }
   delegue &operator=(delegue &&d)
   {
      swap(d);
      return *this;
   }
   ~delegue() noexcept
   {
      delete ptr_;
   }
   // invocation
   R operator()(A arg)
   {
      assert(ptr_);
      return ptr_->invoquer(arg);
   }
};

class X
{
   double val_;
public:
   X(double val)
      : val_{ val }
   {
   }
   double m(int x)
   {
      return x + val_++;
   }
   double mc(int x) const
   {
      return x + val_;
   }
};

double f(int x)
{
   return static_cast<double>(x) / 2.0;
}

int main()
{
   using namespace std;
   delegue<double, int> d = f;
   cout << d(3) << endl;
   X x( 0.5 );
   d = delegue<double, int>(&x, &X::m);
   cout << d(3) << endl;
   d = delegue<double, int>(&x, &X::mc);
   cout << d(3) << endl;
}

Valid XHTML 1.0 Transitional

CSS Valide !