DAW JSON Link
Loading...
Searching...
No Matches
daw_json_parse_common.h
Go to the documentation of this file.
1// Copyright (c) Darrell Wright
2//
3// Distributed under the Boost Software License, Version 1.0. (See accompanying
4// file LICENSE or copy at http://www.boost.org/LICENSE_1_0.txt)
5//
6// Official repository: https://github.com/beached/daw_json_link
7//
8
9#pragma once
10
12
14
28
29#include <daw/cpp_17.h>
30#include <daw/daw_allocator_construct.h>
31#include <daw/daw_arith_traits.h>
32#include <daw/daw_callable.h>
33#include <daw/daw_consteval.h>
34#include <daw/daw_cpp20_concept.h>
35#include <daw/daw_cpp_feature_check.h>
36#include <daw/daw_enable_requires.h>
37#include <daw/daw_fwd_pack_apply.h>
38#include <daw/daw_move.h>
39#include <daw/daw_scope_guard.h>
40#include <daw/daw_string_view.h>
41#include <daw/daw_traits.h>
42
43#include <array>
44#include <cstddef>
45#include <iterator>
46#include <optional>
47#include <string>
48#include <string_view>
49#include <vector>
50
51namespace daw::json {
52 inline namespace DAW_JSON_VER {
53 namespace json_details {
54 template<typename T>
55 DAW_ATTRIB_INLINE DAW_CONSTEVAL JsonParseTypes
56 number_parse_type_impl_test( ) {
57 if constexpr( daw::is_floating_point_v<T> ) {
58 return JsonParseTypes::Real;
59 } else if constexpr( daw::is_signed_v<T> ) {
60 return JsonParseTypes::Signed;
61 } else {
62 static_assert( daw::is_unsigned_v<T> );
63 return JsonParseTypes::Unsigned;
64 }
65 }
66
67 template<typename T>
68 DAW_ATTRIB_INLINE DAW_CONSTEVAL JsonParseTypes number_parse_type_test( ) {
69 if constexpr( std::is_enum_v<T> ) {
70 return number_parse_type_impl_test<std::underlying_type_t<T>>( );
71 } else {
72 return number_parse_type_impl_test<T>( );
73 }
74 }
75
76 template<typename T>
77 inline constexpr JsonParseTypes number_parse_type_v =
78 number_parse_type_test<T>( );
79
80 template<typename, typename = void>
81 struct json_deduced_type_map {
82 static constexpr bool is_null = false;
83 static constexpr auto parse_type = JsonParseTypes::Unknown;
84
85 static constexpr bool type_map_found = false;
86 };
87
88 template<typename JsonType>
89 DAW_REQUIRES( is_a_json_type_v<JsonType> )
90 struct json_deduced_type_map<
91 JsonType DAW_ENABLEIF_S( is_a_json_type_v<JsonType> )> {
92 static constexpr bool is_null = false;
93 static constexpr auto parse_type = JsonParseTypes::Unknown;
94
95 using type = JsonType;
96 static constexpr bool type_map_found = true;
97 };
98
99 template<typename T>
100 DAW_REQUIRES( has_json_data_contract_trait_v<T> )
101 struct json_deduced_type_map<
102 T DAW_ENABLEIF_S( has_json_data_contract_trait_v<T> )> {
103 static constexpr bool is_null = false;
104 using type = typename json_data_contract<T>::type;
105 static_assert( is_json_member_list_v<type>,
106 "Expected a JSON member list" );
107 static constexpr auto parse_type = JsonParseTypes::Unknown;
108
109 static constexpr bool type_map_found = true;
110 };
111
112 template<>
113 struct json_deduced_type_map<daw::string_view> {
114 static constexpr bool is_null = false;
115 static constexpr auto parse_type = JsonParseTypes::StringRaw;
116
117 static constexpr bool type_map_found = true;
118 };
119
120 template<>
121 struct json_deduced_type_map<std::string_view> {
122 static constexpr bool is_null = false;
123 static constexpr auto parse_type = JsonParseTypes::StringRaw;
124
125 static constexpr bool type_map_found = true;
126 };
127
128 template<>
129 struct json_deduced_type_map<std::string> {
130 static constexpr bool is_null = false;
131 static constexpr auto parse_type = JsonParseTypes::StringEscaped;
132
133 static constexpr bool type_map_found = true;
134 };
135
136 template<>
137 struct json_deduced_type_map<bool> {
138 static constexpr bool is_null = false;
139 static constexpr auto parse_type = JsonParseTypes::Bool;
140 static constexpr bool type_map_found = true;
141 };
142
143 // libc++ has a non-conforming vector<bool>::const_reference as it isn't
144 // bool https://bugs.llvm.org/show_bug.cgi?id=16077
145#if defined( _LIBCPP_VERSION )
146 template<>
147 struct json_deduced_type_map<
148 typename std::vector<bool>::const_reference> {
149
150 static constexpr bool is_null = false;
151 static constexpr auto parse_type = JsonParseTypes::Bool;
152
153 static constexpr bool type_map_found = true;
154 };
155#endif
156
157 template<typename Integer>
159 not json_details::has_json_data_contract_trait_v<Integer> and
160 daw::is_integral_v<Integer> )
161 struct json_deduced_type_map<Integer DAW_ENABLEIF_S(
162 not json_details::has_json_data_contract_trait_v<Integer> and
163 daw::is_integral_v<Integer> )> {
164 static constexpr bool is_null = false;
165 static constexpr auto parse_type = daw::is_signed_v<Integer>
166 ? JsonParseTypes::Signed
167 : JsonParseTypes::Unsigned;
168
169 static constexpr bool type_map_found = true;
170 };
171
172 template<typename Enum>
173 DAW_REQUIRES( not json_details::has_json_data_contract_trait_v<Enum> and
174 std::is_enum_v<Enum> )
175 struct json_deduced_type_map<Enum DAW_ENABLEIF_S(
176 not json_details::has_json_data_contract_trait_v<Enum> and
177 std::is_enum_v<Enum> )> {
178 static constexpr bool is_null = false;
179 static constexpr auto parse_type =
180 daw::is_signed_v<std::underlying_type<Enum>>
181 ? JsonParseTypes::Signed
182 : JsonParseTypes::Unsigned;
183
184 static constexpr bool type_map_found = true;
185 };
186
187 template<typename FloatingPoint>
189 not json_details::has_json_data_contract_trait_v<FloatingPoint> and
190 daw::is_floating_point_v<FloatingPoint> )
191 struct json_deduced_type_map<FloatingPoint DAW_ENABLEIF_S(
192 not json_details::has_json_data_contract_trait_v<FloatingPoint> and
193 daw::is_floating_point_v<FloatingPoint> )> {
194 static constexpr bool is_null = false;
195 static constexpr auto parse_type = JsonParseTypes::Real;
196
197 static constexpr bool type_map_found = true;
198 };
199
200 template<typename Tuple>
201 DAW_REQUIRES( not json_details::has_json_data_contract_trait_v<Tuple> and
202 is_tuple_v<Tuple> )
203 struct json_deduced_type_map<Tuple DAW_ENABLEIF_S(
204 not json_details::has_json_data_contract_trait_v<Tuple> and
205 is_tuple_v<Tuple> )> {
206 static constexpr bool is_null = false;
207 static constexpr auto parse_type = JsonParseTypes::Tuple;
208
209 static constexpr bool type_map_found = true;
210 };
211
212 namespace container_detect {
213 template<typename T>
214 using is_string_test =
215 decltype( (void)( std::begin( std::declval<T &>( ) ) ),
216 (void)( std::end( std::declval<T &>( ) ) ),
217 std::declval<typename T::value_type>( ) );
218 } // namespace container_detect
219
220 template<typename String>
221 DAW_CPP20_CONCEPT is_string_v = std::is_same_v<
222 char, daw::detected_t<container_detect::is_string_test, String>>;
223
225 is_associative_container_v,
226 ( (void)( std::begin( std::declval<T &>( ) ) ),
227 (void)( std::end( std::declval<T &>( ) ) ),
228 (void)( std::declval<typename T::value_type>( ) ),
229 (void)( std::declval<typename T::key_type>( ) ),
230 (void)( std::declval<typename T::mapped_type>( ) ) ) );
231
232 template<typename AssociativeContainer>
233 DAW_REQUIRES( not has_json_data_contract_trait_v<AssociativeContainer> and
234 is_associative_container_v<AssociativeContainer> )
235 struct json_deduced_type_map<AssociativeContainer DAW_ENABLEIF_S(
236 not has_json_data_contract_trait_v<AssociativeContainer> and
237 is_associative_container_v<AssociativeContainer> )> {
238 static constexpr bool is_null = false;
239 using key = typename AssociativeContainer::key_type;
240 using value = typename AssociativeContainer::mapped_type;
241 static constexpr auto parse_type = JsonParseTypes::KeyValue;
242
243 static constexpr bool type_map_found = true;
244 };
245
246 template<typename T>
247 DAW_CPP20_CONCEPT is_deduced_array_v =
248 not has_json_data_contract_trait_v<T> and
249 not is_associative_container_v<T> and concepts::is_container_v<T> and
250 not is_string_v<T>;
251
252 template<typename Container>
253 DAW_REQUIRES( is_deduced_array_v<Container> )
254 struct json_deduced_type_map<
255 Container DAW_ENABLEIF_S( is_deduced_array_v<Container> )> {
256 static constexpr bool is_null = false;
257 using value = typename Container::value_type;
258 static constexpr auto parse_type = JsonParseTypes::Array;
259
260 static constexpr bool type_map_found = true;
261 };
262
263 template<typename T>
264 DAW_CPP20_CONCEPT has_nullable_type_map_v =
265 concepts::is_nullable_value_v<T> and
266 not has_json_data_contract_trait_v<T> and
267 daw::is_detected_v<json_deduced_type_map,
268 concepts::nullable_value_type_t<T>>;
269
270 template<typename T>
271 DAW_REQUIRES( has_nullable_type_map_v<T> )
272 struct json_deduced_type_map<
273 T DAW_ENABLEIF_S( has_nullable_type_map_v<T> )> {
274 static constexpr bool is_null = true;
275 using sub_type = concepts::nullable_value_type_t<T>;
276 using type = json_deduced_type_map<sub_type>;
277 static constexpr auto parse_type = type::parse_type;
278 static constexpr bool type_map_found = true;
279 };
280
281 template<typename T>
282 DAW_CPP20_CONCEPT has_deduced_type_mapping_v =
283 json_deduced_type_map<T>::type_map_found;
284
285 template<typename... Ts>
286 DAW_CPP20_CONCEPT are_deduced_type_mapped_v =
287 ( has_deduced_type_mapping_v<Ts> and ... );
288
289 template<typename Mapped, bool Found = true>
290 struct json_link_quick_map_type {
291 static constexpr bool value = Found;
292 using mapped_type = Mapped;
293 };
294
295 template<JsonParseTypes Value>
296 DAW_CPP20_CONCEPT is_arithmetic_parse_type_v =
297 daw::traits::equal_to_any_of_v<Value, JsonParseTypes::Signed,
298 JsonParseTypes::Unsigned,
299 JsonParseTypes::Real>;
300
301 template<typename T>
302 DAW_ATTRIB_INLINE DAW_CONSTEVAL auto json_link_quick_map( ) noexcept {
303 if constexpr( is_a_json_type_v<T> ) {
304 return json_link_quick_map_type<T>{ };
305 } else if constexpr( has_deduced_type_mapping_v<T> ) {
306 using mapped_type_t = json_deduced_type_map<T>;
307 using parse_type = daw::constant<mapped_type_t::parse_type>;
308 using is_null = daw::constant<mapped_type_t::is_null>;
309 if constexpr( parse_type::value == JsonParseTypes::Unknown ) {
310 if constexpr( is_null::value ) {
311 if constexpr( has_json_data_contract_trait_v<
312 typename mapped_type_t::sub_type> ) {
313 return json_link_quick_map_type<
314 json_base::json_class_null<T>>{ };
315 } else {
316 return json_link_quick_map_type<json_base::json_raw_null<T>>{ };
317 }
318 } else {
319 return json_link_quick_map_type<json_base::json_raw<T>>{ };
320 }
321 } else if constexpr( parse_type::value ==
322 JsonParseTypes::StringRaw ) {
323 if constexpr( is_null::value ) {
324 return json_link_quick_map_type<
325 json_base::json_string_raw_null<T>>{ };
326 } else {
327 return json_link_quick_map_type<json_base::json_string_raw<T>>{ };
328 }
329 } else if constexpr( parse_type::value ==
330 JsonParseTypes::StringEscaped ) {
331 if constexpr( is_null::value ) {
332 return json_link_quick_map_type<
333 json_base::json_string_null<T>>{ };
334 } else {
335 return json_link_quick_map_type<json_base::json_string<T>>{ };
336 }
337 } else if constexpr( parse_type::value == JsonParseTypes::StringInsitu ) {
338 if constexpr( is_null::value ) {
339 return json_link_quick_map_type<json_base::json_string_null<T>>{ };
340 } else {
341 return json_link_quick_map_type<json_base::json_string<T>>{ };
342 }
343 } else if constexpr( parse_type::value == JsonParseTypes::Bool ) {
344 if constexpr( is_null::value ) {
345 return json_link_quick_map_type<json_base::json_bool_null<T>>{ };
346 } else {
347 return json_link_quick_map_type<json_base::json_bool<T>>{ };
348 }
349 } else if constexpr( is_arithmetic_parse_type_v<parse_type::value> ) {
350 if constexpr( is_null::value ) {
351 return json_link_quick_map_type<
352 json_base::json_number_null<T>>{ };
353 } else {
354 return json_link_quick_map_type<json_base::json_number<T>>{ };
355 }
356 } else if constexpr( parse_type::value == JsonParseTypes::Tuple ) {
357 if constexpr( is_null::value ) {
358 return json_link_quick_map_type<json_base::json_tuple_null<T>>{ };
359 } else {
360 return json_link_quick_map_type<json_base::json_tuple<T>>{ };
361 }
362 } else if constexpr( parse_type::value == JsonParseTypes::KeyValue ) {
363 if constexpr( is_null::value ) {
364 using b_t = typename mapped_type_t::type;
365 using k_t = typename b_t::key;
366 using v_t = typename b_t::value;
367 return json_link_quick_map_type<
368 json_base::json_key_value_null<T, v_t, k_t>>{ };
369 } else {
370 using k_t = typename mapped_type_t::key;
371 using v_t = typename mapped_type_t::value;
372 return json_link_quick_map_type<
373 json_base::json_key_value<T, v_t, k_t>>{ };
374 }
375 } else if constexpr( parse_type::value == JsonParseTypes::Array ) {
376 if constexpr( is_null::value ) {
377 using b_t = typename mapped_type_t::type;
378 using v_t = typename b_t::value;
379 return json_link_quick_map_type<
380 json_base::json_nullable<
381 T, json_base::json_array<v_t, json_details::unwrapped_t<T>>>>{ };
382 } else {
383 using v_t = typename mapped_type_t::value;
384 return json_link_quick_map_type<json_base::json_array<v_t, T>>{ };
385 }
386#if defined( DAW_JSON_HAS_REFLECTION )
387 } else if constexpr( refl_details::PotentiallyReflectable<T> ) {
388 return json_link_quick_map_type<
389 json_base::json_reflected_class<T>>{ };
390#endif
391 } else {
392 return json_link_quick_map_type<void, false>{ };
393 }
394#if defined( DAW_JSON_HAS_REFLECTION )
395 } else if constexpr( refl_details::PotentiallyReflectable<T> ) {
396 return json_link_quick_map_type<
397 json_base::json_reflected_class<T>>{ };
398#endif
399 } else {
400 return json_link_quick_map_type<void, false>{ };
401 }
402 }
403
405 template<typename T>
406 DAW_CPP20_CONCEPT has_json_link_quick_map_v =
407 decltype( json_link_quick_map<T>( ) )::value;
408
410 template<typename T>
411 using json_link_quick_map_t =
412 typename decltype( json_link_quick_map<T>( ) )::mapped_type;
413
414 template<typename JsonType>
415 struct json_class_map_type {
417 };
418
419 template<typename>
420 struct is_json_class_map : std::false_type {};
421
422 // This maybe specialized
423 template<typename T>
424 inline constexpr bool is_json_class_map_v =
425 has_json_data_contract_trait_v<T> and
426 is_json_class_map_v<json_data_contract_trait_t<T>>;
427
428 template<typename T>
429 DAW_ATTRIB_INLINE DAW_CONSTEVAL auto json_deduced_type_impl( ) noexcept {
430 if constexpr( is_a_basic_json_value<T> ) {
431 return daw::traits::identity<json_base::json_raw<T>>{ };
432#if defined( DAW_JSON_HAS_REFLECTION )
433 } else if constexpr( refl_details::HasTypeLevelAnnotation<T> ) {
434 return daw::traits::identity<
435 refl_details::type_annotation_mapping_t<T>>{ };
436#endif
437 } else if constexpr( is_an_ordered_member_v<T> ) {
438 using type = T;
439 return daw::traits::identity<type>{ };
440 } else if constexpr( has_json_data_contract_trait_v<T> ) {
441 static_assert( not std::is_same_v<T, void> );
442
443 using type = json_base::json_class<T>;
444
445 static_assert( not std::is_same_v<daw::remove_cvref_t<type>, void>,
446 "Detection failure" );
447 static_assert( not is_nonesuch_v<remove_cvref_t<type>>,
448 "Detection failure" );
449 return daw::traits::identity<type>{ };
450 } else if constexpr( has_json_link_quick_map_v<T> ) {
451 static_assert( not std::is_same_v<T, void> );
452 using type = json_link_quick_map_t<T>;
453 using rcvref_type = remove_cvref_t<type>;
454 static_assert( not std::is_same_v<rcvref_type, void>,
455 "Detection failure" );
456 static_assert( not is_nonesuch_v<rcvref_type>, "Detection failure" );
457 return daw::traits::identity<type>{ };
458 } else if constexpr( is_a_json_type_v<T> ) {
459 static_assert( not std::is_same_v<T, void> );
460 using type =
461 typename daw::conditional_t<is_json_class_map_v<T>,
462 json_class_map_type<T>,
463 daw::traits::identity<T>>::type;
464 static_assert( not std::is_same_v<daw::remove_cvref_t<type>, void>,
465 "Detection failure" );
466 static_assert( not is_nonesuch_v<remove_cvref_t<type>>,
467 "Detection failure" );
468 return daw::traits::identity<type>{ };
469 } else if constexpr( concepts::is_nullable_value_v<T> ) {
470 using value_type = concepts::nullable_value_type_t<T>;
471 using sub_type =
472 typename decltype( json_deduced_type_impl<value_type>( ) )::type;
473 using type = json_base::json_nullable<T, sub_type>;
474 return daw::traits::identity<type>{ };
475 } else if constexpr( concepts::is_container_v<T> ) {
476 using type = json_base::json_array<typename T::value_type, T>;
477 return daw::traits::identity<type>{ };
478 } else if constexpr( std::is_empty_v<T> and
479 std::is_default_constructible_v<T> ) {
480 // Allow empty/default constructible types to work without mapping
481 using type = json_details::json_empty_class<T>;
482 return daw::traits::identity<type>{ };
483#if not defined( DAW_JSON_HAS_REFLECTION )
484 } else if constexpr( can_convert_to_tuple_v<T> ) {
485 using type = json_base::json_tuple<T>;
486 return daw::traits::identity<type>{ };
487#endif
488 } else {
489 static_assert( daw::deduced_false_v<T>,
490 "Could not deduced data contract type and there is no "
491 "json_data_contract_specialization" );
492 }
493 }
494
495 template<typename T>
496 using json_deduced_type =
497 typename DAW_TYPEOF( json_deduced_type_impl<T>( ) )::type;
498
499 template<typename T>
500 DAW_CPP20_CONCEPT has_json_deduced_type_v =
501 not std::is_same_v<json_deduced_type<T>,
503
504 template<typename... Ts>
505 DAW_CPP20_CONCEPT all_have_deduced_type_v =
506 ( has_json_deduced_type_v<Ts> and ... );
507
508 template<typename JsonElement, typename Container, typename Constructor>
509 struct json_constructor<
510 json_base::json_array<JsonElement, Container, Constructor>> {
511 using json_element_t = json_deduced_type<JsonElement>;
512 using json_element_parse_to_t = json_result_t<json_element_t>;
513
514 using container_t =
515 daw::conditional_t<std::is_same_v<Container, use_default>,
516 std::vector<json_element_parse_to_t>, Container>;
517
518 using type =
519 daw::conditional_t<std::is_same_v<use_default, Constructor>,
521
522 static_assert(
523 daw::is_callable_v<type, json_element_parse_to_t const *,
524 json_element_parse_to_t const *>,
525 "Constructor must support copy and/or move construction" );
526 };
527
528 template<typename JsonElement, typename Container, typename Constructor>
529 struct json_result<
530 json_base::json_array<JsonElement, Container, Constructor>> {
531 using constructor_t = typename json_constructor<
532 json_base::json_array<JsonElement, Container, Constructor>>::type;
533 using json_element_t = json_deduced_type<JsonElement>;
534 using json_element_parse_to_t =
535 typename json_result<json_element_t>::type;
536 using type =
537 std::invoke_result_t<constructor_t, json_element_parse_to_t const *,
538 json_element_parse_to_t const *>;
539 };
540
541 template<typename T>
542 DAW_CPP20_CONCEPT has_unnamed_default_type_mapping_v =
543 has_json_deduced_type_v<T>;
544
545 template<typename JsonMember>
546 using from_json_result_t = json_result_t<json_deduced_type<JsonMember>>;
547
548 template<typename Constructor, typename... Members>
549 using json_class_parse_result_impl2 =
550 std::invoke_result_t<Constructor, json_result_t<Members>...>;
551
552 template<typename Constructor, typename... Members>
553 using json_class_parse_result_impl =
554 daw::detected_t<json_class_parse_result_impl2, Constructor, Members...>;
555
556 template<typename Constructor, typename... Members>
557 struct could_not_construct_from_members_error;
558
559 template<typename Constructor, typename... Members>
560 using json_class_parse_result_t = typename daw::conditional_t<
561 daw::is_callable_v<Constructor, json_result_t<Members>...>,
562 std::invoke_result<Constructor, json_result_t<Members>...>,
563 daw::traits::identity<could_not_construct_from_members_error<
564 Constructor, Members...>>>::type;
565
566 template<typename JsonMember>
567 using dependent_member_t = typename JsonMember::dependent_member;
568
569 template<typename JsonMember, typename = void>
570 inline constexpr bool has_dependent_member_v = false;
571
572 template<typename JsonMember>
573 inline constexpr bool has_dependent_member_v<
574 JsonMember, std::void_t<dependent_member_t<JsonMember>>> = true;
575
576 DAW_JSON_MAKE_REQ_TYPE_ALIAS_TRAIT( has_nullable_dependent_member_v,
577 T::member_type::dependent_member );
578
579 template<typename JsonMember>
580 DAW_REQUIRES( is_json_nullable_v<JsonMember> )
581 inline constexpr bool has_dependent_member_v<
582 JsonMember DAW_ENABLEIF_S( is_json_nullable_v<JsonMember> )> =
583 has_nullable_dependent_member_v<JsonMember>;
584
585 template<typename Constructor>
586 [[nodiscard]] DAW_ATTRIB_INLINE constexpr auto
587 construct_nullable_empty( ) {
588 if constexpr( daw::is_callable_v<
589 Constructor,
590 concepts::construct_nullable_with_empty_t> ) {
591 return Constructor{ }( concepts::construct_nullable_with_empty );
592 } else {
593 return Constructor{ }( );
594 }
595 }
596 } // namespace json_details
597 } // namespace DAW_JSON_VER
598} // namespace daw::json
DAW_REQUIRES(daw::json::json_details::is_container_opted_into_json_iostreams_v< Container >) std
An opt in ostream interface for containers of types that have JSON mappings.
#define DAW_JSON_MAKE_REQ_TYPE_ALIAS_TRAIT(Name,...)
#define DAW_JSON_MAKE_REQ_TRAIT(Name,...)
typename json_data_contract< T >::type json_data_contract_trait_t
This trait gets us the mapping type from the contract.
JsonParseTypes
The tags used by the parser to determine what parser to call.
Customization point traits.
Mapping class for JSON data structures to C++. It must be specialized in order to parse to a user cla...
This class is used as a way to indicate that a json_data_contract specialization has not been done fo...
Default Constructor for a type. It accounts for aggregate types and uses brace construction for them.
#define DAW_JSON_VER
The version string used in namespace definitions. Must be a valid namespace name.
Definition version.h:20