/* * Copyright (C) 2012 Alec Thomas * All rights reserved. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. * * Author: Alec Thomas */ #pragma once #include #include #include #include #include #include #include #include "entityx/config.h" #include "entityx/3rdparty/simplesignal.h" #include "entityx/help/NonCopyable.h" namespace entityx { /// Used internally by the EventManager. class BaseEvent { public: typedef std::size_t Family; virtual ~BaseEvent(); protected: static Family family_counter_; }; typedef Simple::Signal EventSignal; typedef std::shared_ptr EventSignalPtr; typedef std::weak_ptr EventSignalWeakPtr; /** * Event types should subclass from this. * * struct Explosion : public Event { * Explosion(int damage) : damage(damage) {} * int damage; * }; */ template class Event : public BaseEvent { public: /// Used internally for registration. static Family family() { static Family family = family_counter_++; return family; } }; class BaseReceiver { public: virtual ~BaseReceiver() { for (auto connection : connections_) { auto &ptr = connection.second.first; if (!ptr.expired()) { ptr.lock()->disconnect(connection.second.second); } } } // Return number of signals connected to this receiver. std::size_t connected_signals() const { std::size_t size = 0; for (auto connection : connections_) { if (!connection.second.first.expired()) { size++; } } return size; } private: friend class EventManager; std::unordered_map> connections_; }; template class Receiver : public BaseReceiver { public: virtual ~Receiver() {} }; /** * Handles event subscription and delivery. * * Subscriptions are automatically removed when receivers are destroyed.. */ class EventManager : entityx::help::NonCopyable { public: EventManager(); virtual ~EventManager(); /** * Subscribe an object to receive events of type E. * * Receivers must be subclasses of Receiver and must implement a receive() method accepting the given event type. * * eg. * * struct ExplosionReceiver : public Receiver { * void receive(const Explosion &explosion) { * } * }; * * ExplosionReceiver receiver; * em.subscribe(receiver); */ template void subscribe(Receiver &receiver) { bool (Receiver::*receive)(const E &) = &Receiver::receive; auto sig = signal_for(Event::family()); auto wrapper = EventCallbackWrapper(std::bind(receive, &receiver, std::placeholders::_1)); auto connection = sig->connect(wrapper); BaseReceiver &base = receiver; base.connections_.insert(std::make_pair(Event::family(), std::make_pair(EventSignalWeakPtr(sig), connection))); } /** * Unsubscribe an object in order to not receive events of type E anymore. * * Receivers must have subscribed for event E before unsubscribing from event E. * */ template void unsubscribe(Receiver &receiver) { BaseReceiver &base = receiver; // Assert that it has been subscribed before assert(base.connections_.find(Event::family()) != base.connections_.end()); auto pair = base.connections_[Event::family()]; auto connection = pair.second; auto &ptr = pair.first; if (!ptr.expired()) { ptr.lock()->disconnect(connection); } base.connections_.erase(Event::family()); } template void emit(const E &event) { auto sig = signal_for(Event::family()); sig->emit(&event); } /** * Emit an already constructed event. */ template void emit(std::unique_ptr event) { auto sig = signal_for(Event::family()); sig->emit(event.get()); } /** * Emit an event to receivers. * * This method constructs a new event object of type E with the provided arguments, then delivers it to all receivers. * * eg. * * std::shared_ptr em = new EventManager(); * em->emit(10); * */ template void emit(Args && ... args) { // Using 'E event(std::forward...)' causes VS to fail with an internal error. Hack around it. E event = E(std::forward(args) ...); auto sig = signal_for(std::size_t(Event::family())); sig->emit(&event); } std::size_t connected_receivers() const { std::size_t size = 0; for (EventSignalPtr handler : handlers_) { if (handler) size += handler->size(); } return size; } private: EventSignalPtr &signal_for(std::size_t id) { if (id >= handlers_.size()) handlers_.resize(id + 1); if (!handlers_[id]) handlers_[id] = std::make_shared(); return handlers_[id]; } // Functor used as an event signal callback that casts to E. template struct EventCallbackWrapper { explicit EventCallbackWrapper(std::function callback) : callback(callback) {} bool operator()(const void *event) { return callback(*(static_cast(event))); } std::function callback; }; std::vector handlers_; }; } // namespace entityx