Line data Source code
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 155 : try_size(T&& cont, mp11::mp_int<3>)
127 : {
128 155 : 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 7 : try_size(T&, mp11::mp_int<0>)
148 : {
149 7 : 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 56 : inserter(
166 : T& target,
167 : mp11::mp_int<2>)
168 : {
169 56 : return target.begin();
170 : }
171 :
172 : template<class T>
173 : std::back_insert_iterator<T>
174 590 : inserter(
175 : T& target,
176 : mp11::mp_int<1>)
177 : {
178 590 : return std::back_inserter(target);
179 : }
180 :
181 : template<class T>
182 : std::insert_iterator<T>
183 72 : inserter(
184 : T& target,
185 : mp11::mp_int<0>)
186 : {
187 72 : 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 32 : get( Ctx const& ctx ) noexcept
504 : {
505 32 : 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
|