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