aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKitsune Ral <Kitsune-Ral@users.sf.net>2016-04-11 11:54:22 +0900
committerKitsune Ral <Kitsune-Ral@users.sf.net>2016-09-07 22:17:51 +0900
commitf09b42ca73759207c7d63587c0d41c09ad691c47 (patch)
tree54422a0a3efcbea20cf5d545ef6ffb77e1f67ca6
parent6aee89052be72b57fe4d93f5576bdf99293f5953 (diff)
downloadlibquotient-f09b42ca73759207c7d63587c0d41c09ad691c47.tar.gz
libquotient-f09b42ca73759207c7d63587c0d41c09ad691c47.zip
A generic lookup(), and its usage in Event and RoomMessageEvent
Feel free to use whenever you need to convert another JSON key to some C++ object, or dispatch anything based on a JSON key.
-rw-r--r--CMakeLists.txt1
-rw-r--r--events/event.cpp34
-rw-r--r--events/event.h48
-rw-r--r--events/roommessageevent.cpp72
4 files changed, 92 insertions, 63 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
index ea340476..844b7694 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -81,6 +81,7 @@ else ( CMAKE_VERSION VERSION_LESS "3.1" )
target_compile_features(qmatrixclient PRIVATE cxx_auto_type)
target_compile_features(qmatrixclient PRIVATE cxx_generalized_initializers)
target_compile_features(qmatrixclient PRIVATE cxx_nullptr)
+ target_compile_features(qmatrixclient PRIVATE cxx_variadic_templates)
endif ( CMAKE_VERSION VERSION_LESS "3.1" )
target_link_libraries(qmatrixclient Qt5::Core Qt5::Network Qt5::Gui)
diff --git a/events/event.cpp b/events/event.cpp
index c7a94e9e..95084ddb 100644
--- a/events/event.cpp
+++ b/events/event.cpp
@@ -90,27 +90,19 @@ Event* make(const QJsonObject& obj)
Event* Event::fromJson(const QJsonObject& obj)
{
- struct Factory {
- QString type;
- Event* (*make)(const QJsonObject& obj);
- };
- const Factory evTypes[] {
- { "m.room.message", make<RoomMessageEvent> },
- { "m.room.name", make<RoomNameEvent> },
- { "m.room.aliases", make<RoomAliasesEvent> },
- { "m.room.canonical_alias", make<RoomCanonicalAliasEvent> },
- { "m.room.member", make<RoomMemberEvent> },
- { "m.room.topic", make<RoomTopicEvent> },
- { "m.typing", make<TypingEvent> },
- { "m.receipt", make<ReceiptEvent> },
- // Insert new types before this line
- };
- for (auto e: evTypes)
- {
- if (obj["type"].toString() == e.type)
- return e.make(obj);
- }
- return UnknownEvent::fromJson(obj);
+ auto delegate = lookup(obj.value("type").toString(),
+ "m.room.message", make<RoomMessageEvent>,
+ "m.room.name", make<RoomNameEvent>,
+ "m.room.aliases", make<RoomAliasesEvent>,
+ "m.room.canonical_alias", make<RoomCanonicalAliasEvent>,
+ "m.room.member", make<RoomMemberEvent>,
+ "m.room.topic", make<RoomTopicEvent>,
+ "m.typing", make<TypingEvent>,
+ "m.receipt", make<ReceiptEvent>,
+ /* Insert new event types BEFORE this line */
+ make<UnknownEvent>
+ );
+ return delegate(obj);
}
bool Event::parseJson(const QJsonObject& obj)
diff --git a/events/event.h b/events/event.h
index d9316747..a66f5e68 100644
--- a/events/event.h
+++ b/events/event.h
@@ -83,6 +83,54 @@ namespace QMatrixClient
}
);
}
+
+ /**
+ * @brief Lookup a value by a key in a varargs list
+ *
+ * The below overloaded function template takes the value of its first
+ * argument (selector) as a key and searches for it in the key-value map
+ * passed in a varargs list (every next pair of arguments forms a key-value
+ * pair). If a match is found, the respective value is returned; otherwise,
+ * the last value (fallback) is returned.
+ *
+ * All options should be of the same type or implicitly castable to the
+ * type of the first option. Note that pointers to methods of different
+ * classes are of different object types, in particular.
+ *
+ * Below is an example of usage to select a parser depending on contents of
+ * a JSON object:
+ * {@code
+ * auto parser = lookup(obj.value["type"].toString(),
+ * "type1", fn1,
+ * "type2", fn2,
+ * fallbackFn);
+ * parser(obj);
+ * }
+ *
+ * The implementation is based on tail recursion; every recursion step
+ * removes 2 arguments (match and option). There's no selector value for the
+ * fallback option (the last one); therefore, the total number of lookup()
+ * arguments should be even: selector + n key-value pairs + fallback
+ *
+ * @note Beware of calling lookup() with a <code>const char*</code> selector
+ * (the first parameter) - most likely it won't do what you expect because
+ * of shallow comparison.
+ */
+ template <typename ValueT, typename SelectorT, typename KeyT, typename... Ts>
+ ValueT lookup(SelectorT selector, KeyT key, ValueT value, Ts... remainingMapping)
+ {
+ if( selector == key )
+ return value;
+
+ // Drop the failed key-value pair and recurse with 2 arguments less.
+ return lookup(selector, remainingMapping...);
+ }
+
+ template <typename SelectorT, typename ValueT>
+ ValueT lookup(SelectorT/*unused*/, ValueT fallback)
+ {
+ return fallback;
+ }
}
#endif // QMATRIXCLIENT_EVENT_H
diff --git a/events/roommessageevent.cpp b/events/roommessageevent.cpp
index 09465238..d6166dad 100644
--- a/events/roommessageevent.cpp
+++ b/events/roommessageevent.cpp
@@ -72,25 +72,35 @@ Base* RoomMessageEvent::content() const
return d->content;
}
-template <class ContentT>
-Base* make(const QJsonObject& json)
+using ContentPair = std::pair<MessageEventType, MessageEventContent::Base*>;
+
+template <MessageEventType EnumType, typename ContentT>
+ContentPair make(const QJsonObject& json)
{
- return new ContentT(json);
+ return { EnumType, new ContentT(json) };
}
-Base* makeVideoContent(const QJsonObject& json)
+ContentPair makeVideo(const QJsonObject& json)
{
auto c = new VideoContent(json);
// Only for m.video, the spec puts a thumbnail inside "info" JSON key. Once
// this is fixed, VideoContent creation will switch to make<>().
const QJsonObject infoJson = json["info"].toObject();
if (infoJson.contains("thumbnail_url"))
+ {
c->thumbnail = ImageInfo(infoJson["thumbnail_url"].toString(),
- infoJson["thumbnail_info"].toObject());
-
- return c;
+ infoJson["thumbnail_info"].toObject());
+ }
+ return { MessageEventType::Video, c };
};
+ContentPair makeUnknown(const QJsonObject& json)
+{
+ qDebug() << "RoomMessageEvent: couldn't resolve msgtype, JSON follows:";
+ qDebug() << json;
+ return { MessageEventType::Unknown, new Base };
+}
+
RoomMessageEvent* RoomMessageEvent::fromJson(const QJsonObject& obj)
{
RoomMessageEvent* e = new RoomMessageEvent();
@@ -108,41 +118,19 @@ RoomMessageEvent* RoomMessageEvent::fromJson(const QJsonObject& obj)
{
e->d->plainBody = content["body"].toString();
- struct Factory
- {
- QString jsonTag;
- MessageEventType enumTag;
- Base*(*make)(const QJsonObject& json);
- };
-
- const Factory factories[] {
- { "m.text", MessageEventType::Text, make<TextContent> },
- { "m.emote", MessageEventType::Emote, make<TextContent> },
- { "m.notice", MessageEventType::Notice, make<TextContent> },
- { "m.image", MessageEventType::Image, make<ImageContent> },
- { "m.file", MessageEventType::File, make<FileContent> },
- { "m.location", MessageEventType::Location, make<LocationContent> },
- { "m.video", MessageEventType::Video, makeVideoContent },
- { "m.audio", MessageEventType::Audio, make<AudioContent> },
- // Insert new message types before this line
- };
-
- QString msgtype = content.value("msgtype").toString();
- for (auto f: factories)
- {
- if (msgtype == f.jsonTag)
- {
- e->d->msgtype = f.enumTag;
- e->d->content = f.make(content);
- break;
- }
- }
- if (e->d->msgtype == MessageEventType::Unknown)
- {
- qDebug() << "RoomMessageEvent: unknown msgtype: " << msgtype;
- qDebug() << obj;
- e->d->content = new Base;
- }
+ auto delegate = lookup(content.value("msgtype").toString(),
+ "m.text", make<MessageEventType::Text, TextContent>,
+ "m.emote", make<MessageEventType::Emote, TextContent>,
+ "m.notice", make<MessageEventType::Notice, TextContent>,
+ "m.image", make<MessageEventType::Image, ImageContent>,
+ "m.file", make<MessageEventType::File, FileContent>,
+ "m.location", make<MessageEventType::Location, LocationContent>,
+ "m.video", makeVideo,
+ "m.audio", make<MessageEventType::Audio, AudioContent>,
+ // Insert new message types before this line
+ makeUnknown
+ );
+ std::tie(e->d->msgtype, e->d->content) = delegate(content);
}
else
{