+template <class T> const char* TypedConfigurationElement<T>::get_type_name() // override
+{
+ return ConfigType<T>::type_name;
+}
+
+} // end of anonymous namespace
+
+// **** Config ****
+
+class Config {
+private:
+ // name -> ConfigElement:
+ std::map<std::string, std::unique_ptr<ConfigurationElement>, std::less<>> options;
+ // alias -> ConfigElement from options:
+ std::map<std::string, ConfigurationElement*, std::less<>> aliases;
+ bool warn_for_aliases = true;
+
+public:
+ Config();
+
+ // No copy:
+ Config(Config const&) = delete;
+ Config& operator=(Config const&) = delete;
+
+ ConfigurationElement& operator[](const std::string& name);
+ void alias(const std::string& realname, const std::string& aliasname);
+
+ template <class T, class... A> TypedConfigurationElement<T>* register_option(const std::string& name, A&&... a)
+ {
+ xbt_assert(options.find(name) == options.end(), "Refusing to register the config element '%s' twice.",
+ name.c_str());
+ auto* variable = new TypedConfigurationElement<T>(name, std::forward<A>(a)...);
+ XBT_DEBUG("Register cfg elm %s (%s) of type %s @%p in set %p)", name.c_str(), variable->get_description().c_str(),
+ variable->get_type_name(), variable, this);
+ options[name].reset(variable);
+ variable->update();
+ return variable;
+ }
+
+ void show_aliases() const;
+ void help() const;
+
+protected:
+ ConfigurationElement* get_dict_element(const std::string& name);
+};
+
+Config::Config()
+{
+ atexit(&sg_config_finalize);
+}
+
+inline ConfigurationElement* Config::get_dict_element(const std::string& name)
+{
+ if (auto opt = options.find(name); opt != options.end())
+ return opt->second.get();
+
+ if (auto als = aliases.find(name); als != aliases.end()) {
+ ConfigurationElement* res = als->second;
+ if (warn_for_aliases)
+ XBT_INFO("Option %s has been renamed to %s. Consider switching.", name.c_str(), res->get_key().c_str());
+ return res;
+ }
+
+ std::string msg = "Bad config key: " + name + "\n";
+ std::string kebab = name;
+ std::replace(begin(kebab), end(kebab), '_', '-'); // convert from snake_case to kebab-case
+ if (options.count(kebab) > 0)
+ msg += "Did you mean '" + kebab + "'?\n";
+ msg += "Existing config keys:\n";
+ for (auto const& [opt_name, opt] : options)
+ msg += " " + opt_name + ": (" + opt->get_type_name() + ")" + opt->get_string_value() + "\n";
+ throw std::out_of_range(msg);
+}
+
+inline ConfigurationElement& Config::operator[](const std::string& name)
+{
+ return *(get_dict_element(name));
+}
+
+void Config::alias(const std::string& realname, const std::string& aliasname)
+{
+ xbt_assert(aliases.find(aliasname) == aliases.end(), "Alias '%s' already.", aliasname.c_str());
+ ConfigurationElement* element = this->get_dict_element(realname);
+ xbt_assert(element, "Cannot define an alias to the non-existing option '%s'.", realname.c_str());
+ this->aliases.try_emplace(aliasname, element);
+}
+
+/** @brief Displays the declared aliases and their replacement */
+void Config::show_aliases() const
+{
+ for (auto const& [name, alias] : aliases)
+ XBT_HELP(" %-40s %s", name.c_str(), alias->get_key().c_str());
+}
+
+/** @brief Displays the declared options and their description */
+void Config::help() const
+{
+ for (auto const& [name, opt] : options) {
+ XBT_HELP(" %s: %s", name.c_str(), opt->get_description().c_str());
+ XBT_HELP(" Type: %s; Current value: %s", opt->get_type_name(), opt->get_string_value().c_str());
+ }
+}
+
+// ***** set_default *****
+
+template <class T> XBT_PUBLIC void set_default(const char* name, T value)
+{
+ (*simgrid_config)[name].set_default_value<T>(std::move(value));
+}
+
+template XBT_PUBLIC void set_default<int>(const char* name, int value);
+template XBT_PUBLIC void set_default<double>(const char* name, double value);
+template XBT_PUBLIC void set_default<bool>(const char* name, bool value);
+template XBT_PUBLIC void set_default<std::string>(const char* name, std::string value);
+
+bool is_default(const char* name)
+{
+ return (*simgrid_config)[name].is_default();
+}
+
+// ***** set_value *****
+
+template <class T> XBT_PUBLIC void set_value(const char* name, T value)
+{
+ (*simgrid_config)[name].set_value<T>(std::move(value));
+}
+
+template XBT_PUBLIC void set_value<int>(const char* name, int value);
+template XBT_PUBLIC void set_value<double>(const char* name, double value);
+template XBT_PUBLIC void set_value<bool>(const char* name, bool value);
+template XBT_PUBLIC void set_value<std::string>(const char* name, std::string value);
+
+void set_as_string(const char* name, const std::string& value)
+{
+ (*simgrid_config)[name].set_string_value(value.c_str());
+}
+
+void set_parse(const std::string& opt)
+{
+ std::string options(opt);
+ XBT_DEBUG("List to parse and set:'%s'", options.c_str());
+ while (not options.empty()) {
+ XBT_DEBUG("Still to parse and set: '%s'", options.c_str());
+
+ // skip separators
+ size_t pos = options.find_first_not_of(" \t\n,");
+ options.erase(0, pos);
+ // find option
+ pos = options.find_first_of(" \t\n,");
+ std::string name = options.substr(0, pos);
+ options.erase(0, pos);
+ XBT_DEBUG("parse now:'%s'; parse later:'%s'", name.c_str(), options.c_str());
+
+ if (name.empty())
+ continue;
+
+ pos = name.find(':');
+ xbt_assert(pos != std::string::npos, "Option '%s' badly formatted. Should be of the form 'name:value'",
+ name.c_str());
+
+ std::string val = name.substr(pos + 1);
+ name.erase(pos);
+
+ if (name.rfind("path", 0) != 0)
+ XBT_INFO("Configuration change: Set '%s' to '%s'", name.c_str(), val.c_str());
+
+ set_as_string(name.c_str(), val);
+ }
+}
+
+// ***** get_value *****
+
+template <class T> XBT_PUBLIC T const& get_value(const std::string& name)
+{
+ return (*simgrid_config)[name].get_value<T>();
+}
+
+template XBT_PUBLIC int const& get_value<int>(const std::string& name);
+template XBT_PUBLIC double const& get_value<double>(const std::string& name);
+template XBT_PUBLIC bool const& get_value<bool>(const std::string& name);
+template XBT_PUBLIC std::string const& get_value<std::string>(const std::string& name);
+
+// ***** alias *****
+
+void alias(const char* realname, std::initializer_list<const char*> aliases)
+{
+ for (auto const& aliasname : aliases)
+ simgrid_config->alias(realname, aliasname);
+}
+
+// ***** declare_flag *****
+
+template <class T>
+XBT_PUBLIC void declare_flag(const std::string& name, const std::string& description, T value,
+ std::function<void(const T&)> callback)
+{
+ if (simgrid_config == nullptr)
+ simgrid_config = new simgrid::config::Config();
+ simgrid_config->register_option<T>(name, description, std::move(value), std::move(callback));
+}
+
+template XBT_PUBLIC void declare_flag(const std::string& name, const std::string& description, int value,
+ std::function<void(int const&)> callback);
+template XBT_PUBLIC void declare_flag(const std::string& name, const std::string& description, double value,
+ std::function<void(double const&)> callback);
+template XBT_PUBLIC void declare_flag(const std::string& name, const std::string& description, bool value,
+ std::function<void(bool const&)> callback);
+template XBT_PUBLIC void declare_flag(const std::string& name, const std::string& description, std::string value,
+ std::function<void(std::string const&)> callback);
+
+void finalize()
+{
+ delete simgrid_config;
+ simgrid_config = nullptr;
+}
+
+void show_aliases()
+{
+ simgrid_config->show_aliases();
+}
+
+void help()
+{
+ simgrid_config->help();
+}
+} // namespace simgrid::config
+
+/*----[ Setting ]---------------------------------------------------------*/
+
+/** @brief Set an integer value to \a name within \a cfg
+ *
+ * @param key the name of the variable
+ * @param value the value of the variable
+ */
+void sg_cfg_set_int(const char* key, int value)
+{
+ (*simgrid_config)[key].set_value<int>(value);
+}
+
+/** @brief Set or add a double value to \a name within \a cfg
+ *
+ * @param key the name of the variable
+ * @param value the double to set
+ */
+void sg_cfg_set_double(const char* key, double value)
+{
+ (*simgrid_config)[key].set_value<double>(value);
+}
+
+/** @brief Set or add a string value to \a name within \a cfg
+ *
+ * @param key the name of the variable
+ * @param value the value to be added
+ *
+ */
+void sg_cfg_set_string(const char* key, const char* value)
+{
+ (*simgrid_config)[key].set_value<std::string>(value);
+}
+
+/** @brief Set or add a boolean value to \a name within \a cfg
+ *
+ * @param key the name of the variable
+ * @param value the value of the variable
+ */
+void sg_cfg_set_boolean(const char* key, const char* value)
+{
+ (*simgrid_config)[key].set_value<bool>(simgrid::config::parse_bool(value));
+}
+
+/*----[ Getting ]---------------------------------------------------------*/
+/** @brief Retrieve an integer value of a variable (get a warning if not uniq)
+ *
+ * @param key the name of the variable
+ *
+ * Returns the first value from the config set under the given name.
+ */
+int sg_cfg_get_int(const char* key)
+{
+ return (*simgrid_config)[key].get_value<int>();
+}
+
+/** @brief Retrieve a double value of a variable (get a warning if not uniq)
+ *
+ * @param key the name of the variable
+ *
+ * Returns the first value from the config set under the given name.
+ */
+double sg_cfg_get_double(const char* key)
+{
+ return (*simgrid_config)[key].get_value<double>();
+}
+
+/** @brief Retrieve a boolean value of a variable (get a warning if not uniq)
+ *
+ * @param key the name of the variable
+ *
+ * Returns the first value from the config set under the given name.
+ * If there is more than one value, it will issue a warning.
+ */
+int sg_cfg_get_boolean(const char* key)
+{
+ return (*simgrid_config)[key].get_value<bool>() ? 1 : 0;