Modernize syntax of enable_if and traits to use _t helpers
[gromacs.git] / src / gromacs / utility / keyvaluetree.cpp
blobd0e3c6b7aa08b37b6a97b608a078ce104043ef39
1 /*
2 * This file is part of the GROMACS molecular simulation package.
4 * Copyright (c) 2016,2017,2018, by the GROMACS development team, led by
5 * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
6 * and including many others, as listed in the AUTHORS file in the
7 * top-level source directory and at http://www.gromacs.org.
9 * GROMACS is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU Lesser General Public License
11 * as published by the Free Software Foundation; either version 2.1
12 * of the License, or (at your option) any later version.
14 * GROMACS is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 * Lesser General Public License for more details.
19 * You should have received a copy of the GNU Lesser General Public
20 * License along with GROMACS; if not, see
21 * http://www.gnu.org/licenses, or write to the Free Software Foundation,
22 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
24 * If you want to redistribute modifications to GROMACS, please
25 * consider that scientific software is very special. Version
26 * control is crucial - bugs must be traceable. We will be happy to
27 * consider code for inclusion in the official distribution, but
28 * derived work must not be called official GROMACS. Details are found
29 * in the README & COPYING files - if they are missing, get the
30 * official version at http://www.gromacs.org.
32 * To help us fund GROMACS development, we humbly ask that you cite
33 * the research papers on the package. Check out http://www.gromacs.org.
35 #include "gmxpre.h"
37 #include "keyvaluetree.h"
39 #include <string>
40 #include <vector>
42 #include "gromacs/utility/compare.h"
43 #include "gromacs/utility/gmxassert.h"
44 #include "gromacs/utility/strconvert.h"
45 #include "gromacs/utility/stringutil.h"
46 #include "gromacs/utility/textwriter.h"
48 namespace gmx
51 namespace
54 //! Helper function to split a KeyValueTreePath to its components
55 std::vector<std::string> splitPathElements(const std::string &path)
57 GMX_ASSERT(!path.empty() && path[0] == '/',
58 "Paths to KeyValueTree should start with '/'");
59 return splitDelimitedString(path.substr(1), '/');
62 } // namespace
64 /********************************************************************
65 * KeyValueTreePath
68 KeyValueTreePath::KeyValueTreePath(const char *path)
69 : path_(splitPathElements(path))
73 KeyValueTreePath::KeyValueTreePath(const std::string &path)
74 : path_(splitPathElements(path))
78 std::string KeyValueTreePath::toString() const
80 return "/" + joinStrings(path_, "/");
83 /********************************************************************
84 * KeyValueTreeObject
87 bool KeyValueTreeObject::hasDistinctProperties(const KeyValueTreeObject &obj) const
89 for (const auto &prop : obj.values_)
91 if (keyExists(prop.key()))
93 GMX_RELEASE_ASSERT(!prop.value().isArray(),
94 "Comparison of arrays not implemented");
95 if (prop.value().isObject() && valueMap_.at(prop.key()).isObject())
97 return valueMap_.at(prop.key()).asObject().hasDistinctProperties(prop.value().asObject());
99 return false;
102 return true;
105 /********************************************************************
106 * Key value tree dump
109 //! \cond libapi
110 void dumpKeyValueTree(TextWriter *writer, const KeyValueTreeObject &tree)
112 for (const auto &prop : tree.properties())
114 const auto &value = prop.value();
115 if (value.isObject())
117 writer->writeString(prop.key());
118 writer->writeLine(":");
119 int oldIndent = writer->wrapperSettings().indent();
120 writer->wrapperSettings().setIndent(oldIndent + 2);
121 dumpKeyValueTree(writer, value.asObject());
122 writer->wrapperSettings().setIndent(oldIndent);
124 else
126 int indent = writer->wrapperSettings().indent();
127 writer->writeString(formatString("%*s", -(33-indent), prop.key().c_str()));
128 writer->writeString(" = ");
129 if (value.isArray())
131 writer->writeString("[");
132 for (const auto &elem : value.asArray().values())
134 GMX_RELEASE_ASSERT(!elem.isObject() && !elem.isArray(),
135 "Arrays of objects not currently implemented");
136 writer->writeString(" ");
137 writer->writeString(simpleValueToString(elem));
139 writer->writeString(" ]");
141 else
143 writer->writeString(simpleValueToString(value));
145 writer->writeLine();
149 //! \endcond
151 /********************************************************************
152 * Key value tree comparison
155 namespace
158 class CompareHelper
160 public:
161 CompareHelper(TextWriter *writer, real ftol, real abstol)
162 : writer_(writer), ftol_(ftol), abstol_(abstol)
166 void compareObjects(const KeyValueTreeObject &obj1,
167 const KeyValueTreeObject &obj2)
169 for (const auto &prop1 : obj1.properties())
171 currentPath_.append(prop1.key());
172 if (obj2.keyExists(prop1.key()))
174 compareValues(prop1.value(), obj2[prop1.key()]);
176 else
178 handleMissingKeyInSecondObject(prop1.value());
180 currentPath_.pop_back();
182 for (const auto &prop2 : obj2.properties())
184 currentPath_.append(prop2.key());
185 if (!obj1.keyExists(prop2.key()))
187 handleMissingKeyInFirstObject(prop2.value());
189 currentPath_.pop_back();
193 private:
194 void compareValues(const KeyValueTreeValue &value1,
195 const KeyValueTreeValue &value2)
197 if (value1.type() == value2.type())
199 if (value1.isObject())
201 compareObjects(value1.asObject(), value2.asObject());
203 else if (value1.isArray())
205 GMX_RELEASE_ASSERT(false, "Array comparison not implemented");
207 else if (!areSimpleValuesOfSameTypeEqual(value1, value2))
209 writer_->writeString(currentPath_.toString());
210 writer_->writeLine(formatString(" (%s - %s)", simpleValueToString(value1).c_str(), simpleValueToString(value2).c_str()));
213 else if ((value1.isType<double>() && value2.isType<float>())
214 || (value1.isType<float>() && value2.isType<double>()))
216 const bool firstIsDouble
217 = value1.isType<double>();
218 const float v1 = firstIsDouble ? value1.cast<double>() : value1.cast<float>();
219 const float v2 = firstIsDouble ? value2.cast<float>() : value2.cast<double>();
220 if (!equal_float(v1, v2, ftol_, abstol_))
222 writer_->writeString(currentPath_.toString());
223 writer_->writeLine(formatString(" (%e - %e)", v1, v2));
226 else
228 handleMismatchingTypes(value1, value2);
232 bool areSimpleValuesOfSameTypeEqual(
233 const KeyValueTreeValue &value1,
234 const KeyValueTreeValue &value2)
236 GMX_ASSERT(value1.type() == value2.type(),
237 "Caller should ensure that types are equal");
238 if (value1.isType<bool>())
240 return value1.cast<bool>() == value2.cast<bool>();
242 else if (value1.isType<int>())
244 return value1.cast<int>() == value2.cast<int>();
246 else if (value1.isType<int64_t>())
248 return value1.cast<int64_t>() == value2.cast<int64_t>();
250 else if (value1.isType<double>())
252 return equal_double(value1.cast<double>(), value2.cast<double>(), ftol_, abstol_);
254 else if (value1.isType<float>())
256 return equal_float(value1.cast<float>(), value2.cast<float>(), ftol_, abstol_);
258 else if (value1.isType<std::string>())
260 return value1.cast<std::string>() == value2.cast<std::string>();
262 else
264 GMX_RELEASE_ASSERT(false, "Unknown value type");
265 return false;
269 void handleMismatchingTypes(const KeyValueTreeValue & /* value1 */,
270 const KeyValueTreeValue & /* value2 */)
272 writer_->writeString(currentPath_.toString());
273 writer_->writeString(" type mismatch");
276 void handleMissingKeyInFirstObject(const KeyValueTreeValue &value)
278 const std::string message = formatString(
279 "%s (missing - %s)", currentPath_.toString().c_str(),
280 formatValueForMissingMessage(value).c_str());
281 writer_->writeLine(message);
283 void handleMissingKeyInSecondObject(const KeyValueTreeValue &value)
285 const std::string message = formatString(
286 "%s (%s - missing)", currentPath_.toString().c_str(),
287 formatValueForMissingMessage(value).c_str());
288 writer_->writeLine(message);
291 std::string formatValueForMissingMessage(const KeyValueTreeValue &value)
293 if (value.isObject() || value.isArray())
295 return "present";
297 return simpleValueToString(value);
300 KeyValueTreePath currentPath_;
301 TextWriter *writer_;
302 real ftol_;
303 real abstol_;
306 } // namespace
308 //! \cond libapi
309 void compareKeyValueTrees(TextWriter *writer,
310 const KeyValueTreeObject &tree1,
311 const KeyValueTreeObject &tree2,
312 real ftol,
313 real abstol)
315 CompareHelper helper(writer, ftol, abstol);
316 helper.compareObjects(tree1, tree2);
318 //! \endcond
320 } // namespace gmx