+const char* true_values[] = {
+ "yes", "on", "true", "1"
+};
+const char* false_values[] = {
+ "no", "off", "false", "0"
+};
+
+static bool parse_bool(const char* value)
+{
+ for (const char* const& true_value : true_values)
+ if (std::strcmp(true_value, value) == 0)
+ return true;
+ for (const char* const& false_value : false_values)
+ if (std::strcmp(false_value, value) == 0)
+ return false;
+ throw std::range_error("not a boolean");
+}
+
+static double parse_double(const char* value)
+{
+ char* end;
+ errno = 0;
+ double res = std::strtod(value, &end);
+ if (errno == ERANGE)
+ throw std::range_error("out of range");
+ else if (errno)
+ xbt_die("Unexpected errno");
+ if (end == value || *end != '\0')
+ throw std::range_error("invalid double");
+ else
+ return res;
+}
+
+static long int parse_long(const char* value)
+{
+ char* end;
+ errno = 0;
+ long int res = std::strtol(value, &end, 0);
+ if (errno) {
+ if (res == LONG_MIN && errno == ERANGE)
+ throw std::range_error("underflow");
+ else if (res == LONG_MAX && errno == ERANGE)
+ throw std::range_error("overflow");
+ xbt_die("Unexpected errno");
+ }
+ if (end == value || *end != '\0')
+ throw std::range_error("invalid integer");
+ else
+ return res;
+}
+
+// ***** ConfigType *****
+
+/// A trait which define possible options types:
+template <class T> class ConfigType;
+
+template <> class ConfigType<int> {
+public:
+ static constexpr const char* type_name = "int";
+ static inline double parse(const char* value)
+ {
+ return parse_long(value);
+ }
+};
+template <> class ConfigType<double> {
+public:
+ static constexpr const char* type_name = "double";
+ static inline double parse(const char* value)
+ {
+ return parse_double(value);
+ }
+};
+template <> class ConfigType<std::string> {
+public:
+ static constexpr const char* type_name = "string";
+ static inline std::string parse(const char* value)
+ {
+ return std::string(value);
+ }
+};
+template <> class ConfigType<bool> {
+public:
+ static constexpr const char* type_name = "boolean";
+ static inline bool parse(const char* value)
+ {
+ return parse_bool(value);
+ }
+};
+
+// **** Forward declarations ****
+
+class ConfigurationElement ;
+template<class T> class TypedConfigurationElement;
+
+// **** ConfigurationElement ****
+
+class ConfigurationElement {
+private:
+ std::string key;
+ std::string desc;
+ bool isdefault = true;
+
+public:
+ /* Callback */
+ xbt_cfg_cb_t old_callback = nullptr;
+
+ ConfigurationElement(const char* key, const char* desc) : key(key ? key : ""), desc(desc ? desc : "") {}
+ ConfigurationElement(const char* key, const char* desc, xbt_cfg_cb_t cb)
+ : key(key ? key : ""), desc(desc ? desc : ""), old_callback(cb) {}
+
+ virtual ~ConfigurationElement() = default;
+
+ virtual std::string get_string_value() = 0;
+ virtual void set_string_value(const char* value) = 0;
+ virtual const char* get_type_name() = 0;
+
+ template <class T> T const& get_value() const
+ {
+ return dynamic_cast<const TypedConfigurationElement<T>&>(*this).get_value();
+ }
+ template <class T> void set_value(T value)
+ {
+ dynamic_cast<TypedConfigurationElement<T>&>(*this).set_value(std::move(value));
+ }
+ template <class T> void set_default_value(T value)
+ {
+ dynamic_cast<TypedConfigurationElement<T>&>(*this).set_default_value(std::move(value));
+ }
+ void unset_default() { isdefault = false; }
+ bool is_default() const { return isdefault; }
+
+ std::string const& get_description() const { return desc; }
+ std::string const& get_key() const { return key; }
+};
+
+// **** TypedConfigurationElement<T> ****
+
+// TODO, could we use boost::any with some Type* reference?
+template<class T>
+class TypedConfigurationElement : public ConfigurationElement {
+private:
+ T content;
+ std::function<void(T&)> callback;
+
+public:
+ TypedConfigurationElement(const char* key, const char* desc, T value = T())
+ : ConfigurationElement(key, desc), content(std::move(value))
+ {}
+ TypedConfigurationElement(const char* key, const char* desc, T value, xbt_cfg_cb_t cb)
+ : ConfigurationElement(key, desc, cb), content(std::move(value))
+ {}
+ TypedConfigurationElement(const char* key, const char* desc, T value, std::function<void(T&)> callback)
+ : ConfigurationElement(key, desc), content(std::move(value)), callback(std::move(callback))
+ {}
+ ~TypedConfigurationElement() = default;
+
+ std::string get_string_value() override;
+ const char* get_type_name() override;
+ void set_string_value(const char* value) override;
+
+ void update()
+ {
+ if (old_callback)
+ this->old_callback(get_key().c_str());
+ if (this->callback)
+ this->callback(this->content);
+ }
+
+ T const& get_value() const { return content; }
+
+ void set_value(T value)
+ {
+ this->content = std::move(value);
+ this->update();
+ this->unset_default();
+ }
+
+ void set_default_value(T value)
+ {
+ if (this->is_default()) {
+ this->content = std::move(value);
+ this->update();
+ } else {
+ XBT_DEBUG("Do not override configuration variable '%s' with value '%s' because it was already set.",
+ get_key().c_str(), to_string(value).c_str());
+ }
+ }
+};
+
+template <class T> std::string TypedConfigurationElement<T>::get_string_value() // override
+{
+ return to_string(content);
+}
+
+template <class T> void TypedConfigurationElement<T>::set_string_value(const char* value) // override