aboutsummaryrefslogtreecommitdiff
path: root/lib/events/event.h
diff options
context:
space:
mode:
authorAlexey Rusakov <Kitsune-Ral@users.sf.net>2022-09-05 07:49:16 +0200
committerGitHub <noreply@github.com>2022-09-05 07:49:16 +0200
commit1e263a32fcbc44985e474a626393494a81f15e37 (patch)
tree8811e0a995dcd593cb9f233e02ece9402e76eb1b /lib/events/event.h
parent8cb629f406f5b8b1ff7ce787dd3967d5684e07c3 (diff)
parentbd2736bc9f8b6023ecbc21d0d831856703b853db (diff)
downloadlibquotient-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.h538
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)
{