Version: SMASH-3.3
key.h
Go to the documentation of this file.
1 /*
2  *
3  * Copyright (c) 2024-2025
4  * SMASH Team
5  *
6  * GNU General Public License (GPLv3 or later)
7  *
8  */
9 
10 #ifndef SRC_INCLUDE_SMASH_KEY_H_
11 #define SRC_INCLUDE_SMASH_KEY_H_
12 
13 #include <cassert>
14 #include <functional>
15 #include <optional>
16 #include <sstream>
17 #include <string>
18 #include <string_view>
19 #include <utility>
20 #include <vector>
21 
22 #include "logging.h"
23 #include "stringfunctions.h"
24 #include "traits.h"
25 
26 namespace smash {
27 
32 using Version = std::string;
33 
38 using KeyMetadata = std::initializer_list<std::string_view>;
39 
45 using KeyLabels = std::vector<std::string>;
46 
54 inline KeyLabels operator+(const KeyLabels& lhs, std::string_view rhs) {
55  if (lhs.empty()) {
56  return KeyLabels{std::string{rhs}};
57  } else {
58  KeyLabels result{lhs};
59  result.push_back(std::string{rhs});
60  return result;
61  }
62 }
63 
71 inline KeyLabels operator+(KeyLabels&& lhs, std::string_view rhs) {
72  if (lhs.empty()) {
73  return KeyLabels{std::string{rhs}};
74  } else {
75  lhs.push_back(std::string{rhs});
76  return std::move(lhs); // See https://stackoverflow.com/a/14857144
77  }
78 }
79 
83 enum class DefaultType {
85  Null,
87  Value,
89  Dependent
90 };
91 
92 namespace detail {
93 
100 template <typename T>
101 struct KeyTraits {
114  using validator_type = std::function<bool(const T&)>;
115 };
116 
134 template <typename T>
136  static const typename KeyTraits<T>::validator_type always_true =
137  [](const T&) noexcept { return true; };
138  return always_true;
139 }
140 
141 } // namespace detail
142 
157 template <typename default_type>
158 class Key {
159  static_assert(!std::is_const_v<default_type>);
160  static_assert(!std::is_volatile_v<default_type>);
161  static_assert(!std::is_reference_v<default_type>);
162  static_assert(!std::is_pointer_v<default_type>);
163  static_assert(!std::is_array_v<default_type>);
164  static_assert(!std::is_function_v<default_type>);
165  static_assert(!std::is_member_object_pointer_v<default_type>);
166  static_assert(!std::is_member_function_pointer_v<default_type>);
167 
168  public:
173  struct WrongNumberOfVersions : public std::runtime_error {
174  using std::runtime_error::runtime_error;
175  };
176 
182 
195  explicit Key(
196  const KeyLabels& labels, const KeyMetadata& versions,
197  validator_type validator = detail::get_default_validator<default_type>())
198  : Key{labels, Default<default_type>{}, versions, validator} {}
199 
214  Key(const KeyLabels& labels, default_type value, const KeyMetadata& versions,
215  validator_type validator = detail::get_default_validator<default_type>())
216  : Key{labels, Default<default_type>{value}, versions, validator} {}
217 
233  Key(const KeyLabels& labels, DefaultType type_of_default,
234  const KeyMetadata& versions,
235  validator_type validator = detail::get_default_validator<default_type>())
236  : Key{labels, Default<default_type>{type_of_default}, versions,
237  validator} {}
238 
242  using type = default_type;
243 
251  default_type default_value() const { return default_.value(); }
252 
259  bool has_dependent_default() const noexcept {
260  return default_.is_dependent();
261  }
262 
268  Version introduced_in() const noexcept { return introduced_in_; }
269 
277  Version deprecated_in() const { return deprecated_in_.value(); }
278 
286  Version removed_in() const { return removed_in_.value(); }
287 
293  bool is_deprecated() const noexcept { return deprecated_in_.has_value(); }
294 
300  bool is_allowed() const noexcept { return !removed_in_.has_value(); }
301 
316  bool validate(const default_type& value) const noexcept {
317  try {
318  return validator_(value);
319  } catch (...) {
320  logg[LogArea::Configuration::id].error(
321  "Validator of key " + static_cast<std::string>(*this) +
322  " threw an exception when validating key value.\nThis should not "
323  "happen. Considering value invalid.");
324  return false;
325  }
326  }
327 
336  bool has_same_labels(const KeyLabels& labels) const noexcept {
337  return std::equal(std::begin(labels_), std::end(labels_),
338  std::begin(labels), std::end(labels));
339  }
340 
347  explicit operator std::string() const noexcept {
348  return smash::quote(smash::join(labels_, ": "));
349  }
350 
362  std::string as_yaml([[maybe_unused]] std::optional<default_type> value =
363  std::nullopt) const noexcept {
364  std::stringstream value_as_string{};
365  if constexpr (is_writable_to_stream_v<std::stringstream, default_type>) {
366  if (value) {
367  value_as_string << *value;
368  } else if (default_.type_ == DefaultType::Value) {
369  value_as_string << default_value();
370  }
371  }
372  return as_yaml(value_as_string.str());
373  }
374 
385  std::string as_yaml(std::string value) const noexcept {
386  std::stringstream result{};
387  result << "{" << smash::join(labels_, ": {") << ": " << value
388  << smash::join(std::vector<std::string>(labels_.size(), "}"), "");
389  return result.str();
390  }
391 
397  const KeyLabels& labels() const { return labels_; }
398 
399  private:
420  template <typename T>
421  class Default {
422  public:
427  Default() : type_{DefaultType::Null} {}
433  explicit Default(T in) : value_{std::move(in)} {}
444  explicit Default(DefaultType type) : type_{type} {
445  if (type != DefaultType::Dependent) {
446  throw std::logic_error("Default constructor used with invalid type!");
447  }
448  }
449 
457  T value() const { return value_.value(); }
458 
466  bool is_dependent() const noexcept {
467  return type_ == DefaultType::Dependent;
468  }
469 
470  private:
472  DefaultType type_ = DefaultType::Value;
474  std::optional<T> value_ = std::nullopt;
475  // Make nested class friend of enclosing one. This class is anyhow an
476  // implementation detail and part of Key.
477  friend class Key<T>;
478  };
479 
490  Key(const KeyLabels& labels, Default<default_type> value,
491  const KeyMetadata& versions, validator_type validator)
492  : default_{std::move(value)},
493  labels_{labels.begin(), labels.end()},
494  validator_{std::move(validator)} {
495  /*
496  * The following switch statement is a compact way to initialize the
497  * three version member variables without repetition and lots of logic
498  * clauses. The versions variable can have 1, 2 or 3 entries. The use of
499  * the iterator is needed, since std::initializer_list has no access
500  * operator.
501  */
502  switch (auto it = versions.end(); versions.size()) {
503  case 3:
504  removed_in_ = *(--it);
505  [[fallthrough]];
506  case 2:
507  deprecated_in_ = *(--it);
508  [[fallthrough]];
509  case 1:
510  introduced_in_ = *(--it);
511  break;
512  default:
513  throw WrongNumberOfVersions(
514  "Key constructor needs one, two or three version numbers.");
515  }
516  /* Ensure validator_ is set, which is usually the case unless in scenarios
517  * that are particularly nasty to debug (e.g. calling this constructor from
518  * a static/global object using a static/global validator and hence hitting
519  * the undefined order of static initialisation).
520  *
521  * NOTE: Do NOT throw from here. For non-local static keys we prefer to
522  * assign the canonical default validator when an empty functor is provided;
523  * throwing during static initialization can cause termination or even
524  * undefined behavior.
525  */
526  if (!validator_) {
527  logg[LogArea::Configuration::id].error(
528  "Empty validator used at Key construction time.\nThis should not "
529  "happen. Using default validator instead.");
530  validator_ = detail::get_default_validator<default_type>();
531  }
532  if (default_.value_ && validator_(*(default_.value_)) == false) {
533  throw std::logic_error(
534  "Key " + static_cast<std::string>(*this) +
535  " has been declared with an invalid default value.");
536  }
537  }
538 
540  Version introduced_in_{};
542  std::optional<Version> deprecated_in_{};
544  std::optional<Version> removed_in_{};
548  KeyLabels labels_{};
550  validator_type validator_{detail::get_default_validator<default_type>()};
551 };
552 
553 } // namespace smash
554 
555 #endif // SRC_INCLUDE_SMASH_KEY_H_
Wrapper class around a type with the capability to both store the type of default and its value,...
Definition: key.h:421
T value() const
Retrieve the default value stored in the object.
Definition: key.h:457
Default(T in)
Construct a new Default object storing its default value.
Definition: key.h:433
Default()
Construct a new Default object which denotes a mandatory value without a default.
Definition: key.h:427
Default(DefaultType type)
Construct a new Default object which has a value dependent on external information.
Definition: key.h:444
bool is_dependent() const noexcept
Ask whether the default value depends on other external information.
Definition: key.h:466
Object to store a YAML input file key together with metadata associated to it.
Definition: key.h:158
bool has_dependent_default() const noexcept
Ask whether the default value depends on other other keys.
Definition: key.h:259
bool is_allowed() const noexcept
Get whether the key is still allowed or not.
Definition: key.h:300
bool has_same_labels(const KeyLabels &labels) const noexcept
Check if given labels are the same as those of this object.
Definition: key.h:336
std::string as_yaml(std::string value) const noexcept
Overload of the method taking a string as value.
Definition: key.h:385
Version deprecated_in() const
Get the SMASH version in which the key has been deprecated.
Definition: key.h:277
bool validate(const default_type &value) const noexcept
Get whether the given key value is valid.
Definition: key.h:316
Key(const KeyLabels &labels, Default< default_type > value, const KeyMetadata &versions, validator_type validator)
Private constructor of the Key object.
Definition: key.h:490
Key(const KeyLabels &labels, const KeyMetadata &versions, validator_type validator=detail::get_default_validator< default_type >())
Construct a new Key object without default value.
Definition: key.h:195
Version removed_in() const
Get the SMASH version in which the key has been removed.
Definition: key.h:286
bool is_deprecated() const noexcept
Get whether the key is deprecated or not.
Definition: key.h:293
default_type default_value() const
Get the default value of the key.
Definition: key.h:251
const KeyLabels & labels() const
Method to access the Key labels.
Definition: key.h:397
Key(const KeyLabels &labels, default_type value, const KeyMetadata &versions, validator_type validator=detail::get_default_validator< default_type >())
Construct a new Key object with default value.
Definition: key.h:214
std::string as_yaml([[maybe_unused]] std::optional< default_type > value=std::nullopt) const noexcept
Build and return a YAML-formatted string in the compact form (using braces as single line).
Definition: key.h:362
default_type type
Let the clients of this class have access to the key type.
Definition: key.h:242
Key(const KeyLabels &labels, DefaultType type_of_default, const KeyMetadata &versions, validator_type validator=detail::get_default_validator< default_type >())
Construct a new Key object which is supposed to have a default value, which however depends on other ...
Definition: key.h:233
typename detail::KeyTraits< default_type >::validator_type validator_type
Definition: key.h:181
Version introduced_in() const noexcept
Get the SMASH version in which the key has been introduced.
Definition: key.h:268
std::array< einhard::Logger<>, std::tuple_size< LogArea::AreaTuple >::value > logg
An array that stores all pre-configured Logger objects.
Definition: logging.cc:40
const KeyTraits< T >::validator_type & get_default_validator() noexcept
Function template to get a default trivial validator.
Definition: key.h:135
Definition: action.h:24
std::vector< std::string > KeyLabels
Descriptive alias for storing key labels, i.e.
Definition: key.h:45
DefaultType
New type to explicit distinguish between mandatory and optional keys.
Definition: key.h:83
@ Dependent
Default value which depends on other keys
@ Value
Normal default with a value associated to it.
@ Null
Default "type" for mandatory keys
std::string quote(const std::string &s)
Add quotes around string.
std::string join(const std::vector< std::string > &v, const std::string &delim)
Join strings using delimiter.
std::initializer_list< std::string_view > KeyMetadata
Descriptive alias for storing keys metadata.
Definition: key.h:38
EnergyMomentumTensor operator+(EnergyMomentumTensor a, const EnergyMomentumTensor &b)
Direct addition operator.
std::string Version
Descriptive alias for storing a SMASH version associated to keys metadata.
Definition: key.h:32
Thrown when too few or too many versions are passed to the constructor.
Definition: key.h:173
Class template to store Key traits outside the Key class, allowing for reuse both in the Key class it...
Definition: key.h:101
std::function< bool(const T &)> validator_type
Descriptive alias for the key validator.
Definition: key.h:114