1
0
mirror of https://github.com/wqking/eventpp.git synced 2024-12-26 15:52:40 +08:00

tab->spaces in documents for easier read

This commit is contained in:
wqking 2022-02-04 09:00:50 +08:00
parent 05f3dc8249
commit 6e5f6ee45d
25 changed files with 815 additions and 815 deletions

View File

@ -54,9 +54,9 @@ A typical implementation of `Digester`:
template <typename T>
struct MyDigest
{
TheDigestTypeSuchAsSizeT operator() (const T & value) const {
// compute the digest of value and return the digest.
}
TheDigestTypeSuchAsSizeT operator() (const T & value) const {
// compute the digest of value and return the digest.
}
};
```
Note: the return type of the function call operator (here is TheDigestTypeSuchAsSizeT) must be the same for all T, it can't be different type for different T.
@ -65,12 +65,12 @@ A typical implementation of `Storage`:
```c++
struct MyStorage
{
template <typename T>
MyStorage(const T & value) {
// store the value
}
template <typename T>
MyStorage(const T & value) {
// store the value
}
// any other member functions can be added, such as getting the underlying value.
// any other member functions can be added, such as getting the underlying value.
};
```
Or none template version:
@ -78,10 +78,10 @@ Or none template version:
// In this version, only value of `int` and `std::string` can be stored.
struct MyStorage
{
MyStorage(const int value) {}
MyStorage(const std::string & value) {}
MyStorage(const int value) {}
MyStorage(const std::string & value) {}
// any other member functions can be added, such as getting the underlying value.
// any other member functions can be added, such as getting the underlying value.
};
```

View File

@ -70,74 +70,74 @@ Below is the example code to demonstrate how to use `argumentAdapter`. There are
// Define the event types
enum class EventType
{
// for MouseEvent
mouse,
// for MouseEvent
mouse,
// for KeyEvent
key,
// for KeyEvent
key,
// for MessageEvent
message,
// for MessageEvent
message,
// For either MouseEvent or KeyEvent, we use this type to demonstrate
// how to use conditionalFunctor
input,
// For either MouseEvent or KeyEvent, we use this type to demonstrate
// how to use conditionalFunctor
input,
};
class Event
{
public:
Event() {
}
Event() {
}
// Make the Event polymorphism so we can use dynamic_cast to detect
// if it's a MouseEvent or KeyEvent
virtual ~Event() {
}
// Make the Event polymorphism so we can use dynamic_cast to detect
// if it's a MouseEvent or KeyEvent
virtual ~Event() {
}
};
class MouseEvent : public Event
{
public:
MouseEvent(const int x, const int y)
: x(x), y(y)
{
}
MouseEvent(const int x, const int y)
: x(x), y(y)
{
}
int getX() const { return x; }
int getY() const { return y; }
int getX() const { return x; }
int getY() const { return y; }
private:
int x;
int y;
int x;
int y;
};
class KeyEvent : public Event
{
public:
explicit KeyEvent(const int key)
: key(key)
{
}
explicit KeyEvent(const int key)
: key(key)
{
}
int getKey() const { return key; }
int getKey() const { return key; }
private:
int key;
int key;
};
class MessageEvent : public Event
{
public:
explicit MessageEvent(const std::string & message)
: message(message) {
}
explicit MessageEvent(const std::string & message)
: message(message) {
}
std::string getMessage() const { return message; }
std::string getMessage() const { return message; }
private:
std::string message;
std::string message;
};
// A free function that will be added as listener later.
@ -145,61 +145,61 @@ private:
// lambda, functor object, std::function, free function, etc.
void tutorialArgumentAdapterFreeFunction(const MouseEvent & e)
{
std::cout << "Received MouseEvent in free function, x=" << e.getX() << " y=" << e.getY() << std::endl;
std::cout << "Received MouseEvent in free function, x=" << e.getX() << " y=" << e.getY() << std::endl;
}
void example1()
{
eventpp::EventDispatcher<EventType, void (const Event &)> eventDispatcher;
eventpp::EventDispatcher<EventType, void (const Event &)> eventDispatcher;
// callback 1 -- lambda, or any functor object
// callback 1 -- lambda, or any functor object
// This can't compile because a 'const Event &' can be passed to 'const MouseEvent &'
//eventDispatcher.appendListener(mouseEventId, [](const MouseEvent & e) {});
// This can't compile because a 'const Event &' can be passed to 'const MouseEvent &'
//eventDispatcher.appendListener(mouseEventId, [](const MouseEvent & e) {});
// This compiles. eventpp::argumentAdapter creates a functor object that static_cast
// 'const Event &' to 'const MouseEvent &' automatically.
// Note we need to pass the function type to eventpp::argumentAdapter because the lambda
// doesn't have any function type information and eventpp::argumentAdapter can't deduce
// the type. This rule also applies to other functor object.
eventDispatcher.appendListener(
EventType::mouse,
eventpp::argumentAdapter<void(const MouseEvent &)>([](const MouseEvent & e) {
std::cout << "Received MouseEvent in lambda, x=" << e.getX() << " y=" << e.getY() << std::endl;
})
);
eventDispatcher.appendListener(
EventType::message,
eventpp::argumentAdapter<void(const MessageEvent &)>([](const MessageEvent & e) {
std::cout << "Received MessageEvent in lambda, message=" << e.getMessage() << std::endl;
})
);
// This compiles. eventpp::argumentAdapter creates a functor object that static_cast
// 'const Event &' to 'const MouseEvent &' automatically.
// Note we need to pass the function type to eventpp::argumentAdapter because the lambda
// doesn't have any function type information and eventpp::argumentAdapter can't deduce
// the type. This rule also applies to other functor object.
eventDispatcher.appendListener(
EventType::mouse,
eventpp::argumentAdapter<void(const MouseEvent &)>([](const MouseEvent & e) {
std::cout << "Received MouseEvent in lambda, x=" << e.getX() << " y=" << e.getY() << std::endl;
})
);
eventDispatcher.appendListener(
EventType::message,
eventpp::argumentAdapter<void(const MessageEvent &)>([](const MessageEvent & e) {
std::cout << "Received MessageEvent in lambda, message=" << e.getMessage() << std::endl;
})
);
// callback 2 -- std::function
// We don't need to pass the function type to eventpp::argumentAdapter because it can
// deduce the type from the std::function
eventDispatcher.appendListener(
EventType::key,
eventpp::argumentAdapter(std::function<void(const KeyEvent &)>([](const KeyEvent & e) {
std::cout << "Received KeyEvent in std::function, key=" << e.getKey() << std::endl;
}))
);
// callback 2 -- std::function
// We don't need to pass the function type to eventpp::argumentAdapter because it can
// deduce the type from the std::function
eventDispatcher.appendListener(
EventType::key,
eventpp::argumentAdapter(std::function<void(const KeyEvent &)>([](const KeyEvent & e) {
std::cout << "Received KeyEvent in std::function, key=" << e.getKey() << std::endl;
}))
);
// callback 3 -- free function
// We don't need to pass the function type to eventpp::argumentAdapter because it can
// deduce the type from the free function
eventDispatcher.appendListener(
EventType::mouse,
eventpp::argumentAdapter(tutorialArgumentAdapterFreeFunction)
);
// callback 3 -- free function
// We don't need to pass the function type to eventpp::argumentAdapter because it can
// deduce the type from the free function
eventDispatcher.appendListener(
EventType::mouse,
eventpp::argumentAdapter(tutorialArgumentAdapterFreeFunction)
);
eventDispatcher.dispatch(EventType::mouse, MouseEvent(3, 5));
eventDispatcher.dispatch(EventType::key, KeyEvent(255));
eventDispatcher.dispatch(EventType::message, MessageEvent("Hello, argumentAdapter"));
// In syntax we can dispatch KeyEvent under EventType::mouse, in our case,
// the EventType::mouse listener casts KeyEvent to MouseEvent, which is invalid object,
// and the listener will either use garbled data, or crash.
//eventDispatcher.dispatch(EventType::mouse, KeyEvent(255));
eventDispatcher.dispatch(EventType::mouse, MouseEvent(3, 5));
eventDispatcher.dispatch(EventType::key, KeyEvent(255));
eventDispatcher.dispatch(EventType::message, MessageEvent("Hello, argumentAdapter"));
// In syntax we can dispatch KeyEvent under EventType::mouse, in our case,
// the EventType::mouse listener casts KeyEvent to MouseEvent, which is invalid object,
// and the listener will either use garbled data, or crash.
//eventDispatcher.dispatch(EventType::mouse, KeyEvent(255));
}
void example2()
@ -212,25 +212,25 @@ eventpp::EventDispatcher<EventType, void(const Event &)> eventDispatcher;
// listener 1
eventDispatcher.appendListener(
EventType::input,
eventpp::conditionalFunctor(
eventpp::argumentAdapter<void(const MouseEvent &)>([](const MouseEvent & e) {
std::cout << "Received MouseEvent in conditional tutorial, x=" << e.getX() << " y=" << e.getY() << std::endl;
}),
// This lambda is the condition. We use dynamic_cast to check if the event is desired.
// This is for demonstration purpose, in production you may use a better way than dynamic_cast.
[](const Event & e) { return dynamic_cast<const MouseEvent *>(&e) != nullptr; }
)
EventType::input,
eventpp::conditionalFunctor(
eventpp::argumentAdapter<void(const MouseEvent &)>([](const MouseEvent & e) {
std::cout << "Received MouseEvent in conditional tutorial, x=" << e.getX() << " y=" << e.getY() << std::endl;
}),
// This lambda is the condition. We use dynamic_cast to check if the event is desired.
// This is for demonstration purpose, in production you may use a better way than dynamic_cast.
[](const Event & e) { return dynamic_cast<const MouseEvent *>(&e) != nullptr; }
)
);
// listener 2
eventDispatcher.appendListener(
EventType::input,
eventpp::conditionalFunctor(
eventpp::argumentAdapter<void(const KeyEvent &)>([](const KeyEvent & e) {
std::cout << "Received KeyEvent in conditional tutorial, key=" << e.getKey() << std::endl;
}),
[](const Event & e) { return dynamic_cast<const KeyEvent *>(&e) != nullptr; }
)
EventType::input,
eventpp::conditionalFunctor(
eventpp::argumentAdapter<void(const KeyEvent &)>([](const KeyEvent & e) {
std::cout << "Received KeyEvent in conditional tutorial, key=" << e.getKey() << std::endl;
}),
[](const Event & e) { return dynamic_cast<const KeyEvent *>(&e) != nullptr; }
)
);
// listener 1 will receive this event, listener 2 will not.
@ -255,10 +255,10 @@ eventpp::EventQueue<EventType, void(std::shared_ptr<Event>)> eventQueue;
// This compiles. eventpp::argumentAdapter creates a functor object that static_cast
// 'std::shared_ptr<Event>' to 'std::shared_ptr<MouseEvent>' automatically.
eventQueue.appendListener(
EventType::mouse,
eventpp::argumentAdapter<void(std::shared_ptr<MouseEvent>)>([](std::shared_ptr<MouseEvent> e) {
std::cout << "Received MouseEvent as std::shared_ptr, x=" << e->getX() << " y=" << e->getY() << std::endl;
})
EventType::mouse,
eventpp::argumentAdapter<void(std::shared_ptr<MouseEvent>)>([](std::shared_ptr<MouseEvent> e) {
std::cout << "Received MouseEvent as std::shared_ptr, x=" << e->getX() << " y=" << e->getY() << std::endl;
})
);
eventQueue.enqueue(EventType::mouse, std::make_shared<MouseEvent>(3, 5));

