mirror of
https://github.com/wqking/eventpp.git
synced 2024-12-25 23:30:49 +08:00
Update AnyData tutorial and document
This commit is contained in:
parent
260a82911b
commit
9231cbb93b
273
doc/anydata.md
273
doc/anydata.md
@ -30,74 +30,74 @@ For example, assume we have the event class hierarchy (we will use these event c
|
||||
```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
|
||||
text,
|
||||
// for MouseEvent
|
||||
mouse,
|
||||
// for KeyEvent
|
||||
key,
|
||||
// for MessageEvent
|
||||
message,
|
||||
// For a simple std::string event which is not derived from Event
|
||||
text,
|
||||
};
|
||||
|
||||
class Event
|
||||
{
|
||||
public:
|
||||
Event() {
|
||||
}
|
||||
Event() {
|
||||
}
|
||||
};
|
||||
|
||||
class MouseEvent : public Event
|
||||
{
|
||||
public:
|
||||
MouseEvent(const int x, const int y)
|
||||
: x(x), y(y)
|
||||
{
|
||||
}
|
||||
MouseEvent(const int x, const int y)
|
||||
: x(x), y(y)
|
||||
{
|
||||
}
|
||||
|
||||
int getX() const { return x; }
|
||||
int getY() const { return y; }
|
||||
int getX() const { return x; }
|
||||
int getY() const { return y; }
|
||||
|
||||
private:
|
||||
int x;
|
||||
int y;
|
||||
int x;
|
||||
int y;
|
||||
};
|
||||
|
||||
class KeyEvent : public Event
|
||||
{
|
||||
public:
|
||||
explicit KeyEvent(const int key)
|
||||
: key(key)
|
||||
{
|
||||
}
|
||||
explicit KeyEvent(const int key)
|
||||
: key(key)
|
||||
{
|
||||
}
|
||||
|
||||
int getKey() const { return key; }
|
||||
int getKey() const { return key; }
|
||||
|
||||
private:
|
||||
int key;
|
||||
int key;
|
||||
};
|
||||
|
||||
class MessageEvent : public Event
|
||||
{
|
||||
public:
|
||||
explicit MessageEvent(const std::string & message)
|
||||
: message(message) {
|
||||
}
|
||||
explicit MessageEvent(const std::string & message)
|
||||
: message(message) {
|
||||
}
|
||||
|
||||
std::string getMessage() const { return message; }
|
||||
std::string getMessage() const { return message; }
|
||||
|
||||
private:
|
||||
std::string message;
|
||||
std::string message;
|
||||
};
|
||||
|
||||
// eventMaxSize is the maximum size of all possible types to use in AnyData, it's used to construct AnyData.
|
||||
constexpr std::size_t eventMaxSize = eventpp::maxSizeOf<
|
||||
Event,
|
||||
KeyEvent,
|
||||
MouseEvent,
|
||||
MessageEvent,
|
||||
std::string
|
||||
>();
|
||||
Event,
|
||||
KeyEvent,
|
||||
MouseEvent,
|
||||
MessageEvent,
|
||||
std::string
|
||||
>();
|
||||
```
|
||||
|
||||
Without using `AnyData`, we need to use shared pointer to put an event into an `EventQueue`, for example,
|
||||
@ -144,22 +144,30 @@ class AnyData;
|
||||
```c++
|
||||
eventpp::EventQueue<EventType, void (const EventType, const eventpp::AnyData<eventMaxSize> &)> queue;
|
||||
queue.appendListener(EventType::key, [](const Event & e) {
|
||||
std::cout << "Received KeyEvent, key=" << static_cast<const KeyEvent &>(e).getKey() << std::endl;
|
||||
std::cout << "Received KeyEvent, key=" << static_cast<const KeyEvent &>(e).getKey() << std::endl;
|
||||
});
|
||||
```
|
||||
|
||||
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.
|
||||
Even better, we can use the concrete type as the argument directly, for example,
|
||||
```c++
|
||||
queue.appendListener(EventType::key, [](const KeyEvent & e) {
|
||||
std::cout << "Received KeyEvent, key=" << e.getKey() << std::endl;
|
||||
});
|
||||
```
|
||||
Note such listener should only receive the specified type, here is `KeyEvent`. If it receives other data type, it will crash your program.
|
||||
|
||||
`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<EventType, void (const EventType, const eventpp::AnyData<eventMaxSize> &)> queue;
|
||||
queue.appendListener(EventType::key, [](const EventType type, const void * e) {
|
||||
assert(type == EventType::key);
|
||||
std::cout << "Received KeyEvent, key=" << static_cast<const KeyEvent *>(e)->getKey() << std::endl;
|
||||
assert(type == EventType::key);
|
||||
std::cout << "Received KeyEvent, key=" << static_cast<const KeyEvent *>(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<const std::string *>(e) << std::endl;
|
||||
assert(type == EventType::text);
|
||||
std::cout << "Received text event, text=" << *static_cast<const std::string *>(e) << std::endl;
|
||||
});
|
||||
queue.enqueue(EventType::key, KeyEvent(255));
|
||||
queue.enqueue(EventType::text, std::string("This is a text"));
|
||||
@ -170,7 +178,7 @@ The listener argument can also be `AnyData` directly, for example,
|
||||
|
||||
```c++
|
||||
queue.appendListener(EventType::key, [](const EventType type, const eventpp::AnyData<eventMaxSize> & e) {
|
||||
std::cout << "Received KeyEvent, key=" << e.get<KeyEvent>().getKey() << std::endl;
|
||||
std::cout << "Received KeyEvent, key=" << e.get<KeyEvent>().getKey() << std::endl;
|
||||
});
|
||||
```
|
||||
|
||||
@ -222,104 +230,115 @@ maxSizeOf<KeyEvent, MouseEvent, int, double>();
|
||||
Below is the tutorial code. The complete code can be found in `tests/tutorial/tutorial_anydata.cpp`
|
||||
|
||||
```c++
|
||||
void onMessageEvent(const MessageEvent & e)
|
||||
{
|
||||
std::cout << "Received MessageEvent in free function, message="
|
||||
<< e.getMessage() << std::endl;
|
||||
}
|
||||
|
||||
TEST_CASE("AnyData tutorial 1, basic")
|
||||
{
|
||||
std::cout << std::endl << "AnyData tutorial 1, basic" << std::endl;
|
||||
std::cout << std::endl << "AnyData tutorial 1, basic" << std::endl;
|
||||
|
||||
eventpp::EventQueue<EventType, void (const eventpp::AnyData<eventMaxSize> &)> 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<const KeyEvent &>(e).getKey() << std::endl;
|
||||
});
|
||||
queue.appendListener(EventType::mouse, [](const Event & e) {
|
||||
std::cout << "Received MouseEvent, x=" << static_cast<const MouseEvent &>(e).getX()
|
||||
<< " y=" << static_cast<const MouseEvent &>(e).getY() << std::endl;
|
||||
});
|
||||
queue.appendListener(EventType::message, [](const Event & e) {
|
||||
std::cout << "Received MessageEvent, message="
|
||||
<< static_cast<const MessageEvent &>(e).getMessage() << std::endl;
|
||||
});
|
||||
// Put events into the queue. Any data type, such as KeyEvent, MouseEvent, can be put
|
||||
// as long as the data size doesn't exceed eventMaxSize.
|
||||
queue.enqueue(EventType::key, KeyEvent(255));
|
||||
queue.enqueue(EventType::mouse, MouseEvent(3, 5));
|
||||
queue.enqueue(EventType::message, MessageEvent("Hello, AnyData"));
|
||||
queue.process();
|
||||
eventpp::EventQueue<EventType, void (const eventpp::AnyData<eventMaxSize> &)> 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<const KeyEvent &>(e).getKey() << std::endl;
|
||||
});
|
||||
queue.appendListener(EventType::mouse, [](const Event & e) {
|
||||
std::cout << "Received MouseEvent, x=" << static_cast<const MouseEvent &>(e).getX()
|
||||
<< " y=" << static_cast<const MouseEvent &>(e).getY() << std::endl;
|
||||
});
|
||||
// Even more convenient, the argument type can be the concrete class such as MessageEvent,
|
||||
// but be sure the listener only receive MessageEvent. If it also receives MouseEvent,
|
||||
// we can expect crash.
|
||||
queue.appendListener(EventType::message, [](const MessageEvent & e) {
|
||||
std::cout << "Received MessageEvent, message="
|
||||
<< e.getMessage() << std::endl;
|
||||
});
|
||||
// Not only lambda, we can also use free function, or member function as the listener.
|
||||
queue.appendListener(EventType::message, &onMessageEvent);
|
||||
// Put events into the queue. Any data type, such as KeyEvent, MouseEvent, can be put
|
||||
// as long as the data size doesn't exceed eventMaxSize.
|
||||
queue.enqueue(EventType::key, KeyEvent(255));
|
||||
queue.enqueue(EventType::mouse, MouseEvent(3, 5));
|
||||
queue.enqueue(EventType::message, MessageEvent("Hello, AnyData"));
|
||||
queue.process();
|
||||
}
|
||||
|
||||
TEST_CASE("AnyData tutorial 2, unrelated data")
|
||||
{
|
||||
std::cout << std::endl << "AnyData tutorial 2, unrelated data" << std::endl;
|
||||
std::cout << std::endl << "AnyData tutorial 2, unrelated data" << std::endl;
|
||||
|
||||
// It's possible to send event with data that doesn't have the same base class, such as Event.
|
||||
// To do so, the listener prototype must be `const void *` instead of `const Event &` in previous tutorial.
|
||||
struct Policies {
|
||||
using Callback = std::function<void (const EventType, const void *)>;
|
||||
};
|
||||
eventpp::EventQueue<
|
||||
EventType,
|
||||
void (const EventType, const eventpp::AnyData<eventMaxSize> &),
|
||||
Policies> queue;
|
||||
queue.appendListener(EventType::key, [](const EventType type, const void * e) {
|
||||
REQUIRE(type == EventType::key);
|
||||
std::cout << "Received KeyEvent, key=" << static_cast<const KeyEvent *>(e)->getKey() << std::endl;
|
||||
});
|
||||
queue.appendListener(EventType::mouse, [](const EventType type, const void * e) {
|
||||
REQUIRE(type == EventType::mouse);
|
||||
std::cout << "Received MouseEvent, x=" << static_cast<const MouseEvent *>(e)->getX()
|
||||
<< " y=" << static_cast<const MouseEvent *>(e)->getY() << std::endl;
|
||||
});
|
||||
queue.appendListener(EventType::message, [](const EventType type, const void * e) {
|
||||
REQUIRE(type == EventType::message);
|
||||
std::cout << "Received MessageEvent, message="
|
||||
<< static_cast<const MessageEvent *>(e)->getMessage() << std::endl;
|
||||
});
|
||||
queue.appendListener(EventType::text, [](const EventType type, const void * e) {
|
||||
REQUIRE(type == EventType::text);
|
||||
std::cout << "Received text event, text=" << *static_cast<const std::string *>(e) << std::endl;
|
||||
});
|
||||
// Send events
|
||||
queue.enqueue(EventType::key, KeyEvent(255));
|
||||
queue.enqueue(EventType::mouse, MouseEvent(3, 5));
|
||||
queue.enqueue(EventType::message, MessageEvent("Hello, AnyData"));
|
||||
// Send a std::string as the event data which doesn't derive from Event.
|
||||
queue.enqueue(EventType::text, std::string("This is a text"));
|
||||
queue.process();
|
||||
// It's possible to send event with data that doesn't have the same base class, such as Event.
|
||||
// To do so, the listener prototype must be `const void *` instead of `const Event &` in previous tutorial.
|
||||
struct Policies {
|
||||
using Callback = std::function<void (const EventType, const void *)>;
|
||||
};
|
||||
eventpp::EventQueue<
|
||||
EventType,
|
||||
void (const EventType, const eventpp::AnyData<eventMaxSize> &),
|
||||
Policies> queue;
|
||||
queue.appendListener(EventType::key, [](const EventType type, const void * e) {
|
||||
REQUIRE(type == EventType::key);
|
||||
std::cout << "Received KeyEvent, key=" << static_cast<const KeyEvent *>(e)->getKey() << std::endl;
|
||||
});
|
||||
queue.appendListener(EventType::mouse, [](const EventType type, const void * e) {
|
||||
REQUIRE(type == EventType::mouse);
|
||||
std::cout << "Received MouseEvent, x=" << static_cast<const MouseEvent *>(e)->getX()
|
||||
<< " y=" << static_cast<const MouseEvent *>(e)->getY() << std::endl;
|
||||
});
|
||||
queue.appendListener(EventType::message, [](const EventType type, const void * e) {
|
||||
REQUIRE(type == EventType::message);
|
||||
std::cout << "Received MessageEvent, message="
|
||||
<< static_cast<const MessageEvent *>(e)->getMessage() << std::endl;
|
||||
});
|
||||
queue.appendListener(EventType::text, [](const EventType type, const void * e) {
|
||||
REQUIRE(type == EventType::text);
|
||||
std::cout << "Received text event, text=" << *static_cast<const std::string *>(e) << std::endl;
|
||||
});
|
||||
// Send events
|
||||
queue.enqueue(EventType::key, KeyEvent(255));
|
||||
queue.enqueue(EventType::mouse, MouseEvent(3, 5));
|
||||
queue.enqueue(EventType::message, MessageEvent("Hello, AnyData"));
|
||||
// Send a std::string as the event data which doesn't derive from Event.
|
||||
queue.enqueue(EventType::text, std::string("This is a text"));
|
||||
queue.process();
|
||||
}
|
||||
|
||||
TEST_CASE("AnyData tutorial 3, use AnyData in listener")
|
||||
{
|
||||
std::cout << std::endl << "AnyData tutorial 3, use AnyData in listener" << std::endl;
|
||||
std::cout << std::endl << "AnyData tutorial 3, use AnyData in listener" << std::endl;
|
||||
|
||||
using MyData = eventpp::AnyData<eventMaxSize>;
|
||||
eventpp::EventQueue<EventType, void (const EventType, const MyData &)> queue;
|
||||
queue.appendListener(EventType::key, [](const EventType type, const MyData & e) {
|
||||
REQUIRE(type == EventType::key);
|
||||
REQUIRE(e.isType<KeyEvent>());
|
||||
std::cout << "Received KeyEvent, key=" << e.get<KeyEvent>().getKey() << std::endl;
|
||||
});
|
||||
queue.appendListener(EventType::mouse, [](const EventType type, const MyData & e) {
|
||||
REQUIRE(type == EventType::mouse);
|
||||
REQUIRE(e.isType<MouseEvent>());
|
||||
std::cout << "Received MouseEvent, x=" << e.get<MouseEvent>().getX()
|
||||
<< " y=" << e.get<MouseEvent>().getY() << std::endl;
|
||||
});
|
||||
queue.appendListener(EventType::message, [](const EventType type, const MyData & e) {
|
||||
REQUIRE(type == EventType::message);
|
||||
REQUIRE(e.isType<MessageEvent>());
|
||||
std::cout << "Received MessageEvent, message=" << e.get<MessageEvent>().getMessage() << std::endl;
|
||||
});
|
||||
queue.appendListener(EventType::text, [](const EventType type, const MyData & e) {
|
||||
REQUIRE(type == EventType::text);
|
||||
REQUIRE(e.isType<std::string>());
|
||||
std::cout << "Received text event, text=" << e.get<std::string>() << std::endl;
|
||||
});
|
||||
queue.enqueue(EventType::key, KeyEvent(255));
|
||||
queue.enqueue(EventType::mouse, MouseEvent(3, 5));
|
||||
queue.enqueue(EventType::message, MessageEvent("Hello, AnyData"));
|
||||
queue.enqueue(EventType::text, std::string("This is a text"));
|
||||
queue.process();
|
||||
using MyData = eventpp::AnyData<eventMaxSize>;
|
||||
eventpp::EventQueue<EventType, void (const EventType, const MyData &)> queue;
|
||||
queue.appendListener(EventType::key, [](const EventType type, const MyData & e) {
|
||||
REQUIRE(type == EventType::key);
|
||||
REQUIRE(e.isType<KeyEvent>());
|
||||
std::cout << "Received KeyEvent, key=" << e.get<KeyEvent>().getKey() << std::endl;
|
||||
});
|
||||
queue.appendListener(EventType::mouse, [](const EventType type, const MyData & e) {
|
||||
REQUIRE(type == EventType::mouse);
|
||||
REQUIRE(e.isType<MouseEvent>());
|
||||
std::cout << "Received MouseEvent, x=" << e.get<MouseEvent>().getX()
|
||||
<< " y=" << e.get<MouseEvent>().getY() << std::endl;
|
||||
});
|
||||
queue.appendListener(EventType::message, [](const EventType type, const MyData & e) {
|
||||
REQUIRE(type == EventType::message);
|
||||
REQUIRE(e.isType<MessageEvent>());
|
||||
std::cout << "Received MessageEvent, message=" << e.get<MessageEvent>().getMessage() << std::endl;
|
||||
});
|
||||
queue.appendListener(EventType::text, [](const EventType type, const MyData & e) {
|
||||
REQUIRE(type == EventType::text);
|
||||
REQUIRE(e.isType<std::string>());
|
||||
std::cout << "Received text event, text=" << e.get<std::string>() << std::endl;
|
||||
});
|
||||
queue.enqueue(EventType::key, KeyEvent(255));
|
||||
queue.enqueue(EventType::mouse, MouseEvent(3, 5));
|
||||
queue.enqueue(EventType::message, MessageEvent("Hello, AnyData"));
|
||||
queue.enqueue(EventType::text, std::string("This is a text"));
|
||||
queue.process();
|
||||
}
|
||||
```
|
||||
|
@ -97,6 +97,12 @@ constexpr std::size_t eventMaxSize = eventpp::maxSizeOf<
|
||||
std::string
|
||||
>();
|
||||
|
||||
void onMessageEvent(const MessageEvent & e)
|
||||
{
|
||||
std::cout << "Received MessageEvent in free function, message="
|
||||
<< e.getMessage() << std::endl;
|
||||
}
|
||||
|
||||
TEST_CASE("AnyData tutorial 1, basic")
|
||||
{
|
||||
std::cout << std::endl << "AnyData tutorial 1, basic" << std::endl;
|
||||
@ -112,10 +118,15 @@ TEST_CASE("AnyData tutorial 1, basic")
|
||||
std::cout << "Received MouseEvent, x=" << static_cast<const MouseEvent &>(e).getX()
|
||||
<< " y=" << static_cast<const MouseEvent &>(e).getY() << std::endl;
|
||||
});
|
||||
queue.appendListener(EventType::message, [](const Event & e) {
|
||||
// Even more convenient, the argument type can be the concrete class such as MessageEvent,
|
||||
// but be sure the listener only receive MessageEvent. If it also receives MouseEvent,
|
||||
// we can expect crash.
|
||||
queue.appendListener(EventType::message, [](const MessageEvent & e) {
|
||||
std::cout << "Received MessageEvent, message="
|
||||
<< static_cast<const MessageEvent &>(e).getMessage() << std::endl;
|
||||
<< e.getMessage() << std::endl;
|
||||
});
|
||||
// Not only lambda, we can also use free function, or member function as the listener.
|
||||
queue.appendListener(EventType::message, &onMessageEvent);
|
||||
// Put events into the queue. Any data type, such as KeyEvent, MouseEvent, can be put
|
||||
// as long as the data size doesn't exceed eventMaxSize.
|
||||
queue.enqueue(EventType::key, KeyEvent(255));
|
||||
|
Loading…
x
Reference in New Issue
Block a user