aboutsummaryrefslogtreecommitdiff
path: root/lib/events/event.h
diff options
context:
space:
mode:
Diffstat (limited to 'lib/events/event.h')
-rw-r--r--lib/events/event.h657
1 files changed, 325 insertions, 332 deletions
diff --git a/lib/events/event.h b/lib/events/event.h
index dee1c44a..686bd8e0 100644
--- a/lib/events/event.h
+++ b/lib/events/event.h
@@ -13,7 +13,7 @@
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#pragma once
@@ -22,397 +22,390 @@
#include "logging.h"
#ifdef ENABLE_EVENTTYPE_ALIAS
-#define USE_EVENTTYPE_ALIAS 1
+# define USE_EVENTTYPE_ALIAS 1
#endif
-namespace QMatrixClient
+namespace QMatrixClient {
+// === event_ptr_tt<> and type casting facilities ===
+
+template <typename EventT>
+using event_ptr_tt = std::unique_ptr<EventT>;
+
+/// Unwrap a plain pointer from a smart pointer
+template <typename EventT>
+inline EventT* rawPtr(const event_ptr_tt<EventT>& ptr)
{
- // === event_ptr_tt<> and type casting facilities ===
+ return ptr.get();
+}
- template <typename EventT>
- using event_ptr_tt = std::unique_ptr<EventT>;
+/// Unwrap a plain pointer and downcast it to the specified type
+template <typename TargetEventT, typename EventT>
+inline TargetEventT* weakPtrCast(const event_ptr_tt<EventT>& ptr)
+{
+ return static_cast<TargetEventT*>(rawPtr(ptr));
+}
+
+/// Re-wrap a smart pointer to base into a smart pointer to derived
+template <typename TargetT, typename SourceT>
+[[deprecated("Consider using eventCast() or visit() "
+ "instead")]] inline event_ptr_tt<TargetT>
+ptrCast(event_ptr_tt<SourceT>&& ptr)
+{
+ return unique_ptr_cast<TargetT>(ptr);
+}
+
+// === Standard Matrix key names and basicEventJson() ===
+
+static const auto TypeKey = QStringLiteral("type");
+static const auto BodyKey = QStringLiteral("body");
+static const auto ContentKey = QStringLiteral("content");
+static const auto EventIdKey = QStringLiteral("event_id");
+static const auto UnsignedKey = QStringLiteral("unsigned");
+static const auto StateKeyKey = QStringLiteral("state_key");
+static const auto TypeKeyL = "type"_ls;
+static const auto BodyKeyL = "body"_ls;
+static const auto ContentKeyL = "content"_ls;
+static const auto EventIdKeyL = "event_id"_ls;
+static const auto UnsignedKeyL = "unsigned"_ls;
+static const auto RedactedCauseKeyL = "redacted_because"_ls;
+static const auto PrevContentKeyL = "prev_content"_ls;
+static const auto StateKeyKeyL = "state_key"_ls;
+
+/// Make a minimal correct Matrix event JSON
+template <typename StrT>
+inline QJsonObject basicEventJson(StrT matrixType, const QJsonObject& content)
+{
+ return { { TypeKey, std::forward<StrT>(matrixType) },
+ { ContentKey, content } };
+}
- /// Unwrap a plain pointer from a smart pointer
- template <typename EventT>
- inline EventT* rawPtr(const event_ptr_tt<EventT>& ptr)
- {
- return ptr.get();
- }
+// === Event types and event types registry ===
- /// Unwrap a plain pointer and downcast it to the specified type
- template <typename TargetEventT, typename EventT>
- inline TargetEventT* weakPtrCast(const event_ptr_tt<EventT>& ptr)
- {
- return static_cast<TargetEventT*>(rawPtr(ptr));
- }
+using event_type_t = size_t;
+using event_mtype_t = const char*;
- /// Re-wrap a smart pointer to base into a smart pointer to derived
- template <typename TargetT, typename SourceT>
- [[deprecated("Consider using eventCast() or visit() instead")]]
- inline event_ptr_tt<TargetT> ptrCast(event_ptr_tt<SourceT>&& ptr)
- {
- return unique_ptr_cast<TargetT>(ptr);
- }
+class EventTypeRegistry {
+public:
+ ~EventTypeRegistry() = default;
+
+ static event_type_t initializeTypeId(event_mtype_t matrixTypeId);
- // === Standard Matrix key names and basicEventJson() ===
-
- static const auto TypeKey = QStringLiteral("type");
- static const auto BodyKey = QStringLiteral("body");
- static const auto ContentKey = QStringLiteral("content");
- static const auto EventIdKey = QStringLiteral("event_id");
- static const auto UnsignedKey = QStringLiteral("unsigned");
- static const auto StateKeyKey = QStringLiteral("state_key");
- static const auto TypeKeyL = "type"_ls;
- static const auto BodyKeyL = "body"_ls;
- static const auto ContentKeyL = "content"_ls;
- static const auto EventIdKeyL = "event_id"_ls;
- static const auto UnsignedKeyL = "unsigned"_ls;
- static const auto RedactedCauseKeyL = "redacted_because"_ls;
- static const auto PrevContentKeyL = "prev_content"_ls;
- static const auto StateKeyKeyL = "state_key"_ls;
-
- /// Make a minimal correct Matrix event JSON
- template <typename StrT>
- inline QJsonObject basicEventJson(StrT matrixType,
- const QJsonObject& content)
+ template <typename EventT>
+ static inline event_type_t initializeTypeId()
{
- return { { TypeKey, std::forward<StrT>(matrixType) },
- { ContentKey, content } };
+ return initializeTypeId(EventT::matrixTypeId());
}
- // === Event types and event types registry ===
+ static QString getMatrixType(event_type_t typeId);
- using event_type_t = size_t;
- using event_mtype_t = const char*;
+private:
+ EventTypeRegistry() = default;
+ Q_DISABLE_COPY(EventTypeRegistry)
+ DISABLE_MOVE(EventTypeRegistry)
- class EventTypeRegistry
+ static EventTypeRegistry& get()
{
- public:
- ~EventTypeRegistry() = default;
+ static EventTypeRegistry etr;
+ return etr;
+ }
- static event_type_t initializeTypeId(event_mtype_t matrixTypeId);
+ std::vector<event_mtype_t> eventTypes;
+};
- template <typename EventT>
- static inline event_type_t initializeTypeId()
- {
- return initializeTypeId(EventT::matrixTypeId());
- }
+template <>
+inline event_type_t EventTypeRegistry::initializeTypeId<void>()
+{
+ return initializeTypeId("");
+}
- static QString getMatrixType(event_type_t typeId);
+template <typename EventT>
+struct EventTypeTraits {
+ static event_type_t id()
+ {
+ static const auto id = EventTypeRegistry::initializeTypeId<EventT>();
+ return id;
+ }
+};
- private:
- EventTypeRegistry() = default;
- Q_DISABLE_COPY(EventTypeRegistry)
- DISABLE_MOVE(EventTypeRegistry)
+template <typename EventT>
+inline event_type_t typeId()
+{
+ return EventTypeTraits<std::decay_t<EventT>>::id();
+}
- static EventTypeRegistry& get()
- {
- static EventTypeRegistry etr;
- return etr;
- }
+inline event_type_t unknownEventTypeId() { return typeId<void>(); }
- std::vector<event_mtype_t> eventTypes;
- };
+// === EventFactory ===
- template <>
- inline event_type_t EventTypeRegistry::initializeTypeId<void>()
+/** 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 <typename BaseEventT>
+class EventFactory {
+public:
+ template <typename FnT>
+ static auto addMethod(FnT&& method)
{
- return initializeTypeId("");
+ factories().emplace_back(std::forward<FnT>(method));
+ return 0;
}
+ /** Chain two type factories
+ * Adds the factory class of EventT2 (EventT2::factory_t) to
+ * the list in factory class of EventT1 (EventT1::factory_t) so
+ * that when EventT1::factory_t::make() is invoked, types of
+ * EventT2 factory are looked through as well. This is used
+ * to include RoomEvent types into the more general Event factory,
+ * and state event types into the RoomEvent factory.
+ */
template <typename EventT>
- struct EventTypeTraits
+ static auto chainFactory()
{
- static event_type_t id()
- {
- static const auto id = EventTypeRegistry::initializeTypeId<EventT>();
- return id;
- }
- };
+ return addMethod(&EventT::factory_t::make);
+ }
- template <typename EventT>
- inline event_type_t typeId()
+ static event_ptr_tt<BaseEventT> make(const QJsonObject& json,
+ const QString& matrixType)
{
- return EventTypeTraits<std::decay_t<EventT>>::id();
+ for (const auto& f : factories())
+ if (auto e = f(json, matrixType))
+ return e;
+ return nullptr;
}
- inline event_type_t unknownEventTypeId() { return typeId<void>(); }
+private:
+ static auto& factories()
+ {
+ using inner_factory_tt = std::function<event_ptr_tt<BaseEventT>(
+ const QJsonObject&, const QString&)>;
+ static std::vector<inner_factory_tt> _factories {};
+ return _factories;
+ }
+};
- // === EventFactory ===
+/** Add a type to its default factory
+ * Adds a standard factory method (via makeEvent<>) for a given
+ * type to EventT::factory_t factory class so that it can be
+ * created dynamically from loadEvent<>().
+ *
+ * \tparam EventT the type to enable dynamic creation of
+ * \return the registered type id
+ * \sa loadEvent, Event::type
+ */
+template <typename EventT>
+inline auto setupFactory()
+{
+ qDebug(EVENTS) << "Adding factory method for" << EventT::matrixTypeId();
+ return EventT::factory_t::addMethod([](const QJsonObject& json,
+ const QString& jsonMatrixType) {
+ return EventT::matrixTypeId() == jsonMatrixType ? makeEvent<EventT>(json)
+ : nullptr;
+ });
+}
+
+template <typename EventT>
+inline auto registerEventType()
+{
+ // Initialise exactly once, even if this function is called twice for
+ // the same type (for whatever reason - you never know the ways of
+ // static initialisation is done).
+ static const auto _ = setupFactory<EventT>();
+ return _; // Only to facilitate usage in static initialisation
+}
+
+// === Event ===
+
+class Event {
+ Q_GADGET
+ Q_PROPERTY(Type type READ type CONSTANT)
+ Q_PROPERTY(QJsonObject contentJson READ contentJson CONSTANT)
+public:
+ using Type = event_type_t;
+ using factory_t = EventFactory<Event>;
+
+ 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& operator=(Event&&) = delete;
+ virtual ~Event();
+
+ Type type() const { return _type; }
+ QString matrixType() const;
+ QByteArray originalJson() const;
+ QJsonObject originalJsonObject() const { return fullJson(); }
+
+ const QJsonObject& fullJson() const { return _json; }
+
+ // According to the CS API spec, every event also has
+ // a "content" object; but since its structure is different for
+ // different types, we're implementing it per-event type.
+
+ const QJsonObject contentJson() const;
+ const QJsonObject unsignedJson() const;
- /** Create an event of arbitrary type from its arguments */
- template <typename EventT, typename... ArgTs>
- inline event_ptr_tt<EventT> makeEvent(ArgTs&&... args)
+ template <typename T>
+ T content(const QString& key) const
{
- return std::make_unique<EventT>(std::forward<ArgTs>(args)...);
+ return fromJson<T>(contentJson()[key]);
}
- template <typename BaseEventT>
- class EventFactory
- {
- public:
- template <typename FnT>
- static auto addMethod(FnT&& method)
- {
- factories().emplace_back(std::forward<FnT>(method));
- return 0;
- }
-
- /** Chain two type factories
- * Adds the factory class of EventT2 (EventT2::factory_t) to
- * the list in factory class of EventT1 (EventT1::factory_t) so
- * that when EventT1::factory_t::make() is invoked, types of
- * EventT2 factory are looked through as well. This is used
- * to include RoomEvent types into the more general Event factory,
- * and state event types into the RoomEvent factory.
- */
- template <typename EventT>
- static auto chainFactory()
- {
- return addMethod(&EventT::factory_t::make);
- }
-
- static event_ptr_tt<BaseEventT> make(const QJsonObject& json,
- const QString& matrixType)
- {
- for (const auto& f: factories())
- if (auto e = f(json, matrixType))
- return e;
- return nullptr;
- }
-
- private:
- static auto& factories()
- {
- using inner_factory_tt =
- std::function<event_ptr_tt<BaseEventT>(const QJsonObject&,
- const QString&)>;
- static std::vector<inner_factory_tt> _factories {};
- return _factories;
- }
- };
-
- /** Add a type to its default factory
- * Adds a standard factory method (via makeEvent<>) for a given
- * type to EventT::factory_t factory class so that it can be
- * created dynamically from loadEvent<>().
- *
- * \tparam EventT the type to enable dynamic creation of
- * \return the registered type id
- * \sa loadEvent, Event::type
- */
- template <typename EventT>
- inline auto setupFactory()
+ template <typename T>
+ T content(QLatin1String key) const
{
- qDebug(EVENTS) << "Adding factory method for" << EventT::matrixTypeId();
- return EventT::factory_t::addMethod(
- [] (const QJsonObject& json, const QString& jsonMatrixType)
- {
- return EventT::matrixTypeId() == jsonMatrixType
- ? makeEvent<EventT>(json) : nullptr;
- });
+ return fromJson<T>(contentJson()[key]);
}
- template <typename EventT>
- inline auto registerEventType()
+ friend QDebug operator<<(QDebug dbg, const Event& e)
{
- // Initialise exactly once, even if this function is called twice for
- // the same type (for whatever reason - you never know the ways of
- // static initialisation is done).
- static const auto _ = setupFactory<EventT>();
- return _; // Only to facilitate usage in static initialisation
+ QDebugStateSaver _dss { dbg };
+ dbg.noquote().nospace() << e.matrixType() << '(' << e.type() << "): ";
+ e.dumpTo(dbg);
+ return dbg;
}
- // === Event ===
+ virtual bool isStateEvent() const { return false; }
+ virtual bool isCallEvent() const { return false; }
+ virtual void dumpTo(QDebug dbg) const;
- class Event
- {
- Q_GADGET
- Q_PROPERTY(Type type READ type CONSTANT)
- Q_PROPERTY(QJsonObject contentJson READ contentJson CONSTANT)
- public:
- using Type = event_type_t;
- using factory_t = EventFactory<Event>;
-
- 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& operator=(Event&&) = delete;
- virtual ~Event();
-
- Type type() const { return _type; }
- QString matrixType() const;
- QByteArray originalJson() const;
- QJsonObject originalJsonObject() const { return fullJson(); }
-
- const QJsonObject& fullJson() const { return _json; }
-
- // According to the CS API spec, every event also has
- // a "content" object; but since its structure is different for
- // different types, we're implementing it per-event type.
-
- const QJsonObject contentJson() const;
- const QJsonObject unsignedJson() const;
-
- template <typename T>
- T content(const QString& key) const
- {
- return fromJson<T>(contentJson()[key]);
- }
-
- template <typename T>
- T content(QLatin1String key) const
- {
- return fromJson<T>(contentJson()[key]);
- }
-
- friend QDebug operator<<(QDebug dbg, const Event& e)
- {
- QDebugStateSaver _dss { dbg };
- dbg.noquote().nospace()
- << e.matrixType() << '(' << e.type() << "): ";
- e.dumpTo(dbg);
- return dbg;
- }
-
- virtual bool isStateEvent() const { return false; }
- virtual bool isCallEvent() const { return false; }
- virtual void dumpTo(QDebug dbg) const;
-
- protected:
- QJsonObject& editJson() { return _json; }
-
- private:
- Type _type;
- QJsonObject _json;
- };
- using EventPtr = event_ptr_tt<Event>;
+protected:
+ QJsonObject& editJson() { return _json; }
- template <typename EventT>
- using EventsArray = std::vector<event_ptr_tt<EventT>>;
- using Events = EventsArray<Event>;
+private:
+ Type _type;
+ QJsonObject _json;
+};
+using EventPtr = event_ptr_tt<Event>;
- // === Macros used with event class definitions ===
+template <typename EventT>
+using EventsArray = std::vector<event_ptr_tt<EventT>>;
+using Events = EventsArray<Event>;
- // 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_mtype_t matrixTypeId() { return _Id; } \
+// === Macros used with event class definitions ===
+
+// 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_mtype_t matrixTypeId() { return _Id; } \
static auto typeId() { return QMatrixClient::typeId<_Type>(); } \
// 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) \
- namespace { \
- [[gnu::unused]] \
- static const auto _factoryAdded##_Type = registerEventType<_Type>(); \
- } \
+// 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) \
+ namespace { \
+ [[gnu::unused]] static const auto _factoryAdded##_Type = \
+ registerEventType<_Type>(); \
+ } \
// End of macro
#ifdef USE_EVENTTYPE_ALIAS
- namespace EventType
+namespace EventType {
+ inline event_type_t logEventType(event_type_t id, const char* idName)
{
- inline event_type_t logEventType(event_type_t id, const char* idName)
- {
- qDebug(EVENTS) << "Using id" << id << "for" << idName;
- return id;
- }
+ qDebug(EVENTS) << "Using id" << id << "for" << idName;
+ return id;
}
-
- // This macro provides constants in EventType:: namespace for
- // back-compatibility with libQMatrixClient 0.3 event type system.
-#define DEFINE_EVENTTYPE_ALIAS(_Id, _Type) \
- namespace EventType \
- { \
- [[deprecated("Use is<>(), eventCast<>() or visit<>()")]] \
- static const auto _Id = logEventType(typeId<_Type>(), #_Id); \
- } \
- // End of macro
+} // namespace EventType
+
+// This macro provides constants in EventType:: namespace for
+// back-compatibility with libQMatrixClient 0.3 event type system.
+# define DEFINE_EVENTTYPE_ALIAS(_Id, _Type) \
+ namespace EventType { \
+ [[deprecated("Use is<>(), eventCast<>() or " \
+ "visit<>()")]] static const auto _Id = \
+ logEventType(typeId<_Type>(), #_Id); \
+ } \
+ // End of macro
#else
-#define DEFINE_EVENTTYPE_ALIAS(_Id, _Type) // Nothing
+# define DEFINE_EVENTTYPE_ALIAS(_Id, _Type) // Nothing
#endif
- // === is<>(), eventCast<>() and visit<>() ===
-
- template <typename EventT>
- inline bool is(const Event& e) { return e.type() == typeId<EventT>(); }
-
- inline bool isUnknown(const Event& e) { return e.type() == unknownEventTypeId(); }
+// === is<>(), eventCast<>() and visit<>() ===
- template <typename EventT, typename BasePtrT>
- inline auto eventCast(const BasePtrT& eptr)
- -> decltype(static_cast<EventT*>(&*eptr))
- {
- Q_ASSERT(eptr);
- return is<std::decay_t<EventT>>(*eptr)
- ? static_cast<EventT*>(&*eptr) : nullptr;
- }
-
- // A single generic catch-all visitor
- template <typename BaseEventT, typename FnT>
- inline auto visit(const BaseEventT& event, FnT&& visitor)
- -> decltype(visitor(event))
- {
- return visitor(event);
- }
-
- template <typename T>
- constexpr auto is_event()
- {
- return std::is_base_of<Event, std::decay_t<T>>::value;
- }
+template <typename EventT>
+inline bool is(const Event& e)
+{
+ return e.type() == typeId<EventT>();
+}
- template <typename T, typename FnT>
- constexpr auto needs_cast()
- {
- return !std::is_convertible<T, fn_arg_t<FnT>>::value;
- }
+inline bool isUnknown(const Event& e)
+{
+ return e.type() == unknownEventTypeId();
+}
- // A single type-specific void visitor
- template <typename BaseEventT, typename FnT>
- inline
- std::enable_if_t<
- is_event<BaseEventT>() && needs_cast<BaseEventT, FnT>() &&
- std::is_void<fn_return_t<FnT>>::value>
- visit(const BaseEventT& event, FnT&& visitor)
- {
- using event_type = fn_arg_t<FnT>;
- if (is<std::decay_t<event_type>>(event))
- visitor(static_cast<event_type>(event));
- }
+template <typename EventT, typename BasePtrT>
+inline auto eventCast(const BasePtrT& eptr)
+ -> decltype(static_cast<EventT*>(&*eptr))
+{
+ Q_ASSERT(eptr);
+ return is<std::decay_t<EventT>>(*eptr) ? static_cast<EventT*>(&*eptr)
+ : nullptr;
+}
+
+// A single generic catch-all visitor
+template <typename BaseEventT, typename FnT>
+inline auto visit(const BaseEventT& event, FnT&& visitor)
+ -> decltype(visitor(event))
+{
+ return visitor(event);
+}
- // A single type-specific non-void visitor with an optional default value
- template <typename BaseEventT, typename FnT>
- inline
- std::enable_if_t<
- is_event<BaseEventT>() && needs_cast<BaseEventT, FnT>(),
- fn_return_t<FnT>> // non-voidness is guarded by defaultValue type
- visit(const BaseEventT& event, FnT&& visitor,
- fn_return_t<FnT>&& defaultValue = {})
- {
- using event_type = fn_arg_t<FnT>;
- if (is<std::decay_t<event_type>>(event))
- return visitor(static_cast<event_type>(event));
- return std::forward<fn_return_t<FnT>>(defaultValue);
- }
+template <typename T>
+constexpr auto is_event()
+{
+ return std::is_base_of<Event, std::decay_t<T>>::value;
+}
- // A chain of 2 or more visitors
- template <typename BaseEventT, typename FnT1, typename FnT2, typename... FnTs>
- inline
- std::enable_if_t<is_event<BaseEventT>(), fn_return_t<FnT1>>
- visit(const BaseEventT& event, FnT1&& visitor1, FnT2&& visitor2,
- FnTs&&... visitors)
- {
- using event_type1 = fn_arg_t<FnT1>;
- if (is<std::decay_t<event_type1>>(event))
- return visitor1(static_cast<event_type1&>(event));
- return visit(event, std::forward<FnT2>(visitor2),
- std::forward<FnTs>(visitors)...);
- }
-} // namespace QMatrixClient
+template <typename T, typename FnT>
+constexpr auto needs_cast()
+{
+ return !std::is_convertible<T, fn_arg_t<FnT>>::value;
+}
+
+// A single type-specific void visitor
+template <typename BaseEventT, typename FnT>
+inline std::enable_if_t<is_event<BaseEventT>() && needs_cast<BaseEventT, FnT>()
+ && std::is_void<fn_return_t<FnT>>::value>
+visit(const BaseEventT& event, FnT&& visitor)
+{
+ using event_type = fn_arg_t<FnT>;
+ if (is<std::decay_t<event_type>>(event))
+ visitor(static_cast<event_type>(event));
+}
+
+// A single type-specific non-void visitor with an optional default value
+template <typename BaseEventT, typename FnT>
+inline std::enable_if_t<is_event<BaseEventT>() && needs_cast<BaseEventT, FnT>(),
+ fn_return_t<FnT>> // non-voidness is guarded by
+ // defaultValue type
+visit(const BaseEventT& event, FnT&& visitor,
+ fn_return_t<FnT>&& defaultValue = {})
+{
+ using event_type = fn_arg_t<FnT>;
+ if (is<std::decay_t<event_type>>(event))
+ return visitor(static_cast<event_type>(event));
+ return std::forward<fn_return_t<FnT>>(defaultValue);
+}
+
+// A chain of 2 or more visitors
+template <typename BaseEventT, typename FnT1, typename FnT2, typename... FnTs>
+inline std::enable_if_t<is_event<BaseEventT>(), fn_return_t<FnT1>>
+visit(const BaseEventT& event, FnT1&& visitor1, FnT2&& visitor2,
+ FnTs&&... visitors)
+{
+ using event_type1 = fn_arg_t<FnT1>;
+ if (is<std::decay_t<event_type1>>(event))
+ return visitor1(static_cast<event_type1&>(event));
+ return visit(event, std::forward<FnT2>(visitor2),
+ std::forward<FnTs>(visitors)...);
+}
+} // namespace QMatrixClient
Q_DECLARE_METATYPE(QMatrixClient::Event*)
Q_DECLARE_METATYPE(const QMatrixClient::Event*)