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.

Anúncios

3 comments so far

  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!


Deixe um comentário

Preencha os seus dados abaixo ou clique em um ícone para log in:

Logotipo do WordPress.com

Você está comentando utilizando sua conta WordPress.com. Sair / Alterar )

Imagem do Twitter

Você está comentando utilizando sua conta Twitter. Sair / Alterar )

Foto do Facebook

Você está comentando utilizando sua conta Facebook. Sair / Alterar )

Foto do Google+

Você está comentando utilizando sua conta Google+. Sair / Alterar )

Conectando a %s

%d blogueiros gostam disto: