Skip to content

Commit 9ca7e0a

Browse files
committed
Make the Event concept more flexible.
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.
1 parent 26d8170 commit 9ca7e0a

File tree

3 files changed

+91
-43
lines changed

3 files changed

+91
-43
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ Defining Derived in this manner will allow and event of this type to be
5151
delivered as both Derived and Base.
5252

5353
It is not required that mpm::enable_polymorphic_dispatch be used. Any instance
54-
of a class type can be published, however if polymorphic delivery is desired
54+
of a C++ object type can be published, however if polymorphic delivery is desired
5555
then mpm::enable_polymorphic_dispatch must be used.
5656

5757
## Example Usage

include/mpm/eventbus.h

Lines changed: 50 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -19,16 +19,16 @@ namespace mpm
1919
//! \ingroup Concepts
2020
//! \{
2121
//!
22-
//! An instance of any class type can be published as an event. This
23-
//! definition precludes the publication of (for example) an int.
22+
//! An instance of any C++ object type. That is, anything but a function,
23+
//! reference, or void type
2424
//!
2525
//! \par Requirements
2626
//! Given:\n
2727
//! E, an implementation of the Event concept
2828
//!
29-
//! |Expression | Requirements | Return type |
30-
//! |:-------------------------------|:-----------------------|:-------------------|
31-
//! |std::is_class<E>::value == true | E must be a class type | bool, must be true |
29+
//! |Expression | Requirements | Return type |
30+
//! |:--------------------------------|:-------------------------|:-------------------|
31+
//! |std::is_object<E>::value == true | E must be an object type | bool, must be true |
3232
//! \}
3333

3434
//! \defgroup EventHandler
@@ -57,6 +57,30 @@ namespace mpm
5757

5858
namespace detail
5959
{
60+
//! Adapts an instance of the Event concept into the
61+
//! class hierarchy rooted at mpm::detail::event
62+
template <typename Event>
63+
class adapted_event : public detail::event
64+
{
65+
public:
66+
using dispatch_as = detail::typelist<Event>;
67+
68+
adapted_event(const Event& event)
69+
: m_event{event}
70+
{
71+
}
72+
73+
74+
operator const Event&() const
75+
{
76+
return m_event;
77+
}
78+
79+
private:
80+
const Event& m_event;
81+
};
82+
83+
6084
//! Holds the subscriber event type and the handler instance
6185
//! in a type-erased manner so that they can be put into a
6286
//! homogeneous container (e.g. the std::unordered_multimap as below)
@@ -98,9 +122,13 @@ namespace mpm
98122
template <typename E, typename H, typename Enable=void>
99123
struct model : concept
100124
{
101-
explicit model(H h) : handler(std::move(h)) { }
125+
explicit model(H h)
126+
: handler(std::move(h))
127+
{
128+
}
129+
102130

103-
void deliver(const detail::event& e) override final
131+
void deliver(const event& e) override final
104132
{
105133
handler(static_cast<const E&>(e));
106134
}
@@ -110,21 +138,21 @@ namespace mpm
110138

111139

112140
// Specialization for events that do not use
113-
// enable_polymorphic_dispatch. The diffence is that we must use
114-
// *dynamic_cast* in this case as the dispatch_as event type (E)
115-
// will not have detail::event as a base class
141+
// enable_polymorphic_dispatch. The diffence is that we static_cast
142+
// to adapted_event<E>.
116143
template <typename E, typename H>
117144
struct model<E, H, typename std::enable_if<
118145
!std::is_base_of<detail::event, E>::value>::type> : concept
119146
{
120-
explicit model(H h) : handler(std::move(h)) { }
147+
explicit model(H h)
148+
: handler(std::move(h))
149+
{
150+
}
121151

122-
void deliver(const detail::event& e) override final
152+
153+
void deliver(const event& e) override final
123154
{
124-
if(const E* ptr = dynamic_cast<const E*>(&e))
125-
{
126-
handler(*ptr);
127-
}
155+
handler(static_cast<const adapted_event<E>&>(e));
128156
}
129157

130158
H handler;
@@ -201,19 +229,6 @@ namespace mpm
201229
{
202230
}
203231
};
204-
205-
206-
template <typename Base>
207-
class adapted_event : public detail::event, public Base
208-
{
209-
public:
210-
using dispatch_as = detail::typelist<Base>;
211-
212-
adapted_event(const Base& base)
213-
: Base(base)
214-
{
215-
}
216-
};
217232
}
218233

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

258273
template <typename E>
259-
typename std::enable_if<
260-
!std::is_base_of<detail::event, E>::value &&
261-
std::is_class<E>::value &&
262-
std::is_nothrow_copy_constructible<E>::value>::type
274+
typename std::enable_if<!std::is_base_of<detail::event, E>::value>::type
263275
publish(const E& event) noexcept;
264276

265277
# endif
@@ -323,19 +335,16 @@ namespace mpm
323335
using types = typename detail::dispatch_typelist<Event>::type;
324336
m_subscriptions.observe(
325337
[&](typename subscriptions::const_reference subs){
326-
detail::for_each_type<types>(
327-
detail::deliver<decltype(subs)>(event, subs));
338+
detail::for_each_type<types>(
339+
detail::deliver<decltype(subs)>{event, subs});
328340
}
329341
);
330342
}
331343

