GCC Code Coverage Report


Directory: libs/json/include/boost/json/
File: impl/conversion.hpp
Date: 2025-12-23 17:20:53
Exec Total Coverage
Lines: 18 18 100.0%
Functions: 77 80 96.2%
Branches: 0 0 -%

Line Branch Exec Source
1 //
2 // Copyright (c) 2020 Krystian Stasiowski (sdkrystian@gmail.com)
3 // Copyright (c) 2022 Dmitry Arkhipov (grisumbras@yandex.ru)
4 //
5 // Distributed under the Boost Software License, Version 1.0. (See accompanying
6 // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
7 //
8 // Official repository: https://github.com/boostorg/json
9 //
10
11 #ifndef BOOST_JSON_IMPL_CONVERSION_HPP
12 #define BOOST_JSON_IMPL_CONVERSION_HPP
13
14 #include <boost/json/fwd.hpp>
15 #include <boost/json/value.hpp>
16 #include <boost/json/string_view.hpp>
17 #include <boost/describe/enumerators.hpp>
18 #include <boost/describe/members.hpp>
19 #include <boost/describe/bases.hpp>
20 #include <boost/mp11/algorithm.hpp>
21 #include <boost/mp11/utility.hpp>
22 #include <boost/system/result.hpp>
23
24 #include <iterator>
25 #include <tuple>
26 #include <utility>
27 #ifndef BOOST_NO_CXX17_HDR_VARIANT
28 # include <variant>
29 #endif // BOOST_NO_CXX17_HDR_VARIANT
30
31 namespace boost {
32 namespace json {
33 namespace detail {
34
35 struct no_context
36 {};
37
38 #ifdef __cpp_lib_nonmember_container_access
39 using std::size;
40 #endif
41
42 template<std::size_t I, class T>
43 using tuple_element_t = typename std::tuple_element<I, T>::type;
44
45 template<class T>
46 using iterator_type = decltype(std::begin(std::declval<T&>()));
47 template<class T>
48 using iterator_traits = std::iterator_traits< iterator_type<T> >;
49
50 template<class T>
51 using value_type = typename iterator_traits<T>::value_type;
52 template<class T>
53 using mapped_type = tuple_element_t< 1, value_type<T> >;
54
55 // had to make the metafunction always succeeding in order to make it work
56 // with msvc 14.0
57 template<class T>
58 using key_type_helper = tuple_element_t< 0, value_type<T> >;
59 template<class T>
60 using key_type = mp11::mp_eval_or<
61 void,
62 key_type_helper,
63 T>;
64
65 template<class T>
66 using are_begin_and_end_same = std::is_same<
67 iterator_type<T>,
68 decltype(std::end(std::declval<T&>()))>;
69
70 // msvc 14.0 gets confused when std::is_same is used directly
71 template<class A, class B>
72 using is_same_msvc_140 = std::is_same<A, B>;
73 template<class T>
74 using is_its_own_value = is_same_msvc_140<value_type<T>, T>;
75
76 template<class T>
77 using not_its_own_value = mp11::mp_not< is_its_own_value<T> >;
78
79 template<class T>
80 using begin_iterator_category = typename std::iterator_traits<
81 iterator_type<T>>::iterator_category;
82
83 template<class T>
84 using has_positive_tuple_size = mp11::mp_bool<
85 (std::tuple_size<T>::value > 0) >;
86
87 template<class T>
88 using has_unique_keys = has_positive_tuple_size<decltype(
89 std::declval<T&>().emplace(
90 std::declval<value_type<T>>()))>;
91
92 template<class T>
93 using has_string_type = std::is_same<
94 typename T::string_type, std::basic_string<typename T::value_type> >;
95
96 template<class T>
97 struct is_value_type_pair_helper : std::false_type
98 { };
99 template<class T1, class T2>
100 struct is_value_type_pair_helper<std::pair<T1, T2>> : std::true_type
101 { };
102 template<class T>
103 using is_value_type_pair = is_value_type_pair_helper<value_type<T>>;
104
105 template<class T>
106 using has_size_member_helper
107 = std::is_convertible<decltype(std::declval<T&>().size()), std::size_t>;
108 template<class T>
109 using has_size_member = mp11::mp_valid_and_true<has_size_member_helper, T>;
110 template<class T>
111 using has_free_size_helper
112 = std::is_convertible<
113 decltype(size(std::declval<T const&>())),
114 std::size_t>;
115 template<class T>
116 using has_free_size = mp11::mp_valid_and_true<has_free_size_helper, T>;
117 template<class T>
118 using size_implementation = mp11::mp_cond<
119 has_size_member<T>, mp11::mp_int<3>,
120 has_free_size<T>, mp11::mp_int<2>,
121 std::is_array<T>, mp11::mp_int<1>,
122 mp11::mp_true, mp11::mp_int<0>>;
123
124 template<class T>
125 std::size_t
126 310 try_size(T&& cont, mp11::mp_int<3>)
127 {
128 310 return cont.size();
129 }
130
131 template<class T>
132 std::size_t
133 1 try_size(T& cont, mp11::mp_int<2>)
134 {
135 1 return size(cont);
136 }
137
138 template<class T, std::size_t N>
139 std::size_t
140 1 try_size(T(&)[N], mp11::mp_int<1>)
141 {
142 1 return N;
143 }
144
145 template<class T>
146 std::size_t
147 14 try_size(T&, mp11::mp_int<0>)
148 {
149 14 return 0;
150 }
151
152 template<class T>
153 using has_push_back_helper
154 = decltype(std::declval<T&>().push_back(std::declval<value_type<T>>()));
155 template<class T>
156 using has_push_back = mp11::mp_valid<has_push_back_helper, T>;
157 template<class T>
158 using inserter_implementation = mp11::mp_cond<
159 is_tuple_like<T>, mp11::mp_int<2>,
160 has_push_back<T>, mp11::mp_int<1>,
161 mp11::mp_true, mp11::mp_int<0>>;
162
163 template<class T>
164 iterator_type<T>
165 62 inserter(
166 T& target,
167 mp11::mp_int<2>)
168 {
169 62 return target.begin();
170 }
171
172 template<class T>
173 std::back_insert_iterator<T>
174 1176 inserter(
175 T& target,
176 mp11::mp_int<1>)
177 {
178 1176 return std::back_inserter(target);
179 }
180
181 template<class T>
182 std::insert_iterator<T>
183 122 inserter(
184 T& target,
185 mp11::mp_int<0>)
186 {
187 122 return std::inserter( target, target.end() );
188 }
189
190 using value_from_conversion = mp11::mp_true;
191 using value_to_conversion = mp11::mp_false;
192
193 struct user_conversion_tag { };
194 struct context_conversion_tag : user_conversion_tag { };
195 struct full_context_conversion_tag : context_conversion_tag { };
196 struct native_conversion_tag { };
197 struct value_conversion_tag : native_conversion_tag { };
198 struct object_conversion_tag : native_conversion_tag { };
199 struct array_conversion_tag : native_conversion_tag { };
200 struct string_conversion_tag : native_conversion_tag { };
201 struct bool_conversion_tag : native_conversion_tag { };
202 struct number_conversion_tag : native_conversion_tag { };
203 struct integral_conversion_tag : number_conversion_tag { };
204 struct floating_point_conversion_tag : number_conversion_tag { };
205 struct null_like_conversion_tag { };
206 struct string_like_conversion_tag { };
207 struct map_like_conversion_tag { };
208 struct path_conversion_tag { };
209 struct sequence_conversion_tag { };
210 struct tuple_conversion_tag { };
211 struct described_class_conversion_tag { };
212 struct described_enum_conversion_tag { };
213 struct variant_conversion_tag { };
214 struct optional_conversion_tag { };
215 struct no_conversion_tag { };
216
217 template<class... Args>
218 using supports_tag_invoke = decltype(tag_invoke( std::declval<Args>()... ));
219
220 template<class T>
221 using has_user_conversion_from_impl = supports_tag_invoke<
222 value_from_tag, value&, T&& >;
223 template<class T>
224 using has_user_conversion_to_impl = supports_tag_invoke<
225 value_to_tag<T>, value const& >;
226 template<class T>
227 using has_nonthrowing_user_conversion_to_impl = supports_tag_invoke<
228 try_value_to_tag<T>, value const& >;
229 template< class T, class Dir >
230 using has_user_conversion1 = mp11::mp_if<
231 std::is_same<Dir, value_from_conversion>,
232 mp11::mp_valid<has_user_conversion_from_impl, T>,
233 mp11::mp_or<
234 mp11::mp_valid<has_user_conversion_to_impl, T>,
235 mp11::mp_valid<has_nonthrowing_user_conversion_to_impl, T>>>;
236
237 template< class Ctx, class T >
238 using has_context_conversion_from_impl = supports_tag_invoke<
239 value_from_tag, value&, T&&, Ctx const& >;
240 template< class Ctx, class T >
241 using has_context_conversion_to_impl = supports_tag_invoke<
242 value_to_tag<T>, value const&, Ctx const& >;
243 template< class Ctx, class T >
244 using has_nonthrowing_context_conversion_to_impl = supports_tag_invoke<
245 try_value_to_tag<T>, value const&, Ctx const& >;
246 template< class Ctx, class T, class Dir >
247 using has_user_conversion2 = mp11::mp_if<
248 std::is_same<Dir, value_from_conversion>,
249 mp11::mp_valid<has_context_conversion_from_impl, Ctx, T>,
250 mp11::mp_or<
251 mp11::mp_valid<has_context_conversion_to_impl, Ctx, T>,
252 mp11::mp_valid<has_nonthrowing_context_conversion_to_impl, Ctx, T>>>;
253
254 template< class Ctx, class T >
255 using has_full_context_conversion_from_impl = supports_tag_invoke<
256 value_from_tag, value&, T&&, Ctx const&, Ctx const& >;
257 template< class Ctx, class T >
258 using has_full_context_conversion_to_impl = supports_tag_invoke<
259 value_to_tag<T>, value const&, Ctx const&, Ctx const& >;
260 template< class Ctx, class T >
261 using has_nonthrowing_full_context_conversion_to_impl = supports_tag_invoke<
262 try_value_to_tag<T>, value const&, Ctx const&, Ctx const& >;
263 template< class Ctx, class T, class Dir >
264 using has_user_conversion3 = mp11::mp_if<
265 std::is_same<Dir, value_from_conversion>,
266 mp11::mp_valid<has_full_context_conversion_from_impl, Ctx, T>,
267 mp11::mp_or<
268 mp11::mp_valid<has_full_context_conversion_to_impl, Ctx, T>,
269 mp11::mp_valid<
270 has_nonthrowing_full_context_conversion_to_impl, Ctx, T>>>;
271
272 template< class T >
273 using described_non_public_members = describe::describe_members<
274 T, describe::mod_private | describe::mod_protected>;
275 template< class T >
276 using described_bases = describe::describe_bases<
277 T, describe::mod_any_access>;
278
279 #if defined(BOOST_MSVC) && BOOST_MSVC < 1920
280
281 template< class T >
282 struct described_member_t_impl;
283
284 template< class T, class C >
285 struct described_member_t_impl<T C::*>
286 {
287 using type = T;
288 };
289
290 template< class T, class D >
291 using described_member_t = remove_cvref<
292 typename described_member_t_impl<
293 remove_cvref<decltype(D::pointer)> >::type>;
294
295 #else
296
297 template< class T, class D >
298 using described_member_t = remove_cvref<decltype(
299 std::declval<T&>().* D::pointer )>;
300
301 #endif
302
303 template< class T >
304 using described_members = describe::describe_members<
305 T, describe::mod_any_access | describe::mod_inherited>;
306
307 // user conversion (via tag_invoke)
308 template< class Ctx, class T, class Dir >
309 using user_conversion_category = mp11::mp_cond<
310 has_user_conversion3<Ctx, T, Dir>, full_context_conversion_tag,
311 has_user_conversion2<Ctx, T, Dir>, context_conversion_tag,
312 has_user_conversion1<T, Dir>, user_conversion_tag>;
313
314 // native conversions (constructors and member functions of value)
315 template< class T >
316 using native_conversion_category = mp11::mp_cond<
317 std::is_same<T, value>, value_conversion_tag,
318 std::is_same<T, array>, array_conversion_tag,
319 std::is_same<T, object>, object_conversion_tag,
320 std::is_same<T, string>, string_conversion_tag>;
321
322 // generic conversions
323 template< class T, class Ctx >
324 using generic_conversion_category = mp11::mp_cond<
325 std::is_same<T, bool>, bool_conversion_tag,
326 std::is_integral<T>, integral_conversion_tag,
327 std::is_floating_point<T>, floating_point_conversion_tag,
328 is_null_like<T>, null_like_conversion_tag,
329 is_string_like<T>, string_like_conversion_tag,
330 is_map_like<T, Ctx>, map_like_conversion_tag,
331 is_sequence_like<T>, sequence_conversion_tag,
332 is_tuple_like<T>, tuple_conversion_tag,
333 is_described_class<T>, described_class_conversion_tag,
334 is_described_enum<T>, described_enum_conversion_tag,
335 is_variant_like<T>, variant_conversion_tag,
336 is_optional_like<T>, optional_conversion_tag,
337 is_path_like<T>, path_conversion_tag,
338 // failed to find a suitable implementation
339 mp11::mp_true, no_conversion_tag>;
340
341 template< class T >
342 using nested_type = typename T::type;
343 template< class T1, class T2 >
344 using conversion_category_helper = mp11::mp_eval_if_not<
345 std::is_same<detail::no_conversion_tag, T1>,
346 T1,
347 mp11::mp_eval_or_q, T1, mp11::mp_quote<nested_type>, T2>;
348
349 template< class Ctx >
350 using fix_context = mp11::mp_if< std::is_same<Ctx, no_context>, void, Ctx>;
351
352 template<class T, class Ctx>
353 using representation_or_void = mp11::mp_eval_or<void, represent_as_t, T, Ctx>;
354
355 template< class U >
356 using is_not_void = mp11::mp_bool< !std::is_same<void, U>::value >;
357
358 template< class T, class Ctxs >
359 struct representation_helper
360 {
361 using size = mp11::mp_size<Ctxs>;
362
363 template< class I >
364 using exists = mp11::mp_less<I, size>;
365
366 template< class Ctx >
367 using step = representation_or_void<T, Ctx>;
368 using reps = mp11::mp_transform<step, Ctxs>;
369 using r_index = mp11::mp_find_if< reps, is_not_void >;
370
371 using type = mp11::mp_eval_if<
372 mp11::mp_not< exists<r_index> >,
373 T,
374 mp11::mp_at, reps, r_index>;
375 };
376
377 template< class T, class Ctx >
378 struct conversion_representation_impl
379 : representation_helper< T, mp11::mp_list<Ctx, void> >
380 {};
381
382 template< class T >
383 struct conversion_representation_impl<T, no_context>
384 : representation_helper< T, mp11::mp_list<void> >
385 {};
386
387 template< class T, class... Ctxs >
388 struct conversion_representation_impl< T, std::tuple<Ctxs...> >
389 : representation_helper< T, mp11::mp_list<remove_cvref<Ctxs>..., void> >
390 {};
391
392 template< class T, class Ctx >
393 using conversion_representation
394 = typename conversion_representation_impl<remove_cvref<T>, Ctx>::type;
395
396 template< class Ctx, class T, class Dir >
397 struct conversion_attrs
398 {
399 using representation = conversion_representation<T, Ctx>;
400
401 using category = mp11::mp_fold<
402 mp11::mp_list<
403 mp11::mp_defer<user_conversion_category, Ctx, representation, Dir>,
404 mp11::mp_defer<native_conversion_category, representation>,
405 mp11::mp_defer<generic_conversion_category, representation, Ctx>>,
406 no_conversion_tag,
407 conversion_category_helper>;
408 };
409
410 template< class T >
411 using any_conversion_tag = mp11::mp_not<
412 std::is_same< T, no_conversion_tag > >;
413
414 template< class Ctx, class T, class Dir >
415 using conversion_category = typename conversion_attrs<Ctx, T, Dir>::category;
416
417 template< class T, class Dir, class... Ctxs >
418 struct conversion_attrs< std::tuple<Ctxs...>, T, Dir >
419 {
420 using size = mp11::mp_size_t< sizeof...(Ctxs) >;
421 using ctxs = mp11::mp_list< remove_cvref<Ctxs>... >;
422
423 template< class I >
424 using exists = mp11::mp_less<I, size>;
425
426 using representation = conversion_representation< T, std::tuple<Ctxs...> >;
427
428 using cats = mp11::mp_list<
429 conversion_category<remove_cvref<Ctxs>, representation, Dir>... >;
430
431 using context2 = mp11::mp_find< cats, full_context_conversion_tag >;
432 using context1 = mp11::mp_find< cats, context_conversion_tag >;
433 using context0 = mp11::mp_find< cats, user_conversion_tag >;
434 using index = mp11::mp_cond<
435 exists<context2>, context2,
436 exists<context1>, context1,
437 exists<context0>, context0,
438 mp11::mp_true, mp11::mp_find_if< cats, any_conversion_tag > >;
439 using category = mp11::mp_eval_or<
440 no_conversion_tag,
441 mp11::mp_at, cats, index >;
442 };
443
444 template <class T, class Dir>
445 using can_convert = mp11::mp_not<
446 std::is_same<
447 detail::conversion_category<no_context, T, Dir>,
448 detail::no_conversion_tag>>;
449
450 template<class Impl1, class Impl2>
451 using conversion_round_trips_helper = mp11::mp_or<
452 std::is_same<Impl1, Impl2>,
453 std::is_base_of<user_conversion_tag, Impl1>,
454 std::is_base_of<user_conversion_tag, Impl2>>;
455 template< class Ctx, class T, class Dir >
456 using conversion_round_trips = conversion_round_trips_helper<
457 conversion_category<Ctx, T, Dir>,
458 conversion_category<Ctx, T, mp11::mp_not<Dir>>>;
459
460 template< class T1, class T2 >
461 struct copy_cref_helper
462 {
463 using type = remove_cvref<T2>;
464 };
465 template< class T1, class T2 >
466 using copy_cref = typename copy_cref_helper< T1, T2 >::type;
467
468 template< class T1, class T2 >
469 struct copy_cref_helper<T1 const, T2>
470 {
471 using type = remove_cvref<T2> const;
472 };
473 template< class T1, class T2 >
474 struct copy_cref_helper<T1&, T2>
475 {
476 using type = copy_cref<T1, T2>&;
477 };
478 template< class T1, class T2 >
479 struct copy_cref_helper<T1&&, T2>
480 {
481 using type = copy_cref<T1, T2>&&;
482 };
483
484 template< class Rng, class Traits >
485 using forwarded_value_helper = mp11::mp_if<
486 std::is_convertible<
487 typename Traits::reference,
488 copy_cref<Rng, typename Traits::value_type> >,
489 copy_cref<Rng, typename Traits::value_type>,
490 typename Traits::value_type >;
491
492 template< class Rng >
493 using forwarded_value = forwarded_value_helper<
494 Rng, iterator_traits< Rng > >;
495
496 template< class Ctx, class T, class Dir >
497 struct supported_context
498 {
499 using type = Ctx;
500
501 static
502 type const&
503 60 get( Ctx const& ctx ) noexcept
504 {
505 60 return ctx;
506 }
507 };
508
509 template< class T, class Dir, class... Ctxs >
510 struct supported_context< std::tuple<Ctxs...>, T, Dir >
511 {
512 using Ctx = std::tuple<Ctxs...>;
513 using Attrs = conversion_attrs<Ctx, T, Dir>;
514 using index = typename Attrs::index;
515 using next_supported = supported_context<
516 mp11::mp_at< typename Attrs::ctxs, index >, T, Dir >;
517 using type = typename next_supported::type;
518
519 static
520 type const&
521 19 get( Ctx const& ctx ) noexcept
522 {
523 19 return next_supported::get( std::get<index::value>( ctx ) );
524 }
525 };
526
527 template< class T >
528 using value_result_type = typename std::decay<
529 decltype( std::declval<T&>().value() )>::type;
530
531 template< class T >
532 using can_reset = decltype( std::declval<T&>().reset() );
533
534 template< class T >
535 using has_valueless_by_exception =
536 decltype( std::declval<T const&>().valueless_by_exception() );
537
538 } // namespace detail
539
540 template <class T>
541 struct result_for<T, value>
542 {
543 using type = system::result< detail::remove_cvref<T> >;
544 };
545
546 template<class T>
547 struct is_string_like
548 : std::is_convertible<T, string_view>
549 { };
550
551 template<class T>
552 struct is_path_like
553 : mp11::mp_all<
554 mp11::mp_valid_and_true<detail::is_its_own_value, T>,
555 mp11::mp_valid_and_true<detail::has_string_type, T>>
556 { };
557 template<class T>
558 struct is_sequence_like
559 : mp11::mp_all<
560 mp11::mp_valid_and_true<detail::are_begin_and_end_same, T>,
561 mp11::mp_valid_and_true<detail::not_its_own_value, T>,
562 mp11::mp_valid<detail::begin_iterator_category, T>>
563 { };
564
565 template<class T, class Ctx>
566 struct is_map_like
567 : mp11::mp_all<
568 is_sequence_like<T>,
569 mp11::mp_valid_and_true<detail::is_value_type_pair, T>,
570 is_string_like<
571 detail::conversion_representation<
572 detail::remove_cvref< detail::key_type<T> >, Ctx>>,
573 mp11::mp_valid_and_true<detail::has_unique_keys, T>>
574 { };
575
576 template<class T>
577 struct is_tuple_like
578 : mp11::mp_valid_and_true<detail::has_positive_tuple_size, T>
579 { };
580
581 template<>
582 struct is_null_like<std::nullptr_t>
583 : std::true_type
584 { };
585
586 #ifndef BOOST_NO_CXX17_HDR_VARIANT
587 template<>
588 struct is_null_like<std::monostate>
589 : std::true_type
590 { };
591 #endif // BOOST_NO_CXX17_HDR_VARIANT
592
593 template<class T>
594 struct is_described_class
595 : mp11::mp_and<
596 describe::has_describe_members<T>,
597 mp11::mp_not< std::is_union<T> >,
598 mp11::mp_empty<
599 mp11::mp_eval_or<
600 mp11::mp_list<>, detail::described_non_public_members, T>>,
601 mp11::mp_empty<
602 mp11::mp_eval_or<mp11::mp_list<>, detail::described_bases, T>>>
603 { };
604
605 template<class T>
606 struct is_described_enum
607 : describe::has_describe_enumerators<T>
608 { };
609
610 template<class T>
611 struct is_variant_like : mp11::mp_valid<detail::has_valueless_by_exception, T>
612 { };
613
614 template<class T>
615 struct is_optional_like
616 : mp11::mp_and<
617 mp11::mp_not<std::is_void<
618 mp11::mp_eval_or<void, detail::value_result_type, T>>>,
619 mp11::mp_valid<detail::can_reset, T>>
620 { };
621
622 } // namespace json
623 } // namespace boost
624
625 #endif // BOOST_JSON_IMPL_CONVERSION_HPP
626