View File

@ -8,40 +8,40 @@ Time unit: milliseconds (unless explicitly specified)
<table>
<tr>
<th>Iterations</th>
<th>Queue size</th>
<th>Event count</th>
<th>Event Types</th>
<th>Listener count</th>
<th>Time of single threading</th>
<th>Time of multi threading</th>
<th>Iterations</th>
<th>Queue size</th>
<th>Event count</th>
<th>Event Types</th>
<th>Listener count</th>
<th>Time of single threading</th>
<th>Time of multi threading</th>
</tr>
<tr>
<td>100k</td>
<td>100</td>
<td>10M</td>
<td>100</td>
<td>100</td>
<td>401</td>
<td>1146</td>
<td>100k</td>
<td>100</td>
<td>10M</td>
<td>100</td>
<td>100</td>
<td>401</td>
<td>1146</td>
</tr>
<tr>
<td>100k</td>
<td>1000</td>
<td>100M</td>
<td>100</td>
<td>100</td>
<td>4012</td>
<td>11467</td>
<td>100k</td>
<td>1000</td>
<td>100M</td>
<td>100</td>
<td>100</td>
<td>4012</td>
<td>11467</td>
</tr>
<tr>
<td>100k</td>
<td>1000</td>
<td>100M</td>
<td>1000</td>
<td>1000</td>
<td>4102</td>
<td>11600</td>
<td>100k</td>
<td>1000</td>
<td>100M</td>
<td>1000</td>
<td>1000</td>
<td>4102</td>
<td>11600</td>
</tr>
<table>
@ -53,107 +53,107 @@ The EventQueue is processed in one thread. The Single/Multi threading in the tab
<table>
<tr>
<th>Mutex</th>
<th>Enqueue threads</th>
<th>Process threads</th>
<th>Event count</th>
<th>Event Types</th>
<th>Listener count</th>
<th>Time</th>
<th>Mutex</th>
<th>Enqueue threads</th>
<th>Process threads</th>
<th>Event count</th>
<th>Event Types</th>
<th>Listener count</th>
<th>Time</th>
</tr>
<tr>
<td>std::mutex</td>
<td>1</td>
<td>1</td>
<td>10M</td>
<td>100</td>
<td>100</td>
<td>2283</td>
<td>std::mutex</td>
<td>1</td>
<td>1</td>
<td>10M</td>
<td>100</td>
<td>100</td>
<td>2283</td>
</tr>
<tr>
<td>SpinLock</td>
<td>1</td>
<td>1</td>
<td>10M</td>
<td>100</td>
<td>100</td>
<td>1692</td>
<td>SpinLock</td>
<td>1</td>
<td>1</td>
<td>10M</td>
<td>100</td>
<td>100</td>
<td>1692</td>
</tr>
<tr>
<td>std::mutex</td>
<td>1</td>
<td>3</td>
<td>10M</td>
<td>100</td>
<td>100</td>
<td>3446</td>
<td>std::mutex</td>
<td>1</td>
<td>3</td>
<td>10M</td>
<td>100</td>
<td>100</td>
<td>3446</td>
</tr>
<tr>
<td>SpinLock</td>
<td>1</td>
<td>3</td>
<td>10M</td>
<td>100</td>
<td>100</td>
<td>3025</td>
<td>SpinLock</td>
<td>1</td>
<td>3</td>
<td>10M</td>
<td>100</td>
<td>100</td>
<td>3025</td>
</tr>
<tr>
<td>std::mutex</td>
<td>2</td>
<td>2</td>
<td>10M</td>
<td>100</td>
<td>100</td>
<td>4000</td>
<td>std::mutex</td>
<td>2</td>
<td>2</td>
<td>10M</td>
<td>100</td>
<td>100</td>
<td>4000</td>
</tr>
<tr>
<td>SpinLock</td>
<td>2</td>
<td>2</td>
<td>10M</td>
<td>100</td>
<td>100</td>
<td>3076</td>
<td>SpinLock</td>
<td>2</td>
<td>2</td>
<td>10M</td>
<td>100</td>
<td>100</td>
<td>3076</td>
</tr>
<tr>
<td>std::mutex</td>
<td>4</td>
<td>4</td>
<td>10M</td>
<td>100</td>
<td>100</td>
<td>1971</td>
<td>std::mutex</td>
<td>4</td>
<td>4</td>
<td>10M</td>
<td>100</td>
<td>100</td>
<td>1971</td>
</tr>
<tr>
<td>SpinLock</td>
<td>4</td>
<td>4</td>
<td>10M</td>
<td>100</td>
<td>100</td>
<td>1755</td>
<td>SpinLock</td>
<td>4</td>
<td>4</td>
<td>10M</td>
<td>100</td>
<td>100</td>
<td>1755</td>
</tr>
<tr>
<td>std::mutex</td>
<td>16</td>
<td>16</td>
<td>10M</td>
<td>100</td>
<td>100</td>
<td>928</td>
<td>std::mutex</td>
<td>16</td>
<td>16</td>
<td>10M</td>
<td>100</td>
<td>100</td>
<td>928</td>
</tr>
<tr>
<td>SpinLock</td>
<td>16</td>
<td>16</td>
<td>10M</td>
<td>100</td>
<td>100</td>
<td>2082</td>
<td>SpinLock</td>
<td>16</td>
<td>16</td>
<td>10M</td>
<td>100</td>
<td>100</td>
<td>2082</td>
</tr>
</table>
@ -172,123 +172,123 @@ Iterations: 100,000,000
<table>
<tr>
<th>Function</th>
<th>Compiler</th>
<th>Native invoking</th>
<th>CallbackList single threading</th>
<th>CallbackList multi threading</th>
<th>Function</th>
<th>Compiler</th>
<th>Native invoking</th>
<th>CallbackList single threading</th>
<th>CallbackList multi threading</th>
</tr>
<tr>
<td rowspan="2">Inline global function</td>
<td>MSVC 2017</td>
<td>217</td>
<td>1501</td>
<td>6921</td>
<td rowspan="2">Inline global function</td>
<td>MSVC 2017</td>
<td>217</td>
<td>1501</td>
<td>6921</td>
</tr>
<tr>
<td>GCC 7.2</td>
<td>187</td>
<td>1489</td>
<td>4463</td>
<td>GCC 7.2</td>
<td>187</td>
<td>1489</td>
<td>4463</td>
</tr>
<tr>
<td rowspan="2">Non-inline global function</td>
<td>MSVC 2017</td>
<td>241</td>
<td>1526</td>
<td>6544</td>
<td rowspan="2">Non-inline global function</td>
<td>MSVC 2017</td>
<td>241</td>
<td>1526</td>
<td>6544</td>
</tr>
<tr>
<td>GCC 7.2</td>
<td>233</td>
<td>1488</td>
<td>4787</td>
<td>GCC 7.2</td>
<td>233</td>
<td>1488</td>
<td>4787</td>
</tr>
<tr>
<td rowspan="2">Function object</td>
<td>MSVC 2017</td>
<td>194</td>
<td>1498</td>
<td>6433</td>
<td rowspan="2">Function object</td>
<td>MSVC 2017</td>
<td>194</td>
<td>1498</td>
<td>6433</td>
</tr>
<tr>
<td>GCC 7.2</td>
<td>212</td>
<td>1485</td>
<td>4951</td>
<td>GCC 7.2</td>
<td>212</td>
<td>1485</td>
<td>4951</td>
</tr>
<tr>
<td rowspan="2">Member virtual function</td>
<td>MSVC 2017</td>
<td>207</td>
<td>1533</td>
<td>6558</td>
<td rowspan="2">Member virtual function</td>
<td>MSVC 2017</td>
<td>207</td>
<td>1533</td>
<td>6558</td>
</tr>
<tr>
<td>GCC 7.2</td>
<td>212</td>
<td>1485</td>
<td>4489</td>
<td>GCC 7.2</td>
<td>212</td>
<td>1485</td>
<td>4489</td>
</tr>
<tr>
<td rowspan="2">Member non-virtual function</td>
<td>MSVC 2017</td>
<td>214</td>
<td>1533</td>
<td>6390</td>
<td rowspan="2">Member non-virtual function</td>
<td>MSVC 2017</td>
<td>214</td>
<td>1533</td>
<td>6390</td>
</tr>
<tr>
<td>GCC 7.2</td>
<td>211</td>
<td>1486</td>
<td>4872</td>
<td>GCC 7.2</td>
<td>211</td>
<td>1486</td>
<td>4872</td>
</tr>
<tr>
<td rowspan="2">Member non-inline virtual function</td>
<td>MSVC 2017</td>
<td>206</td>
<td>1522</td>
<td>6578</td>
<td rowspan="2">Member non-inline virtual function</td>
<td>MSVC 2017</td>
<td>206</td>
<td>1522</td>
<td>6578</td>
</tr>
<tr>
<td>GCC 7.2</td>
<td>182</td>
<td>1666</td>
<td>4593</td>
<td>GCC 7.2</td>
<td>182</td>
<td>1666</td>
<td>4593</td>
</tr>
<tr>
<td rowspan="2">Member non-inline non-virtual function</td>
<td>MSVC 2017</td>
<td>206</td>
<td>1491</td>
<td>6992</td>
<td rowspan="2">Member non-inline non-virtual function</td>
<td>MSVC 2017</td>
<td>206</td>
<td>1491</td>
<td>6992</td>
</tr>
<tr>
<td>GCC 7.2</td>
<td>205</td>
<td>1486</td>
<td>4490</td>
<td>GCC 7.2</td>
<td>205</td>
<td>1486</td>
<td>4490</td>
</tr>
<tr>
<td rowspan="2">All functions</td>
<td>MSVC 2017</td>
<td>1374</td>
<td>10951</td>
<td>29973</td>
<td rowspan="2">All functions</td>
<td>MSVC 2017</td>
<td>1374</td>
<td>10951</td>
<td>29973</td>
</tr>
<tr>
<td>GCC 7.2</td>
<td>1223</td>
<td>9770</td>
<td>22958</td>
<td>GCC 7.2</td>
<td>1223</td>
<td>9770</td>
<td>22958</td>
</tr>
</table>
@ -306,40 +306,40 @@ volatile int globalValue = 0;
void globalFunction(int a, const int b)
{
globalValue += a + b;
globalValue += a + b;
}
NON_INLINE void nonInlineGlobalFunction(int a, const int b)
{
globalValue += a + b;
globalValue += a + b;
}
struct FunctionObject
{
void operator() (int a, const int b)
{
globalValue += a + b;
}
void operator() (int a, const int b)
{
globalValue += a + b;
}
virtual void virFunc(int a, const int b)
{
globalValue += a + b;
}
virtual void virFunc(int a, const int b)
{
globalValue += a + b;
}
void nonVirFunc(int a, const int b)
{
globalValue += a + b;
}
void nonVirFunc(int a, const int b)
{
globalValue += a + b;
}
NON_INLINE virtual void nonInlineVirFunc(int a, const int b)
{
globalValue += a + b;
}
NON_INLINE virtual void nonInlineVirFunc(int a, const int b)
{
globalValue += a + b;
}
NON_INLINE void nonInlineNonVirFunc(int a, const int b)
{
globalValue += a + b;
}
NON_INLINE void nonInlineNonVirFunc(int a, const int b)
{
globalValue += a + b;
}
};
#undef NON_INLINE

