/* * The MIT License (MIT) * * Copyright (c) 2017 Toru Niina * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ #ifndef TOML_FOR_MODERN_CPP #define TOML_FOR_MODERN_CPP #include #include #include #include #include #include #include #include #include #include #include namespace toml { class value; using key = std::string; using Boolean = bool; using Integer = std::int64_t; using Float = double; using String = std::string; using Datetime = std::chrono::system_clock::time_point; using Array = std::vector; using Table = std::unordered_map; enum class value_t { Boolean, Integer, Float, String, Datetime, Array, Table, Empty, Unknown, }; template inline std::basic_ostream& operator<<(std::basic_ostream& os, value_t t) { switch(t) { case toml::value_t::Boolean : os << "Boolean"; return os; case toml::value_t::Integer : os << "Integer"; return os; case toml::value_t::Float : os << "Float"; return os; case toml::value_t::String : os << "String"; return os; case toml::value_t::Datetime: os << "Datetime"; return os; case toml::value_t::Array : os << "Array"; return os; case toml::value_t::Table : os << "Table"; return os; case toml::value_t::Empty : os << "Empty"; return os; case toml::value_t::Unknown : os << "Unknown"; return os; default : os << "Nothing"; return os; } } template, typename alloc = std::allocator> inline std::basic_string stringize(value_t t) { switch(t) { case toml::value_t::Boolean : return "Boolean"; case toml::value_t::Integer : return "Integer"; case toml::value_t::Float : return "Float"; case toml::value_t::String : return "String"; case toml::value_t::Datetime: return "Datetime"; case toml::value_t::Array : return "Array"; case toml::value_t::Table : return "Table"; case toml::value_t::Empty : return "Empty"; case toml::value_t::Unknown : return "Unknown"; default : return "Nothing"; } } namespace detail { template using unwrap_t = typename std::decay::type; template struct is_toml_array : std::false_type{}; template<> struct is_toml_array : std::true_type {}; template struct is_toml_table : std::false_type{}; template<> struct is_toml_table : std::true_type {}; template constexpr inline value_t check_type() { return std::is_same, bool>::value ? value_t::Boolean : std::is_integral>::value ? value_t::Integer : std::is_floating_point>::value ? value_t::Float : std::is_same, String>::value ? value_t::String : std::is_same, const char*>::value ? value_t::String : toml::detail::is_toml_array>::value ? value_t::Array : toml::detail::is_toml_table>::value ? value_t::Table : value_t::Unknown; } template struct toml_default_type{}; template<> struct toml_default_type{typedef Boolean type;}; template<> struct toml_default_type{typedef Integer type;}; template<> struct toml_default_type{typedef Float type;}; template<> struct toml_default_type{typedef String type;}; template<> struct toml_default_type{typedef Datetime type;}; template<> struct toml_default_type{typedef Array type;}; template<> struct toml_default_type{typedef Table type;}; template<> struct toml_default_type{typedef void type;}; template<> struct toml_default_type{typedef void type;}; struct storage_base { storage_base(): type(toml::value_t::Empty){} storage_base(toml::value_t t): type(t){} virtual ~storage_base() = default; toml::value_t type; }; template struct storage : public storage_base { static_assert(is_toml_array::value || is_toml_table::value, "toml::detail::storage is for toml::Array or toml::Table!"); typedef T value_type; storage() = default; ~storage() override = default; storage(storage const&) = default; storage(storage&&) = default; storage& operator=(storage const&) = default; storage& operator=(storage&&) = default; storage(value_type const& v) : value(v){} storage(value_type&& v) : value(std::move(v)){} value_type value; }; } // detail /* -------------------------------------------------------------------------- */ template inline std::unique_ptr make_unique(Ts&& ... args) { return std::unique_ptr(new T(std::forward(args)...)); } struct exception : public std::exception { public: virtual ~exception() override = default; virtual const char* what() const noexcept override {return "";} }; struct syntax_error : public toml::exception { public: explicit syntax_error(const std::string& what_arg) : what_(what_arg){} explicit syntax_error(const char* what_arg) : what_(what_arg){} virtual ~syntax_error() override = default; virtual const char* what() const noexcept override {return what_.c_str();} protected: std::string what_; }; struct type_error : public toml::exception { public: explicit type_error(const std::string& what_arg) : what_(what_arg){} explicit type_error(const char* what_arg) : what_(what_arg){} virtual ~type_error() override = default; virtual const char* what() const noexcept override {return what_.c_str();} protected: std::string what_; }; struct internal_error : public toml::exception { public: explicit internal_error(const std::string& what_arg) : what_(what_arg){} explicit internal_error(const char* what_arg) : what_(what_arg){} virtual ~internal_error() override = default; virtual const char* what() const noexcept override {return what_.c_str();} protected: std::string what_; }; /* -------------------------------------------------------------------------- */ template struct value_traits { constexpr static value_t type_index = detail::check_type(); constexpr static bool is_toml_type = (type_index != value_t::Unknown); typedef typename detail::toml_default_type::type type; }; class value { typedef std::unique_ptr storage_ptr; public: value() : type_(value_t::Empty){} ~value(); value(const value& v); value(value&& v); value& operator=(const value& v); value& operator=(value&& v); template::is_toml_type, std::nullptr_t>::type = nullptr> value(T&& v); template::is_toml_type, std::nullptr_t>::type = nullptr> value& operator=(T&& v); value_t type() const {return type_;} template typename detail::toml_default_type::type const& cast() const; template typename detail::toml_default_type::type& cast(); private: void switch_clean(value_t t); template struct switch_assign; template struct switch_cast; static bool should_be_cleaned(value_t vt) { return (vt == value_t::String) || (vt == value_t::Array) || (vt == value_t::Table) || (vt == value_t::Datetime); } private: value_t type_; union { Boolean boolean_; Integer integer_; Float float_; String string_; Datetime datetime_; storage_ptr storage_; //ptr to table or array }; }; template<> struct value::switch_assign { template static void invoke(value& v, valT&& val) { v.boolean_ = static_cast(val); } }; template<> struct value::switch_assign { template static void invoke(value& v, valT&& val) { v.integer_ = static_cast(val); } }; template<> struct value::switch_assign { template static void invoke(value& v, valT&& val) { v.float_ = static_cast(val); } }; template<> struct value::switch_assign { template static void invoke(value& v, valT&& val) { new(&v.string_) String(val); } }; template<> struct value::switch_assign { template static void invoke(value& v, valT&& val) { new(&v.datetime_) Datetime(val); } }; template<> struct value::switch_assign { template static void invoke(value& v, valT&& val) { new(&v.storage_) storage_ptr( toml::make_unique>(val)); } }; template<> struct value::switch_assign { template static void invoke(value& v, valT&& val) { new(&v.storage_) storage_ptr( toml::make_unique>(val)); } }; template<> struct value::switch_cast { static Boolean& invoke(value& v) {return v.boolean_;} static Boolean const& invoke(value const& v) {return v.boolean_;} }; template<> struct value::switch_cast { static Integer& invoke(value& v) {return v.integer_;} static Integer const& invoke(value const& v) {return v.integer_;} }; template<> struct value::switch_cast { static Float& invoke(value& v) {return v.float_;} static Float const& invoke(value const& v) {return v.float_;} }; template<> struct value::switch_cast { static String& invoke(value& v) {return v.string_;} static String const& invoke(value const& v) {return v.string_;} }; template<> struct value::switch_cast { static Datetime& invoke(value& v) {return v.datetime_;} static Datetime const& invoke(value const& v) {return v.datetime_;} }; template<> struct value::switch_cast { // switch_cast assumes tmeplate argument is correct. // if not, the behaviour is undefined. static Array& invoke(value& v) { return static_cast*>(v.storage_.get())->value; } static Array const& invoke(value const& v) { return static_cast*>(v.storage_.get())->value; } }; template<> struct value::switch_cast { static Table& invoke(value& v) { return static_cast*>(v.storage_.get())->value; } static Table const& invoke(value const& v) { return static_cast*>(v.storage_.get())->value; } }; inline void value::switch_clean(value_t t) { switch(t) { case value_t::Boolean : {boolean_.~Boolean(); return;} case value_t::Integer : {integer_.~Integer(); return;} case value_t::Float : {float_.~Float(); return;} case value_t::String : {string_.~String(); return;} case value_t::Datetime : {datetime_.~Datetime(); return;} case value_t::Array : {storage_.~storage_ptr(); return;} case value_t::Table : {storage_.~storage_ptr(); return;} case value_t::Empty : return; case value_t::Unknown : assert(false); default : assert(false); } } inline value::~value() { switch_clean(this->type_); } inline value::value(const value& v) : type_(v.type()) { switch(v.type()) { case value_t::Boolean : { switch_assign::invoke( *this, v.cast()); break; } case value_t::Integer : { switch_assign::invoke( *this, v.cast()); break; } case value_t::Float : { switch_assign::invoke( *this, v.cast()); break; } case value_t::String : { switch_assign::invoke( *this, v.cast()); break; } case value_t::Datetime: { switch_assign::invoke( *this, v.cast()); break; } case value_t::Array : { switch_assign::invoke( *this, v.cast()); break; } case value_t::Table : { switch_assign::invoke( *this, v.cast()); break; } case value_t::Empty : break; case value_t::Unknown : assert(false); default: assert(false); } } inline value::value(value&& v) { this->type_ = v.type_; switch(this->type_) { case value_t::Boolean : { switch_assign::invoke(*this, std::move(v.cast())); break; } case value_t::Integer : { switch_assign::invoke(*this, std::move(v.cast())); break; } case value_t::Float : { switch_assign::invoke(*this, std::move(v.cast())); break; } case value_t::String : { switch_assign::invoke(*this, std::move(v.cast())); break; } case value_t::Datetime: { switch_assign::invoke(*this, std::move(v.cast())); break; } case value_t::Array : { new(&this->storage_) storage_ptr(std::move(v.storage_)); break; } case value_t::Table : { new(&this->storage_) storage_ptr(std::move(v.storage_)); break; } case value_t::Empty : break; case value_t::Unknown : assert(false); default: assert(false); } } inline value& value::operator=(const value& v) { if(should_be_cleaned(this->type_)) { this->switch_clean(this->type_); } this->type_ = v.type(); switch(this->type_) { case value_t::Boolean : { switch_assign::invoke(*this, v.cast()); break; } case value_t::Integer : { switch_assign::invoke(*this, v.cast()); break; } case value_t::Float : { switch_assign::invoke(*this, v.cast()); break; } case value_t::String : { switch_assign::invoke(*this, v.cast()); break; } case value_t::Datetime: { switch_assign::invoke(*this, v.cast()); break; } case value_t::Array : { switch_assign::invoke(*this, v.cast()); break; } case value_t::Table : { switch_assign::invoke(*this, v.cast()); break; } case value_t::Empty : break; case value_t::Unknown : assert(false); default: assert(false); } return *this; } inline value& value::operator=(value&& v) { if(should_be_cleaned(this->type_)) { this->switch_clean(this->type_); } this->type_ = v.type_; switch(this->type_) { case value_t::Boolean : { switch_assign::invoke(*this, std::move(v.cast())); break; } case value_t::Integer : { switch_assign::invoke(*this, std::move(v.cast())); break; } case value_t::Float : { switch_assign::invoke(*this, std::move(v.cast())); break; } case value_t::String : { switch_assign::invoke(*this, std::move(v.cast())); break; } case value_t::Datetime: { switch_assign::invoke(*this, std::move(v.cast())); break; } case value_t::Array : { switch_assign::invoke(*this, std::move(v.cast())); break; } case value_t::Table : { switch_assign::invoke(*this, std::move(v.cast())); break; } case value_t::Empty : break; case value_t::Unknown : assert(false); default: assert(false); } return *this; } template::is_toml_type, std::nullptr_t>::type> value::value(T&& v) : type_(toml::detail::check_type()) { switch_assign()>::invoke( *this, std::forward(v)); } template::is_toml_type, std::nullptr_t>::type> value& value::operator=(T&& v) { if(should_be_cleaned(this->type_)) { switch_clean(this->type_); } this->type_ = toml::detail::check_type(); switch_assign()>::invoke( *this, std::forward(v)); return *this; } template inline typename detail::toml_default_type::type const& value::cast() const { if(T != this->type_) throw type_error("current type: " + stringize(this->type_) + std::string(" is not query type: ") + stringize(T)); return switch_cast::invoke(*this); } template inline typename detail::toml_default_type::type& value::cast() { if(T != this->type_) throw type_error("current type: " + stringize(this->type_) + std::string(" is not query type: ") + stringize(T)); return switch_cast::invoke(*this); } /* -------------------------------------------------------------------------- */ namespace detail { struct has_iterator_impl { template static std::true_type check(typename T::iterator*); template static std::false_type check(...); }; struct has_value_type_impl { template static std::true_type check(typename T::value_type*); template static std::false_type check(...); }; struct has_key_type_impl { template static std::true_type check(typename T::key_type*); template static std::false_type check(...); }; struct has_mapped_type_impl { template static std::true_type check(typename T::mapped_type*); template static std::false_type check(...); }; template struct has_iterator : decltype(has_iterator_impl::check(nullptr)){}; template struct has_value_type : decltype(has_value_type_impl::check(nullptr)){}; template struct has_key_type : decltype(has_key_type_impl::check(nullptr)){}; template struct has_mapped_type : decltype(has_mapped_type_impl::check(nullptr)){}; template struct is_container : std::integral_constant::value && has_value_type::value>{}; template struct is_map : std::integral_constant::value && has_key_type::value && has_mapped_type::value>{}; struct is_key_convertible_impl { template static std::is_convertible check(typename T::key_type*); template static std::false_type check(...); }; template struct is_key_convertible : decltype(is_key_convertible_impl::check(nullptr)){}; } // detail template struct is_castable : std::false_type{}; template struct is_castable : std::integral_constant::value>{}; template struct is_castable : std::integral_constant::value>{}; template struct is_castable : std::integral_constant::value>{}; template struct is_castable : std::integral_constant::value>{}; template struct is_castable : std::integral_constant::value>{}; template struct is_castable : std::integral_constant::value || toml::detail::is_container::value>{}; template struct is_castable : std::integral_constant::value && toml::detail::is_key_convertible::value>{}; template struct is_castable : std::true_type{};// XXX }// toml #endif// TOML_FOR_MODERN_CPP