代码之家  ›  专栏  ›  技术社区  ›  Sebastian Graf

std::tr1::function::target<TFuncPtr>和co-/逆变

  •  3
  • Sebastian Graf  · 技术社区  · 14 年前

    由于我喜欢C++和C++两种语言的编程,所以我将把一个类似C的事件系统作为我的C++ C++ SFMLGUI的一个坚实的基础。

    这只是我的代码摘录,我希望这能澄清我的概念:

    // 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
            {
            };
        };
    };
    

    如您所见,我使用boost::signal作为实际的事件系统,但是我用IEvent接口(实际上是一个抽象类)封装它,以防止事件侦听器通过operator()触发事件。

    为了方便起见,我重载了加法赋值和减法赋值运算符。如果我现在从IEventListener派生事件侦听类,我就可以编写代码,而不必担心函数指针在信号中悬空。

    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;
    }
    

    这个 EventHandler<TEventArgs>::target<TFuncPtr>() 方法仅在TFuncPtr与函子中存储的类型完全相同时返回有效指针,而不考虑协方差。 由于RTTI检查,它禁止将指针作为标准的弱类型C函数指针来访问,这在这种情况下有点烦人。

    EventHandler是DerivedEventArgs类型,但仍然指向pfnBaseEventHandler函数,即使该函数通过构造函数运行。

    在这种情况下,我希望他们能像对待RAII指针类型那样添加一个简单的get()方法。

    由于我对编程很陌生,我想知道是否有办法解决这个问题,最好是在编译时通过模板(我认为这是唯一的方法)。

    1 回复  |  直到 14 年前
        1
  •  2
  •   Sebastian Graf    14 年前

    刚找到解决问题的办法。我好像错过了另一个地方的演员:

    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.");
        }
    
        // ...
    }
    

    这就是它的工作原理。尽管如此,还是要感谢你让我顺利进入这个非常棒的社区!

    推荐文章