View File

@ -35,8 +35,8 @@ eventpp/callbacklist.h
```c++
template <
typename Prototype,
typename Policies = DefaultPolicies
typename Prototype,
typename Policies = DefaultPolicies
>
class CallbackList;
```

View File

@ -10,14 +10,14 @@ For example,
eventpp::CallbackList<void(int)> callbackList;
callbackList.append(
eventpp::conditionalFunctor(
[](const int value) {
std::cout << "We should get value 1." << here;
},
[](const int value) {
return value == 1;
}
)
eventpp::conditionalFunctor(
[](const int value) {
std::cout << "We should get value 1." << here;
},
[](const int value) {
return value == 1;
}
)
);
callbackList(2); // not trigger the callback
callbackList(1); // trigger the callback
@ -44,34 +44,34 @@ eventpp::CallbackList<void(int)> callbackList;
std::vector<int> dataList(3);
callbackList.append(
eventpp::conditionalFunctor(
[&dataList](const int index) {
++dataList[index];
},
[](const int index) {
return index == 0;
}
)
eventpp::conditionalFunctor(
[&dataList](const int index) {
++dataList[index];
},
[](const int index) {
return index == 0;
}
)
);
callbackList.append(
eventpp::conditionalFunctor(
[&dataList](const int index) {
++dataList[index];
},
[](const int index) {
return index == 1;
}
)
eventpp::conditionalFunctor(
[&dataList](const int index) {
++dataList[index];
},
[](const int index) {
return index == 1;
}
)
);
callbackList.append(
eventpp::conditionalFunctor(
[&dataList](const int index) {
++dataList[index];
},
[](const int index) {
return index == 2;
}
)
eventpp::conditionalFunctor(
[&dataList](const int index) {
++dataList[index];
},
[](const int index) {
return index == 2;
}
)
);
REQUIRE(dataList == std::vector<int>{ 0, 0, 0 });

View File

@ -49,47 +49,47 @@ Constructs an instance of ConditionalRemover.
```c++
template <typename Condition>
typename DispatcherType::Handle appendListener(
const typename DispatcherType::Event & event,
const typename DispatcherType::Callback & listener,
const Condition & condition
);
const typename DispatcherType::Event & event,
const typename DispatcherType::Callback & listener,
const Condition & condition
);
template <typename Condition>
typename DispatcherType::Handle prependListener(
const typename DispatcherType::Event & event,
const typename DispatcherType::Callback & listener,
const Condition & condition
);
const typename DispatcherType::Event & event,
const typename DispatcherType::Callback & listener,
const Condition & condition
);
template <typename Condition>
typename DispatcherType::Handle insertListener(
const typename DispatcherType::Event & event,
const typename DispatcherType::Callback & listener,
const typename DispatcherType::Handle & before,
const Condition & condition
);
const typename DispatcherType::Event & event,
const typename DispatcherType::Callback & listener,
const typename DispatcherType::Handle & before,
const Condition & condition
);
```
**Member functions for CallbackList**
```c++
template <typename Condition>
typename CallbackListType::Handle append(
const typename CallbackListType::Callback & listener,
const Condition & condition
);
const typename CallbackListType::Callback & listener,
const Condition & condition
);
template <typename Condition>
typename CallbackListType::Handle prepend(
const typename CallbackListType::Callback & listener,
const Condition & condition
);
const typename CallbackListType::Callback & listener,
const Condition & condition
);
template <typename Condition>
typename CallbackListType::Handle insert(
const typename CallbackListType::Callback & listener,
const typename CallbackListType::Handle & before,
const Condition & condition
);
const typename CallbackListType::Callback & listener,
const typename CallbackListType::Handle & before,
const Condition & condition
);
```
The member functions have the same names with the corresponding underlying class (CallbackList, EventDispatcher, or EventQueue), and also have the same parameters except there is one more parameter, `condition`. `condition` is a predicate function that returns a bool value. It's invoked after each trigger, if it returns true, the listener will be removed.
@ -126,26 +126,26 @@ eventpp::EventDispatcher<int, void ()> dispatcher;
constexpr int event = 3;
dispatcher.appendListener(event, []() {
// listener A
// listener A
});
// Note the ConditionalRemover instance returned by conditionalRemover is invoked
// prependListener and destroyed immediately.
std::string removeWho;
eventpp::conditionalRemover(dispatcher).prependListener(event, [&dataList]() {
// listener B
// listener B
}, [&removeWho]() -> bool {
return removeWho == "removeB";
return removeWho == "removeB";
});
auto handle = eventpp::conditionalRemover(dispatcher).appendListener(event, [&dataList]() {
// listener C
// listener C
}, [&removeWho]() -> bool {
return removeWho == "removeC";
return removeWho == "removeC";
});
eventpp::conditionalRemover(dispatcher).insertListener(event, [&dataList]() {
// listener D
// listener D
}, handle, [&removeWho]() -> bool {
return removeWho == "removeD";
return removeWho == "removeD";
});
dispatcher.dispatch(event);

View File

