#ifndef TOML11_VALUE #define TOML11_VALUE #include "datetime.hpp" #include "traits.hpp" #include "utility.hpp" #include "exception.hpp" #include #include #include #include #include namespace toml { using character = char; class value; using key = std::basic_string; using Boolean = bool; using Integer = std::int64_t; using Float = double; using String = std::basic_string; using Datetime = basic_datetime; using Array = std::vector; using Table = std::unordered_map; enum class value_t : std::uint8_t { Boolean = 1, Integer = 2, Float = 3, String = 4, Datetime = 5, Array = 6, Table = 7, Empty = 0, Unknown = 255, }; 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 constexpr inline value_t check_type() { return std::is_same, toml::Boolean >::value ? value_t::Boolean : std::is_integral>::value ? value_t::Integer : std::is_floating_point>::value ? value_t::Float : std::is_convertible, toml::String >::value ? value_t::String : std::is_convertible, toml::Datetime>::value ? value_t::Datetime: std::is_convertible, toml::Array >::value ? value_t::Array : std::is_convertible, toml::Table >::value ? value_t::Table : value_t::Unknown; } constexpr inline bool is_valid(value_t vt) { return vt != value_t::Unknown; } 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 {}; struct is_key_convertible_impl { template static std::is_convertible check(typename T::key_type*); template static std::false_type check(...); }; /// Intel C++ compiler can not use decltype in parent class declaration, here /// is a hack to work around it. https://stackoverflow.com/a/23953090/4692076 #ifdef __INTEL_COMPILER #define decltype(...) std::enable_if::type #endif template struct is_key_convertible : decltype(is_key_convertible_impl::check(nullptr)){}; #ifdef __INTEL_COMPILER #undef decltype(...) #endif 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() noexcept 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 struct value_traits { constexpr static value_t type_index = detail::check_type(); constexpr static bool is_toml_type = detail::is_valid(detail::check_type()); 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); template::is_toml_type, std::nullptr_t>::type = nullptr> value(std::initializer_list init); value(std::initializer_list> init); 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::is_toml_type, std::nullptr_t>::type> value::value(std::initializer_list init) : type_(toml::value_t::Array) { toml::Array arr; arr.reserve(init.size()); for(auto&& item : init) arr.emplace_back(std::move(item)); switch_assign::invoke(*this, std::move(arr)); } inline value::value( std::initializer_list> init) : type_(toml::value_t::Table) { toml::Table tmp; for(auto&& item : init) tmp.emplace(std::move(item.first), std::move(item.second)); switch_assign::invoke(*this, std::move(tmp)); } 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); } inline bool operator==(const toml::value& lhs, const toml::value& rhs) { if(lhs.type() != rhs.type()) return false; switch(lhs.type()) { case value_t::Boolean : return lhs.cast() == rhs.cast(); case value_t::Integer : return lhs.cast() == rhs.cast(); case value_t::Float : return lhs.cast() == rhs.cast(); case value_t::String : return lhs.cast() == rhs.cast(); case value_t::Datetime: return lhs.cast() == rhs.cast(); case value_t::Array : return lhs.cast() == rhs.cast(); case value_t::Table : return lhs.cast() == rhs.cast(); case value_t::Empty : return true; case value_t::Unknown : return false; default: return false; } } inline bool operator<(const toml::value& lhs, const toml::value& rhs) { if(lhs.type() != rhs.type()) return (lhs.type() < rhs.type()); switch(lhs.type()) { case value_t::Boolean : return lhs.cast() < rhs.cast(); case value_t::Integer : return lhs.cast() < rhs.cast(); case value_t::Float : return lhs.cast() < rhs.cast(); case value_t::String : return lhs.cast() < rhs.cast(); case value_t::Datetime: return lhs.cast() < rhs.cast(); case value_t::Array : return lhs.cast() < rhs.cast(); case value_t::Table : return lhs.cast() < rhs.cast(); case value_t::Empty : return false; case value_t::Unknown : return false; default: return false; } } inline bool operator!=(const toml::value& lhs, const toml::value& rhs) { return !(lhs == rhs); } inline bool operator<=(const toml::value& lhs, const toml::value& rhs) { return (lhs < rhs) || (lhs == rhs); } inline bool operator>(const toml::value& lhs, const toml::value& rhs) { return !(lhs <= rhs); } inline bool operator>=(const toml::value& lhs, const toml::value& rhs) { return !(lhs < rhs); } }// toml #endif// TOML11_VALUE