一、cpptoml.h文件内容

/*** @file cpptoml.h* @author Chase Geigle* @date May 2013*/#ifndef CPPTOML_H
#define CPPTOML_H#include <algorithm>
#include <cassert>
#include <clocale>
#include <cstdint>
#include <cstring>
#include <fstream>
#include <iomanip>
#include <map>
#include <memory>
#include <sstream>
#include <stdexcept>
#include <string>
#include <unordered_map>
#include <vector>#if __cplusplus > 201103L
#define CPPTOML_DEPRECATED(reason) [[deprecated(reason)]]
#elif defined(__clang__)
#define CPPTOML_DEPRECATED(reason) __attribute__((deprecated(reason)))
#elif defined(__GNUG__)
#define CPPTOML_DEPRECATED(reason) __attribute__((deprecated))
#elif defined(_MSC_VER)
#if _MSC_VER < 1910
#define CPPTOML_DEPRECATED(reason) __declspec(deprecated)
#else
#define CPPTOML_DEPRECATED(reason) [[deprecated(reason)]]
#endif
#endifnamespace cpptoml
{
class writer; // forward declaration
class base;   // forward declaration
#if defined(CPPTOML_USE_MAP)
// a std::map will ensure that entries a sorted, albeit at a slight
// performance penalty relative to the (default) unordered_map
using string_to_base_map = std::map<std::string, std::shared_ptr<base>>;
#else
// by default an unordered_map is used for best performance as the
// toml specification does not require entries to be sorted
using string_to_base_map= std::unordered_map<std::string, std::shared_ptr<base>>;
#endif// if defined, `base` will retain type information in form of an enum class
// such that static_cast can be used instead of dynamic_cast
// #define CPPTOML_NO_RTTItemplate <class T>
class option
{public:option() : empty_{true}{// nothing}option(T value) : empty_{false}, value_(std::move(value)){// nothing}explicit operator bool() const{return !empty_;}const T& operator*() const{return value_;}const T* operator->() const{return &value_;}template <class U>T value_or(U&& alternative) const{if (!empty_)return value_;return static_cast<T>(std::forward<U>(alternative));}private:bool empty_;T value_;
};struct local_date
{int year = 0;int month = 0;int day = 0;
};struct local_time
{int hour = 0;int minute = 0;int second = 0;int microsecond = 0;
};struct zone_offset
{int hour_offset = 0;int minute_offset = 0;
};struct local_datetime : local_date, local_time
{
};struct offset_datetime : local_datetime, zone_offset
{static inline struct offset_datetime from_zoned(const struct tm& t){offset_datetime dt;dt.year = t.tm_year + 1900;dt.month = t.tm_mon + 1;dt.day = t.tm_mday;dt.hour = t.tm_hour;dt.minute = t.tm_min;dt.second = t.tm_sec;char buf[16];strftime(buf, 16, "%z", &t);int offset = std::stoi(buf);dt.hour_offset = offset / 100;dt.minute_offset = offset % 100;return dt;}CPPTOML_DEPRECATED("from_local has been renamed to from_zoned")static inline struct offset_datetime from_local(const struct tm& t){return from_zoned(t);}static inline struct offset_datetime from_utc(const struct tm& t){offset_datetime dt;dt.year = t.tm_year + 1900;dt.month = t.tm_mon + 1;dt.day = t.tm_mday;dt.hour = t.tm_hour;dt.minute = t.tm_min;dt.second = t.tm_sec;return dt;}
};CPPTOML_DEPRECATED("datetime has been renamed to offset_datetime")
typedef offset_datetime datetime;class fill_guard
{public:fill_guard(std::ostream& os) : os_(os), fill_{os.fill()}{// nothing}~fill_guard(){os_.fill(fill_);}private:std::ostream& os_;std::ostream::char_type fill_;
};inline std::ostream& operator<<(std::ostream& os, const local_date& dt)
{fill_guard g{os};os.fill('0');using std::setw;os << setw(4) << dt.year << "-" << setw(2) << dt.month << "-" << setw(2)<< dt.day;return os;
}inline std::ostream& operator<<(std::ostream& os, const local_time& ltime)
{fill_guard g{os};os.fill('0');using std::setw;os << setw(2) << ltime.hour << ":" << setw(2) << ltime.minute << ":"<< setw(2) << ltime.second;if (ltime.microsecond > 0){os << ".";int power = 100000;for (int curr_us = ltime.microsecond; curr_us; power /= 10){auto num = curr_us / power;os << num;curr_us -= num * power;}}return os;
}inline std::ostream& operator<<(std::ostream& os, const zone_offset& zo)
{fill_guard g{os};os.fill('0');using std::setw;if (zo.hour_offset != 0 || zo.minute_offset != 0){if (zo.hour_offset > 0){os << "+";}else{os << "-";}os << setw(2) << std::abs(zo.hour_offset) << ":" << setw(2)<< std::abs(zo.minute_offset);}else{os << "Z";}return os;
}inline std::ostream& operator<<(std::ostream& os, const local_datetime& dt)
{return os << static_cast<const local_date&>(dt) << "T"<< static_cast<const local_time&>(dt);
}inline std::ostream& operator<<(std::ostream& os, const offset_datetime& dt)
{return os << static_cast<const local_datetime&>(dt)<< static_cast<const zone_offset&>(dt);
}template <class T, class... Ts>
struct is_one_of;template <class T, class V>
struct is_one_of<T, V> : std::is_same<T, V>
{
};template <class T, class V, class... Ts>
struct is_one_of<T, V, Ts...>
{const static bool value= std::is_same<T, V>::value || is_one_of<T, Ts...>::value;
};template <class T>
class value;template <class T>
struct valid_value: is_one_of<T, std::string, int64_t, double, bool, local_date, local_time,local_datetime, offset_datetime>
{
};template <class T, class Enable = void>
struct value_traits;template <class T>
struct valid_value_or_string_convertible
{const static bool value = valid_value<typename std::decay<T>::type>::value|| std::is_convertible<T, std::string>::value;
};template <class T>
struct value_traits<T, typename std::enable_if<valid_value_or_string_convertible<T>::value>::type>
{using value_type = typename std::conditional<valid_value<typename std::decay<T>::type>::value,typename std::decay<T>::type, std::string>::type;using type = value<value_type>;static value_type construct(T&& val){return value_type(val);}
};template <class T>
struct value_traits<T,typename std::enable_if<!valid_value_or_string_convertible<T>::value&& std::is_floating_point<typename std::decay<T>::type>::value>::type>
{using value_type = typename std::decay<T>::type;using type = value<double>;static value_type construct(T&& val){return value_type(val);}
};template <class T>
struct value_traits<T, typename std::enable_if<!valid_value_or_string_convertible<T>::value&& !std::is_floating_point<typename std::decay<T>::type>::value&& std::is_signed<typename std::decay<T>::type>::value>::type>
{using value_type = int64_t;using type = value<int64_t>;static value_type construct(T&& val){if (val < (std::numeric_limits<int64_t>::min)())throw std::underflow_error{"constructed value cannot be ""represented by a 64-bit signed ""integer"};if (val > (std::numeric_limits<int64_t>::max)())throw std::overflow_error{"constructed value cannot be represented ""by a 64-bit signed integer"};return static_cast<int64_t>(val);}
};template <class T>
struct value_traits<T, typename std::enable_if<!valid_value_or_string_convertible<T>::value&& std::is_unsigned<typename std::decay<T>::type>::value>::type>
{using value_type = int64_t;using type = value<int64_t>;static value_type construct(T&& val){if (val > static_cast<uint64_t>((std::numeric_limits<int64_t>::max)()))throw std::overflow_error{"constructed value cannot be represented ""by a 64-bit signed integer"};return static_cast<int64_t>(val);}
};class array;
class table;
class table_array;template <class T>
struct array_of_trait
{using return_type = option<std::vector<T>>;
};template <>
struct array_of_trait<array>
{using return_type = option<std::vector<std::shared_ptr<array>>>;
};template <class T>
inline std::shared_ptr<typename value_traits<T>::type> make_value(T&& val);
inline std::shared_ptr<array> make_array();namespace detail
{
template <class T>
inline std::shared_ptr<T> make_element();
}inline std::shared_ptr<table> make_table();
inline std::shared_ptr<table_array> make_table_array(bool is_inline = false);#if defined(CPPTOML_NO_RTTI)
/// Base type used to store underlying data type explicitly if RTTI is disabled
enum class base_type
{NONE,STRING,LOCAL_TIME,LOCAL_DATE,LOCAL_DATETIME,OFFSET_DATETIME,INT,FLOAT,BOOL,TABLE,ARRAY,TABLE_ARRAY
};/// Type traits class to convert C++ types to enum member
template <class T>
struct base_type_traits;template <>
struct base_type_traits<std::string>
{static const base_type type = base_type::STRING;
};template <>
struct base_type_traits<local_time>
{static const base_type type = base_type::LOCAL_TIME;
};template <>
struct base_type_traits<local_date>
{static const base_type type = base_type::LOCAL_DATE;
};template <>
struct base_type_traits<local_datetime>
{static const base_type type = base_type::LOCAL_DATETIME;
};template <>
struct base_type_traits<offset_datetime>
{static const base_type type = base_type::OFFSET_DATETIME;
};template <>
struct base_type_traits<int64_t>
{static const base_type type = base_type::INT;
};template <>
struct base_type_traits<double>
{static const base_type type = base_type::FLOAT;
};template <>
struct base_type_traits<bool>
{static const base_type type = base_type::BOOL;
};template <>
struct base_type_traits<table>
{static const base_type type = base_type::TABLE;
};template <>
struct base_type_traits<array>
{static const base_type type = base_type::ARRAY;
};template <>
struct base_type_traits<table_array>
{static const base_type type = base_type::TABLE_ARRAY;
};
#endif/*** A generic base TOML value used for type erasure.*/
class base : public std::enable_shared_from_this<base>
{public:virtual ~base() = default;virtual std::shared_ptr<base> clone() const = 0;/*** Determines if the given TOML element is a value.*/virtual bool is_value() const{return false;}/*** Determines if the given TOML element is a table.*/virtual bool is_table() const{return false;}/*** Converts the TOML element into a table.*/std::shared_ptr<table> as_table(){if (is_table())return std::static_pointer_cast<table>(shared_from_this());return nullptr;}/*** Determines if the TOML element is an array of "leaf" elements.*/virtual bool is_array() const{return false;}/*** Converts the TOML element to an array.*/std::shared_ptr<array> as_array(){if (is_array())return std::static_pointer_cast<array>(shared_from_this());return nullptr;}/*** Determines if the given TOML element is an array of tables.*/virtual bool is_table_array() const{return false;}/*** Converts the TOML element into a table array.*/std::shared_ptr<table_array> as_table_array(){if (is_table_array())return std::static_pointer_cast<table_array>(shared_from_this());return nullptr;}/*** Attempts to coerce the TOML element into a concrete TOML value* of type T.*/template <class T>std::shared_ptr<value<T>> as();template <class T>std::shared_ptr<const value<T>> as() const;template <class Visitor, class... Args>void accept(Visitor&& visitor, Args&&... args) const;#if defined(CPPTOML_NO_RTTI)base_type type() const{return type_;}protected:base(const base_type t) : type_(t){// nothing}private:const base_type type_ = base_type::NONE;#elseprotected:base(){// nothing}
#endif
};/*** A concrete TOML value representing the "leaves" of the "tree".*/
template <class T>
class value : public base
{struct make_shared_enabler{// nothing; this is a private key accessible only to friends};template <class U>friend std::shared_ptr<typename value_traits<U>::type>cpptoml::make_value(U&& val);public:static_assert(valid_value<T>::value, "invalid value type");std::shared_ptr<base> clone() const override;value(const make_shared_enabler&, const T& val) : value(val){// nothing; note that users cannot actually invoke this function// because they lack access to the make_shared_enabler.}bool is_value() const override{return true;}/*** Gets the data associated with this value.*/T& get(){return data_;}/*** Gets the data associated with this value. Const version.*/const T& get() const{return data_;}private:T data_;/*** Constructs a value from the given data.*/
#if defined(CPPTOML_NO_RTTI)value(const T& val) : base(base_type_traits<T>::type), data_(val){}
#elsevalue(const T& val) : data_(val){}
#endifvalue(const value& val) = delete;value& operator=(const value& val) = delete;
};template <class T>
std::shared_ptr<typename value_traits<T>::type> make_value(T&& val)
{using value_type = typename value_traits<T>::type;using enabler = typename value_type::make_shared_enabler;return std::make_shared<value_type>(enabler{}, value_traits<T>::construct(std::forward<T>(val)));
}template <class T>
inline std::shared_ptr<value<T>> base::as()
{
#if defined(CPPTOML_NO_RTTI)if (type() == base_type_traits<T>::type)return std::static_pointer_cast<value<T>>(shared_from_this());elsereturn nullptr;
#elsereturn std::dynamic_pointer_cast<value<T>>(shared_from_this());
#endif
}// special case value<double> to allow getting an integer parameter as a
// double value
template <>
inline std::shared_ptr<value<double>> base::as()
{
#if defined(CPPTOML_NO_RTTI)if (type() == base_type::FLOAT)return std::static_pointer_cast<value<double>>(shared_from_this());if (type() == base_type::INT){auto v = std::static_pointer_cast<value<int64_t>>(shared_from_this());return make_value<double>(static_cast<double>(v->get()));}
#elseif (auto v = std::dynamic_pointer_cast<value<double>>(shared_from_this()))return v;if (auto v = std::dynamic_pointer_cast<value<int64_t>>(shared_from_this()))return make_value<double>(static_cast<double>(v->get()));
#endifreturn nullptr;
}template <class T>
inline std::shared_ptr<const value<T>> base::as() const
{
#if defined(CPPTOML_NO_RTTI)if (type() == base_type_traits<T>::type)return std::static_pointer_cast<const value<T>>(shared_from_this());elsereturn nullptr;
#elsereturn std::dynamic_pointer_cast<const value<T>>(shared_from_this());
#endif
}// special case value<double> to allow getting an integer parameter as a
// double value
template <>
inline std::shared_ptr<const value<double>> base::as() const
{
#if defined(CPPTOML_NO_RTTI)if (type() == base_type::FLOAT)return std::static_pointer_cast<const value<double>>(shared_from_this());if (type() == base_type::INT){auto v = as<int64_t>();// the below has to be a non-const value<double> due to a bug in// libc++: https://llvm.org/bugs/show_bug.cgi?id=18843return make_value<double>(static_cast<double>(v->get()));}
#elseif (auto v= std::dynamic_pointer_cast<const value<double>>(shared_from_this()))return v;if (auto v = as<int64_t>()){// the below has to be a non-const value<double> due to a bug in// libc++: https://llvm.org/bugs/show_bug.cgi?id=18843return make_value<double>(static_cast<double>(v->get()));}
#endifreturn nullptr;
}/*** Exception class for array insertion errors.*/
class array_exception : public std::runtime_error
{public:array_exception(const std::string& err) : std::runtime_error{err}{}
};class array : public base
{public:friend std::shared_ptr<array> make_array();std::shared_ptr<base> clone() const override;virtual bool is_array() const override{return true;}using size_type = std::size_t;/*** arrays can be iterated over*/using iterator = std::vector<std::shared_ptr<base>>::iterator;/*** arrays can be iterated over.  Const version.*/using const_iterator = std::vector<std::shared_ptr<base>>::const_iterator;iterator begin(){return values_.begin();}const_iterator begin() const{return values_.begin();}iterator end(){return values_.end();}const_iterator end() const{return values_.end();}/*** Obtains the array (vector) of base values.*/std::vector<std::shared_ptr<base>>& get(){return values_;}/*** Obtains the array (vector) of base values. Const version.*/const std::vector<std::shared_ptr<base>>& get() const{return values_;}std::shared_ptr<base> at(size_t idx) const{return values_.at(idx);}/*** Obtains an array of value<T>s. Note that elements may be* nullptr if they cannot be converted to a value<T>.*/template <class T>std::vector<std::shared_ptr<value<T>>> array_of() const{std::vector<std::shared_ptr<value<T>>> result(values_.size());std::transform(values_.begin(), values_.end(), result.begin(),[&](std::shared_ptr<base> v) { return v->as<T>(); });return result;}/*** Obtains a option<vector<T>>. The option will be empty if the array* contains values that are not of type T.*/template <class T>inline typename array_of_trait<T>::return_type get_array_of() const{std::vector<T> result;result.reserve(values_.size());for (const auto& val : values_){if (auto v = val->as<T>())result.push_back(v->get());elsereturn {};}return {std::move(result)};}/*** Obtains an array of arrays. Note that elements may be nullptr* if they cannot be converted to a array.*/std::vector<std::shared_ptr<array>> nested_array() const{std::vector<std::shared_ptr<array>> result(values_.size());std::transform(values_.begin(), values_.end(), result.begin(),[&](std::shared_ptr<base> v) -> std::shared_ptr<array> {if (v->is_array())return std::static_pointer_cast<array>(v);return std::shared_ptr<array>{};});return result;}/*** Add a value to the end of the array*/template <class T>void push_back(const std::shared_ptr<value<T>>& val){if (values_.empty() || values_[0]->as<T>()){values_.push_back(val);}else{throw array_exception{"Arrays must be homogenous."};}}/*** Add an array to the end of the array*/void push_back(const std::shared_ptr<array>& val){if (values_.empty() || values_[0]->is_array()){values_.push_back(val);}else{throw array_exception{"Arrays must be homogenous."};}}/*** Convenience function for adding a simple element to the end* of the array.*/template <class T>void push_back(T&& val, typename value_traits<T>::type* = 0){push_back(make_value(std::forward<T>(val)));}/*** Insert a value into the array*/template <class T>iterator insert(iterator position, const std::shared_ptr<value<T>>& value){if (values_.empty() || values_[0]->as<T>()){return values_.insert(position, value);}else{throw array_exception{"Arrays must be homogenous."};}}/*** Insert an array into the array*/iterator insert(iterator position, const std::shared_ptr<array>& value){if (values_.empty() || values_[0]->is_array()){return values_.insert(position, value);}else{throw array_exception{"Arrays must be homogenous."};}}/*** Convenience function for inserting a simple element in the array*/template <class T>iterator insert(iterator position, T&& val,typename value_traits<T>::type* = 0){return insert(position, make_value(std::forward<T>(val)));}/*** Erase an element from the array*/iterator erase(iterator position){return values_.erase(position);}/*** Clear the array*/void clear(){values_.clear();}/*** Reserve space for n values.*/void reserve(size_type n){values_.reserve(n);}private:
#if defined(CPPTOML_NO_RTTI)array() : base(base_type::ARRAY){// empty}
#elsearray() = default;
#endiftemplate <class InputIterator>array(InputIterator begin, InputIterator end) : values_{begin, end}{// nothing}array(const array& obj) = delete;array& operator=(const array& obj) = delete;std::vector<std::shared_ptr<base>> values_;
};inline std::shared_ptr<array> make_array()
{struct make_shared_enabler : public array{make_shared_enabler(){// nothing}};return std::make_shared<make_shared_enabler>();
}namespace detail
{
template <>
inline std::shared_ptr<array> make_element<array>()
{return make_array();
}
} // namespace detail/*** Obtains a option<vector<T>>. The option will be empty if the array* contains values that are not of type T.*/
template <>
inline typename array_of_trait<array>::return_type
array::get_array_of<array>() const
{std::vector<std::shared_ptr<array>> result;result.reserve(values_.size());for (const auto& val : values_){if (auto v = val->as_array())result.push_back(v);elsereturn {};}return {std::move(result)};
}class table;class table_array : public base
{friend class table;friend std::shared_ptr<table_array> make_table_array(bool);public:std::shared_ptr<base> clone() const override;using size_type = std::size_t;/*** arrays can be iterated over*/using iterator = std::vector<std::shared_ptr<table>>::iterator;/*** arrays can be iterated over.  Const version.*/using const_iterator = std::vector<std::shared_ptr<table>>::const_iterator;iterator begin(){return array_.begin();}const_iterator begin() const{return array_.begin();}iterator end(){return array_.end();}const_iterator end() const{return array_.end();}virtual bool is_table_array() const override{return true;}std::vector<std::shared_ptr<table>>& get(){return array_;}const std::vector<std::shared_ptr<table>>& get() const{return array_;}/*** Add a table to the end of the array*/void push_back(const std::shared_ptr<table>& val){array_.push_back(val);}/*** Insert a table into the array*/iterator insert(iterator position, const std::shared_ptr<table>& value){return array_.insert(position, value);}/*** Erase an element from the array*/iterator erase(iterator position){return array_.erase(position);}/*** Clear the array*/void clear(){array_.clear();}/*** Reserve space for n tables.*/void reserve(size_type n){array_.reserve(n);}/*** Whether or not the table array is declared inline. This mostly* matters for parsing, where statically defined arrays cannot be* appended to using the array-of-table syntax.*/bool is_inline() const{return is_inline_;}private:
#if defined(CPPTOML_NO_RTTI)table_array(bool is_inline = false): base(base_type::TABLE_ARRAY), is_inline_(is_inline){// nothing}
#elsetable_array(bool is_inline = false) : is_inline_(is_inline){// nothing}
#endiftable_array(const table_array& obj) = delete;table_array& operator=(const table_array& rhs) = delete;std::vector<std::shared_ptr<table>> array_;const bool is_inline_ = false;
};inline std::shared_ptr<table_array> make_table_array(bool is_inline)
{struct make_shared_enabler : public table_array{make_shared_enabler(bool mse_is_inline) : table_array(mse_is_inline){// nothing}};return std::make_shared<make_shared_enabler>(is_inline);
}namespace detail
{
template <>
inline std::shared_ptr<table_array> make_element<table_array>()
{return make_table_array(true);
}
} // namespace detail// The below are overloads for fetching specific value types out of a value
// where special casting behavior (like bounds checking) is desiredtemplate <class T>
typename std::enable_if<!std::is_floating_point<T>::value&& std::is_signed<T>::value,option<T>>::type
get_impl(const std::shared_ptr<base>& elem)
{if (auto v = elem->as<int64_t>()){if (v->get() < (std::numeric_limits<T>::min)())throw std::underflow_error{"T cannot represent the value requested in get"};if (v->get() > (std::numeric_limits<T>::max)())throw std::overflow_error{"T cannot represent the value requested in get"};return {static_cast<T>(v->get())};}else{return {};}
}template <class T>
typename std::enable_if<!std::is_same<T, bool>::value&& std::is_unsigned<T>::value,option<T>>::type
get_impl(const std::shared_ptr<base>& elem)
{if (auto v = elem->as<int64_t>()){if (v->get() < 0)throw std::underflow_error{"T cannot store negative value in get"};if (static_cast<uint64_t>(v->get()) > (std::numeric_limits<T>::max)())throw std::overflow_error{"T cannot represent the value requested in get"};return {static_cast<T>(v->get())};}else{return {};}
}template <class T>
typename std::enable_if<!std::is_integral<T>::value|| std::is_same<T, bool>::value,option<T>>::type
get_impl(const std::shared_ptr<base>& elem)
{if (auto v = elem->as<T>()){return {v->get()};}else{return {};}
}/*** Represents a TOML keytable.*/
class table : public base
{public:friend class table_array;friend std::shared_ptr<table> make_table();std::shared_ptr<base> clone() const override;/*** tables can be iterated over.*/using iterator = string_to_base_map::iterator;/*** tables can be iterated over. Const version.*/using const_iterator = string_to_base_map::const_iterator;iterator begin(){return map_.begin();}const_iterator begin() const{return map_.begin();}iterator end(){return map_.end();}const_iterator end() const{return map_.end();}bool is_table() const override{return true;}bool empty() const{return map_.empty();}/*** Determines if this key table contains the given key.*/bool contains(const std::string& key) const{return map_.find(key) != map_.end();}/*** Determines if this key table contains the given key. Will* resolve "qualified keys". Qualified keys are the full access* path separated with dots like "grandparent.parent.child".*/bool contains_qualified(const std::string& key) const{return resolve_qualified(key);}/*** Obtains the base for a given key.* @throw std::out_of_range if the key does not exist*/std::shared_ptr<base> get(const std::string& key) const{return map_.at(key);}/*** Obtains the base for a given key. Will resolve "qualified* keys". Qualified keys are the full access path separated with* dots like "grandparent.parent.child".** @throw std::out_of_range if the key does not exist*/std::shared_ptr<base> get_qualified(const std::string& key) const{std::shared_ptr<base> p;resolve_qualified(key, &p);return p;}/*** Obtains a table for a given key, if possible.*/std::shared_ptr<table> get_table(const std::string& key) const{if (contains(key) && get(key)->is_table())return std::static_pointer_cast<table>(get(key));return nullptr;}/*** Obtains a table for a given key, if possible. Will resolve* "qualified keys".*/std::shared_ptr<table> get_table_qualified(const std::string& key) const{if (contains_qualified(key) && get_qualified(key)->is_table())return std::static_pointer_cast<table>(get_qualified(key));return nullptr;}/*** Obtains an array for a given key.*/std::shared_ptr<array> get_array(const std::string& key) const{if (!contains(key))return nullptr;return get(key)->as_array();}/*** Obtains an array for a given key. Will resolve "qualified keys".*/std::shared_ptr<array> get_array_qualified(const std::string& key) const{if (!contains_qualified(key))return nullptr;return get_qualified(key)->as_array();}/*** Obtains a table_array for a given key, if possible.*/std::shared_ptr<table_array> get_table_array(const std::string& key) const{if (!contains(key))return nullptr;return get(key)->as_table_array();}/*** Obtains a table_array for a given key, if possible. Will resolve* "qualified keys".*/std::shared_ptr<table_array>get_table_array_qualified(const std::string& key) const{if (!contains_qualified(key))return nullptr;return get_qualified(key)->as_table_array();}/*** Helper function that attempts to get a value corresponding* to the template parameter from a given key.*/template <class T>option<T> get_as(const std::string& key) const{try{return get_impl<T>(get(key));}catch (const std::out_of_range&){return {};}}/*** Helper function that attempts to get a value corresponding* to the template parameter from a given key. Will resolve "qualified* keys".*/template <class T>option<T> get_qualified_as(const std::string& key) const{try{return get_impl<T>(get_qualified(key));}catch (const std::out_of_range&){return {};}}/*** Helper function that attempts to get an array of values of a given* type corresponding to the template parameter for a given key.** If the key doesn't exist, doesn't exist as an array type, or one or* more keys inside the array type are not of type T, an empty option* is returned. Otherwise, an option containing a vector of the values* is returned.*/template <class T>inline typename array_of_trait<T>::return_typeget_array_of(const std::string& key) const{if (auto v = get_array(key)){std::vector<T> result;result.reserve(v->get().size());for (const auto& b : v->get()){if (auto val = b->as<T>())result.push_back(val->get());elsereturn {};}return {std::move(result)};}return {};}/*** Helper function that attempts to get an array of values of a given* type corresponding to the template parameter for a given key. Will* resolve "qualified keys".** If the key doesn't exist, doesn't exist as an array type, or one or* more keys inside the array type are not of type T, an empty option* is returned. Otherwise, an option containing a vector of the values* is returned.*/template <class T>inline typename array_of_trait<T>::return_typeget_qualified_array_of(const std::string& key) const{if (auto v = get_array_qualified(key)){std::vector<T> result;result.reserve(v->get().size());for (const auto& b : v->get()){if (auto val = b->as<T>())result.push_back(val->get());elsereturn {};}return {std::move(result)};}return {};}/*** Adds an element to the keytable.*/void insert(const std::string& key, const std::shared_ptr<base>& value){map_[key] = value;}/*** Convenience shorthand for adding a simple element to the* keytable.*/template <class T>void insert(const std::string& key, T&& val,typename value_traits<T>::type* = 0){insert(key, make_value(std::forward<T>(val)));}/*** Removes an element from the table.*/void erase(const std::string& key){map_.erase(key);}private:
#if defined(CPPTOML_NO_RTTI)table() : base(base_type::TABLE){// nothing}
#elsetable(){// nothing}
#endiftable(const table& obj) = delete;table& operator=(const table& rhs) = delete;std::vector<std::string> split(const std::string& value,char separator) const{std::vector<std::string> result;std::string::size_type p = 0;std::string::size_type q;while ((q = value.find(separator, p)) != std::string::npos){result.emplace_back(value, p, q - p);p = q + 1;}result.emplace_back(value, p);return result;}// If output parameter p is specified, fill it with the pointer to the// specified entry and throw std::out_of_range if it couldn't be found.//// Otherwise, just return true if the entry could be found or false// otherwise and do not throw.bool resolve_qualified(const std::string& key,std::shared_ptr<base>* p = nullptr) const{auto parts = split(key, '.');auto last_key = parts.back();parts.pop_back();auto cur_table = this;for (const auto& part : parts){cur_table = cur_table->get_table(part).get();if (!cur_table){if (!p)return false;throw std::out_of_range{key + " is not a valid key"};}}if (!p)return cur_table->map_.count(last_key) != 0;*p = cur_table->map_.at(last_key);return true;}string_to_base_map map_;
};/*** Helper function that attempts to get an array of arrays for a given* key.** If the key doesn't exist, doesn't exist as an array type, or one or* more keys inside the array type are not of type T, an empty option* is returned. Otherwise, an option containing a vector of the values* is returned.*/
template <>
inline typename array_of_trait<array>::return_type
table::get_array_of<array>(const std::string& key) const
{if (auto v = get_array(key)){std::vector<std::shared_ptr<array>> result;result.reserve(v->get().size());for (const auto& b : v->get()){if (auto val = b->as_array())result.push_back(val);elsereturn {};}return {std::move(result)};}return {};
}/*** Helper function that attempts to get an array of arrays for a given* key. Will resolve "qualified keys".** If the key doesn't exist, doesn't exist as an array type, or one or* more keys inside the array type are not of type T, an empty option* is returned. Otherwise, an option containing a vector of the values* is returned.*/
template <>
inline typename array_of_trait<array>::return_type
table::get_qualified_array_of<array>(const std::string& key) const
{if (auto v = get_array_qualified(key)){std::vector<std::shared_ptr<array>> result;result.reserve(v->get().size());for (const auto& b : v->get()){if (auto val = b->as_array())result.push_back(val);elsereturn {};}return {std::move(result)};}return {};
}std::shared_ptr<table> make_table()
{struct make_shared_enabler : public table{make_shared_enabler(){// nothing}};return std::make_shared<make_shared_enabler>();
}namespace detail
{
template <>
inline std::shared_ptr<table> make_element<table>()
{return make_table();
}
} // namespace detailtemplate <class T>
std::shared_ptr<base> value<T>::clone() const
{return make_value(data_);
}inline std::shared_ptr<base> array::clone() const
{auto result = make_array();result->reserve(values_.size());for (const auto& ptr : values_)result->values_.push_back(ptr->clone());return result;
}inline std::shared_ptr<base> table_array::clone() const
{auto result = make_table_array(is_inline());result->reserve(array_.size());for (const auto& ptr : array_)result->array_.push_back(ptr->clone()->as_table());return result;
}inline std::shared_ptr<base> table::clone() const
{auto result = make_table();for (const auto& pr : map_)result->insert(pr.first, pr.second->clone());return result;
}/*** Exception class for all TOML parsing errors.*/
class parse_exception : public std::runtime_error
{public:parse_exception(const std::string& err) : std::runtime_error{err}{}parse_exception(const std::string& err, std::size_t line_number): std::runtime_error{err + " at line " + std::to_string(line_number)}{}
};inline bool is_number(char c)
{return c >= '0' && c <= '9';
}inline bool is_hex(char c)
{return is_number(c) || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F');
}/*** Helper object for consuming expected characters.*/
template <class OnError>
class consumer
{public:consumer(std::string::iterator& it, const std::string::iterator& end,OnError&& on_error): it_(it), end_(end), on_error_(std::forward<OnError>(on_error)){// nothing}void operator()(char c){if (it_ == end_ || *it_ != c)on_error_();++it_;}template <std::size_t N>void operator()(const char (&str)[N]){std::for_each(std::begin(str), std::end(str) - 1,[&](char c) { (*this)(c); });}void eat_or(char a, char b){if (it_ == end_ || (*it_ != a && *it_ != b))on_error_();++it_;}int eat_digits(int len){int val = 0;for (int i = 0; i < len; ++i){if (!is_number(*it_) || it_ == end_)on_error_();val = 10 * val + (*it_++ - '0');}return val;}void error(){on_error_();}private:std::string::iterator& it_;const std::string::iterator& end_;OnError on_error_;
};template <class OnError>
consumer<OnError> make_consumer(std::string::iterator& it,const std::string::iterator& end,OnError&& on_error)
{return consumer<OnError>(it, end, std::forward<OnError>(on_error));
}// replacement for std::getline to handle incorrectly line-ended files
// https://stackoverflow.com/questions/6089231/getting-std-ifstream-to-handle-lf-cr-and-crlf
namespace detail
{
inline std::istream& getline(std::istream& input, std::string& line)
{line.clear();std::istream::sentry sentry{input, true};auto sb = input.rdbuf();while (true){auto c = sb->sbumpc();if (c == '\r'){if (sb->sgetc() == '\n')c = sb->sbumpc();}if (c == '\n')return input;if (c == std::istream::traits_type::eof()){if (line.empty())input.setstate(std::ios::eofbit);return input;}line.push_back(static_cast<char>(c));}
}
} // namespace detail/*** The parser class.*/
class parser
{public:/*** Parsers are constructed from streams.*/parser(std::istream& stream) : input_(stream){// nothing}parser& operator=(const parser& parser) = delete;/*** Parses the stream this parser was created on until EOF.* @throw parse_exception if there are errors in parsing*/std::shared_ptr<table> parse(){std::shared_ptr<table> root = make_table();table* curr_table = root.get();while (detail::getline(input_, line_)){line_number_++;auto it = line_.begin();auto end = line_.end();consume_whitespace(it, end);if (it == end || *it == '#')continue;if (*it == '['){curr_table = root.get();parse_table(it, end, curr_table);}else{parse_key_value(it, end, curr_table);consume_whitespace(it, end);eol_or_comment(it, end);}}return root;}private:
#if defined _MSC_VER__declspec(noreturn)
#elif defined __GNUC____attribute__((noreturn))
#endifvoid throw_parse_exception(const std::string& err){throw parse_exception{err, line_number_};}void parse_table(std::string::iterator& it,const std::string::iterator& end, table*& curr_table){// remove the beginning keytable marker++it;if (it == end)throw_parse_exception("Unexpected end of table");if (*it == '[')parse_table_array(it, end, curr_table);elseparse_single_table(it, end, curr_table);}void parse_single_table(std::string::iterator& it,const std::string::iterator& end,table*& curr_table){if (it == end || *it == ']')throw_parse_exception("Table name cannot be empty");std::string full_table_name;bool inserted = false;auto key_end = [](char c) { return c == ']'; };auto key_part_handler = [&](const std::string& part) {if (part.empty())throw_parse_exception("Empty component of table name");if (!full_table_name.empty())full_table_name += '.';full_table_name += part;if (curr_table->contains(part)){
#if !defined(__PGI)auto b = curr_table->get(part);
#else// Workaround for PGI compilerstd::shared_ptr<base> b = curr_table->get(part);
#endifif (b->is_table())curr_table = static_cast<table*>(b.get());else if (b->is_table_array())curr_table = std::static_pointer_cast<table_array>(b)->get().back().get();elsethrow_parse_exception("Key " + full_table_name+ "already exists as a value");}else{inserted = true;curr_table->insert(part, make_table());curr_table = static_cast<table*>(curr_table->get(part).get());}};key_part_handler(parse_key(it, end, key_end, key_part_handler));if (it == end)throw_parse_exception("Unterminated table declaration; did you forget a ']'?");if (*it != ']'){std::string errmsg{"Unexpected character in table definition: "};errmsg += '"';errmsg += *it;errmsg += '"';throw_parse_exception(errmsg);}// table already existedif (!inserted){auto is_value= [](const std::pair<const std::string&,const std::shared_ptr<base>&>& p) {return p.second->is_value();};// if there are any values, we can't add values to this table// since it has already been defined. If there aren't any// values, then it was implicitly created by something like// [a.b]if (curr_table->empty()|| std::any_of(curr_table->begin(), curr_table->end(),is_value)){throw_parse_exception("Redefinition of table "+ full_table_name);}}++it;consume_whitespace(it, end);eol_or_comment(it, end);}void parse_table_array(std::string::iterator& it,const std::string::iterator& end, table*& curr_table){++it;if (it == end || *it == ']')throw_parse_exception("Table array name cannot be empty");auto key_end = [](char c) { return c == ']'; };std::string full_ta_name;auto key_part_handler = [&](const std::string& part) {if (part.empty())throw_parse_exception("Empty component of table array name");if (!full_ta_name.empty())full_ta_name += '.';full_ta_name += part;if (curr_table->contains(part)){
#if !defined(__PGI)auto b = curr_table->get(part);
#else// Workaround for PGI compilerstd::shared_ptr<base> b = curr_table->get(part);
#endif// if this is the end of the table array name, add an// element to the table array that we just looked up,// provided it was not declared inlineif (it != end && *it == ']'){if (!b->is_table_array()){throw_parse_exception("Key " + full_ta_name+ " is not a table array");}auto v = b->as_table_array();if (v->is_inline()){throw_parse_exception("Static array " + full_ta_name+ " cannot be appended to");}v->get().push_back(make_table());curr_table = v->get().back().get();}// otherwise, just keep traversing down the key nameelse{if (b->is_table())curr_table = static_cast<table*>(b.get());else if (b->is_table_array())curr_table = std::static_pointer_cast<table_array>(b)->get().back().get();elsethrow_parse_exception("Key " + full_ta_name+ " already exists as a value");}}else{// if this is the end of the table array name, add a new// table array and a new table inside that array for us to// add keys to nextif (it != end && *it == ']'){curr_table->insert(part, make_table_array());auto arr = std::static_pointer_cast<table_array>(curr_table->get(part));arr->get().push_back(make_table());curr_table = arr->get().back().get();}// otherwise, create the implicitly defined table and move// down to itelse{curr_table->insert(part, make_table());curr_table= static_cast<table*>(curr_table->get(part).get());}}};key_part_handler(parse_key(it, end, key_end, key_part_handler));// consume the last "]]"auto eat = make_consumer(it, end, [this]() {throw_parse_exception("Unterminated table array name");});eat(']');eat(']');consume_whitespace(it, end);eol_or_comment(it, end);}void parse_key_value(std::string::iterator& it, std::string::iterator& end,table* curr_table){auto key_end = [](char c) { return c == '='; };auto key_part_handler = [&](const std::string& part) {// two cases: this key part exists already, in which case it must// be a table, or it doesn't exist in which case we must create// an implicitly defined tableif (curr_table->contains(part)){auto val = curr_table->get(part);if (val->is_table()){curr_table = static_cast<table*>(val.get());}else{throw_parse_exception("Key " + part+ " already exists as a value");}}else{auto newtable = make_table();curr_table->insert(part, newtable);curr_table = newtable.get();}};auto key = parse_key(it, end, key_end, key_part_handler);if (curr_table->contains(key))throw_parse_exception("Key " + key + " already present");if (it == end || *it != '=')throw_parse_exception("Value must follow after a '='");++it;consume_whitespace(it, end);curr_table->insert(key, parse_value(it, end));consume_whitespace(it, end);}template <class KeyEndFinder, class KeyPartHandler>std::stringparse_key(std::string::iterator& it, const std::string::iterator& end,KeyEndFinder&& key_end, KeyPartHandler&& key_part_handler){// parse the key as a series of one or more simple-keys joined with '.'while (it != end && !key_end(*it)){auto part = parse_simple_key(it, end);consume_whitespace(it, end);if (it == end || key_end(*it)){return part;}if (*it != '.'){std::string errmsg{"Unexpected character in key: "};errmsg += '"';errmsg += *it;errmsg += '"';throw_parse_exception(errmsg);}key_part_handler(part);// consume the dot++it;}throw_parse_exception("Unexpected end of key");}std::string parse_simple_key(std::string::iterator& it,const std::string::iterator& end){consume_whitespace(it, end);if (it == end)throw_parse_exception("Unexpected end of key (blank key?)");if (*it == '"' || *it == '\''){return string_literal(it, end, *it);}else{auto bke = std::find_if(it, end, [](char c) {return c == '.' || c == '=' || c == ']';});return parse_bare_key(it, bke);}}std::string parse_bare_key(std::string::iterator& it,const std::string::iterator& end){if (it == end){throw_parse_exception("Bare key missing name");}auto key_end = end;--key_end;consume_backwards_whitespace(key_end, it);++key_end;std::string key{it, key_end};if (std::find(it, key_end, '#') != key_end){throw_parse_exception("Bare key " + key + " cannot contain #");}if (std::find_if(it, key_end,[](char c) { return c == ' ' || c == '\t'; })!= key_end){throw_parse_exception("Bare key " + key+ " cannot contain whitespace");}if (std::find_if(it, key_end,[](char c) { return c == '[' || c == ']'; })!= key_end){throw_parse_exception("Bare key " + key+ " cannot contain '[' or ']'");}it = end;return key;}enum class parse_type{STRING = 1,LOCAL_TIME,LOCAL_DATE,LOCAL_DATETIME,OFFSET_DATETIME,INT,FLOAT,BOOL,ARRAY,INLINE_TABLE};std::shared_ptr<base> parse_value(std::string::iterator& it,std::string::iterator& end){parse_type type = determine_value_type(it, end);switch (type){case parse_type::STRING:return parse_string(it, end);case parse_type::LOCAL_TIME:return parse_time(it, end);case parse_type::LOCAL_DATE:case parse_type::LOCAL_DATETIME:case parse_type::OFFSET_DATETIME:return parse_date(it, end);case parse_type::INT:case parse_type::FLOAT:return parse_number(it, end);case parse_type::BOOL:return parse_bool(it, end);case parse_type::ARRAY:return parse_array(it, end);case parse_type::INLINE_TABLE:return parse_inline_table(it, end);default:throw_parse_exception("Failed to parse value");}}parse_type determine_value_type(const std::string::iterator& it,const std::string::iterator& end){if (it == end){throw_parse_exception("Failed to parse value type");}if (*it == '"' || *it == '\''){return parse_type::STRING;}else if (is_time(it, end)){return parse_type::LOCAL_TIME;}else if (auto dtype = date_type(it, end)){return *dtype;}else if (is_number(*it) || *it == '-' || *it == '+'|| (*it == 'i' && it + 1 != end && it[1] == 'n'&& it + 2 != end && it[2] == 'f')|| (*it == 'n' && it + 1 != end && it[1] == 'a'&& it + 2 != end && it[2] == 'n')){return determine_number_type(it, end);}else if (*it == 't' || *it == 'f'){return parse_type::BOOL;}else if (*it == '['){return parse_type::ARRAY;}else if (*it == '{'){return parse_type::INLINE_TABLE;}throw_parse_exception("Failed to parse value type");}parse_type determine_number_type(const std::string::iterator& it,const std::string::iterator& end){// determine if we are an integer or a floatauto check_it = it;if (*check_it == '-' || *check_it == '+')++check_it;if (check_it == end)throw_parse_exception("Malformed number");if (*check_it == 'i' || *check_it == 'n')return parse_type::FLOAT;while (check_it != end && is_number(*check_it))++check_it;if (check_it != end && *check_it == '.'){++check_it;while (check_it != end && is_number(*check_it))++check_it;return parse_type::FLOAT;}else{return parse_type::INT;}}std::shared_ptr<value<std::string>> parse_string(std::string::iterator& it,std::string::iterator& end){auto delim = *it;assert(delim == '"' || delim == '\'');// end is non-const here because we have to be able to potentially// parse multiple lines in a string, not just oneauto check_it = it;++check_it;if (check_it != end && *check_it == delim){++check_it;if (check_it != end && *check_it == delim){it = ++check_it;return parse_multiline_string(it, end, delim);}}return make_value<std::string>(string_literal(it, end, delim));}std::shared_ptr<value<std::string>>parse_multiline_string(std::string::iterator& it,std::string::iterator& end, char delim){std::stringstream ss;auto is_ws = [](char c) { return c == ' ' || c == '\t'; };bool consuming = false;std::shared_ptr<value<std::string>> ret;auto handle_line = [&](std::string::iterator& local_it,std::string::iterator& local_end) {if (consuming){local_it = std::find_if_not(local_it, local_end, is_ws);// whole line is whitespaceif (local_it == local_end)return;}consuming = false;while (local_it != local_end){// handle escaped charactersif (delim == '"' && *local_it == '\\'){auto check = local_it;// check if this is an actual escape sequence or a// whitespace escaping backslash++check;consume_whitespace(check, local_end);if (check == local_end){consuming = true;break;}ss << parse_escape_code(local_it, local_end);continue;}// if we can end the stringif (std::distance(local_it, local_end) >= 3){auto check = local_it;// check for """if (*check++ == delim && *check++ == delim&& *check++ == delim){local_it = check;ret = make_value<std::string>(ss.str());break;}}ss << *local_it++;}};// handle the remainder of the current linehandle_line(it, end);if (ret)return ret;// start eating lineswhile (detail::getline(input_, line_)){++line_number_;it = line_.begin();end = line_.end();handle_line(it, end);if (ret)return ret;if (!consuming)ss << std::endl;}throw_parse_exception("Unterminated multi-line basic string");}std::string string_literal(std::string::iterator& it,const std::string::iterator& end, char delim){++it;std::string val;while (it != end){// handle escaped charactersif (delim == '"' && *it == '\\'){val += parse_escape_code(it, end);}else if (*it == delim){++it;consume_whitespace(it, end);return val;}else{val += *it++;}}throw_parse_exception("Unterminated string literal");}std::string parse_escape_code(std::string::iterator& it,const std::string::iterator& end){++it;if (it == end)throw_parse_exception("Invalid escape sequence");char value;if (*it == 'b'){value = '\b';}else if (*it == 't'){value = '\t';}else if (*it == 'n'){value = '\n';}else if (*it == 'f'){value = '\f';}else if (*it == 'r'){value = '\r';}else if (*it == '"'){value = '"';}else if (*it == '\\'){value = '\\';}else if (*it == 'u' || *it == 'U'){return parse_unicode(it, end);}else{throw_parse_exception("Invalid escape sequence");}++it;return std::string(1, value);}std::string parse_unicode(std::string::iterator& it,const std::string::iterator& end){bool large = *it++ == 'U';auto codepoint = parse_hex(it, end, large ? 0x10000000 : 0x1000);if ((codepoint > 0xd7ff && codepoint < 0xe000) || codepoint > 0x10ffff){throw_parse_exception("Unicode escape sequence is not a Unicode scalar value");}std::string result;// See Table 3-6 of the Unicode standardif (codepoint <= 0x7f){// 1-byte codepoints: 00000000 0xxxxxxx// repr: 0xxxxxxxresult += static_cast<char>(codepoint & 0x7f);}else if (codepoint <= 0x7ff){// 2-byte codepoints: 00000yyy yyxxxxxx// repr: 110yyyyy 10xxxxxx//// 0x1f = 00011111// 0xc0 = 11000000//result += static_cast<char>(0xc0 | ((codepoint >> 6) & 0x1f));//// 0x80 = 10000000// 0x3f = 00111111//result += static_cast<char>(0x80 | (codepoint & 0x3f));}else if (codepoint <= 0xffff){// 3-byte codepoints: zzzzyyyy yyxxxxxx// repr: 1110zzzz 10yyyyyy 10xxxxxx//// 0xe0 = 11100000// 0x0f = 00001111//result += static_cast<char>(0xe0 | ((codepoint >> 12) & 0x0f));result += static_cast<char>(0x80 | ((codepoint >> 6) & 0x1f));result += static_cast<char>(0x80 | (codepoint & 0x3f));}else{// 4-byte codepoints: 000uuuuu zzzzyyyy yyxxxxxx// repr: 11110uuu 10uuzzzz 10yyyyyy 10xxxxxx//// 0xf0 = 11110000// 0x07 = 00000111//result += static_cast<char>(0xf0 | ((codepoint >> 18) & 0x07));result += static_cast<char>(0x80 | ((codepoint >> 12) & 0x3f));result += static_cast<char>(0x80 | ((codepoint >> 6) & 0x3f));result += static_cast<char>(0x80 | (codepoint & 0x3f));}return result;}uint32_t parse_hex(std::string::iterator& it,const std::string::iterator& end, uint32_t place){uint32_t value = 0;while (place > 0){if (it == end)throw_parse_exception("Unexpected end of unicode sequence");if (!is_hex(*it))throw_parse_exception("Invalid unicode escape sequence");value += place * hex_to_digit(*it++);place /= 16;}return value;}uint32_t hex_to_digit(char c){if (is_number(c))return static_cast<uint32_t>(c - '0');return 10+ static_cast<uint32_t>(c- ((c >= 'a' && c <= 'f') ? 'a' : 'A'));}std::shared_ptr<base> parse_number(std::string::iterator& it,const std::string::iterator& end){auto check_it = it;auto check_end = find_end_of_number(it, end);auto eat_sign = [&]() {if (check_it != end && (*check_it == '-' || *check_it == '+'))++check_it;};auto check_no_leading_zero = [&]() {if (check_it != end && *check_it == '0' && check_it + 1 != check_end&& check_it[1] != '.'){throw_parse_exception("Numbers may not have leading zeros");}};auto eat_digits = [&](bool (*check_char)(char)) {auto beg = check_it;while (check_it != end && check_char(*check_it)){++check_it;if (check_it != end && *check_it == '_'){++check_it;if (check_it == end || !check_char(*check_it))throw_parse_exception("Malformed number");}}if (check_it == beg)throw_parse_exception("Malformed number");};auto eat_hex = [&]() { eat_digits(&is_hex); };auto eat_numbers = [&]() { eat_digits(&is_number); };if (check_it != end && *check_it == '0' && check_it + 1 != check_end&& (check_it[1] == 'x' || check_it[1] == 'o' || check_it[1] == 'b')){++check_it;char base = *check_it;++check_it;if (base == 'x'){eat_hex();return parse_int(it, check_it, 16);}else if (base == 'o'){auto start = check_it;eat_numbers();auto val = parse_int(start, check_it, 8, "0");it = start;return val;}else // if (base == 'b'){auto start = check_it;eat_numbers();auto val = parse_int(start, check_it, 2);it = start;return val;}}eat_sign();check_no_leading_zero();if (check_it != end && check_it + 1 != end && check_it + 2 != end){if (check_it[0] == 'i' && check_it[1] == 'n' && check_it[2] == 'f'){auto val = std::numeric_limits<double>::infinity();if (*it == '-')val = -val;it = check_it + 3;return make_value(val);}else if (check_it[0] == 'n' && check_it[1] == 'a'&& check_it[2] == 'n'){auto val = std::numeric_limits<double>::quiet_NaN();if (*it == '-')val = -val;it = check_it + 3;return make_value(val);}}eat_numbers();if (check_it != end&& (*check_it == '.' || *check_it == 'e' || *check_it == 'E')){bool is_exp = *check_it == 'e' || *check_it == 'E';++check_it;if (check_it == end)throw_parse_exception("Floats must have trailing digits");auto eat_exp = [&]() {eat_sign();check_no_leading_zero();eat_numbers();};if (is_exp)eat_exp();elseeat_numbers();if (!is_exp && check_it != end&& (*check_it == 'e' || *check_it == 'E')){++check_it;eat_exp();}return parse_float(it, check_it);}else{return parse_int(it, check_it);}}std::shared_ptr<value<int64_t>> parse_int(std::string::iterator& it,const std::string::iterator& end,int base = 10,const char* prefix = ""){std::string v{it, end};v = prefix + v;v.erase(std::remove(v.begin(), v.end(), '_'), v.end());it = end;try{return make_value<int64_t>(std::stoll(v, nullptr, base));}catch (const std::invalid_argument& ex){throw_parse_exception("Malformed number (invalid argument: "+ std::string{ex.what()} + ")");}catch (const std::out_of_range& ex){throw_parse_exception("Malformed number (out of range: "+ std::string{ex.what()} + ")");}}std::shared_ptr<value<double>> parse_float(std::string::iterator& it,const std::string::iterator& end){std::string v{it, end};v.erase(std::remove(v.begin(), v.end(), '_'), v.end());it = end;char decimal_point = std::localeconv()->decimal_point[0];std::replace(v.begin(), v.end(), '.', decimal_point);try{return make_value<double>(std::stod(v));}catch (const std::invalid_argument& ex){throw_parse_exception("Malformed number (invalid argument: "+ std::string{ex.what()} + ")");}catch (const std::out_of_range& ex){throw_parse_exception("Malformed number (out of range: "+ std::string{ex.what()} + ")");}}std::shared_ptr<value<bool>> parse_bool(std::string::iterator& it,const std::string::iterator& end){auto eat = make_consumer(it, end, [this]() {throw_parse_exception("Attempted to parse invalid boolean value");});if (*it == 't'){eat("true");return make_value<bool>(true);}else if (*it == 'f'){eat("false");return make_value<bool>(false);}eat.error();return nullptr;}std::string::iterator find_end_of_number(std::string::iterator it,std::string::iterator end){auto ret = std::find_if(it, end, [](char c) {return !is_number(c) && c != '_' && c != '.' && c != 'e' && c != 'E'&& c != '-' && c != '+' && c != 'x' && c != 'o' && c != 'b';});if (ret != end && ret + 1 != end && ret + 2 != end){if ((ret[0] == 'i' && ret[1] == 'n' && ret[2] == 'f')|| (ret[0] == 'n' && ret[1] == 'a' && ret[2] == 'n')){ret = ret + 3;}}return ret;}std::string::iterator find_end_of_date(std::string::iterator it,std::string::iterator end){auto end_of_date = std::find_if(it, end, [](char c) {return !is_number(c) && c != '-';});if (end_of_date != end && *end_of_date == ' ' && end_of_date + 1 != end&& is_number(end_of_date[1]))end_of_date++;return std::find_if(end_of_date, end, [](char c) {return !is_number(c) && c != 'T' && c != 'Z' && c != ':'&& c != '-' && c != '+' && c != '.';});}std::string::iterator find_end_of_time(std::string::iterator it,std::string::iterator end){return std::find_if(it, end, [](char c) {return !is_number(c) && c != ':' && c != '.';});}local_time read_time(std::string::iterator& it,const std::string::iterator& end){auto time_end = find_end_of_time(it, end);auto eat = make_consumer(it, time_end, [&]() { throw_parse_exception("Malformed time"); });local_time ltime;ltime.hour = eat.eat_digits(2);eat(':');ltime.minute = eat.eat_digits(2);eat(':');ltime.second = eat.eat_digits(2);int power = 100000;if (it != time_end && *it == '.'){++it;while (it != time_end && is_number(*it)){ltime.microsecond += power * (*it++ - '0');power /= 10;}}if (it != time_end)throw_parse_exception("Malformed time");return ltime;}std::shared_ptr<value<local_time>>parse_time(std::string::iterator& it, const std::string::iterator& end){return make_value(read_time(it, end));}std::shared_ptr<base> parse_date(std::string::iterator& it,const std::string::iterator& end){auto date_end = find_end_of_date(it, end);auto eat = make_consumer(it, date_end, [&]() { throw_parse_exception("Malformed date"); });local_date ldate;ldate.year = eat.eat_digits(4);eat('-');ldate.month = eat.eat_digits(2);eat('-');ldate.day = eat.eat_digits(2);if (it == date_end)return make_value(ldate);eat.eat_or('T', ' ');local_datetime ldt;static_cast<local_date&>(ldt) = ldate;static_cast<local_time&>(ldt) = read_time(it, date_end);if (it == date_end)return make_value(ldt);offset_datetime dt;static_cast<local_datetime&>(dt) = ldt;int hoff = 0;int moff = 0;if (*it == '+' || *it == '-'){auto plus = *it == '+';++it;hoff = eat.eat_digits(2);dt.hour_offset = (plus) ? hoff : -hoff;eat(':');moff = eat.eat_digits(2);dt.minute_offset = (plus) ? moff : -moff;}else if (*it == 'Z'){++it;}if (it != date_end)throw_parse_exception("Malformed date");return make_value(dt);}std::shared_ptr<base> parse_array(std::string::iterator& it,std::string::iterator& end){// this gets ugly because of the "homogeneity" restriction:// arrays can either be of only one type, or contain arrays// (each of those arrays could be of different types, though)//// because of the latter portion, we don't really have a choice// but to represent them as arrays of base values...++it;// ugh---have to read the first value to determine array type...skip_whitespace_and_comments(it, end);// edge case---empty arrayif (*it == ']'){++it;return make_array();}auto val_end = std::find_if(it, end, [](char c) { return c == ',' || c == ']' || c == '#'; });parse_type type = determine_value_type(it, val_end);switch (type){case parse_type::STRING:return parse_value_array<std::string>(it, end);case parse_type::LOCAL_TIME:return parse_value_array<local_time>(it, end);case parse_type::LOCAL_DATE:return parse_value_array<local_date>(it, end);case parse_type::LOCAL_DATETIME:return parse_value_array<local_datetime>(it, end);case parse_type::OFFSET_DATETIME:return parse_value_array<offset_datetime>(it, end);case parse_type::INT:return parse_value_array<int64_t>(it, end);case parse_type::FLOAT:return parse_value_array<double>(it, end);case parse_type::BOOL:return parse_value_array<bool>(it, end);case parse_type::ARRAY:return parse_object_array<array>(&parser::parse_array, '[', it,end);case parse_type::INLINE_TABLE:return parse_object_array<table_array>(&parser::parse_inline_table, '{', it, end);default:throw_parse_exception("Unable to parse array");}}template <class Value>std::shared_ptr<array> parse_value_array(std::string::iterator& it,std::string::iterator& end){auto arr = make_array();while (it != end && *it != ']'){auto val = parse_value(it, end);if (auto v = val->as<Value>())arr->get().push_back(val);elsethrow_parse_exception("Arrays must be homogeneous");skip_whitespace_and_comments(it, end);if (*it != ',')break;++it;skip_whitespace_and_comments(it, end);}if (it != end)++it;return arr;}template <class Object, class Function>std::shared_ptr<Object> parse_object_array(Function&& fun, char delim,std::string::iterator& it,std::string::iterator& end){auto arr = detail::make_element<Object>();while (it != end && *it != ']'){if (*it != delim)throw_parse_exception("Unexpected character in array");arr->get().push_back(((*this).*fun)(it, end));skip_whitespace_and_comments(it, end);if (it == end || *it != ',')break;++it;skip_whitespace_and_comments(it, end);}if (it == end || *it != ']')throw_parse_exception("Unterminated array");++it;return arr;}std::shared_ptr<table> parse_inline_table(std::string::iterator& it,std::string::iterator& end){auto tbl = make_table();do{++it;if (it == end)throw_parse_exception("Unterminated inline table");consume_whitespace(it, end);if (it != end && *it != '}'){parse_key_value(it, end, tbl.get());consume_whitespace(it, end);}} while (*it == ',');if (it == end || *it != '}')throw_parse_exception("Unterminated inline table");++it;consume_whitespace(it, end);return tbl;}void skip_whitespace_and_comments(std::string::iterator& start,std::string::iterator& end){consume_whitespace(start, end);while (start == end || *start == '#'){if (!detail::getline(input_, line_))throw_parse_exception("Unclosed array");line_number_++;start = line_.begin();end = line_.end();consume_whitespace(start, end);}}void consume_whitespace(std::string::iterator& it,const std::string::iterator& end){while (it != end && (*it == ' ' || *it == '\t'))++it;}void consume_backwards_whitespace(std::string::iterator& back,const std::string::iterator& front){while (back != front && (*back == ' ' || *back == '\t'))--back;}void eol_or_comment(const std::string::iterator& it,const std::string::iterator& end){if (it != end && *it != '#')throw_parse_exception("Unidentified trailing character '"+ std::string{*it}+ "'---did you forget a '#'?");}bool is_time(const std::string::iterator& it,const std::string::iterator& end){auto time_end = find_end_of_time(it, end);auto len = std::distance(it, time_end);if (len < 8)return false;if (it[2] != ':' || it[5] != ':')return false;if (len > 8)return it[8] == '.' && len > 9;return true;}option<parse_type> date_type(const std::string::iterator& it,const std::string::iterator& end){auto date_end = find_end_of_date(it, end);auto len = std::distance(it, date_end);if (len < 10)return {};if (it[4] != '-' || it[7] != '-')return {};if (len >= 19 && (it[10] == 'T' || it[10] == ' ')&& is_time(it + 11, date_end)){// datetime typeauto time_end = find_end_of_time(it + 11, date_end);if (time_end == date_end)return {parse_type::LOCAL_DATETIME};elsereturn {parse_type::OFFSET_DATETIME};}else if (len == 10){// just a regular datereturn {parse_type::LOCAL_DATE};}return {};}std::istream& input_;std::string line_;std::size_t line_number_ = 0;
};/*** Utility function to parse a file as a TOML file. Returns the root table.* Throws a parse_exception if the file cannot be opened.*/
inline std::shared_ptr<table> parse_file(const std::string& filename)
{
#if defined(BOOST_NOWIDE_FSTREAM_INCLUDED_HPP)boost::nowide::ifstream file{filename.c_str()};
#elif defined(NOWIDE_FSTREAM_INCLUDED_HPP)nowide::ifstream file{filename.c_str()};
#elsestd::ifstream file{filename};
#endifif (!file.is_open())throw parse_exception{filename + " could not be opened for parsing"};parser p{file};return p.parse();
}template <class... Ts>
struct value_accept;template <>
struct value_accept<>
{template <class Visitor, class... Args>static void accept(const base&, Visitor&&, Args&&...){// nothing}
};template <class T, class... Ts>
struct value_accept<T, Ts...>
{template <class Visitor, class... Args>static void accept(const base& b, Visitor&& visitor, Args&&... args){if (auto v = b.as<T>()){visitor.visit(*v, std::forward<Args>(args)...);}else{value_accept<Ts...>::accept(b, std::forward<Visitor>(visitor),std::forward<Args>(args)...);}}
};/*** base implementation of accept() that calls visitor.visit() on the concrete* class.*/
template <class Visitor, class... Args>
void base::accept(Visitor&& visitor, Args&&... args) const
{if (is_value()){using value_acceptor= value_accept<std::string, int64_t, double, bool, local_date,local_time, local_datetime, offset_datetime>;value_acceptor::accept(*this, std::forward<Visitor>(visitor),std::forward<Args>(args)...);}else if (is_table()){visitor.visit(static_cast<const table&>(*this),std::forward<Args>(args)...);}else if (is_array()){visitor.visit(static_cast<const array&>(*this),std::forward<Args>(args)...);}else if (is_table_array()){visitor.visit(static_cast<const table_array&>(*this),std::forward<Args>(args)...);}
}/*** Writer that can be passed to accept() functions of cpptoml objects and* will output valid TOML to a stream.*/
class toml_writer
{public:/*** Construct a toml_writer that will write to the given stream*/toml_writer(std::ostream& s, const std::string& indent_space = "\t"): stream_(s), indent_(indent_space), has_naked_endline_(false){// nothing}public:/*** Output a base value of the TOML tree.*/template <class T>void visit(const value<T>& v, bool = false){write(v);}/*** Output a table element of the TOML tree*/void visit(const table& t, bool in_array = false){write_table_header(in_array);std::vector<std::string> values;std::vector<std::string> tables;for (const auto& i : t){if (i.second->is_table() || i.second->is_table_array()){tables.push_back(i.first);}else{values.push_back(i.first);}}for (unsigned int i = 0; i < values.size(); ++i){path_.push_back(values[i]);if (i > 0)endline();write_table_item_header(*t.get(values[i]));t.get(values[i])->accept(*this, false);path_.pop_back();}for (unsigned int i = 0; i < tables.size(); ++i){path_.push_back(tables[i]);if (values.size() > 0 || i > 0)endline();write_table_item_header(*t.get(tables[i]));t.get(tables[i])->accept(*this, false);path_.pop_back();}endline();}/*** Output an array element of the TOML tree*/void visit(const array& a, bool = false){write("[");for (unsigned int i = 0; i < a.get().size(); ++i){if (i > 0)write(", ");if (a.get()[i]->is_array()){a.get()[i]->as_array()->accept(*this, true);}else{a.get()[i]->accept(*this, true);}}write("]");}/*** Output a table_array element of the TOML tree*/void visit(const table_array& t, bool = false){for (unsigned int j = 0; j < t.get().size(); ++j){if (j > 0)endline();t.get()[j]->accept(*this, true);}endline();}/*** Escape a string for output.*/static std::string escape_string(const std::string& str){std::string res;for (auto it = str.begin(); it != str.end(); ++it){if (*it == '\b'){res += "\\b";}else if (*it == '\t'){res += "\\t";}else if (*it == '\n'){res += "\\n";}else if (*it == '\f'){res += "\\f";}else if (*it == '\r'){res += "\\r";}else if (*it == '"'){res += "\\\"";}else if (*it == '\\'){res += "\\\\";}else if (static_cast<uint32_t>(*it) <= UINT32_C(0x001f)){res += "\\u";std::stringstream ss;ss << std::hex << static_cast<uint32_t>(*it);res += ss.str();}else{res += *it;}}return res;}protected:/*** Write out a string.*/void write(const value<std::string>& v){write("\"");write(escape_string(v.get()));write("\"");}/*** Write out a double.*/void write(const value<double>& v){std::stringstream ss;ss << std::showpoint<< std::setprecision(std::numeric_limits<double>::max_digits10)<< v.get();auto double_str = ss.str();auto pos = double_str.find("e0");if (pos != std::string::npos)double_str.replace(pos, 2, "e");pos = double_str.find("e-0");if (pos != std::string::npos)double_str.replace(pos, 3, "e-");stream_ << double_str;has_naked_endline_ = false;}/*** Write out an integer, local_date, local_time, local_datetime, or* offset_datetime.*/template <class T>typename std::enable_if<is_one_of<T, int64_t, local_date, local_time, local_datetime,offset_datetime>::value>::typewrite(const value<T>& v){write(v.get());}/*** Write out a boolean.*/void write(const value<bool>& v){write((v.get() ? "true" : "false"));}/*** Write out the header of a table.*/void write_table_header(bool in_array = false){if (!path_.empty()){indent();write("[");if (in_array){write("[");}for (unsigned int i = 0; i < path_.size(); ++i){if (i > 0){write(".");}if (path_[i].find_first_not_of("ABCDEFGHIJKLMNOPQRSTUVWXYZabcde""fghijklmnopqrstuvwxyz0123456789""_-")== std::string::npos){write(path_[i]);}else{write("\"");write(escape_string(path_[i]));write("\"");}}if (in_array){write("]");}write("]");endline();}}/*** Write out the identifier for an item in a table.*/void write_table_item_header(const base& b){if (!b.is_table() && !b.is_table_array()){indent();if (path_.back().find_first_not_of("ABCDEFGHIJKLMNOPQRSTUVWXYZabcde""fghijklmnopqrstuvwxyz0123456789""_-")== std::string::npos){write(path_.back());}else{write("\"");write(escape_string(path_.back()));write("\"");}write(" = ");}}private:/*** Indent the proper number of tabs given the size of* the path.*/void indent(){for (std::size_t i = 1; i < path_.size(); ++i)write(indent_);}/*** Write a value out to the stream.*/template <class T>void write(const T& v){stream_ << v;has_naked_endline_ = false;}/*** Write an endline out to the stream*/void endline(){if (!has_naked_endline_){stream_ << "\n";has_naked_endline_ = true;}}private:std::ostream& stream_;const std::string indent_;std::vector<std::string> path_;bool has_naked_endline_;
};inline std::ostream& operator<<(std::ostream& stream, const base& b)
{toml_writer writer{stream};b.accept(writer);return stream;
}template <class T>
std::ostream& operator<<(std::ostream& stream, const value<T>& v)
{toml_writer writer{stream};v.accept(writer);return stream;
}inline std::ostream& operator<<(std::ostream& stream, const table& t)
{toml_writer writer{stream};t.accept(writer);return stream;
}inline std::ostream& operator<<(std::ostream& stream, const table_array& t)
{toml_writer writer{stream};t.accept(writer);return stream;
}inline std::ostream& operator<<(std::ostream& stream, const array& a)
{toml_writer writer{stream};a.accept(writer);return stream;
}
} // namespace cpptoml
#endif // CPPTOML_H

二、需要读取的abc.toml

[animals]
a = "cat"
b = "dog"
c = 1[foods]
d = "rice"
e = "dumplings"
f = 2.0

三、读取C++的处理文件read_toml.cpp

#include <iostream>
#include "cpptoml.h"
using namespace std;int main(int argc, char *argv[])
{cout << "开始解析。。。" << endl;auto config = cpptoml::parse_file("/home/joes/catkin_pro/00_catkin_test/src/test01_nav2/cfg/test_tomls.toml");auto a = config->get_qualified_as<string>("animals.a");auto b = config->get_qualified_as<string>("animals.b");auto c = config->get_qualified_as<int>("animals.c");auto d = config->get_qualified_as<string>("foods.d");auto e = config->get_qualified_as<string>("foods.e");auto f = config->get_qualified_as<double>("foods.f");  // 不识别float格式cout << "表格2为: " << endl << *config << endl;cout << "a: " << *a << endl;cout << "b: " << *b << endl;cout << "c: " << *c << endl;cout << "d: " << *d << endl;cout << "e: " << *e << endl;cout << "f: " << *f << endl;return 0;
}

四、输出结果:

C++读取 TOML 格式文件的方法相关推荐

  1. python xlrd读取excel-使用Python xlrd模块读取Excel格式文件的方法

    这是一篇关于如何使用Python xlrd模块读取Excel格式文件的方法的文章,下面的python代码中使用 了xlrd模块的方法,这样就能够很方便的读取 excel 文件内容.同是这个xlrd模块 ...

  2. php生成vcf,php简单读取.vcf格式文件的方法示例

    本文实例讲述了php简单读取.vcf格式文件的方法.分享给大家供大家参考,具体如下: /** * 读取.vcf格式文件 * @param $filename */ function readCvf($ ...

  3. php vcf,php读取 .vcf 格式文件的方法

    这篇文章主要介绍了php读取.vcf格式文件的方法,结合具体实例形式分析了php自定义函数读取vcf格式文件的具体实现方法与相关注意事项,需要的朋友可以参考下 具体如下: /** * 读取.vcf格式 ...

  4. php vcf,php简单读取.vcf格式文件的方法示例

    本文实例讲述了php简单读取.vcf格式文件的方法.分享给大家供大家参考,具体如下: /** * 读取.vcf格式文件 * @param $filename */ function readCvf($ ...

  5. GO读取toml格式文件

    在toml以 # 作为注释,文件内容也是需要有一个 section 其下内容以 key = value 书写,key不能为空,但是可以是空引号,key可以用双引号也可以不用,value是字符串时需要用 ...

  6. c#保存数据格式为.cvs_C#读取csv格式文件的方法

    //读CSV文件类,读取指定的CSV文件,可以导出DataTable public class CsvStreamReader { private ArrayList rowAL;         / ...

  7. python 读取csv文件转成字符串,python实现csv格式文件转为asc格式文件的方法

    一.背景描述 csv格式文件是一种类似于excel的文件格式 asc格式文件是一种可以用text打开的文本文件 csv转asc本来可以用arcgis顺利完成,但由于csv数据量太大(744万行),ar ...

  8. unity向服务器发送xml文件格式,Unity读取Excel文件转换XML格式文件的方法

    Unity读取Excel文件转换XML格式文件的方法 发布时间:2020-06-23 09:34:33 来源:亿速云 阅读:107 作者:清晨 不懂Unity读取Excel文件转换XML格式文件的方法 ...

  9. python读取mp4文件失败_Python代码打开本地.mp4格式文件的方法-mp4文件

    Python开发技术的应用相信有不少的小伙伴都有所了解,简单的说那就是非常的强大,Python开发技术的应用是非常广泛的,本篇文章扣丁学堂Python培训小编就给读者们分享一下Python代码打开本地 ...

最新文章

  1. php基础:switch cass控制结构 代替if
  2. 操作系统——存储管理:分区、分页、分段、请求式分页和虚拟内存
  3. 区块链BaaS云服务(28)TOP Network 之业务链和单向状态通道(Layer-2)
  4. SQL提取时间段内数据
  5. cprintdialog预览_怎样用CPrintDialog来实现打印功能
  6. 价值199的wp移植Emlog主题模板PandaPRO
  7. C++之再探参数绑定bind、bind1st、bind2nd、placeholders占位符
  8. BeautifulSoup库findAll()、find()方法详解
  9. 概念模型——分析模式学习笔记
  10. JMeter分布式负载测试(吞吐量控制器)
  11. [译] Bulma: 2018年你应该关注的CSS框架
  12. NEFU计算机组成原理课程设计之乘法器
  13. 深度学习与计算机视觉教程(2) | 图像分类与机器学习基础(CV通关指南·完结)
  14. 文本特征提取方法介绍
  15. 如何关闭445端口 两种方式教你关闭445端口
  16. 轻量级神经网络架构综述
  17. 我从华为身上学到的项目管理经验 -- 设计篇
  18. python足球联赛赛程_足球联赛赛程表生成
  19. 区块链与金融IT“联姻”的思路和方案
  20. 使用Git将文件上传到暂存区

热门文章

  1. 3d建模真的很累吗?前景怎么样?
  2. android -xmx,关于android:jvmargs = -xmx2048m dex正在处理cordova
  3. Redis主从复制的讲解
  4. coderwhy前端学习笔记七
  5. 事项ON丰宁坝上草原
  6. 使用CSS将多余文字显示成省略号
  7. 视频教程-React Hooks 案例详解(React 进阶必备)-其他
  8. 2008中国软件外包研发竞争力十强企业
  9. 【有利可图网】PS实战系列:人像照片添加渐变逆光效果
  10. 【R语言】用R写for循环批量绘制生存曲线,肠子都悔青了