@ -48,42 +48,42 @@ Constructs an instance of CounterRemover.
**Member functions for EventDispatcher and EventQueue**
```c++
typename DispatcherType::Handle appendListener(
const typename DispatcherType::Event & event,
const typename DispatcherType::Callback & listener,
const int triggerCount = 1
);
const typename DispatcherType::Event & event,
const typename DispatcherType::Callback & listener,
const int triggerCount = 1
);
typename DispatcherType::Handle prependListener(
const typename DispatcherType::Event & event,
const typename DispatcherType::Callback & listener,
const int triggerCount = 1
);
const typename DispatcherType::Event & event,
const typename DispatcherType::Callback & listener,
const int triggerCount = 1
);
typename DispatcherType::Handle insertListener(
const typename DispatcherType::Event & event,
const typename DispatcherType::Callback & listener,
const typename DispatcherType::Handle & before,
const int triggerCount = 1
);
const typename DispatcherType::Event & event,
const typename DispatcherType::Callback & listener,
const typename DispatcherType::Handle & before,
const int triggerCount = 1
);
```
**Member functions for CallbackList**
```c++
typename CallbackListType::Handle append(
const typename CallbackListType::Callback & listener,
const int triggerCount = 1
);
const typename CallbackListType::Callback & listener,
const int triggerCount = 1
);
typename CallbackListType::Handle prepend(
const typename CallbackListType::Callback & listener,
const int triggerCount = 1
);
const typename CallbackListType::Callback & listener,
const int triggerCount = 1
);
typename CallbackListType::Handle insert(
const typename CallbackListType::Callback & listener,
const typename CallbackListType::Handle & before,
const int triggerCount = 1
);
const typename CallbackListType::Callback & listener,
const typename CallbackListType::Handle & before,
const int triggerCount = 1
);
```
The member functions have the same names with the corresponding underlying class (CallbackList, EventDispatcher, or EventQueue), and also have the same parameters except there is one more parameter, `triggerCount`. `triggerCount` is decreased by one on each trigger, and when `triggerCount` is zero or negative, the listener will be removed.
@ -110,19 +110,19 @@ eventpp::EventDispatcher<int, void ()> dispatcher;
constexpr int event = 3;
dispatcher.appendListener(event, []() {
// listener A
// listener A
});
// Note the CounterRemover instance returned by counterRemover is invoked
// prependListener and destroyed immediately.
eventpp::counterRemover(dispatcher).prependListener(event, []() {
// listener B
// listener B
});
auto handle = eventpp::counterRemover(dispatcher).appendListener(event, []() {
// listener C
// listener C
}, 2);
eventpp::counterRemover(dispatcher).insertListener(event, []() {
// listener D
// listener D
}, handle, 3);
dispatcher.dispatch(event);

View File

@ -34,9 +34,9 @@ eventpp/eventdispatcher.h
```c++
template <
typename Event,
typename Prototype,
typename Policies = DefaultPolicies
typename Event,
typename Prototype,
typename Policies = DefaultPolicies
>
class EventDispatcher;
```

View File

@ -15,23 +15,23 @@ class Event
class EventMouseDown : public Event
{
public:
int getX() const;
int getY() const;
int getButton() const;
int getX() const;
int getY() const;
int getButton() const;
private:
int x;
int y;
int button;
int x;
int y;
int button;
};
class EventKeyDown : public Event
{
public:
int getKey() const;
int getKey() const;
private:
int key;
int key;
};
```
@ -62,8 +62,8 @@ The macro always generates a default constructor and a constructor that takes al
Code examples:
```c++
EVENTPP_MAKE_EVENT(
EventDraw, Event, EventType::draw,
(std::string, getText, setText), (int, getX), (double, getSize)
EventDraw, Event, EventType::draw,
(std::string, getText, setText), (int, getX), (double, getSize)
);
```
Generates class like (in pseudo code),
@ -71,45 +71,45 @@ Generates class like (in pseudo code),
class EventDraw : public Event
{
public:
EventDraw(const std::string & text, const int x, const double size)
: Event(EventType::draw), text(text), x(x), size(size)
{
}
EventDraw(const std::string & text, const int x, const double size)
: Event(EventType::draw), text(text), x(x), size(size)
{
}
const std::string & getText() const {
return text;
}
const std::string & getText() const {
return text;
}
void setText(const std::string & value) {
text = value;
}
void setText(const std::string & value) {
text = value;
}
const int getX() const {
return x;
}
const int getX() const {
return x;
}
const double getSize() const {
return size;
}
const double getSize() const {
return size;
}
private:
std::string text;
int x;
double size;
std::string text;
int x;
double size;
};
```
```c++
EVENTPP_MAKE_EVENT(EventDraw, (Event<int, char>), EventType::draw,
(std::string, getText, setText), (int, getX), (double, getSize)
(std::string, getText, setText), (int, getX), (double, getSize)
);
```
Similar to above example, except the base class is `Event<int, char>`.
```c++
EVENTPP_MAKE_EVENT(EventDraw, (Event<int, char>), (EventType::draw, 5),
(std::string, getText, setText), (int, getX), (double, getSize)
(std::string, getText, setText), (int, getX), (double, getSize)
);
```
Similar to above example, except the base class is constructed with `(EventType::draw, 5)` instead of `(EventType::draw)`.

View File

@ -33,9 +33,9 @@ eventpp/eventqueue.h
```c++
template <
typename Event,
typename Prototype,
typename Policies = DefaultPolicies
typename Event,
typename Prototype,
typename Policies = DefaultPolicies
>
class EventQueue;
```
@ -49,16 +49,16 @@ EventQueue has the exactly same template parameters with EventDispatcher. Please
```c++
struct EventQueue::QueuedEvent
{
EventType event;
std::tuple<ArgTypes...> arguments;
EventType event;
std::tuple<ArgTypes...> arguments;
// get the event
EventType getEvent() const;
// get the event
EventType getEvent() const;
// get the argument of index N
// same as std::get<N>(queuedEvent.arguments)
template <std::size_t N>
NthArgType getArgument() const;
// get the argument of index N
// same as std::get<N>(queuedEvent.arguments)
template <std::size_t N>
NthArgType getArgument() const;
};
```
`event` is the EventQueue::Event, `arguments` are the arguments passed in `enqueue`.
@ -170,8 +170,8 @@ Note: though `wait` has work around with spurious wakeup internally, the queue i
`wait` is useful when a thread processes the event queue. A sampel usage is,
```c++
for(;;) {
eventQueue.wait();
eventQueue.process();
eventQueue.wait();
eventQueue.process();
}
```
The code works even if it doesn't `wait`, but doing that will waste CPU power resource.
@ -188,12 +188,12 @@ Return true if the queue is not empty, false if the return is caused by time out
```c++
std::atomic<bool> shouldStop(false);
for(;;) {
while(! eventQueue.waitFor(std::chrono::milliseconds(10)) && ! shouldStop.load()) ;
if(shouldStop.load()) {
break;
}
while(! eventQueue.waitFor(std::chrono::milliseconds(10)) && ! shouldStop.load()) ;
if(shouldStop.load()) {
break;
}
eventQueue.process();
eventQueue.process();
}
```
@ -206,8 +206,8 @@ Retrieve an event from the queue. The event is returned in `queuedEvent`.
```c++
struct EventQueue::QueuedEvent
{
TheEventType event;
std::tuple<ArgumentTypes...> arguments;
TheEventType event;
std::tuple<ArgumentTypes...> arguments;
};
```
`queuedEvent` is a EventQueue::QueuedEvent struct. `event` is the EventQueue::Event, `arguments` are the arguments passed in `enqueue`.
@ -245,10 +245,10 @@ Sample code
using EQ = eventpp::EventQueue<int, void ()>;
EQ queue;
{
EQ::DisableQueueNotify disableNotify(&queue);
// any blocking threads will not be waken up by the below two lines.
queue.enqueue(1);
queue.enqueue(2);
EQ::DisableQueueNotify disableNotify(&queue);
// any blocking threads will not be waken up by the below two lines.
queue.enqueue(1);
queue.enqueue(2);
}
// any blocking threads are waken up here immediately.

View File

