Skip to content

Commit

Permalink
is_optional_like
Browse files Browse the repository at this point in the history
  • Loading branch information
grisumbras committed Aug 23, 2023
1 parent 0c1b469 commit ca2a1b5
Show file tree
Hide file tree
Showing 7 changed files with 118 additions and 72 deletions.
8 changes: 8 additions & 0 deletions doc/qbk/conversion/basics.qbk
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,14 @@ following list of categories. The first matching category is selected.
to the input value converted to its underlying type.]
[The result is the described enumerator, corresponding to the input
__string__.]
][
[Type satisfying __is_optional_like__]
[]
[If the input value is empty, the result is a `null`. Otherwise it is
equivalent to conversion of the object stored inside of optional.]
[The result is default constructed if the input value is `null`. Otherwise
the result is constructed from the result of conversion of the input to
the type stored in optional.]
]]

For composite types (sequences, tuples, described classes, etc.) conversion of
Expand Down
1 change: 1 addition & 0 deletions doc/qbk/main.qbk
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@
[def __is_described_enum__ [link json.ref.boost__json__is_described_enum `is_described_enum`]]
[def __is_map_like__ [link json.ref.boost__json__is_map_like `is_map_like`]]
[def __is_null_like__ [link json.ref.boost__json__is_null_like `is_null_like`]]
[def __is_optional_like__ [link json.ref.boost__json__is_optional_like `is_optional_like`]]
[def __is_sequence_like__ [link json.ref.boost__json__is_sequence_like `is_sequence_like`]]
[def __is_string_like__ [link json.ref.boost__json__is_string_like `is_string_like`]]
[def __is_tuple_like__ [link json.ref.boost__json__is_tuple_like `is_tuple_like`]]
Expand Down
1 change: 1 addition & 0 deletions doc/qbk/quickref.xml
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@
<member><link linkend="json.ref.boost__json__is_described_enum">is_described_enum</link></member>
<member><link linkend="json.ref.boost__json__is_map_like">is_map_like</link></member>
<member><link linkend="json.ref.boost__json__is_null_like">is_null_like</link></member>
<member><link linkend="json.ref.boost__json__is_optional_like">is_optional_like</link></member>
<member><link linkend="json.ref.boost__json__is_sequence_like">is_sequence_like</link></member>
<member><link linkend="json.ref.boost__json__is_string_like">is_string_like</link></member>
<member><link linkend="json.ref.boost__json__is_tuple_like">is_tuple_like</link></member>
Expand Down
32 changes: 32 additions & 0 deletions include/boost/json/conversion.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -331,6 +331,38 @@ struct is_described_class;
template<class T>
struct is_described_enum;

/** Determine if `T` should be treated as an optional
Optionals are serialised as `null` if empty, or as the stored type
otherwise.<br>
Given `t`, a glvalue of type `T`, if
@li <tt>decltype( t.value() )</tt> is well-formed and isn't a void type; and
@li <tt>t.reset()</tt> is well-formed;
then the trait provides the member constant `value`
that is equal to `true`. Otherwise, `value` is equal to `false`.<br>
Users can specialize the trait for their own types if they don't want them
to be treated as optionals. For example:
@code
namespace boost {
namespace json {
template <>
struct is_optional_like<your::optional> : std::false_type
{ };
} // namespace boost
} // namespace json
@endcode
*/
template<class T>
struct is_optional_like;

} // namespace json
} // namespace boost

Expand Down
34 changes: 12 additions & 22 deletions include/boost/json/detail/value_from.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -231,38 +231,28 @@ value_from_impl(
#endif
}

//----------------------------------------------------------
// Contextual conversions

template< class Ctx, class T >
using value_from_category = conversion_category<
Ctx, T, value_from_conversion >;

} // detail

#ifndef BOOST_NO_CXX17_HDR_OPTIONAL
// optionals
template< class T, class Ctx >
void
tag_invoke(
value_from_tag, value& jv, std::optional<T> const& from, Ctx const& ctx )
value_from_impl(
optional_conversion_tag, value& jv, T&& from, Ctx const& ctx )
{
if( from )
value_from( *from, ctx, jv );
else
jv = nullptr;
}

template< class T, class Ctx >
void
tag_invoke(
value_from_tag, value& jv, std::optional<T>&& from, Ctx const& ctx )
{
if( from )
value_from( std::move(*from), ctx, jv );
else
jv = nullptr;
}
//----------------------------------------------------------
// Contextual conversions

template< class Ctx, class T >
using value_from_category = conversion_category<
Ctx, T, value_from_conversion >;

} // detail

#ifndef BOOST_NO_CXX17_HDR_OPTIONAL
inline
void
tag_invoke(
Expand Down
45 changes: 17 additions & 28 deletions include/boost/json/detail/value_to.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -376,18 +376,6 @@ value_to_impl(
*arr, ctx, boost::mp11::make_index_sequence<N>());
}

template< class T>
struct is_optional
: std::false_type
{ };

#ifndef BOOST_NO_CXX17_HDR_OPTIONAL
template< class T>
struct is_optional< std::optional<T> >
: std::true_type
{ };
#endif // BOOST_NO_CXX17_HDR_OPTIONAL

