4

私は C# と C++ の両方でプログラミングするのが好きなので、計画中の C++ SFML-GUI の強固な基盤として、C# のようなイベント システムを実装しようとしています。

これは私のコードの抜粋に過ぎず、これが私の概念を明確にすることを願っています:

// 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 を実際のイベント システムとして使用していますが、イベント リスナーが operator() を介してイベントを発生させないように、IEvent インターフェイス (実際には抽象クラス) でカプセル化しています。

便宜上、加算代入演算子と減算代入演算子をオーバーロードしました。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 が Functor に格納されている型とまったく同じ型である場合にのみ、有効なポインターを返します。RTTI チェックのため、標準の弱く型付けされた C 関数ポインターとしてポインターにアクセスすることは禁止されています。

EventHandler は DerivedEventArgs 型ですが、関数がコンストラクターを介して実行されたとしても、pfnBaseEventHandler 関数を指しています。

つまり、 std::tr1::function 自体が反変性を「サポート」していますが、その型がわからない場合、 std::tr1::funcion オブジェクトから関数ポインターを単純に取得する方法を見つけることができませんテンプレート引数に必要なコンパイル時。

このような場合、RAII ポインター型の場合と同様に単純な get() メソッドを追加していただければ幸いです。

私はプログラミングにまったく慣れていないので、できればテンプレートを介してコンパイル時にこの問題を解決する方法があるかどうかを知りたいです (これが唯一の方法だと思います)。

4

1 に答える 1

2

問題の解決策が見つかりました。別の場所でキャストを見逃したようです:

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

    // ...
}

これは、動作するはずの方法で動作します。それにもかかわらず、この本当に素晴らしいコミュニティへのスムーズな紹介をありがとうございました!

于 2010-07-13T21:12:55.867 に答える