Class `AnyData` is a data structure that can hold any data types without any dynamic heap allocation, and `AnyData` can be passed to `EventQueue` in place of the data it holds. The purpose is to eliminate the heap allocation time which is commonly used as `std::shared_ptr`. `AnyData` can improve the performance by about 30%~50%, comparing to heap allocation with `std::shared_ptr`.
`AnyData` is not type safe. Abusing it may lead you problems. If you understand how it works, and use it properly, you can gain performance improvement.
`AnyData` should be used for extreme performance optimization, such as the core event system in a game engine. In a performance non-critical system such as desktop GUI, you may not need `AnyData`, otherwise, it's premature optimization.
`AnyData` should be only used in `EventQueue`, it's not general purpose class, don't use it for other purpose.
For example, assume we have the event class hierarchy (we will use these event classes in example code all over this document),
```c++
enum class EventType
{
// for MouseEvent
mouse,
// for KeyEvent
key,
// for MessageEvent
message,
// For a simple std::string event which is not derived from Event
`AnyData` requires one constant template parameter. It's the max size of the underlying types. Any data types with any data size can be used to construct `AnyData`. If the data size is not larger than `maxSize`, the data is stored inside `AnyData`. If it's larger, the data is stored on the heap with dynamic allocation.
`AnyData` uses at least `maxSize` bytes, even if the underlying data is only 1 byte long. So `AnyData` might use slightly more memory than the shared pointer solution, but also may not, because shared pointer solution has other memory overhead.
Since `AnyData` can convert to any types automatically, here the listener functions can receive `const Event &` instead of `AnyData`. When `EventQueue` passes `AnyData` to the listener, `AnyData` can cast to `const Event &` automatically.
`AnyData` can convert to reference or pointer. When it converts to reference, the reference refers to the underlying data. When it converts to pointer, the pointer points to the address of the underlying data. The special conversion of pointer allow we use unrelated data types as event arguments and receive the argument as `const void *`. For example,
This is really bad design because then your code is coupled with `eventpp` tightly. I highly recommend you not to do so. But if you do want to do it, here are the `AnyData` member functions which helps you to use it.
Return a reference to the underlying data as type `T`. `AnyData` doesn't check if the underlying data is of type `T`, it simply returns a reference to the underlying data, so it's not type safe.
#### getAddress
```c++
const void * getAddress() const;
```
Return a pointer to the address of the underlying data.
#### isType
```c++
template <typenameT>
bool isType() const;
```
Return true if the underlying data type is `T`, false if not.
This function compares the exactly types, it doesn't check any class hierarchy. For example, if an `AnyData` holds `KeyEvent`, then `isType<KeyEvent>()` will return true, but `isType<Event>()` will return false.
## Global function
### maxSizeOf
```c++
template <typename...Ts>
constexpr std::size_t maxSizeOf();
```
Return the maximum size of types Ts... For example,
```c++
maxSizeOf<KeyEvent,MouseEvent,int,double>();
```
## Tutorial
Below is the tutorial code. The complete code can be found in `tests/tutorial/tutorial_anydata.cpp`