detail/value_to.hpp

100.0% Lines (132/132) 97.3% Functions (367/377)
detail/value_to.hpp
Line TLA Hits Source Code
1 //
2 // Copyright (c) 2019 Vinnie Falco (vinnie.falco@gmail.com)
3 // Copyright (c) 2020 Krystian Stasiowski (sdkrystian@gmail.com)
4 // Copyright (c) 2021 Dmitry Arkhipov (grisumbras@gmail.com)
5 //
6 // Distributed under the Boost Software License, Version 1.0. (See accompanying
7 // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
8 //
9 // Official repository: https://github.com/boostorg/json
10 //
11
12 #ifndef BOOST_JSON_DETAIL_VALUE_TO_HPP
13 #define BOOST_JSON_DETAIL_VALUE_TO_HPP
14
15 #include <boost/core/detail/static_assert.hpp>
16 #include <boost/json/value.hpp>
17 #include <boost/json/conversion.hpp>
18 #include <boost/json/result_for.hpp>
19 #include <boost/describe/enum_from_string.hpp>
20
21 #ifndef BOOST_NO_CXX17_HDR_OPTIONAL
22 # include <optional>
23 #endif
24
25 namespace boost {
26 namespace json {
27
28 namespace detail {
29
30 template<class T>
31 using has_reserve_member_helper = decltype(std::declval<T&>().reserve(0));
32 template<class T>
33 using has_reserve_member = mp11::mp_valid<has_reserve_member_helper, T>;
34 template<class T>
35 using reserve_implementation = mp11::mp_cond<
36 is_tuple_like<T>, mp11::mp_int<2>,
37 has_reserve_member<T>, mp11::mp_int<1>,
38 mp11::mp_true, mp11::mp_int<0>>;
39
40 template<class T>
41 error
42 41 try_reserve(
43 T&,
44 std::size_t size,
45 mp11::mp_int<2>)
46 {
47 41 constexpr std::size_t N = std::tuple_size<remove_cvref<T>>::value;
48 41 if ( N != size )
49 30 return error::size_mismatch;
50 11 return error();
51 }
52
53 template<typename T>
54 error
55 74 try_reserve(
56 T& cont,
57 std::size_t size,
58 mp11::mp_int<1>)
59 {
60 74 cont.reserve(size);
61 74 return error();
62 }
63
64 template<typename T>
65 error
66 57 try_reserve(
67 T&,
68 std::size_t,
69 mp11::mp_int<0>)
70 {
71 57 return error();
72 }
73
74
75 // identity conversion
76 template< class Ctx >
77 system::result<value>
78 value_to_impl(
79 value_conversion_tag,
80 try_value_to_tag<value>,
81 value const& jv,
82 Ctx const& )
83 {
84 return jv;
85 }
86
87 template< class Ctx >
88 value
89 value_to_impl(
90 value_conversion_tag, value_to_tag<value>, value const& jv, Ctx const& )
91 {
92 return jv;
93 }
94
95 // object
96 template< class Ctx >
97 system::result<object>
98 12 value_to_impl(
99 object_conversion_tag,
100 try_value_to_tag<object>,
101 value const& jv,
102 Ctx const& )
103 {
104 12 object const* obj = jv.if_object();
105 12 if( obj )
106 6 return *obj;
107 6 system::error_code ec;
108 6 BOOST_JSON_FAIL(ec, error::not_object);
109 6 return ec;
110 }
111
112 // array
113 template< class Ctx >
114 system::result<array>
115 12 value_to_impl(
116 array_conversion_tag,
117 try_value_to_tag<array>,
118 value const& jv,
119 Ctx const& )
120 {
121 12 array const* arr = jv.if_array();
122 12 if( arr )
123 6 return *arr;
124 6 system::error_code ec;
125 6 BOOST_JSON_FAIL(ec, error::not_array);
126 6 return ec;
127 }
128
129 // string
130 template< class Ctx >
131 system::result<string>
132 12 value_to_impl(
133 string_conversion_tag,
134 try_value_to_tag<string>,
135 value const& jv,
136 Ctx const& )
137 {
138 12 string const* str = jv.if_string();
139 12 if( str )
140 6 return *str;
141 6 system::error_code ec;
142 6 BOOST_JSON_FAIL(ec, error::not_string);
143 6 return ec;
144 }
145
146 // bool
147 template< class Ctx >
148 system::result<bool>
149 49 value_to_impl(
150 bool_conversion_tag, try_value_to_tag<bool>, value const& jv, Ctx const& )
151 {
152 49 auto b = jv.if_bool();
153 49 if( b )
154 42 return *b;
155 7 system::error_code ec;
156 7 BOOST_JSON_FAIL(ec, error::not_bool);
157 7 return {boost::system::in_place_error, ec};
158 }
159
160 // integral and floating point
161 template< class T, class Ctx >
162 system::result<T>
163 3396 value_to_impl(
164 number_conversion_tag, try_value_to_tag<T>, value const& jv, Ctx const& )
165 {
166 3396 system::error_code ec;
167 3396 auto const n = jv.to_number<T>(ec);
168 3396 if( ec.failed() )
169 55 return {boost::system::in_place_error, ec};
170 3341 return {boost::system::in_place_value, n};
171 }
172
173 // null-like conversion
174 template< class T, class Ctx >
175 system::result<T>
176 56 value_to_impl(
177 null_like_conversion_tag,
178 try_value_to_tag<T>,
179 value const& jv,
180 Ctx const& )
181 {
182 56 if( jv.is_null() )
183 35 return {boost::system::in_place_value, T{}};
184 21 system::error_code ec;
185 21 BOOST_JSON_FAIL(ec, error::not_null);
186 21 return {boost::system::in_place_error, ec};
187 }
188
189 // string-like types
190 template< class T, class Ctx >
191 system::result<T>
192 79 value_to_impl(
193 string_like_conversion_tag,
194 try_value_to_tag<T>,
195 value const& jv,
196 Ctx const& )
197 {
198 79 auto str = jv.if_string();
199 79 if( str )
200 67 return {boost::system::in_place_value, T(str->subview())};
201 12 system::error_code ec;
202 12 BOOST_JSON_FAIL(ec, error::not_string);
203 12 return {boost::system::in_place_error, ec};
204 }
205
206 // map-like containers
207 template< class T, class Ctx >
208 system::result<T>
209 74 value_to_impl(
210 map_like_conversion_tag,
211 try_value_to_tag<T>,
212 value const& jv,
213 Ctx const& ctx )
214 {
215 148 return jv.try_as_object() & [&](object const& jo)
216 -> system::result<T>
217 {
218 T res;
219 error const e = detail::try_reserve(
220 res, jo.size(), reserve_implementation<T>());
221 if( e != error() )
222 {
223 system::error_code ec;
224 BOOST_JSON_FAIL( ec, e );
225 return {boost::system::in_place_error, ec};
226 }
227
228 auto ins = detail::inserter(res, inserter_implementation<T>());
229 for(key_value_pair const& kv: jo)
230 {
231 auto elem_res = try_value_to<mapped_type<T>>( kv.value(), ctx );
232 if( elem_res.has_error() )
233 return {boost::system::in_place_error, elem_res.error()};
234 *ins++ = value_type<T>{
235 key_type<T>(kv.key()),
236 std::move(elem_res.unsafe_value())};
237 }
238 return res;
239 148 };
240 }
241
242 // all other containers
243 template< class T, class Ctx >
244 system::result<T>
245 119 value_to_impl(
246 sequence_conversion_tag,
247 try_value_to_tag<T>,
248 value const& jv,
249 Ctx const& ctx )
250 {
251 119 array const* arr = jv.if_array();
252 119 if( !arr )
253 {
254 12 system::error_code ec;
255 12 BOOST_JSON_FAIL(ec, error::not_array);
256 12 return {boost::system::in_place_error, ec};
257 }
258
259 79 T result;
260 107 error const e = detail::try_reserve(
261 result, arr->size(), reserve_implementation<T>());
262 107 if( e != error() )
263 {
264 18 system::error_code ec;
265 18 BOOST_JSON_FAIL( ec, e );
266 18 return {boost::system::in_place_error, ec};
267 }
268
269 89 auto ins = detail::inserter(result, inserter_implementation<T>());
270 3344 for( value const& val: *arr )
271 {
272 3229 auto elem_res = try_value_to<value_type<T>>( val, ctx );
273 3229 if( elem_res.has_error() )
274 13 return {boost::system::in_place_error, elem_res.error()};
275 3216 *ins++ = std::move(elem_res.unsafe_value());
276 }
277 76 return result;
278 79 }
279
280 // tuple-like types
281 template< class T, class Ctx >
282 system::result<T>
283 248 try_make_tuple_elem(value const& jv, Ctx const& ctx, system::error_code& ec)
284 {
285 248 if( ec.failed() )
286 38 return {boost::system::in_place_error, ec};
287
288 210 auto result = try_value_to<T>( jv, ctx );
289 210 ec = result.error();
290 210 return result;
291 57 }
292
293 template <class T, class Ctx, std::size_t... Is>
294 system::result<T>
295 97 try_make_tuple_like(
296 array const& arr, Ctx const& ctx, boost::mp11::index_sequence<Is...>)
297 {
298 97 system::error_code ec;
299 115 auto items = std::make_tuple(
300 try_make_tuple_elem<
301 117 typename std::decay<tuple_element_t<Is, T>>::type >(
302 arr[Is], ctx, ec)
303 ...);
304 #if defined(BOOST_GCC)
305 # pragma GCC diagnostic push
306 # pragma GCC diagnostic ignored "-Wmaybe-uninitialized"
307 #endif
308 97 if( ec.failed() )
309 13 return {boost::system::in_place_error, ec};
310 #if defined(BOOST_GCC)
311 # pragma GCC diagnostic pop
312 #endif
313
314 #if defined(BOOST_CLANG)
315 # pragma clang diagnostic push
316 # pragma clang diagnostic ignored "-Wmissing-braces"
317 #endif
318 return {
319 boost::system::in_place_value,
320 87 T{ (std::move(*std::get<Is>(items)))... }
321 87 };
322 #if defined(BOOST_CLANG)
323 # pragma clang diagnostic pop
324 #endif
325 54 }
326
327 template< class T, class Ctx >
328 system::result<T>
329 121 value_to_impl(
330 tuple_conversion_tag,
331 try_value_to_tag<T>,
332 value const& jv,
333 Ctx const& ctx )
334 {
335 121 system::error_code ec;
336
337 121 array const* arr = jv.if_array();
338 121 if( !arr )
339 {
340 12 BOOST_JSON_FAIL(ec, error::not_array);
341 12 return {boost::system::in_place_error, ec};
342 }
343
344 109 constexpr std::size_t N = std::tuple_size<remove_cvref<T>>::value;
345 109 if( N != arr->size() )
346 {
347 12 BOOST_JSON_FAIL(ec, error::size_mismatch);
348 12 return {boost::system::in_place_error, ec};
349 }
350
351 37 return try_make_tuple_like<T>(
352 97 *arr, ctx, boost::mp11::make_index_sequence<N>());
353 }
354
355 template< class Ctx, class T >
356 struct to_described_member
357 {
358 static_assert(
359 uniquely_named_members<T>::value,
360 "The type has several described members with the same name.");
361
362 using Ds = described_members<T>;
363
364 system::result<T>& res;
365 object const& obj;
366 Ctx const& ctx;
367
368 template< class I >
369 void
370 operator()(I)
371 {
372 if( !res )
373 return;
374
375 using D = mp11::mp_at<Ds, I>;
376 using M = described_member_t<T, D>;
377
378 auto const found = obj.find(D::name);
379 if( found == obj.end() )
380 {
381 BOOST_IF_CONSTEXPR( !is_optional_like<M>::value )
382 {
383 system::error_code ec;
384 BOOST_JSON_FAIL(ec, error::size_mismatch);
385 res = {boost::system::in_place_error, ec};
386 }
387 return;
388 }
389
390 #if defined(__GNUC__) && BOOST_GCC_VERSION >= 80000 && BOOST_GCC_VERSION < 11000
391 # pragma GCC diagnostic push
392 # pragma GCC diagnostic ignored "-Wunused"
393 # pragma GCC diagnostic ignored "-Wunused-variable"
394 #endif
395 auto member_res = try_value_to<M>( found->value(), ctx );
396 #if defined(__GNUC__) && BOOST_GCC_VERSION >= 80000 && BOOST_GCC_VERSION < 11000
397 # pragma GCC diagnostic pop
398 #endif
399 if( member_res )
400 (*res).* D::pointer = std::move(member_res.unsafe_value());
401 else
402 res = {boost::system::in_place_error, member_res.error()};
403 }
404 };
405
406 // described classes
407 template< class T, class Ctx >
408 system::result<T>
409 value_to_impl(
410 described_class_conversion_tag,
411 try_value_to_tag<T>,
412 value const& jv,
413 Ctx const& ctx )
414 {
415 BOOST_CORE_STATIC_ASSERT( std::is_default_constructible<T>::value );
416 system::result<T> res;
417
418 auto* obj = jv.if_object();
419 if( !obj )
420 {
421 system::error_code ec;
422 BOOST_JSON_FAIL(ec, error::not_object);
423 res = {boost::system::in_place_error, ec};
424 return res;
425 }
426
427 to_described_member<Ctx, T> member_converter{res, *obj, ctx};
428
429 using Ds = typename decltype(member_converter)::Ds;
430 constexpr std::size_t N = mp11::mp_size<Ds>::value;
431 mp11::mp_for_each< mp11::mp_iota_c<N> >(member_converter);
432
433 if( !res )
434 return res;
435
436 return res;
437 }
438
439 // described enums
440 template< class T, class Ctx >
441 system::result<T>
442 value_to_impl(
443 described_enum_conversion_tag,
444 try_value_to_tag<T>,
445 value const& jv,
446 Ctx const& )
447 {
448 T val = {};
449 (void)jv;
450 #ifdef BOOST_DESCRIBE_CXX14
451 system::error_code ec;
452
453 auto str = jv.if_string();
454 if( !str )
455 {
456 BOOST_JSON_FAIL(ec, error::not_string);
457 return {system::in_place_error, ec};
458 }
459
460 if( !describe::enum_from_string(str->data(), val) )
461 {
462 BOOST_JSON_FAIL(ec, error::unknown_name);
463 return {system::in_place_error, ec};
464 }
465 #endif
466
467 return {system::in_place_value, val};
468 }
469
470 // optionals
471 template< class T, class Ctx >
472 system::result<T>
473 value_to_impl(
474 optional_conversion_tag,
475 try_value_to_tag<T>,
476 value const& jv,
477 Ctx const& ctx)
478 {
479 using Inner = value_result_type<T>;
480 if( jv.is_null() )
481 return {};
482 else
483 return try_value_to<Inner>(jv, ctx);
484 }
485
486 // variants
487 template< class T, class V, class I >
488 using variant_construction_category = mp11::mp_cond<
489 std::is_constructible< T, variant2::in_place_index_t<I::value>, V >,
490 mp11::mp_int<2>,
491 #ifndef BOOST_NO_CXX17_HDR_VARIANT
492 std::is_constructible< T, std::in_place_index_t<I::value>, V >,
493 mp11::mp_int<1>,
494 #endif // BOOST_NO_CXX17_HDR_VARIANT
495 mp11::mp_true,
496 mp11::mp_int<0> >;
497
498 template< class T, class I, class V >
499 T
500 initialize_variant( V&& v, mp11::mp_int<0> )
501 {
502 T t;
503 t.template emplace<I::value>( std::move(v) );
504 return t;
505 }
506
507 template< class T, class I, class V >
508 T
509 initialize_variant( V&& v, mp11::mp_int<2> )
510 {
511 return T( variant2::in_place_index_t<I::value>(), std::move(v) );
512 }
513
514 #ifndef BOOST_NO_CXX17_HDR_VARIANT
515 template< class T, class I, class V >
516 T
517 initialize_variant( V&& v, mp11::mp_int<1> )
518 {
519 return T( std::in_place_index_t<I::value>(), std::move(v) );
520 }
521 #endif // BOOST_NO_CXX17_HDR_VARIANT
522
523
524 template< class T, class Ctx >
525 struct alternative_converter
526 {
527 system::result<T>& res;
528 value const& jv;
529 Ctx const& ctx;
530
531 template< class I >
532 void operator()( I ) const
533 {
534 if( res )
535 return;
536
537 using V = mp11::mp_at<T, I>;
538 auto attempt = try_value_to<V>(jv, ctx);
539 if( attempt )
540 {
541 using cat = variant_construction_category<T, V, I>;
542 res = initialize_variant<T, I>( std::move(*attempt), cat() );
543 }
544 }
545 };
546
547 template< class T, class Ctx >
548 system::result<T>
549 value_to_impl(
550 variant_conversion_tag,
551 try_value_to_tag<T>,
552 value const& jv,
553 Ctx const& ctx)
554 {
555 system::error_code ec;
556 BOOST_JSON_FAIL(ec, error::exhausted_variants);
557
558 using Is = mp11::mp_iota< mp11::mp_size<T> >;
559
560 system::result<T> res = {system::in_place_error, ec};
561 mp11::mp_for_each<Is>( alternative_converter<T, Ctx>{res, jv, ctx} );
562 return res;
563 }
564
565 template< class T, class Ctx >
566 system::result<T>
567 value_to_impl(
568 path_conversion_tag, try_value_to_tag<T>, value const& jv, Ctx const& )
569 {
570 auto str = jv.if_string();
571 if( !str )
572 {
573 system::error_code ec;
574 BOOST_JSON_FAIL(ec, error::not_string);
575 return {boost::system::in_place_error, ec};
576 }
577
578 string_view sv = str->subview();
579 return {boost::system::in_place_value, T( sv.begin(), sv.end() )};
580 }
581
582 //----------------------------------------------------------
583 // User-provided conversions; throwing -> throwing
584 template< class T, class Ctx >
585 mp11::mp_if< mp11::mp_valid<has_user_conversion_to_impl, T>, T >
586 1 value_to_impl(
587 user_conversion_tag, value_to_tag<T> tag, value const& jv, Ctx const&)
588 {
589 1 return tag_invoke(tag, jv);
590 }
591
592 template<
593 class T,
594 class Ctx,
595 class Sup = supported_context<Ctx, T, value_to_conversion>
596 >
597 mp11::mp_if<
598 mp11::mp_valid< has_context_conversion_to_impl, typename Sup::type, T>, T >
599 1 value_to_impl(
600 context_conversion_tag,
601 value_to_tag<T> tag,
602 value const& jv,
603 Ctx const& ctx )
604 {
605 1 return tag_invoke( tag, jv, Sup::get(ctx) );
606 }
607
608 template<
609 class T,
610 class Ctx,
611 class Sup = supported_context<Ctx, T, value_to_conversion>
612 >
613 mp11::mp_if<
614 mp11::mp_valid<
615 has_full_context_conversion_to_impl, typename Sup::type, T>,
616 T>
617 value_to_impl(
618 full_context_conversion_tag,
619 value_to_tag<T> tag,
620 value const& jv,
621 Ctx const& ctx )
622 {
623 return tag_invoke( tag, jv, Sup::get(ctx), ctx );
624 }
625
626 //----------------------------------------------------------
627 // User-provided conversions; throwing -> nonthrowing
628 template< class T, class Ctx >
629 mp11::mp_if_c< !mp11::mp_valid<has_user_conversion_to_impl, T>::value, T>
630 60 value_to_impl(
631 user_conversion_tag, value_to_tag<T>, value const& jv, Ctx const& )
632 {
633 66 return tag_invoke(try_value_to_tag<T>(), jv).value();
634 }
635
636 template<
637 class T,
638 class Ctx,
639 class Sup = supported_context<Ctx, T, value_to_conversion>
640 >
641 mp11::mp_if_c<
642 !mp11::mp_valid<
643 has_context_conversion_to_impl, typename Sup::type, T>::value,
644 T>
645 3 value_to_impl(
646 context_conversion_tag, value_to_tag<T>, value const& jv, Ctx const& ctx )
647 {
648 3 return tag_invoke( try_value_to_tag<T>(), jv, Sup::get(ctx) ).value();
649 }
650
651 template<
652 class T,
653 class Ctx,
654 class Sup = supported_context<Ctx, T, value_to_conversion>
655 >
656 mp11::mp_if_c<
657 !mp11::mp_valid<
658 has_full_context_conversion_to_impl, typename Sup::type, T>::value,
659 T>
660 value_to_impl(
661 full_context_conversion_tag,
662 value_to_tag<T>,
663 value const& jv,
664 Ctx const& ctx )
665 {
666 return tag_invoke(try_value_to_tag<T>(), jv, Sup::get(ctx), ctx).value();
667 }
668
669 //----------------------------------------------------------
670 // User-provided conversions; nonthrowing -> nonthrowing
671 template< class T, class Ctx >
672 mp11::mp_if<
673 mp11::mp_valid<
674 has_nonthrowing_user_conversion_to_impl, T>, system::result<T> >
675 124 value_to_impl(
676 user_conversion_tag, try_value_to_tag<T>, value const& jv, Ctx const& )
677 {
678 132 return tag_invoke(try_value_to_tag<T>(), jv);
679 }
680
681 template<
682 class T,
683 class Ctx,
684 class Sup = supported_context<Ctx, T, value_to_conversion>
685 >
686 mp11::mp_if<
687 mp11::mp_valid<
688 has_nonthrowing_context_conversion_to_impl, typename Sup::type, T>,
689 system::result<T> >
690 value_to_impl(
691 context_conversion_tag,
692 try_value_to_tag<T> tag,
693 value const& jv,
694 Ctx const& ctx )
695 {
696 return tag_invoke( tag, jv, Sup::get(ctx) );
697 }
698
699 template<
700 class T,
701 class Ctx,
702 class Sup = supported_context<Ctx, T, value_to_conversion>
703 >
704 mp11::mp_if<
705 mp11::mp_valid<
706 has_nonthrowing_full_context_conversion_to_impl,
707 typename Sup::type,
708 T>,
709 system::result<T> >
710 value_to_impl(
711 full_context_conversion_tag,
712 try_value_to_tag<T> tag,
713 value const& jv,
714 Ctx const& ctx )
715 {
716 return tag_invoke( tag, jv, Sup::get(ctx), ctx );
717 }
718
719 //----------------------------------------------------------
720 // User-provided conversions; nonthrowing -> throwing
721
722 template< class T, class... Args >
723 system::result<T>
724 54 wrap_conversion_exceptions( value_to_tag<T>, Args&& ... args )
725 {
726 #ifndef BOOST_NO_EXCEPTIONS
727 try
728 {
729 #endif
730 return {
731 boost::system::in_place_value,
732 54 tag_invoke( value_to_tag<T>(), static_cast<Args&&>(args)... )};
733 #ifndef BOOST_NO_EXCEPTIONS
734 }
735 30 catch( std::bad_alloc const&)
736 {
737 6 throw;
738 }
739 12 catch( system::system_error const& e)
740 {
741 12 return {boost::system::in_place_error, e.code()};
742 }
743 12 catch( ... )
744 {
745 6 system::error_code ec;
746 6 BOOST_JSON_FAIL(ec, error::exception);
747 6 return {boost::system::in_place_error, ec};
748 }
749 #endif
750 }
751
752 template< class T, class Ctx >
753 mp11::mp_if_c<
754 !mp11::mp_valid<has_nonthrowing_user_conversion_to_impl, T>::value,
755 system::result<T> >
756 54 value_to_impl(
757 user_conversion_tag, try_value_to_tag<T>, value const& jv, Ctx const& )
758 {
759 54 return wrap_conversion_exceptions(value_to_tag<T>(), jv);
760 }
761
762 template<
763 class T,
764 class Ctx,
765 class Sup = supported_context<Ctx, T, value_to_conversion>
766 >
767 mp11::mp_if_c<
768 !mp11::mp_valid<
769 has_nonthrowing_context_conversion_to_impl,
770 typename Sup::type,
771 T>::value,
772 system::result<T> >
773 value_to_impl(
774 context_conversion_tag,
775 try_value_to_tag<T>,
776 value const& jv,
777 Ctx const& ctx )
778 {
779 return wrap_conversion_exceptions( value_to_tag<T>(), jv, Sup::get(ctx) );
780 }
781
782 template<
783 class T,
784 class Ctx,
785 class Sup = supported_context<Ctx, T, value_to_conversion>
786 >
787 mp11::mp_if_c<
788 !mp11::mp_valid<
789 has_nonthrowing_full_context_conversion_to_impl,
790 typename Sup::type,
791 T>::value,
792 system::result<T> >
793 value_to_impl(
794 full_context_conversion_tag,
795 try_value_to_tag<T>,
796 value const& jv,
797 Ctx const& ctx )
798 {
799 return wrap_conversion_exceptions(
800 value_to_tag<T>(), jv, Sup::get(ctx), ctx);
801 }
802
803 // no suitable conversion implementation
804 template< class T, class Ctx >
805 T
806 value_to_impl( no_conversion_tag, value_to_tag<T>, value const&, Ctx const& )
807 {
808 static_assert(
809 !std::is_same<T, T>::value,
810 "No suitable tag_invoke overload found for the type");
811 }
812
813 // generic wrapper over non-throwing implementations
814 template< class Impl, class T, class Ctx >
815 T
816 345 value_to_impl( Impl impl, value_to_tag<T>, value const& jv, Ctx const& ctx )
817 {
818 345 return value_to_impl(impl, try_value_to_tag<T>(), jv, ctx).value();
819 }
820
821 template< class Ctx, class T >
822 using value_to_category = conversion_category<
823 Ctx, T, value_to_conversion >;
824
825 } // detail
826
827 #ifndef BOOST_NO_CXX17_HDR_OPTIONAL
828 inline
829 system::result<std::nullopt_t>
830 tag_invoke(
831 try_value_to_tag<std::nullopt_t>,
832 value const& jv)
833 {
834 if( jv.is_null() )
835 return std::nullopt;
836 system::error_code ec;
837 BOOST_JSON_FAIL(ec, error::not_null);
838 return ec;
839 }
840 #endif
841
842 } // namespace json
843 } // namespace boost
844
845 #endif
846