Otros sitios...

Búsqueda

Antispam

Functores en C++

May 26th, 2008 by Jorge Machin

Los functores o functors en C++ son una construcción del lenguaje que nos permite usar objetos como si fueran funciones. Esto es muy útil cuando se esta programando callbacks, interpretes, compiladores y metaprogración.

Una forma de usarlos es encapsulandolos en una clase:

class Function1 {

  public:

    // Default constructor

    Function1() { std::cout <<"Function1 constructor" <<std::endl; }

    // Virtual destructor

    ~Function1() { std::cout <<"Function1 destructor" <<std::endl; }

    //  Function call:

    void operator() (int a) const {
        std::cout <<"operator(): " <<a <<std::endl;
    }
};

La cual podemos usar así:

Function1 function1;
function1();

El lector se puede imaginar que una de las propiedades de los functores es que al agregarle miembros a la clase, estos pueden mantener su estado entre cada una de las llamadas. Sin embargo, es muy probable que la utilidad de los functores se pueda apreciar más con un ejemplo práctico:

Manejador de eventos usando functores

Un manejador de eventos es un subprograma que al recibir un evento, lo comunica a todas los subprogramas que estén subscritos a él. El código fuente es el siguiente:

#include <iostream>
#include <list>
#include <string>

// Una clase base para hacer clases con callbacks:

class callbackbase {

   public:

      virtual void operator()() const { std::cout <<"callbackbase()" <<std::endl; };
      virtual ~callbackbase() { };

};

// El functor que implementa la funcionalidad del callback:

template <class T> class callback : public callbackbase {

   public:

      typedef void (T::*Func)();

      callback( T& t, Func func ) : object(&t), f(func) { }

      void operator()() const { (object->*f)(); }

   private:

      T* object;

      Func f;
};

template<typename T> callback<T> *make_callback( T& t, void (T::*f) () ) {

  return new callback<T>( t, f );

}

// Los tipos de eventos que soportamos:

enum notifications {

   NOTIFICATION_ONE,
   NOTIFICATION_TWO,
   NOTIFICATION_MAX

};

// Nuestro Manejador de Eventos:

class EventManager {

   public:

      void registerHandler( callbackbase *function, enum notifications notification) {

         functions[notification].push_back( function );

      }

      void notifyHandlers( enum notifications notification ) {

         for ( std::list<callbackbase*>::iterator iter = functions[notification].begin(); iter != functions[notification].end(); iter++ ) {

            (*(*iter))();

         }

      }

   private:

      std::list<callbackbase *> functions[NOTIFICATION_MAX];

};

// Clases de ejemplo

class Class1 {

   public:

      Class1( const std::string &instance ) : instance( instance ) { };

      void function1() { std::cout <<"class1::function1: " <<instance <<std::endl; };

      void function2() { std::cout <<"class1::function2: " <<instance <<std::endl; };

   private:

      std::string instance;

};

class Class2 {

   public:

      void function1() { std::cout <<"class2::function1" <<std::endl; };

};

class Class3 : public callbackbase {

   public:

     virtual void operator()() const { std::cout <<"class3::function1" <<std::endl; };

};

//

int main() {

   // Clases que van a recibir los eventos:

   Class1 class1a("1a");
   Class1 class1b("1b");
   Class2 class2;
   Class3 class3;

   // Creamos nuestro manejador de eventos:

   EventManager eventManager;

   // Creamos al vuelo callbacks a funciones de clases que no heredan de callbackbase:

   eventManager.registerHandler( make_callback( class1a, &Class1::function1 ), NOTIFICATION_ONE );

   eventManager.registerHandler( make_callback( class1a, &Class1::function2 ), NOTIFICATION_ONE );

   eventManager.registerHandler( make_callback( class1b, &Class1::function1 ), NOTIFICATION_ONE );

   eventManager.registerHandler( make_callback( class2, &Class2::function1 ), NOTIFICATION_ONE );

   // Registramos una clase que hereda callbackbase:

   eventManager.registerHandler( &class3, NOTIFICATION_ONE );

   // Lanzamos un evento a nuestras funciones:

   eventManager.notifyHandlers( NOTIFICATION_ONE  );

   return 1;

}

Al ejecutarlo, obtenemos la siguiente salida:

class1::function1: 1a
class1::function2: 1a
class1::function1: 1b
class2::function1
class3::function1

Obviamente al programa le falta poder retirar callbacks del manejador de eventos y su administración de memoria, pero sólo busca servir como ejemplo del funcionamiento de los functores.

Hay que aclarar que este mismo programa se puede implementar usando apuntadores a funciones aunque se disminuye un poco el enfoque orientado a objetos.

Posteado en C/C++ |

Deje un comentario

Por favor note: La moderación de comentarios está activada y puede retrasar la publicación de los comentarios. No es necesario enviar de nuevo el mismo comentario.