aboutsummaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
authorAlexey Rusakov <Kitsune-Ral@users.sf.net>2022-01-03 21:59:04 +0100
committerAlexey Rusakov <Kitsune-Ral@users.sf.net>2022-02-11 14:23:49 +0100
commit10ac7c13cdcd62b62af6e89cb726376cfbc53302 (patch)
tree6d8aacb95f9175ed08f213556ccdf2a6fefc363c /lib
parent4f581f22d707fd226a56400817c9e47e54116589 (diff)
downloadlibquotient-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.h51
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()); }