Version: SMASH-3.4
configuration.cc
Go to the documentation of this file.
1 /*
2  *
3  * Copyright (c) 2014-2019,2022,2024,2026
4  * SMASH Team
5  *
6  * GNU General Public License (GPLv3 or later)
7  *
8  */
9 
10 #include "smash/configuration.h"
11 
12 #include <cstdio>
13 #include <filesystem>
14 #include <fstream>
15 #include <numeric>
16 #include <string>
17 #include <vector>
18 
19 #include "yaml-cpp/yaml.h"
20 
22 #include "smash/input_keys.h"
23 #include "smash/inputfunctions.h"
24 #include "smash/logging.h"
25 #include "smash/stringfunctions.h"
26 
27 namespace smash {
28 static constexpr int LConfiguration = LogArea::Configuration::id;
29 
30 // internal helper functions
31 namespace {
32 /**
33  * Reset the passed in node to that one at the provided key, which is expected
34  * to exist in the node. If this is not the case, the node is set to
35  * <tt>std::nullopt</tt>.
36  *
37  * \param[in,out] node An optional node to be reset.
38  * \param[in] key The key expected to exist in the given node.
39  */
40 void descend_one_existing_level(std::optional<YAML::Node> &node,
41  std::string_view key) {
42  if (node) {
43  for (const auto &section : node.value()) {
44  /* Two remarks:
45  1) The Node::operator[] creates an undefined node in the YAML tree if
46  the node corresponding to the passed key does not exist and hence
47  in this function, which descend one level which is expected to
48  exist, we need to use it only if we are sure the node exists.
49  2) Node::reset does what you might expect Node::operator= to do. But
50  operator= assigns a value to the node and so
51  node = node[key]
52  would lead to a further modification of the data structure and
53  this function would not be simply traversal. Note that node and
54  root_node_ point to the same memory and modification to the first
55  would affect the second, too. */
56  if (section.first.Scalar() == key) {
57  node.value().reset(node.value()[key]);
58  return;
59  }
60  }
61  node = std::nullopt;
62  }
63 }
64 
65 /**
66  * Remove all empty maps of a YAML::Node.
67  *
68  * \param[in] root YAML::Node that contains empty maps.
69  * \return YAML::Node from above without empty maps.
70  */
71 YAML::Node remove_empty_maps(YAML::Node root) {
72  if (root.IsMap()) {
73  std::vector<std::string> to_remove(root.size());
74  for (auto n : root) {
75  remove_empty_maps(n.second);
76  // If the node is an empty sequence, we do NOT remove it!
77  if (n.second.IsMap() && n.second.size() == 0) {
78  to_remove.emplace_back(n.first.Scalar());
79  }
80  }
81  for (const auto &key : to_remove) {
82  root.remove(key);
83  }
84  }
85  return root;
86 }
87 
88 /**
89  * Merge two YAML::Nodes
90  *
91  * \param[in] a YAML::Node into which b is merged.
92  * \param[in] b YAML::Node that is merged into a.
93  * \return YAML::Node which is the merge of a and b.
94  */
95 YAML::Node operator|=(YAML::Node a, const YAML::Node &b) {
96  if (b.IsMap()) {
97  for (auto n0 : b) {
98  a[n0.first.Scalar()] |= n0.second;
99  }
100  } else {
101  a = b;
102  }
103  return a;
104 }
105 
106 /**
107  * Build a string with a list of keys as specified in the code.
108  *
109  * @param keys The list of keys.
110  * @return A \c std::string with the desired result.
111  */
112 std::string join_quoted(KeyLabels keys) {
113  return std::accumulate(keys.begin(), keys.end(), std::string{"{"},
114  [](const std::string &ss, const std::string_view &s) {
115  return ss + ((ss.size() == 1) ? "\"" : ", \"") +
116  std::string{s} + // NOLINT(whitespace/braces)
117  "\"";
118  }) +
119  "}";
120 }
121 
122 } // unnamed namespace
123 
124 // Default constructor
125 Configuration::Configuration(const std::filesystem::path &path)
126  : Configuration(path, "config.yaml") {}
127 
128 // Constructor checking for validity of input
129 Configuration::Configuration(const std::filesystem::path &path,
130  const std::filesystem::path &filename) {
131  const auto file_path = path / filename;
132  if (!std::filesystem::exists(file_path)) {
133  throw FileDoesNotExist("The configuration file was expected at '" +
134  file_path.native() +
135  "', but the file does not exist.");
136  }
137  if (has_crlf_line_ending(read_all(std::ifstream((file_path))))) {
138  throw std::runtime_error(
139  "The configuration file has CR LF line endings. Please use LF "
140  "line endings.");
141  }
142  try {
143  root_node_ = YAML::LoadFile(file_path.native());
144  } catch (YAML::ParserException &e) {
145  if (e.msg == "illegal map value" || e.msg == "end of map not found") {
146  const auto line = std::to_string(e.mark.line + 1);
147  throw ParseError("YAML parse error at\n" + file_path.native() + ':' +
148  line + ": " + e.msg +
149  " (check that the indentation of map keys matches)");
150  }
151  throw;
152  }
153 }
154 
156  : root_node_(std::move(other.root_node_)),
157  uncaught_exceptions_(std::move(other.uncaught_exceptions_)),
158  existing_keys_already_taken_(
159  std::move(other.existing_keys_already_taken_)) {
160  other.root_node_.reset();
161  other.uncaught_exceptions_ = 0;
162  other.existing_keys_already_taken_.clear();
163 }
164 
166  // YAML does not offer != operator between nodes
167  if (!(root_node_ == other.root_node_)) {
168  root_node_ = std::move(other.root_node_);
169  uncaught_exceptions_ = std::move(other.uncaught_exceptions_);
171  std::move(other.existing_keys_already_taken_);
172  other.root_node_.reset();
173  other.uncaught_exceptions_ = 0;
174  other.existing_keys_already_taken_.clear();
175  }
176  return *this;
177 }
178 
179 Configuration::~Configuration() noexcept(false) {
180  // Make sure that stack unwinding is not taking place befor throwing
181  if (std::uncaught_exceptions() == uncaught_exceptions_) {
182  // In this scenario is fine to throw
183  if (root_node_.size() != 0) {
184  throw std::logic_error(
185  "Configuration object destroyed with unused keys:\n" + to_string());
186  }
187  }
188  /* If this destructor is called during stack unwinding, it is irrelevant
189  that the Configuration has not be completely parsed. */
190 }
191 
192 void Configuration::merge_yaml(const std::string &yaml) {
193  try {
194  root_node_ |= YAML::Load(yaml);
195  } catch (YAML::ParserException &e) {
196  if (e.msg == "illegal map value" || e.msg == "end of map not found") {
197  const auto line = std::to_string(e.mark.line + 1);
198  throw ParseError("YAML parse error in:\n" + yaml + "\nat line " + line +
199  ": " + e.msg +
200  " (check that the indentation of map keys matches)");
201  }
202  throw;
203  }
204 }
205 
206 std::vector<std::string> Configuration::list_upmost_nodes() {
207  std::vector<std::string> r;
208  r.reserve(root_node_.size());
209  for (auto i : root_node_) {
210  r.emplace_back(i.first.Scalar());
211  }
212  return r;
213 }
214 
216  assert(labels.size() > 0);
217  /* Here we want to descend the YAML tree but not all the way to the last key,
218  because we need the node associated to the previous to last key in order to
219  remove the taken key. */
220  auto last_key_it = labels.end() - 1;
221  auto previous_to_last_node =
222  find_existing_node({labels.begin(), last_key_it});
223  auto to_be_returned{previous_to_last_node};
224  descend_one_existing_level(to_be_returned, *last_key_it);
225  if (!previous_to_last_node || !to_be_returned) {
226  throw std::runtime_error(
227  "Private Configuration::take method called with not existing key: " +
228  join_quoted(labels) + ". This should not have happened.");
229  }
230  previous_to_last_node.value().remove(*last_key_it);
232  if (const KeyLabels key_labels{labels.begin(), labels.end()};
234  existing_keys_already_taken_.push_back(key_labels);
235  }
236  /* NOTE: The second argument in the returned statement to construct Value must
237  * point to a string that is outliving the function scope and it would be
238  * wrong to return e.g. something locally declared in the function. This is
239  * because that argument is underneath of type 'const char* const' and, then,
240  * if it was dangling after returning, it would be wrong to access it.
241  */
242  return {to_be_returned.value(), last_key_it->data()};
243 }
244 
246  auto found_node = find_existing_node({labels.begin(), labels.end()});
247  if (found_node) {
248  // The same remark about the take return value applies here.
249  return {found_node.value(), labels.back().data()};
250  } else {
251  throw std::runtime_error(
252  "Private Configuration::read method called with not existing key: " +
253  join_quoted(labels) + ". This should not have happened.");
254  }
255 }
256 
258  const std::string &key, KeyLabels section) {
259  auto found_node = find_existing_node({section.begin(), section.end()});
260  if (found_node) {
261  std::vector<std::string> to_remove{};
262  bool key_exists = false;
263  for (auto i : found_node.value()) {
264  if (i.first.Scalar() != key) {
265  to_remove.push_back(i.first.Scalar());
266  } else {
267  key_exists = true;
268  }
269  }
270  if (!key_exists) {
271  std::string section_string{" section "};
272  if (section.size() > 0) {
273  section_string += join_quoted({section.begin(), section.end()}) + " ";
274  } else {
275  section_string = " top-level" + section_string;
276  }
277  throw std::invalid_argument("Attempt to remove all keys in" +
278  section_string +
279  "except not existing one: \"" + key + "\"");
280  } else {
281  for (auto i : to_remove) {
282  found_node.value().remove(i);
283  }
284  }
285  } else {
286  throw std::invalid_argument(
287  "Attempt to remove entries in not existing section: " +
288  join_quoted({section.begin(), section.end()}));
289  }
290 }
291 
293  KeyLabels section, Configuration::GetEmpty empty_if_not_existing) {
294  // Same logic as in take method
295  assert(section.size() > 0);
296  auto last_key_it = section.end() - 1;
297  auto previous_to_section_node =
298  find_existing_node({section.begin(), last_key_it});
299  auto sub_conf_root_node{previous_to_section_node};
300  descend_one_existing_level(sub_conf_root_node, *last_key_it);
301  if (!previous_to_section_node || !sub_conf_root_node) {
302  if (empty_if_not_existing == Configuration::GetEmpty::Yes)
303  return Configuration(YAML::Node{});
304  else
305  throw std::runtime_error("Attempt to extract not existing section " +
306  join_quoted({section.begin(), section.end()}));
307  }
308  /* Here sub_conf_root_node cannot be a nullopt, since if it was the function
309  would have returned before and it cannot be that previous_to_section_node
310  is nullopt and sub_conf_root_node is not */
311  else if (sub_conf_root_node->IsNull() || // NOLINT[whitespace/newline]
312  (sub_conf_root_node->IsMap() && sub_conf_root_node->size() == 0)) {
313  // Here we put together the cases of a key without value or with
314  // an empty map {} as value (no need at the moment to distinguish)
315  throw std::runtime_error("Attempt to extract empty section " +
316  join_quoted({section.begin(), section.end()}));
317  } else if (sub_conf_root_node->IsMap() && sub_conf_root_node->size() != 0) {
318  Configuration sub_config{*sub_conf_root_node};
319  previous_to_section_node->remove(*last_key_it);
321  return sub_config;
322  } else { // sequence or scalar or any future new YAML type
323  throw std::runtime_error("Tried to extract configuration section at " +
324  join_quoted({section.begin(), section.end()}) +
325  " to get a key value. Use take instead!");
326  }
327 }
328 
330  KeyLabels section, Configuration::GetEmpty empty_if_not_existing) {
331  auto sub_configuration =
332  extract_sub_configuration(section, empty_if_not_existing);
333  sub_configuration.enclose_into_section(section);
334  return sub_configuration;
335 }
336 
338  /* If the configuration is empty, force its root node to be a map. This is not
339  done in the extract_sub_configuration method, which allows the possibility not
340  to have a map node at root in the returned object, but here we
341  know it will be a map. */
342  if (root_node_.size() == 0) {
343  root_node_ = YAML::Node(YAML::NodeType::Map);
344  }
345  /* Create a new configuration from an empty node adding there the nested-map
346  structure. Then add the old root node to it and reset it to the new root one.
347  Refer to the descend_one_existing_level comment to understand the usage of
348  YAML::Node::operator[] and reset methods. */
349  YAML::Node new_root_node{YAML::NodeType::Map};
350  auto last_node = new_root_node;
351  for (const auto &label : section) {
352  last_node[label] = YAML::Node(YAML::NodeType::Map);
353  last_node.reset(last_node[label]);
354  }
355  last_node = root_node_;
356  root_node_.reset(new_root_node);
357 }
358 
359 std::string Configuration::to_string() const {
360  std::stringstream s;
361  s << root_node_;
362  return s.str();
363 }
364 
365 std::optional<YAML::Node> Configuration::find_existing_node(
366  KeyLabels keys) const {
367  /* Here we do not assert(keys.size()>0) and allow to pass in an empty vector,
368  in which case the passed in YAML:Node is simply returned. This might happen
369  e.g. in the take or extract_sub_configuration methods if called with a
370  label of a key at top level of the configuration file. */
371  std::optional<YAML::Node> node{root_node_};
372  for (const auto &key : keys) {
373  descend_one_existing_level(node, key);
374  }
375  return node;
376 }
377 
379  KeyLabels keys) const {
380  assert(keys.size() > 0);
381  YAML::Node node{root_node_};
382  for (const auto &key : keys) {
383  // See comments in descend_one_existing_level function
384  node.reset(node[key]);
385  }
386  return node;
387 }
388 
389 // internal helper functions
390 namespace {
391 /**
392  * Implementation of the algorithm to translate a YAML tree into
393  * lists of labels, each identifying a key from the YAML root node.
394  *
395  * Since the level of nesting sections in a YAML input file is arbitrary, this
396  * is a typical task to be solved using recursion. The main idea here is to
397  * take advantage of YAML functionality and in particular of the possibility
398  * to iterate over trees and test for nature of a node (is it a Map or not?).
399  * Roughly speaking, from the tree top-level all upmost nodes are extracted
400  * and for each of them, recursively, the same procedure is done over and
401  * over again if they are maps. If a non-map node is found, i.e. a key value
402  * is found, then recursion ends and a new entry is added to \c list .
403  *
404  * @param[in] root_node The root YAML node to extract from.
405  * @param[inout] list The list of lists of labels to be filled.
406  * @param[inout] new_list_entry New list of labels in process to be filled
407  * during recursion.
408  */
409 void fill_list_of_labels_per_key_in_yaml_tree(const YAML::Node &root_node,
410  std::vector<KeyLabels> &list,
411  KeyLabels &new_list_entry) {
412  // Here sub_node is an iterator value, i.e. a key/value pair of nodes,
413  // not a single YAML node (that's how YAML library works)
414  for (const auto &sub_node : root_node) {
415  new_list_entry.push_back(sub_node.first.as<std::string_view>());
416  if (sub_node.second.IsMap())
417  fill_list_of_labels_per_key_in_yaml_tree(sub_node.second, list,
418  new_list_entry);
419  else
420  list.push_back(new_list_entry);
421  new_list_entry.pop_back();
422  }
423 }
424 
425 /**
426  * Create a list of lists of key labels present in the passed YAML node
427  * considered to be the root one of a YAML tree.
428  *
429  * Given a \c YAML::Node, for each key having a value, all labels to reach
430  * the given key from the passed node are collected and a \c std::vector
431  * containing them is built and inserted into the given list. This function
432  * is calling the actual implementation preparing auxiliary needed variables.
433  *
434  * @param[in] root_node The root node of the YAML tree to be considered.
435  *
436  * @return A \c std::vector<KeyLabels> containing the desired information.
437  */
438 auto get_list_of_labels_per_key_in_yaml_tree(const YAML::Node &root_node) {
439  std::vector<KeyLabels> list{};
440  KeyLabels aux{};
441  fill_list_of_labels_per_key_in_yaml_tree(root_node, list, aux);
442  return list;
443 }
444 
445 /**
446  * \brief A utility type to be specialized to check if a type is a \c std::map .
447  *
448  * \tparam T A generic template parameter.
449  */
450 template <typename T>
451 struct IsStdMap {
452  /**
453  * A boolean value to indicate whether \c T is a map or not. Here it is always
454  * \c false, because there is another template specialization that will select
455  * those cases where \c value is going to be \c true.
456  */
457  static constexpr bool value = false;
458 };
459 
460 /**
461  * \brief A specialization of \c IsStdMap<T> for cases where the
462  * boolean value should be set to \c true.
463  *
464  * \tparam MapKey A type to indicate map keys.
465  * \tparam MapValue A type to indicate map values.
466  */
467 template <typename MapKey, typename MapValue>
468 struct IsStdMap<std::map<MapKey, MapValue>> {
469  /**
470  * A boolean value to indicate whether \c T is a map or not. Here it is always
471  * \c true, because this is a template specialization for maps only.
472  */
473  static constexpr bool value = true;
474 };
475 
476 /**
477  * \brief Extract from the \c InputKeys database the labels of keys that
478  * have a \c std::map as type.
479  *
480  * \return A list of key labels.
481  */
483  std::vector<KeyLabels> labels_of_keys_taken_as_map{};
484  for (const auto &keys_variant : smash::InputKeys::all_keys()) {
485  std::visit(
486  [&labels_of_keys_taken_as_map](auto &&var) {
487  /*
488  * The following if checks if the SMASH input key has a map as value
489  * and it deserves some explanation about the type extraction:
490  *
491  * - arg -> object of type: std::cref(const Key<T>)
492  * - arg.get() -> object of type: const Key<T>&
493  * - decltype(arg.get()) -> type: const Key<T>&
494  * - std::decay_t<decltype(arg.get())>::type -> type: Key<T>
495  * - std::decay_t<decltype(arg.get())>::type::value -> type: T
496  */
497  if constexpr (IsStdMap<typename std::decay_t<
498  decltype(var.get())>::type>::value)
499  labels_of_keys_taken_as_map.push_back(var.get().labels());
500  },
501  keys_variant);
502  }
503  return labels_of_keys_taken_as_map;
504 }
505 
506 /**
507  * \brief Remove last labels of keys that are taken as maps in SMASH and remove
508  * duplicates from the resulting list.
509  *
510  * The keys that are taken as maps in SMASH are here collected using the
511  * database \c InputKeys and the list of keys contained in the configuration
512  * must be adjusted by hand. This is a corner case, since YAML nodes that are
513  * maps are **by definition** sections and cannot be distinguished from keys
514  * "with a map value" in the recursive process to create the list of key labels.
515  *
516  * \param[in,out] list_of_input_key_labels The list of key labels to adjust.
517  */
519  std::vector<KeyLabels> &list_of_input_key_labels) {
520  const std::vector<KeyLabels> labels_of_keys_taken_as_map =
522  for (const auto &labels : labels_of_keys_taken_as_map) {
523  std::for_each(list_of_input_key_labels.begin(),
524  list_of_input_key_labels.end(),
525  [&labels](KeyLabels &labels_of_input_key) {
526  if (std::equal(labels.begin(), labels.end(),
527  labels_of_input_key.begin(),
528  labels_of_input_key.begin() + labels.size()))
529  labels_of_input_key = labels;
530  });
531  }
532  // The identical keys in list are now next to each other and we do
533  // not need/want to sort the list before calling std::unique.
534  list_of_input_key_labels.erase(std::unique(list_of_input_key_labels.begin(),
535  list_of_input_key_labels.end()),
536  list_of_input_key_labels.end());
537 }
538 
539 /**
540  * Given some YAML labels (assumed to be in order from the top section),
541  * it is checked whether any valid SMASH key with the same key exists.
542  *
543  * All possible checks are done in a way such that the user is informed about
544  * - if the key has never been valid;
545  * - if the key was valid in the past but it has been removed;
546  * - if the key is valid but deprecated.
547  *
548  * \param[in] labels The series of labels identifying the key.
549  *
550  * \return \c Configuration::Is::Valid if the key is valid;
551  * \return \c Configuration::Is::Deprecated if the key is Deprecated and
552  * \return \c Configuration::Is::Invalid if the key is invalid.
553  */
555  const auto &list = InputKeys::all_keys();
556  auto key_ref_var_it =
557  std::find_if(list.begin(), list.end(), [&labels](auto key) {
558  return std::visit(
559  [&labels](auto &&arg) { return arg.get().has_same_labels(labels); },
560  key);
561  });
562  if (key_ref_var_it == list.end()) {
563  logg[LConfiguration].error("Key ", smash::quote(smash::join(labels, ": ")),
564  " is not a valid SMASH input key.");
566  }
567 
568  smash::InputKeys::key_references_variant found_variant = *key_ref_var_it;
569  const auto key_labels =
570  std::visit([](auto &&var) { return static_cast<std::string>(var.get()); },
571  found_variant);
572 
573  if (std::visit([](auto &&var) { return !var.get().is_allowed(); },
574  found_variant)) {
575  const auto v_removal = std::visit(
576  [](auto &&var) { return var.get().removed_in(); }, found_variant);
577  logg[LConfiguration].error("Key ", key_labels,
578  " has been removed in version ", v_removal,
579  " and it is not valid anymore.");
581  }
582  if (std::visit([](auto &&var) { return var.get().is_deprecated(); },
583  found_variant)) {
584  const auto v_deprecation = std::visit(
585  [](auto &&var) { return var.get().deprecated_in(); }, found_variant);
586  logg[LConfiguration].warn(
587  "Key ", key_labels, " has been deprecated in version ", v_deprecation);
589  } else {
590  logg[LConfiguration].debug("Key ", key_labels, " is valid!");
592  }
593 }
594 
595 /**
596  * \brief Utility function to accumulate validation results of keys.
597  *
598  * This is basically the logic needed to make a full validation of a
599  * configuration, considered that we have three possible states. It extend the
600  * logical AND between two boolean values.
601  *
602  * \param[in,out] result_so_far Status of the configuration so far to be
603  * combined with the new key state.
604  * \param[in] new_value New key state to be considered.
605  */
607  Configuration::Is new_value) {
608  switch (result_so_far) {
610  break;
612  if (new_value != Configuration::Is::Valid) {
613  result_so_far = new_value;
614  }
615  break;
617  result_so_far = new_value;
618  break;
619  }
620 }
621 
622 } // namespace
623 
624 Configuration::Is Configuration::validate(bool full_validation) const {
627  Is validation_result{Is::Valid};
628  for (const auto &key_labels : list) {
629  Is key_state = validate_key(key_labels);
630  if (full_validation) {
631  accumulate_validation(validation_result, key_state);
632  } else {
633  if (key_state != Is::Valid)
634  return key_state;
635  }
636  }
637  return validation_result;
638 }
639 
640 } // namespace smash
Proxy object to be used when taking or reading keys in the configuration.
Interface to the SMASH configuration files.
Is
Return type of Configuration::validate which conveys more information that simply a two-state boolean...
YAML::Node find_node_creating_it_if_not_existing(KeyLabels keys) const
Descend in and if needed modify the YAML tree from the given node using the provided keys.
void merge_yaml(const std::string &yaml)
Merge the configuration in yaml into the existing tree.
std::string to_string() const
Return a string of the current YAML tree.
Configuration extract_sub_configuration(KeyLabels section, Configuration::GetEmpty empty_if_not_existing=Configuration::GetEmpty::No)
Create a new configuration from a then-removed section of the present object.
T read(const Key< T > &key) const
Additional interface for SMASH to read configuration values without removing them.
Configuration(const std::filesystem::path &path)
Read config.yaml from the specified path.
std::optional< YAML::Node > find_existing_node(KeyLabels keys) const
Descend in the YAML tree from the given node using the provided keys.
void enclose_into_section(KeyLabels section)
Enclose the configuration into the given section.
YAML::Node root_node_
The general_config.yaml contents - fully parsed.
int uncaught_exceptions_
Counter to be able to optionally throw in destructor.
bool did_key_exist_and_was_it_already_taken(const KeyLabels &labels) const
Find out whether a key has been already taken.
Is validate(bool full_validation=true) const
Validate content of configuration in terms of YAML keys.
~Configuration() noexcept(false)
Destroy the object, optionally throwing if not all keys were taken.
GetEmpty
Flag to tune method(s) behavior such that it is descriptive from the caller side.
Configuration extract_complete_sub_configuration(KeyLabels section, Configuration::GetEmpty empty_if_not_existing=Configuration::GetEmpty::No)
Alternative method to extract a sub-configuration, which retains the labels from the top-level in the...
T take(const Key< T > &key)
The default interface for SMASH to read configuration values.
void remove_all_entries_in_section_but_one(const std::string &key, KeyLabels section={})
Remove all entries in the given section except for key.
std::vector< std::string > list_upmost_nodes()
Lists all YAML::Nodes from the configuration setup.
std::vector< KeyLabels > existing_keys_already_taken_
List of taken keys to throw on taking same key twice.
Configuration & operator=(const Configuration &)=delete
Prevent Configuration objects from being copy-assigned.
std::array< einhard::Logger<>, std::tuple_size< LogArea::AreaTuple >::value > & logg
An array that stores all pre-configured Logger objects.
Definition: logging.h:245
std::string join_quoted(KeyLabels keys)
Build a string with a list of keys as specified in the code.
auto collect_input_keys_taken_as_maps()
Extract from the InputKeys database the labels of keys that have a std::map as type.
void adjust_list_of_labels_dealing_with_keys_taken_as_maps(std::vector< KeyLabels > &list_of_input_key_labels)
Remove last labels of keys that are taken as maps in SMASH and remove duplicates from the resulting l...
void fill_list_of_labels_per_key_in_yaml_tree(const YAML::Node &root_node, std::vector< KeyLabels > &list, KeyLabels &new_list_entry)
Implementation of the algorithm to translate a YAML tree into lists of labels, each identifying a key...
YAML::Node operator|=(YAML::Node a, const YAML::Node &b)
Merge two YAML::Nodes.
void accumulate_validation(Configuration::Is &result_so_far, Configuration::Is new_value)
Utility function to accumulate validation results of keys.
Configuration::Is validate_key(const KeyLabels &labels)
Given some YAML labels (assumed to be in order from the top section), it is checked whether any valid...
void descend_one_existing_level(std::optional< YAML::Node > &node, std::string_view key)
Reset the passed in node to that one at the provided key, which is expected to exist in the node.
YAML::Node remove_empty_maps(YAML::Node root)
Remove all empty maps of a YAML::Node.
auto get_list_of_labels_per_key_in_yaml_tree(const YAML::Node &root_node)
Create a list of lists of key labels present in the passed YAML node considered to be the root one of...
constexpr int n
Neutron.
Definition: action.h:24
UnaryFunction for_each(Container &&c, UnaryFunction &&f)
Convenience wrapper for std::for_each that operates on a complete container.
Definition: algorithms.h:96
std::vector< std::string_view > KeyLabels
Descriptive alias for storing key labels, i.e.
Definition: key.h:46
std::string quote(const std::string &s)
Add quotes around string.
static constexpr int LConfiguration
std::string to_string(ThermodynamicQuantity quantity)
Convert a ThermodynamicQuantity enum value to its corresponding string.
Definition: stringify.cc:26
bool has_crlf_line_ending(const std::string in)
Check if a line in the string ends with \r\n.
std::string read_all(std::istream &&input)
Utility function to read a complete input stream (e.g.
std::string join(const std::vector< std::string > &v, std::string_view delim)
Join strings using delimiter.
Thrown if the file does not exist.
Thrown for YAML parse errors.
std::variant< std::reference_wrapper< const Key< bool > >, std::reference_wrapper< const Key< int > >, std::reference_wrapper< const Key< int64_t > >, std::reference_wrapper< const Key< double > >, std::reference_wrapper< const Key< std::string > >, std::reference_wrapper< const Key< std::array< int, 3 > >>, std::reference_wrapper< const Key< std::array< double, 2 > >>, std::reference_wrapper< const Key< std::array< double, 3 > >>, std::reference_wrapper< const Key< std::pair< double, double > >>, std::reference_wrapper< const Key< std::vector< double > >>, std::reference_wrapper< const Key< std::vector< std::string > >>, std::reference_wrapper< const Key< std::set< ThermodynamicQuantity > >>, std::reference_wrapper< const Key< std::map< PdgCode, int > >>, std::reference_wrapper< const Key< std::map< std::string, std::string > >>, std::reference_wrapper< const Key< einhard::LogLevel > >, std::reference_wrapper< const Key< BoxInitialCondition > >, std::reference_wrapper< const Key< CalculationFrame > >, std::reference_wrapper< const Key< CharmRescattering > >, std::reference_wrapper< const Key< CollisionCriterion > >, std::reference_wrapper< const Key< DensityType > >, std::reference_wrapper< const Key< DerivativesMode > >, std::reference_wrapper< const Key< ExpansionMode > >, std::reference_wrapper< const Key< FermiMotion > >, std::reference_wrapper< const Key< DileptonBremsPionFormFactor > >, std::reference_wrapper< const Key< FieldDerivativesMode > >, std::reference_wrapper< const Key< FluidizableProcessesBitSet > >, std::reference_wrapper< const Key< FluidizationType > >, std::reference_wrapper< const Key< MultiParticleReactionsBitSet > >, std::reference_wrapper< const Key< SpinInteractionType > >, std::reference_wrapper< const Key< NNbarTreatment > >, std::reference_wrapper< const Key< OutputOnlyFinal > >, std::reference_wrapper< const Key< PdgCode > >, std::reference_wrapper< const Key< PseudoResonance > >, std::reference_wrapper< const Key< ReactionsBitSet > >, std::reference_wrapper< const Key< RestFrameDensityDerivativesMode > >, std::reference_wrapper< const Key< Sampling > >, std::reference_wrapper< const Key< SmearingMode > >, std::reference_wrapper< const Key< SphereInitialCondition > >, std::reference_wrapper< const Key< ThermalizationAlgorithm > >, std::reference_wrapper< const Key< TimeStepMode > >, std::reference_wrapper< const Key< HardStringTransitionMode > >, std::reference_wrapper< const Key< TotalCrossSectionStrategy > >> key_references_variant
Alias for the type to be used in the list of keys.
Definition: input_keys.h:7754
static const std::vector< key_references_variant > & all_keys()
Get list of references to all existing SMASH keys.
Definition: input_keys.cc:32
A utility type to be specialized to check if a type is a std::map .