Skip to content

Commit ca2a1b5

Browse files
committed
is_optional_like
1 parent 0c1b469 commit ca2a1b5

File tree

7 files changed

+118
-72
lines changed

7 files changed

+118
-72
lines changed

doc/qbk/conversion/basics.qbk

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,14 @@ following list of categories. The first matching category is selected.
9595
to the input value converted to its underlying type.]
9696
[The result is the described enumerator, corresponding to the input
9797
__string__.]
98+
][
99+
[Type satisfying __is_optional_like__]
100+
[]
101+
[If the input value is empty, the result is a `null`. Otherwise it is
102+
equivalent to conversion of the object stored inside of optional.]
103+
[The result is default constructed if the input value is `null`. Otherwise
104+
the result is constructed from the result of conversion of the input to
105+
the type stored in optional.]
98106
]]
99107

100108
For composite types (sequences, tuples, described classes, etc.) conversion of

doc/qbk/main.qbk

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@
6161
[def __is_described_enum__ [link json.ref.boost__json__is_described_enum `is_described_enum`]]
6262
[def __is_map_like__ [link json.ref.boost__json__is_map_like `is_map_like`]]
6363
[def __is_null_like__ [link json.ref.boost__json__is_null_like `is_null_like`]]
64+
[def __is_optional_like__ [link json.ref.boost__json__is_optional_like `is_optional_like`]]
6465
[def __is_sequence_like__ [link json.ref.boost__json__is_sequence_like `is_sequence_like`]]
6566
[def __is_string_like__ [link json.ref.boost__json__is_string_like `is_string_like`]]
6667
[def __is_tuple_like__ [link json.ref.boost__json__is_tuple_like `is_tuple_like`]]

doc/qbk/quickref.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@
6969
<member><link linkend="json.ref.boost__json__is_described_enum">is_described_enum</link></member>
7070
<member><link linkend="json.ref.boost__json__is_map_like">is_map_like</link></member>
7171
<member><link linkend="json.ref.boost__json__is_null_like">is_null_like</link></member>
72+
<member><link linkend="json.ref.boost__json__is_optional_like">is_optional_like</link></member>
7273
<member><link linkend="json.ref.boost__json__is_sequence_like">is_sequence_like</link></member>
7374
<member><link linkend="json.ref.boost__json__is_string_like">is_string_like</link></member>
7475
<member><link linkend="json.ref.boost__json__is_tuple_like">is_tuple_like</link></member>

include/boost/json/conversion.hpp

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -331,6 +331,38 @@ struct is_described_class;
331331
template<class T>
332332
struct is_described_enum;
333333

334+
/** Determine if `T` should be treated as an optional
335+
336+
Optionals are serialised as `null` if empty, or as the stored type
337+
otherwise.<br>
338+
339+
Given `t`, a glvalue of type `T`, if
340+
341+
@li <tt>decltype( t.value() )</tt> is well-formed and isn't a void type; and
342+
343+
@li <tt>t.reset()</tt> is well-formed;
344+
345+
then the trait provides the member constant `value`
346+
that is equal to `true`. Otherwise, `value` is equal to `false`.<br>
347+
348+
Users can specialize the trait for their own types if they don't want them
349+
to be treated as optionals. For example:
350+
351+
@code
352+
namespace boost {
353+
namespace json {
354+
355+
template <>
356+
struct is_optional_like<your::optional> : std::false_type
357+
{ };
358+
359+
} // namespace boost
360+
} // namespace json
361+
@endcode
362+
*/
363+
template<class T>
364+
struct is_optional_like;
365+
334366
} // namespace json
335367
} // namespace boost
336368

include/boost/json/detail/value_from.hpp

Lines changed: 12 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -231,38 +231,28 @@ value_from_impl(
231231
#endif
232232
}
233233

234-
//----------------------------------------------------------
235-
// Contextual conversions
236-
237-
template< class Ctx, class T >
238-
using value_from_category = conversion_category<
239-
Ctx, T, value_from_conversion >;
240-
241-
} // detail
242-
243-
#ifndef BOOST_NO_CXX17_HDR_OPTIONAL
234+
// optionals
244235
template< class T, class Ctx >
245236
void
246-
tag_invoke(
247-
value_from_tag, value& jv, std::optional<T> const& from, Ctx const& ctx )
237+
value_from_impl(
238+
optional_conversion_tag, value& jv, T&& from, Ctx const& ctx )
248239
{
249240
if( from )
250241
value_from( *from, ctx, jv );
251242
else
252243
jv = nullptr;
253244
}
254245

