lmi-commits
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[lmi-commits] [lmi] master d385546 10/12: Serialize associative-pair con


From: Greg Chicares
Subject: [lmi-commits] [lmi] master d385546 10/12: Serialize associative-pair containers to and from xml
Date: Sat, 24 Oct 2020 16:51:36 -0400 (EDT)

branch: master
commit d385546a4cebf2424a3f47b0bbbdf859947613d6
Author: Gregory W. Chicares <gchicares@sbcglobal.net>
Commit: Gregory W. Chicares <gchicares@sbcglobal.net>

    Serialize associative-pair containers to and from xml
---
 xml_serialize.hpp      | 73 ++++++++++++++++++++++++++++++++++++++++++++++++--
 xml_serialize_test.cpp | 38 +++++++++++++++++++++++---
 2 files changed, 106 insertions(+), 5 deletions(-)

diff --git a/xml_serialize.hpp b/xml_serialize.hpp
index 49d1822..7b1e36e 100644
--- a/xml_serialize.hpp
+++ b/xml_serialize.hpp
@@ -31,8 +31,10 @@
 
 #include <xmlwrapp/nodes_view.h>
 
+#include <map>
 #include <string>
 #include <type_traits>
+#include <unordered_map>
 #include <utility>                      // pair
 #include <vector>
 
@@ -122,8 +124,8 @@ struct xml_io<std::pair<T1,T2>>
 /// (and non-element nodes) that might have been added manually,
 /// e.g., as documentation.
 ///
-/// C++11 has no way to assert that C is a Sequence; for the nonce,
-/// no other Sequence being used, assert that it's a vector.
+/// C++17 has no assertable "SequenceContainer" concept; for the
+/// nonce, no other Sequence being used, assert that C is a vector.
 
 template<typename C>
 struct xml_sequence_io
@@ -162,6 +164,73 @@ struct xml_io<std::vector<T>>
   :public xml_sequence_io<std::vector<T>>
 {};
 
+/// Serialization for associative-pair containers.
+///
+/// Derive publicly from this to use its implementation when
+/// specializing class template xml_io for a particular sequence.
+///
+/// from_xml() reads only <item> elements, ignoring other elements
+/// (and non-element nodes) that might have been added manually,
+/// e.g., as documentation.
+///
+/// C++ calls std::set and its brethren "associative", even though
+/// they associate a key with...nothing. This implementation is for
+/// containers that associate a key with something. C++20 has no
+/// Concept for this; for the nonce, assert that it's a std::map or
+/// a std::unordered_map.
+
+template<typename C>
+struct xml_pair_container_io
+{
+    using K = typename C::key_type;
+    using T = typename C::mapped_type;
+    // A key value cannot be read into 'K const'; therefore,
+    // 'V' is not the same as 'C::value_type', which is:
+    //        std::pair<K const, T>
+    using V = std::pair<K,T>;
+
+    static_assert
+        (  std::is_same<C,std::map<K,T>>::value
+        || std::is_same<C,std::unordered_map<K,T>>::value
+        );
+
+    static void to_xml(xml::element& parent, C const& c)
+    {
+        parent.clear();
+        for(auto const& i : c)
+            {
+            // This is not equivalent to calling set_element():
+            // multiple <item> elements are expressly permitted.
+            xml::element e("item");
+            xml_io<V>::to_xml(e, i);
+            parent.push_back(e);
+            }
+    }
+
+    static void from_xml(xml::element const& parent, C& c)
+    {
+        c.clear();
+        // It would be good to call std::unordered_map::reserve()
+        // upstream if the number of elements is known.
+        for(auto const& i : parent.elements("item"))
+            {
+            V v;
+            xml_io<V>::from_xml(i, v);
+            c.insert(v);
+            }
+    }
+};
+
+template<typename K, typename T>
+struct xml_io<std::map<K,T>>
+  :public xml_pair_container_io<std::map<K,T>>
+{};
+
+template<typename K, typename T>
+struct xml_io<std::unordered_map<K,T>>
+  :public xml_pair_container_io<std::unordered_map<K,T>>
+{};
+
 /// Serialize a datum into a subelement of the given xml element.
 ///
 /// Precondition: parent has no element with the given tagname.
diff --git a/xml_serialize_test.cpp b/xml_serialize_test.cpp
index d5f79fe..cb66919 100644
--- a/xml_serialize_test.cpp
+++ b/xml_serialize_test.cpp
@@ -27,11 +27,14 @@
 #include "test_tools.hpp"
 #include "timer.hpp"
 
