From 525be6dce815f88ed9cc97480bff461741665e8f Mon Sep 17 00:00:00 2001
From: Kitsune Ral <Kitsune-Ral@users.sf.net>
Date: Sun, 14 Jan 2018 18:32:26 +0900
Subject: EventContent: rewrite without mixins

MSVC is not good at dealing with type parameter packs of member functions, which is what the whole mixin magic in UrlBasedContent<> relied on. So it's one more level of inheritance instead of mixins now.
---
 events/eventcontent.cpp     | 14 +++++-----
 events/eventcontent.h       | 66 ++++++++++++++++++++++++++++++---------------
 events/roommessageevent.cpp | 17 +++---------
 events/roommessageevent.h   | 28 ++++++++++++-------
 4 files changed, 73 insertions(+), 52 deletions(-)

diff --git a/events/eventcontent.cpp b/events/eventcontent.cpp
index 271669e2..c96da9b3 100644
--- a/events/eventcontent.cpp
+++ b/events/eventcontent.cpp
@@ -74,15 +74,13 @@ void ImageInfo::fillInfoJson(QJsonObject* infoJson) const
     infoJson->insert("h", imageSize.height());
 }
 
-WithThumbnail::WithThumbnail(const QJsonObject& infoJson)
-    : thumbnail(infoJson["thumbnail_url"].toString(),
-      infoJson["thumbnail_info"].toObject())
+Thumbnail::Thumbnail(const QJsonObject& infoJson)
+    : ImageInfo(infoJson["thumbnail_url"].toString(),
+                infoJson["thumbnail_info"].toObject())
 { }
 
-void WithThumbnail::fillInfoJson(QJsonObject* infoJson) const
+void Thumbnail::fillInfoJson(QJsonObject* infoJson) const
 {
-    infoJson->insert("thumbnail_url", thumbnail.url.toString());
-    QJsonObject thumbnailInfoJson;
-    thumbnail.fillInfoJson(&thumbnailInfoJson);
-    infoJson->insert("thumbnail_info", thumbnailInfoJson);
+    infoJson->insert("thumbnail_url", url.toString());
+    infoJson->insert("thumbnail_info", toInfoJson<ImageInfo>(*this));
 }
diff --git a/events/eventcontent.h b/events/eventcontent.h
index b37dc923..91e4ca94 100644
--- a/events/eventcontent.h
+++ b/events/eventcontent.h
@@ -27,6 +27,8 @@
 #include <QtCore/QUrl>
 #include <QtCore/QSize>
 
+#include <functional>
+
 namespace QMatrixClient
 {
     namespace EventContent
@@ -144,6 +146,14 @@ namespace QMatrixClient
                 QString originalName;
         };
 
+        template <typename InfoT>
+        QJsonObject toInfoJson(const InfoT& info)
+        {
+            QJsonObject infoJson;
+            info.fillInfoJson(&infoJson);
+            return infoJson;
+        }
+
         /**
          * A content info class for image content types: image, thumbnail, video
          */
@@ -163,18 +173,18 @@ namespace QMatrixClient
         };
 
         /**
-         * A mixin class for an info type that carries a thumbnail
+         * An auxiliary class for an info type that carries a thumbnail
          *
          * This class saves/loads a thumbnail to/from "info" subobject of
          * the JSON representation of event content; namely,
          * "info/thumbnail_url" and "info/thumbnail_info" fields are used.
          */
-        class WithThumbnail
+        class Thumbnail : public ImageInfo
         {
             public:
-                WithThumbnail(const QJsonObject& infoJson);
-                WithThumbnail(const ImageInfo& info)
-                    : thumbnail(info)
+                Thumbnail(const QJsonObject& infoJson);
+                Thumbnail(const ImageInfo& info)
+                    : ImageInfo(info)
                 { }
 
                 /**
@@ -182,9 +192,6 @@ namespace QMatrixClient
                  * and thumbnail URL to "thumbnail_url" node inside "info".
                  */
                 void fillInfoJson(QJsonObject* infoJson) const;
-
-            public:
-                ImageInfo thumbnail;
         };
 
         class TypedBase: public Base
@@ -204,18 +211,18 @@ namespace QMatrixClient
          * the parameter type.
          *
          * \tparam InfoT base info class
-         * \tparam InfoMixinTs... additional info mixin classes (e.g. WithThumbnail)
          */
-        template <class InfoT, class... InfoMixinTs>
-        class UrlBasedContent :
-                public TypedBase, public InfoT, public InfoMixinTs...
+        template <class InfoT>
+        class UrlBasedContent : public TypedBase, public InfoT
         {
             public:
+                UrlBasedContent(QUrl url, InfoT&& info, QString filename = {})
+                    : InfoT(url, std::forward<InfoT>(info), filename)
+                { }
                 explicit UrlBasedContent(const QJsonObject& json)
                     : TypedBase(json)
                     , InfoT(json["url"].toString(), json["info"].toObject(),
                             json["filename"].toString())
-                    , InfoMixinTs(InfoT::originalInfoJson)...
                 { }
 
                 QMimeType type() const override { return InfoT::mimeType; }
@@ -228,12 +235,29 @@ namespace QMatrixClient
                     json->insert("url", InfoT::url.toString());
                     if (!InfoT::originalName.isEmpty())
                         json->insert("filename", InfoT::originalName);
-                    QJsonObject infoJson;
-                    InfoT::fillInfoJson(&infoJson);
-                    // http://en.cppreference.com/w/cpp/language/parameter_pack#Brace-enclosed_initializers
-                    // Looking forward to C++17 and its folding awesomeness.
-                    int d[] = { (InfoMixinTs::fillInfoJson(&infoJson), 0)... };
-                    Q_UNUSED(d);
+                    json->insert("info", toInfoJson<InfoT>(*this));
+                }
+        };
+
+        template <typename InfoT>
+        class UrlWithThumbnailContent : public UrlBasedContent<InfoT>
+        {
+            public:
+                // TODO: POD constructor
+                UrlWithThumbnailContent(const QJsonObject& json)
+                    : UrlBasedContent<InfoT>(json)
+                    , thumbnail(InfoT::originalInfoJson)
+                { }
+
+            public:
+                Thumbnail thumbnail;
+
+            protected:
+                void fillJson(QJsonObject* json) const override
+                {
+                    UrlBasedContent<InfoT>::fillJson(json);
+                    auto infoJson = json->take("info").toObject();
+                    thumbnail.fillInfoJson(&infoJson);
                     json->insert("info", infoJson);
                 }
         };
