From c3a0515ac2ead7b2d0653be3517836cd31be480e Mon Sep 17 00:00:00 2001 From: Alexey Rusakov Date: Thu, 2 Dec 2021 21:18:30 +0100 Subject: Cleanup on Sonar issues --- lib/converters.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'lib/converters.h') diff --git a/lib/converters.h b/lib/converters.h index 90349019..9c3d5749 100644 --- a/lib/converters.h +++ b/lib/converters.h @@ -27,8 +27,8 @@ struct JsonObjectConverter { //! \brief The switchboard for extra conversion algorithms behind from/toJson //! //! This template is mainly intended for partial conversion specialisations -//! since from/toJson are functions and cannot be partially specialised; -//! another case for JsonConverter is to insulate types that can be constructed +//! since from/toJson are functions and cannot be partially specialised. +//! Another case for JsonConverter is to insulate types that can be constructed //! from basic types - namely, QVariant and QUrl can be directly constructed //! from QString and having an overload or specialisation for those leads to //! ambiguity between these and QJsonValue. For trivial (converting -- cgit v1.2.3 From 7350fe82953cf6274b8845a890eafb21a09b9931 Mon Sep 17 00:00:00 2001 From: Alexey Rusakov Date: Wed, 29 Dec 2021 15:59:58 +0100 Subject: Add QUOTIENT_API throughout non-generated code This include all (hopefully) classes/structures and functions that have non-inline definitions, as well as namespaces with Q_NAMESPACE since those have non-inline (as of Qt 5.15) QMetaObject - for that a new macro, QUO_NAMESPACE, has been devised to accommodate the lack of Q_NAMESPACE_EXPORT in Qt before 5.14. --- lib/accountregistry.h | 4 +++- lib/avatar.h | 4 +++- lib/connection.h | 2 +- lib/converters.h | 6 +++--- lib/eventitem.cpp | 4 ++++ lib/eventitem.h | 9 +++++---- lib/events/accountdataevents.h | 2 +- lib/events/callanswerevent.h | 3 +-- lib/events/callhangupevent.h | 2 +- lib/events/callinviteevent.h | 2 +- lib/events/directchatevent.h | 2 +- lib/events/encryptedevent.h | 2 +- lib/events/encryptionevent.h | 5 ++--- lib/events/event.h | 23 +++++++++++++---------- lib/events/eventcontent.h | 19 ++++++++++--------- lib/events/reactionevent.h | 6 +++--- lib/events/receiptevent.h | 2 +- lib/events/roomavatarevent.h | 3 ++- lib/events/roomcreateevent.h | 2 +- lib/events/roomevent.h | 4 ++-- lib/events/roomkeyevent.h | 2 +- lib/events/roommemberevent.h | 5 ++--- lib/events/roommessageevent.h | 8 ++++---- lib/events/roompowerlevelsevent.h | 8 +++----- lib/events/roomtombstoneevent.h | 2 +- lib/events/simplestateevents.h | 3 ++- lib/events/stateevent.h | 2 +- lib/events/stickerevent.h | 2 +- lib/events/typingevent.h | 2 +- lib/eventstats.h | 4 ++-- lib/jobs/basejob.h | 4 ++-- lib/jobs/downloadfilejob.h | 2 +- lib/jobs/mediathumbnailjob.h | 2 +- lib/jobs/requestdata.h | 4 +++- lib/mxcreply.h | 4 +++- lib/networkaccessmanager.h | 4 +++- lib/networksettings.h | 2 +- lib/quotient_common.h | 17 ++++++++++++++++- lib/room.h | 8 ++++---- lib/settings.h | 8 +++++--- lib/ssosession.h | 4 +++- lib/syncdata.h | 2 +- lib/uri.h | 2 +- lib/uriresolver.h | 6 +++--- lib/user.h | 3 ++- lib/util.h | 22 ++++++++++++---------- 46 files changed, 138 insertions(+), 100 deletions(-) (limited to 'lib/converters.h') diff --git a/lib/accountregistry.h b/lib/accountregistry.h index 5efda459..f7a864df 100644 --- a/lib/accountregistry.h +++ b/lib/accountregistry.h @@ -4,6 +4,8 @@ #pragma once +#include "quotient_export.h" + #include #include #include @@ -11,7 +13,7 @@ namespace Quotient { class Connection; -class AccountRegistry : public QAbstractListModel { +class QUOTIENT_API AccountRegistry : public QAbstractListModel { Q_OBJECT public: enum EventRoles { diff --git a/lib/avatar.h b/lib/avatar.h index d4634aea..93f43948 100644 --- a/lib/avatar.h +++ b/lib/avatar.h @@ -3,6 +3,8 @@ #pragma once +#include "quotient_export.h" + #include #include @@ -12,7 +14,7 @@ namespace Quotient { class Connection; -class Avatar { +class QUOTIENT_API Avatar { public: explicit Avatar(); explicit Avatar(QUrl url); diff --git a/lib/connection.h b/lib/connection.h index 0713af16..d669462e 100644 --- a/lib/connection.h +++ b/lib/connection.h @@ -105,7 +105,7 @@ using DirectChatsMap = QMultiHash; using DirectChatUsersMap = QMultiHash; using IgnoredUsersList = IgnoredUsersEvent::content_type; -class Connection : public QObject { +class QUOTIENT_API Connection : public QObject { Q_OBJECT Q_PROPERTY(User* localUser READ user NOTIFY stateChanged) diff --git a/lib/converters.h b/lib/converters.h index 9c3d5749..a6028f1b 100644 --- a/lib/converters.h +++ b/lib/converters.h @@ -183,7 +183,7 @@ struct JsonConverter { }; template <> -struct JsonConverter { +struct QUOTIENT_API JsonConverter { static QJsonValue dump(const QVariant& v); static QVariant load(const QJsonValue& jv); }; @@ -281,9 +281,9 @@ template struct JsonObjectConverter> : public HashMapFromJson> {}; -QJsonObject toJson(const QVariantHash& vh); +QJsonObject QUOTIENT_API toJson(const QVariantHash& vh); template <> -QVariantHash fromJson(const QJsonValue& jv); +QVariantHash QUOTIENT_API fromJson(const QJsonValue& jv); // Conditional insertion into a QJsonObject diff --git a/lib/eventitem.cpp b/lib/eventitem.cpp index 4f1595bc..a2d65d8d 100644 --- a/lib/eventitem.cpp +++ b/lib/eventitem.cpp @@ -25,3 +25,7 @@ void PendingEventItem::setFileUploaded(const QUrl& remoteUrl) } setStatus(EventStatus::FileUploaded); } + +// Not exactly sure why but this helps with the linker not finding +// Quotient::EventStatus::staticMetaObject when building Quaternion +#include "moc_eventitem.cpp" diff --git a/lib/eventitem.h b/lib/eventitem.h index 0ab1a01d..2f1e72cc 100644 --- a/lib/eventitem.h +++ b/lib/eventitem.h @@ -4,6 +4,7 @@ #pragma once #include "events/stateevent.h" +#include "quotient_common.h" #include #include @@ -11,7 +12,7 @@ namespace Quotient { namespace EventStatus { - Q_NAMESPACE + QUO_NAMESPACE /** Special marks an event can assume * @@ -33,7 +34,7 @@ namespace EventStatus { Q_ENUM_NS(Code) } // namespace EventStatus -class EventItemBase { +class QUOTIENT_API EventItemBase { public: explicit EventItemBase(RoomEventPtr&& e) : evt(std::move(e)) { @@ -74,7 +75,7 @@ private: std::any data; }; -class TimelineItem : public EventItemBase { +class QUOTIENT_API TimelineItem : public EventItemBase { public: // For compatibility with Qt containers, even though we use // a std:: container now for the room timeline @@ -103,7 +104,7 @@ inline const CallEventBase* EventItemBase::viewAs() const return evt->isCallEvent() ? weakPtrCast(evt) : nullptr; } -class PendingEventItem : public EventItemBase { +class QUOTIENT_API PendingEventItem : public EventItemBase { public: using EventItemBase::EventItemBase; diff --git a/lib/events/accountdataevents.h b/lib/events/accountdataevents.h index 9cf77be3..c0f2202d 100644 --- a/lib/events/accountdataevents.h +++ b/lib/events/accountdataevents.h @@ -50,7 +50,7 @@ struct JsonObjectConverter { using TagsMap = QHash; #define DEFINE_SIMPLE_EVENT(_Name, _TypeId, _ContentType, _ContentKey) \ - class _Name : public Event { \ + class QUOTIENT_API _Name : public Event { \ public: \ using content_type = _ContentType; \ DEFINE_EVENT_TYPEID(_TypeId, _Name) \ diff --git a/lib/events/callanswerevent.h b/lib/events/callanswerevent.h index 4c01c941..8ffe60f2 100644 --- a/lib/events/callanswerevent.h +++ b/lib/events/callanswerevent.h @@ -7,7 +7,7 @@ #include "roomevent.h" namespace Quotient { -class CallAnswerEvent : public CallEventBase { +class QUOTIENT_API CallAnswerEvent : public CallEventBase { public: DEFINE_EVENT_TYPEID("m.call.answer", CallAnswerEvent) @@ -26,6 +26,5 @@ public: return contentPart("answer"_ls).value("sdp"_ls).toString(); } }; - REGISTER_EVENT_TYPE(CallAnswerEvent) } // namespace Quotient diff --git a/lib/events/callhangupevent.h b/lib/events/callhangupevent.h index f3f82833..b0017c59 100644 --- a/lib/events/callhangupevent.h +++ b/lib/events/callhangupevent.h @@ -7,7 +7,7 @@ #include "roomevent.h" namespace Quotient { -class CallHangupEvent : public CallEventBase { +class QUOTIENT_API CallHangupEvent : public CallEventBase { public: DEFINE_EVENT_TYPEID("m.call.hangup", CallHangupEvent) diff --git a/lib/events/callinviteevent.h b/lib/events/callinviteevent.h index 80b7d651..47362b5c 100644 --- a/lib/events/callinviteevent.h +++ b/lib/events/callinviteevent.h @@ -7,7 +7,7 @@ #include "roomevent.h" namespace Quotient { -class CallInviteEvent : public CallEventBase { +class QUOTIENT_API CallInviteEvent : public CallEventBase { public: DEFINE_EVENT_TYPEID("m.call.invite", CallInviteEvent) diff --git a/lib/events/directchatevent.h b/lib/events/directchatevent.h index e2143779..2018d3d6 100644 --- a/lib/events/directchatevent.h +++ b/lib/events/directchatevent.h @@ -6,7 +6,7 @@ #include "event.h" namespace Quotient { -class DirectChatEvent : public Event { +class QUOTIENT_API DirectChatEvent : public Event { public: DEFINE_EVENT_TYPEID("m.direct", DirectChatEvent) diff --git a/lib/events/encryptedevent.h b/lib/events/encryptedevent.h index de89a7c6..81343a29 100644 --- a/lib/events/encryptedevent.h +++ b/lib/events/encryptedevent.h @@ -25,7 +25,7 @@ namespace Quotient { * in general. It's possible, because RoomEvent interface is similar to Event's * one and doesn't add new restrictions, just provides additional features. */ -class EncryptedEvent : public RoomEvent { +class QUOTIENT_API EncryptedEvent : public RoomEvent { public: DEFINE_EVENT_TYPEID("m.room.encrypted", EncryptedEvent) diff --git a/lib/events/encryptionevent.h b/lib/events/encryptionevent.h index 14439fcc..dfb28b2f 100644 --- a/lib/events/encryptionevent.h +++ b/lib/events/encryptionevent.h @@ -8,7 +8,7 @@ #include "stateevent.h" namespace Quotient { -class EncryptionEventContent : public EventContent::Base { +class QUOTIENT_API EncryptionEventContent : public EventContent::Base { public: enum EncryptionType : size_t { MegolmV1AesSha2 = 0, Undefined }; @@ -26,7 +26,7 @@ protected: using EncryptionType = EncryptionEventContent::EncryptionType; -class EncryptionEvent : public StateEvent { +class QUOTIENT_API EncryptionEvent : public StateEvent { Q_GADGET public: DEFINE_EVENT_TYPEID("m.room.encryption", EncryptionEvent) @@ -51,6 +51,5 @@ public: int rotationPeriodMs() const { return content().rotationPeriodMs; } int rotationPeriodMsgs() const { return content().rotationPeriodMsgs; } }; - REGISTER_EVENT_TYPE(EncryptionEvent) } // namespace Quotient diff --git a/lib/events/event.h b/lib/events/event.h index 8f62872d..8a0076d0 100644 --- a/lib/events/event.h +++ b/lib/events/event.h @@ -60,14 +60,14 @@ inline QJsonObject basicEventJson(const QString& matrixType, using event_type_t = size_t; using event_mtype_t = const char*; -class EventTypeRegistry { +class QUOTIENT_API EventTypeRegistry { public: ~EventTypeRegistry() = default; static event_type_t initializeTypeId(event_mtype_t matrixTypeId); template - static inline event_type_t initializeTypeId() + static event_type_t initializeTypeId() { return initializeTypeId(EventT::matrixTypeId()); } @@ -190,7 +190,7 @@ namespace _impl { // === Event === -class Event { +class QUOTIENT_API Event { public: using Type = event_type_t; static inline _impl::EventFactory factory { "Event" }; @@ -243,7 +243,7 @@ public: return fromJson(unsignedJson()[std::forward(key)]); } - friend QDebug operator<<(QDebug dbg, const Event& e) + friend QUOTIENT_API QDebug operator<<(QDebug dbg, const Event& e) { QDebugStateSaver _dss { dbg }; dbg.noquote().nospace() << e.matrixType() << '(' << e.type() << "): "; @@ -272,17 +272,20 @@ using Events = EventsArray; // 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 Quotient::typeId<_Type>(); } \ +#define DEFINE_EVENT_TYPEID(_Id, _Type) \ + static QUOTIENT_EXPORT constexpr event_mtype_t matrixTypeId() \ + { \ + return _Id; \ + } \ + static QUOTIENT_EXPORT auto typeId() { return Quotient::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) \ - [[maybe_unused]] inline const auto _factoryAdded##_Type = \ - _Type::factory.addMethod<_Type>(); \ +#define REGISTER_EVENT_TYPE(_Type) \ + [[maybe_unused]] QUOTIENT_API inline const auto _factoryAdded##_Type = \ + _Type::factory.addMethod<_Type>(); \ // End of macro // === Event loading === diff --git a/lib/events/eventcontent.h b/lib/events/eventcontent.h index f609a603..bfa7d926 100644 --- a/lib/events/eventcontent.h +++ b/lib/events/eventcontent.h @@ -6,14 +6,15 @@ // This file contains generic event content definitions, applicable to room // message events as well as other events (e.g., avatars). +#include "encryptedfile.h" +#include "quotient_export.h" + #include #include #include #include #include -#include "encryptedfile.h" - class QFileInfo; namespace Quotient { @@ -28,7 +29,7 @@ namespace EventContent { * assumed but not required that a content object can also be created * from plain data. */ - class Base { + class QUOTIENT_API Base { public: explicit Base(QJsonObject o = {}) : originalJson(std::move(o)) {} virtual ~Base() = default; @@ -76,7 +77,7 @@ namespace EventContent { * * This class is not polymorphic. */ - class FileInfo { + class QUOTIENT_API FileInfo { public: FileInfo() = default; explicit FileInfo(const QFileInfo& fi); @@ -121,7 +122,7 @@ namespace EventContent { /** * A content info class for image content types: image, thumbnail, video */ - class ImageInfo : public FileInfo { + class QUOTIENT_API ImageInfo : public FileInfo { public: ImageInfo() = default; explicit ImageInfo(const QFileInfo& fi, QSize imageSize = {}); @@ -146,7 +147,7 @@ namespace EventContent { * the JSON representation of event content; namely, * "info/thumbnail_url" and "info/thumbnail_info" fields are used. */ - class Thumbnail : public ImageInfo { + class QUOTIENT_API Thumbnail : public ImageInfo { public: Thumbnail() = default; // Allow empty thumbnails Thumbnail(const QJsonObject& infoJson, const Omittable &file = none); @@ -160,7 +161,7 @@ namespace EventContent { void fillInfoJson(QJsonObject* infoJson) const; }; - class TypedBase : public Base { + class QUOTIENT_API TypedBase : public Base { public: virtual QMimeType type() const = 0; virtual const FileInfo* fileInfo() const { return nullptr; } @@ -183,7 +184,7 @@ namespace EventContent { * \tparam InfoT base info class */ template - class UrlBasedContent : public TypedBase, public InfoT { + class QUOTIENT_API UrlBasedContent : public TypedBase, public InfoT { public: using InfoT::InfoT; explicit UrlBasedContent(const QJsonObject& json) @@ -215,7 +216,7 @@ namespace EventContent { }; template - class UrlWithThumbnailContent : public UrlBasedContent { + class QUOTIENT_API UrlWithThumbnailContent : public UrlBasedContent { public: // NB: when using inherited constructors, thumbnail has to be // initialised separately diff --git a/lib/events/reactionevent.h b/lib/events/reactionevent.h index 5a2b98c4..ce11eaed 100644 --- a/lib/events/reactionevent.h +++ b/lib/events/reactionevent.h @@ -7,7 +7,7 @@ namespace Quotient { -struct EventRelation { +struct QUOTIENT_API EventRelation { using reltypeid_t = const char*; static constexpr reltypeid_t Reply() { return "m.in_reply_to"; } static constexpr reltypeid_t Annotation() { return "m.annotation"; } @@ -31,12 +31,12 @@ struct EventRelation { } }; template <> -struct JsonObjectConverter { +struct QUOTIENT_API JsonObjectConverter { static void dumpTo(QJsonObject& jo, const EventRelation& pod); static void fillFrom(const QJsonObject& jo, EventRelation& pod); }; -class ReactionEvent : public RoomEvent { +class QUOTIENT_API ReactionEvent : public RoomEvent { public: DEFINE_EVENT_TYPEID("m.reaction", ReactionEvent) diff --git a/lib/events/receiptevent.h b/lib/events/receiptevent.h index 9683deef..5e077e47 100644 --- a/lib/events/receiptevent.h +++ b/lib/events/receiptevent.h @@ -19,7 +19,7 @@ struct ReceiptsForEvent { }; using EventsWithReceipts = QVector; -class ReceiptEvent : public Event { +class QUOTIENT_API ReceiptEvent : public Event { public: DEFINE_EVENT_TYPEID("m.receipt", ReceiptEvent) explicit ReceiptEvent(const EventsWithReceipts& ewrs); diff --git a/lib/events/roomavatarevent.h b/lib/events/roomavatarevent.h index 8618ba31..c54b5801 100644 --- a/lib/events/roomavatarevent.h +++ b/lib/events/roomavatarevent.h @@ -7,7 +7,8 @@ #include "stateevent.h" namespace Quotient { -class RoomAvatarEvent : public StateEvent { +class QUOTIENT_API RoomAvatarEvent + : public StateEvent { // It's a bit of an overkill to use a full-fledged ImageContent // because in reality m.room.avatar usually only has a single URL, // without a thumbnail. But The Spec says there be thumbnails, and diff --git a/lib/events/roomcreateevent.h b/lib/events/roomcreateevent.h index b3ad287c..016855b9 100644 --- a/lib/events/roomcreateevent.h +++ b/lib/events/roomcreateevent.h @@ -7,7 +7,7 @@ #include "quotient_common.h" namespace Quotient { -class RoomCreateEvent : public StateEventBase { +class QUOTIENT_API RoomCreateEvent : public StateEventBase { public: DEFINE_EVENT_TYPEID("m.room.create", RoomCreateEvent) diff --git a/lib/events/roomevent.h b/lib/events/roomevent.h index 8be58481..3fbb247e 100644 --- a/lib/events/roomevent.h +++ b/lib/events/roomevent.h @@ -11,7 +11,7 @@ namespace Quotient { class RedactionEvent; /** This class corresponds to m.room.* events */ -class RoomEvent : public Event { +class QUOTIENT_API RoomEvent : public Event { public: static inline _impl::EventFactory factory { "RoomEvent" }; @@ -70,7 +70,7 @@ using RoomEventPtr = event_ptr_tt; using RoomEvents = EventsArray; using RoomEventsRange = Range; -class CallEventBase : public RoomEvent { +class QUOTIENT_API CallEventBase : public RoomEvent { public: CallEventBase(Type type, event_mtype_t matrixType, const QString& callId, int version, const QJsonObject& contentJson = {}); diff --git a/lib/events/roomkeyevent.h b/lib/events/roomkeyevent.h index d021fbec..c4df7936 100644 --- a/lib/events/roomkeyevent.h +++ b/lib/events/roomkeyevent.h @@ -6,7 +6,7 @@ #include "event.h" namespace Quotient { -class RoomKeyEvent : public Event +class QUOTIENT_API RoomKeyEvent : public Event { public: DEFINE_EVENT_TYPEID("m.room_key", RoomKeyEvent) diff --git a/lib/events/roommemberevent.h b/lib/events/roommemberevent.h index 0fb464d4..5e446dbe 100644 --- a/lib/events/roommemberevent.h +++ b/lib/events/roommemberevent.h @@ -10,7 +10,7 @@ #include "quotient_common.h" namespace Quotient { -class MemberEventContent : public EventContent::Base { +class QUOTIENT_API MemberEventContent : public EventContent::Base { public: using MembershipType [[deprecated("Use Quotient::Membership instead")]] = Membership; @@ -33,7 +33,7 @@ protected: using MembershipType [[deprecated("Use Membership instead")]] = Membership; -class RoomMemberEvent : public StateEvent { +class QUOTIENT_API RoomMemberEvent : public StateEvent { Q_GADGET public: DEFINE_EVENT_TYPEID("m.room.member", RoomMemberEvent) @@ -95,6 +95,5 @@ doLoadEvent(const QJsonObject& json, const QString& matrixType) return makeEvent(json); return makeEvent(unknownEventTypeId(), json); } - REGISTER_EVENT_TYPE(RoomMemberEvent) } // namespace Quotient diff --git a/lib/events/roommessageevent.h b/lib/events/roommessageevent.h index 56597ddc..0c901b7a 100644 --- a/lib/events/roommessageevent.h +++ b/lib/events/roommessageevent.h @@ -16,7 +16,7 @@ namespace MessageEventContent = EventContent; // Back-compatibility /** * The event class corresponding to m.room.message events */ -class RoomMessageEvent : public RoomEvent { +class QUOTIENT_API RoomMessageEvent : public RoomEvent { Q_GADGET public: DEFINE_EVENT_TYPEID("m.room.message", RoomMessageEvent) @@ -120,7 +120,7 @@ namespace EventContent { * Available fields: mimeType, body. The body can be either rich text * or plain text, depending on what mimeType specifies. */ - class TextContent : public TypedBase { + class QUOTIENT_API TextContent : public TypedBase { public: TextContent(QString text, const QString& contentType, Omittable relatesTo = none); @@ -149,7 +149,7 @@ namespace EventContent { * - thumbnail.mimeType * - thumbnail.imageSize */ - class LocationContent : public TypedBase { + class QUOTIENT_API LocationContent : public TypedBase { public: LocationContent(const QString& geoUri, const Thumbnail& thumbnail = {}); explicit LocationContent(const QJsonObject& json); @@ -168,7 +168,7 @@ namespace EventContent { * A base class for info types that include duration: audio and video */ template - class PlayableContent : public ContentT { + class QUOTIENT_API PlayableContent : public ContentT { public: using ContentT::ContentT; PlayableContent(const QJsonObject& json) diff --git a/lib/events/roompowerlevelsevent.h b/lib/events/roompowerlevelsevent.h index 0346fc0d..80e27048 100644 --- a/lib/events/roompowerlevelsevent.h +++ b/lib/events/roompowerlevelsevent.h @@ -7,7 +7,7 @@ #include "stateevent.h" namespace Quotient { -class PowerLevelsEventContent : public EventContent::Base { +class QUOTIENT_API PowerLevelsEventContent : public EventContent::Base { public: struct Notifications { int room; @@ -34,7 +34,8 @@ protected: void fillJson(QJsonObject* o) const override; }; -class RoomPowerLevelsEvent : public StateEvent { +class QUOTIENT_API RoomPowerLevelsEvent + : public StateEvent { Q_GADGET public: DEFINE_EVENT_TYPEID("m.room.power_levels", RoomPowerLevelsEvent) @@ -61,9 +62,6 @@ public: int powerLevelForEvent(const QString& eventId) const; int powerLevelForState(const QString& eventId) const; int powerLevelForUser(const QString& userId) const; - -private: }; - REGISTER_EVENT_TYPE(RoomPowerLevelsEvent) } // namespace Quotient diff --git a/lib/events/roomtombstoneevent.h b/lib/events/roomtombstoneevent.h index 30e53738..e336c448 100644 --- a/lib/events/roomtombstoneevent.h +++ b/lib/events/roomtombstoneevent.h @@ -6,7 +6,7 @@ #include "stateevent.h" namespace Quotient { -class RoomTombstoneEvent : public StateEventBase { +class QUOTIENT_API RoomTombstoneEvent : public StateEventBase { public: DEFINE_EVENT_TYPEID("m.room.tombstone", RoomTombstoneEvent) diff --git a/lib/events/simplestateevents.h b/lib/events/simplestateevents.h index 9ce78609..d6557012 100644 --- a/lib/events/simplestateevents.h +++ b/lib/events/simplestateevents.h @@ -30,7 +30,8 @@ namespace EventContent { } // namespace EventContent #define DEFINE_SIMPLE_STATE_EVENT(_Name, _TypeId, _ValueType, _ContentKey) \ - class _Name : public StateEvent> { \ + class QUOTIENT_API _Name \ + : public StateEvent> { \ public: \ using value_type = content_type::value_type; \ DEFINE_EVENT_TYPEID(_TypeId, _Name) \ diff --git a/lib/events/stateevent.h b/lib/events/stateevent.h index c37965aa..6095d628 100644 --- a/lib/events/stateevent.h +++ b/lib/events/stateevent.h @@ -17,7 +17,7 @@ inline QJsonObject basicStateEventJson(const QString& matrixTypeId, { ContentKey, content } }; } -class StateEventBase : public RoomEvent { +class QUOTIENT_API StateEventBase : public RoomEvent { public: static inline _impl::EventFactory factory { "StateEvent" }; diff --git a/lib/events/stickerevent.h b/lib/events/stickerevent.h index 93671086..0957dca3 100644 --- a/lib/events/stickerevent.h +++ b/lib/events/stickerevent.h @@ -11,7 +11,7 @@ namespace Quotient { /// Sticker messages are specialised image messages that are displayed without /// controls (e.g. no "download" link, or light-box view on click, as would be /// displayed for for m.image events). -class StickerEvent : public RoomEvent +class QUOTIENT_API StickerEvent : public RoomEvent { public: DEFINE_EVENT_TYPEID("m.sticker", StickerEvent) diff --git a/lib/events/typingevent.h b/lib/events/typingevent.h index 7456100a..522f7e42 100644 --- a/lib/events/typingevent.h +++ b/lib/events/typingevent.h @@ -6,7 +6,7 @@ #include "event.h" namespace Quotient { -class TypingEvent : public Event { +class QUOTIENT_API TypingEvent : public Event { public: DEFINE_EVENT_TYPEID("m.typing", TypingEvent) diff --git a/lib/eventstats.h b/lib/eventstats.h index 77c661a7..a10c81fb 100644 --- a/lib/eventstats.h +++ b/lib/eventstats.h @@ -16,7 +16,7 @@ namespace Quotient { //! \note It's just a simple grouping of counters and is not automatically //! updated from the room as subsequent syncs arrive. //! \sa Room::unreadStats, Room::partiallyReadStats, Room::isEventNotable -struct EventStats { +struct QUOTIENT_API EventStats { Q_GADGET Q_PROPERTY(qsizetype notableCount MEMBER notableCount CONSTANT) Q_PROPERTY(qsizetype highlightCount MEMBER highlightCount CONSTANT) @@ -109,6 +109,6 @@ public: bool isValidFor(const Room* room, const marker_t& marker) const; }; -QDebug operator<<(QDebug dbg, const EventStats& es); +QUOTIENT_API QDebug operator<<(QDebug dbg, const EventStats& es); } diff --git a/lib/jobs/basejob.h b/lib/jobs/basejob.h index ddf243ed..f41fc63c 100644 --- a/lib/jobs/basejob.h +++ b/lib/jobs/basejob.h @@ -20,7 +20,7 @@ class ConnectionData; enum class HttpVerb { Get, Put, Post, Delete }; -class BaseJob : public QObject { +class QUOTIENT_API BaseJob : public QObject { Q_OBJECT Q_PROPERTY(QUrl requestUrl READ requestUrl CONSTANT) Q_PROPERTY(int maxRetries READ maxRetries WRITE setMaxRetries) @@ -470,7 +470,7 @@ private: QScopedPointer d; }; -inline bool isJobPending(BaseJob* job) +inline bool QUOTIENT_API isJobPending(BaseJob* job) { return job && job->error() == BaseJob::Pending; } diff --git a/lib/jobs/downloadfilejob.h b/lib/jobs/downloadfilejob.h index 0752af89..d9f3b686 100644 --- a/lib/jobs/downloadfilejob.h +++ b/lib/jobs/downloadfilejob.h @@ -6,7 +6,7 @@ #include "csapi/content-repo.h" namespace Quotient { -class DownloadFileJob : public GetContentJob { +class QUOTIENT_API DownloadFileJob : public GetContentJob { public: using GetContentJob::makeRequestUrl; static QUrl makeRequestUrl(QUrl baseUrl, const QUrl& mxcUri); diff --git a/lib/jobs/mediathumbnailjob.h b/lib/jobs/mediathumbnailjob.h index 3183feb1..c9f6da35 100644 --- a/lib/jobs/mediathumbnailjob.h +++ b/lib/jobs/mediathumbnailjob.h @@ -8,7 +8,7 @@ #include namespace Quotient { -class MediaThumbnailJob : public GetContentThumbnailJob { +class QUOTIENT_API MediaThumbnailJob : public GetContentThumbnailJob { public: using GetContentThumbnailJob::makeRequestUrl; static QUrl makeRequestUrl(QUrl baseUrl, const QUrl& mxcUri, diff --git a/lib/jobs/requestdata.h b/lib/jobs/requestdata.h index 4f05e5ff..41ad833a 100644 --- a/lib/jobs/requestdata.h +++ b/lib/jobs/requestdata.h @@ -3,6 +3,8 @@ #pragma once +#include "quotient_export.h" + #include #include @@ -19,7 +21,7 @@ namespace Quotient { * as well as JSON (and possibly other structures in the future) to * a QByteArray consumed by QNetworkAccessManager request methods. */ -class RequestData { +class QUOTIENT_API RequestData { public: RequestData(const QByteArray& a = {}); RequestData(const QJsonObject& jo); diff --git a/lib/mxcreply.h b/lib/mxcreply.h index efaf01c6..23049b7d 100644 --- a/lib/mxcreply.h +++ b/lib/mxcreply.h @@ -3,13 +3,15 @@ #pragma once +#include "quotient_export.h" + #include #include namespace Quotient { class Room; -class MxcReply : public QNetworkReply +class QUOTIENT_API MxcReply : public QNetworkReply { public: explicit MxcReply(); diff --git a/lib/networkaccessmanager.h b/lib/networkaccessmanager.h index 87bc12a1..d06f9736 100644 --- a/lib/networkaccessmanager.h +++ b/lib/networkaccessmanager.h @@ -3,6 +3,8 @@ #pragma once +#include "quotient_export.h" + #include #include @@ -10,7 +12,7 @@ namespace Quotient { class Room; class Connection; -class NetworkAccessManager : public QNetworkAccessManager { +class QUOTIENT_API NetworkAccessManager : public QNetworkAccessManager { Q_OBJECT public: NetworkAccessManager(QObject* parent = nullptr); diff --git a/lib/networksettings.h b/lib/networksettings.h index df11a9c8..c1446355 100644 --- a/lib/networksettings.h +++ b/lib/networksettings.h @@ -10,7 +10,7 @@ Q_DECLARE_METATYPE(QNetworkProxy::ProxyType) namespace Quotient { -class NetworkSettings : public SettingsGroup { +class QUOTIENT_API NetworkSettings : public SettingsGroup { Q_OBJECT QTNT_DECLARE_SETTING(QNetworkProxy::ProxyType, proxyType, setProxyType) QTNT_DECLARE_SETTING(QString, proxyHostName, setProxyHostName) diff --git a/lib/quotient_common.h b/lib/quotient_common.h index 0e3e2a40..a5926e8c 100644 --- a/lib/quotient_common.h +++ b/lib/quotient_common.h @@ -3,6 +3,8 @@ #pragma once +#include "quotient_export.h" + #include #include @@ -26,8 +28,21 @@ #define DECL_DEPRECATED_ENUMERATOR(Deprecated, Recommended) \ Deprecated Q_DECL_ENUMERATOR_DEPRECATED_X("Use " #Recommended) = Recommended +#if QT_VERSION < QT_VERSION_CHECK(5, 14, 0) +// The first line is a usual way to indicate a namespace to moc; +// the second line redeclares the namespace static metaobject with +// QUOTIENT_API so that dynamically linked clients could serialise +// flag/enum values from the namespace. +#define QUO_NAMESPACE \ +Q_NAMESPACE \ +extern QUOTIENT_API const QMetaObject staticMetaObject; +#else +// Since Qt 5.14.0, it's all packed in a single macro +#define QUO_NAMESPACE Q_NAMESPACE_EXPORT(QUOTIENT_API) +#endif + namespace Quotient { -Q_NAMESPACE +QUO_NAMESPACE // std::array {} needs explicit template parameters on macOS because // Apple stdlib doesn't have deduction guides for std::array. C++20 has diff --git a/lib/room.h b/lib/room.h index 85c51a87..63a4aaea 100644 --- a/lib/room.h +++ b/lib/room.h @@ -45,7 +45,7 @@ class RedactEventJob; * This is specifically tuned to work with QML exposing all traits as * Q_PROPERTY values. */ -class FileTransferInfo { +class QUOTIENT_API FileTransferInfo { Q_GADGET Q_PROPERTY(bool isUpload MEMBER isUpload CONSTANT) Q_PROPERTY(bool active READ active CONSTANT) @@ -73,7 +73,7 @@ public: //! \brief Data structure for a room member's read receipt //! \sa Room::lastReadReceipt -class ReadReceipt { +class QUOTIENT_API ReadReceipt { Q_GADGET Q_PROPERTY(QString eventId MEMBER eventId CONSTANT) Q_PROPERTY(QDateTime timestamp MEMBER timestamp CONSTANT) @@ -110,7 +110,7 @@ private: Q_PROPERTY(Type type MEMBER type CONSTANT) }; -class Room : public QObject { +class QUOTIENT_API Room : public QObject { Q_OBJECT Q_PROPERTY(Connection* connection READ connection CONSTANT) Q_PROPERTY(User* localUser READ localUser CONSTANT) @@ -1037,7 +1037,7 @@ private: void setJoinState(JoinState state); }; -class MemberSorter { +class QUOTIENT_API MemberSorter { public: explicit MemberSorter(const Room* r) : room(r) {} diff --git a/lib/settings.h b/lib/settings.h index efd0d714..b66879c5 100644 --- a/lib/settings.h +++ b/lib/settings.h @@ -3,6 +3,8 @@ #pragma once +#include "quotient_export.h" + #include #include #include @@ -11,7 +13,7 @@ class QVariant; namespace Quotient { -class Settings : public QSettings { +class QUOTIENT_API Settings : public QSettings { Q_OBJECT public: /// Add a legacy organisation/application name to migrate settings from @@ -76,7 +78,7 @@ protected: QSettings legacySettings { legacyOrganizationName, legacyApplicationName }; }; -class SettingsGroup : public Settings { +class QUOTIENT_API SettingsGroup : public Settings { public: explicit SettingsGroup(QString path, QObject* parent = nullptr) : Settings(parent) @@ -124,7 +126,7 @@ private: setValue(QStringLiteral(qsettingname), std::move(newValue)); \ } -class AccountSettings : public SettingsGroup { +class QUOTIENT_API AccountSettings : public SettingsGroup { Q_OBJECT Q_PROPERTY(QString userId READ userId CONSTANT) QTNT_DECLARE_SETTING(QString, deviceId, setDeviceId) diff --git a/lib/ssosession.h b/lib/ssosession.h index 72dd60c4..a658c043 100644 --- a/lib/ssosession.h +++ b/lib/ssosession.h @@ -3,6 +3,8 @@ #pragma once +#include "quotient_export.h" + #include #include @@ -29,7 +31,7 @@ class Connection; * connection->prepareForSso(initialDeviceName)->ssoUrl()); * \endcode */ -class SsoSession : public QObject { +class QUOTIENT_API SsoSession : public QObject { Q_OBJECT Q_PROPERTY(QUrl ssoUrl READ ssoUrl CONSTANT) Q_PROPERTY(QUrl callbackUrl READ callbackUrl CONSTANT) diff --git a/lib/syncdata.h b/lib/syncdata.h index 36d2e0bf..e29540c2 100644 --- a/lib/syncdata.h +++ b/lib/syncdata.h @@ -22,7 +22,7 @@ constexpr auto HighlightCountKey = "highlight_count"_ls; * means that nothing has come from the server; heroes.value().isEmpty() * means a peculiar case of a room with the only member - the current user. */ -struct RoomSummary { +struct QUOTIENT_API RoomSummary { Omittable joinedMemberCount; Omittable invitedMemberCount; Omittable heroes; //< mxids of users to take part in the room diff --git a/lib/uri.h b/lib/uri.h index d8b892b6..78cd27c8 100644 --- a/lib/uri.h +++ b/lib/uri.h @@ -23,7 +23,7 @@ namespace Quotient { * its type, and obtain components, also in either unencoded (for displaying) * or encoded (for APIs) form. */ -class Uri : private QUrl { +class QUOTIENT_API Uri : private QUrl { Q_GADGET public: enum Type : char { diff --git a/lib/uriresolver.h b/lib/uriresolver.h index ff97324a..9140046c 100644 --- a/lib/uriresolver.h +++ b/lib/uriresolver.h @@ -25,7 +25,7 @@ class User; * gradual implementation. Derived classes are encouraged to override as many * of them as possible. */ -class UriResolverBase { +class QUOTIENT_API UriResolverBase { public: /*! \brief Resolve the resource and dispatch an action depending on its type * @@ -105,7 +105,7 @@ protected: * * \sa UriResolverBase, UriDispatcher */ -UriResolveResult +QUOTIENT_API UriResolveResult visitResource(Connection* account, const Uri& uri, std::function userHandler, std::function roomEventHandler, @@ -141,7 +141,7 @@ inline UriResolveResult checkResource(Connection* account, const Uri& uri) * synchronously - the returned value is the result of resolving the URI, * not acting on it. */ -class UriDispatcher : public QObject, public UriResolverBase { +class QUOTIENT_API UriDispatcher : public QObject, public UriResolverBase { Q_OBJECT public: explicit UriDispatcher(QObject* parent = nullptr) : QObject(parent) {} diff --git a/lib/user.h b/lib/user.h index 78b72bf2..435304ce 100644 --- a/lib/user.h +++ b/lib/user.h @@ -5,6 +5,7 @@ #pragma once #include "avatar.h" +#include "quotient_export.h" #include @@ -13,7 +14,7 @@ class Connection; class Room; class RoomMemberEvent; -class User : public QObject { +class QUOTIENT_API User : public QObject { Q_OBJECT Q_PROPERTY(QString id READ id CONSTANT) Q_PROPERTY(bool isGuest READ isGuest CONSTANT) diff --git a/lib/util.h b/lib/util.h index 97f0ecbc..399f93c2 100644 --- a/lib/util.h +++ b/lib/util.h @@ -4,6 +4,8 @@ #pragma once +#include "quotient_export.h" + #include #include @@ -244,26 +246,26 @@ inline std::pair findFirstOf(InputIt first, InputIt last, } /** Convert what looks like a URL or a Matrix ID to an HTML hyperlink */ -void linkifyUrls(QString& htmlEscapedText); +QUOTIENT_API void linkifyUrls(QString& htmlEscapedText); /** Sanitize the text before showing in HTML * * This does toHtmlEscaped() and removes Unicode BiDi marks. */ -QString sanitized(const QString& plainText); +QUOTIENT_API QString sanitized(const QString& plainText); /** Pretty-print plain text into HTML * * This includes HTML escaping of <,>,",& and calling linkifyUrls() */ -QString prettyPrint(const QString& plainText); +QUOTIENT_API QString prettyPrint(const QString& plainText); /** Return a path to cache directory after making sure that it exists * * The returned path has a trailing slash, clients don't need to append it. * \param dir path to cache directory relative to the standard cache path */ -QString cacheLocation(const QString& dirName); +QUOTIENT_API QString cacheLocation(const QString& dirName); /** Hue color component of based of the hash of the string. * @@ -272,13 +274,13 @@ QString cacheLocation(const QString& dirName); * Naming and range are the same as QColor's hueF method: * https://doc.qt.io/qt-5/qcolor.html#integer-vs-floating-point-precision */ -qreal stringToHueF(const QString& s); +QUOTIENT_API qreal stringToHueF(const QString& s); /** Extract the serverpart from MXID */ -QString serverPart(const QString& mxId); +QUOTIENT_API QString serverPart(const QString& mxId); -QString versionString(); -int majorVersion(); -int minorVersion(); -int patchVersion(); +QUOTIENT_API QString versionString(); +QUOTIENT_API int majorVersion(); +QUOTIENT_API int minorVersion(); +QUOTIENT_API int patchVersion(); } // namespace Quotient -- cgit v1.2.3 From bf82aeea369cacfc93a0e6d6d9feb01f1f2afdb2 Mon Sep 17 00:00:00 2001 From: Alexey Rusakov Date: Tue, 18 Jan 2022 11:43:21 +0100 Subject: Don't use 'static' on top-level/namespace scope When internal linkage is necessary, anonymous namespaces fulfil the same purpose in a better way. See also: https://stackoverflow.com/questions/4422507/superiority-of-unnamed-namespace-over-static --- lib/converters.h | 2 +- lib/events/roommessageevent.cpp | 18 +++++++++++------- lib/settings.cpp | 9 +++++---- lib/uri.cpp | 6 +++++- 4 files changed, 22 insertions(+), 13 deletions(-) (limited to 'lib/converters.h') diff --git a/lib/converters.h b/lib/converters.h index a6028f1b..8eea1cd3 100644 --- a/lib/converters.h +++ b/lib/converters.h @@ -355,7 +355,7 @@ namespace _impl { }; } // namespace _impl -static constexpr bool IfNotEmpty = false; +constexpr bool IfNotEmpty = false; /*! Add a key-value pair to QJsonObject or QUrlQuery * diff --git a/lib/events/roommessageevent.cpp b/lib/events/roommessageevent.cpp index 0f58d8a6..5ab0f845 100644 --- a/lib/events/roommessageevent.cpp +++ b/lib/events/roommessageevent.cpp @@ -19,13 +19,15 @@ using namespace EventContent; using MsgType = RoomMessageEvent::MsgType; -static constexpr auto RelatesToKey = "m.relates_to"_ls; -static constexpr auto MsgTypeKey = "msgtype"_ls; -static constexpr auto FormattedBodyKey = "formatted_body"_ls; -static constexpr auto TextTypeKey = "m.text"_ls; -static constexpr auto EmoteTypeKey = "m.emote"_ls; -static constexpr auto NoticeTypeKey = "m.notice"_ls; -static constexpr auto HtmlContentTypeId = "org.matrix.custom.html"_ls; +namespace { // Supporting internal definitions + +constexpr auto RelatesToKey = "m.relates_to"_ls; +constexpr auto MsgTypeKey = "msgtype"_ls; +constexpr auto FormattedBodyKey = "formatted_body"_ls; +constexpr auto TextTypeKey = "m.text"_ls; +constexpr auto EmoteTypeKey = "m.emote"_ls; +constexpr auto NoticeTypeKey = "m.notice"_ls; +constexpr auto HtmlContentTypeId = "org.matrix.custom.html"_ls; template TypedBase* make(const QJsonObject& json) @@ -87,6 +89,8 @@ inline bool isReplacement(const Omittable& rel) return rel && rel->type == RelatesTo::ReplacementTypeId(); } +} // anonymous namespace + QJsonObject RoomMessageEvent::assembleContentJson(const QString& plainBody, const QString& jsonMsgType, TypedBase* content) diff --git a/lib/settings.cpp b/lib/settings.cpp index 20734a7e..2491d89d 100644 --- a/lib/settings.cpp +++ b/lib/settings.cpp @@ -110,10 +110,11 @@ QUO_DEFINE_SETTING(AccountSettings, QString, deviceName, "device_name", {}, QUO_DEFINE_SETTING(AccountSettings, bool, keepLoggedIn, "keep_logged_in", false, setKeepLoggedIn) -static constexpr auto HomeserverKey = "homeserver"_ls; -static constexpr auto AccessTokenKey = "access_token"_ls; -static constexpr auto EncryptionAccountPickleKey = - "encryption_account_pickle"_ls; +namespace { +constexpr auto HomeserverKey = "homeserver"_ls; +constexpr auto AccessTokenKey = "access_token"_ls; +constexpr auto EncryptionAccountPickleKey = "encryption_account_pickle"_ls; +} QUrl AccountSettings::homeserver() const { diff --git a/lib/uri.cpp b/lib/uri.cpp index 11c59b69..6b7d1d20 100644 --- a/lib/uri.cpp +++ b/lib/uri.cpp @@ -10,12 +10,14 @@ using namespace Quotient; +namespace { + struct ReplacePair { QLatin1String uriString; char sigil; }; /// \brief Defines bi-directional mapping of path prefixes and sigils /// /// When there are two prefixes for the same sigil, the first matching /// entry for a given sigil is used. -static const ReplacePair replacePairs[] = { +const ReplacePair replacePairs[] = { { "u/"_ls, '@' }, { "user/"_ls, '@' }, { "roomid/"_ls, '!' }, @@ -27,6 +29,8 @@ static const ReplacePair replacePairs[] = { { "event/"_ls, '$' } }; +} + Uri::Uri(QByteArray primaryId, QByteArray secondaryId, QString query) { if (primaryId.isEmpty()) -- cgit v1.2.3 From abbab8d8f8c566bc2c9cdf766c6fbb11d978ca47 Mon Sep 17 00:00:00 2001 From: Alexey Rusakov Date: Sun, 23 Jan 2022 17:11:22 +0100 Subject: Omittable: split out from util.h and refresh Improvements: - Quotient::lift() - a way to invoke a function on an optional (including Omittable) or a pointer if it's 'truthy'. Doesn't need enhanced function_traits<>, only the standard library; works on any number of arguments that can be dereferenced and casted to bool. - then() - the version of lift() as a member function. - edit() was renamed to ensure() (edit() might become a read-write counterpart of then() at some point). It's not really used across libQuotient codebase (or elsewhere) but is staying there just in case. It can also accept an initializer, removing the requirement of default-constructibility. - Quotient::merge() is simplified, with one universal implementation covering both Omittable/optional and plain values. - All that now lives in its dedicated pair of files, further decluttering util.h --- CMakeLists.txt | 1 + gtad/gtad.yaml | 2 +- lib/connection.h | 1 + lib/converters.h | 1 + lib/omittable.cpp | 34 +++++++++ lib/omittable.h | 223 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ lib/util.h | 141 ---------------------------------- 7 files changed, 261 insertions(+), 142 deletions(-) create mode 100644 lib/omittable.cpp create mode 100644 lib/omittable.h (limited to 'lib/converters.h') diff --git a/CMakeLists.txt b/CMakeLists.txt index 599424ab..13c8f591 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -127,6 +127,7 @@ list(APPEND lib_SRCS lib/quotient_common.h lib/quotient_export.h lib/function_traits.h lib/function_traits.cpp + lib/omittable.h lib/omittable.cpp lib/networkaccessmanager.h lib/networkaccessmanager.cpp lib/connectiondata.h lib/connectiondata.cpp lib/connection.h lib/connection.cpp diff --git a/gtad/gtad.yaml b/gtad/gtad.yaml index 943ac013..03c23886 100644 --- a/gtad/gtad.yaml +++ b/gtad/gtad.yaml @@ -45,7 +45,7 @@ analyzer: types: - +set: &UseOmittable useOmittable: - omittedValue: 'none' # Quotient::none in lib/util.h + omittedValue: 'none' # Quotient::none in lib/omittable.h +on: - integer: - int64: qint64 diff --git a/lib/connection.h b/lib/connection.h index 28688cc1..dc2eaad1 100644 --- a/lib/connection.h +++ b/lib/connection.h @@ -8,6 +8,7 @@ #include "ssosession.h" #include "qt_connection_util.h" #include "quotient_common.h" +#include "util.h" #include "csapi/login.h" #include "csapi/create_room.h" diff --git a/lib/converters.h b/lib/converters.h index 8eea1cd3..e7bc6898 100644 --- a/lib/converters.h +++ b/lib/converters.h @@ -3,6 +3,7 @@ #pragma once +#include "omittable.h" #include "util.h" #include diff --git a/lib/omittable.cpp b/lib/omittable.cpp new file mode 100644 index 00000000..245ae721 --- /dev/null +++ b/lib/omittable.cpp @@ -0,0 +1,34 @@ +// SPDX-FileCopyrightText: 2021 Kitsune Ral +// SPDX-License-Identifier: LGPL-2.1-or-later + +#include "omittable.h" + +// Omittable<> tests +using namespace Quotient; + +Omittable testFn(bool) { return 0; } +bool testFn2(int) { return false; } +static_assert( + std::is_same_v>().then(testFn)), + Omittable>); +static_assert( + std::is_same_v< + decltype(std::declval>().then_or(testFn, 0)), int>); +static_assert( + std::is_same_v>().then(testFn)), + Omittable>); +static_assert(std::is_same_v>() + .then(testFn2) + .then(testFn)), + Omittable>); +static_assert(std::is_same_v>() + .then(testFn) + .then_or(testFn2, false)), + bool>); + +constexpr auto visitTestFn(int, bool) { return false; } +static_assert( + std::is_same_v, decltype(lift(testFn2, Omittable()))>); +static_assert(std::is_same_v, + decltype(lift(visitTestFn, Omittable(), + Omittable()))>); diff --git a/lib/omittable.h b/lib/omittable.h new file mode 100644 index 00000000..b5efecf5 --- /dev/null +++ b/lib/omittable.h @@ -0,0 +1,223 @@ +// SPDX-FileCopyrightText: 2018 Kitsune Ral +// SPDX-License-Identifier: LGPL-2.1-or-later + +#pragma once + +#include +#include + +namespace Quotient { + +template +class Omittable; + +constexpr auto none = std::nullopt; + +//! \brief Lift an operation into dereferenceable types (Omittables or pointers) +//! +//! This is a more generic version of Omittable::then() that extends to +//! an arbitrary number of arguments of any type that is dereferenceable (unary +//! operator*() can be applied to it) and (explicitly or implicitly) convertible +//! to bool. This allows to streamline checking for nullptr/none before applying +//! the operation on the underlying types. \p fn is only invoked if all \p args +//! are "truthy" (i.e. (... && bool(args)) == true). +//! \param fn A callable that should accept the types stored inside +//! Omittables/pointers passed in \p args +//! \return Always an Omittable: if \p fn returns another type, lift() wraps +//! it in an Omittable; if \p fn returns an Omittable, that return value +//! (or none) is returned as is. +template +inline auto lift(FnT&& fn, MaybeTs&&... args) +{ + return (... && bool(args)) + ? Omittable(std::invoke(std::forward(fn), *args...)) + : none; +} + +/** `std::optional` with tweaks + * + * The tweaks are: + * - streamlined assignment (operator=)/emplace()ment of values that can be + * used to implicitly construct the underlying type, including + * direct-list-initialisation, e.g.: + * \code + * struct S { int a; char b; } + * Omittable o; + * o = { 1, 'a' }; // std::optional would require o = S { 1, 'a' } + * \endcode + * - entirely deleted value(). The technical reason is that Xcode 10 doesn't + * have it; but besides that, value_or() or (after explicit checking) + * `operator*()`/`operator->()` are better alternatives within Quotient + * that doesn't practice throwing exceptions (as doesn't most of Qt). + * - disabled non-const lvalue operator*() and operator->(), as it's too easy + * to inadvertently cause a value change through them. + * - ensure() to provide a safe and explicit lvalue accessor instead of + * those above. Allows chained initialisation of nested Omittables: + * \code + * struct Inner { int member = 10; Omittable innermost; }; + * struct Outer { int anotherMember = 10; Omittable inner; }; + * Omittable o; // = { 10, std::nullopt }; + * o.ensure().inner.ensure().innermost.emplace(42); + * \endcode + * - merge() - a soft version of operator= that only overwrites its first + * operand with the second one if the second one is not empty. + * - then() and then_or() to streamline read-only interrogation in a "monadic" + * interface. + */ +template +class Omittable : public std::optional { +public: + using base_type = std::optional; + using value_type = std::decay_t; + + using std::optional::optional; + + // Overload emplace() and operator=() to allow passing braced-init-lists + // (the standard emplace() does direct-initialisation but + // not direct-list-initialisation). + using base_type::operator=; + Omittable& operator=(const value_type& v) + { + base_type::operator=(v); + return *this; + } + Omittable& operator=(value_type&& v) + { + base_type::operator=(std::move(v)); + return *this; + } + + using base_type::emplace; + T& emplace(const T& val) { return base_type::emplace(val); } + T& emplace(T&& val) { return base_type::emplace(std::move(val)); } + + // Use value_or() or check (with operator! or has_value) before accessing + // with operator-> or operator* + // The technical reason is that Xcode 10 has incomplete std::optional + // that has no value(); but using value() may also mean that you rely + // on the optional throwing an exception (which is not assumed practice + // throughout Quotient) or that you spend unnecessary CPU cycles on + // an extraneous has_value() check. + auto& value() = delete; + const auto& value() const = delete; + + template + value_type& ensure(U&& defaultValue = value_type {}) + { + return this->has_value() ? this->operator*() + : this->emplace(std::forward(defaultValue)); + } + value_type& ensure(const value_type& defaultValue) + { + return ensure<>(defaultValue); + } + value_type& ensure(value_type&& defaultValue) + { + return ensure<>(std::move(defaultValue)); + } + + //! Merge the value from another Omittable + //! \return true if \p other is not omitted and the value of + //! the current Omittable was different (or omitted), + //! in other words, if the current Omittable has changed; + //! false otherwise + template + auto merge(const std::optional& other) + -> std::enable_if_t, bool> + { + if (!other || (this->has_value() && **this == *other)) + return false; + this->emplace(*other); + return true; + } + + // Hide non-const lvalue operator-> and operator* as these are + // a bit too surprising: value() & doesn't lazy-create an object; + // and it's too easy to inadvertently change the underlying value. + + const value_type* operator->() const& { return base_type::operator->(); } + value_type* operator->() && { return base_type::operator->(); } + const value_type& operator*() const& { return base_type::operator*(); } + value_type& operator*() && { return base_type::operator*(); } + + // The below is inspired by the proposed std::optional monadic operations + // (http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2021/p0798r6.html). + + //! \brief Lift a callable into the Omittable + //! + //! 'Lifting', as used in functional programming, means here invoking + //! a callable (e.g., a function) on the contents of the Omittable if it has + //! any and wrapping the returned value (that may be of a different type T2) + //! into a new Omittable\. If the current Omittable is empty, + //! the invocation is skipped altogether and Omittable\{none} is + //! returned instead. + //! \note if \p fn already returns an Omittable (i.e., it is a 'functor', + //! in functional programming terms), then() will not wrap another + //! Omittable around but will just return what \p fn returns. The + //! same doesn't hold for the parameter: if \p fn accepts an Omittable + //! you have to wrap it in another Omittable before calling then(). + //! \return `none` if the current Omittable has `none`; + //! otherwise, the Omittable returned from a call to \p fn + //! \tparam FnT a callable with \p T (or const T&) + //! returning Omittable, T2 is any supported type + //! \sa then_or, transform + template + auto then(FnT&& fn) const& + { + return lift(std::forward(fn), *this); + } + + //! \brief Lift a callable into the rvalue Omittable + //! + //! This is an rvalue overload for then(). + template + auto then(FnT&& fn) && + { + return lift(std::forward(fn), *this); + } + + //! \brief Lift a callable into the const lvalue Omittable, with a fallback + //! + //! This effectively does the same what then() does, except that it returns + //! a value of type returned by the callable, or the provided fallback value + //! if the current Omittable is empty. This is a typesafe version to apply + //! an operation on an Omittable without having to deal with another + //! Omittable afterwards. + template + auto then_or(FnT&& fn, FallbackT&& fallback) const& + { + return then(std::forward(fn)) + .value_or(std::forward(fallback)); + } + + //! \brief Lift a callable into the rvalue Omittable, with a fallback + //! + //! This is an overload for functions that accept rvalue + template + auto then_or(FnT&& fn, FallbackT&& fallback) && + { + return then(std::forward(fn)) + .value_or(std::forward(fallback)); + } +}; + +template +Omittable(T&&) -> Omittable; + +//! \brief Merge the value from an optional +//! This is an adaptation of Omittable::merge() to the case when the value +//! on the left hand side is not an Omittable. +//! \return true if \p rhs is not omitted and the \p lhs value was different, +//! in other words, if \p lhs has changed; +//! false otherwise +template +inline auto merge(T1& lhs, const std::optional& rhs) + -> std::enable_if_t, bool> +{ + if (!rhs || lhs == *rhs) + return false; + lhs = *rhs; + return true; +} + +} // namespace Quotient diff --git a/lib/util.h b/lib/util.h index 3505b62f..753eb1ea 100644 --- a/lib/util.h +++ b/lib/util.h @@ -9,10 +9,8 @@ #include #include -#include #include #include -#include #ifndef Q_DISABLE_MOVE // Q_DISABLE_MOVE was introduced in Q_VERSION_CHECK(5,13,0) @@ -52,145 +50,6 @@ struct HashQ { template using UnorderedMap = std::unordered_map>; -namespace _impl { - template - constexpr auto IsOmittableValue = false; - template - constexpr auto IsOmittable = IsOmittableValue>; -} - -constexpr auto none = std::nullopt; - -/** `std::optional` with tweaks - * - * The tweaks are: - * - streamlined assignment (operator=)/emplace()ment of values that can be - * used to implicitly construct the underlying type, including - * direct-list-initialisation, e.g.: - * \code - * struct S { int a; char b; } - * Omittable o; - * o = { 1, 'a' }; // std::optional would require o = S { 1, 'a' } - * \endcode - * - entirely deleted value(). The technical reason is that Xcode 10 doesn't - * have it; but besides that, value_or() or (after explicit checking) - * `operator*()`/`operator->()` are better alternatives within Quotient - * that doesn't practice throwing exceptions (as doesn't most of Qt). - * - disabled non-const lvalue operator*() and operator->(), as it's too easy - * to inadvertently cause a value change through them. - * - edit() to provide a safe and explicit lvalue accessor instead of those - * above. Requires the underlying type to be default-constructible. - * Allows chained initialisation of nested Omittables: - * \code - * struct Inner { int member = 10; Omittable innermost; }; - * struct Outer { int anotherMember = 10; Omittable inner; }; - * Omittable o; // = { 10, std::nullopt }; - * o.edit().inner.edit().innermost.emplace(42); - * \endcode - * - merge() - a soft version of operator= that only overwrites its first - * operand with the second one if the second one is not empty. - */ -template -class Omittable : public std::optional { -public: - using base_type = std::optional; - using value_type = std::decay_t; - - using std::optional::optional; - - // Overload emplace() and operator=() to allow passing braced-init-lists - // (the standard emplace() does direct-initialisation but - // not direct-list-initialisation). - using base_type::operator=; - Omittable& operator=(const value_type& v) - { - base_type::operator=(v); - return *this; - } - Omittable& operator=(value_type&& v) - { - base_type::operator=(v); - return *this; - } - using base_type::emplace; - T& emplace(const T& val) { return base_type::emplace(val); } - T& emplace(T&& val) { return base_type::emplace(std::move(val)); } - - // use value_or() or check (with operator! or has_value) before accessing - // with operator-> or operator* - // The technical reason is that Xcode 10 has incomplete std::optional - // that has no value(); but using value() may also mean that you rely - // on the optional throwing an exception (which is not assumed practice - // throughout Quotient) or that you spend unnecessary CPU cycles on - // an extraneous has_value() check. - value_type& value() = delete; - const value_type& value() const = delete; - value_type& edit() - { - return this->has_value() ? base_type::operator*() : this->emplace(); - } - - [[deprecated("Use '!o' or '!o.has_value()' instead of 'o.omitted()'")]] - bool omitted() const - { - return !this->has_value(); - } - - //! Merge the value from another Omittable - //! \return true if \p other is not omitted and the value of - //! the current Omittable was different (or omitted), - //! in other words, if the current Omittable has changed; - //! false otherwise - template - auto merge(const Omittable& other) - -> std::enable_if_t, bool> - { - if (!other || (this->has_value() && **this == *other)) - return false; - emplace(*other); - return true; - } - - // Hide non-const lvalue operator-> and operator* as these are - // a bit too surprising: value() & doesn't lazy-create an object; - // and it's too easy to inadvertently change the underlying value. - - const value_type* operator->() const& { return base_type::operator->(); } - value_type* operator->() && { return base_type::operator->(); } - const value_type& operator*() const& { return base_type::operator*(); } - value_type& operator*() && { return base_type::operator*(); } -}; -template -Omittable(T&&) -> Omittable; - -namespace _impl { - template - constexpr auto IsOmittableValue> = true; -} - -template -inline auto merge(Omittable& lhs, T2&& rhs) -{ - return lhs.merge(std::forward(rhs)); -} - -//! \brief Merge the value from an Omittable -//! This is an adaptation of Omittable::merge() to the case when the value -//! on the left hand side is not an Omittable. -//! \return true if \p rhs is not omitted and the \p lhs value was different, -//! in other words, if \p lhs has changed; -//! false otherwise -template -inline auto merge(T1& lhs, const Omittable& rhs) - -> std::enable_if_t - && std::is_convertible_v, bool> -{ - if (!rhs || lhs == *rhs) - return false; - lhs = *rhs; - return true; -} - constexpr auto operator"" _ls(const char* s, std::size_t size) { return QLatin1String(s, int(size)); -- cgit v1.2.3 From 1215888438b901ad80e8bf9882cdcc6a3f057399 Mon Sep 17 00:00:00 2001 From: Alexey Rusakov Date: Sat, 4 Dec 2021 20:12:59 +0100 Subject: converters.h: slightly clearer code in _impl::addTo --- lib/converters.h | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'lib/converters.h') diff --git a/lib/converters.h b/lib/converters.h index e7bc6898..8ddf6e45 100644 --- a/lib/converters.h +++ b/lib/converters.h @@ -288,6 +288,8 @@ QVariantHash QUOTIENT_API fromJson(const QJsonValue& jv); // Conditional insertion into a QJsonObject +constexpr bool IfNotEmpty = false; + namespace _impl { template inline void addTo(QJsonObject& o, const QString& k, ValT&& v) @@ -333,7 +335,7 @@ namespace _impl { // This one is for types that have isEmpty() when Force is false template - struct AddNode().isEmpty())> { + struct AddNode().isEmpty())> { template static void impl(ContT& container, const QString& key, ForwardedT&& value) @@ -343,9 +345,9 @@ namespace _impl { } }; - // This one unfolds Omittable<> (also only when Force is false) + // This one unfolds Omittable<> (also only when IfNotEmpty is requested) template - struct AddNode, false> { + struct AddNode, IfNotEmpty> { template static void impl(ContT& container, const QString& key, const OmittableT& value) @@ -356,8 +358,6 @@ namespace _impl { }; } // namespace _impl -constexpr bool IfNotEmpty = false; - /*! Add a key-value pair to QJsonObject or QUrlQuery * * Adds a key-value pair(s) specified by \p key and \p value to -- cgit v1.2.3 From 10ac7c13cdcd62b62af6e89cb726376cfbc53302 Mon Sep 17 00:00:00 2001 From: Alexey Rusakov Date: Mon, 3 Jan 2022 21:59:04 +0100 Subject: Enable to/fromJson to work with non-assignable objects Previously you could not use toJson() on a polymorphic structure such as one of those defined in eventcontent.h because it is not assignable and the default specialisation of JsonObjectConverter used assignment. To avoid that limitation, one had to specialise JsonObjectConverter for each descendant of EventContent::Base, which is a lot of boilerplate. The new JsonConverter (the template underpinning the "default" to/fromJson implementation) improves on two things: 1. dump() allows to construct your own QJsonObject - or anything else convertable to QJsonValue - instead of requiring to fill in the pre-constructed one. 2. load() allows to construct your value type directly from QJsonObject instead of default-constructing it in advance. --- lib/converters.h | 51 +++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 39 insertions(+), 12 deletions(-) (limited to 'lib/converters.h') diff --git a/lib/converters.h b/lib/converters.h index 8ddf6e45..515c96fd 100644 --- a/lib/converters.h +++ b/lib/converters.h @@ -14,6 +14,7 @@ #include #include +#include #include class QVariant; @@ -21,10 +22,29 @@ class QVariant; namespace Quotient { template struct JsonObjectConverter { - static void dumpTo(QJsonObject& jo, const T& pod) { jo = pod.toJson(); } - static void fillFrom(const QJsonObject& jo, T& pod) { pod = T(jo); } + // To be implemented in specialisations + static void dumpTo(QJsonObject&, const T&) = delete; + static void fillFrom(const QJsonObject&, T&) = delete; }; +namespace _impl { + template + struct JsonExporter { + static QJsonObject dump(const T& data) + { + QJsonObject jo; + JsonObjectConverter::dumpTo(jo, data); + return jo; + } + }; + + template + struct JsonExporter< + T, std::enable_if_t>> { + static auto dump(const T& data) { return data.toJson(); } + }; +} + //! \brief The switchboard for extra conversion algorithms behind from/toJson //! //! This template is mainly intended for partial conversion specialisations @@ -41,18 +61,25 @@ struct JsonObjectConverter { //! that they are not supported and it's not feasible to support those by means //! of overloading toJson() and specialising fromJson(). template -struct JsonConverter { - static QJsonObject dump(const T& pod) - { - QJsonObject jo; - JsonObjectConverter::dumpTo(jo, pod); - return jo; - } +struct JsonConverter : _impl::JsonExporter { + // Unfortunately, if constexpr doesn't work with dump() and T::toJson + // because trying to check invocability of T::toJson hits a hard + // (non-SFINAE) compilation error if the member is not there. Hence a bit + // more verbose SFINAE construct in _impl::JsonExporter. + static T doLoad(const QJsonObject& jo) { - T pod; - JsonObjectConverter::fillFrom(jo, pod); - return pod; + // 'else' below are required to suppress code generation for unused + // branches - 'return' is not enough + if constexpr (std::is_same_v) + return jo; + else if constexpr (std::is_constructible_v) + return T(jo); + else { + T pod; + JsonObjectConverter::fillFrom(jo, pod); + return pod; + } } static T load(const QJsonValue& jv) { return doLoad(jv.toObject()); } static T load(const QJsonDocument& jd) { return doLoad(jd.object()); } -- cgit v1.2.3