+#include <map>
 #include <string>
+#include <unordered_map>
 #include <utility>                      // pair
 #include <vector>
 
-// All /write.*/ functions save xml to this string.
+// All /write.*/ functions save xml to this string, and
+// all /read.*/ functions read from it.
 
 std::string dom_string;
 
@@ -39,18 +42,25 @@ std::string dom_string;
 
 int const number_of_elements = 20;
 
-// /[dsv]0/: constant values for /write.*/ functions.
-// /[dsv]1/: variables for /read.*/ functions.
+using omap = std::          map<std::string, float>;
+using umap = std::unordered_map<int        , float>;
+
+// /[dspvou]0/: constant values for /write.*/ functions.
+// /[dspvou]1/: variables for /read.*/ functions.
 
 double             const d0(2.718281828459045235360);
 std::string        const s0("string with ampersand & embedded spaces");
 std::pair<int,int> const p0 {17, 19};
 std::vector<int>   const v0 {10, 2, 4}; // Be a pepper...
+omap               const o0 {{"a",  1.1f}, {"b",  2.2f}, {"c",  3.3f}};
+umap               const u0 {{1, 101.01f}, {2, 202.02f}, {3, 303.03f}};
 
 double                   d1;
 std::string              s1;
 std::pair<int,int>       p1;
 std::vector<int>         v1;
+omap                     o1;
+umap                     u1;
 
 void write()
 {
@@ -60,6 +70,8 @@ void write()
     xml_serialize::set_element(root, "s", s0);
     xml_serialize::set_element(root, "p", p0);
     xml_serialize::set_element(root, "v", v0);
+    xml_serialize::set_element(root, "o", o0);
+    xml_serialize::set_element(root, "u", u0);
     dom_string = document.str();
 }
 
@@ -71,6 +83,8 @@ void read()
     xml_serialize::get_element(root, "s", s1);
     xml_serialize::get_element(root, "p", p1);
     xml_serialize::get_element(root, "v", v1);
+    xml_serialize::get_element(root, "o", o1);
+    xml_serialize::get_element(root, "u", u1);
 }
 
 void write_erroneous()
@@ -91,6 +105,8 @@ void read_erroneous()
     xml_serialize::get_element(root, "s", s1);
     xml_serialize::get_element(root, "p", p1);
     xml_serialize::get_element(root, "v", v1);
+    xml_serialize::get_element(root, "o", o1);
+    xml_serialize::get_element(root, "u", u1);
     xml_serialize::get_element(root, "f", f1); // Error: no <f> element.
 }
 
@@ -152,6 +168,12 @@ void mete_read_p()  {mete_read ("p", p1);}
 void mete_write_v() {mete_write("v", v0);}
 void mete_read_v()  {mete_read ("v", v1);}
 
+void mete_write_o() {mete_write("o", o0);}
+void mete_read_o()  {mete_read ("o", o1);}
+
+void mete_write_u() {mete_write("u", u0);}
+void mete_read_u()  {mete_read ("u", u1);}
+
 int test_main(int, char*[])
 {
     write();
@@ -179,6 +201,12 @@ int test_main(int, char*[])
     BOOST_TEST(v0 == v1);
     BOOST_TEST_EQUAL(v0.size(), v1.size());
 
+    BOOST_TEST(o0 == o1);
+    BOOST_TEST_EQUAL(o0.size(), o1.size());
+
+    BOOST_TEST(u0 == u1);
+    BOOST_TEST_EQUAL(u0.size(), u1.size());
+
     std::string found
         ("Assertion 'parent.end() == parent.find(name.c_str())' failed."
         );
@@ -198,6 +226,10 @@ int test_main(int, char*[])
     std::cout << "  Read  p     : " << TimeAnAliquot(mete_read_p ) << '\n';
     std::cout << "  Write v     : " << TimeAnAliquot(mete_write_v) << '\n';
     std::cout << "  Read  v     : " << TimeAnAliquot(mete_read_v ) << '\n';
+    std::cout << "  Write o     : " << TimeAnAliquot(mete_write_o) << '\n';
+    std::cout << "  Read  o     : " << TimeAnAliquot(mete_read_o ) << '\n';
+    std::cout << "  Write u     : " << TimeAnAliquot(mete_write_u) << '\n';
+    std::cout << "  Read  u     : " << TimeAnAliquot(mete_read_u ) << '\n';
     std::cout << std::endl;
 
     return 0;



reply via email to

[Prev in Thread] Current Thread [Next in Thread]