diff --git a/README.md b/README.md index c0bd794..47a4df2 100644 --- a/README.md +++ b/README.md @@ -51,7 +51,7 @@ Defining Derived in this manner will allow and event of this type to be delivered as both Derived and Base. It is not required that mpm::enable_polymorphic_dispatch be used. Any instance -of a class type can be published, however if polymorphic delivery is desired +of a C++ object type can be published, however if polymorphic delivery is desired then mpm::enable_polymorphic_dispatch must be used. ## Example Usage diff --git a/include/mpm/eventbus.h b/include/mpm/eventbus.h index 6a783fd..e4e6db8 100644 --- a/include/mpm/eventbus.h +++ b/include/mpm/eventbus.h @@ -19,16 +19,16 @@ namespace mpm //! \ingroup Concepts //! \{ //! - //! An instance of any class type can be published as an event. This - //! definition precludes the publication of (for example) an int. + //! An instance of any C++ object type. That is, anything but a function, + //! reference, or void type //! //! \par Requirements //! Given:\n //! E, an implementation of the Event concept //! - //! |Expression | Requirements | Return type | - //! |:-------------------------------|:-----------------------|:-------------------| - //! |std::is_class::value == true | E must be a class type | bool, must be true | + //! |Expression | Requirements | Return type | + //! |:--------------------------------|:-------------------------|:-------------------| + //! |std::is_object::value == true | E must be an object type | bool, must be true | //! \} //! \defgroup EventHandler @@ -57,6 +57,30 @@ namespace mpm namespace detail { + //! Adapts an instance of the Event concept into the + //! class hierarchy rooted at mpm::detail::event + template + class adapted_event : public detail::event + { + public: + using dispatch_as = detail::typelist; + + adapted_event(const Event& event) + : m_event{event} + { + } + + + operator const Event&() const + { + return m_event; + } + + private: + const Event& m_event; + }; + + //! Holds the subscriber event type and the handler instance //! in a type-erased manner so that they can be put into a //! homogeneous container (e.g. the std::unordered_multimap as below) @@ -98,9 +122,13 @@ namespace mpm template struct model : concept { - explicit model(H h) : handler(std::move(h)) { } + explicit model(H h) + : handler(std::move(h)) + { + } + - void deliver(const detail::event& e) override final + void deliver(const event& e) override final { handler(static_cast(e)); } @@ -110,21 +138,21 @@ namespace mpm // Specialization for events that do not use - // enable_polymorphic_dispatch. The diffence is that we must use - // *dynamic_cast* in this case as the dispatch_as event type (E) - // will not have detail::event as a base class + // enable_polymorphic_dispatch. The diffence is that we static_cast + // to adapted_event. template struct model::value>::type> : concept { - explicit model(H h) : handler(std::move(h)) { } + explicit model(H h) + : handler(std::move(h)) + { + } - void deliver(const detail::event& e) override final + + void deliver(const event& e) override final { - if(const E* ptr = dynamic_cast(&e)) - { - handler(*ptr); - } + handler(static_cast&>(e)); } H handler; @@ -201,19 +229,6 @@ namespace mpm { } }; - - - template - class adapted_event : public detail::event, public Base - { - public: - using dispatch_as = detail::typelist; - - adapted_event(const Base& base) - : Base(base) - { - } - }; } //! A small POD type representing a subscription @@ -256,10 +271,7 @@ namespace mpm publish(const E& event) noexcept; template - typename std::enable_if< - !std::is_base_of::value && - std::is_class::value && - std::is_nothrow_copy_constructible::value>::type + typename std::enable_if::value>::type publish(const E& event) noexcept; # endif @@ -323,8 +335,8 @@ namespace mpm using types = typename detail::dispatch_typelist::type; m_subscriptions.observe( [&](typename subscriptions::const_reference subs){ - detail::for_each_type( - detail::deliver(event, subs)); + detail::for_each_type( + detail::deliver{event, subs}); } ); } @@ -332,10 +344,7 @@ namespace mpm template template - typename std::enable_if< - !std::is_base_of::value && - std::is_class::value && - std::is_nothrow_copy_constructible::value>::type + typename std::enable_if::value>::type basic_eventbus::publish(const Event& event) noexcept { publish(detail::adapted_event{event}); @@ -348,8 +357,8 @@ namespace mpm cookie basic_eventbus::subscribe(const EventHandler& handler) { - static_assert(std::is_class::value, - "Events must be class types"); + static_assert(std::is_object::value, + "Events must be object types"); static_assert(noexcept(handler(std::declval())), "Need noexcept handler for Event"); diff --git a/test/test_eventbus.cpp b/test/test_eventbus.cpp index 1ffe5f1..eb7ded4 100644 --- a/test/test_eventbus.cpp +++ b/test/test_eventbus.cpp @@ -21,9 +21,19 @@ struct very_derived_event : derived_event }; -struct non_polymorphic_event +class non_polymorphic_event { + public: + non_polymorphic_event() + { + } + + int l = 4; + + private: + non_polymorphic_event(const non_polymorphic_event&); + non_polymorphic_event& operator=(const non_polymorphic_event&); }; // todo C++14 - all event handlers can use const auto& @@ -129,3 +139,32 @@ TEST_CASE("raii", "[mpm.eventbus]") //scoped_subscription that is "empty" } } + + +TEST_CASE("publish non-class type", "[mpm.eventbus]") +{ + mpm::eventbus ebus; + int calls = 0; + bool b = false; + auto intsub = mpm::scoped_subscription { + ebus, [&](int i) noexcept { calls += i; } + }; + auto boolsub = mpm::scoped_subscription { + ebus, [&](bool event) noexcept { b = event; } + }; + ebus.publish(12); + ebus.publish(true); + CHECK(12 == calls); + CHECK(b); +} + + +TEST_CASE("publish pointer", "[mpm.eventbus]") +{ + mpm::eventbus ebus; + non_polymorphic_event npe; + auto subscription = mpm::scoped_subscription { + ebus, [&](non_polymorphic_event* ptr) noexcept { CHECK(ptr == &npe); } + }; + ebus.publish(&npe); +}