2010-07-12 3 views
3

Puisque j'adore la programmation en C# et en C++, je suis sur le point d'implémenter un système d'événements de type C# comme une base solide pour mon projet SFML C++. -GUI.std :: tr1 :: function :: target <TFuncPtr> et co-/contravariance

Ceci est seulement un extrait de mon code et j'espère que cela clarifie mon concept:

// Event.h 
// STL headers: 
#include <functional> 
#include <type_traits> 
#include <iostream> 
// boost headers: 
#include <boost/signals/trackable.hpp> 
#include <boost/signal.hpp> 

namespace Utils 
{ 
    namespace Gui 
    { 
     #define IMPLEMENTS_EVENT(EVENTNAME, EVENTARGS) public: \ 
      Utils::Gui::IEvent<EVENTARGS>& EVENTNAME() { return m_on##EVENTNAME; } \ 
     protected: \ 
      virtual void On##EVENTNAME(EVENTARGS& e) { m_on##EVENTNAME(this, e); } \ 
     private: \ 
      Utils::Gui::Event<EVENTARGS> m_on##EVENTNAME; 


     #define MAKE_EVENTFIRING_CLASS(EVENTNAME, EVENTARGS) class Fires##EVENTNAME##Event \ 
     { \ 
      IMPLEMENTS_EVENT(EVENTNAME, EVENTARGS); \ 
     }; 


     class EventArgs 
     { 
     public: 
      static EventArgs Empty; 
     }; 

     EventArgs EventArgs::Empty = EventArgs(); 

     template<class TEventArgs> 
     class EventHandler : public std::function<void (void*, TEventArgs&)> 
     { 
      static_assert(std::is_base_of<EventArgs, TEventArgs>::value, 
       "EventHandler must be instantiated with a TEventArgs template paramater type deriving from EventArgs."); 
     public: 
      typedef void Signature(void*, TEventArgs&); 
      typedef void (*HandlerPtr)(void*, TEventArgs&); 

      EventHandler() : std::function<Signature>() { } 

      template<class TContravariantEventArgs> 
      EventHandler(const EventHandler<TContravariantEventArgs>& rhs) 
       : std::function<Signature>(reinterpret_cast<HandlerPtr>(*rhs.target<EventHandler<TContravariantEventArgs>::HandlerPtr>())) 
      { 
       static_assert(std::is_base_of<TContravariantEventArgs, TEventArgs>::value, 
        "The eventHandler instance to copy does not suffice the rules of contravariance."); 
      } 

      template<class F> 
      EventHandler(F f) : std::function<Signature>(f) { } 

      template<class F, class Allocator> 
      EventHandler(F f, Allocator alloc) : std::function<Signature>(f, alloc) { } 
     }; 

     template<class TEventArgs> 
     class IEvent 
     { 
     public: 
      typedef boost::signal<void (void*, TEventArgs&)> SignalType; 

      void operator+= (const EventHandler<TEventArgs>& eventHandler) 
      { 
       Subscribe(eventHandler); 
      } 

      void operator-= (const EventHandler<TEventArgs>& eventHandler) 
      { 
       Unsubscribe(eventHandler); 
      } 

      virtual void Subscribe(const EventHandler<TEventArgs>& eventHandler) = 0; 

      virtual void Subscribe(const EventHandler<TEventArgs>& eventHandler, int group) = 0; 

      virtual void Unsubscribe(const EventHandler<TEventArgs>& eventHandler) = 0; 
     }; 

     template<class TEventArgs> 
     class Event : public IEvent<TEventArgs> 
     { 
     public: 
      virtual void Subscribe(const EventHandler<TEventArgs>& eventHandler) 
      { 
       m_signal.connect(*eventHandler.target<EventHandler<TEventArgs>::HandlerPtr>()); 
      } 

      virtual void Subscribe(const EventHandler<TEventArgs>& eventHandler, int group) 
      { 
       m_signal.connect(group, *eventHandler.target<EventHandler<TEventArgs>::HandlerPtr>()); 
      } 

      virtual void Unsubscribe(const EventHandler<TEventArgs>& eventHandler) 
      { 
       m_signal.disconnect(*eventHandler.target<EventHandler<TEventArgs>::HandlerPtr>()); 
      } 

      void operator() (void* sender, TEventArgs& e) 
      { 
       m_signal(sender, e); 
      } 

     private: 
      SignalType m_signal; 
     }; 

     class IEventListener : public boost::signals::trackable 
     { 
     }; 
    }; 
}; 

Comme vous pouvez le voir, j'utilise le signal boost :: mon système d'événement réel, mais j'encapsuler avec l'interface IEvent (qui est en fait une classe abstraite) pour empêcher les écouteurs d'événements de déclencher l'événement via operator().

Pour plus de commodité, j'ai surchargé les opérateurs d'affectation d'affectation et de soustraction. Si je dérive maintenant mes classes d'écoute d'événements de IEventListener, je suis capable d'écrire du code sans avoir à m'inquiéter du pointeur de fonction qui pend dans le signal.

Jusqu'à présent, je teste mes résultats, mais j'ai du mal avec std::tr1::function::target<TFuncPtr>():

class BaseEventArgs : public Utils::Gui::EventArgs 
{ 
}; 

class DerivedEventArgs : public BaseEventArgs 
{ 
}; 

void Event_BaseEventRaised(void* sender, BaseEventArgs& e) 
{ 
    std::cout << "Event_BaseEventRaised called"; 
} 

void Event_DerivedEventRaised(void* sender, DerivedEventArgs& e) 
{ 
    std::cout << "Event_DerivedEventRaised called"; 
} 

int main() 
{ 
    using namespace Utils::Gui; 
    typedef EventHandler<BaseEventArgs>::HandlerPtr pfnBaseEventHandler; 
    typedef EventHandler<DerivedEventArgs>::HandlerPtr pfnNewEventHandler; 

    // BaseEventHandler with a function taking a BaseEventArgs 
    EventHandler<BaseEventArgs> baseEventHandler(Event_BaseEventRaised); 
    // DerivedEventHandler with a function taking a DerivedEventArgs 
    EventHandler<DerivedEventArgs> newEventHandler(Event_DerivedEventRaised); 
    // DerivedEventHandler with a function taking a BaseEventArgs -> Covariance 
    EventHandler<DerivedEventArgs> covariantBaseEventHandler(Event_BaseEventRaised); 

    const pfnBaseEventHandler* pBaseFunc = baseEventHandler.target<pfnBaseEventHandler>(); 
    std::cout << "baseEventHandler function pointer is " << ((pBaseFunc != nullptr) ? "valid" : "invalid") << std::endl; 

    const pfnNewEventHandler* pNewFunc = newEventHandler.target<pfnNewEventHandler>(); 
    std::cout << "baseEventHandler function pointer is " << ((pNewFunc != nullptr) ? "valid" : "invalid") << std::endl; 

    // Here is the error, covariantBaseEventHandler actually stores a pfnBaseEventHandler: 
    pNewFunc = covariantBaseEventHandler.target<pfnNewEventHandler>(); 
    std::cout << "covariantBaseEventHandler as pfnNewEventHandler function pointer is " << ((pNewFunc != nullptr) ? "valid" : "invalid") << std::endl; 

    // This works as expected, but template forces compile-time knowledge of the function pointer type 
    pBaseFunc = covariantBaseEventHandler.target<pfnBaseEventHandler>(); 
    std::cout << "covariantBaseEventHandler as pfnBaseEventHandler function pointer is " << ((pBaseFunc != nullptr) ? "valid" : "invalid") << std::endl; 

    return EXIT_SUCCESS; 
} 

La méthode EventHandler<TEventArgs>::target<TFuncPtr>() ne renverra un pointeur valide si TFuncPtr est exactement le même type que stocké dans le Functor, quel que soit de covariance. En raison de la vérification RTTI, il interdit d'accéder au pointeur en tant que pointeur de fonction C standard faiblement typé, ce qui est plutôt gênant dans des cas comme celui-ci. Le gestionnaire d'événements est de type DerivedEventArgs mais pointe néanmoins sur une fonction pfnBaseEventHandler même si la fonction a traversé le constructeur. Cela signifie que la fonction std :: tr1 :: "supporte" la contravariance, mais je ne trouve pas le moyen de sortir simplement le pointeur de la fonction de l'objet std :: tr1 :: funcion si je ne le fais pas Je connais son type au moment de la compilation qui est requis pour un argument template.

J'apprécierais dans de tels cas qu'ils ont ajouté une méthode simple get() comme ils l'ont fait pour les types de pointeurs RAII. Étant donné que je suis relativement novice en programmation, j'aimerais savoir s'il existe un moyen de résoudre ce problème, de préférence au moment de la compilation via des modèles (ce qui, à mon avis, serait la seule solution).

+0

Pour mettre en forme du code, sélectionnez et appuyez sur le bouton 1010 au-dessus de la zone d'entrée de texte - n'utilisez pas de balises HTML et/ou de code. –

+0

@Sebastion: Vous codez le format en utilisant le petit bouton '101010' en haut de la fenêtre d'édition. (Et vous avez lu l'aide flottant à la droite de la fenêtre d'édition pour obtenir de l'aide.) Je suis entré et j'ai formaté votre code. – sbi