255-
template< class T, class Ctx >
256-
void
257-
tag_invoke(
258-
value_from_tag, value& jv, std::optional<T>&& from, Ctx const& ctx )
259-
{
260-
if( from )
261-
value_from( std::move(*from), ctx, jv );
262-
else
263-
jv = nullptr;
264-
}
246+
//----------------------------------------------------------
247+
// Contextual conversions
265248

249+
template< class Ctx, class T >
250+
using value_from_category = conversion_category<
251+
Ctx, T, value_from_conversion >;
252+
253+
} // detail
254+
255+
#ifndef BOOST_NO_CXX17_HDR_OPTIONAL
266256
inline
267257
void
268258
tag_invoke(

include/boost/json/detail/value_to.hpp

Lines changed: 17 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -376,18 +376,6 @@ value_to_impl(
376376
*arr, ctx, boost::mp11::make_index_sequence<N>());
377377
}
378378

379-
template< class T>
380-
struct is_optional
381-
: std::false_type
382-
{ };
383-
384-
#ifndef BOOST_NO_CXX17_HDR_OPTIONAL
385-
template< class T>
386-
struct is_optional< std::optional<T> >
387-
: std::true_type
388-
{ };
389-
#endif // BOOST_NO_CXX17_HDR_OPTIONAL
390-
391379
template< class Ctx, class T, bool non_throwing = true >
392380
struct to_described_member
393381
{
@@ -418,7 +406,7 @@ struct to_described_member
418406
auto const found = obj.find(D::name);
419407
if( found == obj.end() )
420408
{
421-
BOOST_IF_CONSTEXPR( !is_optional<M>::value )
409+
BOOST_IF_CONSTEXPR( !is_optional_like<M>::value )
422410
{
423411
error_code ec;
424412
BOOST_JSON_FAIL(ec, error::unknown_name);
@@ -517,6 +505,22 @@ value_to_impl(
517505
return {system::in_place_value, val};
518506
}
519507

508+
// optionals
509+
template< class T, class Ctx >
510+
result<T>
511+
value_to_impl(
512+
optional_conversion_tag,
513+
try_value_to_tag<T>,
514+
value const& jv,
515+
Ctx const& ctx)
516+
{
517+
using Inner = value_result_type<T>;
518+
if( jv.is_null() )
519+
return {};
520+
else
521+
return try_value_to<Inner>(jv, ctx);
522+
}
523+
520524
//----------------------------------------------------------
521525
// User-provided conversions; throwing -> throwing
522526
template< class T, class Ctx >
@@ -822,22 +826,7 @@ using value_to_category = conversion_category<
822826

823827
} // detail
824828

825-
// std::optional
826829
#ifndef BOOST_NO_CXX17_HDR_OPTIONAL
827-
template< class T, class Ctx1, class Ctx2 >
828-
result< std::optional<T> >
829-
tag_invoke(
830-
try_value_to_tag< std::optional<T> >,
831-
value const& jv,
832-
Ctx1 const&,
833-
Ctx2 const& ctx)
834-
{
835-
if( jv.is_null() )
836-
return std::optional<T>();
837-
else
838-
return try_value_to<T>(jv, ctx);
839-
}
840-
841830
inline
842831
result<std::nullopt_t>
843832
tag_invoke(

include/boost/json/impl/conversion.hpp

Lines changed: 47 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,7 @@ struct sequence_conversion_tag { };
152152
struct tuple_conversion_tag { };
153153
struct described_class_conversion_tag { };
154154
struct described_enum_conversion_tag { };
155+
struct optional_conversion_tag { };
155156
struct no_conversion_tag { };
156157

157158
template<class... Args>
@@ -216,31 +217,40 @@ template< class T >
216217
using described_bases = describe::describe_bases<
217218
T, describe::mod_any_access>;
218219

220+
template< class T >
221+
using library_conversion_category = mp11::mp_cond<
222+
// native conversions (constructors and member functions of value)
223+
std::is_same<T, value>, value_conversion_tag,
224+
std::is_same<T, array>, array_conversion_tag,
225+
std::is_same<T, object>, object_conversion_tag,
226+
std::is_same<T, string>, string_conversion_tag,
227+
std::is_same<T, bool>, bool_conversion_tag,
228+
std::is_arithmetic<T>, number_conversion_tag,
229+
// generic conversions
230+
is_null_like<T>, null_like_conversion_tag,
231+
is_string_like<T>, string_like_conversion_tag,
232+
is_map_like<T>, map_like_conversion_tag,
233+
is_sequence_like<T>, sequence_conversion_tag,
234+
is_tuple_like<T>, tuple_conversion_tag,
235+
is_described_class<T>, described_class_conversion_tag,
236+
is_described_enum<T>, described_enum_conversion_tag,
237+
is_optional_like<T>, optional_conversion_tag,
238+
// failed to find a suitable implementation
239+
mp11::mp_true, no_conversion_tag>;
240+
241+
template< class Ctx, class T, class Dir >
242+
using user_conversion_category = mp11::mp_cond<
243+
// user conversion (via tag_invoke)
244+
has_user_conversion3<Ctx, T, Dir>, full_context_conversion_tag,
245+
has_user_conversion2<Ctx, T, Dir>, context_conversion_tag,
246+
has_user_conversion1<T, Dir>, user_conversion_tag>;
247+
219248
template< class Ctx, class T, class Dir >
220249
struct conversion_category_impl
221250
{
222-
using type = mp11::mp_cond<
223-
// user conversion (via tag_invoke)
224-
has_user_conversion3<Ctx, T, Dir>, full_context_conversion_tag,
225-
has_user_conversion2<Ctx, T, Dir>, context_conversion_tag,
226-
has_user_conversion1<T, Dir>, user_conversion_tag,
227-
// native conversions (constructors and member functions of value)
228-
std::is_same<T, value>, value_conversion_tag,
229-
std::is_same<T, array>, array_conversion_tag,
230-
std::is_same<T, object>, object_conversion_tag,
231-
std::is_same<T, string>, string_conversion_tag,
232-
std::is_same<T, bool>, bool_conversion_tag,
233-
std::is_arithmetic<T>, number_conversion_tag,
234-
// generic conversions
235-
is_null_like<T>, null_like_conversion_tag,
236-
is_string_like<T>, string_like_conversion_tag,
237-
is_map_like<T>, map_like_conversion_tag,
238-
is_sequence_like<T>, sequence_conversion_tag,
239-
is_tuple_like<T>, tuple_conversion_tag,
240-
is_described_class<T>, described_class_conversion_tag,
241-
is_described_enum<T>, described_enum_conversion_tag,
242-
// failed to find a suitable implementation
243-
mp11::mp_true, no_conversion_tag>;
251+
using type = mp11::mp_eval_or<
252+
library_conversion_category<T>,
253+
user_conversion_category, Ctx, T, Dir>;
244254
};
245255
template< class Ctx, class T, class Dir >
246256
using conversion_category =
@@ -362,6 +372,13 @@ struct supported_context< std::tuple<Ctxs...>, T, Dir >
362372
}
363373
};
364374

375+
template< class T >
376+
using value_result_type = typename std::decay<
377+
decltype( std::declval<T&>().value() )>::type;
378+
379+
template< class T >
380+
using can_reset = decltype( std::declval<T&>().reset() );
381+
365382
} // namespace detail
366383

367384
template <class T>
@@ -425,6 +442,14 @@ struct is_described_enum
425442
: describe::has_describe_enumerators<T>
426443
{ };
427444

445+
template<class T>
446+
struct is_optional_like
447+
: mp11::mp_and<
448+
mp11::mp_not<std::is_void<
449+
mp11::mp_eval_or<void, detail::value_result_type, T>>>,
450+
mp11::mp_valid<detail::can_reset, T>>
451+
{ };
452+
428453
} // namespace json
429454
} // namespace boost
430455

0 commit comments

Comments
 (0)