1
0
mirror of https://github.com/wqking/eventpp.git synced 2024-12-27 00:17:02 +08:00
eventpp/doc/anyid.md
2022-05-31 17:27:15 +08:00

158 lines
6.1 KiB
Markdown

# Class AnyId reference
<!--begintoc-->
## Table Of Contents
* [Description](#a2_1)
* [API reference](#a2_2)
* [Header](#a3_1)
* [Class AnyId template parameters](#a3_2)
* [Public types](#a3_3)
* [Member functions](#a3_4)
* [Global type AnyHashableId](#a2_3)
* [Comparison AnyId](#a2_4)
* [When to use AnyId?](#a2_5)
<!--endtoc-->
<a id="a2_1"></a>
## Description
The template class `AnyId` can be used as the event ID type in `EventDispatcher` and `EventQueue`, then any types can be used as the event type.
For example,
```c++
eventpp::EventQueue<eventpp::AnyId<>, void()> eventQueue;
eventQueue.appendListener(3, []() {}); // listener 1
eventQueue.appendListener(std::string("hello"), []() {}); // listener 2
eventQueue.dispatch(3); // trigger listener 1
eventQueue.dispatch(std::string("hello")); // trigger listener 2
```
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
// eventQueue.appendListener(std::string("hello"), []() {});
```
For an `int` event type, we can't use `std::string` as the event ID.
With `AnyId` in previous example code, we can pass any types as the event ID.
<a id="a2_2"></a>
## API reference
<a id="a3_1"></a>
### Header
eventpp/utilities/anyid.h
<a id="a3_2"></a>
### Class AnyId template parameters
```c++
template <template <typename> class Digester = std::hash, typename Storage = EmptyStorage>
class AnyId;
```
`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.
`Digester` is used to convert any types to a specified type and `AnyId` stores the digest instead of the value itself.
`Storage` is used to store the actual value.
A typical implementation of `Digester`:
```c++
template <typename T>
struct MyDigest
{
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.
A typical implementation of `Storage`:
```c++
struct MyStorage
{
template <typename T>
MyStorage(const T & value) {
// store the value
}
// any other member functions can be added, such as getting the underlying value.
};
```
Or none template version:
```c++
// In this version, only value of `int` and `std::string` can be stored.
struct MyStorage
{
MyStorage(const int value) {}
MyStorage(const std::string & value) {}
// any other member functions can be added, such as getting the underlying value.
};
```
<a id="a3_3"></a>
### Public types
`DigestType`: the digest type that returned by `Digester`. If `Digester` is `std::hash`, `DigestType` is `std::size_t`.
<a id="a3_4"></a>
### Member functions
#### constructors
```c++
AnyId();
template <typename T>
AnyId(const T & value);
```
Any value can be converted to `AnyId` implicitly.
#### getDigest
```c++
DigestType getDigest() const;
```
Return the digest for the value that passed in the constructor.
```c++
const Storage & getValue() const;
```
Return the value that's stored in `Storage`. The default `Storage` is an empty structure, so you can't get the real value from it.
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`.
<a id="a2_3"></a>
## Global type AnyHashableId
```c++
using AnyHashableId = AnyId<>;
```
`AnyHashableId` is an instantiation of `AnyId` with the default parameters. It can be used in place of the event ID in `EventDispatcher` or `EventQueue`.
In the example code in the beginning of this document, the `eventpp::AnyId<>` can be replaced with `eventpp::AnyHashableId`.
<a id="a2_4"></a>
## Comparison AnyId
`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).
If the `Storage` supports the operators, the values in the storage are compared. In this case, it doesn't matter if digest collides.
If the `Storage` doesn't support the operators, only the digests are compared. In this case, if digest collides, the result is in collision.
<a id="a2_5"></a>
## When to use AnyId?
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.
If you find there are good reasons to mix the event types and there are good cases to use `AnyId`, you can let me know.