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