config: std::pair serialization 35/49035/7
authorJan Olszak <j.olszak@samsung.com>
Wed, 30 Sep 2015 10:40:16 +0000 (12:40 +0200)
committerJan Olszak <j.olszak@samsung.com>
Wed, 7 Oct 2015 11:37:09 +0000 (13:37 +0200)
[Feature]       Serialization of pairs
[Cause]         N/A
[Solution]      N/A
[Verification]  Build, install, run tests.

Change-Id: I5c860e72d91491699deace1c50e213b0538440a4

libs/config/from-fdstore-visitor.hpp
libs/config/from-gvariant-visitor.hpp
libs/config/from-json-visitor.hpp
libs/config/from-kvjson-visitor.hpp
libs/config/kvstore.hpp
libs/config/to-fdstore-visitor.hpp
libs/config/to-gvariant-visitor.hpp
libs/config/to-json-visitor.hpp
libs/config/visit-fields.hpp [new file with mode: 0644]
tests/unit_tests/config/testconfig-example.hpp
tests/unit_tests/config/ut-configuration.cpp

index 1013a61..f0c8dbe 100644 (file)
 #include "config/is-visitable.hpp"
 #include "config/fdstore.hpp"
 #include "config/types.hpp"
+#include "config/visit-fields.hpp"
 
 #include <array>
 #include <string>
+#include <utility>
 #include <vector>
 
 namespace config {
@@ -110,6 +112,12 @@ private:
             readInternal(value);
         }
     }
+
+    template<typename ... T>
+    void readInternal(std::pair<T...>& values)
+    {
+        visitFields(values, this, std::string());
+    }
 };
 
 } // namespace config
index 213f917..afc1beb 100644 (file)
 #include "config/exception.hpp"
 #include "config/is-union.hpp"
 #include "config/types.hpp"
+#include "config/visit-fields.hpp"
 
 #include <string>
 #include <vector>
 #include <array>
+#include <utility>
 #include <memory>
 #include <cassert>
+
 #include <glib.h>
 
 namespace config {
@@ -175,6 +178,28 @@ private:
         }
     }
 
+    struct HelperVisitor
+    {
+        template<typename T>
+        static void visit(GVariantIter* iter, T&& value)
+        {
+            auto child = makeUnique(g_variant_iter_next_value(iter));
+            fromGVariant(child.get(), value);
+        }
+    };
+
+    template<typename ... T>
+    static void fromGVariant(GVariant* object, std::pair<T...>& values)
+    {
+        checkType(object, G_VARIANT_TYPE_ARRAY);
+
+        GVariantIter iter;
+        g_variant_iter_init(&iter, object);
+
+        HelperVisitor visitor;
+        visitFields(values, &visitor, &iter);
+    }
+
     template<typename T>
     static typename std::enable_if<isUnion<T>::value>::type
     fromGVariant(GVariant* object, T& value)
index 041027e..9bdf19b 100644 (file)
 
 #include "config/is-visitable.hpp"
 #include "config/exception.hpp"
+#include "config/visit-fields.hpp"
 
 #include <json.h>
 #include <string>
 #include <cstring>
 #include <vector>
 #include <array>
+#include <utility>
 
 namespace config {
 
@@ -170,6 +172,26 @@ private:
         }
     }
 
+    struct HelperVisitor
+    {
+        template<typename T>
+        static void visit(json_object* object, std::size_t& idx, T&& value)
+        {
+            fromJsonObject(json_object_array_get_idx(object, idx), value);
+            idx += 1;
+        }
+    };
+
+    template<typename ... T>
+    static void fromJsonObject(json_object* object, std::pair<T...>& values)
+    {
+        checkType(object, json_type_array);
+
+        std::size_t idx = 0;
+        HelperVisitor visitor;
+        visitFields(values, &visitor, object, idx);
+    }
+
     template<typename T, class = typename std::enable_if<isVisitable<T>::value>::type>
     static void fromJsonObject(json_object* object, T& value)
     {
index 96f3888..d9dbce7 100644 (file)
@@ -28,7 +28,9 @@
 
 #include "config/from-kvstore-visitor.hpp"
 #include "config/is-union.hpp"
+#include "config/visit-fields.hpp"
 
+#include <utility>
 #include <json.h>
 
 namespace config {
@@ -65,6 +67,12 @@ public:
         getValue(name, value);
     }
 
+    template<typename T>
+    void visit(std::size_t& idx, T& value) {
+        getValue(idx, value);
+        idx += 1;
+    }
+
 private:
     KVStore& mStore;
     std::string mKeyPrefix;
@@ -105,14 +113,13 @@ private:
         std::string k = key(mKeyPrefix, name);
         if (mStore.exists(k)) {
             t = mStore.get<T>(k);
-        }
-        else {
+        } else {
             json_object* object = nullptr;
             if (mObject) {
                 json_object_object_get_ex(mObject, name.c_str(), &object);
             }
             if (!object) {
-                throw ConfigException("Missing json field " + key(mKeyPrefix, name));
+                throw ConfigException("Missing json field " + k);
             }
             fromJsonObject(object, t);
         }
@@ -186,6 +193,25 @@ private:
         }
     }
 
