2011-03-03 13 views
5

Estoy intentando agregar un sistema de mensajería simple a mi proyecto, donde los eventos pueden ser invocados por una función, lo que dará lugar a que se llamen todas las devoluciones de llamada a ese evento.C++: Funciones de miembro de clase como devoluciones de llamada de evento

Ahora, la forma lógica de hacerlo es usar punteros a funciones. Sería posible pasar fácilmente el puntero a la función de devolución de llamada deseada al administrador de eventos, para registrarse. Una función de devolución de llamada de evento siempre devolvería un int y tomaría un void* como argumento.

Sin embargo, no quiero registrar funciones globales estáticas como mis devoluciones de llamada de eventos. Me gustaría hacerlo con funciones de miembro de clase.

  • ¿Es posible lograr esto con C++? Almacenar y llamar punteros a funciones miembro de diferentes clases pero con el mismo encabezado de función .

  • Si esto no es posible, ¿tiene alguna sugerencia sobre cómo podría solucionar este problema? Realmente me gustaría agregar oyentes de eventos directamente a mis clases.

+0

Haga un esfuerzo para evitar el vacío *. Por lo general no es necesario en C++ y abre la posibilidad de errores extraños en sistemas más complejos: fuerza la coherencia entre el emisor y el receptor sin la ayuda del compilador. – stefaanv

Respuesta

0

No, no es posible (a menos que haga C++/cli con .net).

Ahora, aún puede crear funciones estáticas, pasarles un objeto como parámetro, y lo único que harán es llamar a su función miembro en ese objeto. (En realidad, se requerirá un yeso primero).

3

¡Por supuesto que es posible! Eche un vistazo a Boost.Signal2 y Boost.Bind.

Boost.Signal2 básicamente implementa un sistema de señal y ranuras que es exactamente lo que necesita. Luego, puede usar boost::bind que es una generalización de std::bind1st y std::bind2nd para obtener envoltorios de objetos de función básicamente para cualquier cosa que se le ocurra (en su caso, métodos de miembro). Es realmente poderoso.

Ver este ejecutivo boost tutorial.

0

Lo más cercano que he logrado es registrar una función de miembro estático como devolución de llamada. El miembro estático toma el puntero del objeto (este) como argumento además de los argumentos enviados por el controlador de eventos y lo usa para llamar a la función miembro.

class myClass{ 
public: 
    static void callback(void *arg, void *obj) 
    { 
    if (obj) 
     reinterpret_cast<myClass*>(obj)->cb(arg); 
    } 

private: 
    void cb(void *arg); 
}; 

Registro myClass::callback y this con su manejador. Es posible que deba ajustar esto en la estructura a la que hace referencia arg si tiene restricciones en lo que se puede devolver.

1

no estoy completamente seguro de lo que quiere archivar, pero tal vez debería mirar a Boost Signals2

Es muy útil si desea crear algún tipo de mecanismo/ranura de la señal.

6

Sí es posible. C++0x has the function class that handles this, y como otros han señalado, Boost tiene instalaciones similares.

También puede rodar su propia, pero la sintaxis no es para los débiles de corazón:

#include <iostream> 

class Callable 
{ 
    public: 

     virtual ~Callable() {} 
     virtual int operator() (void* args) = 0; 
}; 

class CallableFreeFunction : public Callable 
{ 
    public: 

     CallableFreeFunction(int (*func)(void*)) : func_(func) {} 

     virtual int operator() (void* args) { return (*func_)(args); } 

    private: 

     int (*func_)(void*); 
}; 

template <typename tClass> 
class ClassMemberCallable : public Callable 
{ 
    public: 

     ClassMemberCallable(tClass* instance, int (tClass::*memberfunction)(void*)) : instance_(instance), memberfunc_(memberfunction) {} 

     virtual int operator() (void* args) { return (instance_->*memberfunc_)(args); } 

    private: 

     tClass* instance_; 
     int (tClass::*memberfunc_)(void*); 
}; 

class Foo 
{ 
    public: 

     int derp(void* args) 
     { 
      std::cout << args << '\n'; 
      return 2; 
     } 
}; 

int freefunctionfoo(void* args) 
{ 
    std::cout << "free" << args << '\n'; 
    return 2; 
} 

int main(int argc, char* argv[]) 
{ 
    Foo myfoo; 

    Callable* callable = new ClassMemberCallable<Foo>(&myfoo, &Foo::derp); 

    (*callable)(0); 

    delete callable; 

    callable = new CallableFreeFunction(freefunctionfoo); 

    (*callable)(0); 

    delete callable; 

    std::cin.get(); 

    return 0; 
} 

Esto demuestra una manera de manejar ambas funciones gratuitas, y las funciones miembro de una manera opaca. Este es un ejemplo simple, y se puede hacer más genérico y robusto de varias maneras. Yo te refiero a estas páginas de ayuda de sintaxis:

http://www.newty.de/fpt/index.html

http://www.parashift.com/c++-faq-lite/pointers-to-members.html

También recomiendo mirar esto por más ideas:

http://www.codeproject.com/KB/cpp/FastDelegate.aspx

1

Aquí es mi intento no tan bueno para hacer un trabajo como ese: Primero que nada necesitas una clase de controlador de eventos base, así que vamos a llamarlo EvtHandler por ahora:

class Event; //implement this yourself, it shall contain general but good info about event 

class EvtHandler 
{ 
public: 
    virtual void handleEvent (Event & evt); 
}; 

Entonces cada clase que se supone para controlar los eventos de una manera, debe derivar de esta clase, y puede implementar nuevas funciones como todo lo que quieran en la medida que devuelven el mismo tipo de datos (void en este caso) y recibe los mismos parámetros (Evento en este caso). De esta manera:

class Foo : public EvtHandler 
{ 
public: 
    void handleFooEvent (Event & event); 
}; 

Entonces implementado centros de mensajes para cada evento especial, que tenía que registrar detectores y eventos de despacho cuando sea necesario:

class ShutdownMessageCenter 
{ 
typedef std::map<EventHandler *, event_func> ListenerMap; 
public: 

    void register (EvtHandler * handler, void(EvtHandler::*memFunc)(Event &)) { 
     m_lmap[handler] = memFunc; 
    } 

    void callListeners() { 
     Event shutdown_event (EM_SHUTDOWN /*just imagine this can mean something, idk*/); 
     ListenerMap::iterator itr = m_lmap.begin(); 
     for (; itr != m_lmap.end(); ++itr) { 
      EvtHandler * handler = itr->first; 
      void (EvtHandler::*func)(Event &) = itr->second; 
      (handler->*func)(shutdown_event); 
      } 
     } 

private: 
    ListenerMap m_lmap; 
}; 

entonces se podría registrar sus EvtHandlers a este centro de mensajes particular para ¡ejemplo!

ShutdownMessageCenter message_center; 
EvtHandler * some_handler = new EvtHandler(); 
Foo * some_foo = new Foo(); 
message_center.register (some_handler, &EvtHandler::handleEvent); 
message_center.register (some_foo, static_cast<void (EvtHandler::*)(Event &)>(&Foo::handleFooEvent); 
message_center.callListeners(); 

Pero una vez más, esto no es bueno en absoluto, ¡solo pensé en compartir! Perdón por el desastre, jaja!

Cuestiones relacionadas