+0

Oui, j'ai lu sur ce sujet indentant et testé, mais comme l'aperçu n'a pas immédiatement formaté le code, je suis allé avec les balises HTML. Maintenant, je suis définitivement plus sage. Merci à vous trois d'avoir rangé mes affaires. @Roger Pate: Oui, c'est ce que je soupçonnais ... Je vais y aller maintenant et essayer de mettre en place un exemple plus court. –

Répondre

1

Je viens de trouver une solution au problème. Il semble que je viens de manquer un casting à un endroit différent:

template<class TEventArgs> 
class EventHandler : public std::function<void (void*, TEventArgs&)> 
{ 
public: 
    typedef void Signature(void*, TEventArgs&); 
    typedef void (*HandlerPtr)(void*, TEventArgs&); 

    // ... 

    template<class TContravariantEventArgs> 
    EventHandler(const EventHandler<TContravariantEventArgs>& rhs) 
     : std::function<Signature>(reinterpret_cast<HandlerPtr>(*rhs.target<EventHandler<TContravariantEventArgs>::HandlerPtr>())) 
    { 
     static_assert(std::is_base_of<TContravariantEventArgs, TEventArgs>::value, 
      "The eventHandler instance to copy does not suffice the rules of contravariance."); 
    } 

    // ... 
} 

Cela fonctionne comment il est censé fonctionner. Merci néanmoins de m'avoir donné une introduction en douceur dans cette communauté vraiment géniale!

Questions connexes