Functores en C++
May 26th, 2008 by Jorge MachinLos 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:
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();
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 <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::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++ |