1 | // SPDX-FileCopyrightText: 2016 Kitsune Ral <Kitsune-Ral@users.sf.net> |
2 | // SPDX-License-Identifier: LGPL-2.1-or-later |
3 | |
4 | #pragma once |
5 | |
6 | #include <Quotient/converters.h> |
7 | #include <Quotient/function_traits.h> |
8 | #include "single_key_value.h" |
9 | |
10 | namespace Quotient { |
11 | // === event_ptr_tt<> and basic type casting facilities === |
12 | |
13 | template <typename EventT> |
14 | using event_ptr_tt = std::unique_ptr<EventT>; |
15 | |
16 | template <typename EventT> |
17 | [[deprecated("Use std::to_address() instead" )]] |
18 | inline EventT* rawPtr(const event_ptr_tt<EventT>& ptr) |
19 | { |
20 | return ptr.get(); |
21 | } |
22 | |
23 | /// Unwrap a plain pointer and downcast it to the specified type |
24 | template <typename TargetEventT, typename EventT> |
25 | inline TargetEventT* weakPtrCast(const event_ptr_tt<EventT>& ptr) |
26 | { |
27 | return static_cast<TargetEventT*>(std::to_address(ptr)); |
28 | } |
29 | |
30 | // === Standard Matrix key names and basicEventJson() === |
31 | |
32 | constexpr inline auto TypeKey = "type"_ls ; |
33 | constexpr inline auto BodyKey = "body"_ls ; |
34 | constexpr inline auto ContentKey = "content"_ls ; |
35 | constexpr inline auto EventIdKey = "event_id"_ls ; |
36 | constexpr inline auto SenderKey = "sender"_ls ; |
37 | constexpr inline auto RoomIdKey = "room_id"_ls ; |
38 | constexpr inline auto UnsignedKey = "unsigned"_ls ; |
39 | constexpr inline auto RedactedCauseKey = "redacted_because"_ls ; |
40 | constexpr inline auto PrevContentKey = "prev_content"_ls ; |
41 | constexpr inline auto StateKeyKey = "state_key"_ls ; |
42 | [[deprecated("use TypeKey" )]] constexpr inline auto TypeKeyL = TypeKey; |
43 | [[deprecated("use BodyKey" )]] constexpr inline auto BodyKeyL = BodyKey; |
44 | [[deprecated("use ContentKey" )]] constexpr inline auto ContentKeyL = ContentKey; |
45 | [[deprecated("use EventIdKey" )]] constexpr inline auto EventIdKeyL = EventIdKey; |
46 | [[deprecated("use SenderKey" )]] constexpr inline auto SenderKeyL = SenderKey; |
47 | [[deprecated("use RoomIdKey" )]] constexpr inline auto RoomIdKeyL = RoomIdKey; |
48 | [[deprecated("use UnsignedKey" )]] constexpr inline auto UnsignedKeyL = UnsignedKey; |
49 | [[deprecated("use RedactedCauseKey" )]] constexpr inline auto RedactedCauseKeyL = RedactedCauseKey; |
50 | [[deprecated("use PrevContentKey" )]] constexpr inline auto PrevContentKeyL = PrevContentKey; |
51 | [[deprecated("use StateKeyKey" )]] constexpr inline auto StateKeyKeyL = StateKeyKey; |
52 | |
53 | using event_type_t = QLatin1String; |
54 | |
55 | // TODO: Remove in 0.8 |
56 | struct QUOTIENT_API EventTypeRegistry { |
57 | [[deprecated("event_type_t is a string since libQuotient 0.7, use it directly instead" )]] |
58 | static QString getMatrixType(event_type_t typeId); |
59 | |
60 | EventTypeRegistry() = delete; |
61 | ~EventTypeRegistry() = default; |
62 | Q_DISABLE_COPY_MOVE(EventTypeRegistry) |
63 | }; |
64 | |
65 | // === EventMetaType === |
66 | |
67 | class Event; |
68 | |
69 | // TODO: move over to std::derived_from<Event> once it's available everywhere |
70 | template <typename EventT, typename BaseEventT = Event> |
71 | concept EventClass = std::is_base_of_v<BaseEventT, EventT>; |
72 | |
73 | template <EventClass EventT> |
74 | bool is(const Event& e); |
75 | |
76 | //! \brief The base class for event metatypes |
77 | //! |
78 | //! You should not normally have to use this directly, unless you need to devise |
79 | //! a whole new kind of event metatypes. |
80 | class QUOTIENT_API AbstractEventMetaType { |
81 | public: |
82 | // The public fields here are const and are not to be changeable anyway. |
83 | // NOLINTBEGIN(misc-non-private-member-variables-in-classes) |
84 | const char* const className; ///< C++ class name this metatype is for |
85 | const AbstractEventMetaType* const baseType; |
86 | const event_type_t matrixId; |
87 | // NOLINTEND(misc-non-private-member-variables-in-classes) |
88 | |
89 | explicit AbstractEventMetaType(const char* className, |
90 | AbstractEventMetaType* nearestBase = nullptr, |
91 | const char* matrixId = nullptr) |
92 | : className(className), baseType(nearestBase), matrixId(matrixId) |
93 | { |
94 | if (nearestBase) |
95 | nearestBase->addDerived(newType: this); |
96 | } |
97 | |
98 | void addDerived(const AbstractEventMetaType* newType); |
99 | |
100 | virtual ~AbstractEventMetaType() = default; |
101 | |
102 | protected: |
103 | // Allow template specialisations to call into one another |
104 | template <class EventT> |
105 | friend class EventMetaType; |
106 | |
107 | // The returned value indicates whether a generic object has to be created |
108 | // on the top level when `event` is empty, instead of returning nullptr |
109 | virtual bool doLoadFrom(const QJsonObject& fullJson, const QString& type, |
110 | Event*& event) const = 0; |
111 | |
112 | private: |
113 | std::vector<const AbstractEventMetaType*> derivedTypes{}; |
114 | Q_DISABLE_COPY_MOVE(AbstractEventMetaType) |
115 | }; |
116 | |
117 | // Any event metatype is unique (note Q_DISABLE_COPY_MOVE above) so can be |
118 | // identified by its address |
119 | inline bool operator==(const AbstractEventMetaType& lhs, |
120 | const AbstractEventMetaType& rhs) |
121 | { |
122 | return &lhs == &rhs; |
123 | } |
124 | |
125 | //! \brief A family of event meta-types to load and match events |
126 | //! |
127 | //! TL;DR for the loadFrom() story: |
128 | //! - for base event types, use QUO_BASE_EVENT and, if you have additional |
129 | //! validation (e.g., JSON has to contain a certain key - see StateEvent |
130 | //! for a real example), define it in the static EventT::isValid() member |
131 | //! function accepting QJsonObject and returning bool. |
132 | //! - for leaf (specific) event types - simply use QUO_EVENT and it will do |
133 | //! everything necessary, including the TypeId definition. |
134 | //! \sa QUO_EVENT, QUO_BASE_EVENT |
135 | template <class EventT> |
136 | class QUOTIENT_API EventMetaType : public AbstractEventMetaType { |
137 | // Above: can't constrain EventT to be EventClass because it's incomplete |
138 | // at the point of EventMetaType<EventT> instantiation. |
139 | public: |
140 | using AbstractEventMetaType::AbstractEventMetaType; |
141 | |
142 | //! \brief Try to load an event from JSON, with dynamic type resolution |
143 | //! |
144 | //! The generic logic defined in this class template and invoked applies to |
145 | //! all event types defined in the library and boils down to the following: |
146 | //! 1. |
147 | //! a. If EventT has TypeId defined (which normally is a case of |
148 | //! all leaf - specific - event types, via QUO_EVENT macro) and |
149 | //! \p type doesn't exactly match it, nullptr is immediately returned. |
150 | //! b. In absence of TypeId, an event type is assumed to be a base; |
151 | //! its derivedTypes are examined, and this algorithm is applied |
152 | //! recursively on each. |
153 | //! 2. Optional validation: if EventT (or, due to the way inheritance works, |
154 | //! any of its base event types) has a static isValid() predicate and |
155 | //! the event JSON does not satisfy it, nullptr is immediately returned |
156 | //! to the upper level or to the loadFrom() caller. This is how existence |
157 | //! of `state_key` is checked in any type derived from StateEvent. |
158 | //! 3. If step 1b above returned non-nullptr, immediately return it. |
159 | //! 4. |
160 | //! a. If EventT::isValid() or EventT::TypeId (either, or both) exist and |
161 | //! are satisfied (see steps 1a and 2 above), an object of this type |
162 | //! is created from the passed JSON and returned. In case of a base |
163 | //! event type, this will be a generic (aka "unknown") event. |
164 | //! b. If neither exists, a generic event is only created and returned |
165 | //! when on the top level (i.e., outside of recursion into |
166 | //! derivedTypes); lower levels return nullptr instead and the type |
167 | //! lookup continues. The latter is a case of a derived base event |
168 | //! metatype (e.g. RoomEvent) called from its base event metatype |
169 | //! (i.e., Event). If no matching type derived from RoomEvent is found, |
170 | //! the nested lookup returns nullptr rather than a generic RoomEvent, |
171 | //! so that other types derived from Event could be examined. |
172 | event_ptr_tt<EventT> loadFrom(const QJsonObject& fullJson, |
173 | const QString& type) const |
174 | { |
175 | Event* event = nullptr; |
176 | const bool goodEnough = doLoadFrom(fullJson, type, event); |
177 | if (!event && goodEnough) |
178 | return event_ptr_tt<EventT>{ new EventT(fullJson) }; |
179 | return event_ptr_tt<EventT>{ static_cast<EventT*>(event) }; |
180 | } |
181 | |
182 | private: |
183 | bool doLoadFrom(const QJsonObject& fullJson, const QString& type, |
184 | Event*& event) const override |
185 | { |
186 | if constexpr (requires { EventT::TypeId; }) { |
187 | if (EventT::TypeId != type) |
188 | return false; |
189 | } else { |
190 | for (const auto& p : derivedTypes) { |
191 | p->doLoadFrom(fullJson, type, event); |
192 | if (event) { |
193 | Q_ASSERT(is<EventT>(*event)); |
194 | return false; |
195 | } |
196 | } |
197 | } |
198 | if constexpr (requires { EventT::isValid; }) { |
199 | if (!EventT::isValid(fullJson)) |
200 | return false; |
201 | } else if constexpr (!requires { EventT::TypeId; }) |
202 | return true; // Create a generic event object if on the top level |
203 | event = new EventT(fullJson); |
204 | return false; |
205 | } |
206 | }; |
207 | |
208 | // === Event creation facilities === |
209 | |
210 | //! \brief Create an event of arbitrary type from its arguments |
211 | //! |
212 | //! This should not be used to load events from JSON - use loadEvent() for that. |
213 | //! \sa loadEvent |
214 | template <EventClass EventT, typename... ArgTs> |
215 | inline event_ptr_tt<EventT> makeEvent(ArgTs&&... args) |
216 | { |
217 | return std::make_unique<EventT>(std::forward<ArgTs>(args)...); |
218 | } |
219 | |
220 | template <EventClass EventT> |
221 | constexpr const auto& mostSpecificMetaType() |
222 | { |
223 | if constexpr (requires { EventT::MetaType; }) |
224 | return EventT::MetaType; |
225 | else |
226 | return EventT::BaseMetaType; |
227 | } |
228 | |
229 | //! \brief Create an event with proper type from a JSON object |
230 | //! |
231 | //! Use this factory template to detect the type from the JSON object |
232 | //! contents (the detected event type should derive from the template |
233 | //! parameter type) and create an event object of that type. |
234 | template <EventClass EventT> |
235 | inline event_ptr_tt<EventT> loadEvent(const QJsonObject& fullJson) |
236 | { |
237 | return mostSpecificMetaType<EventT>().loadFrom( |
238 | fullJson, fullJson[TypeKey].toString()); |
239 | } |
240 | |
241 | //! \brief Create an event from a type string and content JSON |
242 | //! |
243 | //! Use this template to resolve the C++ type from the Matrix type string in |
244 | //! \p matrixType and create an event of that type by passing all parameters |
245 | //! to EventT::basicJson(). |
246 | template <EventClass EventT> |
247 | inline event_ptr_tt<EventT> loadEvent(const QString& matrixType, |
248 | const auto&... otherBasicJsonParams) |
249 | { |
250 | return mostSpecificMetaType<EventT>().loadFrom( |
251 | EventT::basicJson(matrixType, otherBasicJsonParams...), matrixType); |
252 | } |
253 | |
254 | template <EventClass EventT> |
255 | struct JsonConverter<event_ptr_tt<EventT>> |
256 | : JsonObjectUnpacker<event_ptr_tt<EventT>> { |
257 | // No dump() to avoid any ambiguity on whether a given export to JSON uses |
258 | // fullJson() or only contentJson() |
259 | using JsonObjectUnpacker<event_ptr_tt<EventT>>::load; |
260 | static auto load(const QJsonObject& jo) |
261 | { |
262 | return loadEvent<EventT>(jo); |
263 | } |
264 | }; |
265 | |
266 | // === Event === |
267 | |
268 | class QUOTIENT_API Event { |
269 | public: |
270 | static inline EventMetaType<Event> BaseMetaType { "Event" }; |
271 | virtual const AbstractEventMetaType& metaType() const |
272 | { |
273 | return BaseMetaType; |
274 | } |
275 | |
276 | Q_DISABLE_COPY(Event) |
277 | Event(Event&&) noexcept = default; |
278 | Event& operator=(Event&&) = delete; |
279 | virtual ~Event(); |
280 | |
281 | /// Make a minimal correct Matrix event JSON |
282 | static QJsonObject basicJson(const QString& matrixType, |
283 | const QJsonObject& content) |
284 | { |
285 | return { { TypeKey, matrixType }, { ContentKey, content } }; |
286 | } |
287 | |
288 | //! \brief Event Matrix type, as identified by its metatype object |
289 | //! |
290 | //! For event types that have defined C++ classes this will return the same |
291 | //! string as type(); for generic/unknown events it will contain |
292 | //! a descriptive/generic string defined by the respective base event type |
293 | //! (that can be empty). |
294 | //! \sa matrixType |
295 | [[deprecated("Use matrixType() to get type id stored in event JSON, " |
296 | "or metaType().matrixId if you (unlikely) need type id as " |
297 | "per the metatype system" )]] |
298 | auto type() const { return metaType().matrixId; } |
299 | |
300 | //! \brief Exact Matrix type stored in JSON |
301 | QString matrixType() const; |
302 | |
303 | template <EventClass EventT> |
304 | bool is() const |
305 | { |
306 | return Quotient::is<EventT>(*this); |
307 | } |
308 | |
309 | //! \brief Apply one of the visitors based on the actual event type |
310 | //! |
311 | //! This function uses function_traits template and is() to find the first |
312 | //! of the passed visitor invocables that can be called with this event |
313 | //! object, downcasting `*this` in a type-safe way to the most specific type |
314 | //! accepted by the visitor. Without this function, you can still write |
315 | //! a stack of, for example, |
316 | //! `(else) if (const auto* evtPtr = eventCast<...>(baseEvtPtr))` |
317 | //! blocks but switchType() provides a more concise and isolating syntax: |
318 | //! there's no `else` or trailing `return/break` to forget, for one. |
319 | //! The visitors have to all return the same type (possibly void). |
320 | //! Here's how you might use this function: |
321 | //! \code |
322 | //! RoomEventPtr eptr = /* get the event pointer from somewhere */; |
323 | //! const auto result = eptr->switchOnType( |
324 | //! [](const RoomMemberEvent& memberEvent) { |
325 | //! // Do what's needed if eptr points to a RoomMemberEvent |
326 | //! return 1; |
327 | //! }, |
328 | //! [](const CallEvent& callEvent) { |
329 | //! // Do what's needed if eptr points to a CallEvent or any |
330 | //! // class derived from it |
331 | //! return 2; |
332 | //! }, |
333 | //! 3); /* the default value to return if nothing above matched */ |
334 | //! \endcode |
335 | //! As the example shows, the last parameter can optionally be |
336 | //! a plain returned value instead of a visitor. |
337 | template <typename... VisitorTs> |
338 | auto switchOnType(VisitorTs&&... visitors) const; |
339 | |
340 | [[deprecated("Use fullJson() and stringify it with QJsonDocument::toJson() " |
341 | "or by other means" )]] |
342 | QByteArray originalJson() const; |
343 | [[deprecated("Use fullJson() instead" )]] // |
344 | QJsonObject originalJsonObject() const { return fullJson(); } |
345 | |
346 | const QJsonObject& fullJson() const { return _json; } |
347 | |
348 | // According to the CS API spec, every event also has |
349 | // a "content" object; but since its structure is different for |
350 | // different types, we're implementing it per-event type. |
351 | |
352 | // NB: const return types below are meant to catch accidental attempts |
353 | // to change event JSON (e.g., consider contentJson()["inexistentKey"]). |
354 | |
355 | const QJsonObject contentJson() const; |
356 | |
357 | //! \brief Get a part of the content object, assuming a given type |
358 | //! |
359 | //! This retrieves the value under `content.<key>` from the event JSON and |
360 | //! then converts it to \p T using fromJson(). |
361 | //! \sa contentJson, fromJson |
362 | template <typename T, typename KeyT> |
363 | const T contentPart(KeyT&& key) const |
364 | { |
365 | return fromJson<T>(contentJson()[std::forward<KeyT>(key)]); |
366 | } |
367 | |
368 | template <typename T> |
369 | [[deprecated("Use contentPart() to get a part of the event content" )]] // |
370 | T content(const QString& key) const |
371 | { |
372 | return contentPart<T>(key); |
373 | } |
374 | |
375 | const QJsonObject unsignedJson() const; |
376 | |
377 | //! \brief Get a part of the unsigned object, assuming a given type |
378 | //! |
379 | //! This retrieves the value under `unsigned.<key>` from the event JSON and |
380 | //! then converts it to \p T using fromJson(). |
381 | //! \sa unsignedJson, fromJson |
382 | template <typename T, typename KeyT> |
383 | const T unsignedPart(KeyT&& key) const |
384 | { |
385 | return fromJson<T>(unsignedJson()[std::forward<KeyT>(key)]); |
386 | } |
387 | |
388 | friend QUOTIENT_API QDebug operator<<(QDebug dbg, const Event& e) |
389 | { |
390 | QDebugStateSaver _dss { dbg }; |
391 | dbg.noquote().nospace() |
392 | << e.matrixType() << '(' << e.metaType().className << "): " ; |
393 | e.dumpTo(dbg); |
394 | return dbg; |
395 | } |
396 | |
397 | // State events are quite special in Matrix; so isStateEvent() is here, |
398 | // as an exception. For other base events, Event::is<>() and |
399 | // Quotient::is<>() should be used; don't add is* methods here |
400 | bool isStateEvent() const; |
401 | [[deprecated("Use is<CallEvent>() instead" )]] bool isCallEvent() const; |
402 | |
403 | protected: |
404 | friend class EventMetaType<Event>; // To access the below constructor |
405 | |
406 | explicit Event(const QJsonObject& json); |
407 | |
408 | QJsonObject& editJson() { return _json; } |
409 | virtual void dumpTo(QDebug dbg) const; |
410 | |
411 | private: |
412 | QJsonObject _json; |
413 | }; |
414 | using EventPtr = event_ptr_tt<Event>; |
415 | |
416 | template <EventClass EventT> |
417 | using EventsArray = std::vector<event_ptr_tt<EventT>>; |
418 | using Events = EventsArray<Event>; |
419 | |
420 | // === Facilities for event class definitions === |
421 | |
422 | //! \brief A template base class to derive your event type from |
423 | //! |
424 | //! This simple class template generates commonly used event constructor |
425 | //! signatures and the content() method with the appropriate return type. |
426 | //! The generic version here is only used with non-trivial \p ContentT (if you |
427 | //! don't need to create an event from its content structure, just go and derive |
428 | //! straight from the respective \p EventBaseT instead of using EventTemplate); |
429 | //! specialisations may override that and provide useful semantics even without |
430 | //! \p ContentT (see EventTemplate<CallEvent>, e.g.). |
431 | //! |
432 | //! The template uses CRTP to pick the event type id from the actual class; |
433 | //! it will fail to compile if \p EventT doesn't provide TypeId. It also uses |
434 | //! the base event type's basicJson(); if you need extra keys to be inserted |
435 | //! you may want to bypass this template as writing the code to that effect in |
436 | //! your class will likely be clearer and more concise. |
437 | //! \sa https://en.wikipedia.org/wiki/Curiously_recurring_template_pattern |
438 | //! \sa DEFINE_SIMPLE_EVENT |
439 | template <typename EventT, EventClass BaseEventT, typename ContentT = void> |
440 | class EventTemplate : public BaseEventT { |
441 | // Above: can't constrain EventT to be EventClass because it's incomplete |
442 | // by CRTP definition. |
443 | public: |
444 | static_assert( |
445 | !std::is_same_v<ContentT, void>, |
446 | "If you see this, you tried to use EventTemplate with the default" |
447 | " ContentT type, which is void. This default is only used with explicit" |
448 | " specialisations (see CallEvent, e.g.). Otherwise, if you don't intend" |
449 | " to use the content part of EventTemplate then you don't need" |
450 | " EventTemplate; just use the base event class directly" ); |
451 | using content_type = ContentT; |
452 | |
453 | explicit EventTemplate(const QJsonObject& json) |
454 | : BaseEventT(json) |
455 | {} |
456 | explicit EventTemplate(const ContentT& c) |
457 | : BaseEventT(EventT::basicJson(EventT::TypeId, toJson(c))) |
458 | {} |
459 | |
460 | ContentT content() const { return fromJson<ContentT>(this->contentJson()); } |
461 | }; |
462 | |
463 | //! \brief Supply event metatype information in base event types |
464 | //! |
465 | //! Use this macro in a public section of your base event class to provide |
466 | //! type identity and enable dynamic loading of generic events of that type. |
467 | //! Do _not_ add this macro if your class is an intermediate wrapper and is not |
468 | //! supposed to be instantiated on its own. Provides BaseMetaType static field |
469 | //! initialised by parameters passed to the macro, and a metaType() override |
470 | //! pointing to that BaseMetaType. |
471 | //! \sa EventMetaType |
472 | #define QUO_BASE_EVENT(CppType_, BaseCppType_, ...) \ |
473 | friend class EventMetaType<CppType_>; \ |
474 | static inline EventMetaType<CppType_> BaseMetaType{ \ |
475 | #CppType_, &BaseCppType_::BaseMetaType __VA_OPT__(, ) __VA_ARGS__ \ |
476 | }; \ |
477 | static_assert(&CppType_::BaseMetaType == &BaseMetaType, \ |
478 | #CppType_ " is wrong here - check for copy-pasta"); \ |
479 | const AbstractEventMetaType& metaType() const override \ |
480 | { \ |
481 | return BaseMetaType; \ |
482 | } \ |
483 | // End of macro |
484 | |
485 | //! \brief Supply event metatype information in (specific) event types |
486 | //! |
487 | //! Use this macro in a public section of your event class to provide type |
488 | //! identity and enable dynamic loading of generic events of that type. |
489 | //! Do _not_ use this macro if your class is an intermediate wrapper and is not |
490 | //! supposed to be instantiated on its own. Provides MetaType static field |
491 | //! initialised as described below; a metaType() override pointing to it; and |
492 | //! the TypeId static field that is equal to MetaType.matrixId. |
493 | //! |
494 | //! The first two macro parameters are used as the first two EventMetaType |
495 | //! constructor parameters; the third EventMetaType parameter is always |
496 | //! BaseMetaType; and additional base types can be passed in extra macro |
497 | //! parameters if you need to include the same event type in more than one |
498 | //! event factory hierarchy (e.g., EncryptedEvent). |
499 | //! \sa EventMetaType |
500 | #define QUO_EVENT(CppType_, MatrixType_) \ |
501 | friend class EventMetaType<CppType_>; \ |
502 | static inline const EventMetaType<CppType_> MetaType{ #CppType_, \ |
503 | &BaseMetaType, \ |
504 | MatrixType_ }; \ |
505 | static_assert(&CppType_::MetaType == &MetaType, \ |
506 | #CppType_ " is wrong here - check for copy-pasta"); \ |
507 | static inline const auto& TypeId = MetaType.matrixId; \ |
508 | const AbstractEventMetaType& metaType() const override \ |
509 | { \ |
510 | return MetaType; \ |
511 | } \ |
512 | [[deprecated("Use " #CppType_ "::TypeId directly instead")]] \ |
513 | static constexpr auto matrixTypeId() { return MatrixType_; } \ |
514 | [[deprecated("Use " #CppType_ "::TypeId directly instead")]] \ |
515 | static auto typeId() { return TypeId; } \ |
516 | // End of macro |
517 | |
518 | //! \deprecated This is the old name for what is now known as QUO_EVENT |
519 | #define DEFINE_EVENT_TYPEID(Id_, Type_) QUO_EVENT(Type_, Id_) |
520 | |
521 | #define QUO_CONTENT_GETTER_X(PartType_, PartName_, JsonKey_) \ |
522 | PartType_ PartName_() const \ |
523 | { \ |
524 | static const auto PartName_##JsonKey = JsonKey_; \ |
525 | return contentPart<PartType_>(PartName_##JsonKey); \ |
526 | } |
527 | |
528 | //! \brief Define an inline method obtaining a content part |
529 | //! |
530 | //! This macro adds a const method that extracts a JSON value at the key |
531 | //! <tt>toSnakeCase(PartName_)</tt> (sic) and converts it to the type |
532 | //! \p PartType_. Effectively, the generated method is an equivalent of |
533 | //! \code |
534 | //! contentPart<PartType_>(Quotient::toSnakeCase(#PartName_##_ls)); |
535 | //! \endcode |
536 | #define QUO_CONTENT_GETTER(PartType_, PartName_) \ |
537 | QUO_CONTENT_GETTER_X(PartType_, PartName_, toSnakeCase(#PartName_##_ls)) |
538 | |
539 | //! \deprecated This macro was used after an event class definition |
540 | //! to enable its dynamic loading; it is completely superseded by QUO_EVENT |
541 | #define REGISTER_EVENT_TYPE(Type_) |
542 | |
543 | /// \brief Define a new event class with a single key-value pair in the content |
544 | /// |
545 | /// This macro defines a new event class \p Name_ derived from \p Base_, |
546 | /// with Matrix event type \p TypeId_, providing a getter named \p GetterName_ |
547 | /// for a single value of type \p ValueType_ inside the event content. |
548 | /// To retrieve the value the getter uses a JSON key name that corresponds to |
549 | /// its own (getter's) name but written in snake_case. \p GetterName_ must be |
550 | /// in camelCase, no quotes (an identifier, not a literal). |
551 | #define DEFINE_SIMPLE_EVENT(Name_, Base_, TypeId_, ValueType_, GetterName_, \ |
552 | JsonKey_) \ |
553 | constexpr inline auto Name_##ContentKey = JsonKey_##_ls; \ |
554 | class QUOTIENT_API Name_ \ |
555 | : public EventTemplate< \ |
556 | Name_, Base_, \ |
557 | EventContent::SingleKeyValue<ValueType_, Name_##ContentKey>> { \ |
558 | public: \ |
559 | QUO_EVENT(Name_, TypeId_) \ |
560 | using value_type = ValueType_; \ |
561 | using EventTemplate::EventTemplate; \ |
562 | QUO_CONTENT_GETTER_X(ValueType_, GetterName_, Name_##ContentKey) \ |
563 | }; \ |
564 | // End of macro |
565 | |
566 | // === is<>(), eventCast<>() and switchOnType<>() === |
567 | |
568 | template <EventClass EventT> |
569 | inline bool is(const Event& e) |
570 | { |
571 | // Protect against accidental putting QUO_*EVENT to a private section |
572 | static_assert(requires { &EventT::metaType; }, |
573 | "Event class doesn't have a public metaType() override - " |
574 | "did you misplace the QUO_*EVENT macro?" ); |
575 | if constexpr (requires { EventT::MetaType; }) { |
576 | return &e.metaType() == &EventT::MetaType; |
577 | } else { |
578 | const auto* p = &e.metaType(); |
579 | do { |
580 | if (p == &EventT::BaseMetaType) |
581 | return true; |
582 | } while ((p = p->baseType) != nullptr); |
583 | return false; |
584 | } |
585 | } |
586 | |
587 | //! \brief Cast the event pointer down in a type-safe way |
588 | //! |
589 | //! Checks that the event \p eptr points to actually is of the requested type |
590 | //! and returns a (plain) pointer to the event downcast to that type. \p eptr |
591 | //! can be either "dumb" (BaseEventT*) or "smart" (`event_ptr_tt<>`). This |
592 | //! overload doesn't affect the event ownership - if the original pointer owns |
593 | //! the event it must outlive the downcast pointer to keep it from dangling. |
594 | template <EventClass EventT, typename BasePtrT> |
595 | inline auto eventCast(const BasePtrT& eptr) |
596 | -> decltype(static_cast<EventT*>(std::to_address(eptr))) |
597 | { |
598 | return eptr && is<std::decay_t<EventT>>(*eptr) |
599 | ? static_cast<EventT*>(std::to_address(eptr)) |
600 | : nullptr; |
601 | } |
602 | |
603 | //! \brief Cast the event pointer down in a type-safe way, with moving |
604 | //! |
605 | //! Checks that the event \p eptr points to actually is of the requested type; |
606 | //! if (and only if) it is, releases the pointer, downcasts it to the requested |
607 | //! event type and returns a new smart pointer wrapping the downcast one. |
608 | //! Unlike the non-moving eventCast() overload, this one only accepts a smart |
609 | //! pointer, and that smart pointer should be an rvalue (either a temporary, |
610 | //! or as a result of std::move()). The ownership, respectively, is transferred |
611 | //! to the new pointer; the original smart pointer is reset to nullptr, as is |
612 | //! normal for `unique_ptr<>::release()`. |
613 | //! \note If \p eptr's event type does not match \p EventT it retains ownership |
614 | //! after calling this overload; if it is a temporary, this normally |
615 | //! leads to the event getting deleted along with the end of |
616 | //! the temporary's lifetime. |
617 | template <EventClass EventT, typename BaseEventT> |
618 | inline auto eventCast(event_ptr_tt<BaseEventT>&& eptr) |
619 | { |
620 | return eptr && is<std::decay_t<EventT>>(*eptr) |
621 | ? event_ptr_tt<EventT>(static_cast<EventT*>(eptr.release())) |
622 | : nullptr; |
623 | } |
624 | |
625 | namespace _impl { |
626 | template <typename FnT, typename BaseT> |
627 | concept Invocable_With_Downcast = requires |
628 | { |
629 | requires EventClass<BaseT>; |
630 | std::is_base_of_v<BaseT, std::remove_cvref_t<fn_arg_t<FnT>>>; |
631 | }; |
632 | } |
633 | |
634 | template <EventClass BaseT, typename TailT> |
635 | inline auto switchOnType(const BaseT& event, TailT&& tail) |
636 | { |
637 | if constexpr (std::is_invocable_v<TailT, BaseT>) { |
638 | return tail(event); |
639 | } else if constexpr (_impl::Invocable_With_Downcast<TailT, BaseT>) { |
640 | using event_type = fn_arg_t<TailT>; |
641 | if (is<std::decay_t<event_type>>(event)) |
642 | return tail(static_cast<event_type>(event)); |
643 | return std::invoke_result_t<TailT, event_type>(); // Default-constructed |
644 | } else { // Treat it as a value to return |
645 | return std::forward<TailT>(tail); |
646 | } |
647 | } |
648 | |
649 | template <typename FnT1, typename... FnTs> |
650 | inline auto switchOnType(const EventClass auto& event, FnT1&& fn1, FnTs&&... fns) |
651 | { |
652 | using event_type1 = fn_arg_t<FnT1>; |
653 | if (is<std::decay_t<event_type1>>(event)) |
654 | return fn1(static_cast<event_type1>(event)); |
655 | return switchOnType(event, std::forward<FnTs>(fns)...); |
656 | } |
657 | |
658 | template <EventClass BaseT, typename... FnTs> |
659 | [[deprecated("The new name for visit() is switchOnType()" )]] // |
660 | inline auto visit(const BaseT& event, FnTs&&... fns) |
661 | { |
662 | return switchOnType(event, std::forward<FnTs>(fns)...); |
663 | } |
664 | |
665 | template <typename... VisitorTs> |
666 | inline auto Event::switchOnType(VisitorTs&&... visitors) const |
667 | { |
668 | return Quotient::switchOnType(*this, |
669 | std::forward<VisitorTs>(visitors)...); |
670 | } |
671 | |
672 | // A facility overload that calls void-returning switchOnType() on each event |
673 | // over a range of event pointers |
674 | // TODO: replace with ranges::for_each once all standard libraries have it |
675 | template <typename RangeT, typename... FnTs> |
676 | inline auto visitEach(RangeT&& events, FnTs&&... fns) |
677 | requires std::is_void_v< |
678 | decltype(switchOnType(**begin(events), std::forward<FnTs>(fns)...))> |
679 | { |
680 | for (auto&& evtPtr: events) |
681 | switchOnType(*evtPtr, std::forward<FnTs>(fns)...); |
682 | } |
683 | } // namespace Quotient |
684 | Q_DECLARE_METATYPE(Quotient::Event*) |
685 | Q_DECLARE_METATYPE(const Quotient::Event*) |
686 | |