template< class Ctx, class T, bool non_throwing = true >
struct to_described_member
{
Expand Down Expand Up @@ -418,7 +406,7 @@ struct to_described_member
auto const found = obj.find(D::name);
if( found == obj.end() )
{
BOOST_IF_CONSTEXPR( !is_optional<M>::value )
BOOST_IF_CONSTEXPR( !is_optional_like<M>::value )
{
error_code ec;
BOOST_JSON_FAIL(ec, error::unknown_name);
Expand Down Expand Up @@ -517,6 +505,22 @@ value_to_impl(
return {system::in_place_value, val};
}

// optionals
template< class T, class Ctx >
result<T>
value_to_impl(
optional_conversion_tag,
try_value_to_tag<T>,
value const& jv,
Ctx const& ctx)
{
using Inner = value_result_type<T>;
if( jv.is_null() )
return {};
else
return try_value_to<Inner>(jv, ctx);
}

//----------------------------------------------------------
// User-provided conversions; throwing -> throwing
template< class T, class Ctx >
Expand Down Expand Up @@ -822,22 +826,7 @@ using value_to_category = conversion_category<

} // detail

// std::optional
#ifndef BOOST_NO_CXX17_HDR_OPTIONAL
template< class T, class Ctx1, class Ctx2 >
result< std::optional<T> >
tag_invoke(
try_value_to_tag< std::optional<T> >,
value const& jv,
Ctx1 const&,
Ctx2 const& ctx)
{
if( jv.is_null() )
return std::optional<T>();
else
return try_value_to<T>(jv, ctx);
}

inline
result<std::nullopt_t>
tag_invoke(
Expand Down
69 changes: 47 additions & 22 deletions include/boost/json/impl/conversion.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,7 @@ struct sequence_conversion_tag { };
struct tuple_conversion_tag { };
struct described_class_conversion_tag { };
struct described_enum_conversion_tag { };
struct optional_conversion_tag { };
struct no_conversion_tag { };

template<class... Args>
Expand Down Expand Up @@ -216,31 +217,40 @@ template< class T >
using described_bases = describe::describe_bases<
T, describe::mod_any_access>;

template< class T >
using library_conversion_category = mp11::mp_cond<
// native conversions (constructors and member functions of value)
std::is_same<T, value>, value_conversion_tag,
std::is_same<T, array>, array_conversion_tag,
std::is_same<T, object>, object_conversion_tag,
std::is_same<T, string>, string_conversion_tag,
std::is_same<T, bool>, bool_conversion_tag,
std::is_arithmetic<T>, number_conversion_tag,
// generic conversions
is_null_like<T>, null_like_conversion_tag,
is_string_like<T>, string_like_conversion_tag,
is_map_like<T>, map_like_conversion_tag,
is_sequence_like<T>, sequence_conversion_tag,
is_tuple_like<T>, tuple_conversion_tag,
is_described_class<T>, described_class_conversion_tag,
is_described_enum<T>, described_enum_conversion_tag,
is_optional_like<T>, optional_conversion_tag,
// failed to find a suitable implementation
mp11::mp_true, no_conversion_tag>;

template< class Ctx, class T, class Dir >
using user_conversion_category = mp11::mp_cond<
// user conversion (via tag_invoke)
has_user_conversion3<Ctx, T, Dir>, full_context_conversion_tag,
has_user_conversion2<Ctx, T, Dir>, context_conversion_tag,
has_user_conversion1<T, Dir>, user_conversion_tag>;

template< class Ctx, class T, class Dir >
struct conversion_category_impl
{
using type = mp11::mp_cond<
// user conversion (via tag_invoke)
has_user_conversion3<Ctx, T, Dir>, full_context_conversion_tag,
has_user_conversion2<Ctx, T, Dir>, context_conversion_tag,
has_user_conversion1<T, Dir>, user_conversion_tag,
// native conversions (constructors and member functions of value)
std::is_same<T, value>, value_conversion_tag,
std::is_same<T, array>, array_conversion_tag,
std::is_same<T, object>, object_conversion_tag,
std::is_same<T, string>, string_conversion_tag,
std::is_same<T, bool>, bool_conversion_tag,
std::is_arithmetic<T>, number_conversion_tag,
// generic conversions
is_null_like<T>, null_like_conversion_tag,
is_string_like<T>, string_like_conversion_tag,
is_map_like<T>, map_like_conversion_tag,
is_sequence_like<T>, sequence_conversion_tag,
is_tuple_like<T>, tuple_conversion_tag,
is_described_class<T>, described_class_conversion_tag,
is_described_enum<T>, described_enum_conversion_tag,
// failed to find a suitable implementation
mp11::mp_true, no_conversion_tag>;
using type = mp11::mp_eval_or<
library_conversion_category<T>,
user_conversion_category, Ctx, T, Dir>;
};
template< class Ctx, class T, class Dir >
using conversion_category =
Expand Down Expand Up @@ -362,6 +372,13 @@ struct supported_context< std::tuple<Ctxs...>, T, Dir >
}
};

template< class T >
using value_result_type = typename std::decay<
decltype( std::declval<T&>().value() )>::type;

template< class T >
using can_reset = decltype( std::declval<T&>().reset() );

} // namespace detail

template <class T>
Expand Down Expand Up @@ -425,6 +442,14 @@ struct is_described_enum
: describe::has_describe_enumerators<T>
{ };

template<class T>
struct is_optional_like
: mp11::mp_and<
mp11::mp_not<std::is_void<
mp11::mp_eval_or<void, detail::value_result_type, T>>>,
mp11::mp_valid<detail::can_reset, T>>
{ };

} // namespace json
} // namespace boost

Expand Down

0 comments on commit ca2a1b5

Please sign in to comment.