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
10namespace Quotient {
11// === event_ptr_tt<> and basic type casting facilities ===
12
13template <typename EventT>
14using event_ptr_tt = std::unique_ptr<EventT>;
15
16template <typename EventT>
17[[deprecated("Use std::to_address() instead")]]
18inline 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
24template <typename TargetEventT, typename EventT>
25inline 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
32constexpr inline auto TypeKey = "type"_ls;
33constexpr inline auto BodyKey = "body"_ls;
34constexpr inline auto ContentKey = "content"_ls;
35constexpr inline auto EventIdKey = "event_id"_ls;
36constexpr inline auto SenderKey = "sender"_ls;
37constexpr inline auto RoomIdKey = "room_id"_ls;
38constexpr inline auto UnsignedKey = "unsigned"_ls;
39constexpr inline auto RedactedCauseKey = "redacted_because"_ls;
40constexpr inline auto PrevContentKey = "prev_content"_ls;
41constexpr 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
53using event_type_t = QLatin1String;
54
55// TODO: Remove in 0.8
56struct 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
67class Event;
68
69// TODO: move over to std::derived_from<Event> once it's available everywhere
70template <typename EventT, typename BaseEventT = Event>
71concept EventClass = std::is_base_of_v<BaseEventT, EventT>;
72
73template <EventClass EventT>
74bool 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.
80class QUOTIENT_API AbstractEventMetaType {
81public:
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
102protected:
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
112private:
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
119inline 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
135template <class EventT>
136class 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.
139public:
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
182private:
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
214template <EventClass EventT, typename... ArgTs>
215inline event_ptr_tt<EventT> makeEvent(ArgTs&&... args)
216{
217 return std::make_unique<EventT>(std::forward<ArgTs>(args)...);
218}
219
220template <EventClass EventT>
221constexpr 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.
234template <EventClass EventT>
235inline 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().
246template <EventClass EventT>
247inline 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
254template <EventClass EventT>
255struct 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
268class QUOTIENT_API Event {
269public:
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
403protected:
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
411private:
412 QJsonObject _json;
413};
414using EventPtr = event_ptr_tt<Event>;
415
416template <EventClass EventT>
417using EventsArray = std::vector<event_ptr_tt<EventT>>;
418using 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
439template <typename EventT, EventClass BaseEventT, typename ContentT = void>
440class EventTemplate : public BaseEventT {
441 // Above: can't constrain EventT to be EventClass because it's incomplete
442 // by CRTP definition.
443public:
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
568template <EventClass EventT>
569inline 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.
594template <EventClass EventT, typename BasePtrT>
595inline 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.
617template <EventClass EventT, typename BaseEventT>
618inline 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
625namespace _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
634template <EventClass BaseT, typename TailT>
635inline 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
649template <typename FnT1, typename... FnTs>
650inline 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
658template <EventClass BaseT, typename... FnTs>
659[[deprecated("The new name for visit() is switchOnType()")]] //
660inline auto visit(const BaseT& event, FnTs&&... fns)
661{
662 return switchOnType(event, std::forward<FnTs>(fns)...);
663}
664
665template <typename... VisitorTs>
666inline 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
675template <typename RangeT, typename... FnTs>
676inline 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
684Q_DECLARE_METATYPE(Quotient::Event*)
685Q_DECLARE_METATYPE(const Quotient::Event*)
686