#ifndef TOML11_VALUE #define TOML11_VALUE #include "traits.hpp" #include "utility.hpp" #include "exception.hpp" #include "types.hpp" #include #include #include #include #include namespace toml { namespace detail { 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(std::is_same::value || std::is_same::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; }; template constexpr value_t value_traits::type_index; template constexpr bool value_traits::is_toml_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()) { constexpr value_t kind = toml::detail::check_type(); switch_assign::invoke(*this, std::forward(v)); } template::is_toml_type, std::nullptr_t>::type> value& value::operator=(T&& v) { constexpr value_t kind = toml::detail::check_type(); if(should_be_cleaned(this->type_)) { switch_clean(this->type_); } this->type_ = kind; 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