@ -9,9 +9,9 @@ eventpp/utilities/eventutil.h
```c++
template <typename DispatcherType>
bool removeListener(
DispatcherType & dispatcher,
const typename DispatcherType::Event & event,
const typename DispatcherType::Callback & listener
DispatcherType & dispatcher,
const typename DispatcherType::Event & event,
const typename DispatcherType::Callback & listener
);
```
The function finds `listener` of `event` in `dispatcher`, if it finds one, removes the listener and returns true, otherwise returns false.
@ -21,8 +21,8 @@ This function requires the listener be able to be compared with equal operator (
```c++
template <typename CallbackListType>
bool removeListener(
CallbackListType & callbackList,
const typename CallbackListType::Callback & callback
CallbackListType & callbackList,
const typename CallbackListType::Callback & callback
);
```
The function finds `callback` in `callbackList`, if it finds one, removes the callback and returns true, otherwise returns false.
@ -32,9 +32,9 @@ This function requires the callback be able to be compared with equal operator (
```c++
template <typename DispatcherType>
bool hasListener(
DispatcherType & dispatcher,
const typename DispatcherType::Event & event,
const typename DispatcherType::Callback & listener
DispatcherType & dispatcher,
const typename DispatcherType::Event & event,
const typename DispatcherType::Callback & listener
);
```
The function finds `listener` of `event` in `dispatcher`, returns true if it finds any one, otherwise returns false.
@ -43,8 +43,8 @@ This function requires the listener be able to be compared with equal operator (
```c++
template <typename DispatcherType>
bool hasAnyListener(
DispatcherType & dispatcher,
const typename DispatcherType::Event & event
DispatcherType & dispatcher,
const typename DispatcherType::Event & event
);
```
The function finds any listener of `event` in `dispatcher`, returns true if it finds any one, otherwise returns false.
@ -52,8 +52,8 @@ The function finds any listener of `event` in `dispatcher`, returns true if it f
```c++
template <typename CallbackListType>
bool hasListener(
CallbackListType & callbackList,
const typename CallbackListType::Callback & callback
CallbackListType & callbackList,
const typename CallbackListType::Callback & callback
);
```
The function finds `callback` in `callbackList`, returns true if it finds one, otherwise returns false.
@ -62,7 +62,7 @@ This function requires the callback be able to be compared with equal operator (
```c++
template <typename CallbackListType>
bool hasAnyListener(
CallbackListType & callbackList
CallbackListType & callbackList
);
```
The function finds any callback in `callbackList`, returns true if it finds any one, otherwise returns false.

View File

@ -58,7 +58,7 @@ For example,
class MyEventDispatcher : public EventDispatcher<blah blah>
{
public:
virtual ~MyEventDispatcher();
virtual ~MyEventDispatcher();
};
class MyClass : public MyEventDispatcher
@ -86,22 +86,22 @@ eventpp::CallbackList<void ()> mainLoopTasks;
void mainLoop()
{
for(;;) {
// Do any stuff in the loop
for(;;) {
// Do any stuff in the loop
mainLoopTasks();
}
mainLoopTasks();
}
}
class MyEventQueue : public eventpp::EventQueue<blah blah>
{
public:
MyEventQueue()
{
mainLoopTasks.append([this]() {
process();
});
}
MyEventQueue()
{
mainLoopTasks.append([this]() {
process();
});
}
};
```
@ -118,25 +118,25 @@ eventpp::CallbackList<void ()> mainLoopTasks;
void threadMain()
{
while(! stopped) {
ioService.poll();
mainLoopTasks();
sleepSomeTime();
}
while(! stopped) {
ioService.poll();
mainLoopTasks();
sleepSomeTime();
}
ioService.run();
mainLoopTasks();
ioService.run();
mainLoopTasks();
}
class MyEventQueue : public eventpp::EventQueue<blah blah>
{
public:
MyEventQueue()
{
mainLoopTasks.append([this]() {
process();
});
}
MyEventQueue()
{
mainLoopTasks.append([this]() {
process();
});
}
};
```

View File

@ -32,8 +32,8 @@ eventpp/hetercallbacklist.h
```c++
template <
typename PrototypeList,
typename Policies = DefaultPolicies
typename PrototypeList,
typename Policies = DefaultPolicies
>
class HeterCallbackList;
```

View File

@ -31,9 +31,9 @@ eventpp/hetereventdispatcher.h
```c++
template <
typename Event,
typename PrototypeList,
typename Policies = DefaultPolicies
typename Event,
typename PrototypeList,
typename Policies = DefaultPolicies
>
class HeterEventDispatcher;
```

View File

@ -31,9 +31,9 @@ eventpp/eventqueue.h
```c++
template <
typename Event,
typename PrototypeList,
typename Policies = DefaultPolicies
typename Event,
typename PrototypeList,
typename Policies = DefaultPolicies
>
class HeterEventQueue;
```
@ -137,8 +137,8 @@ Note: though `wait` has work around with spurious wakeup internally, the queue i
`wait` is useful when a thread processes the event queue. A sampel usage is,
```c++
for(;;) {
eventQueue.wait();
eventQueue.process();
eventQueue.wait();
eventQueue.process();
}
```
The code works event if it doesn't `wait`, but doing that will waste CPU power resource.
@ -155,12 +155,12 @@ Return true if the queue is not empty, false if the return is caused by time out
```c++
std::atomic<bool> shouldStop(false);
for(;;) {
while(! eventQueue.waitFor(std::chrono::milliseconds(10)) && ! shouldStop.load()) ;
if(shouldStop.load()) {
break;
}
while(! eventQueue.waitFor(std::chrono::milliseconds(10)) && ! shouldStop.load()) ;
if(shouldStop.load()) {
break;
}
eventQueue.process();
eventQueue.process();
}
```
@ -177,10 +177,10 @@ Sample code
using EQ = eventpp::HeterEventQueue<int, void ()>;
EQ queue;
{
EQ::DisableQueueNotify disableNotify(&queue);
// any blocking threads will not be waken up by the below two lines.
queue.enqueue(1);
queue.enqueue(2);
EQ::DisableQueueNotify disableNotify(&queue);
// any blocking threads will not be waken up by the below two lines.
queue.enqueue(1);
queue.enqueue(2);
}
// any blocking threads are waken up here immediately.

View File

@ -30,22 +30,22 @@ eventpp/hetereventqueue.h
```c++
template <
typename PrototypeList,
typename Policies = DefaultPolicies
typename PrototypeList,
typename Policies = DefaultPolicies
>
class HeterCallbackList;
template <
typename Event,
typename PrototypeList,
typename Policies = DefaultPolicies
typename Event,
typename PrototypeList,
typename Policies = DefaultPolicies
>
class HeterEventDispatcher;
template <
typename Event,
typename PrototypeList,
typename Policies = DefaultPolicies
typename Event,
typename PrototypeList,
typename Policies = DefaultPolicies
>
class HeterEventQueue;
```
@ -54,22 +54,22 @@ For comparison, below are the template parameters for the homogeneous counterpar
```c++
template <
typename Prototype,
typename Policies = DefaultPolicies
typename Prototype,
typename Policies = DefaultPolicies
>
class CallbackList;
template <
typename Event,
typename Prototype,
typename Policies = DefaultPolicies
typename Event,
typename Prototype,
typename Policies = DefaultPolicies
>
class EventDispatcher;
template <
typename Event,
typename Prototype,
typename Policies = DefaultPolicies
typename Event,
typename Prototype,
typename Policies = DefaultPolicies
>
class EventQueue;
```

View File

@ -28,22 +28,22 @@ EventDispatcher is ideal when there are many kinds of events, or the number of e
```c++
enum class MyEventType
{
redraw,
mouseDown,
mouseUp,
//... maybe 200 other events here
redraw,
mouseDown,
mouseUp,
//... maybe 200 other events here
};
struct MyEvent {
MyEventType type;
// data that all events may need
MyEventType type;
// data that all events may need
};
struct MyEventPolicies
{
static MyEventType getEvent(const MyEvent & e) {
return e.type;
}
static MyEventType getEvent(const MyEvent & e) {
return e.type;
}
};
eventpp::EventDispatcher<MyEventType, void(const MyEvent &), MyEventPolicies> dispatcher;

View File

@ -47,7 +47,7 @@ class MyMixin : public Base
To enable mixins, add them to the `Mixins` type in the policies class. For example, to enable `MixinFilter`, define the dispatcher as,
```c++
struct MyPolicies {
using Mixins = eventpp::MixinList<eventpp::MixinFilter>;
using Mixins = eventpp::MixinList<eventpp::MixinFilter>;
};
eventpp::EventDispatcher<int, void (), MyPolicies> dispatcher;
```
@ -121,19 +121,19 @@ Return true if the filter is removed successfully.
**Code**
```c++
struct MyPolicies {
using Mixins = eventpp::MixinList<eventpp::MixinFilter>;
using Mixins = eventpp::MixinList<eventpp::MixinFilter>;
};
eventpp::EventDispatcher<int, void (int e, int i, std::string), MyPolicies> dispatcher;
dispatcher.appendListener(3, [](const int e, const int i, const std::string & s) {
std::cout
<< "Got event 3, i was 1 but actural is " << i
<< " s was Hello but actural is " << s
<< std::endl
;
std::cout
<< "Got event 3, i was 1 but actural is " << i
<< " s was Hello but actural is " << s
<< std::endl
;
});
dispatcher.appendListener(5, [](const int e, const int i, const std::string & s) {
std::cout << "Shout not got event 5" << std::endl;
std::cout << "Shout not got event 5" << std::endl;
});
// Add three event filters.
@ -141,27 +141,27 @@ dispatcher.appendListener(5, [](const int e, const int i, const std::string & s)
// The first filter modifies the input arguments to other values, then the subsequence filters
// and listeners will see the modified values.
dispatcher.appendFilter([](const int e, int & i, std::string & s) -> bool {
std::cout << "Filter 1, e is " << e << " passed in i is " << i << " s is " << s << std::endl;
i = 38;
s = "Hi";
std::cout << "Filter 1, changed i is " << i << " s is " << s << std::endl;
return true;
std::cout << "Filter 1, e is " << e << " passed in i is " << i << " s is " << s << std::endl;
i = 38;
s = "Hi";
std::cout << "Filter 1, changed i is " << i << " s is " << s << std::endl;
return true;
});
// The second filter filters out all event of 5. So no listeners on event 5 can be triggered.
// The third filter is not invoked on event 5 also.
dispatcher.appendFilter([](const int e, int & i, std::string & s) -> bool {
std::cout << "Filter 2, e is " << e << " passed in i is " << i << " s is " << s << std::endl;
if(e == 5) {
return false;
}
return true;
std::cout << "Filter 2, e is " << e << " passed in i is " << i << " s is " << s << std::endl;
if(e == 5) {
return false;
}
return true;
});
// The third filter just prints the input arguments.
dispatcher.appendFilter([](const int e, int & i, std::string & s) -> bool {
std::cout << "Filter 3, e is " << e << " passed in i is " << i << " s is " << s << std::endl;
return true;
std::cout << "Filter 3, e is " << e << " passed in i is " << i << " s is " << s << std::endl;
return true;
});
// Dispatch the events, the first argument is always the event type.

View File

@ -43,10 +43,10 @@ Its default implementation `OrderedQueueListCompare` looks like,
```c++
struct OrderedQueueListCompare
{
template <typename T>
bool operator() (const T & a, const T & b) const {
return a.event < b.event;
}
template <typename T>
bool operator() (const T & a, const T & b) const {
return a.event < b.event;
}
};
```
The typename T is actually the `EventQueue::QueuedEvent`. But since `OrderedQueueList` is used in the policies that are used to construct the EventQueue, it's not possible to specify the actual type in `OrderedQueueListCompare`, so here we use a template operator.
@ -56,8 +56,8 @@ The typename T is actually the `EventQueue::QueuedEvent`. But since `OrderedQueu
```c++
struct QueuedEvent
{
EventType event;
std::tuple<Args> arguments;
EventType event;
std::tuple<Args> arguments;
};
```
`event` is the event sent to the queue.
@ -70,56 +70,56 @@ struct QueuedEvent
// First let's define the event struct. e is the event type, priority determines the priority.
struct MyEvent
{
int e;
int priority;
int e;
int priority;
};
// The comparison function object used by eventpp::OrderedQueueList.
// The function compares the event by priority.
struct MyCompare
{
template <typename T>
bool operator() (const T & a, const T & b) const {
return std::get<0>(a.arguments).priority > std::get<0>(b.arguments).priority;
}
template <typename T>
bool operator() (const T & a, const T & b) const {
return std::get<0>(a.arguments).priority > std::get<0>(b.arguments).priority;
}
};
// Define the EventQueue policy
struct MyPolicy
{
template <typename Item>
using QueueList = eventpp::OrderedQueueList<Item, MyCompare >;
template <typename Item>
using QueueList = eventpp::OrderedQueueList<Item, MyCompare >;
static int getEvent(const MyEvent & event) {
return event.e;
}
static int getEvent(const MyEvent & event) {
return event.e;
}
};
void main()
{
using EQ = eventpp::EventQueue<int, void(const MyEvent &), MyPolicy>;
EQ queue;
using EQ = eventpp::EventQueue<int, void(const MyEvent &), MyPolicy>;
EQ queue;
queue.appendListener(3, [](const MyEvent & event) {
std::cout << "Get event " << event.e << "(should be 3)." << " priority: " << event.priority << std::endl;
});
queue.appendListener(5, [](const MyEvent & event) {
std::cout << "Get event " << event.e << "(should be 5)." << " priority: " << event.priority << std::endl;
});
queue.appendListener(7, [](const MyEvent & event) {
std::cout << "Get event " << event.e << "(should be 7)." << " priority: " << event.priority << std::endl;
});
queue.appendListener(3, [](const MyEvent & event) {
std::cout << "Get event " << event.e << "(should be 3)." << " priority: " << event.priority << std::endl;
});
queue.appendListener(5, [](const MyEvent & event) {
std::cout << "Get event " << event.e << "(should be 5)." << " priority: " << event.priority << std::endl;
});
queue.appendListener(7, [](const MyEvent & event) {
std::cout << "Get event " << event.e << "(should be 7)." << " priority: " << event.priority << std::endl;
});
// Add an event, the first number 5 is the event type, the second number 100 is the priority.
// After the queue processes, the events will be processed from higher priority to lower priority.
queue.enqueue(MyEvent{ 5, 100 });
queue.enqueue(MyEvent{ 5, 200 });
queue.enqueue(MyEvent{ 7, 300 });
queue.enqueue(MyEvent{ 7, 400 });
queue.enqueue(MyEvent{ 3, 500 });
queue.enqueue(MyEvent{ 3, 600 });
// Add an event, the first number 5 is the event type, the second number 100 is the priority.
// After the queue processes, the events will be processed from higher priority to lower priority.
queue.enqueue(MyEvent{ 5, 100 });
queue.enqueue(MyEvent{ 5, 200 });
queue.enqueue(MyEvent{ 7, 300 });
queue.enqueue(MyEvent{ 7, 400 });
queue.enqueue(MyEvent{ 3, 500 });
queue.enqueue(MyEvent{ 3, 600 });
queue.process();
queue.process();
}
```

View File

@ -42,39 +42,39 @@ Sample code
```c++
// Define an Event to hold all parameters.
struct MyEvent {
int type;
std::string message;
int param;
int type;
std::string message;
int param;
};
// Define policies to let the dispatcher knows how to
// extract the event type.
struct MyEventPolicies
{
static int getEvent(const MyEvent & e, bool /*b*/) {
return e.type;
}
static int getEvent(const MyEvent & e, bool /*b*/) {
return e.type;
}
};
// Pass MyEventPolicies as the third template argument of EventDispatcher.
// Note: the first template argument is the event type type int, not MyEvent.
eventpp::EventDispatcher<
int,
void (const MyEvent &, bool),
MyEventPolicies
int,
void (const MyEvent &, bool),
MyEventPolicies
> dispatcher;
// Add a listener.
// Note: the first argument is the event type of type int, not MyEvent.
dispatcher.appendListener(3, [](const MyEvent & e, bool b) {
std::cout
<< std::boolalpha
<< "Got event 3" << std::endl
<< "Event::type is " << e.type << std::endl
<< "Event::message is " << e.message << std::endl
<< "Event::param is " << e.param << std::endl
<< "b is " << b << std::endl
;
std::cout
<< std::boolalpha
<< "Got event 3" << std::endl
<< "Event::type is " << e.type << std::endl
<< "Event::message is " << e.message << std::endl
<< "Event::param is " << e.param << std::endl
<< "b is " << b << std::endl
;
});
// Dispatch the event.
@ -93,35 +93,35 @@ Sample code
```c++
struct MyEvent {
MyEvent() : type(0), canceled(false) {
}
explicit MyEvent(const int type)
: type(type), canceled(false) {
}
MyEvent() : type(0), canceled(false) {
}
explicit MyEvent(const int type)
: type(type), canceled(false) {
}
int type;
mutable bool canceled;
int type;
mutable bool canceled;
};
struct MyEventPolicies
{
static int getEvent(const MyEvent & e) {
return e.type;
}
static int getEvent(const MyEvent & e) {
return e.type;
}
static bool canContinueInvoking(const MyEvent & e) {
return ! e.canceled;
}
static bool canContinueInvoking(const MyEvent & e) {
return ! e.canceled;
}
};
eventpp::EventDispatcher<int, void (const MyEvent &), MyEventPolicies> dispatcher;
dispatcher.appendListener(3, [](const MyEvent & e) {
std::cout << "Got event 3" << std::endl;
e.canceled = true;
std::cout << "Got event 3" << std::endl;
e.canceled = true;
});
dispatcher.appendListener(3, [](const MyEvent & e) {
std::cout << "Should not get this event 3" << std::endl;
std::cout << "Should not get this event 3" << std::endl;
});
dispatcher.dispatch(MyEvent(3));
@ -158,12 +158,12 @@ A typical `Threading` type looks like
```c++
struct MultipleThreading
{
using Mutex = std::mutex;
using Mutex = std::mutex;
template <typename T>
using Atomic = std::atomic<T>;
template <typename T>
using Atomic = std::atomic<T>;
using ConditionVariable = std::condition_variable;
using ConditionVariable = std::condition_variable;
};
```
For `SingleThreading`, all the types `Mutex`, `Atomic`, and `ConditionVariable` are dummy types that don't do anything.
@ -177,15 +177,15 @@ Below is the sample code for how to use `SpinLock`
```c++
struct MultipleThreadingSpinLock
{
using Mutex = eventpp::SpinLock;
using Mutex = eventpp::SpinLock;
template <typename T>
using Atomic = std::atomic<T>;
template <typename T>
using Atomic = std::atomic<T>;
using ConditionVariable = std::condition_variable;
using ConditionVariable = std::condition_variable;
};
struct MyEventPolicies {
using Threading = MultipleThreadingSpinLock;
using Threading = MultipleThreadingSpinLock;
};
eventpp::EventDispatcher<int, void (), MyEventPolicies> dispatcher;
eventpp::CallbackList<void (), MyEventPolicies> callbackList;
@ -194,18 +194,18 @@ eventpp::CallbackList<void (), MyEventPolicies> callbackList;
`eventpp` provides a shortcut template class to customize the threading.
```c++
template <
typename Mutex_,
template <typename > class Atomic_ = std::atomic,
typename ConditionVariable_ = std::condition_variable
typename Mutex_,
template <typename > class Atomic_ = std::atomic,
typename ConditionVariable_ = std::condition_variable
>
struct GeneralThreading
{
using Mutex = Mutex_;
using Mutex = Mutex_;
template <typename T>
using Atomic = Atomic_<T>;
template <typename T>
using Atomic = Atomic_<T>;
using ConditionVariable = ConditionVariable_;
using ConditionVariable = ConditionVariable_;
};
```
@ -213,7 +213,7 @@ So the previous sample code for spinlock can be rewritten as
```c++
struct MyEventPolicies {
using Threading = eventpp::GeneralThreading<eventpp::SpinLock>;
using Threading = eventpp::GeneralThreading<eventpp::SpinLock>;
};
eventpp::EventDispatcher<int, void (), MyEventPolicies> dispatcher;
eventpp::CallbackList<void (), MyEventPolicies> callbackList;
@ -270,12 +270,12 @@ Examples to demonstrate argument passing mode
```c++
struct MyPolicies
{
using ArgumentPassingMode = ArgumentPassingAutoDetect;
using ArgumentPassingMode = ArgumentPassingAutoDetect;
};
eventpp::EventDispatcher<
int,
void(int, const std::string &),
MyPolicies
int,
void(int, const std::string &),
MyPolicies
> dispatcher;
// or just
//eventpp::EventDispatcher<int, void(int, const std::string &)> dispatcher;
@ -286,12 +286,12 @@ dispatcher.dispatch(3, 8, "hello"); // Compile OK
```c++
struct MyPolicies
{
using ArgumentPassingMode = ArgumentPassingIncludeEvent;
using ArgumentPassingMode = ArgumentPassingIncludeEvent;
};
eventpp::EventDispatcher<
int,
void(int, const std::string &),
MyPolicies
int,
void(int, const std::string &),
MyPolicies
> dispatcher;
dispatcher.dispatch(3, "hello"); // Compile OK
//dispatcher.dispatch(3, 8, "hello"); // Compile failure
@ -300,12 +300,12 @@ dispatcher.dispatch(3, "hello"); // Compile OK
```c++
struct MyPolicies
{
using ArgumentPassingMode = ArgumentPassingExcludeEvent;
using ArgumentPassingMode = ArgumentPassingExcludeEvent;
};
eventpp::EventDispatcher<
int,
void(int, const std::string &),
MyPolicies
int,
void(int, const std::string &),
MyPolicies
> dispatcher;
//dispatcher.dispatch(3, "hello"); // Compile failure
dispatcher.dispatch(3, 8, "hello"); // Compile OK
@ -367,10 +367,10 @@ To use policies, declare a struct, define the policies in it, and pass the struc
```c++
struct MyPolicies //the struct name doesn't matter
{
template <typename ...Args>
static int getEvent(const MyEvent & e, const Args &...) {
return e.type;
}
template <typename ...Args>
static int getEvent(const MyEvent & e, const Args &...) {
return e.type;
}
};
EventDispatcher<int, void(const MyEvent &), MyPolicies> dispatcher;
```

View File

@ -58,24 +58,24 @@ void reset();
void setDispatcher(DispatcherType & dispatcher);
typename DispatcherType::Handle appendListener(
const typename DispatcherType::Event & event,
const typename DispatcherType::Callback & listener
);
const typename DispatcherType::Event & event,
const typename DispatcherType::Callback & listener
);
typename DispatcherType::Handle prependListener(
const typename DispatcherType::Event & event,
const typename DispatcherType::Callback & listener
);
const typename DispatcherType::Event & event,
const typename DispatcherType::Callback & listener
);
typename DispatcherType::Handle insertListener(
const typename DispatcherType::Event & event,
const typename DispatcherType::Callback & listener,
const typename DispatcherType::Handle & before
);
const typename DispatcherType::Event & event,
const typename DispatcherType::Callback & listener,
const typename DispatcherType::Handle & before
);
bool removeListener(
const typename DispatcherType::Event & event,
const typename DispatcherType::Handle handle
);
const typename DispatcherType::Event & event,
const typename DispatcherType::Handle handle
);
```
**Member functions for CallbackList**
@ -85,17 +85,17 @@ void reset();
void setCallbackList(CallbackListType & callbackList);
typename CallbackListType::Handle append(
const typename CallbackListType::Callback & callback
);
const typename CallbackListType::Callback & callback
);
typename CallbackListType::Handle prepend(
const typename CallbackListType::Callback & callback
);
const typename CallbackListType::Callback & callback
);
typename CallbackListType::Handle insert(
const typename CallbackListType::Callback & callback,
const typename CallbackListType::Handle & before
);
const typename CallbackListType::Callback & callback,
const typename CallbackListType::Handle & before
);
bool remove(const typename CallbackListType::Handle handle);
```
@ -119,39 +119,39 @@ using Remover = eventpp::ScopedRemover<eventpp::EventDispatcher<int, void ()> >;
constexpr int event = 3;
dispatcher.appendListener(event, [&dataList]() {
// listener A
// listener A
});
{
Remover r1(dispatcher);
r1.prependListener(event, [&dataList]() {
// listener B
});
{
Remover r2(dispatcher);
auto handle = r2.appendListener(event, [&dataList]() {
// listener C
});
{
Remover r3(dispatcher);
r3.insertListener(event, [&dataList]() {
// listener D
}, handle);
Remover r1(dispatcher);
r1.prependListener(event, [&dataList]() {
// listener B
});
{
Remover r2(dispatcher);
auto handle = r2.appendListener(event, [&dataList]() {
// listener C
});
{
Remover r3(dispatcher);
r3.insertListener(event, [&dataList]() {
// listener D
}, handle);
dispatcher.dispatch(event);
// All listeners were triggered.
}
dispatcher.dispatch(event);
// All listeners were triggered.
}
// listener D was removed
// listener D was removed
dispatcher.dispatch(event);
// Listeners A, B, C were triggered.
}
dispatcher.dispatch(event);
// Listeners A, B, C were triggered.
}
// listener C was removed
// listener C was removed
dispatcher.dispatch(event);
// Listeners A, B were triggered.
dispatcher.dispatch(event);
// Listeners A, B were triggered.
}
// listener B was removed
@ -173,12 +173,12 @@ SomeDispatcher someDispatcher;
class MyClass
{
MyClass()
{
someDispatcher.appendListener(SomeEvent, callback of myListener);
}
MyClass()
{
someDispatcher.appendListener(SomeEvent, callback of myListener);
}
void myListener() {}
void myListener() {}
};
```
@ -191,14 +191,14 @@ SomeDispatcher someDispatcher;
class MyClass
{
MyClass() : scopedRemover(someDispatcher)
{
scopedRemover.appendListener(SomeEvent, callback of myListener);
}
MyClass() : scopedRemover(someDispatcher)
{
scopedRemover.appendListener(SomeEvent, callback of myListener);
}
void myListener() {}
void myListener() {}
eventpp::ScopedRemover<SomeDispatcher> scopedRemover;
eventpp::ScopedRemover<SomeDispatcher> scopedRemover;
};
```

View File

@ -19,10 +19,10 @@ eventpp::CallbackList<void ()> callbackList;
// Lambda is not required, any function or std::function
// or whatever function object with the required prototype is fine.
callbackList.append([]() {
std::cout << "Got callback 1." << std::endl;
std::cout << "Got callback 1." << std::endl;
});
callbackList.append([]() {
std::cout << "Got callback 2." << std::endl;
std::cout << "Got callback 2." << std::endl;
});
// Invoke the callback list
@ -44,7 +44,7 @@ The *prototype* is C++ function type, such as `void (int)`, `void (const std::st
Now let's add a callback.
```c++
callbackList.append([]() {
std::cout << "Got callback 1." << std::endl;
std::cout << "Got callback 1." << std::endl;
});
```
Function `append` takes one arguments, the *callback*.
@ -65,12 +65,12 @@ During the invoking, all callbacks will be invoked one by one in the order of th
eventpp::CallbackList<void (const std::string &, const bool)> callbackList;
callbackList.append([](const std::string & s, const bool b) {
std::cout << std::boolalpha << "Got callback 1, s is " << s << " b is " << b << std::endl;
std::cout << std::boolalpha << "Got callback 1, s is " << s << " b is " << b << std::endl;
});
// The callback prototype doesn't need to be exactly same as the callback list.
// It would be fine as long as the arguments are compatible with the callbacklist.
callbackList.append([](std::string s, int b) {
std::cout << std::boolalpha << "Got callback 2, s is " << s << " b is " << b << std::endl;
std::cout << std::boolalpha << "Got callback 2, s is " << s << " b is " << b << std::endl;
});
// Invoke the callback list
@ -96,13 +96,13 @@ CL::Handle handle2;
// Add some callbacks.
callbackList.append([]() {
std::cout << "Got callback 1." << std::endl;
std::cout << "Got callback 1." << std::endl;
});
handle2 = callbackList.append([]() {
std::cout << "Got callback 2." << std::endl;
std::cout << "Got callback 2." << std::endl;
});
callbackList.append([]() {
std::cout << "Got callback 3." << std::endl;
std::cout << "Got callback 3." << std::endl;
});
callbackList.remove(handle2);
@ -127,30 +127,30 @@ CL callbackList;
// Add some callbacks.
callbackList.append([]() {
std::cout << "Got callback 1." << std::endl;
std::cout << "Got callback 1." << std::endl;
});
callbackList.append([]() {
std::cout << "Got callback 2." << std::endl;
std::cout << "Got callback 2." << std::endl;
});
callbackList.append([]() {
std::cout << "Got callback 3." << std::endl;
std::cout << "Got callback 3." << std::endl;
});
// Now call forEach to remove the second callback
// The forEach callback prototype is void(const CallbackList::Handle & handle, const CallbackList::Callback & callback)
int index = 0;
callbackList.forEach([&callbackList, &index](const CL::Handle & handle, const CL::Callback & callback) {
std::cout << "forEach(Handle, Callback), invoked " << index << std::endl;
if(index == 1) {
callbackList.remove(handle);
std::cout << "forEach(Handle, Callback), removed second callback" << std::endl;
}
++index;
std::cout << "forEach(Handle, Callback), invoked " << index << std::endl;
if(index == 1) {
callbackList.remove(handle);
std::cout << "forEach(Handle, Callback), removed second callback" << std::endl;
}
++index;
});
// The forEach callback prototype can also be void(const CallbackList::Callback & callback)
callbackList.forEach([&callbackList, &index](const CL::Callback & callback) {
std::cout << "forEach(Callback), invoked" << std::endl;
std::cout << "forEach(Callback), invoked" << std::endl;
});
// Invoke the callback list

View File

@ -22,13 +22,13 @@ eventpp::EventDispatcher<int, void ()> dispatcher;
// Lambda is not required, any function or std::function
// or whatever function object with the required prototype is fine.
dispatcher.appendListener(3, []() {
std::cout << "Got event 3." << std::endl;
std::cout << "Got event 3." << std::endl;
});
dispatcher.appendListener(5, []() {
std::cout << "Got event 5." << std::endl;
std::cout << "Got event 5." << std::endl;
});
dispatcher.appendListener(5, []() {
std::cout << "Got another event 5." << std::endl;
std::cout << "Got another event 5." << std::endl;
});
// Dispatch the events, the first argument is always the event type.
@ -53,7 +53,7 @@ The *prototype* is C++ function type, such as `void (int)`, `void (const std::st
Now let's add a listener.
```c++
dispatcher.appendListener(3, []() {
std::cout << "Got event 3." << std::endl;
std::cout << "Got event 3." << std::endl;
});
```
Function `appendListener` takes at least two arguments. The first argument is the *event* of type *event type*, here is `int`. The second is the *callback*.
@ -76,15 +76,15 @@ During the dispatching, all listeners of that event will be invoked one by one i
eventpp::EventDispatcher<int, void (const std::string &, const bool)> dispatcher;
dispatcher.appendListener(3, [](const std::string & s, const bool b) {
std::cout << std::boolalpha << "Got event 3, s is " << s << " b is " << b << std::endl;
std::cout << std::boolalpha << "Got event 3, s is " << s << " b is " << b << std::endl;
});
// The listener prototype doesn't need to be exactly same as the dispatcher.
// It would be find as long as the arguments is compatible with the dispatcher.
dispatcher.appendListener(5, [](std::string s, int b) {
std::cout << std::boolalpha << "Got event 5, s is " << s << " b is " << b << std::endl;
std::cout << std::boolalpha << "Got event 5, s is " << s << " b is " << b << std::endl;
});
dispatcher.appendListener(5, [](const std::string & s, const bool b) {
std::cout << std::boolalpha << "Got another event 5, s is " << s << " b is " << b << std::endl;
std::cout << std::boolalpha << "Got another event 5, s is " << s << " b is " << b << std::endl;
});
// Dispatch the events, the first argument is always the event type.
@ -107,39 +107,39 @@ The listener's prototype is not required to be same as the dispatcher, it's fine
```c++
// Define an Event to hold all parameters.
struct MyEvent {
int type;
std::string message;
int param;
int type;
std::string message;
int param;
};
// Define policies to let the dispatcher knows how to
// extract the event type.
struct MyEventPolicies
{
static int getEvent(const MyEvent & e, bool /*b*/) {
return e.type;
}
static int getEvent(const MyEvent & e, bool /*b*/) {
return e.type;
}
};
// Pass MyEventPolicies as the third template argument of EventDispatcher.
// Note: the first template argument is the event type type int, not MyEvent.
eventpp::EventDispatcher<
int,
void (const MyEvent &, bool),
MyEventPolicies
int,
void (const MyEvent &, bool),
MyEventPolicies
> dispatcher;
// Add a listener.
// Note: the first argument is the event type of type int, not MyEvent.
dispatcher.appendListener(3, [](const MyEvent & e, bool b) {
std::cout
<< std::boolalpha
<< "Got event 3" << std::endl
<< "Event::type is " << e.type << std::endl
<< "Event::message is " << e.message << std::endl
<< "Event::param is " << e.param << std::endl
<< "b is " << b << std::endl
;
std::cout
<< std::boolalpha
<< "Got event 3" << std::endl
<< "Event::type is " << e.type << std::endl
<< "Event::message is " << e.message << std::endl
<< "Event::param is " << e.param << std::endl
<< "b is " << b << std::endl
;
});
// Dispatch the event.

View File

@ -13,15 +13,15 @@ Note if you are going to try the tutorial code, you'd better test the code under
eventpp::EventQueue<int, void (const std::string &, std::unique_ptr<int> &)> queue;
queue.appendListener(3, [](const std::string & s, std::unique_ptr<int> & n) {
std::cout << "Got event 3, s is " << s << " n is " << *n << std::endl;
std::cout << "Got event 3, s is " << s << " n is " << *n << std::endl;
});
// The listener prototype doesn't need to be exactly same as the dispatcher.
// It would be find as long as the arguments is compatible with the dispatcher.
queue.appendListener(5, [](std::string s, const std::unique_ptr<int> & n) {
std::cout << "Got event 5, s is " << s << " n is " << *n << std::endl;
std::cout << "Got event 5, s is " << s << " n is " << *n << std::endl;
});
queue.appendListener(5, [](const std::string & s, std::unique_ptr<int> & n) {
std::cout << "Got another event 5, s is " << s << " n is " << *n << std::endl;
std::cout << "Got another event 5, s is " << s << " n is " << *n << std::endl;
});
// Enqueue the events, the first argument is always the event type.
@ -59,19 +59,19 @@ constexpr int otherEvent = 2;
// Start a thread to process the event queue.
// All listeners are invoked in that thread.
std::thread thread([stopEvent, otherEvent, &queue]() {
volatile bool shouldStop = false;
queue.appendListener(stopEvent, [&shouldStop](int) {
shouldStop = true;
});
queue.appendListener(otherEvent, [](const int index) {
std::cout << "Got event, index is " << index << std::endl;
});
volatile bool shouldStop = false;
queue.appendListener(stopEvent, [&shouldStop](int) {
shouldStop = true;
});
queue.appendListener(otherEvent, [](const int index) {
std::cout << "Got event, index is " << index << std::endl;
});
while(! shouldStop) {
queue.wait();
while(! shouldStop) {
queue.wait();
queue.process();
}
queue.process();
}
});
// Enqueue an event from the main thread. After sleeping for 10 milliseconds,
@ -85,20 +85,20 @@ std::this_thread::sleep_for(std::chrono::milliseconds(10));
std::cout << "Should have triggered event with index = 2" << std::endl;
{
// EventQueue::DisableQueueNotify is a RAII class that
// disables waking up any waiting threads.
// So no events should be triggered in this code block.
// DisableQueueNotify is useful when adding lots of events at the same time
// and only want to wake up the waiting threads after all events are added.
EQ::DisableQueueNotify disableNotify(&queue);
// EventQueue::DisableQueueNotify is a RAII class that
// disables waking up any waiting threads.
// So no events should be triggered in this code block.
// DisableQueueNotify is useful when adding lots of events at the same time
// and only want to wake up the waiting threads after all events are added.
EQ::DisableQueueNotify disableNotify(&queue);
queue.enqueue(otherEvent, 10);
std::this_thread::sleep_for(std::chrono::milliseconds(10));
std::cout << "Should NOT trigger event with index = 10" << std::endl;
queue.enqueue(otherEvent, 10);
std::this_thread::sleep_for(std::chrono::milliseconds(10));
std::cout << "Should NOT trigger event with index = 10" << std::endl;
queue.enqueue(otherEvent, 11);
std::this_thread::sleep_for(std::chrono::milliseconds(10));
std::cout << "Should NOT trigger event with index = 11" << std::endl;
queue.enqueue(otherEvent, 11);
std::this_thread::sleep_for(std::chrono::milliseconds(10));
std::cout << "Should NOT trigger event with index = 11" << std::endl;
}
// The DisableQueueNotify object is destroyed here, and has resumed
// waking up waiting threads. So the events should be triggered.

View File

@ -52,8 +52,8 @@ In brief, MSVC, GCC, Clang that has well support for C++11, or released after 20
* The library: C++11.
* To develop the library
* Unit tests: C++17.
* Tutorials: C++11.
* Benchmakrs: C++11.
* Tutorials: C++11.
* Benchmakrs: C++11.
## Quick start
@ -101,10 +101,10 @@ Note: the method 2 doesn't work well with MingW on Windows. It works well on Lin
#include "eventpp/callbacklist.h"
eventpp::CallbackList<void (const std::string &, const bool)> callbackList;
callbackList.append([](const std::string & s, const bool b) {
std::cout << std::boolalpha << "Got callback 1, s is " << s << " b is " << b << std::endl;
std::cout << std::boolalpha << "Got callback 1, s is " << s << " b is " << b << std::endl;
});
callbackList.append([](std::string s, int b) {
std::cout << std::boolalpha << "Got callback 2, s is " << s << " b is " << b << std::endl;
std::cout << std::boolalpha << "Got callback 2, s is " << s << " b is " << b << std::endl;
});
callbackList("Hello world", true);
```
@ -114,13 +114,13 @@ callbackList("Hello world", true);
#include "eventpp/eventdispatcher.h"
eventpp::EventDispatcher<int, void ()> dispatcher;
dispatcher.appendListener(3, []() {
std::cout << "Got event 3." << std::endl;
std::cout << "Got event 3." << std::endl;
});
dispatcher.appendListener(5, []() {
std::cout << "Got event 5." << std::endl;
std::cout << "Got event 5." << std::endl;
});
dispatcher.appendListener(5, []() {
std::cout << "Got another event 5." << std::endl;
std::cout << "Got another event 5." << std::endl;
});
// dispatch event 3
dispatcher.dispatch(3);
@ -133,10 +133,10 @@ dispatcher.dispatch(5);
eventpp::EventQueue<int, void (const std::string &, const bool)> queue;
queue.appendListener(3, [](const std::string s, bool b) {
std::cout << std::boolalpha << "Got event 3, s is " << s << " b is " << b << std::endl;
std::cout << std::boolalpha << "Got event 3, s is " << s << " b is " << b << std::endl;
});
queue.appendListener(5, [](const std::string s, bool b) {
std::cout << std::boolalpha << "Got event 5, s is " << s << " b is " << b << std::endl;
std::cout << std::boolalpha << "Got event 5, s is " << s << " b is " << b << std::endl;
});
// The listeners are not triggered during enqueue.