`ArgumentPassingMode`: the argument passing mode. Default is `ArgumentPassingAutoDetect`. See [Argument passing mode](#argument-passing-mode) for details.
`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.
`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.
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.
Return a handle which represents the listener. The handle can be used to remove this listener or insert other listener before this listener.
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`.
If *event getter* inherits from tag eventpp::EventGetterBase, it's the event type getter. See [Tutorial 3](tutorial_eventdispatcher.md#tutorial3) for example.
An *event getter* must have a type `Event` which is the event type and used in the internal 'std::map`, and it must have a static function `getEvent`, which receives the arguments of callback prototype and return the event type.
To add a listener
```c++
dispatcher.appendListener(5, [](const MyEvent & e, bool b) {});
```
Note the first argument is the `Event` in the *event getter*, here is `int`, not `MyEvent`.
To dispatch or enqueue an event
```c++
MyEvent myEvent { 5, "Hello", 38 };
dispatcher.dispatch(myEvent, true);
```
Note the first argument is `MyEvent`, not `Event`.
`dispatch` and `enqueue` use the function `getEvent` in the *event getter* to deduct the event type.
`dispatch` and `enqueue` don't assume the meaning of any arguments. How to get the event type completely depends on `getEvent`. `getEvent` can simple return a member for the first argument, or concatenate all arguments, or even hash the arguments and return the hash value as the event type.
`EventDispatcher<>::appendFilter(filter)` adds an event filter to the dispatcher. The `filter` receives the arguments which types are the callback prototype with lvalue reference, and must return a boolean value. Return `true` to allow the dispatcher continues the dispatching, `false` to prevent the dispatcher from invoking any subsequence listeners and filters.
The event filters are invoked for all events, and invoked before any listeners are invoked.
The event filters can modify the arguments since the arguments are passed as lvalue reference, no matter whether they are reference in the callback prototype (of course we can't modify a reference to const).
Below table shows the cases of how event filters receive the arguments.
|Argument type in callback prototype |Argument type received by filter |Can filter modify the argument? | Comment |
|-----|-----|:-----:|-----|
|int, const int |int &, int & |Yes |The constness of the value is discarded|
|const int &, const int *|const int &, const int *& |No |The constness of the reference/pointer must be respected|
Event filter is a powerful and useful technology, below is some sample use cases, though the real world use cases are unlimited.
1, Capture and block all interested events. For example, in a GUI window system, all windows can receive mouse events. However, when a window is under mouse dragging, only the window under dragging should receive the mouse events even when the mouse is moving on other window. So when the dragging starts, the window can add a filter. The filter redirects all mouse events to the window and prevent other listeners from the mouse events, and bypass all other events.
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.
The listener's first parameter is also `int`. Depending how the event is dispatched, the listener's first argument can be either the event type, or an extra argument.
```c++
dispatcher.dispatch(3, "hello");
```
The event *3* is dispatched with an argument *"hello"*, the listener will be invoked with the arguments `(3, "hello")`, the first argument is the event type.
```c++
dispatcher.dispatch(3, 8, "hello");
```
The event *3* is dispatched with two arguments *8* and *"hello"*, the listener will be invoked with the arguments `(8, "hello")`, the first argument is the extra argument, and the event type is omitted.
So by default, EventDispatcher automatically detects the argument count of `dispatch` and listeners prototype, and calls the listeners either with or without the event type.
The default rule is convenient, permissive, and, may be error prone. The second parameter `typename ArgumentPassingMode` in the policies can control the behavior.
```c++
struct ArgumentPassingAutoDetect;
struct ArgumentPassingIncludeEvent;
struct ArgumentPassingExcludeEvent;
```
`ArgumentPassingAutoDetect`: the default policy. Auto detects whether to pass the event type.
`ArgumentPassingIncludeEvent`: always passes the event type. If the argument count doesn't match, compiling fails.
`ArgumentPassingExcludeEvent`: always omits and doesn't pass the event type. If the argument count doesn't match, compiling fails.
Assumes the number of arguments in the listener prototype is P, the number of arguments (include the event type) in `dispatch` is D, then the relationship of P and D is,
For `ArgumentPassingAutoDetect`: P == D or P + 1 == D
For `ArgumentPassingIncludeEvent`: P == D
For `ArgumentPassingExcludeEvent`: P + 1 == D
**Note**: the same rules also applies to `EventDispatcher<>::enqueue`, since `enqueue` has same parameters as `dispatch`.
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.
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).