6
\$\begingroup\$

I am learning C++ and have been trying to create an event system for use in a small game. This will be the mechanism by which game entities communicate. I would be eternally grateful if someone with experience could critique my code. Running through it myself, I'm a little concerned about the use of Runtime Type Information (RTTI). I know that this is discouraged but I can't find any alternative to providing some basic type safety.

class EventDispatcher
{
public:
    /**
     * Callback function type for event handling.
     */
    template<typename EventType>
    struct Callback
    {
        /**
         * Constructor.
         */
        Callback(boost::function<void(const EventType& event)> callback) :
        handler(callback)
        {
            // Nothing to do.
        }

        /**
         * Callback function.
         */
        boost::function<void(const EventType& event)> handler;
    };

    /**
     * Constructor.
     */
    EventDispatcher();

    /**
     * Destructor.
     */
    ~EventDispatcher();

    /**
     * Subscribe to receive events.
     *
     * @param callback Event handler callback.
     */
    template <typename EventType>
    void Subscribe(Callback<EventType> callback)
    {
        GetSpecificDispatcher<EventType>()->Subscribe(callback);
    }

    /**
     * Immediately broadcast an event to subscribers.
     *
     * @param args... Event constructor arguments.
     */
    template <typename EventType, typename... Arguments>
    void Broadcast(const Arguments... args)
    {
        GetSpecificDispatcher<EventType>()->Broadcast(EventType(args...));
    }

    /**
     * Queue an event to be broadcast to subscribers on the next Update
     * call.
     *
     * @param args... Event constructor arguments.
     */
    template <typename EventType, typename... Arguments>
    void Enqueue(const Arguments... args)
    {
        GetSpecificDispatcher<EventType>()->Enqueue(EventType(args...));
    }

    /**
     * Broadcasts all events in the queue to subscribers.
     */
    void Update()
    {
        for (auto iter = dispatchers.begin(); iter != dispatchers.end(); ++iter)
        {
            iter->second->Update();
        }
    }

protected:
    /**
     * Event specific dispatcher interface.
     */
    class ISpecificDispatcher
    {
    public:
        virtual void Update() = 0;
    };

    /**
     * Event specific dispatcher.
     */
    template<typename EventType>
    class SpecificDispatcher : public ISpecificDispatcher
    {
    public:
        /**
         * Subscribe to receive events.
         *
         * @param callback Event handler callback.
         */
        void Subscribe(Callback<EventType> callback)
        {
            callbacks.push_back(callback);
        }

        /**
         * Immediately broadcast an event to subscribers.
         *
         * @param args... Event constructor arguments.
         */
        template <typename... Arguments>
        void Broadcast(const Arguments... args)
        {
            EventType event(args...);
            for (const Callback<EventType>& callback : callbacks)
            {
                callback.handler(event);
            }
        }

        /**
         * Queue an event to be broadcast to subscribers on the next Update
         * call.
         *
         * @param args... Event constructor arguments.
         */
        template <typename... Arguments>
        void Enqueue(const Arguments... args)
        {
            eventQueue.push(EventType(args...));
        }

        /**
         * Broadcasts all events in the queue to subscribers.
         */
        virtual void Update()
        {
            while (!eventQueue.empty())
            {
                const EventType& event = eventQueue.front();
                for (const Callback<EventType>& callback : callbacks)
                {
                    callback.handler(event);
                }
                eventQueue.pop();
            }
        }

    private:
        /**
         * Queued events.
         */
        std::queue<EventType> eventQueue;

        /**
         * Event callback handlers.
         */
        std::vector<Callback<EventType>> callbacks;
    };

    /**
     * Returns a pointer to the specific dispatcher for the event type.
     * A specific dispatcher is first created if one does not exist.
     */
    template<typename EventType>
    boost::shared_ptr<SpecificDispatcher<EventType>> GetSpecificDispatcher()
    {
        auto iter = dispatchers.find(typeid(EventType).name());
        if (iter == dispatchers.end())
        {
            // A specific dispatcher was not found.
            // We better create one!
            boost::shared_ptr<SpecificDispatcher<EventType>> specificDispatcher(new SpecificDispatcher<EventType>());
            dispatchers[typeid(EventType).name()] = specificDispatcher;
            return specificDispatcher;
        }
        else
        {
            return boost::static_pointer_cast<SpecificDispatcher<EventType>>(iter->second);
        }
    }

private:
    /**
     * Event specific dispatchers.
     */
    std::map<std::string, boost::shared_ptr<ISpecificDispatcher>> dispatchers;
};
\$\endgroup\$
0

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.