From 0b0141809be1d20248a39f16d4ed25a9cc64afea Mon Sep 17 00:00:00 2001
From: Christoph Lehmann <christoph.lehmann@ufz.de>
Date: Fri, 15 Jan 2016 19:42:19 +0100
Subject: [PATCH] [BL,T] improved move assignment

---
 BaseLib/ConfigTreeNew.cpp        | 54 ++++++++++++++++++--------------
 BaseLib/ConfigTreeNew.h          | 11 +++++--
 Tests/BaseLib/TestConfigTree.cpp | 35 ++++++++++++++++++++-
 3 files changed, 73 insertions(+), 27 deletions(-)

diff --git a/BaseLib/ConfigTreeNew.cpp b/BaseLib/ConfigTreeNew.cpp
index eed2bfb36cb..8373a7facfc 100644
--- a/BaseLib/ConfigTreeNew.cpp
+++ b/BaseLib/ConfigTreeNew.cpp
@@ -54,37 +54,21 @@ ConfigTreeNew(ConfigTreeNew && other)
 
 ConfigTreeNew::~ConfigTreeNew()
 {
-    if (!_tree) return;
-
-    for (auto const& p : *_tree)
-    {
-        markVisitedDecrement(p.first);
-    }
-
-    for (auto const& p : _visited_params)
-    {
-        if (p.second.count > 0) {
-            warning("Key <" + p.first + "> has been read " + std::to_string(p.second.count)
-                    + " time(s) more than it was present in the configuration tree.");
-        } else if (p.second.count < 0) {
-            warning("Key <" + p.first + "> has been read " + std::to_string(-p.second.count)
-                    + " time(s) less than it was present in the configuration tree.");
-        }
-    }
+    checkFullyRead();
 }
 
 ConfigTreeNew&
 ConfigTreeNew::
 operator=(ConfigTreeNew&& other)
 {
-    _tree = other._tree;
-    other._tree = nullptr;
-    _path = other._path;
-    _visited_params = std::move(other._visited_params);
+    checkFullyRead();
 
-    // TODO Caution: That might be a very nontrivial operation (copying a std::function).
-    _onerror = other._onerror;
-    _onwarning = other._onwarning;
+    _tree           = other._tree;
+    other._tree     = nullptr;
+    _path           = std::move(other._path);
+    _visited_params = std::move(other._visited_params);
+    _onerror        = std::move(other._onerror);
+    _onwarning      = std::move(other._onwarning);
 
     return *this;
 }
@@ -236,4 +220,26 @@ markVisitedDecrement(std::string const& key) const
     }
 }
 
+void
+ConfigTreeNew::checkFullyRead() const
+{
+    if (!_tree) return;
+
+    for (auto const& p : *_tree)
+    {
+        markVisitedDecrement(p.first);
+    }
+
+    for (auto const& p : _visited_params)
+    {
+        if (p.second.count > 0) {
+            warning("Key <" + p.first + "> has been read " + std::to_string(p.second.count)
+                    + " time(s) more than it was present in the configuration tree.");
+        } else if (p.second.count < 0) {
+            warning("Key <" + p.first + "> has been read " + std::to_string(-p.second.count)
+                    + " time(s) less than it was present in the configuration tree.");
+        }
+    }
+}
+
 }
diff --git a/BaseLib/ConfigTreeNew.h b/BaseLib/ConfigTreeNew.h
index a14748733b2..50f55b8ea91 100644
--- a/BaseLib/ConfigTreeNew.h
+++ b/BaseLib/ConfigTreeNew.h
@@ -192,8 +192,6 @@ public:
     //! used anymore!
     ConfigTreeNew(ConfigTreeNew && other);
 
-    ConfigTreeNew() = delete;
-
     //! copying is not compatible with the semantics of this class
     ConfigTreeNew& operator=(ConfigTreeNew const&) = delete;
 
@@ -364,6 +362,10 @@ private:
     //! and the number of times it exists in the ConfigTree
     void markVisitedDecrement(std::string const& key) const;
 
+    //! Helper method that checks if the top level of this tree has
+    //! been red entirely (and not too often).
+    void checkFullyRead() const;
+
     //! returns a short string at suitable for error/warning messages
     static std::string shortString(std::string const& s);
 
@@ -375,6 +377,11 @@ private:
 
     //! A map key -> (count, type) keeping track which parameters have been read how often
     //! and which datatype they have.
+    //!
+    //! This member will be written to when reading from the config tree.
+    //! Therefore it has to be mutable in order to be able to read from
+    //! constant instances, e.g., those passed as constant references to
+    //! temporaries.
     mutable std::map<std::string, CountType> _visited_params;
 
     Callback _onerror;
diff --git a/Tests/BaseLib/TestConfigTree.cpp b/Tests/BaseLib/TestConfigTree.cpp
index 250ac3e15dc..7b5bb62362e 100644
--- a/Tests/BaseLib/TestConfigTree.cpp
+++ b/Tests/BaseLib/TestConfigTree.cpp
@@ -400,7 +400,7 @@ TEST(BaseLibConfigTree, ConfigTreeStringLiterals)
 }
 
 // String literals are somewhat special for template classes
-TEST(BaseLibConfigTree, ConfigTreeMove)
+TEST(BaseLibConfigTree, ConfigTreeMoveConstruct)
 {
     const char xml[] =
             "<s>test</s>"
@@ -428,3 +428,36 @@ TEST(BaseLibConfigTree, ConfigTreeMove)
     DO_EXPECT(cbs, false, false);
 }
 
+// String literals are somewhat special for template classes
+TEST(BaseLibConfigTree, ConfigTreeMoveAssign)
+{
+    const char xml[] =
+            "<s>test</s>"
+            "<t>Test</t>";
+
+    boost::property_tree::ptree ptree;
+    std::istringstream xml_str(xml);
+    read_xml(xml_str, ptree);
+
+    Callbacks cbs;
+    {
+        BaseLib::ConfigTreeNew conf(ptree, cbs.get_error_cb(), cbs.get_warning_cb());
+
+        EXPECT_EQ("test", conf.getConfParam<std::string>("s", "XX"));
+        DO_EXPECT(cbs, false, false);
+
+        BaseLib::ConfigTreeNew conf2(ptree, cbs.get_error_cb(), cbs.get_warning_cb());
+        conf2 = std::move(conf);
+        // Expect warning because config tree has not been traversed
+        // entirely before.
+        DO_EXPECT(cbs, false, true);
+
+        EXPECT_EQ("XX",   conf2.getConfParam<std::string>("n", "XX"));
+        DO_EXPECT(cbs, false, false);
+
+        conf2.checkConfParam("t", "Test");
+        DO_EXPECT(cbs, false, false);
+    } // ConfigTree destroyed here
+    DO_EXPECT(cbs, false, false);
+}
+
-- 
GitLab