mirror of
https://github.com/wqking/eventpp.git
synced 2024-12-25 23:30:49 +08:00
AnyData stores large data on heap instead of compiling error
This commit is contained in:
parent
b25e2703a5
commit
7afab328dd
@ -2,19 +2,22 @@
|
||||
<!--begintoc-->
|
||||
## Table Of Contents
|
||||
|
||||
* [Description](#a2_1)
|
||||
* [Use AnyData](#a2_2)
|
||||
* [Header](#a3_1)
|
||||
* [Class AnyData template parameters](#a3_2)
|
||||
* [Use AnyData in EventQueue, the simplest but not recommend way](#a3_3)
|
||||
* [Use AnyData in EventQueue, the recommend way](#a3_4)
|
||||
* [Extend with new events](#a3_5)
|
||||
* [Global function](#a2_3)
|
||||
* [maxSizeOf](#a3_6)
|
||||
* [Tutorial](#a2_4)
|
||||
- [Class AnyData reference](#class-anydata-reference)
|
||||
- [Table Of Contents](#table-of-contents)
|
||||
- [Description](#description)
|
||||
- [Use AnyData](#use-anydata)
|
||||
- [Header](#header)
|
||||
- [Class AnyData template parameters](#class-anydata-template-parameters)
|
||||
- [Use AnyData in EventQueue, the simplest but not recommend way](#use-anydata-in-eventqueue-the-simplest-but-not-recommend-way)
|
||||
- [get](#get)
|
||||
- [getAddress](#getaddress)
|
||||
- [isType](#istype)
|
||||
- [Use AnyData in EventQueue, the recommend way](#use-anydata-in-eventqueue-the-recommend-way)
|
||||
- [Global function](#global-function)
|
||||
- [maxSizeOf](#maxsizeof)
|
||||
- [Tutorial](#tutorial)
|
||||
<!--endtoc-->
|
||||
|
||||
<a id="a2_1"></a>
|
||||
## Description
|
||||
|
||||
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`.
|
||||
@ -119,15 +122,12 @@ eventQueue.enqueue(EventType::key, KeyEvent(123));
|
||||
eventQueue.enqueue(EventType::mouse, MouseEvent(100, 200));
|
||||
```
|
||||
|
||||
<a id="a2_2"></a>
|
||||
## Use AnyData
|
||||
|
||||
<a id="a3_1"></a>
|
||||
### Header
|
||||
|
||||
eventpp/utilities/anydata.h
|
||||
|
||||
<a id="a3_2"></a>
|
||||
### Class AnyData template parameters
|
||||
|
||||
```c++
|
||||
@ -135,10 +135,9 @@ template <std::size_t maxSize>
|
||||
class AnyData;
|
||||
```
|
||||
|
||||
`AnyData` requires one constant template parameter. It's the max size of the underlying types. Any data types can be used to construct `AnyData`, as long as the data type size is not larger than `maxSize`. If it's larger, compile time error is produced.
|
||||
`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.
|
||||
|
||||
<a id="a3_3"></a>
|
||||
### Use AnyData in EventQueue, the simplest but not recommend way
|
||||
|
||||
`AnyData` can be used as the callback arguments in EventQueue.
|
||||
@ -184,7 +183,6 @@ 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.
|
||||
|
||||
<a id="a3_4"></a>
|
||||
### Use AnyData in EventQueue, the recommend way
|
||||
|
||||
```c++
|
||||
@ -219,26 +217,8 @@ queue.enqueue(EventType::text, std::string("This is a text"));
|
||||
queue.process();
|
||||
```
|
||||
|
||||
<a id="a3_5"></a>
|
||||
### Extend with new events
|
||||
|
||||
`AnyData` requires compile time known max data size. What if a user needs to add new events but he can't modify the `AnyData` declaration thus can't modify the max size?
|
||||
The user can add any new events as long as the data size is not larger then the max size. If any new events has larger data, the data can be put in dynamic allocated memory and hold in `std::shared_ptr`. For example,
|
||||
|
||||
```c++
|
||||
class MyLargeEvent : public Event
|
||||
{
|
||||
private:
|
||||
std::shared_ptr<char> myLargeGigaBytesData;
|
||||
};
|
||||
```
|
||||
|
||||
Then it works as long as `sizeof(MyLargeEvent) <= eventMaxSize`.
|
||||
|
||||
<a id="a2_3"></a>
|
||||
## Global function
|
||||
|
||||
<a id="a3_6"></a>
|
||||
### maxSizeOf
|
||||
|
||||
```c++
|
||||
@ -251,7 +231,6 @@ Return the maximum size of types Ts... For example,
|
||||
maxSizeOf<KeyEvent, MouseEvent, int, double>();
|
||||
```
|
||||
|
||||
<a id="a2_4"></a>
|
||||
## Tutorial
|
||||
|
||||
Below is the tutorial code. The complete code can be found in `tests/tutorial/tutorial_anydata.cpp`
|
||||
|
@ -18,13 +18,17 @@
|
||||
#include <type_traits>
|
||||
#include <cstdint>
|
||||
#include <cassert>
|
||||
#include <memory>
|
||||
|
||||
namespace eventpp {
|
||||
|
||||
namespace anydata_internal_ {
|
||||
|
||||
template <typename T>
|
||||
void funcMoveConstruct(void * object, void * buffer);
|
||||
void funcDeleteObject(void * object)
|
||||
{
|
||||
delete static_cast<T *>(object);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void funcFreeObject(void * object)
|
||||
@ -32,26 +36,6 @@ void funcFreeObject(void * object)
|
||||
static_cast<T *>(object)->~T();
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
auto doFuncCopyConstruct(void * object, void * buffer)
|
||||
-> typename std::enable_if<std::is_copy_constructible<T>::value>::type
|
||||
{
|
||||
new (buffer) T(*(T *)object);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
auto doFuncCopyConstruct(void * object, void * buffer)
|
||||
-> typename std::enable_if<! std::is_copy_constructible<T>::value>::type
|
||||
{
|
||||
funcMoveConstruct<T>(object, buffer);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void funcCopyConstruct(void * object, void * buffer)
|
||||
{
|
||||
doFuncCopyConstruct<T>(object, buffer);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
auto doFuncMoveConstruct(void * object, void * buffer)
|
||||
-> typename std::enable_if<std::is_move_constructible<T>::value>::type
|
||||
@ -88,10 +72,16 @@ const AnyDataFunctions * doGetAnyDataFunctions()
|
||||
return &functions;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
struct RemoveCvRef
|
||||
{
|
||||
using Type = typename std::remove_cv<typename std::remove_reference<T>::type>::type;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
const AnyDataFunctions * getAnyDataFunctions()
|
||||
{
|
||||
using U = typename std::remove_reference<typename std::remove_cv<T>::type>::type;
|
||||
using U = typename RemoveCvRef<T>::Type;
|
||||
return doGetAnyDataFunctions<U>();
|
||||
}
|
||||
|
||||
@ -113,11 +103,56 @@ struct MaxSizeOf <T>
|
||||
static constexpr std::size_t value = sizeof(T);
|
||||
};
|
||||
|
||||
class LargeData
|
||||
{
|
||||
public:
|
||||
template <typename T>
|
||||
explicit LargeData(T && object) : data(), deleter() {
|
||||
using U = typename RemoveCvRef<T>::Type;
|
||||
deleter = &funcDeleteObject<U>;
|
||||
data = new U(std::forward<T>(object));
|
||||
}
|
||||
|
||||
~LargeData() {
|
||||
if(data != nullptr) {
|
||||
assert(deleter != nullptr);
|
||||
deleter(data);
|
||||
}
|
||||
}
|
||||
|
||||
LargeData(LargeData && other) : data(), deleter() {
|
||||
std::swap(data, other.data);
|
||||
std::swap(deleter, other.deleter);
|
||||
}
|
||||
|
||||
LargeData(const LargeData & other) = delete;
|
||||
LargeData & operator = (const LargeData & other) = delete;
|
||||
LargeData & operator = (LargeData && other) = delete;
|
||||
|
||||
const void * getAddress() const {
|
||||
return data;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
bool isType() const {
|
||||
using U = typename RemoveCvRef<T>::Type;
|
||||
return deleter == &funcDeleteObject<U>;
|
||||
}
|
||||
|
||||
private:
|
||||
void * data;
|
||||
void (*deleter)(void *);
|
||||
};
|
||||
|
||||
} //namespace anydata_internal_
|
||||
|
||||
template <std::size_t maxSize>
|
||||
template <std::size_t maxSize_>
|
||||
class AnyData
|
||||
{
|
||||
private:
|
||||
using LargeData = anydata_internal_::LargeData;
|
||||
static constexpr std::size_t maxSize = maxSize_ < sizeof(LargeData) ? sizeof(LargeData) : maxSize_;
|
||||
|
||||
static_assert(maxSize > 0, "AnyData: maxSize must be greater than 0");
|
||||
|
||||
public:
|
||||
@ -128,13 +163,20 @@ public:
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
AnyData(T && object) : functions(anydata_internal_::getAnyDataFunctions<T>()), buffer() {
|
||||
AnyData(T && object,
|
||||
typename std::enable_if<(sizeof(typename anydata_internal_::RemoveCvRef<T>::Type) <= maxSize)>::type * = 0)
|
||||
: functions(anydata_internal_::getAnyDataFunctions<T>()), buffer() {
|
||||
using U = typename std::remove_reference<T>::type;
|
||||
static_assert(sizeof(U) <= maxSize, "AnyData: object size must not be greater than maxSize");
|
||||
|
||||
new (buffer.data()) U(std::forward<T>(object));
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
AnyData(T && object,
|
||||
typename std::enable_if<(sizeof(typename anydata_internal_::RemoveCvRef<T>::Type) > maxSize)>::type * = 0)
|
||||
: functions(anydata_internal_::getAnyDataFunctions<LargeData>()), buffer() {
|
||||
new (buffer.data()) LargeData(std::forward<T>(object));
|
||||
}
|
||||
|
||||
AnyData(AnyData && other) : functions(other.functions), buffer() {
|
||||
if(functions != nullptr) {
|
||||
functions->moveConstruct(other.buffer.data(), buffer.data());
|
||||
@ -154,12 +196,22 @@ public:
|
||||
const void * getAddress() const {
|
||||
assert(functions != nullptr);
|
||||
|
||||
return buffer.data();
|
||||
if(! isLargerData()) {
|
||||
return buffer.data();
|
||||
}
|
||||
else {
|
||||
return ((const LargeData *)buffer.data())->getAddress();
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
bool isType() const {
|
||||
return anydata_internal_::getAnyDataFunctions<T>() == functions;
|
||||
if(! isLargerData()) {
|
||||
return anydata_internal_::getAnyDataFunctions<T>() == functions;
|
||||
}
|
||||
else {
|
||||
return ((const LargeData *)buffer.data())->isType<T>();
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
@ -172,6 +224,11 @@ public:
|
||||
return (T *)getAddress();
|
||||
}
|
||||
|
||||
private:
|
||||
bool isLargerData() const {
|
||||
return functions == anydata_internal_::getAnyDataFunctions<LargeData>();
|
||||
}
|
||||
|
||||
private:
|
||||
const anydata_internal_::AnyDataFunctions * functions;
|
||||
std::array<std::uint8_t, maxSize> buffer;
|
||||
|
@ -33,6 +33,18 @@ struct EventB : Event {
|
||||
int b2;
|
||||
};
|
||||
|
||||
struct LargeEventA : Event {
|
||||
int a;
|
||||
std::array<void *, 100> dummy;
|
||||
};
|
||||
|
||||
struct LargeEventB : Event {
|
||||
int b1;
|
||||
int b2;
|
||||
std::array<void *, 100> dummy;
|
||||
};
|
||||
|
||||
template <typename A, typename B>
|
||||
void doExecuteEventQueue(
|
||||
const std::string & message,
|
||||
const size_t queueSize,
|
||||
@ -62,8 +74,8 @@ void doExecuteEventQueue(
|
||||
]{
|
||||
for(size_t iterate = 0; iterate < iterateCount; ++iterate) {
|
||||
for(size_t i = 0; i < queueSize; ++i) {
|
||||
eventQueue.enqueue(i % eventCount, std::make_shared<EventA>());
|
||||
eventQueue.enqueue(i % eventCount, std::make_shared<EventB>());
|
||||
eventQueue.enqueue(i % eventCount, std::make_shared<A>());
|
||||
eventQueue.enqueue(i % eventCount, std::make_shared<B>());
|
||||
}
|
||||
eventQueue.process();
|
||||
}
|
||||
@ -80,6 +92,7 @@ void doExecuteEventQueue(
|
||||
;
|
||||
}
|
||||
|
||||
template <typename A, typename B>
|
||||
void doExecuteEventQueueWithAnyData(
|
||||
const std::string & message,
|
||||
const size_t queueSize,
|
||||
@ -113,8 +126,8 @@ void doExecuteEventQueueWithAnyData(
|
||||
]{
|
||||
for(size_t iterate = 0; iterate < iterateCount; ++iterate) {
|
||||
for(size_t i = 0; i < queueSize; ++i) {
|
||||
eventQueue.enqueue(i % eventCount, EventA());
|
||||
eventQueue.enqueue(i % eventCount, EventB());
|
||||
eventQueue.enqueue(i % eventCount, A());
|
||||
eventQueue.enqueue(i % eventCount, B());
|
||||
}
|
||||
eventQueue.process();
|
||||
}
|
||||
@ -136,7 +149,9 @@ void doExecuteEventQueueWithAnyData(
|
||||
|
||||
TEST_CASE("b8, EventQueue, AnyData")
|
||||
{
|
||||
doExecuteEventQueue("Without AnyData", 100, 1000 * 100, 100);
|
||||
doExecuteEventQueueWithAnyData("With AnyData", 100, 1000 * 100, 100);
|
||||
doExecuteEventQueue<EventA, EventB>("Without AnyData, small data", 100, 1000 * 100, 100);
|
||||
doExecuteEventQueueWithAnyData<EventA, EventB>("With AnyData, small data", 100, 1000 * 100, 100);
|
||||
doExecuteEventQueue<LargeEventA, LargeEventB>("Without AnyData, large data", 100, 1000 * 100, 100);
|
||||
doExecuteEventQueueWithAnyData<LargeEventA, LargeEventB>("With AnyData, large data", 100, 1000 * 100, 100);
|
||||
}
|
||||
|
||||
|
@ -22,6 +22,26 @@
|
||||
|
||||
namespace {
|
||||
|
||||
template <typename T>
|
||||
bool isWithinAnyData(const T & anyData, const void * address)
|
||||
{
|
||||
return (std::ptrdiff_t)address >= (std::ptrdiff_t)&anyData
|
||||
&& (std::ptrdiff_t)address < (std::ptrdiff_t)&anyData + (std::ptrdiff_t)sizeof(T)
|
||||
;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
bool isWithinAnyData(const T & anyData)
|
||||
{
|
||||
return isWithinAnyData(anyData, anyData.getAddress());
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
bool isLargeAnyData(const T & anyData)
|
||||
{
|
||||
return ! isWithinAnyData(anyData);
|
||||
}
|
||||
|
||||
TEST_CASE("AnyData, maxSizeOf")
|
||||
{
|
||||
REQUIRE(eventpp::maxSizeOf<
|
||||
@ -40,60 +60,126 @@ TEST_CASE("AnyData, maxSizeOf")
|
||||
);
|
||||
}
|
||||
|
||||
TEST_CASE("AnyData, default")
|
||||
struct LargeDataBase
|
||||
{
|
||||
using Data = eventpp::AnyData<64>;
|
||||
eventpp::EventQueue<int, void (const Data &)> queue;
|
||||
queue.appendListener(3, [](const Data & value) {
|
||||
REQUIRE(value.isType<int>());
|
||||
REQUIRE((long)value == 5);
|
||||
REQUIRE(value.get<long>() == 5);
|
||||
});
|
||||
queue.enqueue(3, 5);
|
||||
queue.process();
|
||||
std::array<void *, 100> largeData;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
bool isLargeDataBase()
|
||||
{
|
||||
return sizeof(T) >= sizeof(LargeDataBase);
|
||||
}
|
||||
|
||||
TEST_CASE("AnyData, unique_ptr")
|
||||
struct DataUniquePtr
|
||||
{
|
||||
using Ptr = std::unique_ptr<int>;
|
||||
using Data = eventpp::AnyData<sizeof(Ptr)>;
|
||||
Data data(Ptr(new int(5)));
|
||||
REQUIRE(data.isType<Ptr>());
|
||||
REQUIRE(*data.get<Ptr>() == 5);
|
||||
Data data2(Ptr(new int(5)));
|
||||
REQUIRE(data2.isType<Ptr>());
|
||||
REQUIRE(*data2.get<Ptr>() == 5);
|
||||
Data data3(Ptr(new int(8)));
|
||||
REQUIRE(data3.isType<Ptr>());
|
||||
REQUIRE(*data3.get<Ptr>() == 8);
|
||||
std::unique_ptr<std::string> ptr;
|
||||
};
|
||||
|
||||
struct LargeDataUniquePtr : DataUniquePtr, LargeDataBase {};
|
||||
|
||||
TEMPLATE_TEST_CASE("AnyData, DataUniquePtr", "", DataUniquePtr, LargeDataUniquePtr)
|
||||
{
|
||||
using Type = TestType;
|
||||
using MyAnyData = eventpp::AnyData<sizeof(DataUniquePtr)>;
|
||||
Type data {};
|
||||
data.ptr = std::unique_ptr<std::string>(new std::string("Hello"));
|
||||
REQUIRE(data.ptr);
|
||||
MyAnyData anyData { std::move(data) };
|
||||
REQUIRE(anyData.isType<Type>());
|
||||
REQUIRE(! data.ptr);
|
||||
REQUIRE(isLargeAnyData(anyData) == isLargeDataBase<Type>());
|
||||
REQUIRE(*anyData.get<DataUniquePtr>().ptr == "Hello");
|
||||
REQUIRE(*anyData.get<Type>().ptr == "Hello");
|
||||
}
|
||||
|
||||
TEST_CASE("AnyData, shared_ptr")
|
||||
struct DataSharedPtr
|
||||
{
|
||||
using Ptr = std::shared_ptr<int>;
|
||||
using Data = eventpp::AnyData<sizeof(Ptr)>;
|
||||
Ptr ptr(std::make_shared<int>(8));
|
||||
REQUIRE(ptr.use_count() == 1);
|
||||
{
|
||||
Data data(ptr);
|
||||
REQUIRE(ptr.use_count() == 2);
|
||||
REQUIRE(data.isType<Ptr>());
|
||||
REQUIRE(*data.get<Ptr>() == 8);
|
||||
Data data2(ptr);
|
||||
REQUIRE(ptr.use_count() == 3);
|
||||
REQUIRE(data2.isType<Ptr>());
|
||||
REQUIRE(*data2.get<Ptr>() == 8);
|
||||
REQUIRE(*data.get<Ptr>() == 8);
|
||||
REQUIRE(*ptr == 8);
|
||||
std::shared_ptr<std::string> ptr;
|
||||
};
|
||||
|
||||
*ptr = 5;
|
||||
REQUIRE(*data2.get<Ptr>() == 5);
|
||||
REQUIRE(*data.get<Ptr>() == 5);
|
||||
REQUIRE(*ptr == 5);
|
||||
struct LargeDataSharedPtr : DataSharedPtr, LargeDataBase {};
|
||||
|
||||
REQUIRE(ptr.use_count() == 3);
|
||||
TEMPLATE_TEST_CASE("AnyData, DataSharedPtr", "", DataSharedPtr, LargeDataSharedPtr)
|
||||
{
|
||||
using Type = TestType;
|
||||
using MyAnyData = eventpp::AnyData<sizeof(DataSharedPtr)>;
|
||||
Type data {};
|
||||
data.ptr = std::make_shared<std::string>("Hello");
|
||||
REQUIRE(data.ptr.use_count() == 1);
|
||||
SECTION("copy") {
|
||||
MyAnyData anyData { data };
|
||||
REQUIRE(anyData.isType<Type>());
|
||||
REQUIRE(data.ptr.use_count() == 2);
|
||||
REQUIRE(anyData.get<DataSharedPtr>().ptr.use_count() == 2);
|
||||
REQUIRE(isLargeAnyData(anyData) == isLargeDataBase<Type>());
|
||||
REQUIRE(*anyData.get<DataSharedPtr>().ptr == "Hello");
|
||||
REQUIRE(*anyData.get<Type>().ptr == "Hello");
|
||||
*data.ptr = "world";
|
||||
REQUIRE(*anyData.get<DataSharedPtr>().ptr == "world");
|
||||
REQUIRE(*anyData.get<Type>().ptr == "world");
|
||||
}
|
||||
REQUIRE(ptr.use_count() == 1);
|
||||
SECTION("move") {
|
||||
MyAnyData anyData { std::move(data) };
|
||||
REQUIRE(anyData.isType<Type>());
|
||||
REQUIRE(data.ptr.use_count() == 0);
|
||||
REQUIRE(anyData.get<DataSharedPtr>().ptr.use_count() == 1);
|
||||
REQUIRE(isLargeAnyData(anyData) == isLargeDataBase<Type>());
|
||||
REQUIRE(*anyData.get<DataSharedPtr>().ptr == "Hello");
|
||||
REQUIRE(*anyData.get<Type>().ptr == "Hello");
|
||||
}
|
||||
}
|
||||
|
||||
struct LifeCounter
|
||||
{
|
||||
int ctors;
|
||||
};
|
||||
|
||||
struct DataLifeCounter
|
||||
{
|
||||
explicit DataLifeCounter(LifeCounter * counter) : counter(counter) {
|
||||
++counter->ctors;
|
||||
}
|
||||
|
||||
DataLifeCounter(const DataLifeCounter & other) : counter(other.counter) {
|
||||
++counter->ctors;
|
||||
}
|
||||
|
||||
DataLifeCounter(DataLifeCounter && other) : counter(other.counter) {
|
||||
++counter->ctors;
|
||||
}
|
||||
|
||||
~DataLifeCounter() {
|
||||
--counter->ctors;
|
||||
}
|
||||
|
||||
LifeCounter * counter;
|
||||
};
|
||||
|
||||
struct LargeDataLifeCounter : DataLifeCounter, LargeDataBase
|
||||
{
|
||||
explicit LargeDataLifeCounter(LifeCounter * counter) : DataLifeCounter(counter), LargeDataBase() {
|
||||
}
|
||||
};
|
||||
|
||||
TEMPLATE_TEST_CASE("AnyData, DataLifeCounter", "", DataLifeCounter, LargeDataLifeCounter)
|
||||
{
|
||||
using Type = TestType;
|
||||
using MyAnyData = eventpp::AnyData<sizeof(DataLifeCounter)>;
|
||||
LifeCounter lifeCounter {};
|
||||
REQUIRE(lifeCounter.ctors == 0);
|
||||
{
|
||||
Type data { &lifeCounter };
|
||||
REQUIRE(lifeCounter.ctors == 1);
|
||||
{
|
||||
MyAnyData anyData { data };
|
||||
REQUIRE(anyData.isType<Type>());
|
||||
REQUIRE(lifeCounter.ctors == 2);
|
||||
REQUIRE(isLargeAnyData(anyData) == isLargeDataBase<Type>());
|
||||
}
|
||||
REQUIRE(lifeCounter.ctors == 1);
|
||||
}
|
||||
REQUIRE(lifeCounter.ctors == 0);
|
||||
}
|
||||
|
||||
enum class EventType {
|
||||
@ -122,6 +208,17 @@ struct EventMouse : Event {
|
||||
EventMouse(const int x, const int y) : Event(EventType::mouse), x(x), y(y) {
|
||||
}
|
||||
};
|
||||
|
||||
struct LargeEventKey : EventKey, LargeDataBase {
|
||||
explicit LargeEventKey(const int key) : EventKey(key), LargeDataBase() {
|
||||
}
|
||||
};
|
||||
|
||||
struct LargeEventMouse : EventMouse, LargeDataBase {
|
||||
explicit LargeEventMouse(const int x, const int y) : EventMouse(x, y), LargeDataBase() {
|
||||
}
|
||||
};
|
||||
|
||||
constexpr std::size_t eventMaxSize = eventpp::maxSizeOf<
|
||||
Event, EventKey, EventMouse, std::string
|
||||
>();
|
||||
@ -164,8 +261,8 @@ TEST_CASE("AnyData, Policies")
|
||||
expectedKey = 8;
|
||||
expectedX = 12345678;
|
||||
expectedY = 9876532;
|
||||
queue.enqueue(EventType::mouse, EventMouse(12345678, 9876532));
|
||||
queue.enqueue(EventType::key, EventKey(8));
|
||||
queue.enqueue(EventType::mouse, LargeEventMouse(12345678, 9876532));
|
||||
queue.enqueue(EventType::key, LargeEventKey(8));
|
||||
queue.process();
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user