332344

333345
template <typename A>
334346
template <typename Event>
335-
typename std::enable_if<
336-
!std::is_base_of<detail::event, Event>::value &&
337-
std::is_class<Event>::value &&
338-
std::is_nothrow_copy_constructible<Event>::value>::type
347+
typename std::enable_if<!std::is_base_of<detail::event, Event>::value>::type
339348
basic_eventbus<A>::publish(const Event& event) noexcept
340349
{
341350
publish(detail::adapted_event<Event>{event});
@@ -348,8 +357,8 @@ namespace mpm
348357
cookie
349358
basic_eventbus<A>::subscribe(const EventHandler& handler)
350359
{
351-
static_assert(std::is_class<Event>::value,
352-
"Events must be class types");
360+
static_assert(std::is_object<Event>::value,
361+
"Events must be object types");
353362
static_assert(noexcept(handler(std::declval<const Event&>())),
354363
"Need noexcept handler for Event");
355364

test/test_eventbus.cpp

Lines changed: 40 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,19 @@ struct very_derived_event : derived_event
2121
};
2222

2323

24-
struct non_polymorphic_event
24+
class non_polymorphic_event
2525
{
26+
public:
27+
non_polymorphic_event()
28+
{
29+
}
30+
31+
2632
int l = 4;
33+
34+
private:
35+
non_polymorphic_event(const non_polymorphic_event&);
36+
non_polymorphic_event& operator=(const non_polymorphic_event&);
2737
};
2838

2939
// todo C++14 - all event handlers can use const auto&
@@ -129,3 +139,32 @@ TEST_CASE("raii", "[mpm.eventbus]")
129139
//scoped_subscription that is "empty"
130140
}
131141
}
142+
143+
144+
TEST_CASE("publish non-class type", "[mpm.eventbus]")
145+
{
146+
mpm::eventbus ebus;
147+
int calls = 0;
148+
bool b = false;
149+
auto intsub = mpm::scoped_subscription<int> {
150+
ebus, [&](int i) noexcept { calls += i; }
151+
};
152+
auto boolsub = mpm::scoped_subscription<bool> {
153+
ebus, [&](bool event) noexcept { b = event; }
154+
};
155+
ebus.publish(12);
156+
ebus.publish(true);
157+
CHECK(12 == calls);
158+
CHECK(b);
159+
}
160+
161+
162+
TEST_CASE("publish pointer", "[mpm.eventbus]")
163+
{
164+
mpm::eventbus ebus;
165+
non_polymorphic_event npe;
166+
auto subscription = mpm::scoped_subscription<non_polymorphic_event*> {
167+
ebus, [&](non_polymorphic_event* ptr) noexcept { CHECK(ptr == &npe); }
168+
};
169+
ebus.publish(&npe);
170+
}

0 commit comments

Comments
 (0)