+    template<typename ... T>
+    void getValue(const std::string& name, std::pair<T...>& values)
+    {
+        json_object* object = nullptr;
+        if (mObject && json_object_object_get_ex(mObject, name.c_str(), &object)) {
+            checkType(object, json_type_array);
+        }
+
+        std::string k = key(mKeyPrefix, name);
+        FromKVJsonVisitor visitor(*this, name, false);
+        if (mStore.exists(k)) {
+            json_object_put(visitor.mObject);
+            visitor.mObject = nullptr;
+        }
+
+        std::size_t idx = 0;
+        visitFields(values, &visitor, idx);
+    }
+
     template<typename T, typename std::enable_if<!isVisitable<T>::value, int>::type = 0>
     void getValue(int i, T& t)
     {
index fe30e21..1206d08 100644 (file)
@@ -27,6 +27,7 @@
 
 #include "config/sqlite3/connection.hpp"
 #include "config/sqlite3/statement.hpp"
+#include "config/visit-fields.hpp"
 
 #include <algorithm>
 #include <initializer_list>
@@ -36,6 +37,7 @@
 #include <string>
 #include <vector>
 #include <atomic>
+#include <utility>
 
 namespace config {
 
@@ -143,6 +145,8 @@ private:
     void setInternal(const std::string& key, const std::vector<T>& values);
     template<typename T, std::size_t N>
     void setInternal(const std::string& key, const std::array<T, N>& values);
+    template<typename ... T>
+    void setInternal(const std::string& key, const std::pair<T...>& values);
 
     std::string getInternal(const std::string& key, std::string*);
     char* getInternal(const std::string& key, char**);
@@ -153,6 +157,8 @@ private:
     std::vector<T> getInternal(const std::string& key, std::vector<T>*);
     template<typename T, std::size_t N>
     std::array<T, N> getInternal(const std::string& key, std::array<T, N>*);
+    template<typename ... T>
+    std::pair<T...> getInternal(const std::string& key, std::pair<T...>*);
 
     std::string mPath;
     sqlite3::Connection mConn;
@@ -221,6 +227,26 @@ void KVStore::setInternal(const std::string& key, const std::array<T, N>& values
     setInternal(key, strValues);
 }
 
+struct SetTupleVisitor
+{
+    template<typename T>
+    static void visit(std::vector<std::string>::iterator& it, const T& value)
+    {
+        *it = toString<T>(value);
+        ++it;
+    }
+};
+
+template<typename ... T>
+void KVStore::setInternal(const std::string& key, const std::pair<T...>& values)
+{
+    std::vector<std::string> strValues(std::tuple_size<std::pair<T...>>::value);
+
+    SetTupleVisitor visitor;
+    visitFields(values, &visitor, strValues.begin());
+    setInternal(key, strValues);
+}
+
 template<typename T>
 T KVStore::getInternal(const std::string& key, T*)
 {
@@ -255,6 +281,28 @@ std::vector<T> KVStore::getInternal(const std::string& key, std::vector<T>*)
     return values;
 }
 
+struct GetTupleVisitor
+{
+    template<typename T>
+    static void visit(std::vector<std::string>::iterator& it, T& value)
+    {
+        value = fromString<T>(*it);
+        ++it;
+    }
+};
+
+template<typename ... T>
+std::pair<T...> KVStore::getInternal(const std::string& key, std::pair<T...>*)
+{
+    std::vector<std::string> strValues = getInternal(key, static_cast<std::vector<std::string>*>(nullptr));
+    std::pair<T...> values;
+
+    GetTupleVisitor visitor;
+    visitFields(values, &visitor, strValues.begin());
+
+    return values;
+}
+
 /**
  * Concatenates all parameters into one std::string.
  * Uses '.' to connect the terms.
index 7ce8058..ba9bb8e 100644 (file)
 #include "config/is-visitable.hpp"
 #include "config/fdstore.hpp"
 #include "config/types.hpp"
+#include "config/visit-fields.hpp"
 
 #include <array>
 #include <cstring>
 #include <string>
 #include <vector>
+#include <utility>
+
 
 namespace config {
 
@@ -105,6 +108,12 @@ private:
         }
     }
 
+    template<typename ... T>
+    void writeInternal(const std::pair<T...>& values)
+    {
+        visitFields(values, this, std::string());
+    }
+
 };
 
 } // namespace config
index 1bd1375..09c04e6 100644 (file)
 #include "config/is-visitable.hpp"
 #include "config/is-union.hpp"
 #include "config/types.hpp"
+#include "config/visit-fields.hpp"
 
 #include <array>
 #include <string>
 #include <vector>
 #include <glib.h>
+#include <utility>
 
 namespace config {
 
@@ -77,15 +79,33 @@ public:
 private:
     GVariantBuilder* mBuilder;
 
-    void writeInternal(std::int32_t value) { add("i", value); };
-    void writeInternal(std::int64_t value) { add("x", value); };
-    void writeInternal(std::uint32_t value) { add("u", value); };
-    void writeInternal(std::uint64_t value) { add("t", value); };
-    void writeInternal(bool value) { add("b", value); };
-    void writeInternal(double value) { add("d", value); };
-    void writeInternal(const std::string& value) { add("s", value.c_str()); };
-    void writeInternal(const char* value) { add("s", value); };
-    void writeInternal(const config::FileDescriptor& value) { add("h", value.value); };
+    void writeInternal(std::int32_t value) {
+        add("i", value);
+    };
+    void writeInternal(std::int64_t value) {
+        add("x", value);
+    };
+    void writeInternal(std::uint32_t value) {
+        add("u", value);
+    };
+    void writeInternal(std::uint64_t value) {
+        add("t", value);
+    };
+    void writeInternal(bool value) {
+        add("b", value);
+    };
+    void writeInternal(double value) {
+        add("d", value);
+    };
+    void writeInternal(const std::string& value) {
+        add("s", value.c_str());
+    };
+    void writeInternal(const char* value) {
+        add("s", value);
+    };
+    void writeInternal(const config::FileDescriptor& value) {
+        add("h", value.value);
+    };
 
     template<typename T>
     void writeInternal(const std::vector<T>& value)
@@ -115,6 +135,14 @@ private:
         }
     }
 
+    template<typename ... T>
+    void writeInternal(const std::pair<T...>& values)
+    {
+        g_variant_builder_open(mBuilder, G_VARIANT_TYPE_ARRAY);
+        visitFields(values, this, std::string());
+        g_variant_builder_close(mBuilder);
+    }
+
     template<typename T>
     typename std::enable_if<isVisitable<T>::value && !isUnion<T>::value>::type
     writeInternal(const T& value)
index 3c746d2..3b8facf 100644 (file)
 
 #include "config/is-visitable.hpp"
 #include "config/exception.hpp"
+#include "config/visit-fields.hpp"
 
-#include <json.h>
+#include <array>
 #include <string>
 #include <vector>
+#include <utility>
+
+#include <json.h>
 
 namespace config {
 
@@ -145,6 +149,25 @@ private:
         return array;
     }
 
+    struct HelperVisitor
+    {
+        template<typename T>
+        static void visit(json_object* array, const T& value)
+        {
+            json_object_array_add(array, toJsonObject(value));
+        }
+    };
+
+    template<typename ... T>
+    static json_object* toJsonObject(const std::pair<T...>& values)
+    {
+        json_object* array = json_object_new_array();
+
+        HelperVisitor visitor;
+        visitFields(values, &visitor, array);
+        return array;
+    }
+
     template<typename T, class = typename std::enable_if<isVisitable<T>::value>::type>
     static json_object* toJsonObject(const T& value)
     {
diff --git a/libs/config/visit-fields.hpp b/libs/config/visit-fields.hpp
new file mode 100644 (file)
index 0000000..1840c42
--- /dev/null
@@ -0,0 +1,65 @@
+/*
+ *  Copyright (c) 2014 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *  Contact: Jan Olszak (j.olszak@samsung.com)
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License
+ */
+
+/**
+ * @file
+ * @author  Jan Olszak (j.olszak@samsung.com)
+ * @brief   Helper function for iterating tuples, pairs and arrays
+ */
+
+
+#ifndef CONFIG_VISIT_FIELDS_HPP
+#define CONFIG_VISIT_FIELDS_HPP
+
+#include <string>
+#include <tuple>
+
+namespace {
+
+template<int I, class T, class F, typename ... A>
+struct visitImpl
+{
+    static void visit(T& t, F f, A&& ... args)
+    {
+        visitImpl<I - 1, T, F, A...>::visit(t, f, std::forward<A>(args)...);
+        f->visit(args..., std::get<I>(t));
+    }
+};
+
+template<class T, class F, typename ... A>
+struct visitImpl<0, T, F, A...>
+{
+    static void visit(T& t, F f, A&& ... args)
+    {
+        f->visit(args..., std::get<0>(t));
+    }
+};
+
+} // namespace
+
+namespace config {
+
+template<class T, class F, typename ... A>
+void visitFields(T& t, F f, A ... args)
+{
+    visitImpl<std::tuple_size<T>::value - 1, T, F, A...>::visit(t, f, std::forward<A>(args)...);
+}
+
+} // namespace config
+
+#endif // CONFIG_VISIT_FIELDS_HPP
index c6f8476..3cdd876 100644 (file)
@@ -99,6 +99,8 @@ struct TestConfig {
 
     std::array<int, 2> intArray;
 
+    std::pair<int, int> intIntPair;
+
     SubConfig subObj;
     std::vector<SubConfig> subVector;
 
@@ -124,6 +126,8 @@ struct TestConfig {
 
         intArray,
 
+        intIntPair,
+
         subObj,
         subVector,
 
@@ -164,16 +168,17 @@ const std::string jsonTestString =
     "\"stringVector\": [ \"a\", \"b\" ], "
     "\"doubleVector\": [ 0.000000, 1.000000, 2.000000 ], "
     "\"intArray\": [ 0, 1 ], "
+    "\"intIntPair\": [ 8, 9 ], "
     "\"subObj\": { \"intVal\": 54321, \"intVector\": [ 1, 2 ], \"subSubObj\": { \"intVal\": 234 } }, "
     "\"subVector\": [ { \"intVal\": 123, \"intVector\": [ 3, 4 ], \"subSubObj\": { \"intVal\": 345 } }, "
-        "{ \"intVal\": 456, \"intVector\": [ 5, 6 ], \"subSubObj\": { \"intVal\": 567 } } ], "
+    "{ \"intVal\": 456, \"intVector\": [ 5, 6 ], \"subSubObj\": { \"intVal\": 567 } } ], "
     "\"union1\": { \"type\": \"int\", \"value\": 2 }, "
     "\"union2\": { \"type\": \"SubConfig\", \"value\": { \"intVal\": 54321, \"intVector\": [ 1 ], "
-        "\"subSubObj\": { \"intVal\": 234 } } }, "
+    "\"subSubObj\": { \"intVal\": 234 } } }, "
     "\"unions\": [ "
-        "{ \"type\": \"int\", \"value\": 2 }, "
-        "{ \"type\": \"SubConfig\", \"value\": { \"intVal\": 54321, \"intVector\": [ 1 ], "
-            "\"subSubObj\": { \"intVal\": 234 } } } ] }";
+    "{ \"type\": \"int\", \"value\": 2 }, "
+    "{ \"type\": \"SubConfig\", \"value\": { \"intVal\": 54321, \"intVector\": [ 1 ], "
+    "\"subSubObj\": { \"intVal\": 234 } } } ] }";
 
 const std::string jsonEmptyTestString =
     "{ \"intVal\": 0, "
@@ -188,6 +193,7 @@ const std::string jsonEmptyTestString =
     "\"stringVector\": [ ], "
     "\"doubleVector\": [ ], "
     "\"intArray\": [ ], "
+    "\"intIntPair\": [ ], "
     "\"subObj\": { \"intVal\": 0, \"intVector\": [ ], \"subSubObj\": { \"intVal\": 0 } }, "
     "\"subVector\": [ ], "
     "\"union1\": { \"type\": \"int\", \"value\": 0 }, "
index a134b7b..e50ed92 100644 (file)
@@ -88,6 +88,9 @@ BOOST_AUTO_TEST_CASE(FromJsonString)
     BOOST_CHECK_EQUAL(0,   testConfig.intArray[0]);
     BOOST_CHECK_EQUAL(1,   testConfig.intArray[1]);
 
+    BOOST_CHECK_EQUAL(8, testConfig.intIntPair.first);
+    BOOST_CHECK_EQUAL(9, testConfig.intIntPair.second);
+
     BOOST_CHECK_EQUAL(54321, testConfig.subObj.intVal);
     BOOST_CHECK_EQUAL(2,     testConfig.subObj.intVector.size());
     BOOST_CHECK_EQUAL(1,     testConfig.subObj.intVector[0]);