roadrunner  2.6.0
Fast simulator for SBML models
Setting.h
1 //
2 // Created by Ciaran on 30/04/2021.
3 //
4 
5 #ifndef ROADRUNNER_SETTING_H
6 #define ROADRUNNER_SETTING_H
7 
8 #include <variant>
9 #include <iostream>
10 #include <vector>
11 #include <sstream>
12 #include <limits>
13 #include <type_traits>
14 #include <functional>
15 #include <typeinfo>
16 #include "setting_t.h"
17 #include "rrException.h"
18 
19 namespace rr {
20 
21 
78  class Setting {
79  public:
80 
86  explicit Setting(setting_t value);
87 
97  template<class T>
98  Setting(T settingValue) :
99  value_(setting_t(settingValue)) {};
100 
109  Setting(const char *settingValue);
110 
116  explicit Setting(std::int64_t settingValue);
117 
118 
125  Setting() = default;
126 
132  enum TypeId {
133  EMPTY = 0, // std::monostate. Empty is default when variant instantiated with nothing
134  STRING = 1,
135  BOOL = 2,
136  INT32 = 3,
137  UINT32 = 4,
138  INT64 = 5,
139  UINT64 = 6,
140  FLOAT = 7,
141  DOUBLE = 8,
142  CHAR = 9,
143  UCHAR = 10,
144  DOUBLEVECTOR = 11,
145  };
146 
155  TypeId type() const;
156 
162  template<class T>
163  bool isType() {
164  return std::holds_alternative<T>(value_);
165  }
166 
171  template<class T>
172  static bool isValidType() {
174  }
175 
182  template<class T>
183  T *get_if() {
184  checkValidType<T>();
185  return std::get_if<T>(&value_);
186  }
187 
196  template<class SettingType>
197  SettingType get() {
198  return std::get<SettingType>(value_);
199  }
200 
212  template<class As>
213  As getAs() const{
214  const std::type_info &inf = typeInfo();
215  return visit([&](auto &&val) {
216  if constexpr (std::is_convertible_v<decltype(val), As>) {
217  std::ostringstream os;
218  os << "Cannot retrieve setting value: you have requested the value as a ";
219  os << "\"" << typeid(As).name() << "\", but the value of the setting is ";
220  std::ostringstream oss_val;
221 
222  // if we have some form of negative signed integer
223  bool isNegInt = false;
224  uint64_t uint64val = 0;
225  int64_t int64val = 0;
226  if (auto intValue = std::get_if<int>(&value_))
227  {
228  if (*intValue < 0)
229  {
230  isNegInt = true;
231  int64val = *intValue;
232  }
233  uint64val = *intValue;
234  oss_val << "\"" << *intValue << "\", which is ";
235  }
236  else if (auto intValue = std::get_if<int64_t>(&value_))
237  {
238  if (*intValue < 0)
239  {
240  isNegInt = true;
241  int64val = *intValue;
242  }
243  oss_val << "\"" << *intValue << "\", which is ";
244  uint64val = *intValue;
245  }
246  else if (auto intValue = std::get_if<unsigned int>(&value_))
247  {
248  oss_val << "\"" << *intValue << "\", which is ";
249  uint64val = *intValue;
250  }
251  else if (auto intValue = std::get_if<uint64_t>(&value_))
252  {
253  oss_val << "\"" << *intValue << "\", which is ";
254  uint64val = *intValue;
255  }
256  if (isNegInt)
257  {
258  // we cannot convert to an unsigned type
259  if (typeid(As) == typeid(unsigned int) || typeid(As) == typeid(unsigned long)) {
260  // would prefer to throw std::bad_variant_access, but it seems
261  // it does not have the appropriate constructor (?)
262  os << oss_val.str() << "negative." << std::endl;
263  throw std::invalid_argument(os.str());
264  return As{}; // oddly enough, this *is* necessary
265  }
266  }
267  // if we have a double, with value greater than std::numeric_limits<float>::max
268  // and we try to convert to float, we have an error
269  if (auto lValue = std::get_if<float>(&value_)) {
270  if (*lValue > ((std::numeric_limits<float>::max)())) {
271  os << "\"" << *lValue << "\", which is too large." << std::endl;
272  throw std::invalid_argument(os.str());
273  return As{}; // must be present
274  }
275  }
276  //Now check if the value is larger than the max value for the type:
277  if (typeid(As) == typeid(int) &&
278  (uint64val > ((std::int64_t)(std::numeric_limits<int>::max)()) && !isNegInt) ||
279  (isNegInt && int64val < ((std::int64_t)(std::numeric_limits<int>::min)())))
280  {
281  os << oss_val.str() << "too large." << std::endl;
282  throw std::invalid_argument(os.str());
283  return As{}; // must be present
284  }
285  else if (typeid(As) == typeid(unsigned int) &&
286  uint64val > ((std::int64_t)(std::numeric_limits<unsigned int>::max)()))
287  {
288  os << oss_val.str() << "too large." << std::endl;
289  throw std::invalid_argument(os.str());
290  return As{}; // must be present
291  }
292  else if (typeid(As) == typeid(int64_t) &&
293  (uint64val > ((std::int64_t)(std::numeric_limits<int64_t>::max)()) && !isNegInt))
294  {
295  os << oss_val.str() << "too large." << std::endl;
296  throw std::invalid_argument(os.str());
297  return As{}; // must be present
298  }
299  return As(val);
300  } else {
301  std::ostringstream os;
302  os << "Setting::getAs:TypeError. You have requested the conversion "
303  "of a \"" << typeid(decltype(val)).name() << "\" to a ";
304  os << "\"" << typeid(As).name() << "\" but this Setting contains ";
305  os << "a \"" << inf.name() << "\". Note, see Setting::toString() "
306  "for string representation." << std::endl;
307  // would prefer to throw std::bad_variant_access, but it seems
308  // it does not have the appropriate constructor (?)
309  throw std::invalid_argument(os.str());
310  return As{}; // oddly enough, this *is* necessary
311  }
312  });
313  }
314 
320  template<class SettingType>
321  SettingType get() const {
322  return std::get<SettingType>(value_);
323  }
324 
342  template<typename T>
343  operator T() const {
344  return std::visit(
345  [&](auto const &val) {
346  if constexpr (std::is_convertible_v<decltype(val), T>) {
347  // if all is good, we construct a object of type T and return it.
348  return getAs<T>();
349  } else {
350  // T is not supported conversion type from typeid(value_)
351  throw std::bad_variant_access{};
352  return T{}; // oddly enough, this *is* necessary
353  }
354  }, value_);
355  }
356 
369  operator std::vector<double>() {
370  if (auto vec = get_if<std::vector<double>>()) {
371  return *vec;
372  } else {
373  throw std::invalid_argument("Setting::operator std::vector<double>: "
374  "could not cast Setting to double vector because the "
375  "type contained in this Setting is not a double vector");
376  }
377  }
378 
379 
399  template<typename T, class = typename std::enable_if<isValidVariantType<T, setting_t>::value>::type>
400  bool operator==(const T &otherSetting) {
401  if (auto settingValue = get_if<T>()) {
402  return *settingValue == otherSetting;
403  }
404  return false;
405  }
406 
411  template<typename T,
412  class = typename std::enable_if<isValidVariantType<T, setting_t>::value>::type>
413  bool operator!=(const T &setting) {
414  return !(*this == setting);
415  }
416 
421  bool operator==(const Setting &setting);
422 
430  bool operator==(const char *setting);
431 
439  bool operator!=(const char *setting);
440 
445  bool operator!=(const Setting &setting);
446 
447 
453  template<typename T, class = typename std::enable_if<isValidVariantType<T, setting_t>::value>::type>
454  Setting &operator=(const T &setting) {
455  checkValidType<T>();
456  // no need to check self assignment with variant
457  value_ = setting_t(setting);
458  return *this;
459  }
460 
466  template<typename T, class = typename std::enable_if<isValidVariantType<T, setting_t>::value>::type>
467  Setting &operator=(T &&setting) noexcept {
468  checkValidType<T>();
469  // no need to check self assignment with variant
470  value_ = std::move(setting_t(setting));
471  return *this;
472  }
473 
489  template<class Func>
490  decltype(auto) visit(Func function) const {
491  return std::visit(function, value_);
492  }
493 
499  const std::type_info &typeInfo() const;
500 
501 
507  Setting &operator=(const Setting &setting);
508 
513  Setting &operator=(Setting &&setting) noexcept;
514 
519  Setting(const Setting &setting);
520 
525  Setting(Setting &&setting) noexcept;
526 
531  [[nodiscard]] const setting_t &getValue() const;
532 
537  [[nodiscard]] std::string pythonRepr() const;
538 
542  [[nodiscard]] bool isString() const;
543 
547  [[nodiscard]] bool isInteger() const;
548 
552  [[nodiscard]] bool isNumeric() const;
553 
557  [[nodiscard]] bool isBool() const;
558 
562  bool isEmpty() const;
563 
567  bool isSigned() const;
568 
572  bool isDoubleVector() const;
573 
577  bool isDouble() const;
578 
589  static Setting parse(std::string &val);
590 
595  std::string toString();
596 
597  private:
598 
599  setting_t value_;
600 
601 
607  template<class T>
608  void checkValidType() {
609  if (!Setting::isValidType<T>()) {
610  std::ostringstream err;
611  err << "Setting does not support ";
612  err << "type \"" << typeid(T).name() << "\"";
613  throw std::invalid_argument(err.str());
614  }
615  }
616  };
617 
618 }
619 #endif //ROADRUNNER_SETTING_H
Store a roadrunner option (or setting) as a Variant type.
Definition: Setting.h:78
std::string toString()
gets a string representation of the type stored in this Setting.
Definition: Setting.cpp:161
Setting()=default
default constructor.
bool isDoubleVector() const
true if this is a std::vector of doubles
Definition: Setting.cpp:50
T * get_if()
get the value of this Setting as type T if the value in this Setting is of type T.
Definition: Setting.h:183
TypeId type() const
returns the type of std::variant contained within this Setting.
Definition: Setting.cpp:22
bool isString() const
is this variant a std::string.
Definition: Setting.cpp:26
bool isDouble() const
true if this is a double
Definition: Setting.cpp:54
bool isEmpty() const
true if empty.
Definition: Setting.cpp:42
const std::type_info & typeInfo() const
returns the std::type_info for the object type contained within this Setting.
Definition: Setting.cpp:139
SettingType get()
return the value held by this Setting as a type SettingType.
Definition: Setting.h:197
const setting_t & getValue() const
getter for the std::variant_t underlying this Setting.
Definition: Setting.cpp:71
As getAs() const
explicitly convert this Setting to type As.
Definition: Setting.h:213
bool isType()
determines whether the current value of Setting is of type T.
Definition: Setting.h:163
static bool isValidType()
test for membership of type T in setting_t, aka supported types
Definition: Setting.h:172
decltype(auto) visit(Func function) const
equality operator for comparing object otherSetting of type T against the Setting contained within th...
Definition: Setting.h:490
bool isNumeric() const
is this a numeric type.
Definition: Setting.cpp:34
bool isSigned() const
true if this is a signed number.
Definition: Setting.cpp:46
std::string pythonRepr() const
Convert to Python-compatible representation.
Definition: Setting.cpp:58
Setting(T settingValue)
constructor for creating a Setting from a supperted type T.
Definition: Setting.h:98
static Setting parse(std::string &val)
Parses the std::string which must be in JSON format.
Definition: Setting.cpp:75
bool isInteger() const
was an integer stored here.
Definition: Setting.cpp:30
Setting & operator=(const Setting &setting)
move assignment operator for when rhs is a Setting.
Definition: Setting.cpp:119
TypeId
types that correspond to the index of the position of the type in the variant template.
Definition: Setting.h:132
bool isBool() const
is this a boolean type.
Definition: Setting.cpp:38
SettingType get() const
return the value held by this setting as type SettingType.
Definition: Setting.h:321
Generic type checking mechanism for membership of type T in variant ALL_T.
Definition: setting_t.h:38