diff options
author | Alexey Rusakov <Kitsune-Ral@users.sf.net> | 2022-08-12 16:46:01 +0200 |
---|---|---|
committer | Alexey Rusakov <Kitsune-Ral@users.sf.net> | 2022-09-04 18:42:11 +0200 |
commit | 17cd3beaefa5501a902e08c7644e8cd97c9091a0 (patch) | |
tree | ca98dd3a927bb45f6ff5f4749b5e04f83e4ed321 /lib/roomstateview.h | |
parent | a18f505fe7ca66556d66538a7c9b9ff31d2c1b29 (diff) | |
download | libquotient-17cd3beaefa5501a902e08c7644e8cd97c9091a0.tar.gz libquotient-17cd3beaefa5501a902e08c7644e8cd97c9091a0.zip |
Streamline event types
This commit introduces a few things to further reduce the boilerplate
across event type definitions:
- Event type is no more separately stored in Event and therefore no more
passed to base event constructors. Until the previous commit, it was
used by is() to quickly match the event type; with the new event
metatype class, the same is achieved even quicker by comparing
metatype pointers.
- EventTemplate is a generalisation of StateEvent for all event types
providing common constructor signatures and content() for (most) leaf
event types. StateEvent therefore has become a partial specialisation
of EventTemplate for types derived from StateEventBase; as the known
client code base does not use it directly, a compatibility alias is
not provided. Also, DEFINE_SIMPLE_EVENT now expands into a class
deriving from EventTemplate.
- On top of StateEvent->EventTemplate specialisation,
KeyedStateEventBase and KeylessStateEventBase types are introduced
with appropriate constructor signatures (with or without state_key,
respectively) to allow `using` of them from derived event types.
To facilitate writing of constraints, concepts for keyed and keyless
state event types are also introduced; RoomStateView, e.g., makes use
of those to provide appropriate method signatures.
- typeId(), unknownEventTypeId(), UnknownEventTypeId are no more
provided - they weren't used throughout the known code base
(Quaternion, NeoChat), and the concept of "unknown event types" is
hereby eliminated entirely.
- RoomKeyEvent no more accepts senderId as a parameter; it has never
been a good practice as the sender is assigned by Connection anyway.
Diffstat (limited to 'lib/roomstateview.h')
-rw-r--r-- | lib/roomstateview.h | 114 |
1 files changed, 89 insertions, 25 deletions
diff --git a/lib/roomstateview.h b/lib/roomstateview.h index 29cce00e..13b375f2 100644 --- a/lib/roomstateview.h +++ b/lib/roomstateview.h @@ -11,6 +11,16 @@ namespace Quotient { class Room; +// NB: Both concepts below expect EvT::needsStateKey to exist so you can't +// express one via negation of the other (there's still an invalid case of +// a non-state event where needsStateKey is not even defined). + +template <typename FnT, class EvT = std::decay_t<fn_arg_t<FnT>>> +concept Keyed_State_Fn = EvT::needsStateKey; + +template <typename FnT, class EvT = std::decay_t<fn_arg_t<FnT>>> +concept Keyless_State_Fn = !EvT::needsStateKey; + class QUOTIENT_API RoomStateView : private QHash<StateEventKey, const StateEventBase*> { Q_GADGET @@ -36,31 +46,48 @@ public: //! \brief Get a state event with the given event type and state key //! //! This is a typesafe overload that accepts a C++ event type instead of - //! its Matrix name. - //! \warning In libQuotient 0.7 the return type changed to an Omittable with - //! a reference wrapper inside - you have to check that it - //! has_value() before using. Alternatively you can now use - //! queryCurrentState() to access state safely. - template <typename EvT> + //! its Matrix name. It is only defined for events with state key (i.e., + //! derived from KeyedStateEvent). + template <Keyed_State_Event EvT> const EvT* get(const QString& stateKey = {}) const { - static_assert(std::is_base_of_v<StateEventBase, EvT>); - if (const auto* evt = get(EvT::matrixTypeId(), stateKey)) { - Q_ASSERT(evt->matrixType() == EvT::matrixTypeId() + if (const auto* evt = get(EvT::TypeId, stateKey)) { + Q_ASSERT(evt->matrixType() == EvT::TypeId && evt->stateKey() == stateKey); return eventCast<const EvT>(evt); } return nullptr; } + //! \brief Get a state event with the given event type + //! + //! This is a typesafe overload that accepts a C++ event type instead of + //! its Matrix name. This overload only defined for events that do not use + //! state key (i.e., derived from KeylessStateEvent). + template <Keyless_State_Event EvT> + const EvT* get() const + { + if (const auto* evt = get(EvT::TypeId)) { + Q_ASSERT(evt->matrixType() == EvT::TypeId); + return eventCast<const EvT>(evt); + } + return nullptr; + } + using QHash::contains; bool contains(const QString& evtType, const QString& stateKey = {}) const; - template <typename EvT> + template <Keyed_State_Event EvT> bool contains(const QString& stateKey = {}) const { - return contains(EvT::matrixTypeId(), stateKey); + return contains(EvT::TypeId, stateKey); + } + + template <Keyless_State_Event EvT> + bool contains() const + { + return contains(EvT::TypeId); } //! \brief Get the content of the current state event with the given @@ -78,48 +105,85 @@ public: const QVector<const StateEventBase*> eventsOfType(const QString& evtType) const; + //! \brief Run a function on a state event with the given type and key + //! + //! Use this overload when there's no predefined event type or the event + //! type is unknown at compile time. + //! \return an Omittable with either the result of the function call, or + //! with `none` if the event is not found or the function fails template <typename FnT> auto query(const QString& evtType, const QString& stateKey, FnT&& fn) const { return lift(std::forward<FnT>(fn), get(evtType, stateKey)); } - template <typename FnT> + //! \brief Run a function on a state event with the given type and key + //! + //! This is an overload for keyed state events (those that have + //! `needsStateKey == true`) with type defined at compile time. + //! \return an Omittable with either the result of the function call, or + //! with `none` if the event is not found or the function fails + template <Keyed_State_Fn FnT> auto query(const QString& stateKey, FnT&& fn) const { using EventT = std::decay_t<fn_arg_t<FnT>>; - static_assert(std::is_base_of_v<StateEventBase, EventT>); return lift(std::forward<FnT>(fn), get<EventT>(stateKey)); } + //! \brief Run a function on a keyless state event with the given type + //! + //! This is an overload for keyless state events (those having + //! `needsStateKey == false`) with type defined at compile time. + //! \return an Omittable with either the result of the function call, or + //! with `none` if the event is not found or the function fails + template <Keyless_State_Fn FnT> + auto query(FnT&& fn) const + { + using EventT = std::decay_t<fn_arg_t<FnT>>; + return lift(std::forward<FnT>(fn), get<EventT>()); + } + + //! \brief Same as query() but with a fallback value + //! + //! This is a shortcut for `query().value_or()`, passing respective + //! arguments to the respective functions. This is an overload for the case + //! when the event type cannot be fixed at compile time. + //! \return the result of \p fn execution, or \p fallback if the requested + //! event doesn't exist or the function fails template <typename FnT, typename FallbackT> auto queryOr(const QString& evtType, const QString& stateKey, FnT&& fn, FallbackT&& fallback) const { - return lift(std::forward<FnT>(fn), get(evtType, stateKey)) + return query(evtType, stateKey, std::forward<FnT>(fn)) .value_or(std::forward<FallbackT>(fallback)); } - template <typename FnT> - auto query(FnT&& fn) const - { - return query({}, std::forward<FnT>(fn)); - } - + //! \brief Same as query() but with a fallback value + //! + //! This is a shortcut for `query().value_or()`, passing respective + //! arguments to the respective functions. This is an overload for the case + //! when the event type cannot be fixed at compile time. + //! \return the result of \p fn execution, or \p fallback if the requested + //! event doesn't exist or the function fails template <typename FnT, typename FallbackT> auto queryOr(const QString& stateKey, FnT&& fn, FallbackT&& fallback) const { - using EventT = std::decay_t<fn_arg_t<FnT>>; - static_assert(std::is_base_of_v<StateEventBase, EventT>); - return lift(std::forward<FnT>(fn), get<EventT>(stateKey)) + return query(stateKey, std::forward<FnT>(fn)) .value_or(std::forward<FallbackT>(fallback)); } + //! \brief Same as query() but with a fallback value + //! + //! This is a shortcut for `query().value_or()`, passing respective + //! arguments to the respective functions. This is an overload for the case + //! when the event type cannot be fixed at compile time. + //! \return the result of \p fn execution, or \p fallback if the requested + //! event doesn't exist or the function fails template <typename FnT, typename FallbackT> auto queryOr(FnT&& fn, FallbackT&& fallback) const { - return queryOr({}, std::forward<FnT>(fn), - std::forward<FallbackT>(fallback)); + return query(std::forward<FnT>(fn)) + .value_or(std::forward<FallbackT>(fallback)); } private: |