Skip to content

Commit

Permalink
Make the Event concept more flexible.
Browse files Browse the repository at this point in the history
With a little more cleverness in adapted_event we can remove the requirements
for Events being CopyConstructible Class types. Now we can publish integers and
pointers and other dumb stuff.
  • Loading branch information
mmcshane committed Apr 22, 2016
1 parent 26d8170 commit 9ca7e0a
Show file tree
Hide file tree
Showing 3 changed files with 91 additions and 43 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
91 changes: 50 additions & 41 deletions include/mpm/eventbus.h
Original file line number Diff line number Diff line change
Expand Up @@ -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<E>::value == true | E must be a class type | bool, must be true |
//! |Expression | Requirements | Return type |
//! |:--------------------------------|:-------------------------|:-------------------|
//! |std::is_object<E>::value == true | E must be an object type | bool, must be true |
//! \}

//! \defgroup EventHandler
Expand Down Expand Up @@ -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 <typename Event>
class adapted_event : public detail::event
{
public:
using dispatch_as = detail::typelist<Event>;

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)
Expand Down Expand Up @@ -98,9 +122,13 @@ namespace mpm
template <typename E, typename H, typename Enable=void>
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<const E&>(e));
}
Expand All @@ -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<E>.
template <typename E, typename H>
struct model<E, H, typename std::enable_if<
!std::is_base_of<detail::event, E>::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<const E*>(&e))
{
handler(*ptr);
}
handler(static_cast<const adapted_event<E>&>(e));
}

H handler;
Expand Down Expand Up @@ -201,19 +229,6 @@ namespace mpm
{
}
};


template <typename Base>
class adapted_event : public detail::event, public Base
{
public:
using dispatch_as = detail::typelist<Base>;

adapted_event(const Base& base)
: Base(base)
{
}
};
}

//! A small POD type representing a subscription
Expand Down Expand Up @@ -256,10 +271,7 @@ namespace mpm
publish(const E& event) noexcept;

template <typename E>
typename std::enable_if<
!std::is_base_of<detail::event, E>::value &&
std::is_class<E>::value &&
std::is_nothrow_copy_constructible<E>::value>::type
typename std::enable_if<!std::is_base_of<detail::event, E>::value>::type
publish(const E& event) noexcept;

# endif
Expand Down Expand Up @@ -323,19 +335,16 @@ namespace mpm
using types = typename detail::dispatch_typelist<Event>::type;
m_subscriptions.observe(
[&](typename subscriptions::const_reference subs){
detail::for_each_type<types>(
detail::deliver<decltype(subs)>(event, subs));
detail::for_each_type<types>(
detail::deliver<decltype(subs)>{event, subs});
}
);
}


template <typename A>
template <typename Event>
typename std::enable_if<
!std::is_base_of<detail::event, Event>::value &&
std::is_class<Event>::value &&
std::is_nothrow_copy_constructible<Event>::value>::type
typename std::enable_if<!std::is_base_of<detail::event, Event>::value>::type
basic_eventbus<A>::publish(const Event& event) noexcept
{
publish(detail::adapted_event<Event>{event});
Expand All @@ -348,8 +357,8 @@ namespace mpm
cookie
basic_eventbus<A>::subscribe(const EventHandler& handler)
{
static_assert(std::is_class<Event>::value,
"Events must be class types");
static_assert(std::is_object<Event>::value,
"Events must be object types");
static_assert(noexcept(handler(std::declval<const Event&>())),
"Need noexcept handler for Event");

Expand Down
41 changes: 40 additions & 1 deletion test/test_eventbus.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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&
Expand Down Expand Up @@ -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<int> {
ebus, [&](int i) noexcept { calls += i; }
};
auto boolsub = mpm::scoped_subscription<bool> {
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<non_polymorphic_event*> {
ebus, [&](non_polymorphic_event* ptr) noexcept { CHECK(ptr == &npe); }
};
ebus.publish(&npe);
}

0 comments on commit 9ca7e0a

Please sign in to comment.