@@ -256,7 +280,7 @@ namespace QMatrixClient
          *   - mimeType
          *   - imageSize
          */
-        using ImageContent = UrlBasedContent<ImageInfo, WithThumbnail>;
+        using ImageContent = UrlWithThumbnailContent<ImageInfo>;
 
         /**
          * Content class for m.file
@@ -274,6 +298,6 @@ namespace QMatrixClient
          *   - thumbnail.mimeType
          *   - thumbnail.imageSize (QSize for "h" and "w" in JSON)
          */
-        using FileContent = UrlBasedContent<FileInfo, WithThumbnail>;
+        using FileContent = UrlWithThumbnailContent<FileInfo>;
     }  // namespace EventContent
 }  // namespace QMatrixClient
diff --git a/events/roommessageevent.cpp b/events/roommessageevent.cpp
index 20e81564..3c5d10ad 100644
--- a/events/roommessageevent.cpp
+++ b/events/roommessageevent.cpp
@@ -159,13 +159,13 @@ void TextContent::fillJson(QJsonObject* json) const
 }
 
 LocationContent::LocationContent(const QString& geoUri, const ImageInfo& thumbnail)
-    : WithThumbnail(thumbnail), geoUri(geoUri)
+    : geoUri(geoUri), thumbnail(thumbnail)
 { }
 
 LocationContent::LocationContent(const QJsonObject& json)
     : TypedBase(json)
-    , WithThumbnail(json["info"].toObject())
     , geoUri(json["geo_uri"].toString())
+    , thumbnail(json["info"].toObject())
 { }
 
 QMimeType LocationContent::type() const
@@ -177,16 +177,5 @@ void LocationContent::fillJson(QJsonObject* o) const
 {
     Q_ASSERT(o);
     o->insert("geo_uri", geoUri);
-    QJsonObject infoJson;
-    WithThumbnail::fillInfoJson(&infoJson);
-    o->insert("info", infoJson);
-}
-
-WithDuration::WithDuration(const QJsonObject& infoJson)
-    : duration(infoJson["duration"].toInt())
-{ }
-
-void WithDuration::fillInfoJson(QJsonObject* infoJson) const
-{
-    infoJson->insert("duration", duration);
+    o->insert("info", toInfoJson(thumbnail));
 }
diff --git a/events/roommessageevent.h b/events/roommessageevent.h
index 6b551b76..867d8880 100644
--- a/events/roommessageevent.h
+++ b/events/roommessageevent.h
@@ -112,7 +112,7 @@ namespace QMatrixClient
          *   - thumbnail.mimeType
          *   - thumbnail.imageSize
          */
-        class LocationContent: public TypedBase, public WithThumbnail
+        class LocationContent: public TypedBase
         {
             public:
                 LocationContent(const QString& geoUri,
@@ -123,21 +123,32 @@ namespace QMatrixClient
 
             public:
                 QString geoUri;
+                Thumbnail thumbnail;
 
             protected:
                 void fillJson(QJsonObject* o) const override;
         };
 
         /**
-         * A mixin class for info types that include duration: audio and video
+         * A base class for info types that include duration: audio and video
          */
-        class WithDuration
+        template <typename ContentT>
+        class PlayableContent : public ContentT
         {
             public:
-                explicit WithDuration(int duration) : duration(duration) { }
-                WithDuration(const QJsonObject& infoJson);
+                PlayableContent(const QJsonObject& json)
+                    : ContentT(json)
+                    , duration(ContentT::originalInfoJson["duration"].toInt())
+                { }
 
-                void fillInfoJson(QJsonObject* infoJson) const;
+            protected:
+                void fillJson(QJsonObject* json) const override
+                {
+                    ContentT::fillJson(json);
+                    auto infoJson = json->take("info").toObject();
+                    infoJson.insert("duration", duration);
+                    json->insert("info", infoJson);
+                }
 
             public:
                 int duration;
@@ -162,8 +173,7 @@ namespace QMatrixClient
          *   - mimeType
          *   - imageSize
          */
-        using VideoContent =
-            UrlBasedContent<ImageInfo, WithThumbnail, WithDuration>;
+        using VideoContent = PlayableContent<UrlWithThumbnailContent<ImageInfo>>;
 
         /**
          * Content class for m.audio
@@ -177,6 +187,6 @@ namespace QMatrixClient
          *   - mimeType ("mimetype" in JSON)
          *   - duration
          */
-        using AudioContent = UrlBasedContent<FileInfo, WithDuration>;
+        using AudioContent = PlayableContent<UrlBasedContent<FileInfo>>;
     }  // namespace EventContent
 }  // namespace QMatrixClient
-- 
cgit v1.2.3