Functors – ponteiros para métodos

Me pediram para postar um exemplo de um sistema de eventos usando o assunto do post passado (ponteiros para funções e métodos), mas decidi que antes de fazer esse post, irei falar sobre functors – pois irei utilizar esse conceito no sistema de eventos.

Como falei no post anterior, um ponteiro para um método de uma classe tem uma sintaxe estranha e que pode facilmente complicar códigos, e além disso necessitamos de uma referência ao objeto do qual esse método será executado. Para facilita então o uso desses ponteiros especiais, vamos criar um conjunto simples de classes que encapsulam a funcionalidade de um ponteiro para método – esse conceito é chamado de Functor.

Para que a classe seja genérica, necessitaremos utilizar templates. Mas, como um template na prática gera uma classe diferente para cada tipo, necessitamos criar uma classe-base para que nossos functors possam ser tratados por código genérico. E aqui está o código da classe:


class Functor
{
public:
    virtual void operator() () = 0;
};

É uma classe abstrata bem simples – com apenas um operador virtual, o operador (). Isso nos permite fazer algo do tipo:


void funcao(Functor &func)
{
    // chama funcao encapsulada pelo functor
    func();
}

É logico q a classe Functor apenas nao resolve nada, então vamos olhar agora o código da classe que realmente faz a diferença – TemplateFunctor


template <class T>
class TemplateFunctor : public Functor
{
protected:
    // definindo o tipo do ponteiro
    typedef void (T::*metodo)();
    // o ponteiro para o objeto em questao
    T *_pObj;
    // o ponteiro para o método
    metodo _fpMetodo;
public:
    // construtor
    TemplateFunctor ( T *o, metodo m ) : _pObj(o),_fpMetodo(m) {}

    // operador de chamada de funcao ()
    void operator() ()
    {
        (_pObj->*_fpMetodo) ();
    }
};

Agora sim, temos um functor funcionando. A classe é relativamente simples – temos a definição do tipo do ponteiro, a definição dos ponteiros para o objeto e para o método, um construtor que garante a construção apropriada do functor, e o operador () q nos permite chamar o método através deste functor como se o objeto fosse o próprio método. Vamos exemplificar a seguir:


// uma classe qualquer
class A
{
public:
    A() {}

    void metodoQualquer() { /*faz algo*/ }
};

// nao podemos ter um functor sem um objeto
A a();
// criando o functor - passando objeto e método
TemplateFunctor<A> aFunc(&a, &A::metodoQualquer);
// chamando o método através do functor
aFunc();

Algo que provavelmente voce está se pergutando agora é – e se eu quiser métodos com outros tipos de retorno e parametros? Bom, para isso é necessária a criação de classes functor diferentes ou, então, planejar a sua própria functor para trabalhar com parameterização genérica (receber uma union, ou um ponteiro void). Porém, para um sistema de eventos, por exemplo, onde todos os métodos chamados terão a mesma assinatura, este tipo de functor facilita bastante a codificação.

Como sempre, postem suas dúvidas e sugestoes, estarei mais que disponivel para responde-las.

3 comentários até agora

  1. vinigodoy on

    Muito bom! Posso dar uma sugestão de leitura?
    http://www.tutok.sk/fastgl/callback.html

    Acho que um dos materiais mais completos sobre o assunto encontrados na net.

  2. André on

    Muito interessante este conceito de Functor, nunca tinha ouvido falar. Muito bem explicado.

  3. diegomazala on

    Bem interessante. Parabéns pelo artigo!


Leave a reply