mirror of
https://github.com/wqking/eventpp.git
synced 2024-12-27 00:17:02 +08:00
158 lines
6.1 KiB
Markdown
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.
|