//! @file AnyMap.h #ifndef CT_ANYMAP_H #define CT_ANYMAP_H #include "cantera/base/ct_defs.h" #include "cantera/base/global.h" #include "cantera/base/ctexceptions.h" #include #include #include #include #include namespace boost { class any; } namespace Cantera { class AnyMap; //! A wrapper for a variable whose type is determined at runtime /*! * Instances of AnyValue are used as values in an AnyMap. Values are converted * to a concrete type using the templated as() method or convenience methods * such as asString() and asDouble(). See AnyMap for usage examples. * * Elements are set using assignment, and the assignment operator has been * overloaded for specific types so that only those types are allowed to be * used in an AnyValue. */ class AnyValue { public: AnyValue(); ~AnyValue(); AnyValue(AnyValue const& other); AnyValue(AnyValue&& other); AnyValue& operator=(AnyValue const& other); AnyValue& operator=(AnyValue&& other); AnyValue& operator[](const std::string& key); bool hasKey(const std::string& key) const; // The value knows the name of its corresponding key in order to provide // comprehensible error messages. void setKey(const std::string& key); template const T& as() const; template T& as(); const std::type_info& type(); template bool is() const; explicit AnyValue(const std::string& value); AnyValue& operator=(const std::string& value); AnyValue& operator=(const char* value); const std::string& asString() const; explicit AnyValue(double value); AnyValue& operator=(double value); double& asDouble(); const double& asDouble() const; explicit AnyValue(bool value); AnyValue& operator=(bool value); bool& asBool(); const bool& asBool() const; explicit AnyValue(long int value); AnyValue& operator=(long int value); AnyValue& operator=(int value); long int& asInt(); const long int& asInt() const; template AnyValue& operator=(const std::vector& value); template const std::vector& asVector() const; template std::vector& asVector(); AnyValue& operator=(const AnyMap& value); AnyValue& operator=(AnyMap&& value); template AnyValue& operator=(const std::unordered_map items); template AnyValue& operator=(const std::map items); template std::map asMap() const; private: std::string demangle(const std::type_info& type) const; std::string m_key; std::unique_ptr m_value; static std::map s_typenames; }; // Implicit conversion to vector template<> const std::vector& AnyValue::asVector() const; template<> std::vector& AnyValue::asVector(); // Implicit conversion of long int to double if accessed as a vector template<> const std::vector& AnyValue::asVector() const; template<> std::vector& AnyValue::asVector(); // Implicit conversion of long int to double if accessed as a vector> template<> const std::vector& AnyValue::asVector() const; template<> std::vector& AnyValue::asVector(); //! A map of string keys to values whose type can vary at runtime /*! * Values in an AnyMap are held by instances of AnyValue. Instances of AnyMap * can be nested to form a tree. * * ## Setting elements * * ``` * AnyMap breakfast; * breakfast["spam"] = 123.4; // Creates a value of type 'double' * breakfast["eggs"] = "scrambled"; // Creates a value of type 'std::string' * * // Create a nested AnyMap named "beans" which has a key named "baked" * // whose value is a vector * std::vector v{3.14, 1.59, 2.65}; * breakfast["beans/baked"] = v; * // Equivalently: * breakfast["beans"]["baked"] = v; * * // Create a nested AnyMap with values of the same type * std::map breads{{"wheat", 4.0}, {"white", 2.5}}; * breakfast["toast"] = breads; * // Equivalent to: * breakfast["toast"]["wheat"] = 4.0 * breakfast["toast"]["white"] = 2.5 * ``` * * ## Accessing elements * * ``` * double val1 = breakfast["spam"].asDouble(); * std::string val2 = breakfast["eggs"].asString(); * vector_fp val3 = breakfast["beans"]["baked"].asVector(); * * std::map = breakfast["toast"].asMap(); * ``` * * ## Checking for elements * * ``` * try { * breakfast["waffle"].asDouble(); * } except (std::exception& err) { * // Exception will be thrown. * // 'breakfast' will have an empty key named "waffle" * } * * try { * breakfast.at("grits").asDouble(); * } except (std::exception& err) { * // Exception will be thrown and no new key will be added * } * * if (breakfast.hasKey("grits")) { * // do something with this entry * } * ``` * * ## Checking element types * * ``` * if (breakfast["sausage"].is>()) { * // access using asVector * } else if (breakfast["sausage"].type() == typeid(vector)) { * // access using asVector * } * ``` */ class AnyMap { public: AnyMap() {}; //! Create an AnyMap from a YAML file. /*! * Searches the Cantera include path for the file. */ static AnyMap fromYamlFile(const std::string& name); //! Create an AnyMap from a string containing a YAML document static AnyMap fromYamlString(const std::string& yaml); AnyValue& operator[](const std::string& key); const AnyValue& at(const std::string& key) const; bool hasKey(const std::string& key) const; bool getBool(const std::string& key, bool default_) const; long int getInt(const std::string& key, long int default_) const; double getDouble(const std::string& key, double default_) const; const std::string& getString(const std::string& key, const std::string& default_) const; private: template const T& get(const std::string& key, const T& default_, std::function getter) const; std::unordered_map m_data; friend class AnyValue; }; } #ifndef CANTERA_API_NO_BOOST #include "cantera/base/AnyMap.inl.h" #endif #endif