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 : using Ds = described_members<T>;
378 :
379 : system::result<T>& res;
380 : object const& obj;
381 : Ctx const& ctx;
382 :
383 : template< class I >
384 : void
385 : operator()(I)
386 : {
387 : if( !res )
388 : return;
389 :
390 : using D = mp11::mp_at<Ds, I>;
391 : using M = described_member_t<T, D>;
392 :
393 : auto const found = obj.find(D::name);
394 : if( found == obj.end() )
395 : {
396 : BOOST_IF_CONSTEXPR( !is_optional_like<M>::value )
397 : {
398 : system::error_code ec;
399 : BOOST_JSON_FAIL(ec, error::size_mismatch);
400 : res = {boost::system::in_place_error, ec};
401 :
402 : BOOST_JSON_INTRUSIVE_MESSAGE(std::format("the key >> {} << is non optional and missing in path {}", D::name, BOOST_JSON_INTRUSIVE::composePath()));
403 : }
404 : return;
405 : }
406 :
407 : BOOST_JSON_INTRUSIVE_PATH_PUSH(D::name)
408 :
409 : #if defined(__GNUC__) && BOOST_GCC_VERSION >= 80000 && BOOST_GCC_VERSION < 11000
410 : # pragma GCC diagnostic push
411 : # pragma GCC diagnostic ignored "-Wunused"
412 : # pragma GCC diagnostic ignored "-Wunused-variable"
413 : #endif
414 : auto member_res = try_value_to<M>( found->value(), ctx );
415 : #if defined(__GNUC__) && BOOST_GCC_VERSION >= 80000 && BOOST_GCC_VERSION < 11000
416 : # pragma GCC diagnostic pop
417 : #endif
418 : if( member_res ){
419 : (*res).* D::pointer = std::move(*member_res);
420 : BOOST_JSON_INTRUSIVE_PATH_POP
421 : }
422 : else
423 : res = {boost::system::in_place_error, member_res.error()};
424 : }
425 : };
426 :
427 : // described classes
428 : template< class T, class Ctx >
429 : system::result<T>
430 : value_to_impl(
431 : described_class_conversion_tag,
432 : try_value_to_tag<T>,
433 : value const& jv,
434 : Ctx const& ctx )
435 : {
436 : BOOST_STATIC_ASSERT( std::is_default_constructible<T>::value );
437 : system::result<T> res;
438 :
439 : auto* obj = jv.if_object();
440 : if( !obj )
441 : {
442 : system::error_code ec;
443 : BOOST_JSON_FAIL(ec, error::not_object);
444 : res = {boost::system::in_place_error, ec};
445 : return res;
446 : }
447 :
448 : to_described_member<Ctx, T> member_converter{res, *obj, ctx};
449 :
450 : using Ds = typename decltype(member_converter)::Ds;
451 : constexpr std::size_t N = mp11::mp_size<Ds>::value;
452 : mp11::mp_for_each< mp11::mp_iota_c<N> >(member_converter);
453 :
454 : if( !res )
455 : return res;
456 :
457 : return res;
458 : }
459 :
460 : // described enums
461 : template< class T, class Ctx >
462 : system::result<T>
463 : value_to_impl(
464 : described_enum_conversion_tag,
465 : try_value_to_tag<T>,
466 : value const& jv,
467 : Ctx const& )
468 : {
469 : T val = {};
470 : (void)jv;
471 : #ifdef BOOST_DESCRIBE_CXX14
472 : system::error_code ec;
473 :
474 : auto str = jv.if_string();
475 : if( !str )
476 : {
477 : BOOST_JSON_FAIL(ec, error::not_string);
478 : return {system::in_place_error, ec};
479 : }
480 :
481 : if( !describe::enum_from_string(str->data(), val) )
482 : {
483 : BOOST_JSON_FAIL(ec, error::unknown_name);
484 : return {system::in_place_error, ec};
485 : }
486 : #endif
487 :
488 : return {system::in_place_value, val};
489 : }
490 :
491 : // optionals
492 : template< class T, class Ctx >
493 : system::result<T>
494 : value_to_impl(
495 : optional_conversion_tag,
496 : try_value_to_tag<T>,
497 : value const& jv,
498 : Ctx const& ctx)
499 : {
500 : using Inner = value_result_type<T>;
501 : if( jv.is_null() )
502 : return {};
503 : else
504 : return try_value_to<Inner>(jv, ctx);
505 : }
506 :
507 : // variants
508 : template< class T, class V, class I >
509 : using variant_construction_category = mp11::mp_cond<
510 : std::is_constructible< T, variant2::in_place_index_t<I::value>, V >,
511 : mp11::mp_int<2>,
512 : #ifndef BOOST_NO_CXX17_HDR_VARIANT
513 : std::is_constructible< T, std::in_place_index_t<I::value>, V >,
514 : mp11::mp_int<1>,
515 : #endif // BOOST_NO_CXX17_HDR_VARIANT
516 : mp11::mp_true,
517 : mp11::mp_int<0> >;
518 :
519 : template< class T, class I, class V >
520 : T
521 : initialize_variant( V&& v, mp11::mp_int<0> )
522 : {
523 : T t;
524 : t.template emplace<I::value>( std::move(v) );
525 : return t;
526 : }
527 :
528 : template< class T, class I, class V >
529 : T
530 : initialize_variant( V&& v, mp11::mp_int<2> )
531 : {
532 : return T( variant2::in_place_index_t<I::value>(), std::move(v) );
533 : }
534 :
535 : #ifndef BOOST_NO_CXX17_HDR_VARIANT
536 : template< class T, class I, class V >
537 : T
538 : initialize_variant( V&& v, mp11::mp_int<1> )
539 : {
540 : return T( std::in_place_index_t<I::value>(), std::move(v) );
541 : }
542 : #endif // BOOST_NO_CXX17_HDR_VARIANT
543 :
544 :
545 : template< class T, class Ctx >
546 : struct alternative_converter
547 : {
548 : system::result<T>& res;
549 : value const& jv;
550 : Ctx const& ctx;
551 :
552 : template< class I >
553 : void operator()( I ) const
554 : {
555 : if( res )
556 : return;
557 :
558 : using V = mp11::mp_at<T, I>;
559 : auto attempt = try_value_to<V>(jv, ctx);
560 : if( attempt )
561 : {
562 : using cat = variant_construction_category<T, V, I>;
563 : res = initialize_variant<T, I>( std::move(*attempt), cat() );
564 : }
565 : }
566 : };
567 :
568 : template< class T, class Ctx >
569 : system::result<T>
570 : value_to_impl(
571 : variant_conversion_tag,
572 : try_value_to_tag<T>,
573 : value const& jv,
574 : Ctx const& ctx)
575 : {
576 : system::error_code ec;
577 : BOOST_JSON_FAIL(ec, error::exhausted_variants);
578 :
579 : using Is = mp11::mp_iota< mp11::mp_size<T> >;
580 :
581 : system::result<T> res = {system::in_place_error, ec};
582 : mp11::mp_for_each<Is>( alternative_converter<T, Ctx>{res, jv, ctx} );
583 : return res;
584 : }
585 :
586 : template< class T, class Ctx >
587 : system::result<T>
588 : value_to_impl(
589 : path_conversion_tag, try_value_to_tag<T>, value const& jv, Ctx const& )
590 : {
591 : auto str = jv.if_string();
592 : if( !str )
593 : {
594 : system::error_code ec;
595 : BOOST_JSON_FAIL(ec, error::not_string);
596 : return {boost::system::in_place_error, ec};
597 : }
598 :
599 : string_view sv = str->subview();
600 : return {boost::system::in_place_value, T( sv.begin(), sv.end() )};
601 : }
602 :
603 : //----------------------------------------------------------
604 : // User-provided conversions; throwing -> throwing
605 : template< class T, class Ctx >
606 : mp11::mp_if< mp11::mp_valid<has_user_conversion_to_impl, T>, T >
607 1 : value_to_impl(
608 : user_conversion_tag, value_to_tag<T> tag, value const& jv, Ctx const&)
609 : {
610 1 : return tag_invoke(tag, jv);
611 : }
612 :
613 : template<
614 : class T,
615 : class Ctx,
616 : class Sup = supported_context<Ctx, T, value_to_conversion>
617 : >
618 : mp11::mp_if<
619 : mp11::mp_valid< has_context_conversion_to_impl, typename Sup::type, T>, T >
620 1 : value_to_impl(
621 : context_conversion_tag,
622 : value_to_tag<T> tag,
623 : value const& jv,
624 : Ctx const& ctx )
625 : {
626 1 : return tag_invoke( tag, jv, Sup::get(ctx) );
627 : }
628 :
629 : template<
630 : class T,
631 : class Ctx,
632 : class Sup = supported_context<Ctx, T, value_to_conversion>
633 : >
634 : mp11::mp_if<
635 : mp11::mp_valid<
636 : has_full_context_conversion_to_impl, typename Sup::type, T>,
637 : T>
638 : value_to_impl(
639 : full_context_conversion_tag,
640 : value_to_tag<T> tag,
641 : value const& jv,
642 : Ctx const& ctx )
643 : {
644 : return tag_invoke( tag, jv, Sup::get(ctx), ctx );
645 : }
646 :
647 : //----------------------------------------------------------
648 : // User-provided conversions; throwing -> nonthrowing
649 : template< class T, class Ctx >
650 : mp11::mp_if_c< !mp11::mp_valid<has_user_conversion_to_impl, T>::value, T>
651 60 : value_to_impl(
652 : user_conversion_tag, value_to_tag<T>, value const& jv, Ctx const& )
653 : {
654 60 : auto res = tag_invoke(try_value_to_tag<T>(), jv);
655 60 : if( res.has_error() )
656 12 : throw_system_error( res.error() );
657 96 : return std::move(*res);
658 32 : }
659 :
660 : template<
661 : class T,
662 : class Ctx,
663 : class Sup = supported_context<Ctx, T, value_to_conversion>
664 : >
665 : mp11::mp_if_c<
666 : !mp11::mp_valid<
667 : has_context_conversion_to_impl, typename Sup::type, T>::value,
668 : T>
669 3 : value_to_impl(
670 : context_conversion_tag, value_to_tag<T>, value const& jv, Ctx const& ctx )
671 : {
672 3 : auto res = tag_invoke( try_value_to_tag<T>(), jv, Sup::get(ctx) );
673 3 : if( res.has_error() )
674 1 : throw_system_error( res.error() );
675 4 : return std::move(*res);
676 : }
677 :
678 : template<
679 : class T,
680 : class Ctx,
681 : class Sup = supported_context<Ctx, T, value_to_conversion>
682 : >
683 : mp11::mp_if_c<
684 : !mp11::mp_valid<
685 : has_full_context_conversion_to_impl, typename Sup::type, T>::value,
686 : T>
687 : value_to_impl(
688 : full_context_conversion_tag,
689 : value_to_tag<T>,
690 : value const& jv,
691 : Ctx const& ctx )
692 : {
693 : auto res = tag_invoke(try_value_to_tag<T>(), jv, Sup::get(ctx), ctx);
694 : if( res.has_error() )
695 : throw_system_error( res.error() );
696 : return std::move(*res);
697 : }
698 :
699 : //----------------------------------------------------------
700 : // User-provided conversions; nonthrowing -> nonthrowing
701 : template< class T, class Ctx >
702 : mp11::mp_if<
703 : mp11::mp_valid<
704 : has_nonthrowing_user_conversion_to_impl, T>, system::result<T> >
705 124 : value_to_impl(
706 : user_conversion_tag, try_value_to_tag<T>, value const& jv, Ctx const& )
707 : {
708 132 : return tag_invoke(try_value_to_tag<T>(), jv);
709 : }
710 :
711 : template<
712 : class T,
713 : class Ctx,
714 : class Sup = supported_context<Ctx, T, value_to_conversion>
715 : >
716 : mp11::mp_if<
717 : mp11::mp_valid<
718 : has_nonthrowing_context_conversion_to_impl, typename Sup::type, T>,
719 : system::result<T> >
720 : value_to_impl(
721 : context_conversion_tag,
722 : try_value_to_tag<T> tag,
723 : value const& jv,
724 : Ctx const& ctx )
725 : {
726 : return tag_invoke( tag, jv, Sup::get(ctx) );
727 : }
728 :
729 : template<
730 : class T,
731 : class Ctx,
732 : class Sup = supported_context<Ctx, T, value_to_conversion>
733 : >
734 : mp11::mp_if<
735 : mp11::mp_valid<
736 : has_nonthrowing_full_context_conversion_to_impl,
737 : typename Sup::type,
738 : T>,
739 : system::result<T> >
740 : value_to_impl(
741 : full_context_conversion_tag,
742 : try_value_to_tag<T> tag,
743 : value const& jv,
744 : Ctx const& ctx )
745 : {
746 : return tag_invoke( tag, jv, Sup::get(ctx), ctx );
747 : }
748 :
749 : //----------------------------------------------------------
750 : // User-provided conversions; nonthrowing -> throwing
751 :
752 : template< class T, class... Args >
753 : system::result<T>
754 36 : wrap_conversion_exceptions( value_to_tag<T>, Args&& ... args )
755 : {
756 : #ifndef BOOST_NO_EXCEPTIONS
757 : try
758 : {
759 : #endif
760 : return {
761 : boost::system::in_place_value,
762 36 : tag_invoke( value_to_tag<T>(), static_cast<Args&&>(args)... )};
763 : #ifndef BOOST_NO_EXCEPTIONS
764 : }
765 30 : catch( std::bad_alloc const&)
766 : {
767 6 : throw;
768 : }
769 12 : catch( system::system_error const& e)
770 : {
771 12 : return {boost::system::in_place_error, e.code()};
772 : }
773 12 : catch( ... )
774 : {
775 6 : system::error_code ec;
776 6 : BOOST_JSON_FAIL(ec, error::exception);
777 6 : return {boost::system::in_place_error, ec};
778 : }
779 : #endif
780 : }
781 :
782 : template< class T, class Ctx >
783 : mp11::mp_if_c<
784 : !mp11::mp_valid<has_nonthrowing_user_conversion_to_impl, T>::value,
785 : system::result<T> >
786 36 : value_to_impl(
787 : user_conversion_tag, try_value_to_tag<T>, value const& jv, Ctx const& )
788 : {
789 36 : return wrap_conversion_exceptions(value_to_tag<T>(), jv);
790 : }
791 :
792 : template<
793 : class T,
794 : class Ctx,
795 : class Sup = supported_context<Ctx, T, value_to_conversion>
796 : >
797 : mp11::mp_if_c<
798 : !mp11::mp_valid<
799 : has_nonthrowing_context_conversion_to_impl,
800 : typename Sup::type,
801 : T>::value,
802 : system::result<T> >
803 : value_to_impl(
804 : context_conversion_tag,
805 : try_value_to_tag<T>,
806 : value const& jv,
807 : Ctx const& ctx )
808 : {
809 : return wrap_conversion_exceptions( value_to_tag<T>(), jv, Sup::get(ctx) );
810 : }
811 :
812 : template<
813 : class T,
814 : class Ctx,
815 : class Sup = supported_context<Ctx, T, value_to_conversion>
816 : >
817 : mp11::mp_if_c<
818 : !mp11::mp_valid<
819 : has_nonthrowing_full_context_conversion_to_impl,
820 : typename Sup::type,
821 : T>::value,
822 : system::result<T> >
823 : value_to_impl(
824 : full_context_conversion_tag,
825 : try_value_to_tag<T>,
826 : value const& jv,
827 : Ctx const& ctx )
828 : {
829 : return wrap_conversion_exceptions(
830 : value_to_tag<T>(), jv, Sup::get(ctx), ctx);
831 : }
832 :
833 : // no suitable conversion implementation
834 : template< class T, class Ctx >
835 : T
836 : value_to_impl( no_conversion_tag, value_to_tag<T>, value const&, Ctx const& )
837 : {
838 : static_assert(
839 : !std::is_same<T, T>::value,
840 : "No suitable tag_invoke overload found for the type");
841 : }
842 :
843 : // generic wrapper over non-throwing implementations
844 : template< class Impl, class T, class Ctx >
845 : T
846 339 : value_to_impl( Impl impl, value_to_tag<T>, value const& jv, Ctx const& ctx )
847 : {
848 339 : return value_to_impl(impl, try_value_to_tag<T>(), jv, ctx).value();
849 : }
850 :
851 : template< class Ctx, class T >
852 : using value_to_category = conversion_category<
853 : Ctx, T, value_to_conversion >;
854 :
855 : } // detail
856 :
857 : #ifndef BOOST_NO_CXX17_HDR_OPTIONAL
858 : inline
859 : system::result<std::nullopt_t>
860 : tag_invoke(
861 : try_value_to_tag<std::nullopt_t>,
862 : value const& jv)
863 : {
864 : if( jv.is_null() )
865 : return std::nullopt;
866 : system::error_code ec;
867 : BOOST_JSON_FAIL(ec, error::not_null);
868 : return ec;
869 : }
870 : #endif
871 :
872 : } // namespace json
873 : } // namespace boost
874 :
875 : #endif
|