| 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 | |