diff options
author | Alexey Rusakov <Kitsune-Ral@users.sf.net> | 2022-01-03 21:59:04 +0100 |
---|---|---|
committer | Alexey Rusakov <Kitsune-Ral@users.sf.net> | 2022-02-11 14:23:49 +0100 |
commit | 10ac7c13cdcd62b62af6e89cb726376cfbc53302 (patch) | |
tree | 6d8aacb95f9175ed08f213556ccdf2a6fefc363c /lib | |
parent | 4f581f22d707fd226a56400817c9e47e54116589 (diff) | |
download | libquotient-10ac7c13cdcd62b62af6e89cb726376cfbc53302.tar.gz libquotient-10ac7c13cdcd62b62af6e89cb726376cfbc53302.zip |
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.
Diffstat (limited to 'lib')
-rw-r--r-- | lib/converters.h | 51 |
1 files changed, 39 insertions, 12 deletions
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 <QtCore/QUrlQuery> #include <QtCore/QVector> +#include <type_traits> #include <vector> class QVariant; @@ -21,10 +22,29 @@ class QVariant; namespace Quotient { template <typename T> 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 <typename T, typename = void> + struct JsonExporter { + static QJsonObject dump(const T& data) + { + QJsonObject jo; + JsonObjectConverter<T>::dumpTo(jo, data); + return jo; + } + }; + + template <typename T> + struct JsonExporter< + T, std::enable_if_t<std::is_invocable_v<decltype(&T::toJson), 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 <typename T> -struct JsonConverter { - static QJsonObject dump(const T& pod) - { - QJsonObject jo; - JsonObjectConverter<T>::dumpTo(jo, pod); - return jo; - } +struct JsonConverter : _impl::JsonExporter<T> { + // 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<T>::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<T, QJsonObject>) + return jo; + else if constexpr (std::is_constructible_v<T, QJsonObject>) + return T(jo); + else { + T pod; + JsonObjectConverter<T>::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()); } |