Note the `eventpp::AnyId<>` in the example code, it's an instantiation of `AnyId` with default template parameters. It's in the place of where an event type should be, such as int.
Without `AnyId`, a typical EventQueue looks like,
```c++
eventpp::EventQueue<int,void()> eventQueue;
eventQueue.appendListener(3, []() {});
// This doesn't compile because std::string can't be converted to int
`Digester`: a template class that has one template parameter. It has a function call operator that receives one value and returns the digest of the value. The returned digest must be hashable, i.e, it must be able to be passed to `std::hash`. One of such `Digester` is `std::hash`. The parameter default value is `std::hash`. An event ID that's converted to `AnyId` must be able to pass to `Digester` function call operator. For example, if `Digester` is `std::hash`, the event ID must be hashable, aka, it must be able to be passed to `std::hash`, so `int` and `std::string` works, but `const char *` not.
`Storage`: a class that can be constructed with any types of values which are going to be used in `AnyId`. One of such `Storage` is `std::any` (in C++17). The parameter default value is an empty storage class that can be constructed with any types and it doesn't hold the value.
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.
If `std::any` is used as the `Storage` parameter when instantiating the `AnyId` template, `getValue` returns the `std::any` thus the value can be obtained from the `std::any`.
`AnyHashableId` is an instantiation of `AnyId` with the default parameters. It can be used in place of the event ID in `EventDispatcher` or `EventQueue`.
`AnyId` supports `operator ==` for being used in `std::unordered_map`, and `operator <` for being used in `std::map` (which map is used depending on the policies), in `EventDispatcher` and `EventQueue`.
`AnyId` compares the digest first (the digest must be comparable).
Even though `AnyId` looks smart and very flexible, I highly don't encourage you to use it at all because that means the architecture has flaws. You should always prefer to single event type, such as `int`, or `std::string`, than mixing them.
If you want to use `AnyHashableId` (aka, `AnyId<>`), don't forget to take into account of the collision created by `std::hash`, and be sure your event IDs don't collide with each other. Instead of using `std::hash`, you may implement more safer digester, such as SHA256.