#ifndef TOML11_GET_HPP #define TOML11_GET_HPP #include #include "from.hpp" #include "types.hpp" #include "value.hpp" #include "version.hpp" #if defined(TOML11_HAS_STRING_VIEW) #include #endif // string_view namespace toml { inline namespace TOML11_INLINE_VERSION_NAMESPACE { // ============================================================================ // T is toml::value; identity transformation. template cxx::enable_if_t>::value, T>& get(basic_value& v) { return v; } template cxx::enable_if_t>::value, T> const& get(const basic_value& v) { return v; } template cxx::enable_if_t>::value, T> get(basic_value&& v) { return basic_value(std::move(v)); } // ============================================================================ // exact toml::* type template cxx::enable_if_t>::value, T> & get(basic_value& v) { constexpr auto ty = detail::type_to_enum>::value; return detail::getter::get(v); } template cxx::enable_if_t>::value, T> const& get(const basic_value& v) { constexpr auto ty = detail::type_to_enum>::value; return detail::getter::get(v); } template cxx::enable_if_t>::value, T> get(basic_value&& v) { constexpr auto ty = detail::type_to_enum>::value; return detail::getter::get(std::move(v)); } // ============================================================================ // T is toml::basic_value template cxx::enable_if_t, cxx::negation>> >::value, T> get(basic_value v) { return T(std::move(v)); } // ============================================================================ // integer convertible from toml::value::integer_type template cxx::enable_if_t, cxx::negation>, detail::is_not_toml_type>, cxx::negation>, cxx::negation> >::value, T> get(const basic_value& v) { return static_cast(v.as_integer()); } // ============================================================================ // floating point convertible from toml::value::floating_type template cxx::enable_if_t, detail::is_not_toml_type>, cxx::negation>, cxx::negation> >::value, T> get(const basic_value& v) { return static_cast(v.as_floating()); } // ============================================================================ // std::string with different char/trait/allocator template cxx::enable_if_t>, detail::is_1byte_std_basic_string >::value, T> get(const basic_value& v) { return detail::string_conv>(v.as_string()); } // ============================================================================ // std::string_view #if defined(TOML11_HAS_STRING_VIEW) template cxx::enable_if_t::string_type>::value, T> get(const basic_value& v) { return T(v.as_string()); } #endif // string_view // ============================================================================ // std::chrono::duration from toml::local_time template cxx::enable_if_t::value, T> get(const basic_value& v) { return std::chrono::duration_cast( std::chrono::nanoseconds(v.as_local_time())); } // ============================================================================ // std::chrono::system_clock::time_point from toml::datetime variants template cxx::enable_if_t< std::is_same::value, T> get(const basic_value& v) { switch(v.type()) { case value_t::local_date: { return std::chrono::system_clock::time_point(v.as_local_date()); } case value_t::local_datetime: { return std::chrono::system_clock::time_point(v.as_local_datetime()); } case value_t::offset_datetime: { return std::chrono::system_clock::time_point(v.as_offset_datetime()); } default: { const auto loc = v.location(); throw type_error(format_error("toml::get: " "bad_cast to std::chrono::system_clock::time_point", loc, "the actual type is " + to_string(v.type())), loc); } } } // ============================================================================ // forward declaration to use this recursively. ignore this and go ahead. // array-like (w/ push_back) template cxx::enable_if_t, // T is a container detail::has_push_back_method, // .push_back() works detail::is_not_toml_type>, // but not toml::array cxx::negation>, // but not std::basic_string #if defined(TOML11_HAS_STRING_VIEW) cxx::negation>, // but not std::basic_string_view #endif cxx::negation>, // no T.from_toml() cxx::negation>, // no toml::from cxx::negation&>> >::value, T> get(const basic_value&); // std::array template cxx::enable_if_t::value, T> get(const basic_value&); // std::forward_list template cxx::enable_if_t::value, T> get(const basic_value&); // std::pair template cxx::enable_if_t::value, T> get(const basic_value&); // std::tuple template cxx::enable_if_t::value, T> get(const basic_value&); // std::map (key is convertible from toml::value::key_type) template cxx::enable_if_t, // T is map detail::is_not_toml_type>, // but not toml::table std::is_convertible::key_type, typename T::key_type>, // keys are convertible cxx::negation>, // no T.from_toml() cxx::negation>, // no toml::from cxx::negation&>> >::value, T> get(const basic_value& v); // std::map (key is not convertible from toml::value::key_type, but // is a std::basic_string) template cxx::enable_if_t, // T is map detail::is_not_toml_type>, // but not toml::table cxx::negation::key_type, typename T::key_type>>, // keys are NOT convertible detail::is_1byte_std_basic_string, // is std::basic_string cxx::negation>, // no T.from_toml() cxx::negation>, // no toml::from cxx::negation&>> >::value, T> get(const basic_value& v); // toml::from::from_toml(v) template cxx::enable_if_t::value, T> get(const basic_value&); // has T.from_toml(v) but no from template cxx::enable_if_t, // has T.from_toml() cxx::negation>, // no toml::from std::is_default_constructible // T{} works >::value, T> get(const basic_value&); // T(const toml::value&) and T is not toml::basic_value, // and it does not have `from` nor `from_toml`. template cxx::enable_if_t&>, // has T(const basic_value&) cxx::negation>, // but not basic_value itself cxx::negation>, // no .from_toml() cxx::negation> // no toml::from >::value, T> get(const basic_value&); // ============================================================================ // array-like types; most likely STL container, like std::vector, etc. template cxx::enable_if_t, // T is a container detail::has_push_back_method, // .push_back() works detail::is_not_toml_type>, // but not toml::array cxx::negation>, // but not std::basic_string #if defined(TOML11_HAS_STRING_VIEW) cxx::negation>, // but not std::basic_string_view #endif cxx::negation>, // no T.from_toml() cxx::negation>, // no toml::from cxx::negation&>> >::value, T> get(const basic_value& v) { using value_type = typename T::value_type; const auto& a = v.as_array(); T container; detail::try_reserve(container, a.size()); // if T has .reserve(), call it for(const auto& elem : a) { container.push_back(get(elem)); } return container; } // ============================================================================ // std::array template cxx::enable_if_t::value, T> get(const basic_value& v) { using value_type = typename T::value_type; const auto& a = v.as_array(); T container; if(a.size() != container.size()) { const auto loc = v.location(); throw std::out_of_range(format_error("toml::get: while converting to an array: " " array size is " + std::to_string(container.size()) + " but there are " + std::to_string(a.size()) + " elements in toml array.", loc, "here")); } for(std::size_t i=0; i(a.at(i)); } return container; } // ============================================================================ // std::forward_list template cxx::enable_if_t::value, T> get(const basic_value& v) { using value_type = typename T::value_type; T container; for(const auto& elem : v.as_array()) { container.push_front(get(elem)); } container.reverse(); return container; } // ============================================================================ // std::pair template cxx::enable_if_t::value, T> get(const basic_value& v) { using first_type = typename T::first_type; using second_type = typename T::second_type; const auto& ar = v.as_array(); if(ar.size() != 2) { const auto loc = v.location(); throw std::out_of_range(format_error("toml::get: while converting std::pair: " " but there are " + std::to_string(ar.size()) + " > 2 elements in toml array.", loc, "here")); } return std::make_pair(::toml::get(ar.at(0)), ::toml::get(ar.at(1))); } // ============================================================================ // std::tuple. namespace detail { template T get_tuple_impl(const Array& a, cxx::index_sequence) { return std::make_tuple( ::toml::get::type>(a.at(I))...); } } // detail template cxx::enable_if_t::value, T> get(const basic_value& v) { const auto& ar = v.as_array(); if(ar.size() != std::tuple_size::value) { const auto loc = v.location(); throw std::out_of_range(format_error("toml::get: while converting std::tuple: " " there are " + std::to_string(ar.size()) + " > " + std::to_string(std::tuple_size::value) + " elements in toml array.", loc, "here")); } return detail::get_tuple_impl(ar, cxx::make_index_sequence::value>{}); } // ============================================================================ // std::unordered_set template cxx::enable_if_t::value, T> get(const basic_value& v) { using value_type = typename T::value_type; const auto& a = v.as_array(); T container; for (const auto& elem : a) { container.insert(get(elem)); } return container; } // ============================================================================ // map-like types; most likely STL map, like std::map or std::unordered_map. // key is convertible from toml::value::key_type template cxx::enable_if_t, // T is map detail::is_not_toml_type>, // but not toml::table std::is_convertible::key_type, typename T::key_type>, // keys are convertible cxx::negation>, // no T.from_toml() cxx::negation>, // no toml::from cxx::negation&>> >::value, T> get(const basic_value& v) { using key_type = typename T::key_type; using mapped_type = typename T::mapped_type; static_assert( std::is_convertible::key_type, key_type>::value, "toml::get only supports map type of which key_type is " "convertible from toml::basic_value::key_type."); T m; for(const auto& kv : v.as_table()) { m.emplace(key_type(kv.first), get(kv.second)); } return m; } // key is NOT convertible from toml::value::key_type but std::basic_string template cxx::enable_if_t, // T is map detail::is_not_toml_type>, // but not toml::table cxx::negation::key_type, typename T::key_type>>, // keys are NOT convertible detail::is_1byte_std_basic_string, // is std::basic_string cxx::negation>, // no T.from_toml() cxx::negation>, // no toml::from cxx::negation&>> >::value, T> get(const basic_value& v) { using key_type = typename T::key_type; using mapped_type = typename T::mapped_type; T m; for(const auto& kv : v.as_table()) { m.emplace(detail::string_conv(kv.first), get(kv.second)); } return m; } // ============================================================================ // user-defined, but convertible types. // toml::from template cxx::enable_if_t::value, T> get(const basic_value& v) { return ::toml::from::from_toml(v); } // has T.from_toml(v) but no from template cxx::enable_if_t, // has T.from_toml() cxx::negation>, // no toml::from std::is_default_constructible // T{} works >::value, T> get(const basic_value& v) { T ud; ud.from_toml(v); return ud; } // T(const toml::value&) and T is not toml::basic_value, // and it does not have `from` nor `from_toml`. template cxx::enable_if_t&>, // has T(const basic_value&) cxx::negation>, // but not basic_value itself cxx::negation>, // no .from_toml() cxx::negation> // no toml::from >::value, T> get(const basic_value& v) { return T(v); } // ============================================================================ // get_or(value, fallback) template cxx::enable_if_t::value, basic_value> const& get_or(const basic_value& v, const basic_value&) { return v; } template cxx::enable_if_t::value, basic_value>& get_or(basic_value& v, basic_value&) { return v; } template cxx::enable_if_t::value, basic_value> get_or(basic_value&& v, basic_value&&) { return v; } // ---------------------------------------------------------------------------- // specialization for the exact toml types (return type becomes lvalue ref) template cxx::enable_if_t< detail::is_exact_toml_type>::value, T> const& get_or(const basic_value& v, const T& opt) noexcept { try { return get>(v); } catch(...) { return opt; } } template cxx::enable_if_t>, detail::is_exact_toml_type> >::value, T>& get_or(basic_value& v, T& opt) noexcept { try { return get>(v); } catch(...) { return opt; } } template cxx::enable_if_t, basic_value>::value, cxx::remove_cvref_t> get_or(basic_value&& v, T&& opt) noexcept { try { return get>(std::move(v)); } catch(...) { return cxx::remove_cvref_t(std::forward(opt)); } } // ---------------------------------------------------------------------------- // specialization for string literal // template // typename basic_value::string_type // get_or(const basic_value& v, // const typename basic_value::string_type::value_type (&opt)[N]) // { // try // { // return v.as_string(); // } // catch(...) // { // return typename basic_value::string_type(opt); // } // } // // The above only matches to the literal, like `get_or(v, "foo");` but not // ```cpp // const auto opt = "foo"; // const auto str = get_or(v, opt); // ``` // . And the latter causes an error. // To match to both `"foo"` and `const auto opt = "foo"`, we take a pointer to // a character here. template typename basic_value::string_type get_or(const basic_value& v, const typename basic_value::string_type::value_type* opt) { try { return v.as_string(); } catch(...) { return typename basic_value::string_type(opt); } } // ---------------------------------------------------------------------------- // others (require type conversion and return type cannot be lvalue reference) template cxx::enable_if_t>, cxx::negation>>, cxx::negation, typename basic_value::string_type::value_type const*>> >::value, cxx::remove_cvref_t> get_or(const basic_value& v, T&& opt) { try { return get>(v); } catch(...) { return cxx::remove_cvref_t(std::forward(opt)); } } } // TOML11_INLINE_VERSION_NAMESPACE } // toml #endif // TOML11_GET_HPP