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