Utopia 2
Framework for studying models of complex & adaptive systems.
Loading...
Searching...
No Matches
cfg_utils.hh
Go to the documentation of this file.
1#ifndef DATAIO_CFG_UTILS_HH
2#define DATAIO_CFG_UTILS_HH
3
4#include <list>
5#include <sstream>
6
7#include <boost/core/demangle.hpp>
8#include <armadillo>
9#include <yaml-cpp/yaml.h>
10
11#include "../core/exceptions.hh"
12#include "../core/string.hh"
13#include "../core/types.hh"
14
15namespace Utopia
16{
17namespace DataIO
18{
19
62// end group ConfigUtilities
67// .. Config reading helper functions .........................................
68
69// TODO Make this an actual Utopia exception
71template < class Exc >
72YAML::Exception
74 const Config& node,
75 std::string prefix = {})
76{
77 // The string stream for the new, improved error message
78 std::stringstream e_msg;
79 e_msg << prefix << " ";
80 e_msg << "Got " << boost::core::demangle(typeid(e).name()) << ". ";
81
82 // Create a custom error message depending on whether the node is a
83 // zombie or a mark is available
84 if (not node)
85 {
86 // Was a zombie
87 e_msg << "The given node was a Zombie! Check that the key you are "
88 "trying to read from actually exists. ";
89 }
90 else if (not node.Mark().is_null())
91 {
92 // A mark is available to use as a hint
93 // NOTE Mark() provides the line and column in the config file, if
94 // available, i.e.: if not a zombie node
95 e_msg << "Check that the corresponding line of the config file "
96 "matches the desired read operation or type conversion. ";
97 }
98
99 // Give some information on the node's content:
100 e_msg << "The content of the node is:" << std::endl << YAML::Dump(node);
101
102 // Return the custom exception object; can be thrown on other side
103 return YAML::Exception(node.Mark(), e_msg.str());
104}
105
107
109std::string
111{
112 std::stringstream s;
113 s << YAML::Dump(node);
114 return s.str();
115}
116
117} // namespace DataIO
118
124// NOTE All code below is not inside the Utopia::DataIO namespace to make
125// includes into models more convenient.
126
128
156template < typename ReturnType >
158get_as(const std::string& key, const DataIO::Config& node)
159{
160 try
161 {
162 return node[key].template as< ReturnType >();
163 }
164 catch (YAML::Exception& e)
165 {
166 if (not node[key])
167 {
168 throw KeyError(key, node);
169 }
170 // else: throw an improved error message
172 "Could not read key '" + key +
173 "' from given config node!");
174 }
175 catch (std::exception& e)
176 {
177 // Some other exception; provide at least some info and context
178 std::cerr << boost::core::demangle(typeid(e).name())
179 << " occurred during reading key '" << key
180 << "' from config!" << std::endl;
181
182 // Re-throw the original exception
183 throw;
184 }
185 catch (...)
186 {
187 throw std::runtime_error("Unexpected exception occurred during "
188 "reading key '" + key + "'' from config!");
189 }
190}
191
193template < typename ReturnType >
195get_as(const std::string& key, const DataIO::Config& node, ReturnType fallback)
196{
197 try
198 {
200 }
201 catch (KeyError&)
202 {
203 return fallback;
204 }
205 // All other errors will (rightly) be thrown
206}
207
208// end group ConfigUtilities
213// Armadillo-related specialization
214// TODO These should be implemented specializations of the get_as function!
215namespace DataIO
216{
217
219
226template < typename CVecT, DimType dim = 0 >
227CVecT
228get_as_arma_vec(const std::string& key, const DataIO::Config& node)
229{
230 // Extract the field vector element type; assuming Armadillo interface
231 using element_t = typename CVecT::elem_type;
232
233 // Check if it can be constructed from a vector
234 if constexpr (std::is_constructible< CVecT, std::vector< element_t > >())
235 {
237 }
238 else
239 {
240 static_assert(dim > 0,
241 "Need template argument dim given if target type is not "
242 "constructible from std::vector.");
243
244 // Needs to be constructed element-wise
245 CVecT cvec;
247
248 for (DimType i = 0; i < dim; i++)
249 {
250 cvec[i] = vec[i];
251 }
252
253 return cvec;
254 }
255}
256} // namespace DataIO
257
258
265
267template < DimType dim >
269get_as_SpaceVec(const std::string& key, const DataIO::Config& node)
270{
271 return DataIO::get_as_arma_vec< SpaceVecType< dim >, dim >(key, node);
272}
273
275
277template < DimType dim >
279get_as_MultiIndex(const std::string& key, const DataIO::Config& node)
280{
281 return DataIO::get_as_arma_vec< MultiIndexType< dim >, dim >(key, node);
282}
283
284// end group ConfigUtilities
289// -- INTERNALLY USED Functions to work with Config trees ---------------------
290
291namespace _internal {
292
294
301template<class T, class Keys = std::list<std::string> >
304 const T& val)
305{
306 // Get the next key
307 std::string key;
308 while (key.empty() and not key_sequence.empty()) {
309 key = key_sequence.front();
310 key_sequence.pop_front();
311 }
312 if (key.empty()) {
313 throw std::invalid_argument(
314 "During recursive_setitem, failed to retrieve a valid key for "
315 "continuing recursion. Make sure the given key sequence ("
316 + join(key_sequence, " -> ") + ") contains no empty elements!"
317 );
318 }
319
320 // Check for end of recursion
321 if (key_sequence.empty()) {
322 if (d.IsScalar()) {
323 // Discard the scalar and replace it with an empty mapping
324 d = Config{};
325 }
326 d[key] = val;
327 return d;
328 }
329
330 // Continue recursion, creating the intermediate node if it does not exist
331 if (not d[key]) {
332 d[key] = Config{};
333 }
334 d[key] = __recursive_setitem(d[key], std::forward<Keys>(key_sequence),
335 val);
336 return d;
337}
338
339
341
361 const std::vector<std::string>& key_sequence)
362{
363 using Utopia::KeyError;
364 using Utopia::join;
365
366 Config rv = YAML::Clone(d);
367 for (const auto& key : key_sequence ) {
368 try {
370 }
371 catch (KeyError& err) {
372 throw KeyError(
373 key, rv,
374 "recursive_getitem failed for key or key sequence '"
375 + join(key_sequence, " -> ") + "'!"
376 );
377 }
378 }
379 return rv;
380}
381
383
399 const std::string& key_sequence,
400 const std::string& delims = ".")
401{
403}
404
405
407
421template<class T>
423 std::list<std::string> key_sequence,
424 const T val)
425{
426 if (key_sequence.empty()) {
427 throw std::invalid_argument(
428 "Key sequence for recursive_setitem may not be empty!"
429 );
430 }
431 d = __recursive_setitem(YAML::Clone(d), std::move(key_sequence), val);
432}
433
435
448template<class T>
450 const std::string& key_sequence,
451 const T val,
452 const std::string& delims = ".")
453{
455 d, split<std::list<std::string>>(key_sequence, delims), val
456 );
457}
458
459} // namespace _internal
460} // namespace Utopia
461
462#endif // DATAIO_CFG_UTILS_HH
For access to a dict-like structure with a bad key.
Definition exceptions.hh:67
MultiIndexType< dim > get_as_MultiIndex(const std::string &key, const DataIO::Config &node)
Special case of Utopia::get_as to retrieve an entry as MultiIndex.
Definition cfg_utils.hh:279
YAML::Node Config
Type of a variadic dictionary-like data structure used throughout Utopia.
Definition types.hh:71
SpaceVecType< dim > get_as_SpaceVec(const std::string &key, const DataIO::Config &node)
Special case of Utopia::get_as to retrieve an entry as SpaceVec.
Definition cfg_utils.hh:269
ReturnType get_as(const std::string &key, const DataIO::Config &node)
This function is a wrapper around the yaml-cpp YAML::Node::as function.
Definition cfg_utils.hh:158
Config __recursive_setitem(Config d, Keys &&key_sequence, const T &val)
Helper function for recursive_setitem.
Definition cfg_utils.hh:302
Config recursive_getitem(const Config &d, const std::vector< std::string > &key_sequence)
Recursively retrieve an element from the configuration tree.
Definition cfg_utils.hh:360
YAML::Exception improve_yaml_exception(const Exc &e, const Config &node, std::string prefix={})
Improves yaml-cpp exceptions occurring for a given node.
Definition cfg_utils.hh:73
void recursive_setitem(Config &d, std::list< std::string > key_sequence, const T val)
Recursively sets an element in a configuration tree.
Definition cfg_utils.hh:422
std::string to_string(const Config &node)
Given a config node, returns a string representation of it.
Definition cfg_utils.hh:110
CVecT get_as_arma_vec(const std::string &key, const DataIO::Config &node)
Retrieve a config entry as Armadillo column vector using get_.
Definition cfg_utils.hh:228
Container select_entities(const Manager &mngr, const DataIO::Config &sel_cfg)
Select entities according to parameters specified in a configuration.
Definition select.hh:213
Definition agent.hh:11
DataIO::Config Config
Type of a variadic dictionary-like data structure used throughout Utopia.
Definition types.hh:80
SeqCont split(const std::string &s, const std::string &delims=" ")
Splits a string and returns a container of string segments.
Definition string.hh:45
std::string join(const Cont &cont, const std::string &delim=", ")
Joins together the strings in a container.
Definition string.hh:18
unsigned short DimType
Type for dimensions, i.e. very small unsigned integers.
Definition types.hh:34