diff --git a/doc/externals.hpp b/doc/externals.hpp index 7d803797d..0a35f1e80 100644 --- a/doc/externals.hpp +++ b/doc/externals.hpp @@ -212,6 +212,16 @@ struct bad_alloc {}; /// @see https://en.cppreference.com/w/cpp/container/map struct map {}; +/// !EXTERNAL! +/// +/// @see https://en.cppreference.com/cpp/utility/variant/monostate +struct monostate {}; + +/// !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..47b580a23 100644 --- a/doc/pages/reference.adoc +++ b/doc/pages/reference.adoc @@ -33,6 +33,10 @@ a| *Classes* + <> + <> +*Aliases* + +<> + +<> + a| *Functions* + <> + <> + @@ -47,12 +51,7 @@ a| *Functions* + <> + <> -*P0308R0* + -<> + -<> + -<> - -| *Operators* + +*Operators* + <> + <> + <> + @@ -61,37 +60,61 @@ a| *Functions* + <> + <> + -*Aliases* + -<> + -<> +| *Type Traits* + +<> + +<> + +<> + +<> + +<> + +<> + +<> + +<> + +<> + +<> + +<> + +<> + +<> + +<> + +<> + +<> + +<> + +<> + +<> + +<> + +<> + +<> -*Constants* + +| *Constants* + <> + <> + +<> + <> + <> + <> + <> + <> -| *Type Traits* + -<> + -<> + -<> + +*P0308R0* + +<> + +<> + +<> + +4+h|Deprecated + +a| <> + <> + -<> + +<> +a| <> + -<> + +<> +a| <> + <> + -<> + +<> +a| <> + -<> + -<> + -<> + -<> + -<> +<> |=== diff --git a/include/boost/json/conversion.hpp b/include/boost/json/conversion.hpp index 5774e2b50..d5cb94e86 100644 --- a/include/boost/json/conversion.hpp +++ b/include/boost/json/conversion.hpp @@ -12,6 +12,7 @@ #include #include +#include #include @@ -25,6 +26,62 @@ struct supported_context; } // namespace detail +/** Conversion categories supported by the library. + + With the exception of `unknown` every enumerator of this enum represents a + conversion category supported by this library. These categories are used + to pick appropriate implementations of @ref value_to, @ref try_value_to, + @ref value_from, @ref parse_into, and @ref serialize. + + `unknown` category is assigned to types for which the library could not + deduce an appropriate category. Using the aforementioned functions with + objects of such types will result in compilation failure. + */ +enum class conversion_category +{ + /// See @ref unknown_category + unknown, + /// See @ref boolean_category + boolean, + /// See @ref integer_category + integer, + /// See @ref floating_point_category + floating_point, + /// See @ref null_category + null, + /// See @ref string_category + string, + /// See @ref variant_category + variant, + /// See @ref optional_category + optional, + /// See @ref map_category + map, + /// See @ref sequence_category + sequence, + /// See @ref tuple_category + tuple, + /// See @ref described_class_category + described_class, + /// See @ref described_enum_category + described_enum, + /// See @ref path_category + path, + + // internal categories +#ifndef BOOST_JSON_DOCS + json_value, + json_object, + json_array, + json_string, + json_value_ref, + + user, + user_context, + user_full_context, +#endif +}; + /** Customization point tag. This tag type is used by the function @ref value_from to select overloads @@ -68,6 +125,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.93.0.** + Switch to using @ref conversion_category_of 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`. @@ -94,8 +154,37 @@ struct try_value_to_tag { }; template struct is_string_like; +/** Conversion category for strings. + + String types are represented as JSON strings. + + By default a type is considered a string if it is convertible to @ref + string_view. + + Users can specialize the trait for their own types if they want them to be + treated like strings. For example: + + @code + namespace boost { namespace json { + + template <> + struct is_string_like : string_category + { }; + + } } + @endcode + + @par Matching Types + @ref string_view, @ref std::string, @ref std::string_view. +*/ +using string_category = std::integral_constant< + conversion_category, conversion_category::string>; + /** Determine if `T` can be treated like `std::filesystem::path` during conversions. + @warning This trait is deprecated and **will be removed in Boost 1.93.0.** + Switch to using @ref conversion_category_of and @ref path_category. + Given `t`, a glvalue of type `T`, if - given `It`, the type denoted by `decltype(std::begin(t))`, @@ -135,8 +224,49 @@ struct is_string_like; template struct is_path_like; +/** 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`; and + + - 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_of 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_of : path_category + { }; + + } } + @endcode + + @par Matching Types + @ref std::filesystem::path, @ref boost::filesystem::path. +*/ +using path_category = std::integral_constant< + conversion_category, conversion_category::path>; + /** Determine if `T` can be treated like a sequence during conversions. + @warning This trait is deprecated and **will be removed in Boost 1.93.0.** + Switch to using @ref conversion_category_of and @ref sequence_category. + Given `t`, a glvalue of type `T`, if - given `It`, the type denoted by `decltype(std::begin(t))`, @@ -145,7 +275,7 @@ struct is_path_like; - `decltype(std::end(t))` also denotes the type `It`; and - - `std::iterator_traits::value_type` is not `T`; and + - `std::iterator_traits::value_type` is not `T`. then the trait provides the member constant `value` that is equal to `true`. Otherwise, `value` is equal to `false`. @@ -174,9 +304,49 @@ struct is_path_like; template struct is_sequence_like; +/** 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`; and + + - 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`; and + + - `std::iterator_traits::value_type` is not `T`. + + Users can specialize @ref conversion_category_of for their own types if + they want them to be treated like sequences. For example: + + @code + namespace boost { namespace json { + + template <> + struct conversion_category_of : sequence_category + { }; + + } } + @endcode + + @par Matching Types + Any {req_SequenceContainer}, array types. +*/ +using sequence_category = std::integral_constant< + conversion_category, conversion_category::sequence>; + /** 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.93.0.** + Switch to using @ref conversion_category_of and @ref map_category. + Given `t`, a glvalue of type `T`, if - `is_sequence_like::value` is `true`; and @@ -219,8 +389,63 @@ struct is_sequence_like; template struct is_map_like; +/** 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`; and + + - 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`; and + + - given types `K` and `M`, `std::iterator_traits::value_type` denotes + `std::pair`; and + + - `conversion_category_of::value` is + @ref conversion_category::string; and + + - given `v`, a value of type `std::iterator_traits::value_type`, + `std::tuple_size< decltype(t.emplace(v)) >::value` is a positive number. + + Less formally, `T` should be a sequence of `std::pair`s with unique + string-like keys. + + @note The restriction for `t.emplace()` return type ensures that the + container does not allow duplicate keys. + + Users can specialize @ref conversion_category_of for their own types if + they want them to be treated like maps. For example: + + @code + namespace boost { namespace json { + + template <> + struct is_map_like : map_category + { }; + + } } + @endcode + + @par Matching Types + @ref std::map, @ref std::unordered_map. +*/ +using map_category = std::integral_constant< + conversion_category, conversion_category::map>; + /** Determine if `T` can be treated like a tuple during conversions. + @warning This trait is deprecated and **will be removed in Boost 1.93.0.** + Switch to using @ref conversion_category_of 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`. @@ -249,8 +474,38 @@ struct is_map_like; template struct is_tuple_like; +/** 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_of for their own types if + they want them to be treated like tuples. For example: + + @code + namespace boost { namespace json { + + template <> + struct conversion_category_of : tuple_category + { }; + + } } + @endcode + + + @par Matching Types + @ref std::tuple, @ref std::pair. +*/ +using tuple_category = std::integral_constant< + conversion_category, conversion_category::tuple>; + /** Determine if `T` can be treated like null during conversions. + @warning This trait is deprecated and **will be removed in Boost 1.93.0.** + Switch to using @ref conversion_category_of 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: @@ -277,9 +532,39 @@ struct is_null_like : std::false_type { }; +/** Conversion category for null types. + + Null types are represented in JSON as the `null` literal. + + By default only the types @ref std::nullptr_t and @ref std::monostate are + considered null types. + + Users can specialize @ref conversion_category_of for their own types if + they want them to be treated as nulls. For example: + + @code + namespace boost { namespace json { + + template <> + struct conversion_category_of : null_category + { }; + + } } + @endcode + + @par Matching Types + @ref std::nullptr_t, @ref std::monostate. +*/ +using null_category = std::integral_constant< + conversion_category, conversion_category::null>; + /** Determine if `T` should be treated as a described class. - Described classes are serialised as objects with an element for each + @warning This trait is deprecated and **will be removed in Boost 1.93.0.** + Switch to using @ref conversion_category_of and + @ref described_class_category. + + Described classes are serialized 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, and no nested objects are created for bases. @@ -327,9 +612,59 @@ struct is_null_like template struct is_described_class; +/** 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 + + - `boost::describe::has_describe_members::value` is `true`; and + + - given `L`, a class template of the form `template struct L {}`, + `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_of 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_of + : described_class_category + { }; + + } } + @endcode + + Users can also specialize the trait for their own types _with_ described + non-public data members to enable this conversion implementation. Note that + non-public bases are not supported regardless. + + @see [Boost.Describe](https://www.boost.org/doc/libs/latest/libs/describe/doc/html/describe.html). +*/ +using described_class_category = std::integral_constant< + conversion_category, conversion_category::described_class>; + /** Determine if `T` should be treated as a described enum. - Described enums are serialised as strings when their value equals to a + @warning This trait is deprecated and **will be removed in Boost 1.93.0.** + Switch to using @ref conversion_category_of and + @ref described_enum_category. + + Described enums are serialized 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. @@ -358,10 +693,42 @@ struct is_described_class; template struct is_described_enum; +/** Conversion category for described enums. + + Described enums are serialized 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_of 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_of + : described_enum_category + { }; + + } } + @endcode + + @see [Boost.Describe](https://www.boost.org/doc/libs/latest/libs/describe/doc/html/describe.html). +*/ +using described_enum_category = std::integral_constant< + conversion_category, conversion_category::described_enum>; + /** Determine if `T` should be treated as a variant. - Variants are serialised the same way their active alternative is - serialised. The opposite conversion selects the first alternative for which + @warning This trait is deprecated and **will be removed in Boost 1.93.0.** + Switch to using @ref conversion_category_of and @ref variant_category. + + Variants are serialized the same way their active alternative is + serialized. The opposite conversion selects the first alternative for which conversion succeeds. Given `t`, a glvalue of type ` const T`, if `t.valueless_by_exception()` is @@ -389,9 +756,40 @@ struct is_described_enum; template struct is_variant_like; +/** Conversion category for variants. + + Variants are serialized the same way their active alternative is + serialized. 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_of for their own types if + they want them to be treated as variants. For example: + + @code + namespace boost { namespace json { + + template <> + struct conversion_category_of : variant_category + { }; + + } } + @endcode + + @par Mathcing Types + @ref std::variant, @ref boost::variant2::variant. +*/ +using variant_category = std::integral_constant< + conversion_category, conversion_category::variant>; + /** Determine if `T` should be treated as an optional - Optionals are serialised as `null` if empty, or as the stored type + @warning This trait is deprecated and **will be removed in Boost 1.93.0.** + Switch to using @ref conversion_category_of and @ref optional_category. + + Optionals are serialized as `null` if empty, or as the stored type otherwise. Given `t`, a glvalue of type `T`, if @@ -423,6 +821,166 @@ struct is_variant_like; template struct is_optional_like; +/** Conversion category for optionals. + + Optionals are represented in JSON as `null` if not engaged (i.e. does not + store 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_of for their own types if + they want them to be treated as optionals. For example: + + @code + namespace boost { namespace json { + + template <> + struct conversion_category_of : optional_category + { }; + + } } + @endcode + + @par Matching Types + @ref std::optional, @ref boost::optional. +*/ +using optional_category = std::integral_constant< + conversion_category, conversion_category::optional>; + +/** Unknown conversion category. + + This category is assigned by the library's conversion category deduction + mechanism when it fails. +*/ +using unknown_category = std::integral_constant< + conversion_category, conversion_category::unknown>; + +/** Conversion category for booleans. + + Booleans are represented in JSON as `true` or `false` literals. + + By default the type `bool` is considered a boolean. + + Users can specialize @ref conversion_category_of for their own types if + they want them to be treated as booleans. For example: + + @code + namespace boost { namespace json { + + template <> + struct conversion_category_of : boolean_category + { }; + + } } + @endcode + + @par Matching Types + `bool`. +*/ +using boolean_category = std::integral_constant< + conversion_category, conversion_category::boolean>; + +/** Conversion category for integers. + + Integers are represented in JSON as numbers. + + By default a type `T` is considered an integer if + `std::is_integral::value` is `true`. + + Users can specialize @ref conversion_category_of for their own types if + they want them to be treated as integers. For example: + + @code + namespace boost { namespace json { + + template <> + struct conversion_category_of : integer_category + { }; + + } } + @endcode + + @par Matching Types + [Integral types](https://en.cppreference.com/cpp/language/types#Integral_types). +*/ +using integer_category = std::integral_constant< + conversion_category, conversion_category::integer>; + +/** Conversion category for floating-point types. + + Floating-point types are represented in JSON as numbers. + + By default a type `T` is considered an integer if + `std::is_floating_point::value` is `true`. + + Users can specialize @ref conversion_category_of for their own types if + they want them to be treated as floating-point types. For example: + + @code + namespace boost { namespace json { + + template <> + struct conversion_category_of : floating_point_category + { }; + + } } + @endcode + + @par Matching Types + [Fundamental floating-point types](https://en.cppreference.com/cpp/language/types#Floating-point_types). +*/ +using floating_point_category = std::integral_constant< + conversion_category, conversion_category::floating_point>; + +/** Determines the category of conversion of a type. + + Provides the member constant `value` that is equal to an enumerator of + @ref conversion_category that represents the suitable conversion category + of T. The primary template attempts to deduce the category. + + Users can specialize the trait for their own types if the deduction fails. + For example: + + @code + namespace boost { + namespace json { + + template <> + struct conversion_category : string_category + { }; + + } // namespace boost + } // namespace json + @endcode + + @see @ref value_from, @ref value_to. +*/ +template< class T > +struct conversion_category_of +#ifndef BOOST_JSON_DOCS + : mp11::mp_cond< + std::is_same, boolean_category, + std::is_integral, integer_category, + std::is_floating_point, floating_point_category, + 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> +#endif +{ }; + } // namespace json } // namespace boost diff --git a/include/boost/json/detail/parse_into.hpp b/include/boost/json/detail/parse_into.hpp index 003c8d3d1..2b61bd4f2 100644 --- a/include/boost/json/detail/parse_into.hpp +++ b/include/boost/json/detail/parse_into.hpp @@ -36,9 +36,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 @@ -75,12 +75,12 @@ namespace boost { namespace json { namespace detail { -template< class Impl, class T, class Parent > +template< conversion_category C, class T, class Parent > 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::value, V, P>; template class handler_error_base { @@ -268,7 +268,7 @@ bool integral_in_range( std::uint64_t v ) } template< class V, class P > -class converting_handler +class converting_handler : public scalar_handler { private: @@ -312,7 +312,7 @@ class converting_handler // floating point handler template< class V, class P> -class converting_handler +class converting_handler : public scalar_handler { private: @@ -350,7 +350,7 @@ class converting_handler // string handler template< class V, class P > -class converting_handler +class converting_handler : public scalar_handler { private: @@ -389,7 +389,7 @@ class converting_handler // bool handler template< class V, class P > -class converting_handler +class converting_handler : public scalar_handler { private: @@ -410,7 +410,7 @@ class converting_handler // null handler template< class V, class P > -class converting_handler +class converting_handler : public scalar_handler { private: @@ -431,7 +431,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 @@ -479,7 +479,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" ); }; @@ -536,9 +536,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> @@ -611,9 +611,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> @@ -800,7 +800,7 @@ struct tuple_accessor }; template< class T, class P > -class converting_handler +class converting_handler { private: @@ -1121,7 +1121,7 @@ struct ignoring_handler }; template -class converting_handler +class converting_handler { #if !defined(BOOST_DESCRIBE_CXX14) @@ -1466,7 +1466,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; @@ -1663,7 +1663,7 @@ class converting_handler // optional handler template -class converting_handler +class converting_handler { private: using inner_type = value_result_type; @@ -1788,7 +1788,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 122e398a7..a0e8c58a8 100644 --- a/include/boost/json/detail/value_from.hpp +++ b/include/boost/json/detail/value_from.hpp @@ -47,14 +47,14 @@ struct append_tuple_element { template< class T, class Ctx > void -value_from_impl( user_conversion_tag, value& jv, T&& from, Ctx const& ) +value_from_impl( user_category, value& jv, T&& from, Ctx const& ) { tag_invoke( value_from_tag(), jv, static_cast(from) ); } template< class T, class Ctx > void -value_from_impl( context_conversion_tag, value& jv, T&& from, Ctx const& ctx) +value_from_impl( user_context_category, value& jv, T&& from, Ctx const& ctx) { using Sup = supported_context; tag_invoke( value_from_tag(), jv, static_cast(from), Sup::get(ctx) ); @@ -63,7 +63,7 @@ value_from_impl( context_conversion_tag, value& jv, T&& from, Ctx const& ctx) template< class T, class Ctx > void value_from_impl( - full_context_conversion_tag, value& jv, T&& from, Ctx const& ctx) + user_full_context_category, value& jv, T&& from, Ctx const& ctx) { using Sup = supported_context; tag_invoke( @@ -73,9 +73,14 @@ value_from_impl( //---------------------------------------------------------- // Native conversion -template< class T, class Ctx > +template< class Cat, class T, class Ctx > void -value_from_impl( native_conversion_tag, value& jv, T&& from, Ctx const& ) +value_from_impl( + Cat, + value& jv, + T&& from, + Ctx const&, + typename std::enable_if::value>* = nullptr) { jv = std::forward(from); } @@ -83,7 +88,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()); @@ -93,7 +98,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); @@ -102,7 +107,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(); @@ -116,7 +121,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())); @@ -133,7 +138,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; @@ -146,7 +151,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, @@ -184,7 +189,7 @@ struct from_described_member template< class T, class Ctx > void value_from_impl( - described_class_conversion_tag, value& jv, T&& from, Ctx const& ctx ) + described_class_category, value& jv, T&& from, Ctx const& ctx ) { object& obj = jv.emplace_object(); from_described_member member_converter{ @@ -199,8 +204,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; @@ -222,8 +226,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 ); @@ -248,14 +251,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; @@ -266,7 +269,7 @@ value_from_impl( path_conversion_tag, value& jv, T&& from, Ctx const& ) // Contextual conversions template< class Ctx, class T > -using value_from_category = conversion_category< +using value_from_category = extended_conversion_category< Ctx, T, value_from_conversion >; } // detail diff --git a/include/boost/json/detail/value_to.hpp b/include/boost/json/detail/value_to.hpp index 25a827971..1b0a6b2b7 100644 --- a/include/boost/json/detail/value_to.hpp +++ b/include/boost/json/detail/value_to.hpp @@ -76,7 +76,7 @@ try_reserve( template< class Ctx > system::result value_to_impl( - value_conversion_tag, + json_value_category, try_value_to_tag, value const& jv, Ctx const& ) @@ -87,7 +87,7 @@ value_to_impl( template< class Ctx > value value_to_impl( - value_conversion_tag, value_to_tag, value const& jv, Ctx const& ) + json_value_category, value_to_tag, value const& jv, Ctx const& ) { return jv; } @@ -96,7 +96,7 @@ value_to_impl( template< class Ctx > system::result value_to_impl( - object_conversion_tag, + json_object_category, try_value_to_tag, value const& jv, Ctx const& ) @@ -113,7 +113,7 @@ value_to_impl( template< class Ctx > system::result value_to_impl( - array_conversion_tag, + json_array_category, try_value_to_tag, value const& jv, Ctx const& ) @@ -130,7 +130,7 @@ value_to_impl( template< class Ctx > system::result value_to_impl( - string_conversion_tag, + json_string_category, try_value_to_tag, value const& jv, Ctx const& ) @@ -147,7 +147,7 @@ value_to_impl( template< class Ctx > system::result value_to_impl( - bool_conversion_tag, try_value_to_tag, value const& jv, Ctx const& ) + boolean_category, try_value_to_tag, value const& jv, Ctx const& ) { auto b = jv.if_bool(); if( b ) @@ -157,11 +157,17 @@ value_to_impl( return {boost::system::in_place_error, ec}; } -// integral and floating point -template< class T, class Ctx > +template< class Cat, class T, class Ctx > system::result value_to_impl( - number_conversion_tag, try_value_to_tag, value const& jv, Ctx const& ) + Cat, + try_value_to_tag, + value const& jv, + Ctx const&, + typename std::enable_if< + Cat::value == conversion_category::integer + || Cat::value == conversion_category::floating_point>::type* = nullptr + ) { system::error_code ec; auto const n = jv.to_number(ec); @@ -174,7 +180,7 @@ value_to_impl( template< class T, class Ctx > system::result value_to_impl( - null_like_conversion_tag, + null_category, try_value_to_tag, value const& jv, Ctx const& ) @@ -190,10 +196,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 ) @@ -207,7 +210,7 @@ value_to_impl( template< class T, class Ctx > system::result value_to_impl( - map_like_conversion_tag, + map_category, try_value_to_tag, value const& jv, Ctx const& ctx ) @@ -247,10 +250,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 ) @@ -331,10 +331,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; @@ -411,7 +408,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 ) @@ -444,7 +441,7 @@ value_to_impl( template< class T, class Ctx > system::result value_to_impl( - described_enum_conversion_tag, + described_enum_category, try_value_to_tag, value const& jv, Ctx const& ) @@ -475,7 +472,7 @@ value_to_impl( template< class T, class Ctx > system::result value_to_impl( - optional_conversion_tag, + optional_category, try_value_to_tag, value const& jv, Ctx const& ctx) @@ -551,7 +548,7 @@ struct alternative_converter template< class T, class Ctx > system::result value_to_impl( - variant_conversion_tag, + variant_category, try_value_to_tag, value const& jv, Ctx const& ctx) @@ -569,7 +566,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& ) + path_category, try_value_to_tag, value const& jv, Ctx const& ) { auto str = jv.if_string(); if( !str ) @@ -588,7 +585,7 @@ value_to_impl( template< class T, class Ctx > mp11::mp_if< mp11::mp_valid, T > value_to_impl( - user_conversion_tag, value_to_tag tag, value const& jv, Ctx const&) + user_category, value_to_tag tag, value const& jv, Ctx const&) { return tag_invoke(tag, jv); } @@ -601,7 +598,7 @@ template< mp11::mp_if< mp11::mp_valid< has_context_conversion_to_impl, typename Sup::type, T>, T > value_to_impl( - context_conversion_tag, + user_context_category, value_to_tag tag, value const& jv, Ctx const& ctx ) @@ -619,7 +616,7 @@ mp11::mp_if< has_full_context_conversion_to_impl, typename Sup::type, T>, T> value_to_impl( - full_context_conversion_tag, + user_full_context_category, value_to_tag tag, value const& jv, Ctx const& ctx ) @@ -632,7 +629,7 @@ value_to_impl( template< class T, class Ctx > mp11::mp_if_c< !mp11::mp_valid::value, T> value_to_impl( - user_conversion_tag, value_to_tag, value const& jv, Ctx const& ) + user_category, value_to_tag, value const& jv, Ctx const& ) { auto res = tag_invoke(try_value_to_tag(), jv); if( res.has_error() ) @@ -650,7 +647,7 @@ mp11::mp_if_c< has_context_conversion_to_impl, typename Sup::type, T>::value, T> value_to_impl( - context_conversion_tag, value_to_tag, value const& jv, Ctx const& ctx ) + user_context_category, value_to_tag, value const& jv, Ctx const& ctx ) { auto res = tag_invoke( try_value_to_tag(), jv, Sup::get(ctx) ); if( res.has_error() ) @@ -668,7 +665,7 @@ mp11::mp_if_c< has_full_context_conversion_to_impl, typename Sup::type, T>::value, T> value_to_impl( - full_context_conversion_tag, + user_full_context_category, value_to_tag, value const& jv, Ctx const& ctx ) @@ -686,7 +683,7 @@ mp11::mp_if< mp11::mp_valid< has_nonthrowing_user_conversion_to_impl, T>, system::result > value_to_impl( - user_conversion_tag, try_value_to_tag, value const& jv, Ctx const& ) + user_category, try_value_to_tag, value const& jv, Ctx const& ) { return tag_invoke(try_value_to_tag(), jv); } @@ -701,7 +698,7 @@ mp11::mp_if< has_nonthrowing_context_conversion_to_impl, typename Sup::type, T>, system::result > value_to_impl( - context_conversion_tag, + user_context_category, try_value_to_tag tag, value const& jv, Ctx const& ctx ) @@ -721,7 +718,7 @@ mp11::mp_if< T>, system::result > value_to_impl( - full_context_conversion_tag, + user_full_context_category, try_value_to_tag tag, value const& jv, Ctx const& ctx ) @@ -767,7 +764,7 @@ mp11::mp_if_c< !mp11::mp_valid::value, system::result > value_to_impl( - user_conversion_tag, try_value_to_tag, value const& jv, Ctx const& ) + user_category, try_value_to_tag, value const& jv, Ctx const& ) { return wrap_conversion_exceptions(value_to_tag(), jv); } @@ -784,7 +781,7 @@ mp11::mp_if_c< T>::value, system::result > value_to_impl( - context_conversion_tag, + user_context_category, try_value_to_tag, value const& jv, Ctx const& ctx ) @@ -804,7 +801,7 @@ mp11::mp_if_c< T>::value, system::result > value_to_impl( - full_context_conversion_tag, + user_full_context_category, try_value_to_tag, value const& jv, Ctx const& ctx ) @@ -816,7 +813,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, @@ -832,7 +829,7 @@ value_to_impl( Impl impl, value_to_tag, value const& jv, Ctx const& ctx ) } template< class Ctx, class T > -using value_to_category = conversion_category< +using value_to_category = extended_conversion_category< Ctx, T, value_to_conversion >; } // detail diff --git a/include/boost/json/impl/conversion.hpp b/include/boost/json/impl/conversion.hpp index 13a460973..c738f737d 100644 --- a/include/boost/json/impl/conversion.hpp +++ b/include/boost/json/impl/conversion.hpp @@ -189,30 +189,40 @@ inserter( using value_from_conversion = mp11::mp_true; 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 { }; -struct array_conversion_tag : native_conversion_tag { }; -struct string_conversion_tag : native_conversion_tag { }; -struct bool_conversion_tag : native_conversion_tag { }; -struct value_ref_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 { }; +using user_category = std::integral_constant< + conversion_category, conversion_category::user>; +using user_context_category = std::integral_constant< + conversion_category, conversion_category::user_context>; +using user_full_context_category = std::integral_constant< + conversion_category, conversion_category::user_full_context>; +using json_value_category = std::integral_constant< + conversion_category, conversion_category::json_value>; +using json_object_category = std::integral_constant< + conversion_category, conversion_category::json_object>; +using json_array_category = std::integral_constant< + conversion_category, conversion_category::json_array>; +using json_string_category = std::integral_constant< + conversion_category, conversion_category::json_string>; +using json_value_ref_category = std::integral_constant< + conversion_category, conversion_category::json_value_ref>; + +template< class Cat > +using is_user_conversion = mp11::mp_bool< + Cat::value == conversion_category::user + || Cat::value == conversion_category::user_context + || Cat::value == conversion_category::user_full_context>; + +template< class Cat > +using is_native_conversion = mp11::mp_bool< + Cat::value == conversion_category::user + || Cat::value == conversion_category::json_value + || Cat::value == conversion_category::json_object + || Cat::value == conversion_category::json_array + || Cat::value == conversion_category::json_string + || Cat::value == conversion_category::json_value_ref + || Cat::value == conversion_category::boolean + || Cat::value == conversion_category::integer + || Cat::value == conversion_category::floating_point>; template using supports_tag_invoke = decltype(tag_invoke( std::declval()... )); @@ -346,86 +356,68 @@ using uniquely_named_members = std::true_type; // user conversion (via tag_invoke) template< class Ctx, class T, class Dir > using user_conversion_category = mp11::mp_cond< - has_user_conversion3, full_context_conversion_tag, - has_user_conversion2, context_conversion_tag, - has_user_conversion1, user_conversion_tag>; + has_user_conversion3, user_full_context_category, + has_user_conversion2, user_context_category, + has_user_conversion1, user_category>; // native conversions (constructors and member functions of value) template< class T > using native_conversion_category = mp11::mp_cond< - std::is_same, value_conversion_tag, - std::is_same, array_conversion_tag, - std::is_same, object_conversion_tag, - std::is_same, string_conversion_tag>; + std::is_same, json_value_ref_category, + std::is_same, json_value_category, + std::is_same, json_array_category, + std::is_same, json_object_category, + std::is_same, json_string_category>; -// generic conversions template< class T > -using generic_conversion_category = mp11::mp_cond< - // std::is_same>, init_list_tag, - std::is_same, value_ref_tag, - - 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>; +using conversion_category_t = std::integral_constant< + conversion_category, conversion_category_of::value>; 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, +using extended_conversion_category_impl_helper = mp11::mp_eval_if_not< + std::is_same, T1, mp11::mp_eval_or_q, T1, mp11::mp_quote, T2>; template< class Ctx, class T, class Dir > -struct conversion_category_impl +struct extended_conversion_category_impl { using type = mp11::mp_fold< mp11::mp_list< mp11::mp_defer, mp11::mp_defer, - mp11::mp_defer>, - no_conversion_tag, - conversion_category_impl_helper>; + mp11::mp_defer>, + unknown_category, + extended_conversion_category_impl_helper>; }; template< class Ctx, class T, class Dir > -using conversion_category = - typename conversion_category_impl< Ctx, T, Dir >::type; +using extended_conversion_category = + typename extended_conversion_category_impl::type; template< class T > -using any_conversion_tag = mp11::mp_not< - std::is_same< T, no_conversion_tag > >; +using any_conversion_tag = mp11::mp_not< std::is_same >; template< class T, class Dir, class... Ctxs > -struct conversion_category_impl< std::tuple, T, Dir > +struct extended_conversion_category_impl< std::tuple, T, Dir > { using ctxs = mp11::mp_list< remove_cvref... >; using cats = mp11::mp_list< - conversion_category, T, Dir>... >; + extended_conversion_category, 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< cats, user_full_context_category >; + using context1 = mp11::mp_find< cats, user_context_category >; + using context0 = mp11::mp_find< cats, user_category >; 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, + unknown_category, mp11::mp_at, cats, index >; }; @@ -435,18 +427,18 @@ struct no_context template using can_convert = mp11::mp_not< std::is_same< - detail::conversion_category, - detail::no_conversion_tag>>; + detail::extended_conversion_category, + unknown_category>>; template using conversion_round_trips_helper = mp11::mp_or< std::is_same, - std::is_base_of, - std::is_base_of>; + is_user_conversion, + is_user_conversion>; template< class Ctx, class T, class Dir > using conversion_round_trips = conversion_round_trips_helper< - conversion_category, - conversion_category>>; + extended_conversion_category, + extended_conversion_category>>; template< class T1, class T2 > struct copy_cref_helper @@ -501,7 +493,7 @@ template< class T, class Dir, class... Ctxs > struct supported_context< std::tuple, T, Dir > { using Ctx = std::tuple; - using impl = conversion_category_impl; + using impl = extended_conversion_category_impl; using index = typename impl::index; using next_supported = supported_context< mp11::mp_at< typename impl::ctxs, index >, T, Dir >; diff --git a/include/boost/json/impl/serializer.hpp b/include/boost/json/impl/serializer.hpp index 0a7255d06..ae08c3130 100644 --- a/include/boost/json/impl/serializer.hpp +++ b/include/boost/json/impl/serializer.hpp @@ -54,7 +54,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 ) @@ -71,7 +71,7 @@ write_impl(null_like_conversion_tag, writer& w, stream& ss) template BOOST_FORCEINLINE bool -write_impl(bool_conversion_tag, writer& w, stream& ss) +write_impl(boolean_category, writer& w, stream& ss) { BOOST_ASSERT( w.p_ ); auto const t = *reinterpret_cast(w.p_); @@ -97,7 +97,7 @@ write_impl(bool_conversion_tag, writer& w, stream& ss) template BOOST_FORCEINLINE bool -write_impl(integral_conversion_tag, writer& w, stream& ss0) +write_impl(integer_category, writer& w, stream& ss0) { #if defined(_MSC_VER) # pragma warning( push ) @@ -161,7 +161,7 @@ write_impl(integral_conversion_tag, writer& w, stream& ss0) template BOOST_FORCEINLINE bool -write_impl(floating_point_conversion_tag, writer& w, stream& ss0) +write_impl(floating_point_category, writer& w, stream& ss0) { #if defined(_MSC_VER) # pragma warning( push ) @@ -182,7 +182,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 ) @@ -204,7 +204,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; @@ -276,7 +276,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; @@ -395,7 +395,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); @@ -522,7 +522,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; @@ -598,7 +598,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; @@ -682,7 +682,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; @@ -726,7 +726,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; @@ -775,7 +775,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 ) @@ -823,7 +823,7 @@ template bool write_impl(writer& w, stream& ss) { - using cat = detail::generic_conversion_category; + using cat = typename conversion_category_of::type; return write_impl( cat(), w, ss ); } diff --git a/include/boost/json/impl/serializer.ipp b/include/boost/json/impl/serializer.ipp index 15e46c909..09f601e2c 100644 --- a/include/boost/json/impl/serializer.ipp +++ b/include/boost/json/impl/serializer.ipp @@ -362,7 +362,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); } @@ -371,14 +371,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 diff --git a/test/conversion.cpp b/test/conversion.cpp index b566bf58d..1ff483927 100644 --- a/test/conversion.cpp +++ b/test/conversion.cpp @@ -44,6 +44,9 @@ struct pseudo_sequence1 struct pseudo_sequence2 : pseudo_sequence1 { }; +struct pseudo_sequence3 : pseudo_sequence1 +{ }; + struct pseudo_tuple1 { }; @@ -166,6 +169,10 @@ template <> struct is_sequence_like : std::false_type { }; +template <> +struct conversion_category_of : map_category +{ }; + template <> struct is_tuple_like : std::false_type { }; @@ -180,11 +187,18 @@ class conversion_test void run() { - BOOST_CORE_STATIC_ASSERT( is_string_like::value ); + BOOST_CORE_STATIC_ASSERT( + conversion_category_of::value + == conversion_category::string); BOOST_CORE_STATIC_ASSERT( !is_string_like::value ); + BOOST_CORE_STATIC_ASSERT( + conversion_category_of::value + == conversion_category::sequence); BOOST_CORE_STATIC_ASSERT( is_sequence_like::value ); - BOOST_CORE_STATIC_ASSERT( !is_sequence_like::value ); + BOOST_CORE_STATIC_ASSERT( + conversion_category_of::value + == conversion_category::map); BOOST_CORE_STATIC_ASSERT( is_tuple_like::value ); BOOST_CORE_STATIC_ASSERT( !is_tuple_like::value );