diff --git a/doc/externals.hpp b/doc/externals.hpp index 7d803797d..5a057dc7c 100644 --- a/doc/externals.hpp +++ b/doc/externals.hpp @@ -212,6 +212,11 @@ struct bad_alloc {}; /// @see https://en.cppreference.com/w/cpp/container/map struct map {}; +/// !EXTERNAL! +/// +/// @see https://en.cppreference.com/w/cpp/container/multimap +struct multimap {}; + /// !EXTERNAL! /// /// @see https://en.cppreference.com/w/cpp/container/unordered_map diff --git a/doc/pages/reference.adoc b/doc/pages/reference.adoc index 95849d615..92fb2b8c5 100644 --- a/doc/pages/reference.adoc +++ b/doc/pages/reference.adoc @@ -75,24 +75,36 @@ a| *Functions* + <> | *Type Traits* + +<> + +<> + +<> + <> + <> + <> + -<> + -<> + -<> + -<> + -<> + -<> + -<> + -<> + -<> + -<> + +<> + +<> + +<> + +<> + +<> + +<> + +<> + +<> + <> + <> + <> + <> +4+h| Deprecated +| <> + + <> + + <> +| <> + + <> +| <> + + <> + + <> +| <> + + <> |=== include::{entities-file}[leveloffset=+1] diff --git a/include/boost/json/conversion.hpp b/include/boost/json/conversion.hpp index 5774e2b50..c31fdeaec 100644 --- a/include/boost/json/conversion.hpp +++ b/include/boost/json/conversion.hpp @@ -23,6 +23,9 @@ namespace detail { template< class Ctx, class T, class Dir > struct supported_context; +struct no_context +{}; + } // namespace detail /** Customization point tag. @@ -68,6 +71,9 @@ struct try_value_to_tag { }; /** Determine if `T` can be treated like a string during conversions. + @warning This trait is deprecated and **will be removed in Boost 1.92.0.** + Switch to using @ref conversion_category_for and @ref string_category. + Provides the member constant `value` that is equal to `true`, if `T` is convertible to @ref string_view. Otherwise, `value` is equal to `false`. @@ -96,6 +102,9 @@ struct is_string_like; /** Determine if `T` can be treated like `std::filesystem::path` during conversions. + @warning This trait is deprecated and **will be removed in Boost 1.92.0.** + Switch to using @ref conversion_category_for and @ref path_category. + Given `t`, a glvalue of type `T`, if - given `It`, the type denoted by `decltype(std::begin(t))`, @@ -137,6 +146,9 @@ struct is_path_like; /** Determine if `T` can be treated like a sequence during conversions. + @warning This trait is deprecated and **will be removed in Boost 1.92.0.** + Switch to using @ref conversion_category_for and @ref sequence_category. + Given `t`, a glvalue of type `T`, if - given `It`, the type denoted by `decltype(std::begin(t))`, @@ -177,6 +189,9 @@ struct is_sequence_like; /** Determine if `T` can be treated like a 1-to-1 mapping during conversions. + @warning This trait is deprecated and **will be removed in Boost 1.92.0.** + Switch to using @ref conversion_category_for and @ref map_category. + Given `t`, a glvalue of type `T`, if - `is_sequence_like::value` is `true`; and @@ -221,6 +236,9 @@ struct is_map_like; /** Determine if `T` can be treated like a tuple during conversions. + @warning This trait is deprecated and **will be removed in Boost 1.92.0.** + Switch to using @ref conversion_category_for and @ref tuple_category. + Provides the member constant `value` that is equal to `true`, if `std::tuple_size::value` is a positive number. Otherwise, `value` is equal to `false`. @@ -251,6 +269,9 @@ struct is_tuple_like; /** Determine if `T` can be treated like null during conversions. + @warning This trait is deprecated and **will be removed in Boost 1.92.0.** + Switch to using @ref conversion_category_for and @ref null_category. + Primary template instantiations provide the member constant `value` that is equal to `false`. Users can specialize the trait for their own types if they **do** want them to be treated as nulls. For example: @@ -273,12 +294,15 @@ struct is_tuple_like; @see @ref value_from, @ref value_to */ template -struct is_null_like - : std::false_type +struct is_null_like : std::false_type { }; /** Determine if `T` should be treated as a described class. + @warning This trait is deprecated and **will be removed in Boost 1.92.0.** + Switch to using @ref conversion_category_for and @ref + described_class_category. + Described classes are serialised as objects with an element for each described data member. Described bases are serialized in a flattened way, that is members of bases are serialized as direct elements of the object, @@ -329,6 +353,10 @@ struct is_described_class; /** Determine if `T` should be treated as a described enum. + @warning This trait is deprecated and **will be removed in Boost 1.92.0.** + Switch to using @ref conversion_category_for and @ref + described_enum_category. + Described enums are serialised as strings when their value equals to a described enumerator, and as integers otherwise. The reverse operation does not convert numbers to enums values, though, and instead produces @@ -360,6 +388,9 @@ struct is_described_enum; /** Determine if `T` should be treated as a variant. + @warning This trait is deprecated and **will be removed in Boost 1.92.0.** + Switch to using @ref conversion_category_for and @ref variant_category. + Variants are serialised the same way their active alternative is serialised. The opposite conversion selects the first alternative for which conversion succeeds. @@ -391,6 +422,9 @@ struct is_variant_like; /** Determine if `T` should be treated as an optional + @warning This trait is deprecated and **will be removed in Boost 1.92.0.** + Switch to using @ref conversion_category_for and @ref optional_category. + Optionals are serialised as `null` if empty, or as the stored type otherwise. @@ -423,6 +457,379 @@ struct is_variant_like; template struct is_optional_like; +/** Determines the category of conversion of a type. +*/ +template< + class T, + class Ctx +#ifndef BOOST_JSON_DOCS + = detail::no_context +#else + = __see_below__ +#endif + > +struct conversion_category_for; + +/** Conversion category for null types. + + Null types are represented in JSON as the `null` literal. + + By default only the type `std::nullptr_t` is considered a null type. + + Users can specialize @ref conversion_category_for for their own types if + they want them to be treated as nulls. For example: + + @code + namespace boost { + namespace json { + + template <> + struct conversion_category_for + { + using type = null_category; + }; + + } // namespace boost + } // namespace json + @endcode + + @par Matching Types + @ref std::nullptr_t. +*/ +struct null_category {}; + +/** Conversion category for strings. + + By default a type is considered a string if it is convertible to @ref + string_view. + + Users can specialize @ref conversion_category_for for their own types if + they want them to be treated as strings. For example: + + @code + namespace boost { + namespace json { + + template <> + struct conversion_category_for + { + using type = string_category; + }; + + } // namespace boost + } // namespace json + @endcode + + @par Matching Types + @ref string, @ref string_view, @ref std::string, @ref std::string_view. +*/ +struct string_category {}; + +/** Conversion category for variants. + + Variants are serialised the same way their active alternative is + serialised. The opposite conversion selects the first alternative for which + conversion succeeds. + + By default a type `T` is considered a variant if given `t`, a glvalue of + type ` const T`, `t.valueless_by_exception()` is well-formed. + + Users can specialize @ref conversion_category_for for their own types if + they want them to be treated as variants. For example: + + @code + namespace boost { + namespace json { + + template <> + struct conversion_category_for + { + using type = variant_category; + }; + + } // namespace boost + } // namespace json + @endcode + + @par Mathcing Types + @ref std::variant, @ref boost::variant2::variant. +*/ +struct variant_category {}; + +/** Conversion category for optionals. + + Optionals are represented in JSON as `null` if unengaged (i.e. doesn't have + a value), or as the stored type otherwise. + + By default a type `T` is considered an optional if given `t`, a glvalue of + type `T`, + + - `decltype( t.value() )` is well-formed and isn't a void type; and + - `t.reset()` is well-formed. + + Users can specialize @ref conversion_category_for for their own types if + they want them to be treated as optionals. For example: + + @code + namespace boost { + namespace json { + + template <> + struct conversion_category_for + { + using type = optional_category; + }; + + } // namespace boost + } // namespace json + @endcode + + @par Matching Types + @ref std::optional, @ref boost::optional. +*/ +struct optional_category {}; + +/** Conversion category for maps. + + Maps are represented in JSON as objects. Such representation limits this + category to 1-to-1 maps (as opposed to 1-to-many e.g. @ref std::multimap) + with string keys. + + By default a type `T` is considered a map if given `t`, a glvalue of type + `T`, + + - `is_sequence_like::value` is `true`; and + + - given type `It` denoting `decltype(std::begin(t))`, and types `K` and + `M`, `std::iterator_traits::value_type` denotes `std::pair`; + and + + - `std::is_string_like::value` is `true`; and + + - given `v`, a glvalue of type `V`, and `E`, the type denoted by + `decltype(t.emplace(v))`, `std::is_tuple_like::value` is `true`. + + @note The restriction for `t.emplace()` return type ensures that the + container does not allow duplicate keys. + + Users can specialize @ref conversion_category_for for their own types if + they want them to be treated like maps. For example: + + @code + namespace boost { + namespace json { + + template <> + struct conversion_category_for + { + using type = map_category; + }; + + } // namespace boost + } // namespace json + @endcode + + @par Matching Types + @ref std::map, @ref std::unordered_map. +*/ +struct map_category {}; + +/** Conversion category for sequences. + + Sequences are represented in JSON as arrays. + + By default a type `T` is considered a sequence if given `t`, a glvalue of + type `T`, + + - given `It`, the type denoted by `decltype(std::begin(t))`, + `std::iterator_traits::iterator_category` is well-formed and denotes + a type; and + + - `decltype(std::end(t))` also denotes the type `It`; and + + - `std::iterator_traits::value_type` is not `T`. + + Users can specialize @ref conversion_category_for for their own types if + they want them to be treated like sequences. For example: + + @code + namespace boost { + namespace json { + + template <> + struct conversion_category_for + { + using type = sequence_category; + }; + + } // namespace boost + } // namespace json + @endcode + + @par Mathcing Types + Any {req_SequenceContainer}, array types. +*/ +struct sequence_category {}; + +/** Conversion category for tuples. + + Tuples are represented in JSON as arrays. + + By default a type `T` is considered a tuple if `std::tuple_size::value` + is a positive number. + + Users can specialize @ref conversion_category_for for their own types if + they want them to be treated like tuples. For example: + + @code + namespace boost { + namespace json { + + template <> + struct conversion_category_for + { + using type = tuple_category; + }; + + } // namespace boost + } // namespace json + @endcode + + + @par Matching Types + @ref std::tuple, @ref std::pair. +*/ +struct tuple_category {}; + +/** Conversion category for described classes. + + Described classes are represented in JSON as objects with an element for + each described data member. Described bases are flattened, that is members + of bases are represented as direct elements of the object rather than + elements of some subobjects. + + By default a described class should not have non-public described members + (including inherited members) or non-public non-empty described bases. Or + more formally a type `T` is considered a described class if, given `L`, a + class template of the form `template struct L {}`, + + - `boost::describe::has_describe_members::value` is `true`; and + + - `boost::describe::describe_members` denotes + `L<>`; and + + - `std::is_union::value` is `false`. + + @note Shadowed members are ignored both for requirements checking and for + performing conversions. + + Users can specialize @ref conversion_category_for for their own types if + they want them to be treated as described classes. For example: + + @code + namespace boost { + namespace json { + + template <> + struct conversion_category_for + { + using type = described_class_category; + }; + + } // namespace boost + } // namespace json + @endcode + + @see [Boost.Describe](https://www.boost.org/doc/libs/develop/libs/describe/doc/html/describe.html). +*/ +struct described_class_category {}; + +/** Conversion category for described enums. + + Described enums are serialised as strings when their value equals to a + described enumerator, and as integers otherwise. The reverse operation + does not convert numbers to enums values, though, and instead produces + an error. + + By default a type `T` is considered a described enum if + `boost::describe::has_describe_enumerators::value` is `true`. + + Users can specialize @ref conversion_category_for for their own enums if + they want them to be treated as described enums. For example: + + @code + namespace boost { + namespace json { + + template <> + struct conversion_category_for + { + using type = described_enum_category; + }; + + } // namespace boost + } // namespace json + @endcode + + @see [Boost.Describe](https://www.boost.org/doc/libs/develop/libs/describe/doc/html/describe.html). +*/ +struct described_enum_category {}; + +/** Conversion category for filesystem paths. + + Paths are represented in JSON as strings. + + By default a type `T` is considered a path if given `t`, a glvalue of type + `T`, + + - given `It`, the type denoted by `decltype(std::begin(t))`, + `std::iterator_traits::iterator_category` is well-formed and denotes + a type; and + + - `std::iterator_traits::value_type` is `T`; and + + - `T::value_type` is well-formed and denotes a type; and + + - `T::string_type` is well-formed, denotes a type, and is an alias for + `std::basic_string< T::value_type >`. + + Users can specialize @ref conversion_category_for for their own types if + they don't want them to be treated like filesystem paths. For example: + + @code + namespace boost { + namespace json { + + template <> + struct conversion_category_for + { + using type = path_category; + }; + + } // namespace boost + } // namespace json + @endcode + + @par Matching Types + @ref std::filesystem::path, @ref boost::filesystem::path. +*/ +struct path_category {}; + +struct unknown_category {}; + +template< + class T, + class Ctx +#ifndef BOOST_JSON_DOCS + = detail::no_context +#else + = __see_below__ +#endif + > +using conversion_category_for_t + = typename conversion_category_for::type; + } // namespace json } // namespace boost diff --git a/include/boost/json/detail/parse_into.hpp b/include/boost/json/detail/parse_into.hpp index 231d917e9..61d1bbe7c 100644 --- a/include/boost/json/detail/parse_into.hpp +++ b/include/boost/json/detail/parse_into.hpp @@ -35,9 +35,9 @@ * handler (in this case, it's the top handler, into_handler). The type is * actually an alias to class template converting_handler, which has a separate * specialisation for every conversion category from the list of generic - * conversion categories (e.g. sequence_conversion_tag, tuple_conversion_tag, - * etc.) Instantiations of the template store a pointer to the parent handler - * and a pointer to the value T. + * conversion categories (e.g. sequence_category, tuple_category, etc.) + * Instantiations of the template store a pointer to the parent handler and + * a pointer to the value T. * * The nested handler handles specific parser events by setting error_code to * an appropriate value, if it receives an event it isn't supposed to handle @@ -51,8 +51,8 @@ * necessary for correct handling of composite types (e.g. sequences). * * Finally, nested handlers should always call parent's signal_end member - * function if they don't handle on_array_end themselves. This is necessary - * to correctly handle nested composites (e.g. sequences inside sequences). + * function if they don't handle on_array_end themselves. This is necessary to + * correctly handle nested composites (e.g. sequences inside sequences). * signal_end can return false and set error state when the containing parser * requires more elements. * @@ -63,11 +63,11 @@ * * To reiterate, only into_handler has to handle on_comment_part, on_comment, * on_document_begin, and on_document_end; only handlers for composites and - * into_handler has to provide signal_value and signal_end; all handlers - * except for into_handler have to call their parent's signal_end from - * their on_array_begin, if they don't handle it themselves; once a handler - * receives an event that finishes its current value, it should call its - * parent's signal_value. + * into_handler has to provide signal_value and signal_end; all handlers except + * for into_handler have to call their parent's signal_end from their + * on_array_begin, if they don't handle it themselves; once a handler receives + * an event that finishes its current value, it should call its parent's + * signal_value. */ namespace boost { @@ -79,7 +79,7 @@ class converting_handler; // get_handler template< class V, class P > -using get_handler = converting_handler< generic_conversion_category, V, P >; +using get_handler = converting_handler, V, P>; template class handler_error_base { @@ -349,7 +349,7 @@ class converting_handler // string handler template< class V, class P > -class converting_handler +class converting_handler : public scalar_handler { private: @@ -409,7 +409,7 @@ class converting_handler // null handler template< class V, class P > -class converting_handler +class converting_handler : public scalar_handler { private: @@ -430,7 +430,7 @@ class converting_handler // described enum handler template< class V, class P > -class converting_handler +class converting_handler : public scalar_handler { #ifndef BOOST_DESCRIBE_CXX14 @@ -478,7 +478,7 @@ class converting_handler }; template< class V, class P > -class converting_handler +class converting_handler { static_assert( sizeof(V) == 0, "This type is not supported" ); }; @@ -535,9 +535,9 @@ clear_container( } template< class V, class P > -class converting_handler +class converting_handler : public composite_handler< - converting_handler, + converting_handler, detail::value_type, P, error::not_array> @@ -610,9 +610,9 @@ class converting_handler // map handler template< class V, class P > -class converting_handler +class converting_handler : public composite_handler< - converting_handler, + converting_handler, detail::mapped_type, P, error::not_object> @@ -799,7 +799,7 @@ struct tuple_accessor }; template< class T, class P > -class converting_handler +class converting_handler { private: @@ -1120,7 +1120,7 @@ struct ignoring_handler }; template -class converting_handler +class converting_handler { #if !defined(BOOST_DESCRIBE_CXX14) @@ -1465,7 +1465,7 @@ using inner_handler_variant = mp11::mp_push_front< variant2::monostate>; template< class T, class P > -class converting_handler +class converting_handler { private: using variant_size = mp11::mp_size; @@ -1662,7 +1662,7 @@ class converting_handler // optional handler template -class converting_handler +class converting_handler { private: using inner_type = value_result_type; @@ -1787,7 +1787,7 @@ class converting_handler // path handler template< class V, class P > -class converting_handler +class converting_handler : public scalar_handler { private: diff --git a/include/boost/json/detail/value_from.hpp b/include/boost/json/detail/value_from.hpp index 3a09b419e..cfb6c541d 100644 --- a/include/boost/json/detail/value_from.hpp +++ b/include/boost/json/detail/value_from.hpp @@ -82,7 +82,7 @@ value_from_impl( native_conversion_tag, value& jv, T&& from, Ctx const& ) // null-like types template< class T, class Ctx > void -value_from_impl( null_like_conversion_tag, value& jv, T&&, Ctx const& ) +value_from_impl(null_category, value& jv, T&&, Ctx const&) { // do nothing BOOST_ASSERT(jv.is_null()); @@ -92,7 +92,7 @@ value_from_impl( null_like_conversion_tag, value& jv, T&&, Ctx const& ) // string-like types template< class T, class Ctx > void -value_from_impl( string_like_conversion_tag, value& jv, T&& from, Ctx const& ) +value_from_impl(string_category, value& jv, T&& from, Ctx const&) { auto sv = static_cast(from); jv.emplace_string().assign(sv); @@ -101,7 +101,7 @@ value_from_impl( string_like_conversion_tag, value& jv, T&& from, Ctx const& ) // map-like types template< class T, class Ctx > void -value_from_impl( map_like_conversion_tag, value& jv, T&& from, Ctx const& ctx ) +value_from_impl(map_category, value& jv, T&& from, Ctx const& ctx) { using std::get; object& obj = jv.emplace_object(); @@ -115,7 +115,7 @@ value_from_impl( map_like_conversion_tag, value& jv, T&& from, Ctx const& ctx ) // ranges template< class T, class Ctx > void -value_from_impl( sequence_conversion_tag, value& jv, T&& from, Ctx const& ctx ) +value_from_impl(sequence_category, value& jv, T&& from, Ctx const& ctx) { array& result = jv.emplace_array(); result.reserve(detail::try_size(from, size_implementation())); @@ -132,7 +132,7 @@ value_from_impl( sequence_conversion_tag, value& jv, T&& from, Ctx const& ctx ) // tuple-like types template< class T, class Ctx > void -value_from_impl( tuple_conversion_tag, value& jv, T&& from, Ctx const& ctx ) +value_from_impl(tuple_category, value& jv, T&& from, Ctx const& ctx) { constexpr std::size_t n = std::tuple_size>::value; @@ -145,7 +145,7 @@ value_from_impl( tuple_conversion_tag, value& jv, T&& from, Ctx const& ctx ) // no suitable conversion implementation template< class T, class Ctx > void -value_from_impl( no_conversion_tag, value&, T&&, Ctx const& ) +value_from_impl(unknown_category, value&, T&&, Ctx const&) { static_assert( !std::is_same::value, @@ -182,8 +182,7 @@ struct from_described_member // described classes template< class T, class Ctx > void -value_from_impl( - described_class_conversion_tag, value& jv, T&& from, Ctx const& ctx ) +value_from_impl(described_class_category, value& jv, T&& from, Ctx const& ctx) { object& obj = jv.emplace_object(); from_described_member member_converter{ @@ -198,8 +197,7 @@ value_from_impl( // described enums template< class T, class Ctx > void -value_from_impl( - described_enum_conversion_tag, value& jv, T from, Ctx const& ) +value_from_impl(described_enum_category, value& jv, T from, Ctx const&) { (void)jv; (void)from; @@ -221,8 +219,7 @@ value_from_impl( // optionals template< class T, class Ctx > void -value_from_impl( - optional_conversion_tag, value& jv, T&& from, Ctx const& ctx ) +value_from_impl(optional_category, value& jv, T&& from, Ctx const& ctx) { if( from ) value_from( *from, ctx, jv ); @@ -247,14 +244,14 @@ struct value_from_visitor template< class Ctx, class T > void -value_from_impl( variant_conversion_tag, value& jv, T&& from, Ctx const& ctx ) +value_from_impl(variant_category, value& jv, T&& from, Ctx const& ctx) { visit( value_from_visitor{ jv, ctx }, static_cast(from) ); } template< class Ctx, class T > void -value_from_impl( path_conversion_tag, value& jv, T&& from, Ctx const& ) +value_from_impl(path_category, value& jv, T&& from, Ctx const&) { std::string s = from.generic_string(); string_view sv = s; diff --git a/include/boost/json/detail/value_to.hpp b/include/boost/json/detail/value_to.hpp index 09eff9b11..ebfc04399 100644 --- a/include/boost/json/detail/value_to.hpp +++ b/include/boost/json/detail/value_to.hpp @@ -172,11 +172,7 @@ value_to_impl( // null-like conversion template< class T, class Ctx > system::result -value_to_impl( - null_like_conversion_tag, - try_value_to_tag, - value const& jv, - Ctx const& ) +value_to_impl(null_category, try_value_to_tag, value const& jv, Ctx const&) { if( jv.is_null() ) return {boost::system::in_place_value, T{}}; @@ -189,10 +185,7 @@ value_to_impl( template< class T, class Ctx > system::result value_to_impl( - string_like_conversion_tag, - try_value_to_tag, - value const& jv, - Ctx const& ) + string_category, try_value_to_tag, value const& jv, Ctx const&) { auto str = jv.if_string(); if( str ) @@ -206,10 +199,7 @@ value_to_impl( template< class T, class Ctx > system::result value_to_impl( - map_like_conversion_tag, - try_value_to_tag, - value const& jv, - Ctx const& ctx ) + map_category, try_value_to_tag, value const& jv, Ctx const& ctx) { object const* obj = jv.if_object(); if( !obj ) @@ -246,10 +236,7 @@ value_to_impl( template< class T, class Ctx > system::result value_to_impl( - sequence_conversion_tag, - try_value_to_tag, - value const& jv, - Ctx const& ctx ) + sequence_category, try_value_to_tag, value const& jv, Ctx const& ctx) { array const* arr = jv.if_array(); if( !arr ) @@ -321,10 +308,7 @@ try_make_tuple_like( template< class T, class Ctx > system::result value_to_impl( - tuple_conversion_tag, - try_value_to_tag, - value const& jv, - Ctx const& ctx ) + tuple_category, try_value_to_tag, value const& jv, Ctx const& ctx) { system::error_code ec; @@ -401,7 +385,7 @@ struct to_described_member template< class T, class Ctx > system::result value_to_impl( - described_class_conversion_tag, + described_class_category, try_value_to_tag, value const& jv, Ctx const& ctx ) @@ -434,10 +418,7 @@ value_to_impl( template< class T, class Ctx > system::result value_to_impl( - described_enum_conversion_tag, - try_value_to_tag, - value const& jv, - Ctx const& ) + described_enum_category, try_value_to_tag, value const& jv, Ctx const&) { T val = {}; (void)jv; @@ -465,10 +446,7 @@ value_to_impl( template< class T, class Ctx > system::result value_to_impl( - optional_conversion_tag, - try_value_to_tag, - value const& jv, - Ctx const& ctx) + optional_category, try_value_to_tag, value const& jv, Ctx const& ctx) { using Inner = value_result_type; if( jv.is_null() ) @@ -541,10 +519,7 @@ struct alternative_converter template< class T, class Ctx > system::result value_to_impl( - variant_conversion_tag, - try_value_to_tag, - value const& jv, - Ctx const& ctx) + variant_category, try_value_to_tag, value const& jv, Ctx const& ctx) { system::error_code ec; BOOST_JSON_FAIL(ec, error::exhausted_variants); @@ -558,8 +533,7 @@ value_to_impl( template< class T, class Ctx > system::result -value_to_impl( - path_conversion_tag, try_value_to_tag, value const& jv, Ctx const& ) +value_to_impl(path_category, try_value_to_tag, value const& jv, Ctx const&) { auto str = jv.if_string(); if( !str ) @@ -806,7 +780,7 @@ value_to_impl( // no suitable conversion implementation template< class T, class Ctx > T -value_to_impl( no_conversion_tag, value_to_tag, value const&, Ctx const& ) +value_to_impl(unknown_category, value_to_tag, value const&, Ctx const&) { static_assert( !std::is_same::value, diff --git a/include/boost/json/impl/conversion.hpp b/include/boost/json/impl/conversion.hpp index 72aff42bc..404092451 100644 --- a/include/boost/json/impl/conversion.hpp +++ b/include/boost/json/impl/conversion.hpp @@ -190,6 +190,7 @@ using value_to_conversion = mp11::mp_false; struct user_conversion_tag { }; struct context_conversion_tag : user_conversion_tag { }; struct full_context_conversion_tag : context_conversion_tag { }; + struct native_conversion_tag { }; struct value_conversion_tag : native_conversion_tag { }; struct object_conversion_tag : native_conversion_tag { }; @@ -199,17 +200,31 @@ struct bool_conversion_tag : native_conversion_tag { }; struct number_conversion_tag : native_conversion_tag { }; struct integral_conversion_tag : number_conversion_tag { }; struct floating_point_conversion_tag : number_conversion_tag { }; -struct null_like_conversion_tag { }; -struct string_like_conversion_tag { }; -struct map_like_conversion_tag { }; -struct path_conversion_tag { }; -struct sequence_conversion_tag { }; -struct tuple_conversion_tag { }; -struct described_class_conversion_tag { }; -struct described_enum_conversion_tag { }; -struct variant_conversion_tag { }; -struct optional_conversion_tag { }; -struct no_conversion_tag { }; + +} // detail + +template< class T, class Ctx > +struct conversion_category_for +{ + using type = mp11::mp_cond< + std::is_same, detail::bool_conversion_tag, + std::is_integral, detail::integral_conversion_tag, + std::is_floating_point, detail::floating_point_conversion_tag, + is_null_like, null_category, + is_string_like, string_category, + is_variant_like, variant_category, + is_optional_like, optional_category, + is_map_like, map_category, + is_sequence_like, sequence_category, + is_tuple_like, tuple_category, + is_described_class, described_class_category, + is_described_enum, described_enum_category, + is_path_like, path_category, + // failed to find a suitable implementation + mp11::mp_true, unknown_category>; +}; + +namespace detail { template using supports_tag_invoke = decltype(tag_invoke( std::declval()... )); @@ -355,30 +370,11 @@ using native_conversion_category = mp11::mp_cond< std::is_same, object_conversion_tag, std::is_same, string_conversion_tag>; -// generic conversions -template< class T > -using generic_conversion_category = mp11::mp_cond< - std::is_same, bool_conversion_tag, - std::is_integral, integral_conversion_tag, - std::is_floating_point, floating_point_conversion_tag, - is_null_like, null_like_conversion_tag, - is_string_like, string_like_conversion_tag, - is_variant_like, variant_conversion_tag, - is_optional_like, optional_conversion_tag, - is_map_like, map_like_conversion_tag, - is_sequence_like, sequence_conversion_tag, - is_tuple_like, tuple_conversion_tag, - is_described_class, described_class_conversion_tag, - is_described_enum, described_enum_conversion_tag, - is_path_like, path_conversion_tag, - // failed to find a suitable implementation - mp11::mp_true, no_conversion_tag>; - template< class T > using nested_type = typename T::type; template< class T1, class T2 > using conversion_category_impl_helper = mp11::mp_eval_if_not< - std::is_same, + std::is_same, T1, mp11::mp_eval_or_q, T1, mp11::mp_quote, T2>; template< class Ctx, class T, class Dir > @@ -388,8 +384,8 @@ struct conversion_category_impl mp11::mp_list< mp11::mp_defer, mp11::mp_defer, - mp11::mp_defer>, - no_conversion_tag, + mp11::mp_defer>, + unknown_category, conversion_category_impl_helper>; }; template< class Ctx, class T, class Dir > @@ -398,7 +394,7 @@ using conversion_category = template< class T > using any_conversion_tag = mp11::mp_not< - std::is_same< T, no_conversion_tag > >; + std::is_same< T, unknown_category > >; template< class T, class Dir, class... Ctxs > struct conversion_category_impl< std::tuple, T, Dir > @@ -410,27 +406,21 @@ struct conversion_category_impl< std::tuple, T, Dir > template< class I > using exists = mp11::mp_less< I, mp11::mp_size >; - using context2 = mp11::mp_find< cats, full_context_conversion_tag >; - using context1 = mp11::mp_find< cats, context_conversion_tag >; - using context0 = mp11::mp_find< cats, user_conversion_tag >; + using context2 = mp11::mp_find; + using context1 = mp11::mp_find; + using context0 = mp11::mp_find; using index = mp11::mp_cond< exists, context2, exists, context1, exists, context0, - mp11::mp_true, mp11::mp_find_if< cats, any_conversion_tag > >; - using type = mp11::mp_eval_or< - no_conversion_tag, - mp11::mp_at, cats, index >; + mp11::mp_true, mp11::mp_find_if >; + using type = mp11::mp_eval_or; }; -struct no_context -{}; - template using can_convert = mp11::mp_not< std::is_same< - detail::conversion_category, - detail::no_conversion_tag>>; + detail::conversion_category, unknown_category>>; template using conversion_round_trips_helper = mp11::mp_or< diff --git a/include/boost/json/impl/serializer.hpp b/include/boost/json/impl/serializer.hpp index 9eb4946b1..9ef344705 100644 --- a/include/boost/json/impl/serializer.hpp +++ b/include/boost/json/impl/serializer.hpp @@ -53,7 +53,7 @@ write_impl(writer& w, stream& ss); template BOOST_FORCEINLINE bool -write_impl(null_like_conversion_tag, writer& w, stream& ss) +write_impl(null_category, writer& w, stream& ss) { #if defined(_MSC_VER) # pragma warning( push ) @@ -181,7 +181,7 @@ write_impl(floating_point_conversion_tag, writer& w, stream& ss0) template BOOST_FORCEINLINE bool -write_impl(string_like_conversion_tag, writer& w, stream& ss0) +write_impl(string_category, writer& w, stream& ss0) { #if defined(_MSC_VER) # pragma warning( push ) @@ -203,7 +203,7 @@ write_impl(string_like_conversion_tag, writer& w, stream& ss0) template BOOST_FORCEINLINE bool -write_impl(sequence_conversion_tag, writer& w, stream& ss0) +write_impl(sequence_category, writer& w, stream& ss0) { using It = iterator_type; using Elem = value_type; @@ -275,7 +275,7 @@ write_impl(sequence_conversion_tag, writer& w, stream& ss0) template BOOST_FORCEINLINE bool -write_impl(map_like_conversion_tag, writer& w, stream& ss0) +write_impl(map_category, writer& w, stream& ss0) { using It = iterator_type; using Mapped = mapped_type; @@ -394,7 +394,7 @@ struct serialize_tuple_elem_helper template BOOST_FORCEINLINE bool -write_impl(tuple_conversion_tag, writer& w, stream& ss0) +write_impl(tuple_category, writer& w, stream& ss0) { T const* pt; local_stream ss(ss0); @@ -521,7 +521,7 @@ struct serialize_struct_elem_helper template BOOST_FORCEINLINE bool -write_impl(described_class_conversion_tag, writer& w, stream& ss0) +write_impl(described_class_category, writer& w, stream& ss0) { using Ds = described_members; @@ -597,7 +597,7 @@ write_impl(described_class_conversion_tag, writer& w, stream& ss0) template BOOST_FORCEINLINE bool -write_impl(described_enum_conversion_tag, writer& w, stream& ss) +write_impl(described_enum_category, writer& w, stream& ss) { #ifdef BOOST_DESCRIBE_CXX14 using Integer = typename std::underlying_type::type; @@ -681,7 +681,7 @@ struct serialize_variant_elem_helper template BOOST_FORCEINLINE bool -write_impl(variant_conversion_tag, writer& w, stream& ss) +write_impl(variant_category, writer& w, stream& ss) { T const* pt; @@ -725,7 +725,7 @@ write_impl(variant_conversion_tag, writer& w, stream& ss) template BOOST_FORCEINLINE bool -write_impl(optional_conversion_tag, writer& w, stream& ss) +write_impl(optional_category, writer& w, stream& ss) { using Elem = value_result_type; @@ -774,7 +774,7 @@ write_impl(optional_conversion_tag, writer& w, stream& ss) template BOOST_FORCEINLINE bool -write_impl(path_conversion_tag, writer& w, stream& ss) +write_impl(path_category, writer& w, stream& ss) { #if defined(_MSC_VER) # pragma warning( push ) @@ -822,8 +822,8 @@ template bool write_impl(writer& w, stream& ss) { - using cat = detail::generic_conversion_category; - return write_impl( cat(), w, ss ); + using Cat = conversion_category_for_t; + return write_impl( Cat(), w, ss ); } } // namespace detail diff --git a/include/boost/json/impl/serializer.ipp b/include/boost/json/impl/serializer.ipp index 56f39ade4..ca39f7b89 100644 --- a/include/boost/json/impl/serializer.ipp +++ b/include/boost/json/impl/serializer.ipp @@ -361,7 +361,7 @@ write_value(writer& w, stream& ss); template< class T, bool StackEmpty > BOOST_FORCEINLINE bool -write_impl(no_conversion_tag, writer& w, stream& ss) +write_impl(unknown_category, writer& w, stream& ss) { return write_value(w, ss); } @@ -370,14 +370,14 @@ template bool write_array(writer& w, stream& ss) { - return write_impl(sequence_conversion_tag(), w, ss); + return write_impl(sequence_category(), w, ss); } template bool write_object(writer& w, stream& ss) { - return write_impl(map_like_conversion_tag(), w, ss); + return write_impl(map_category(), w, ss); } template