diff options
author | Alexey Rusakov <Kitsune-Ral@users.sf.net> | 2022-09-05 07:49:16 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2022-09-05 07:49:16 +0200 |
commit | 1e263a32fcbc44985e474a626393494a81f15e37 (patch) | |
tree | 8811e0a995dcd593cb9f233e02ece9402e76eb1b /lib/events/event.h | |
parent | 8cb629f406f5b8b1ff7ce787dd3967d5684e07c3 (diff) | |
parent | bd2736bc9f8b6023ecbc21d0d831856703b853db (diff) | |
download | libquotient-1e263a32fcbc44985e474a626393494a81f15e37.tar.gz libquotient-1e263a32fcbc44985e474a626393494a81f15e37.zip |
Merge pull request #565 from quotient-im/kitsune/streamline-event-types-2
Streamline event types, part 2
Diffstat (limited to 'lib/events/event.h')
-rw-r--r-- | lib/events/event.h | 538 |
1 files changed, 376 insertions, 162 deletions
diff --git a/lib/events/event.h b/lib/events/event.h index b7454337..0abef1f0 100644 --- a/lib/events/event.h +++ b/lib/events/event.h @@ -4,11 +4,11 @@ #pragma once #include "converters.h" -#include "logging.h" #include "function_traits.h" +#include "single_key_value.h" namespace Quotient { -// === event_ptr_tt<> and type casting facilities === +// === event_ptr_tt<> and basic type casting facilities === template <typename EventT> using event_ptr_tt = std::unique_ptr<EventT>; @@ -48,141 +48,233 @@ const QString RoomIdKey { RoomIdKeyL }; const QString UnsignedKey { UnsignedKeyL }; const QString StateKeyKey { StateKeyKeyL }; -// === Event types === - using event_type_t = QLatin1String; -using event_mtype_t = const char*; - -class QUOTIENT_API EventTypeRegistry { -public: - ~EventTypeRegistry() = default; - [[deprecated("event_type_t is a string now, use it directly instead")]] +// TODO: Remove in 0.8 +struct QUOTIENT_API EventTypeRegistry { + [[deprecated("event_type_t is a string since libQuotient 0.7, use it directly instead")]] static QString getMatrixType(event_type_t typeId); -private: - EventTypeRegistry() = default; + EventTypeRegistry() = delete; + ~EventTypeRegistry() = default; Q_DISABLE_COPY_MOVE(EventTypeRegistry) }; -template <typename EventT> -constexpr event_type_t typeId() -{ - return std::decay_t<EventT>::TypeId; -} +// === EventMetaType === -constexpr event_type_t UnknownEventTypeId = "?"_ls; -[[deprecated("Use UnknownEventTypeId")]] -constexpr event_type_t unknownEventTypeId() { return UnknownEventTypeId; } +class Event; -// === Event creation facilities === +// TODO: move over to std::derived_from<Event> once it's available everywhere +template <typename EventT, typename BaseEventT = Event> +concept EventClass = std::is_base_of_v<BaseEventT, EventT>; -//! Create an event of arbitrary type from its arguments -template <typename EventT, typename... ArgTs> -inline event_ptr_tt<EventT> makeEvent(ArgTs&&... args) -{ - return std::make_unique<EventT>(std::forward<ArgTs>(args)...); -} +template <EventClass EventT> +bool is(const Event& e); -namespace _impl { - class QUOTIENT_API EventFactoryBase { - public: - EventFactoryBase(const EventFactoryBase&) = delete; - - protected: // This class is only to inherit from - explicit EventFactoryBase(const char* name) - : name(name) - {} - void logAddingMethod(event_type_t TypeId, size_t newSize); - - private: - const char* const name; - }; -} // namespace _impl - -//! \brief A family of event factories to create events from CS API responses +//! \brief The base class for event metatypes //! -//! Each of these factories, as instantiated by event base types (Event, -//! RoomEvent etc.) is capable of producing an event object derived from -//! \p BaseEventT, using the JSON payload and the event type passed to its -//! make() method. Don't use these directly to make events; use loadEvent() -//! overloads as the frontend for these. Never instantiate new factories -//! outside of base event classes. -//! \sa loadEvent, setupFactory, Event::factory, RoomEvent::factory, -//! StateEventBase::factory -template <typename BaseEventT> -class EventFactory : public _impl::EventFactoryBase { -private: - using method_t = event_ptr_tt<BaseEventT> (*)(const QJsonObject&, - const QString&); - std::vector<method_t> methods {}; - - template <class EventT> - static event_ptr_tt<BaseEventT> makeIfMatches(const QJsonObject& json, - const QString& matrixType) +//! You should not normally have to use this directly, unless you need to devise +//! a whole new kind of event metatypes. +class QUOTIENT_API AbstractEventMetaType { +public: + // The public fields here are const and are not to be changeable anyway. + // NOLINTBEGIN(misc-non-private-member-variables-in-classes) + const char* const className; + const event_type_t matrixId; + const AbstractEventMetaType* const baseType = nullptr; + // NOLINTEND(misc-non-private-member-variables-in-classes) + + explicit AbstractEventMetaType(const char* className) + : className(className) + {} + explicit AbstractEventMetaType(const char* className, event_type_t matrixId, + AbstractEventMetaType& nearestBase) + : className(className), matrixId(matrixId), baseType(&nearestBase) { - // If your matrix event type is not all ASCII, it's your problem - // (see https://github.com/matrix-org/matrix-doc/pull/2758) - return EventT::TypeId == matrixType ? makeEvent<EventT>(json) : nullptr; + nearestBase.addDerived(this); } + void addDerived(AbstractEventMetaType *newType); + + virtual ~AbstractEventMetaType() = default; + +protected: + // Allow template specialisations to call into one another + template <class EventT> + friend class EventMetaType; + + // The returned value indicates whether a generic object has to be created + // on the top level when `event` is empty, instead of returning nullptr + virtual bool doLoadFrom(const QJsonObject& fullJson, const QString& type, + Event*& event) const = 0; + +private: + std::vector<const AbstractEventMetaType*> derivedTypes{}; + Q_DISABLE_COPY_MOVE(AbstractEventMetaType) +}; + +// Any event metatype is unique (note Q_DISABLE_COPY_MOVE above) so can be +// identified by its address +inline bool operator==(const AbstractEventMetaType& lhs, + const AbstractEventMetaType& rhs) +{ + return &lhs == &rhs; +} + +//! \brief A family of event meta-types to load and match events +//! +//! TL;DR for the loadFrom() story: +//! - for base event types, use QUO_BASE_EVENT and, if you have additional +//! validation (e.g., JSON has to contain a certain key - see StateEvent +//! for a real example), define it in the static EventT::isValid() member +//! function accepting QJsonObject and returning bool. +//! - for leaf (specific) event types - simply use QUO_EVENT and it will do +//! everything necessary, including the TypeId definition. +//! \sa QUO_EVENT, QUO_BASE_EVENT +template <class EventT> +class QUOTIENT_API EventMetaType : public AbstractEventMetaType { + // Above: can't constrain EventT to be EventClass because it's incomplete + // at the point of EventMetaType<EventT> instantiation. public: - explicit EventFactory(const char* fName) - : EventFactoryBase { fName } - {} + using AbstractEventMetaType::AbstractEventMetaType; - //! \brief Add a method to create events of a given type + //! \brief Try to load an event from JSON, with dynamic type resolution //! - //! Adds a standard factory method (makeIfMatches) for \p EventT so that - //! event objects of this type can be created dynamically by loadEvent. - //! The caller is responsible for ensuring this method is called only - //! once per type. - //! \sa loadEvent, Quotient::loadEvent - template <class EventT> - const auto& addMethod() + //! The generic logic defined in this class template and invoked applies to + //! all event types defined in the library and boils down to the following: + //! 1. + //! a. If EventT has TypeId defined (which normally is a case of + //! all leaf - specific - event types, via QUO_EVENT macro) and + //! \p type doesn't exactly match it, nullptr is immediately returned. + //! b. In absence of TypeId, an event type is assumed to be a base; + //! its derivedTypes are examined, and this algorithm is applied + //! recursively on each. + //! 2. Optional validation: if EventT (or, due to the way inheritance works, + //! any of its base event types) has a static isValid() predicate and + //! the event JSON does not satisfy it, nullptr is immediately returned + //! to the upper level or to the loadFrom() caller. This is how existence + //! of `state_key` is checked in any type derived from StateEvent. + //! 3. If step 1b above returned non-nullptr, immediately return it. + //! 4. + //! a. If EventT::isValid() or EventT::TypeId (either, or both) exist and + //! are satisfied (see steps 1a and 2 above), an object of this type + //! is created from the passed JSON and returned. In case of a base + //! event type, this will be a generic (aka "unknown") event. + //! b. If neither exists, a generic event is only created and returned + //! when on the top level (i.e., outside of recursion into + //! derivedTypes); lower levels return nullptr instead and the type + //! lookup continues. The latter is a case of a derived base event + //! metatype (e.g. RoomEvent) called from its base event metatype + //! (i.e., Event). If no matching type derived from RoomEvent is found, + //! the nested lookup returns nullptr rather than a generic RoomEvent, + //! so that other types derived from Event could be examined. + event_ptr_tt<EventT> loadFrom(const QJsonObject& fullJson, + const QString& type) const { - const auto m = &makeIfMatches<EventT>; - const auto it = std::find(methods.cbegin(), methods.cend(), m); - if (it != methods.cend()) - return *it; - logAddingMethod(EventT::TypeId, methods.size() + 1); - return methods.emplace_back(m); + Event* event = nullptr; + const bool goodEnough = doLoadFrom(fullJson, type, event); + if (!event && goodEnough) + return event_ptr_tt<EventT>{ new EventT(fullJson) }; + return event_ptr_tt<EventT>{ static_cast<EventT*>(event) }; } - auto loadEvent(const QJsonObject& json, const QString& matrixType) +private: + bool doLoadFrom(const QJsonObject& fullJson, const QString& type, + Event*& event) const override { - for (const auto& f : methods) - if (auto e = f(json, matrixType)) - return e; - return makeEvent<BaseEventT>(UnknownEventTypeId, json); + if constexpr (requires { EventT::TypeId; }) { + if (EventT::TypeId != type) + return false; + } else { + for (const auto& p : derivedTypes) { + p->doLoadFrom(fullJson, type, event); + if (event) { + Q_ASSERT(is<EventT>(*event)); + return false; + } + } + } + if constexpr (requires { EventT::isValid; }) { + if (!EventT::isValid(fullJson)) + return false; + } else if constexpr (!requires { EventT::TypeId; }) + return true; // Create a generic event object if on the top level + event = new EventT(fullJson); + return false; } }; -//! \brief Point of customisation to dynamically load events +// === Event creation facilities === + +//! \brief Create an event of arbitrary type from its arguments //! -//! The default specialisation of this calls BaseEventT::factory.loadEvent() -//! and if that fails (i.e. returns nullptr) creates an unknown event of -//! BaseEventT. Other specialisations may reuse other factories, add validations -//! common to BaseEventT events, and so on. -template <class BaseEventT> -event_ptr_tt<BaseEventT> doLoadEvent(const QJsonObject& json, - const QString& matrixType) +//! This should not be used to load events from JSON - use loadEvent() for that. +//! \sa loadEvent +template <EventClass EventT, typename... ArgTs> +inline event_ptr_tt<EventT> makeEvent(ArgTs&&... args) { - return BaseEventT::factory.loadEvent(json, matrixType); + return std::make_unique<EventT>(std::forward<ArgTs>(args)...); } +template <EventClass EventT> +constexpr const auto& mostSpecificMetaType() +{ + if constexpr (requires { EventT::MetaType; }) + return EventT::MetaType; + else + return EventT::BaseMetaType; +} + +//! \brief Create an event with proper type from a JSON object +//! +//! Use this factory template to detect the type from the JSON object +//! contents (the detected event type should derive from the template +//! parameter type) and create an event object of that type. +template <EventClass EventT> +inline event_ptr_tt<EventT> loadEvent(const QJsonObject& fullJson) +{ + return mostSpecificMetaType<EventT>().loadFrom( + fullJson, fullJson[TypeKeyL].toString()); +} + +//! \brief Create an event from a type string and content JSON +//! +//! Use this template to resolve the C++ type from the Matrix type string in +//! \p matrixType and create an event of that type by passing all parameters +//! to BaseEventT::basicJson(). +template <EventClass EventT> +inline event_ptr_tt<EventT> loadEvent(const QString& matrixType, + const auto&... otherBasicJsonParams) +{ + return mostSpecificMetaType<EventT>().loadFrom( + EventT::basicJson(matrixType, otherBasicJsonParams...), matrixType); +} + +template <EventClass EventT> +struct JsonConverter<event_ptr_tt<EventT>> + : JsonObjectUnpacker<event_ptr_tt<EventT>> { + // No dump() to avoid any ambiguity on whether a given export to JSON uses + // fullJson() or only contentJson() + using JsonObjectUnpacker<event_ptr_tt<EventT>>::load; + static auto load(const QJsonObject& jo) + { + return loadEvent<EventT>(jo); + } +}; + // === Event === class QUOTIENT_API Event { public: using Type = event_type_t; - static inline EventFactory<Event> factory { "Event" }; + static inline EventMetaType<Event> BaseMetaType { "Event" }; + virtual const AbstractEventMetaType& metaType() const + { + return BaseMetaType; + } - explicit Event(Type type, const QJsonObject& json); - explicit Event(Type type, event_mtype_t matrixType, - const QJsonObject& contentJson = {}); Q_DISABLE_COPY(Event) - Event(Event&&) = default; + Event(Event&&) noexcept = default; Event& operator=(Event&&) = delete; virtual ~Event(); @@ -193,8 +285,26 @@ public: return { { TypeKey, matrixType }, { ContentKey, content } }; } - Type type() const { return _type; } + //! \brief Event Matrix type, as identified by its metatype object + //! + //! For generic/unknown events it will contain a descriptive/generic string + //! defined by the respective base event type (that can be empty). + //! \sa matrixType + Type type() const { return metaType().matrixId; } + + //! \brief Exact Matrix type stored in JSON + //! + //! Coincides with the result of type() (but is slower) for events defined + //! in C++ (not necessarily in the library); for generic/unknown events + //! the returned value will be different. QString matrixType() const; + + template <EventClass EventT> + bool is() const + { + return Quotient::is<EventT>(*this); + } + [[deprecated("Use fullJson() and stringify it with QJsonDocument::toJson() " "or by other means")]] QByteArray originalJson() const; @@ -212,6 +322,11 @@ public: const QJsonObject contentJson() const; + //! \brief Get a part of the content object, assuming a given type + //! + //! This retrieves the value under `content.<key>` from the event JSON and + //! then converts it to \p T using fromJson(). + //! \sa contentJson, fromJson template <typename T, typename KeyT> const T contentPart(KeyT&& key) const { @@ -227,6 +342,11 @@ public: const QJsonObject unsignedJson() const; + //! \brief Get a part of the unsigned object, assuming a given type + //! + //! This retrieves the value under `unsigned.<key>` from the event JSON and + //! then converts it to \p T using fromJson(). + //! \sa unsignedJson, fromJson template <typename T, typename KeyT> const T unsignedPart(KeyT&& key) const { @@ -236,28 +356,138 @@ public: friend QUOTIENT_API QDebug operator<<(QDebug dbg, const Event& e) { QDebugStateSaver _dss { dbg }; - dbg.noquote().nospace() << e.matrixType() << '(' << e.type() << "): "; + dbg.noquote().nospace() + << e.matrixType() << '(' << e.metaType().className << "): "; e.dumpTo(dbg); return dbg; } - virtual bool isStateEvent() const { return false; } - virtual bool isCallEvent() const { return false; } + // State events are quite special in Matrix; so isStateEvent() is here, + // as an exception. For other base events, Event::is<>() and + // Quotient::is<>() should be used; don't add is* methods here + bool isStateEvent() const; + [[deprecated("Use is<CallEvent>() instead")]] bool isCallEvent() const; protected: + friend class EventMetaType<Event>; // To access the below constructor + + explicit Event(const QJsonObject& json); + QJsonObject& editJson() { return _json; } virtual void dumpTo(QDebug dbg) const; private: - Type _type; QJsonObject _json; }; using EventPtr = event_ptr_tt<Event>; -template <typename EventT> +template <EventClass EventT> using EventsArray = std::vector<event_ptr_tt<EventT>>; using Events = EventsArray<Event>; +// === Facilities for event class definitions === + +//! \brief A template base class to derive your event type from +//! +//! This simple class template generates commonly used event constructor +//! signatures and the content() method with the appropriate return type. +//! The generic version here is only used with non-trivial \p ContentT (if you +//! don't need to create an event from its content structure, just go and derive +//! straight from the respective \p EventBaseT instead of using EventTemplate); +//! specialisations may override that and provide useful semantics even without +//! \p ContentT (see EventTemplate<CallEvent>, e.g.). +//! +//! The template uses CRTP to pick the event type id from the actual class; +//! it will fail to compile if \p EventT doesn't provide TypeId. It also uses +//! the base event type's basicJson(); if you need extra keys to be inserted +//! you may want to bypass this template as writing the code to that effect in +//! your class will likely be clearer and more concise. +//! \sa https://en.wikipedia.org/wiki/Curiously_recurring_template_pattern +//! \sa DEFINE_SIMPLE_EVENT +template <typename EventT, EventClass BaseEventT, typename ContentT = void> +class EventTemplate : public BaseEventT { + // Above: can't constrain EventT to be EventClass because it's incomplete + // by CRTP definition. +public: + static_assert( + !std::is_same_v<ContentT, void>, + "If you see this, you tried to use EventTemplate with the default" + " ContentT type, which is void. This default is only used with explicit" + " specialisations (see CallEvent, e.g.). Otherwise, if you don't intend" + " to use the content part of EventTemplate then you don't need" + " EventTemplate; just use the base event class directly"); + using content_type = ContentT; + + explicit EventTemplate(const QJsonObject& json) + : BaseEventT(json) + {} + explicit EventTemplate(const ContentT& c) + : BaseEventT(EventT::basicJson(EventT::TypeId, toJson(c))) + {} + + ContentT content() const { return fromJson<ContentT>(this->contentJson()); } +}; + +//! \brief Supply event metatype information in base event types +//! +//! Use this macro in a public section of your base event class to provide +//! type identity and enable dynamic loading of generic events of that type. +//! Do _not_ add this macro if your class is an intermediate wrapper and is not +//! supposed to be instantiated on its own. Provides BaseMetaType static field +//! initialised by parameters passed to the macro, and a metaType() override +//! pointing to that BaseMetaType. +//! \sa EventMetaType, EventMetaType::SuppressLoadDerived +#define QUO_BASE_EVENT(CppType_, ...) \ + friend class EventMetaType<CppType_>; \ + static inline EventMetaType<CppType_> BaseMetaType{ \ + #CppType_ __VA_OPT__(,) __VA_ARGS__ }; \ + const AbstractEventMetaType& metaType() const override \ + { \ + return BaseMetaType; \ + } \ + // End of macro + +//! Supply event metatype information in (specific) event types +//! +//! Use this macro in a public section of your event class to provide type +//! identity and enable dynamic loading of generic events of that type. +//! Do _not_ use this macro if your class is an intermediate wrapper and is not +//! supposed to be instantiated on its own. Provides MetaType static field +//! initialised as described below; a metaType() override pointing to it; and +//! the TypeId static field that is equal to MetaType.matrixId. +//! +//! The first two macro parameters are used as the first two EventMetaType +//! constructor parameters; the third EventMetaType parameter is always +//! BaseMetaType; and additional base types can be passed in extra macro +//! parameters if you need to include the same event type in more than one +//! event factory hierarchy (e.g., EncryptedEvent). +//! \sa EventMetaType +#define QUO_EVENT(CppType_, MatrixType_, ...) \ + static inline const auto& TypeId = MatrixType_##_ls; \ + friend class EventMetaType<CppType_>; \ + static inline const EventMetaType<CppType_> MetaType{ \ + #CppType_, TypeId, BaseMetaType __VA_OPT__(,) __VA_ARGS__ \ + }; \ + const AbstractEventMetaType& metaType() const override \ + { \ + return MetaType; \ + } \ + [[deprecated("Use " #CppType_ "::TypeId directly instead")]] \ + static constexpr const char* matrixTypeId() { return MatrixType_; } \ + [[deprecated("Use " #CppType_ "::TypeId directly instead")]] \ + static event_type_t typeId() { return TypeId; } \ + // End of macro + +//! \deprecated This is the old name for what is now known as QUO_EVENT +#define DEFINE_EVENT_TYPEID(Type_, Id_) QUO_EVENT(Type_, Id_) + +#define QUO_CONTENT_GETTER_X(PartType_, PartName_, JsonKey_) \ + PartType_ PartName_() const \ + { \ + static const auto PartName_##JsonKey = JsonKey_; \ + return contentPart<PartType_>(PartName_##JsonKey); \ + } + //! \brief Define an inline method obtaining a content part //! //! This macro adds a const method that extracts a JSON value at the key @@ -266,32 +496,12 @@ using Events = EventsArray<Event>; //! \code //! contentPart<PartType_>(Quotient::toSnakeCase(#PartName_##_ls)); //! \endcode -#define QUO_CONTENT_GETTER(PartType_, PartName_) \ - PartType_ PartName_() const \ - { \ - static const auto JsonKey = toSnakeCase(#PartName_##_ls); \ - return contentPart<PartType_>(JsonKey); \ - } - -// === Facilities for event class definitions === +#define QUO_CONTENT_GETTER(PartType_, PartName_) \ + QUO_CONTENT_GETTER_X(PartType_, PartName_, toSnakeCase(#PartName_##_ls)) -// This macro should be used in a public section of an event class to -// provide matrixTypeId() and typeId(). -#define DEFINE_EVENT_TYPEID(Id_, Type_) \ - static constexpr event_type_t TypeId = Id_##_ls; \ - [[deprecated("Use " #Type_ "::TypeId directly instead")]] \ - static constexpr event_mtype_t matrixTypeId() { return Id_; } \ - [[deprecated("Use " #Type_ "::TypeId directly instead")]] \ - static event_type_t typeId() { return TypeId; } \ - // End of macro - -// This macro should be put after an event class definition (in .h or .cpp) -// to enable its deserialisation from a /sync and other -// polymorphic event arrays -#define REGISTER_EVENT_TYPE(Type_) \ - [[maybe_unused]] inline const auto& factoryMethodFor##Type_ = \ - Type_::factory.addMethod<Type_>(); \ - // End of macro +//! \deprecated This macro was used after an event class definition +//! to enable its dynamic loading; it is completely superseded by QUO_EVENT +#define REGISTER_EVENT_TYPE(Type_) /// \brief Define a new event class with a single key-value pair in the content /// @@ -301,35 +511,36 @@ using Events = EventsArray<Event>; /// To retrieve the value the getter uses a JSON key name that corresponds to /// its own (getter's) name but written in snake_case. \p GetterName_ must be /// in camelCase, no quotes (an identifier, not a literal). -#define DEFINE_SIMPLE_EVENT(Name_, Base_, TypeId_, ValueType_, GetterName_) \ - class QUOTIENT_API Name_ : public Base_ { \ - public: \ - using content_type = ValueType_; \ - DEFINE_EVENT_TYPEID(TypeId_, Name_) \ - explicit Name_(const QJsonObject& obj) : Base_(TypeId, obj) {} \ - explicit Name_(const content_type& content) \ - : Name_(Base_::basicJson(TypeId, { { JsonKey, toJson(content) } })) \ - {} \ - auto GetterName_() const \ - { \ - return contentPart<content_type>(JsonKey); \ - } \ - static inline const auto JsonKey = toSnakeCase(#GetterName_##_ls); \ - }; \ - REGISTER_EVENT_TYPE(Name_) \ +#define DEFINE_SIMPLE_EVENT(Name_, Base_, TypeId_, ValueType_, GetterName_, \ + JsonKey_) \ + constexpr auto Name_##ContentKey = JsonKey_##_ls; \ + class QUOTIENT_API Name_ \ + : public EventTemplate< \ + Name_, Base_, \ + EventContent::SingleKeyValue<ValueType_, Name_##ContentKey>> { \ + public: \ + QUO_EVENT(Name_, TypeId_) \ + using value_type = ValueType_; \ + using EventTemplate::EventTemplate; \ + QUO_CONTENT_GETTER_X(ValueType_, GetterName_, Name_##ContentKey) \ + }; \ // End of macro // === is<>(), eventCast<>() and switchOnType<>() === -template <class EventT> +template <EventClass EventT> inline bool is(const Event& e) { - return e.type() == typeId<EventT>(); -} - -inline bool isUnknown(const Event& e) -{ - return e.type() == UnknownEventTypeId; + if constexpr (requires { EventT::MetaType; }) { + return &e.metaType() == &EventT::MetaType; + } else { + const auto* p = &e.metaType(); + do { + if (p == &EventT::BaseMetaType) + return true; + } while ((p = p->baseType) != nullptr); + return false; + } } //! \brief Cast the event pointer down in a type-safe way @@ -339,7 +550,7 @@ inline bool isUnknown(const Event& e) //! can be either "dumb" (BaseEventT*) or "smart" (`event_ptr_tt<>`). This //! overload doesn't affect the event ownership - if the original pointer owns //! the event it must outlive the downcast pointer to keep it from dangling. -template <class EventT, typename BasePtrT> +template <EventClass EventT, typename BasePtrT> inline auto eventCast(const BasePtrT& eptr) -> decltype(static_cast<EventT*>(&*eptr)) { @@ -362,7 +573,7 @@ inline auto eventCast(const BasePtrT& eptr) //! after calling this overload; if it is a temporary, this normally //! leads to the event getting deleted along with the end of //! the temporary's lifetime. -template <class EventT, typename BaseEventT> +template <EventClass EventT, typename BaseEventT> inline auto eventCast(event_ptr_tt<BaseEventT>&& eptr) { return eptr && is<std::decay_t<EventT>>(*eptr) @@ -371,12 +582,15 @@ inline auto eventCast(event_ptr_tt<BaseEventT>&& eptr) } namespace _impl { - template <typename FnT, class BaseT> - concept Invocable_With_Downcast = + template <typename FnT, typename BaseT> + concept Invocable_With_Downcast = requires + { + requires EventClass<BaseT>; std::is_base_of_v<BaseT, std::remove_cvref_t<fn_arg_t<FnT>>>; + }; } -template <class BaseT, typename TailT> +template <EventClass BaseT, typename TailT> inline auto switchOnType(const BaseT& event, TailT&& tail) { if constexpr (std::is_invocable_v<TailT, BaseT>) { @@ -391,7 +605,7 @@ inline auto switchOnType(const BaseT& event, TailT&& tail) } } -template <class BaseT, typename FnT1, typename... FnTs> +template <EventClass BaseT, typename FnT1, typename... FnTs> inline auto switchOnType(const BaseT& event, FnT1&& fn1, FnTs&&... fns) { using event_type1 = fn_arg_t<FnT1>; @@ -400,7 +614,7 @@ inline auto switchOnType(const BaseT& event, FnT1&& fn1, FnTs&&... fns) return switchOnType(event, std::forward<FnTs>(fns)...); } -template <class BaseT, typename... FnTs> +template <EventClass BaseT, typename... FnTs> [[deprecated("The new name for visit() is switchOnType()")]] // inline auto visit(const BaseT& event, FnTs&&... fns) { |