From 6c3fe7899ba70ab12f83ff19f3f9455751a76e56 Mon Sep 17 00:00:00 2001 From: wqking Date: Fri, 5 May 2023 08:59:28 +0800 Subject: [PATCH] Removed the need of Callback policy in AnyData from tutorial and document --- doc/anydata.md | 102 +++++++++------------- tests/benchmark/b8_eventqueue_anydata.cpp | 5 +- tests/tutorial/tutorial_anydata.cpp | 14 +-- tests/unittest/test_anydata.cpp | 9 +- 4 files changed, 47 insertions(+), 83 deletions(-) diff --git a/doc/anydata.md b/doc/anydata.md index e081345..89928bf 100644 --- a/doc/anydata.md +++ b/doc/anydata.md @@ -3,19 +3,18 @@ ## Table Of Contents - [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) + - [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](#use-anydata-in-eventqueue) + - [get](#get) + - [getAddress](#getaddress) + - [isType](#istype) + - [Global function](#global-function) + - [maxSizeOf](#maxsizeof) + - [Tutorial](#tutorial) ## Description @@ -116,7 +115,7 @@ One solution is to use a small object pool to reuse the allocated objects, anoth Now with `AnyData`, we can eliminate the usage of shared pointer and heap allocation, for example, ```c++ -using Queue = eventpp::EventQueue &), SomePolicies>; +using Queue = eventpp::EventQueue &)>; Queue eventQueue; eventQueue.enqueue(EventType::key, KeyEvent(123)); eventQueue.enqueue(EventType::mouse, MouseEvent(100, 200)); @@ -138,15 +137,36 @@ class AnyData; `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. -### Use AnyData in EventQueue, the simplest but not recommend way +### Use AnyData in EventQueue `AnyData` can be used as the callback arguments in EventQueue. ```c++ eventpp::EventQueue &)> queue; +queue.appendListener(EventType::key, [](const Event & e) { + std::cout << "Received KeyEvent, key=" << static_cast(e).getKey() << std::endl; +}); ``` -In such form, the listener function prototype must be `void (const EventType, const eventpp::AnyData &)`, for example, +Since `AnyData` can convert to any types automatically, here the listener functions can receive `const Event &` instead of `AnyData`. It looks more nature than the "not recommend" method. 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, + +```c++ +eventpp::EventQueue &)> queue; +queue.appendListener(EventType::key, [](const EventType type, const void * e) { + assert(type == EventType::key); + std::cout << "Received KeyEvent, key=" << static_cast(e)->getKey() << std::endl; +}); +queue.appendListener(EventType::text, [](const EventType type, const void * e) { + assert(type == EventType::text); + std::cout << "Received text event, text=" << *static_cast(e) << std::endl; +}); +queue.enqueue(EventType::key, KeyEvent(255)); +queue.enqueue(EventType::text, std::string("This is a text")); +queue.process(); +``` + +The listener argument can also be `AnyData` directly, for example, ```c++ queue.appendListener(EventType::key, [](const EventType type, const eventpp::AnyData & e) { @@ -154,7 +174,7 @@ queue.appendListener(EventType::key, [](const EventType type, const eventpp::Any }); ``` -Then every listener function must receive `AnyData` as an argument. This is really bad idea 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. +I would not recommend it. It is really bad idea 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. #### get @@ -183,40 +203,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()` will return true, but `isType()` will return false. -### Use AnyData in EventQueue, the recommend way - -```c++ -struct Policies { - using Callback = std::function; -}; -eventpp::EventQueue &, Policies)> queue; -queue.appendListener(EventType::key, [](const Event & e) { - std::cout << "Received KeyEvent, key=" << static_cast(e).getKey() << std::endl; -}); -``` - -We specify the `Callback` policy as `std::function`, and pass the policy to `EventQueue`. Now the listener functions can receive `const Event &` instead of `AnyData`. It looks more nature than the "not recommend" method. -The magic is, `AnyData` can convert to any types automatically. So 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, - -```c++ -struct Policies { - using Callback = std::function; -}; -eventpp::EventQueue &), Policies> queue; -queue.appendListener(EventType::key, [](const EventType type, const void * e) { - assert(type == EventType::key); - std::cout << "Received KeyEvent, key=" << static_cast(e)->getKey() << std::endl; -}); -queue.appendListener(EventType::text, [](const EventType type, const void * e) { - assert(type == EventType::text); - std::cout << "Received text event, text=" << *static_cast(e) << std::endl; -}); -queue.enqueue(EventType::key, KeyEvent(255)); -queue.enqueue(EventType::text, std::string("This is a text")); -queue.process(); -``` - ## Global function ### maxSizeOf @@ -240,17 +226,9 @@ TEST_CASE("AnyData tutorial 1, basic") { std::cout << std::endl << "AnyData tutorial 1, basic" << std::endl; - // Define the Policies struct, set Callback as std::function, - // then the listener can have prototype as `void (const Event &)`. - // If we don't customize the Callback, the listener prototype have to be - // `void (const eventpp::AnyData &)`. - // The argument is `const Event &` because all events we send in this tutorial are derived from Event. - struct Policies { - using Callback = std::function; - }; - // Construct EventQueue with Policies. Here we use `const eventpp::AnyData &` as the - // callback argument. - eventpp::EventQueue &), Policies> queue; + eventpp::EventQueue &)> queue; + // Append a listener. Note the listener argument is `const Event &` which is different with the prototype in + // the queue definition. This works since `AnyData` can convert to any data type automatically. queue.appendListener(EventType::key, [](const Event & e) { std::cout << "Received KeyEvent, key=" << static_cast(e).getKey() << std::endl; diff --git a/tests/benchmark/b8_eventqueue_anydata.cpp b/tests/benchmark/b8_eventqueue_anydata.cpp index 8cfd01c..7c3295a 100644 --- a/tests/benchmark/b8_eventqueue_anydata.cpp +++ b/tests/benchmark/b8_eventqueue_anydata.cpp @@ -103,10 +103,7 @@ void doExecuteEventQueueWithAnyData( { constexpr std::size_t maxSize = sizeof(EventB) * 2; using Data = eventpp::AnyData; - struct Policies { - using Callback = std::function; - }; - using EQ = eventpp::EventQueue; + using EQ = eventpp::EventQueue; EQ eventQueue; if(listenerCount == 0) { diff --git a/tests/tutorial/tutorial_anydata.cpp b/tests/tutorial/tutorial_anydata.cpp index 927bc0a..e732d19 100644 --- a/tests/tutorial/tutorial_anydata.cpp +++ b/tests/tutorial/tutorial_anydata.cpp @@ -101,17 +101,9 @@ TEST_CASE("AnyData tutorial 1, basic") { std::cout << std::endl << "AnyData tutorial 1, basic" << std::endl; - // Define the Policies struct, set Callback as std::function, - // then the listener can have prototype as `void (const Event &)`. - // If we don't customize the Callback, the listener prototype have to be - // `void (const eventpp::AnyData &)`. - // The argument is `const Event &` because all events we send in this tutorial are derived from Event. - struct Policies { - using Callback = std::function; - }; - // Construct EventQueue with Policies. Here we use `const eventpp::AnyData &` as the - // callback argument. - eventpp::EventQueue &), Policies> queue; + eventpp::EventQueue &)> queue; + // Append a listener. Note the listener argument is `const Event &` which is different with the prototype in + // the queue definition. This works since `AnyData` can convert to any data type automatically. queue.appendListener(EventType::key, [](const Event & e) { std::cout << "Received KeyEvent, key=" << static_cast(e).getKey() << std::endl; diff --git a/tests/unittest/test_anydata.cpp b/tests/unittest/test_anydata.cpp index 265a1f8..a71c309 100644 --- a/tests/unittest/test_anydata.cpp +++ b/tests/unittest/test_anydata.cpp @@ -223,7 +223,7 @@ constexpr std::size_t eventMaxSize = eventpp::maxSizeOf< Event, EventKey, EventMouse, std::string >(); -TEST_CASE("AnyData, data") +TEST_CASE("AnyData, use AnyData as callback argument") { using Data = eventpp::AnyData; eventpp::EventQueue queue; @@ -236,13 +236,10 @@ TEST_CASE("AnyData, data") queue.process(); } -TEST_CASE("AnyData, Policies") +TEST_CASE("AnyData, use Event as callback argument") { using Data = eventpp::AnyData; - struct Policies { - using Callback = std::function; - }; - eventpp::EventQueue queue; + eventpp::EventQueue queue; int expectedKey; int expectedX; int expectedY;