diff --git a/doc/callbacklist.md b/doc/callbacklist.md
index 35a7f8c..f0ae7b9 100644
--- a/doc/callbacklist.md
+++ b/doc/callbacklist.md
@@ -1,26 +1,37 @@
# Class CallbackList reference
+
## Table Of Contents
-- [API reference](#apis)
-- [Nested callback safety](#nested-callback-safety)
-- [Time complexities](#time-complexities)
-- [Internal data structure](#internal-data-structure)
-
+* [Description](#a2_1)
+* [API reference](#a2_2)
+ * [Header](#a3_1)
+ * [Template parameters](#a3_2)
+ * [Public types](#a3_3)
+ * [Member functions](#a3_4)
+* [Nested callback safety](#a2_3)
+* [Time complexities](#a2_4)
+* [Internal data structure](#a2_5)
+
+
+
## Description
CallbackList is the fundamental class in eventpp. The other classes EventDispatcher and EventQueue are built on CallbackList.
-CallbackList holds a list of callbacks. On invocation, CallbackList simply invokes each callbacks one by one. Think CallbackList as the signal/slot system in Qt, or the callback function pointer in some Windows APIs (such as lpCompletionRoutine in `ReadFileEx`).
+CallbackList holds a list of callbacks. At the time of the call, CallbackList simply invokes each callback one by one. Consider CallbackList as the signal/slot system in Qt, or the callback function pointer in some Windows APIs (such as lpCompletionRoutine in `ReadFileEx`).
+The *callback* can be any callback target -- functions, pointers to functions, , pointers to member functions, lambda expressions, and function objects.
-
+
## API reference
+
### Header
eventpp/callbacklist.h
-**Template parameters**
+
+### Template parameters
```c++
template <
@@ -32,13 +43,17 @@ class CallbackList;
`Prototype`: the callback prototype. It's C++ function type such as `void(int, std::string, const MyClass *)`.
`Policies`: the policies to configure and extend the callback list. The default value is `DefaultPolicies`. See [document of policies](policies.md) for details.
+
### Public types
-`Handle`: the handle type returned by appendListener, prependListener and insertListener. A handle can be used to insert a callback or remove a callback. To check if a `Handle` is empty, convert it to boolean, *false* is empty. `Handle` is copyable.
+`Handle`: the handle type returned by append, prepend and insert. A handle can be used to insert a callback or remove a callback. To check if a `Handle` is empty, convert it to boolean, *false* is empty. `Handle` is copyable.
`Callback`: the callback storage type.
+
### Member functions
+#### constructors
+
```c++
CallbackList() noexcept;
CallbackList(const CallbackList & other);
@@ -49,11 +64,15 @@ CallbackList & operator = (CallbackList && other) noexcept;
CallbackList can be copied, moved, assigned, and move assigned.
+#### empty
+
```c++
bool empty() const;
```
Return true if the callback list is empty.
-Note: in multi threading, this function returning true doesn't guarantee the list is empty. The list may become non-empty immediately after the function returns true.
+Note: in multi threading, this function returning true doesn't guarantee that the list is empty. The list may immediately become non-empty after the function returns true.
+
+#### bool casting operator
```c++
operator bool() const;
@@ -61,32 +80,39 @@ operator bool() const;
Return true if the callback list is not empty.
This operator allows a CallbackList instance be used in condition statement.
+#### append
+
```c++
Handle append(const Callback & callback);
```
Add the *callback* to the callback list.
The callback is added to the end of the callback list.
-Return a handle which represents the callback. The handle can be used to remove this callback or insert other callback before this callback.
-If `append` is called in another callback during the invoking of the callback list, the new callback is guaranteed not triggered during the same callback list invoking.
+Return a handle that represents the callback. The handle can be used to remove this callback or to insert additional callbacks before this callback.
+If `append` is called in another callback during the invoking of the callback list, the new callback is guaranteed not to be triggered during the same callback list invoking.
The time complexity is O(1).
+#### prepend
+
```c++
Handle prepend(const Callback & callback);
```
Add the *callback* to the callback list.
The callback is added to the beginning of the callback list.
-Return a handle which represents the callback. The handle can be used to remove this callback or insert other callback before this callback.
-If `prepend` is called in another callback during the invoking of the callback list, the new callback is guaranteed not triggered during the same callback list invoking.
+Return a handle that represents the callback. The handle can be used to remove this callback or to insert additional callbacks before this callback.
+If `prepend` is called in another callback during the invoking of the callback list, the new callback is guaranteed not to be triggered during the same callback list invoking.
The time complexity is O(1).
+#### insert
+
```c++
Handle insert(const Callback & callback, const Handle before);
```
Insert the *callback* to the callback list before the callback handle *before*. If *before* is not found, *callback* is added at the end of the callback list.
-Return a handle which represents the callback. The handle can be used to remove this callback or insert other callback before this callback.
-If `insert` is called in another callback during the invoking of the callback list, the new callback is guaranteed not triggered during the same callback list invoking.
+Return a handle that represents the callback. The handle can be used to remove this callback or to insert additional callbacks before this callback.
+If `insert` is called in another callback during the invoking of the callback list, the new callback is guaranteed not to be triggered during the same callback list invoking.
The time complexity is O(1).
+#### remove
```c++
bool remove(const Handle handle);
```
@@ -94,49 +120,54 @@ Remove the callback *handle* from the callback list.
Return true if the callback is removed successfully, false if the callback is not found.
The time complexity is O(1).
+#### forEach
+
```c++
template
-void forEach(Func && func);
+void forEach(Func && func) const;
```
Apply `func` to all callbacks.
-The `func` can be one of the three prototypes:
+The `func` can be one of the two prototypes:
```c++
-AnyReturnType func(const EventDispatcher::Handle &, const EventDispatcher::Callback &);
-AnyReturnType func(const EventDispatcher::Handle &);
-AnyReturnType func(const EventDispatcher::Callback &);
+AnyReturnType func(const CallbackList::Handle &, const CallbackList::Callback &);
+AnyReturnType func(const CallbackList::Callback &);
```
**Note**: the `func` can remove any callbacks, or add other callbacks, safely.
+#### forEachIf
+
```c++
template
-bool forEachIf(Func && func);
+bool forEachIf(Func && func) const;
```
Apply `func` to all callbacks. `func` must return a boolean value, and if the return value is false, forEachIf stops the looping immediately.
Return `true` if all callbacks are invoked, or `event` is not found, `false` if `func` returns `false`.
+#### invoking operator
+
```c++
-void operator() (Args ...args);
+void operator() (Args ...args) const;
```
Invoke each callbacks in the callback list.
The callbacks are called with arguments `args`.
The callbacks are called in the thread same as the callee of `operator()`.
-
+
## Nested callback safety
1. If a callback adds another callback to the callback list during a invoking, the new callback is guaranteed not to be triggered within the same invoking. This is guaranteed by an unsigned 64 bits integer counter. This rule will be broken is the counter is overflowed to zero in a invoking, but this rule will continue working on the subsequence invoking.
2. Any callbacks that are removed during a invoking are guaranteed not triggered.
3. All above points are not true in multiple threading. That's to say, if one thread is invoking a callback list, the other thread add or remove a callback, the added or removed callback may be called during the invoking.
-
+
## Time complexities
- `append`: O(1)
- `prepend`: O(1)
- `insert`: O(1)
- `remove`: O(1)
-
+
## Internal data structure
CallbackList uses doubly linked list to manage the callbacks.
-Each node is linked by shared pointer. Using shared pointer allows the node be removed while iterating.
+Each node is linked by a shared pointer. Using shared pointer allows nodes to be removed during iterating.
diff --git a/doc/conditionalremover.md b/doc/conditionalremover.md
index 8d6d0f3..1115b12 100644
--- a/doc/conditionalremover.md
+++ b/doc/conditionalremover.md
@@ -1,17 +1,32 @@
# Class ConditionalRemover reference
+
+## Table Of Contents
+
+* [Description](#a2_1)
+* [API reference](#a2_2)
+ * [Header](#a3_1)
+ * [Template parameters](#a3_2)
+ * [Member functions](#a3_3)
+ * [Free functions](#a3_4)
+ * [Sample code](#a3_5)
+
+
+
## Description
ConditionalRemover is a utility class that automatically removes listeners after the listeners are triggered and certain condition is satisfied.
ConditionalRemover is a pure functional class. After the member functions in ConditionalRemover are invoked, the ConditionalRemover object can be destroyed safely.
-
+
## API reference
+
### Header
eventpp/utilities/conditionalremover.h
+
### Template parameters
```c++
@@ -19,8 +34,9 @@ template
class ConditionalRemover;
```
-`DispatcherType` can be CallbackList, EventDispatcher, or EventQueue.
+`DispatcherType` can be CallbackList, EventDispatcher, EventQueue, HeterCallbackList, HeterEventDispatcher, or HeterEventQueue.
+
### Member functions
```c++
@@ -78,6 +94,7 @@ typename CallbackListType::Handle insert(
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 of prototype `bool()`. It's invoked after each trigger, if it returns true, the listener will be removed.
+
### Free functions
```c++
@@ -87,6 +104,7 @@ ConditionalRemover conditionalRemover(DispatcherType & dispatche
Since ConditionalRemover takes one template parameter and it's verbose to instantiate its instance, the function `conditionalRemover` is used to construct an instance of ConditionalRemover via the deduced argument.
+
### Sample code
```c++
diff --git a/doc/counterremover.md b/doc/counterremover.md
index 344116b..f070fb4 100644
--- a/doc/counterremover.md
+++ b/doc/counterremover.md
@@ -1,17 +1,32 @@
# Class CounterRemover reference
+
+## Table Of Contents
+
+* [Description](#a2_1)
+* [API reference](#a2_2)
+ * [Header](#a3_1)
+ * [Template parameters](#a3_2)
+ * [Member functions](#a3_3)
+ * [Free functions](#a3_4)
+ * [Sample code](#a3_5)
+
+
+
## Description
CounterRemover is a utility class that automatically removes listeners after the listeners are triggered for certain times.
CounterRemover is a pure functional class. After the member functions in CounterRemover are invoked, the CounterRemover object can be destroyed safely.
-
+
## API reference
+
### Header
eventpp/utilities/counterremover.h
+
### Template parameters
```c++
@@ -19,8 +34,9 @@ template
class CounterRemover;
```
-`DispatcherType` can be CallbackList, EventDispatcher, or EventQueue.
+`DispatcherType` can be CallbackList, EventDispatcher, EventQueue, HeterCallbackList, HeterEventDispatcher, or HeterEventQueue.
+
### Member functions
```c++
@@ -73,6 +89,7 @@ typename CallbackListType::Handle insert(
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.
The default value of `triggerCount` is 1, that means the listener is removed after the first trigger, which is one shot listener.
+
### Free functions
```c++
@@ -82,6 +99,7 @@ CounterRemover counterRemover(DispatcherType & dispatcher);
Since CounterRemover takes one template parameter and it's verbose to instantiate its instance, the function `counterRemover` is used to construct an instance of CounterRemover via the deduced argument.
+
### Sample code
```c++
diff --git a/doc/eventdispatcher.md b/doc/eventdispatcher.md
index 84e997a..c90e418 100644
--- a/doc/eventdispatcher.md
+++ b/doc/eventdispatcher.md
@@ -1,26 +1,36 @@
# Class EventDispatcher reference
+
## Table Of Contents
-- [API reference](#apis)
-- [Nested listener safety](#nested-listener-safety)
-- [Time complexities](#time-complexities)
-- [Internal data structure](#internal-data-structure)
-
+* [Description](#a2_1)
+* [API reference](#a2_2)
+ * [Header](#a3_1)
+ * [Template parameters](#a3_2)
+ * [Public types](#a3_3)
+ * [Member functions](#a3_4)
+* [Nested listener safety](#a2_3)
+* [Time complexities](#a2_4)
+* [Internal data structure](#a2_5)
+
+
+
## Description
EventDispatcher is something like std::map.
EventDispatcher holds a map of `` pairs. On dispatching, EventDispatcher finds the CallbackList of the event type, then invoke the callback list. The invocation is always synchronous. The listeners are triggered when `EventDispatcher::dispatch` is called.
-
+
## API reference
+
### Header
eventpp/eventdispatcher.h
-**Template parameters**
+
+### Template parameters
```c++
template <
@@ -34,14 +44,18 @@ class EventDispatcher;
`Prototype`: the listener prototype. It's C++ function type such as `void(int, std::string, const MyClass *)`.
`Policies`: the policies to configure and extend the dispatcher. The default value is `DefaultPolicies`. See [document of policies](policies.md) for details.
+
### Public types
`Handle`: the handle type returned by appendListener, prependListener and insertListener. A handle can be used to insert a listener or remove a listener. To check if a `Handle` is empty, convert it to boolean, *false* is empty. `Handle` is copyable.
`Callback`: the callback storage type.
`Event`: the event type.
+
### Member functions
+#### constructors
+
```c++
EventDispatcher();
EventDispatcher(const EventDispatcher & other);
@@ -52,6 +66,8 @@ EventDispatcher & operator = (EventDispatcher && other) noexcept;
EventDispatcher can be copied, moved, assigned, and move assigned.
+#### appendListener
+
```c++
Handle appendListener(const Event & event, const Callback & callback);
```
@@ -62,6 +78,8 @@ If `appendListener` is called in another listener during a dispatching, the new
If the same callback is added twice, it results duplicated listeners.
The time complexity is O(1).
+#### prependListener
+
```c++
Handle prependListener(const Event & event, const Callback & callback);
```
@@ -71,6 +89,8 @@ Return a handle which represents the listener. The handle can be used to remove
If `prependListener` is called in another listener during a dispatching, the new listener is guaranteed not triggered during the same dispatching.
The time complexity is O(1).
+#### insertListener
+
```c++
Handle insertListener(const Event & event, const Callback & callback, const Handle before);
```
@@ -79,6 +99,8 @@ Return a handle which represents the listener. The handle can be used to remove
If `insertListener` is called in another listener during a dispatching, the new listener is guaranteed not triggered during the same dispatching.
The time complexity is O(1).
+#### removeListener
+
```c++
bool removeListener(const Event & event, const Handle handle);
```
@@ -86,29 +108,22 @@ Remove the listener *handle* which listens to *event* from the dispatcher.
Return true if the listener is removed successfully, false if the listener is not found.
The time complexity is O(1).
-```c++
-void dispatch(Args ...args);
-
-template
-void dispatch(T && first, Args ...args);
-```
-Dispatch an event. The event type is deducted from the arguments of `dispatch`.
-In both overloads, the listeners are called with arguments `args`.
-The function is synchronous. The listeners are called in the thread same as the caller of `dispatch`.
+#### forEach
```c++
template
void forEach(const Event & event, Func && func);
```
Apply `func` to all listeners of `event`.
-The `func` can be one of the three prototypes:
+The `func` can be one of the two prototypes:
```c++
AnyReturnType func(const EventDispatcher::Handle &, const EventDispatcher::Callback &);
-AnyReturnType func(const EventDispatcher::Handle &);
AnyReturnType func(const EventDispatcher::Callback &);
```
**Note**: the `func` can remove any listeners, or add other listeners, safely.
+#### forEachIf
+
```c++
template
bool forEachIf(const Event & event, Func && func);
@@ -116,13 +131,25 @@ bool forEachIf(const Event & event, Func && func);
Apply `func` to all listeners of `event`. `func` must return a boolean value, and if the return value is false, forEachIf stops the looping immediately.
Return `true` if all listeners are invoked, or `event` is not found, `false` if `func` returns `false`.
-
+#### dispatch
+
+```c++
+void dispatch(Args ...args);
+
+template
+void dispatch(T && first, Args ...args);
+```
+Dispatch an event. The event type is deducted from the arguments of `dispatch`.
+In both overloads, the listeners are called with arguments `args`.
+The function is synchronous. The listeners are called in the thread same as the caller of `dispatch`.
+
+
## Nested listener safety
1. If a listener adds another listener of the same event to the dispatcher during a dispatching, the new listener is guaranteed not to be triggered within the same dispatching. This is guaranteed by an unsigned 64 bits integer counter. This rule will be broken is the counter is overflowed to zero in a dispatching, but this rule will continue working on the subsequence dispatching.
2. Any listeners that are removed during a dispatching are guaranteed not triggered.
3. All above points are not true in multiple threading. That's to say, if one thread is invoking a callback list, the other thread add or remove a callback, the added or removed callback may be triggered during the invoking.
-
+
## Time complexities
The time complexities being discussed here is about when operating on the listener in the underlying list, and `n` is the number of listeners. It doesn't include the event searching in the underlying `std::map` which is always O(log n).
- `appendListener`: O(1)
@@ -131,7 +158,7 @@ The time complexities being discussed here is about when operating on the listen
- `removeListener`: O(1)
- `enqueue`: O(1)
-
+
## Internal data structure
EventDispatcher uses [CallbackList](callbacklist.md) to manage the listener callbacks.
diff --git a/doc/eventqueue.md b/doc/eventqueue.md
index 3824b64..ddae8d0 100644
--- a/doc/eventqueue.md
+++ b/doc/eventqueue.md
@@ -1,30 +1,39 @@
# Class EventQueue reference
+
## Table Of Contents
-- [API reference](#apis)
-- [Internal data structure](#internal-data-structure)
-
-EventQueue includes all functions of [EventDispatcher](eventdispatcher.md) and adds event queue features. Note: EventQueue doesn't inherit from EventDispatcher, don't try to cast EventQueue to EventDispatcher.
-
+* [Description](#a2_1)
+* [API reference](#a2_2)
+ * [Header](#a3_1)
+ * [Template parameters](#a3_2)
+ * [Public types](#a3_3)
+ * [Member functions](#a3_4)
+ * [Inner class EventQueue::DisableQueueNotify](#a3_5)
+* [Internal data structure](#a2_3)
+
+
+
## Description
EventQueue includes all features of EventDispatcher and adds event queue features. Note: EventQueue doesn't inherit from EventDispatcher, don't try to cast EventQueue to EventDispatcher.
-EventQueue is asynchronous. Event are cached in the queue when `EventQueue::enqueue` is called, and dispatched later when `EventQueue::process` is called.
-EventQueue is equivalent to the event system (QEvent) in Qt, or the message processing in Windows.
+EventQueue is asynchronous. Events are cached in the queue when `EventQueue::enqueue` is called, and dispatched later when `EventQueue::process` is called.
+EventQueue is equivalent to the event system (QEvent) in Qt, or the message processing in Windows API.
-
+
## API reference
+
### Header
eventpp/eventqueue.h
-**Template parameters**
+
+### Template parameters
```c++
template <
- typename Key,
+ typename Event,
typename Prototype,
typename Policies = DefaultPolicies
>
@@ -33,19 +42,24 @@ class EventQueue;
EventQueue has the exactly same template parameters with EventDispatcher. Please reference [EventDispatcher document](eventdispatcher.md) for details.
+
### Public types
-`QueuedEvent`: the data type of event stored in the queue. It's declaration is,
+`QueuedEvent`: the data type of event stored in the queue. It's declaration in pseudo code is,
```c++
-using QueuedEvent = std::tuple<
- typename std::remove_cv::type>::type,
- typename std::remove_cv::type>::type...
->;
+struct EventQueue::QueuedEvent
+{
+ TheEventType event;
+ std::tuple arguments;
+};
```
-It's a `std::tuple`, the first member is always the event type, the other members are the arguments.
+`event` is the EventQueue::Event, `arguments` are the arguments passed in `enqueue`.
+
### Member functions
+#### constructors
+
```c++
EventQueue();
EventQueue(const EventQueue & other);
@@ -57,6 +71,8 @@ EventQueue & operator = (EventQueue && other) noexcept;
EventDispatcher can be copied, moved, assigned, and move assigned.
Note: the queued events are not copied, moved, assigned, or move assigned, only the listeners are performed these operations.
+#### enqueue
+
```c++
template
void enqueue(A ...args);
@@ -72,6 +88,8 @@ If an argument is a pointer, only the pointer will be stored. The object it poin
`enqueue` wakes up any threads that are blocked by `wait` or `waitFor`.
The time complexity is O(1).
+#### process
+
```c++
bool process();
```
@@ -82,6 +100,8 @@ Any new events added to the queue during `process()` are not dispatched during c
`process()` is efficient in single thread event processing, it processes all events in the queue in current thread. To process events from multiple threads efficiently, use `processOne()`.
Note: if `process()` is called from multiple threads simultaneously, the events in the event queue are guaranteed dispatched only once.
+#### processOne
+
```c++
bool processOne();
```
@@ -92,6 +112,8 @@ Any new events added to the queue during `processOne()` are not dispatched durin
If there are multiple threads processing events, `processOne()` is more efficient than `process()` because it can split the events processing to different threads. However, if there is only one thread processing events, 'process()' is more efficient.
Note: if `processOne()` is called from multiple threads simultaneously, the events in the event queue are guaranteed dispatched only once.
+#### processIf
+
```c++
template
bool processIf(F && func);
@@ -103,12 +125,16 @@ Process the event queue. Before processing an event, the event is passed to `fun
1. Process certain events in certain thread. For example, in a GUI application, the UI related events may be only desired to processed in the main thread.
2. Process the events until certain time. For example, in a game engine, the event process may be limited to only several milliseconds, the remaining events will be process in next game loop.
+#### emptyQueue
+
```c++
-bool empty() const;
+bool emptyQueue() const;
```
Return true if there is no any event in the event queue, false if there are any events in the event queue.
Note: in multiple threading environment, the empty state may change immediately after the function returns.
-Note: don't write loop as `while(! eventQueue.empty()) {}`. It's dead loop since the compiler will inline the code and the change of empty state is never seen by the loop. The safe approach is `while(eventQueue.waitFor(std::chrono::nanoseconds(0))) ;`.
+Note: don't write loop as `while(! eventQueue.emptyQueue()) {}`. It's dead loop since the compiler will inline the code and the change of empty state is never seen by the loop. The safe approach is `while(eventQueue.waitFor(std::chrono::nanoseconds(0))) ;`.
+
+#### clearEvents
```c++
void clearEvents();
@@ -116,6 +142,8 @@ void clearEvents();
Clear all queued events without dispatching them.
This is useful to clear any references such as shared pointer in the queued events to avoid cyclic reference.
+#### wait
+
```c++
void wait() const;
```
@@ -130,6 +158,8 @@ for(;;) {
```
The code works event if it doesn't `wait`, but doing that will waste CPU power resource.
+#### waitFor
+
```c++
template
bool waitFor(const std::chrono::duration & duration) const;
@@ -149,15 +179,26 @@ for(;;) {
}
```
+#### peekEvent
+
```c++
bool peekEvent(EventQueue::QueuedEvent * queuedEvent);
```
Retrieve an event from the queue. The event is returned in `queuedEvent`.
-`queuedEvent` is a std::tuple, which the first element is the EventQueue::Event, and the other elements are the arguments passed in `enqueue`.
+```c++
+struct EventQueue::QueuedEvent
+{
+ TheEventType event;
+ std::tuple arguments;
+};
+```
+`queuedEvent` is a EventQueue::QueuedEvent struct. `event` is the EventQueue::Event, `arguments` are the arguments passed in `enqueue`.
If the queue is empty, the function returns false, otherwise true if an event is retrieved successfully.
After the function returns, the original even is still in the queue.
Note: `peekEvent` doesn't work with any non-copyable event arguments. If `peekEvent` is called when any arguments are non-copyable, compile fails.
+#### takeEvent
+
```c++
bool takeEvent(EventQueue::QueuedEvent * queuedEvent);
```
@@ -166,18 +207,22 @@ If the queue is empty, the function returns false, otherwise true if an event is
After the function returns, the original even is removed from the queue.
Note: `takeEvent` works with non-copyable event arguments.
+#### dispatch
+
```c++
void dispatch(const QueuedEvent & queuedEvent);
```
Dispatch an event which was returned by `peekEvent` or `takeEvent`.
-**Inner class EventQueue::DisableQueueNotify**
+
+### Inner class EventQueue::DisableQueueNotify
`EventQueue::DisableQueueNotify` is a RAII class that temporarily prevents the event queue from waking up any waiting threads. When any `DisableQueueNotify` object exist, calling `enqueue` doesn't wake up any threads that are blocked by `wait`. When the `DisableQueueNotify` object is out of scope, the waking up is resumed. If there are more than one `DisableQueueNotify` objects, the waking up is only resumed after all `DisableQueueNotify` objects are destroyed.
+`DisableQueueNotify` is useful to improve performance when batching adding events to the queue. For example, in a main loop of a game engine, `DisableQueueNotify` can be created on the start in a frame, then the game adding events to the queue, and the `DisableQueueNotify` is destroyed at the end of a frame and the events are processed.
To use `DisableQueueNotify`, construct it with a pointer to event queue.
-Sampe code
+Sample code
```c++
using EQ = eventpp::EventQueue;
EQ queue;
@@ -193,11 +238,10 @@ EQ queue;
queue.enqueue(3);
```
-
+
## Internal data structure
EventQueue uses three `std::list` to manage the event queue.
-The first busy list holds all nodes with queued events.
+The first busy list holds all nodes of queued events.
The second idle list holds all idle nodes. After an event is dispatched and removed from the queue, instead of freeing the memory, EventQueue moves the unused node to the idle list. This can improve performance and avoid memory fragment.
The third list is a local temporary list used in function `process()`. During processing, the busy list is swapped to the temporary list, all events are dispatched from the temporary list, then the temporary list is returned and appended to the idle list.
-
diff --git a/doc/eventutil.md b/doc/eventutil.md
index 6c67453..7f97058 100644
--- a/doc/eventutil.md
+++ b/doc/eventutil.md
@@ -1,6 +1,6 @@
# Utilities reference
-**Header**
+## Header
eventpp/eventutil.h
diff --git a/doc/faq.md b/doc/faq.md
index 10d1c5d..7d0afa2 100644
--- a/doc/faq.md
+++ b/doc/faq.md
@@ -1,5 +1,20 @@
# Frequently Asked Questions
+
+## Table Of Contents
+
+* [Why can't rvalue reference be used as callback prototype in EventDispatcher and CallbackList? Such as CallbackList](#a2_1)
+* [Can the callback prototype have return value? Such as CallbackList?](#a2_2)
+* [Why can't callback prototype be function pointer such as CallbackList?](#a2_3)
+* [Why aren't there APIs to remove listeners directly from an EventDispatcher? Why do we have to remove by handle?](#a2_4)
+* [Isn't CallbackList equivalent to std::vector? It's simple for me to use std::vector directly.](#a2_5)
+* [I want to inherit my class from EventDispatcher, but EventDispatcher's destructor is not virtual?](#a2_6)
+* [How to automatically remove listeners when certain object is destroyed (aka auto disconnection)?](#a2_7)
+* [How to process all EventQueue instances in a single main loop?](#a2_8)
+* [How to integrate EventQueue with boost::asio::io_service?](#a2_9)
+
+
+
## Why can't rvalue reference be used as callback prototype in EventDispatcher and CallbackList? Such as CallbackList
```c++
@@ -11,24 +26,29 @@ The above code doesn't compile. This is intended design and not a bug.
A rvalue reference `std::string &&` means the argument can be moved by the callback and become invalid (or empty). Keep in mind CallbackList invokes many callbacks one by one. So what happens if the first callback moves the argument and the other callbacks get empty value? In above code example, that means the first callback sees the value "Hello" and moves it, then the other callbacks will see empty string, not "Hello"!
To avoid such potential bugs, rvalue reference is forbidden deliberately.
+
## Can the callback prototype have return value? Such as CallbackList?
Yes you can, but both EventDispatcher and CallbackList just discard the return value. It's not efficient nor useful to return value from EventDispatcher and CallbackList.
+
## Why can't callback prototype be function pointer such as CallbackList?
It's rather easy to support function pointer, but it's year 2018 at the time written, and there is proposal for C++20 standard already, so let's use modern C++ features. Stick with function type `void ()` instead of function pointer `void (*)()`.
+
## Why aren't there APIs to remove listeners directly from an EventDispatcher? Why do we have to remove by handle?
Both `EventDispatcher::removeListener(const Event & event, const Handle handle)` and `CallbackList::remove(const Handle handle)` requires the handle of a listener is passed in. So why can't we pass the listener object directly? The reason is, it's not guaranteed that the underlying callback storage is comparable while removing a listener object requires the comparable ability. Indeed the default callback storage, `std::function` is not comparable.
If we use some customized callback storage and we are sure it's comparable, there is free functions 'removeListener' in [utility APIs](eventutil.md).
+
## Isn't CallbackList equivalent to std::vector? It's simple for me to use std::vector directly.
`CallbackList` works like a `std::vector`. But one common usage is to implement one-shot callback that a callback removes itself from the callback list when it's invoked. In such case a simple `std::vector` will bang and crash.
With `CallbackList` a callback can be removed at any time, even when the callback list is under invoking.
+
## I want to inherit my class from EventDispatcher, but EventDispatcher's destructor is not virtual?
It's intended not to use any virtual functions in eventpp to avoid bloating the code size. New class can still inherit from EventDispatcher, as long as the object is not deleted via a pointer to EventDispatcher, which will cause resource leak. If you need to delete object via pointer to base class, make your own base class that inherits from EventDispatcher, and make the base class destructor virtual.
@@ -49,10 +69,12 @@ MyEventDispatcher * myObject = new MyClass();
delete myObject;
```
+
## How to automatically remove listeners when certain object is destroyed (aka auto disconnection)?
[Use utility class ScopedRemover](scopedremover.md)
+
## How to process all EventQueue instances in a single main loop?
It's common to have a single main loop in a GUI or game application, and there are various EventQueue instances in the system. How to process all the EventQueue instances? Let's see some pseudo code first,
@@ -85,6 +107,7 @@ public:
The idea is, the main loop invoke a callback list in each loop, and each event queue registers its process to the callback list.
+
## How to integrate EventQueue with boost::asio::io_service?
A common use case is there are multiple threads that executing boost::asio::io_service::run(). To integrate EventQueue with boost asio, we need to replace `run()` with `poll()` to avoid blocking. So a typical thread will look like,
@@ -118,4 +141,3 @@ public:
```
Note that after the while loop is finished, the ioService is still run and mainLoopTasks is still invoked, that's to clean up any remaining tasks.
-
diff --git a/doc/hetercallbacklist.md b/doc/hetercallbacklist.md
new file mode 100644
index 0000000..d50ddae
--- /dev/null
+++ b/doc/hetercallbacklist.md
@@ -0,0 +1,157 @@
+# Class HeterCallbackList reference
+
+
+## Table Of Contents
+
+* [Description](#a2_1)
+* [API reference](#a2_2)
+ * [Header](#a3_1)
+ * [Template parameters](#a3_2)
+ * [Public types](#a3_3)
+ * [Member functions](#a3_4)
+
+
+
+## Description
+
+HeterCallbackList is the heterogeneous counterpart of CallbackList in eventpp. The other classes HeterEventDispatcher and HeterEventQueue are built on HeterCallbackList.
+
+HeterCallbackList holds a list of callbacks. The callbacks can have different prototypes. At the time of the call, HeterCallbackList invokes each callback which matches the invoking parameters.
+The *callback* can be any callback target -- functions, pointers to functions, , pointers to member functions, lambda expressions, and function objects.
+
+
+## API reference
+
+
+### Header
+
+eventpp/hetercallbacklist.h
+
+
+### Template parameters
+
+```c++
+template <
+ typename PrototypeList,
+ typename Policies = DefaultPolicies
+>
+class HeterCallbackList;
+```
+`Prototype`: a list of function types in `eventpp::HeterTuple`, such as `eventpp::HeterTuple`.
+`Policies`: the policies to configure and extend the callback list. The default value is `DefaultPolicies`. See [document of policies](policies.md) for details.
+
+
+### Public types
+
+`Handle`: the handle type returned by append, prepend and insert. A handle can be used to insert a callback or remove a callback. To check if a `Handle` is empty, convert it to boolean, *false* is empty. `Handle` is copyable.
+`Callback`: the callback storage type.
+
+
+### Member functions
+
+#### constructors
+
+```c++
+HeterCallbackList() noexcept;
+HeterCallbackList(const HeterCallbackList & other);
+HeterCallbackList(HeterCallbackList && other) noexcept;
+HeterCallbackList & operator = (const HeterCallbackList & other);
+HeterCallbackList & operator = (HeterCallbackList && other) noexcept;
+```
+
+HeterCallbackList can be copied, moved, assigned, and move assigned.
+
+#### empty
+
+```c++
+bool empty() const;
+```
+Return true if the callback list is empty.
+Note: in multi threading, this function returning true doesn't guarantee that the list is empty. The list may immediately become non-empty after the function returns true.
+
+#### bool casting operator
+
+```c++
+operator bool() const;
+```
+Return true if the callback list is not empty.
+This operator allows a HeterCallbackList instance be used in condition statement.
+
+#### append
+
+```c++
+template
+Handle append(const C & callback);
+```
+Add the `callback` to the callback list.
+The callback is added to the end of the callback list.
+The callback type `C` must be specified in `PrototypeList`.
+Return a handle that represents the callback. The handle can be used to remove this callback or to insert additional callbacks before this callback.
+If `append` is called in another callback during the invoking of the callback list, the new callback is guaranteed not to be triggered during the same callback list invoking.
+The time complexity is O(1).
+
+#### prepend
+
+```c++
+template
+Handle prepend(const C & callback);
+```
+Add the *callback* to the callback list.
+The callback is added to the beginning of the callback list.
+The callback type `C` must be specified in `PrototypeList`.
+Return a handle that represents the callback. The handle can be used to remove this callback or to insert additional callbacks before this callback.
+If `prepend` is called in another callback during the invoking of the callback list, the new callback is guaranteed not to be triggered during the same callback list invoking.
+The time complexity is O(1).
+
+#### insert
+
+```c++
+template
+Handle insert(const C & callback, const Handle & before);
+```
+Insert the *callback* to the callback list before the callback handle *before*. If *before* is not found, *callback* is added at the end of the callback list.
+The callback type `C` must be specified in `PrototypeList`.
+Return a handle that represents the callback. The handle can be used to remove this callback or to insert additional callbacks before this callback.
+If `insert` is called in another callback during the invoking of the callback list, the new callback is guaranteed not to be triggered during the same callback list invoking.
+The time complexity is O(1).
+
+#### remove
+
+```c++
+bool remove(const Handle & handle);
+```
+Remove the callback *handle* from the callback list.
+Return true if the callback is removed successfully, false if the callback is not found.
+The time complexity is O(1).
+
+#### forEach
+
+```c++
+template
+void forEach(Func && func) const;
+```
+Apply `func` to all callbacks which has the `Prototype`.
+The `func` can be one of the two prototypes:
+```c++
+AnyReturnType func(const HeterCallbackList::Handle & handle, const std::function & callback);
+AnyReturnType func(const std::function & callback);
+```
+**Note**: the `func` can remove any callbacks, or add other callbacks, safely.
+
+#### forEachIf
+```c++
+template
+bool forEachIf(Func && func) const;
+```
+Apply `func` to all callbacks. `func` must return a boolean value, and if the return value is false, forEachIf stops the looping immediately.
+Return `true` if all callbacks are invoked, or `event` is not found, `false` if `func` returns `false`.
+
+#### invoking operator
+
+```c++
+template
+void operator() (Args && ...args) const;
+```
+Invoke each callbacks that can be called with `Args` in the callback list.
+The callbacks are called with arguments `args`.
+The callbacks are called in the thread same as the callee of `operator()`.
diff --git a/doc/hetereventdispatcher.md b/doc/hetereventdispatcher.md
new file mode 100644
index 0000000..7e042db
--- /dev/null
+++ b/doc/hetereventdispatcher.md
@@ -0,0 +1,145 @@
+# Class HeterEventDispatcher reference
+
+
+## Table Of Contents
+
+* [Description](#a2_1)
+* [API reference](#a2_2)
+ * [Header](#a3_1)
+ * [Template parameters](#a3_2)
+ * [Public types](#a3_3)
+ * [Member functions](#a3_4)
+
+
+
+## Description
+
+HeterEventDispatcher is something like std::map.
+
+HeterEventDispatcher holds a map of `` pairs. On dispatching, HeterEventDispatcher finds the HeterCallbackList of the event type, then invoke the callback list. The invocation is always synchronous. The listeners are triggered when `HeterEventDispatcher::dispatch` is called.
+
+
+## API reference
+
+
+### Header
+
+eventpp/hetereventdispatcher.h
+
+
+### Template parameters
+
+```c++
+template <
+ typename Event,
+ typename PrototypeList,
+ typename Policies = DefaultPolicies
+>
+class HeterEventDispatcher;
+```
+`Event`: the *event type*. The type used to identify the event. Events with same type are the same event. The event type must be able to be used as the key in `std::map` or `std::unordered_map`, so it must be either comparable with `operator <` or has specialization of `std::hash`.
+`Prototype`: a list of function types in `eventpp::HeterTuple`, such as `eventpp::HeterTuple`.
+`Policies`: the policies to configure and extend the dispatcher. The default value is `DefaultPolicies`. See [document of policies](policies.md) for details.
+
+
+### Public types
+
+`Handle`: the handle type returned by appendListener, prependListener and insertListener. A handle can be used to insert a listener or remove a listener. To check if a `Handle` is empty, convert it to boolean, *false* is empty. `Handle` is copyable.
+`Event`: the event type.
+
+
+### Member functions
+
+#### constructors
+
+```c++
+HeterEventDispatcher();
+HeterEventDispatcher(const HeterEventDispatcher & other);
+HeterEventDispatcher(HeterEventDispatcher && other) noexcept;
+HeterEventDispatcher & operator = (const HeterEventDispatcher & other);
+HeterEventDispatcher & operator = (HeterEventDispatcher && other) noexcept;
+```
+
+HeterEventDispatcher can be copied, moved, assigned, and move assigned.
+
+#### appendListener
+
+```c++
+template
+Handle appendListener(const Event & event, const C & callback);
+```
+Add the *callback* to the dispatcher to listen to *event*.
+The listener is added to the end of the listener list.
+The callback type `C` must be specified in `PrototypeList`.
+Return a handle which represents the listener. The handle can be used to remove this listener or insert other listener before this listener.
+If `appendListener` is called in another listener during a dispatching, the new listener is guaranteed not triggered during the same dispatching.
+If the same callback is added twice, it results duplicated listeners.
+The time complexity is O(1).
+
+#### prependListener
+
+```c++
+template
+Handle prependListener(const Event & event, const C & callback);
+```
+Add the *callback* to the dispatcher to listen to *event*.
+The listener is added to the beginning of the listener list.
+The callback type `C` must be specified in `PrototypeList`.
+Return a handle which represents the listener. The handle can be used to remove this listener or insert other listener before this listener.
+If `prependListener` is called in another listener during a dispatching, the new listener is guaranteed not triggered during the same dispatching.
+The time complexity is O(1).
+
+#### insertListener
+
+```c++
+template
+Handle insertListener(const Event & event, const C & callback, const Handle & before);
+```
+Insert the *callback* to the dispatcher to listen to *event* before the listener handle *before*. If *before* is not found, *callback* is added at the end of the listener list.
+The callback type `C` must be specified in `PrototypeList`.
+Return a handle which represents the listener. The handle can be used to remove this listener or insert other listener before this listener.
+If `insertListener` is called in another listener during a dispatching, the new listener is guaranteed not triggered during the same dispatching.
+The time complexity is O(1).
+
+#### removeListener
+
+```c++
+bool removeListener(const Event & event, const Handle handle);
+```
+Remove the listener *handle* which listens to *event* from the dispatcher.
+Return true if the listener is removed successfully, false if the listener is not found.
+The time complexity is O(1).
+
+#### forEach
+
+```c++
+template
+void forEach(const Event & event, Func && func) const;
+```
+Apply `func` to all callbacks which has the `Prototype`.
+The `func` can be one of the two prototypes:
+```c++
+AnyReturnType func(const HeterEventDispatcher::Handle & handle, const std::function & callback);
+AnyReturnType func(const std::function & callback);
+```
+**Note**: the `func` can remove any listeners, or add other listeners, safely.
+
+#### forEachIf
+
+```c++
+template
+bool forEachIf(const Event & event, Func && func) const;
+```
+Apply `func` to all listeners of `event`. `func` must return a boolean value, and if the return value is false, forEachIf stops the looping immediately.
+Return `true` if all listeners are invoked, or `event` is not found, `false` if `func` returns `false`.
+
+#### dispatch
+
+```c++
+template
+void dispatch(T && first, Args && ...args) const
+```
+Dispatch an event. The event type is deducted from the arguments of `dispatch`.
+Invoke each callbacks that can be called with `Args` in the callback list.
+The listeners are called with arguments `args`.
+The function is synchronous. The listeners are called in the thread same as the caller of `dispatch`.
diff --git a/doc/hetereventqueue.md b/doc/hetereventqueue.md
new file mode 100644
index 0000000..8d9c538
--- /dev/null
+++ b/doc/hetereventqueue.md
@@ -0,0 +1,189 @@
+# Class HeterEventQueue reference
+
+
+## Table Of Contents
+
+* [Description](#a2_1)
+* [API reference](#a2_2)
+ * [Header](#a3_1)
+ * [Template parameters](#a3_2)
+ * [Public types](#a3_3)
+ * [Member functions](#a3_4)
+ * [Inner class HeterEventQueue::DisableQueueNotify](#a3_5)
+
+
+
+## Description
+
+HeterEventQueue includes all features of HeterEventDispatcher and adds event queue features. Note: HeterEventQueue doesn't inherit from HeterEventDispatcher, don't try to cast HeterEventQueue to HeterEventDispatcher.
+HeterEventQueue is asynchronous. Events are cached in the queue when `HeterEventQueue::enqueue` is called, and dispatched later when `HeterEventQueue::process` is called.
+
+
+## API reference
+
+
+### Header
+
+eventpp/eventqueue.h
+
+
+### Template parameters
+
+```c++
+template <
+ typename Event,
+ typename PrototypeList,
+ typename Policies = DefaultPolicies
+>
+class HeterEventQueue;
+```
+
+HeterEventQueue has the exactly same template parameters with EventDispatcher. Please reference [HeterEventDispatcher document](hetereventdispatcher.md) for details.
+
+
+### Public types
+
+
+### Member functions
+
+#### constructors
+
+```c++
+HeterEventQueue();
+HeterEventQueue(const HeterEventQueue & other);
+HeterEventQueue(HeterEventQueue && other) noexcept;
+HeterEventQueue & operator = (const HeterEventQueue & other);
+HeterEventQueue & operator = (HeterEventQueue && other) noexcept;
+```
+
+EventDispatcher can be copied, moved, assigned, and move assigned.
+Note: the queued events are not copied, moved, assigned, or move assigned, only the listeners are performed these operations.
+
+#### enqueue
+
+```c++
+template
+void enqueue(T && first, Args && ...args);
+```
+Put an event into the event queue. The event type is deducted from the arguments of `enqueue`.
+All copyable arguments are copied to internal data structure. All non-copyable but movable arguments are moved.
+HeterEventQueue requires the arguments either copyable or movable.
+If an argument is a reference to a base class and a derived object is passed in, only the base object will be stored and the derived object is lost. Usually shared pointer should be used in such situation.
+If an argument is a pointer, only the pointer will be stored. The object it points to must be available until the event is processed.
+`enqueue` wakes up any threads that are blocked by `wait` or `waitFor`.
+The time complexity is O(1).
+
+#### process
+
+```c++
+bool process();
+```
+Process the event queue. All events in the event queue are dispatched once and then removed from the queue.
+The function returns true if any events were processed, false if no event was processed.
+The listeners are called in the thread same as the caller of `process`.
+Any new events added to the queue during `process()` are not dispatched during current `process()`.
+`process()` is efficient in single thread event processing, it processes all events in the queue in current thread. To process events from multiple threads efficiently, use `processOne()`.
+Note: if `process()` is called from multiple threads simultaneously, the events in the event queue are guaranteed dispatched only once.
+
+#### processOne
+
+```c++
+bool processOne();
+```
+Process one event in the event queue. The first event in the event queue is dispatched once and then removed from the queue.
+The function returns true if one event was processed, false if no event was processed.
+The listener is called in the thread same as the caller of `processOne`.
+Any new events added to the queue during `processOne()` are not dispatched during current `processOne()`.
+If there are multiple threads processing events, `processOne()` is more efficient than `process()` because it can split the events processing to different threads. However, if there is only one thread processing events, 'process()' is more efficient.
+Note: if `processOne()` is called from multiple threads simultaneously, the events in the event queue are guaranteed dispatched only once.
+
+#### processIf
+
+```c++
+template
+bool processIf(F && func);
+```
+Process the event queue. Before processing an event, the event is passed to `func` and the event will be processed only if `func` returns true.
+`func` takes exactly the same arguments as `HeterEventQueue::enqueue`, and returns a boolean value.
+`processIf` returns true if any event was dispatched, false if no event was dispatched.
+`processIf` has some good use scenarios:
+1. Process certain events in certain thread. For example, in a GUI application, the UI related events may be only desired to processed in the main thread.
+2. Process the events until certain time. For example, in a game engine, the event process may be limited to only several milliseconds, the remaining events will be process in next game loop.
+
+#### emptyQueue
+
+```c++
+bool emptyQueue() const;
+```
+Return true if there is no any event in the event queue, false if there are any events in the event queue.
+Note: in multiple threading environment, the empty state may change immediately after the function returns.
+Note: don't write loop as `while(! eventQueue.emptyQueue()) {}`. It's dead loop since the compiler will inline the code and the change of empty state is never seen by the loop. The safe approach is `while(eventQueue.waitFor(std::chrono::nanoseconds(0))) ;`.
+
+#### clearEvents
+
+```c++
+void clearEvents();
+```
+Clear all queued events without dispatching them.
+This is useful to clear any references such as shared pointer in the queued events to avoid cyclic reference.
+
+#### wait
+
+```c++
+void wait() const;
+```
+`wait` causes the current thread to block until there is new event arrives in the queue.
+Note: though `wait` has work around with spurious wakeup internally, the queue is not guaranteed not empty after `wait` returns.
+`wait` is useful when a thread processes the event queue. A sampel usage is,
+```c++
+for(;;) {
+ eventQueue.wait();
+ eventQueue.process();
+}
+```
+The code works event if it doesn't `wait`, but doing that will waste CPU power resource.
+
+#### waitFor
+
+```c++
+template
+bool waitFor(const std::chrono::duration & duration) const;
+```
+Wait for no longer than *duration* time out.
+Return true if the queue is not empty, false if the return is caused by time out.
+`waitFor` is useful when a event queue processing thread has other condition to check. For example,
+```c++
+std::atomic shouldStop(false);
+for(;;) {
+ while(! eventQueue.waitFor(std::chrono::milliseconds(10)) && ! shouldStop.load()) ;
+ if(shouldStop.load()) {
+ break;
+ }
+
+ eventQueue.process();
+}
+```
+
+
+### Inner class HeterEventQueue::DisableQueueNotify
+
+`HeterEventQueue::DisableQueueNotify` is a RAII class that temporarily prevents the event queue from waking up any waiting threads. When any `DisableQueueNotify` object exist, calling `enqueue` doesn't wake up any threads that are blocked by `wait`. When the `DisableQueueNotify` object is out of scope, the waking up is resumed. If there are more than one `DisableQueueNotify` objects, the waking up is only resumed after all `DisableQueueNotify` objects are destroyed.
+`DisableQueueNotify` is useful to improve performance when batching adding events to the queue. For example, in a main loop of a game engine, `DisableQueueNotify` can be created on the start in a frame, then the game adding events to the queue, and the `DisableQueueNotify` is destroyed at the end of a frame and the events are processed.
+
+To use `DisableQueueNotify`, construct it with a pointer to event queue.
+
+Sample code
+```c++
+using EQ = eventpp::HeterEventQueue;
+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);
+}
+// any blocking threads are waken up here immediately.
+
+// any blocking threads will be waken up by below line since there is no DisableQueueNotify.
+queue.enqueue(3);
+```
diff --git a/doc/heterogeneous.md b/doc/heterogeneous.md
new file mode 100644
index 0000000..41f05ff
--- /dev/null
+++ b/doc/heterogeneous.md
@@ -0,0 +1,80 @@
+# Overview of heterogeneous classes
+
+## Description
+
+'CallbackList', 'EventDispatcher', and 'EventQueue' are homogeneous. All listeners must have the same prototype. For example,
+
+```c++
+eventpp::EventDispatcher dispatcher;
+dispatcher.appendListener(3, []() {}); // OK
+dispatcher.appendListener(3, [](std::string) {}); // wrong, can't listen for void(std::string)
+```
+
+There are heterogeneous counterparts, 'HeterCallbackList', 'HeterEventDispatcher', and 'HeterEventQueue'. These classes allow listeners having different prototypes. For example,
+
+```c++
+eventpp::HeterEventDispatcher > dispatcher;
+dispatcher.appendListener(3, []() {}); // OK
+dispatcher.appendListener(3, [](std::string) {}); // OK
+```
+
+## Usage
+
+### Headers
+
+eventpp/hetercallbacklist.h
+eventpp/hetereventdispatcher.h
+eventpp/hetereventqueue.h
+
+### Template parameters
+
+```c++
+template <
+ typename PrototypeList,
+ typename Policies = DefaultPolicies
+>
+class HeterCallbackList;
+
+template <
+ typename Event,
+ typename PrototypeList,
+ typename Policies = DefaultPolicies
+>
+class HeterEventDispatcher;
+
+template <
+ typename Event,
+ typename PrototypeList,
+ typename Policies = DefaultPolicies
+>
+class HeterEventQueue;
+```
+
+For comparison, below are the template parameters for the homogeneous counterparts
+
+```c++
+template <
+ typename Prototype,
+ typename Policies = DefaultPolicies
+>
+class CallbackList;
+
+template <
+ typename Event,
+ typename Prototype,
+ typename Policies = DefaultPolicies
+>
+class EventDispatcher;
+
+template <
+ typename Event,
+ typename Prototype,
+ typename Policies = DefaultPolicies
+>
+class EventQueue;
+```
+
+The only difference is the `Prototype` in homo-classes becomes `PrototypeList` in heter-classes.
+In the homo-classes, `Prototype` is a single function type such as `void ()`.
+In the heter-classes, `PrototypeList` is a list of function types in `eventpp::HeterTuple`, such as `eventpp::HeterTuple`.
+Note: Ideally it would be better to use `std::tuple` instead of `eventpp::HeterTuple`, but the problem is that the tuple is instantiated in HeterEventDispatcher which cause compile error that function type can't be instantiated.
diff --git a/doc/introduction.md b/doc/introduction.md
index b988be1..0267ad7 100644
--- a/doc/introduction.md
+++ b/doc/introduction.md
@@ -1,20 +1,20 @@
# Introduction to eventpp library
-eventpp includes three major classes, CallbackList, EventDispatcher, and EventQueue. Each class has different purpose and usage.
+eventpp includes three main classes, CallbackList, EventDispatcher, and EventQueue. Each class has a different purpose and usage.
## Class CallbackList
CallbackList is the fundamental class in eventpp. The other classes EventDispatcher and EventQueue are built on CallbackList.
-CallbackList holds a list of callbacks. On invocation, CallbackList simply invokes each callbacks one by one. Think CallbackList as the signal/slot system in Qt, or the callback function pointer in some Windows APIs (such as lpCompletionRoutine in `ReadFileEx`).
+CallbackList holds a list of callbacks. At the time of the call, CallbackList simply invokes each callback one by one. Consider CallbackList as the signal/slot system in Qt, or the callback function pointer in some Windows APIs (such as lpCompletionRoutine in `ReadFileEx`).
The *callback* can be any callback target -- functions, pointers to functions, , pointers to member functions, lambda expressions, and function objects.
-CallbackList is ideal when there are very few kinds of events. Each event can have its own CallbackList, and each CallbackList can have different prototype. For example,
+CallbackList is ideal when there are very few kinds of events. Each event can have its own CallbackList, and each CallbackList can have a different prototype. For example,
```c++
eventpp::CallbackList onStart;
eventpp::CallbackList onStop;
```
-However, if there are lots of kinds of events, hundreds to unlimited (this is quite common in a GUI or game system), using CallbackList for each events will be crazy. This is how EventDispatcher comes useful.
+However, if there are many kinds of events, hundreds of to unlimited (this is quite common in GUI or game systems), it would be crazy to use CallbackList for each event. This is how EventDispatcher comes useful.
## Class EventDispatcher
@@ -22,7 +22,7 @@ EventDispatcher is something like std::map.
EventDispatcher holds a map of `` pairs. On dispatching, EventDispatcher finds the CallbackList of the event type, then invoke the callback list. The invocation is always synchronous. The listeners are triggered when `EventDispatcher::dispatch` is called.
-EventDispatcher is ideal when there are lots of kinds of events, or the number of events can't be determined. Each event is distinguished by an event type. For example,
+EventDispatcher is ideal when there are many kinds of events, or the number of events cannot be predetermined. Each event is distinguished by the event type. For example,
```c++
enum class MyEventType
{
@@ -48,7 +48,7 @@ eventpp::EventDispatcher di
dispatcher.dispatch(MyEvent { MyEventType::redraw });
```
(Note: if you are confused with MyEventPolicies in above sample, please read the [document of policies](policies.md), and just consider the dispatcher as `eventpp::EventDispatcher dispatcher` for now.)
-The disadvantage of EventDispatcher is that all events must have the same callback prototype (`void(const MyEvent &)` in the sample code). The common solution is that the callback takes a base class of Event and all events derive their own event data from Event. In the sample code, MyEvent is the base event class, the callback takes one argument of const reference to MyEvent.
+The disadvantage of EventDispatcher is that all events must have the same callback prototype (`void(const MyEvent &)` in the sample code). The common solution is that the callback takes a base class of Event and all events derive their own event data from Event. In the sample code, MyEvent is the base event class, the callback takes an argument of `const reference to MyEvent`.
## Class EventQueue
@@ -69,8 +69,8 @@ queue.process();
```
## Thread safety
-All classes are thread safe. All public functions can be invoked from multiple threads simultaneously. If it fails, please report a bug.
-The library guarantees the integration of each single function invocation, such as `EventDispatcher::appendListener`, 'CallbackList::remove`, but it doesn't guarantee the order of the operations in multiple threads. For example, if a thread is dispatching an event, the other thread removes a listener in the mean time, the removed listener may be still triggered after it's removed.
+All classes are thread-safe. You can call all public functions from multiple threads at the same time. If it fails, please report a bug.
+The library guarantees the integration of each single function call, such as `EventDispatcher::appendListener`, 'CallbackList::remove`, but it does not guarantee the order of operations in multiple threads. For example, if a thread is dispatching an event, another thread removes a listener at the same time, the removed listener may be still triggered after it's removed.
## Exception safety
@@ -81,5 +81,3 @@ Exceptions may be thrown by underlying code when,
Almost all operations guarantee strong exception safety, which means the underlying data remains original value on exception is thrown.
An except is `EventQueue::process`, on exception, the remaining events will not be dispatched, and the queue becomes empty.
-
-
diff --git a/doc/mixins.md b/doc/mixins.md
index 4dfa837..84d7ccb 100644
--- a/doc/mixins.md
+++ b/doc/mixins.md
@@ -1,5 +1,19 @@
# Mixins
+
+## Table Of Contents
+
+* [Introduction](#a2_1)
+* [Define a mixin](#a2_2)
+* [Inject(enable) mixins to EventDispatcher](#a2_3)
+* [Optional interceptor points](#a2_4)
+* [MixinFilter](#a2_5)
+ * [Public type](#a3_1)
+ * [Functions](#a3_2)
+ * [Sample code for MixinFilter](#a3_3)
+
+
+
## Introduction
A mixin is used to inject code in the EventDispatcher/EventQueue inheritance hierarchy to extend the functionalities. In this document we will use EventDispatcher as the example, the usage for EventQueue is exactly the same.
@@ -15,6 +29,7 @@ EventDispatcher <- MixinA <- MixinB <- EventDispatcherBase
```
The mixins can use all public and protected members (types, functions and data) in EventDispatcherBase. All public members in the mixins are visible and usable by the user.
+
## Define a mixin
A mixin is a template class with one template argument. The mixin must inherit from it's template argument.
@@ -26,6 +41,7 @@ class MyMixin : public Base
};
```
+
## Inject(enable) mixins to EventDispatcher
To enable mixins, add them to the `Mixins` type in the policies class. For example, to enable `MixinFilter`, define the dispatcher as,
@@ -42,6 +58,7 @@ EventDispatcher <- MixinA <- MixinB <- MixinC <- EventDispatcherBase
```
The front mixin is the lowest in the hierarchy.
+
## Optional interceptor points
A mixin can have special named functions that are called at certain point. The special functions must be public.
@@ -54,6 +71,7 @@ bool mixinBeforeDispatch(Args && ...args) const;
The function returns `true` to continue the dispatch, `false` will stop any further dispatching.
For multiple mixins, this function is called in the order of they appearing in MixinList in the policies class.
+
## MixinFilter
MixinFilter allows all events are filtered or modified before dispatching.
@@ -77,10 +95,12 @@ Event filter is a powerful and useful technology, below is some sample use cases
2, Setup catch-all event listener. For example, in a phone book system, the system sends events based on the actions, such as adding a phone number, remove a phone number, look up a phone number, etc. A module may be only interested in special area code of a phone number, not the actions. One approach is the module can listen to all possible events (add, remove, look up), but this is very fragile -- how about a new action event is added and the module forgets to listen on it? The better approach is the module add a filter and check the area code in the filter.
+
### Public type
`FilterHandle`: the handle type returned by appendFilter. A filter handle can be used to remove a filter. To check if a `FilterHandle` is empty, convert it to boolean, *false* is empty. `FilterHandle` is copyable.
+
### Functions
```c++
@@ -95,6 +115,7 @@ bool removeFilter(const FilterHandle & filterHandle);
Remove a filter from the dispatcher.
Return true if the filter is removed successfully.
+
### Sample code for MixinFilter
**Code**
diff --git a/doc/policies.md b/doc/policies.md
index f1470cd..4deadf9 100644
--- a/doc/policies.md
+++ b/doc/policies.md
@@ -1,5 +1,21 @@
# Policies
+
+## Table Of Contents
+
+* [Introduction](#a2_1)
+* [Policies](#a2_2)
+ * [Function getEvent](#a3_1)
+ * [Function canContinueInvoking](#a3_2)
+ * [Type Mixins](#a3_3)
+ * [Type Callback](#a3_4)
+ * [Type Threading](#a3_5)
+* [Type ArgumentPassingMode](#a2_3)
+ * [Template Map](#a3_6)
+* [How to use policies](#a2_4)
+
+
+
## Introduction
eventpp uses policy based design to configure and extend each components' behavior. The last template parameter in EventDispatcher, EventQueue, and CallbackList is the policies class. All those three classes have default policies class named `DefaultPolicies`.
@@ -7,8 +23,10 @@ A policy is either a type or a static function member in the policies class. All
All policies are optional. If any policy is omitted, the default value is used. In fact `DefaultPolicies` is just an empty struct.
The same policy mechanism applies to all three classes, EventDispatcher, EventQueue, and CallbackList, though not all classes requires the same policy.
+
## Policies
+
### Function getEvent
**Prototype**: `static EventKey getEvent(const Args &...)`. The function receives same arguments as `EventDispatcher::dispatch` and `EventQueue::enqueue`, and must return an event type.
@@ -63,6 +81,7 @@ dispatcher.appendListener(3, [](const MyEvent & e, bool b) {
dispatcher.dispatch(MyEvent { 3, "Hello world", 38 }, true);
```
+
### Function canContinueInvoking
**Prototype**: `static bool canContinueInvoking(const Args &...)`. The function receives same arguments as `EventDispatcher::dispatch` and `EventQueue::enqueue`, and must return true if the event dispatching or callback list invoking can continue, false if the dispatching should stop.
@@ -107,6 +126,7 @@ dispatcher.appendListener(3, [](const MyEvent & e) {
dispatcher.dispatch(MyEvent(3));
```
+
### Type Mixins
**Default value**: `using Mixins = eventpp::MixinList<>`. No mixins are enabled.
@@ -114,6 +134,7 @@ dispatcher.dispatch(MyEvent(3));
A mixin is used to inject code in the EventDispatcher/EventQueue inheritance hierarchy to extend the functionalities. For more details, please read the [document of mixins](mixins.md).
+
### Type Callback
**Default value**: `using Callback = std::function`.
@@ -121,6 +142,7 @@ A mixin is used to inject code in the EventDispatcher/EventQueue inheritance hie
`Callback` is the underlying storage type to hold the callback. Default is `std::function`.
+
### Type Threading
**Default value**: `using Threading = eventpp::MultipleThreading`.
@@ -196,6 +218,7 @@ eventpp::EventDispatcher dispatcher;
eventpp::CallbackList callbackList;
```
+
## Type ArgumentPassingMode
**Default value**: `using ArgumentPassingMode = ArgumentPassingAutoDetect`.
@@ -287,6 +310,7 @@ eventpp::EventDispatcher<
dispatcher.dispatch(3, 8, "hello"); // Compile OK
```
+
### Template Map
**Prototype**:
@@ -302,6 +326,7 @@ using Map = // std::map or other map type
`Map` must support operations `[]`, `find()`, and `end()`.
If `Map` is not specified, eventpp will auto determine the type. If the event type supports `std::hash`, `std::unordered_map` is used, otherwise, `std::map` is used.
+
## How to use policies
To use policies, declare a struct, define the policies in it, and pass the struct to CallbackList, EventDispatcher, or EventQueue.
diff --git a/doc/scopedremover.md b/doc/scopedremover.md
index 9886ca3..8987989 100644
--- a/doc/scopedremover.md
+++ b/doc/scopedremover.md
@@ -1,16 +1,31 @@
# Class ScopedRemover reference
+
+## Table Of Contents
+
+* [Description](#a2_1)
+* [API reference](#a2_2)
+ * [Header](#a3_1)
+ * [Template parameters](#a3_2)
+ * [Member functions](#a3_3)
+ * [Sample code](#a3_4)
+ * [Automatic disconnection](#a3_5)
+
+
+
## Description
ScopedRemover is a utility class that automatically removes listeners when ScopedRemover object goes out of scope.
-
+
## API reference
+
### Header
eventpp/utilities/scopedremover.h
+
### Template parameters
```c++
@@ -18,8 +33,9 @@ template
class ScopedRemover;
```
-`DispatcherType` can be CallbackList, EventDispatcher, or EventQueue.
+`DispatcherType` can be CallbackList, EventDispatcher, EventQueue, HeterCallbackList, HeterEventDispatcher, or HeterEventQueue.
+
### Member functions
```c++
@@ -76,6 +92,7 @@ The function `setDispatcher()` and `setCallbackList` sets the dispatcher or call
The other member functions that have the same names with the corresponding underlying class (CallbackList, EventDispatcher, or EventQueue). Those functions add listener to the dispatcher.
+
### Sample code
```c++
@@ -129,6 +146,7 @@ dispatcher.dispatch(event);
```
+
### Automatic disconnection
ScopedRemover can be used to auto disconnect listeners when the object involved in the listeners is destroyed. For example, pseudo code,
@@ -170,4 +188,3 @@ class MyClass
```
In above code, when the object of MyClass is destroyed, `myListener` is automatically removed from `someDispatcher`, `someDispatcher` will not invoke on any dangling pointer.
-
diff --git a/doc/tutorial_callbacklist.md b/doc/tutorial_callbacklist.md
index 2e2df20..685c8d1 100644
--- a/doc/tutorial_callbacklist.md
+++ b/doc/tutorial_callbacklist.md
@@ -1,16 +1,9 @@
# Tutorials of CallbackList
-## Table Of Contents
+
-- [Tutorial 1 -- Basic usage](#tutorial1)
-- [Tutorial 2 -- Callback with parameters](#tutorial2)
-- [Tutorial 3 -- Remove](#tutorial3)
-- [Tutorial 4 -- For each](#tutorial4)
-
-
## Tutorials
-
### CallbackList tutorial 1, basic
**Code**
@@ -62,7 +55,6 @@ callbackList();
```
During the invoking, all callbacks will be invoked one by one in the order of they were added.
-
### CallbackList tutorial 2, callback with parameters
**Code**
@@ -91,7 +83,6 @@ callbackList("Hello world", true);
Now the callback list prototype takes two parameters, `const std::string &` and `const bool`.
The callback's prototype is not required to be same as the callback list, it's fine as long as the prototype is compatible with the callback list. See the second callback, `[](std::string s, int b)`, its prototype is not same as the callback list.
-
### CallbackList tutorial 3, remove
**Code**
@@ -125,7 +116,6 @@ callbackList();
**Remarks**
-
### CallbackList tutorial 4, for each
**Code**
@@ -176,4 +166,3 @@ callbackList();
> Got callback 3.
**Remarks**
-
diff --git a/doc/tutorial_eventdispatcher.md b/doc/tutorial_eventdispatcher.md
index 8db27ad..abccdbe 100644
--- a/doc/tutorial_eventdispatcher.md
+++ b/doc/tutorial_eventdispatcher.md
@@ -1,15 +1,9 @@
# Tutorials of EventDispatcher
-## Table Of Contents
+
-- [Tutorial 1 -- Basic usage](#tutorial1)
-- [Tutorial 2 -- Listener with parameters](#tutorial2)
-- [Tutorial 3 -- Customized event struct](#tutorial3)
-
-
## Tutorials
-
### Tutorial 1 -- Basic usage
**Code**
@@ -72,7 +66,6 @@ dispatcher.dispatch(5);
Here we dispatched two events, one is event 3, the other is event 5.
During the dispatching, all listeners of that event will be invoked one by one in the order of they were added.
-
### Tutorial 2 -- Listener with parameters
**Code**
@@ -106,7 +99,6 @@ dispatcher.dispatch(5, "World", false);
Now the dispatcher callback prototype takes two parameters, `const std::string &` and `const bool`.
The listener's prototype is not required to be same as the dispatcher, it's fine as long as the prototype is compatible with the dispatcher. See the second listener, `[](std::string s, int b)`, its prototype is not same as the dispatcher.
-
### Tutorial 3 -- Customized event struct
**Code**
@@ -163,4 +155,3 @@ dispatcher.dispatch(MyEvent { 3, "Hello world", 38 }, true);
**Remarks**
A common situation is an Event class is defined as the base class, all other events derive from Event, and the actual event type is a data member of Event (think QEvent in Qt). To let EventDispatcher knows how to get the event type from class Event, policies (the third template parameter) is used.
-
diff --git a/doc/tutorial_eventqueue.md b/doc/tutorial_eventqueue.md
index d79a624..aca6f73 100644
--- a/doc/tutorial_eventqueue.md
+++ b/doc/tutorial_eventqueue.md
@@ -1,11 +1,7 @@
# Tutorials of EventQueue
-## Table Of Contents
+
-- [Tutorial 1 -- Basic usage](#tutorial1)
-- [Tutorial 2 -- multiple threading](#tutorial2)
-
-
### Tutorial 1 -- Basic usage
**Code**
@@ -46,7 +42,6 @@ A typical use case is in a GUI application, each components call `EventQueue<>::
`EventQueue` supports non-copyable object as the event arguments, such as the unique pointer in the tutorial.
-
### Tutorial 2 -- multiple threading
**Code**
diff --git a/include/eventpp/callbacklist.h b/include/eventpp/callbacklist.h
index e88d84f..d6a0686 100644
--- a/include/eventpp/callbacklist.h
+++ b/include/eventpp/callbacklist.h
@@ -15,6 +15,7 @@
#define CALLBACKLIST_H_588722158669
#include "eventpolicies.h"
+#include "internal/typeutil_i.h"
#include
#include
@@ -22,26 +23,11 @@
#include
#include
#include
-#include
namespace eventpp {
namespace internal_ {
-template
-struct CanInvoke
-{
- template
- static auto invoke(int) -> decltype(std::declval()(std::declval()...), std::true_type());
-
- template
- static auto invoke(...) -> std::false_type;
-
- enum {
- value = !! decltype(invoke(0))()
- };
-};
-
template <
typename Prototype,
typename PoliciesType
@@ -69,7 +55,7 @@ class CallbackListBase<
>::Type;
using CanContinueInvoking = typename SelectCanContinueInvoking<
- Policies, HasFunctionCanContinueInvoking::value
+ Policies, HasFunctionCanContinueInvoking::value
>::Type;
struct Node;
@@ -250,7 +236,7 @@ class CallbackListBase<
return append(callback);
}
- bool remove(const Handle handle)
+ bool remove(const Handle & handle)
{
std::lock_guard lockGuard(mutex);
auto node = handle.lock();
@@ -323,13 +309,6 @@ class CallbackListBase<
return func(Handle(node), node->callback);
}
- template
- auto doForEachInvoke(Func && func, NodePtr & node) const
- -> typename std::enable_if::value, RT>::type
- {
- return func(Handle(node));
- }
-
template
auto doForEachInvoke(Func && func, NodePtr & node) const
-> typename std::enable_if::value, RT>::type
diff --git a/include/eventpp/eventdispatcher.h b/include/eventpp/eventdispatcher.h
index fe2b430..4b7bcd7 100644
--- a/include/eventpp/eventdispatcher.h
+++ b/include/eventpp/eventdispatcher.h
@@ -19,10 +19,7 @@
#include
#include
#include
-#include