Skip to content

Commit a07e38b

Browse files
committed
is_variant_like
1 parent 557676b commit a07e38b

File tree

8 files changed

+209
-92
lines changed

8 files changed

+209
-92
lines changed

doc/qbk/conversion/basics.qbk

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,12 @@ 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_variant_like__]
100+
[`std::variant` and similar types, e.g. `boost::variant2::variant`.]
101+
[The result is equal to the result of conversion of the active variant
102+
alternative.]
103+
[The result holds the first alternative for which a conversion succeeds.]
98104
][
99105
[Type satisfying __is_optional_like__]
100106
[]

doc/qbk/main.qbk

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@
6565
[def __is_sequence_like__ [link json.ref.boost__json__is_sequence_like `is_sequence_like`]]
6666
[def __is_string_like__ [link json.ref.boost__json__is_string_like `is_string_like`]]
6767
[def __is_tuple_like__ [link json.ref.boost__json__is_tuple_like `is_tuple_like`]]
68+
[def __is_variant_like__ [link json.ref.boost__json__is_variant_like `is_variant_like`]]
6869
[def __key_value_pair__ [link json.ref.boost__json__key_value_pair `key_value_pair`]]
6970
[def __kind__ [link json.ref.boost__json__kind `kind`]]
7071
[def __make_shared_resource__ [link json.ref.boost__json__make_shared_resource `make_shared_resource`]]

doc/qbk/quickref.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,7 @@
7373
<member><link linkend="json.ref.boost__json__is_sequence_like">is_sequence_like</link></member>
7474
<member><link linkend="json.ref.boost__json__is_string_like">is_string_like</link></member>
7575
<member><link linkend="json.ref.boost__json__is_tuple_like">is_tuple_like</link></member>
76+
<member><link linkend="json.ref.boost__json__is_variant_like">is_variant_like</link></member>
7677
<member><link linkend="json.ref.boost__json__result_for">result_for</link></member>
7778
<member><link linkend="json.ref.boost__json__try_value_to_tag">try_value_to_tag</link></member>
7879
<member><link linkend="json.ref.boost__json__value_from_tag">value_from_tag</link></member>

include/boost/json/conversion.hpp

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

334+
/** Determine if `T` should be treated as a variant
335+
336+
Variants are serialised the same way their active alternative is
337+
serialised. The opposite conversion selects the first alternative for which
338+
conversion succeeds.<br>
339+
340+
Given `t`, a glvalue of type ` const T`, if
341+
<tt>t.valueless_by_exception()</tt> is well-formed, then the trait provides
342+
the member constant `value` that is equal to `true`. Otherwise, `value` is
343+
equal to `false`.<br>
344+
345+
Users can specialize the trait for their own types if they don't want them
346+
to be treated as variants. For example:
347+
348+
@code
349+
namespace boost {
350+
namespace json {
351+
352+
template <>
353+
struct is_variant_like<your::variant> : std::false_type
354+
{ };
355+
356+
} // namespace boost
357+
} // namespace json
358+
@endcode
359+
*/
360+
template<class T>
361+
struct is_variant_like;
362+
334363
/** Determine if `T` should be treated as an optional
335364
336365
Optionals are serialised as `null` if empty, or as the stored type

include/boost/json/detail/value_from.hpp

Lines changed: 22 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -152,22 +152,6 @@ value_from_impl( no_conversion_tag, value&, T&&, Ctx const& )
152152
"No suitable tag_invoke overload found for the type");
153153
}
154154

155-
#ifndef BOOST_NO_CXX17_HDR_VARIANT
156-
template< class Ctx >
157-
struct value_from_visitor
158-
{
159-
value& jv;
160-
Ctx const& ctx;
161-
162-
template<class T>
163-
void
164-
operator()(T&& t)
165-
{
166-
value_from( static_cast<T&&>(t), ctx, jv );
167-
}
168-
};
169-
#endif // BOOST_NO_CXX17_HDR_VARIANT
170-
171155
template< class Ctx, class T >
172156
struct from_described_member
173157
{
@@ -243,6 +227,28 @@ value_from_impl(
243227
jv = nullptr;
244228
}
245229

230+
// variants
231+
template< class Ctx >
232+
struct value_from_visitor
233+
{
234+
value& jv;
235+
Ctx const& ctx;
236+
237+
template<class T>
238+
void
239+
operator()(T&& t)
240+
{
241+
value_from( static_cast<T&&>(t), ctx, jv );
242+
}
243+
};
244+
245+
template< class Ctx, class T >
246+
void
247+
value_from_impl( variant_conversion_tag, value& jv, T&& from, Ctx const& ctx )
248+
{
249+
visit( value_from_visitor<Ctx>{ jv, ctx }, static_cast<T&&>(from) );
250+
}
251+
246252
//----------------------------------------------------------
247253
// Contextual conversions
248254

@@ -266,28 +272,6 @@ tag_invoke(
266272
}
267273
#endif
268274

269-
#ifndef BOOST_NO_CXX17_HDR_VARIANT
270-
// std::variant
271-
template< class Ctx, class... Ts >
272-
void
273-
tag_invoke(
274-
value_from_tag, value& jv, std::variant<Ts...>&& from, Ctx const& ctx )
275-
{
276-
std::visit( detail::value_from_visitor<Ctx>{ jv, ctx }, std::move(from) );
277-
}
278-
279-
template< class Ctx, class... Ts >
280-
void
281-
tag_invoke(
282-
value_from_tag,
283-
value& jv,
284-
std::variant<Ts...> const& from,
285-
Ctx const& ctx )
286-
{
287-
std::visit( detail::value_from_visitor<Ctx>{ jv, ctx }, from );
288-
}
289-
#endif // BOOST_NO_CXX17_HDR_VARIANT
290-
291275
} // namespace json
292276
} // namespace boost
293277

include/boost/json/detail/value_to.hpp

Lines changed: 78 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -521,6 +521,84 @@ value_to_impl(
521521
return try_value_to<Inner>(jv, ctx);
522522
}
523523

524+
// variants
525+
template< class T, class V, class I >
526+
using variant_construction_category = mp11::mp_cond<
527+
std::is_constructible< T, variant2::in_place_index_t<I::value>, V >,
528+
mp11::mp_int<2>,
529+
#ifndef BOOST_NO_CXX17_HDR_VARIANT
530+
std::is_constructible< T, std::in_place_index_t<I::value>, V >,
531+
mp11::mp_int<1>,
532+
#endif // BOOST_NO_CXX17_HDR_VARIANT
533+
mp11::mp_true,
534+
mp11::mp_int<0> >;
535+
536+
template< class T, class I, class V >
537+
T
538+
initialize_variant( V&& v, mp11::mp_int<0> )
539+
{
540+
T t;
541+
t.template emplace<I::value>( std::move(v) );
542+
return t;
543+
}
544+
545+
template< class T, class I, class V >
546+
T
547+
initialize_variant( V&& v, mp11::mp_int<2> )
548+
{
549+
return T( variant2::in_place_index_t<I::value>(), std::move(v) );
550+
}
551+
552+
#ifndef BOOST_NO_CXX17_HDR_VARIANT
553+
template< class T, class I, class V >
554+
T
555+
initialize_variant( V&& v, mp11::mp_int<1> )
556+
{
557+
return T( std::in_place_index_t<I::value>(), std::move(v) );
558+
}
559+
#endif // BOOST_NO_CXX17_HDR_VARIANT
560+
561+
template< class T, class Ctx >
562+
struct alternative_converter
563+
{
564+
result<T>& res;
565+
value const& jv;
566+
Ctx const& ctx;
567+
568+
template< class I >
569+
void operator()( I ) const
570+
{
571+
if( res )
572+
return;
573+
574+
using V = mp11::mp_at<T, I>;
575+
auto attempt = try_value_to<V>(jv, ctx);
576+
if( attempt )
577+
{
578+
using cat = variant_construction_category<T, V, I>;
579+
res = initialize_variant<T, I>( std::move(*attempt), cat() );
580+
}
581+
}
582+
};
583+
584+
template< class T, class Ctx >
585+
result<T>
586+
value_to_impl(
587+
variant_conversion_tag,
588+
try_value_to_tag<T>,
589+
value const& jv,
590+
Ctx const& ctx)
591+
{
592+
error_code ec;
593+
BOOST_JSON_FAIL(ec, error::exhausted_variants);
594+
595+
using Is = mp11::mp_iota< mp11::mp_size<T> >;
596+
597+
result<T> res = {system::in_place_error, ec};
598+
mp11::mp_for_each<Is>( alternative_converter<T, Ctx>{res, jv, ctx} );
599+
return res;
600+
}
601+
524602
//----------------------------------------------------------
525603
// User-provided conversions; throwing -> throwing
526604
template< class T, class Ctx >
@@ -841,35 +919,6 @@ tag_invoke(
841919
}
842920
#endif
843921

844-
// std::variant
845-
#ifndef BOOST_NO_CXX17_HDR_VARIANT
846-
template< class... Ts, class Ctx1, class Ctx2 >
847-
result< std::variant<Ts...> >
848-
tag_invoke(
849-
try_value_to_tag< std::variant<Ts...> >,
850-
value const& jv,
851-
Ctx1 const&,
852-
Ctx2 const& ctx)
853-
{
854-
error_code ec;
855-
BOOST_JSON_FAIL(ec, error::exhausted_variants);
856-
857-
using Variant = std::variant<Ts...>;
858-
result<Variant> res = {system::in_place_error, ec};
859-
mp11::mp_for_each< mp11::mp_iota_c<sizeof...(Ts)> >([&](auto I) {
860-
if( res )
861-
return;
862-
863-
using T = std::variant_alternative_t<I.value, Variant>;
864-
auto attempt = try_value_to<T>(jv, ctx);
865-
if( attempt)
866-
res.emplace(std::in_place_index_t<I>(), std::move(*attempt));
867-
});
868-
869-
return res;
870-
}
871-
#endif // BOOST_NO_CXX17_HDR_VARIANT
872-
873922
} // namespace json
874923
} // namespace boost
875924

include/boost/json/impl/conversion.hpp

Lines changed: 49 additions & 25 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 variant_conversion_tag { };
155156
struct optional_conversion_tag { };
156157
struct no_conversion_tag { };
157158

@@ -217,40 +218,55 @@ template< class T >
217218
using described_bases = describe::describe_bases<
218219
T, describe::mod_any_access>;
219220

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-
221+
// user conversion (via tag_invoke)
241222
template< class Ctx, class T, class Dir >
242223
using user_conversion_category = mp11::mp_cond<
243-
// user conversion (via tag_invoke)
244224
has_user_conversion3<Ctx, T, Dir>, full_context_conversion_tag,
245225
has_user_conversion2<Ctx, T, Dir>, context_conversion_tag,
246226
has_user_conversion1<T, Dir>, user_conversion_tag>;
247227

228+
// native conversions (constructors and member functions of value)
229+
template< class T >
230+
using native_conversion_category = mp11::mp_cond<
231+
std::is_same<T, value>, value_conversion_tag,
232+
std::is_same<T, array>, array_conversion_tag,
233+
std::is_same<T, object>, object_conversion_tag,
234+
std::is_same<T, string>, string_conversion_tag>;
235+
236+
// generic conversions
237+
template< class T >
238+
using generic_conversion_category = mp11::mp_cond<
239+
std::is_same<T, bool>, bool_conversion_tag,
240+
std::is_arithmetic<T>, number_conversion_tag,
241+
is_null_like<T>, null_like_conversion_tag,
242+
is_string_like<T>, string_like_conversion_tag,
243+
is_map_like<T>, map_like_conversion_tag,
244+
is_sequence_like<T>, sequence_conversion_tag,
245+
is_tuple_like<T>, tuple_conversion_tag,
246+
is_described_class<T>, described_class_conversion_tag,
247+
is_described_enum<T>, described_enum_conversion_tag,
248+
is_variant_like<T>, variant_conversion_tag,
249+
is_optional_like<T>, optional_conversion_tag,
250+
// failed to find a suitable implementation
251+
mp11::mp_true, no_conversion_tag>;
252+
253+
template< class T >
254+
using nested_type = typename T::type;
255+
template< class T1, class T2 >
256+
using conversion_category_impl_helper = mp11::mp_eval_if_not<
257+
std::is_same<detail::no_conversion_tag, T1>,
258+
T1,
259+
mp11::mp_eval_or_q, T1, mp11::mp_quote<nested_type>, T2>;
248260
template< class Ctx, class T, class Dir >
249261
struct conversion_category_impl
250262
{
251-
using type = mp11::mp_eval_or<
252-
library_conversion_category<T>,
253-
user_conversion_category, Ctx, T, Dir>;
263+
using type = mp11::mp_fold<
264+
mp11::mp_list<
265+
mp11::mp_defer<user_conversion_category, Ctx, T, Dir>,
266+
mp11::mp_defer<native_conversion_category, T>,
267+
mp11::mp_defer<generic_conversion_category, T>>,
268+
no_conversion_tag,
269+
conversion_category_impl_helper>;
254270
};
255271
template< class Ctx, class T, class Dir >
256272
using conversion_category =
@@ -379,6 +395,10 @@ using value_result_type = typename std::decay<
379395
template< class T >
380396
using can_reset = decltype( std::declval<T&>().reset() );
381397

398+
template< class T >
399+
using has_valueless_by_exception =
400+
decltype( std::declval<T const&>().valueless_by_exception() );
401+
382402
} // namespace detail
383403

384404
template <class T>
@@ -442,6 +462,10 @@ struct is_described_enum
442462
: describe::has_describe_enumerators<T>
443463
{ };
444464

465+
template<class T>
466+
struct is_variant_like : mp11::mp_valid<detail::has_valueless_by_exception, T>
467+
{ };
468+
445469
template<class T>
446470
struct is_optional_like
447471
: mp11::mp_and<

0 commit comments

Comments
 (0)