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 = json_base_type_t<mapped_type_t>;
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 = json_base_type_t<mapped_type_t>;
378 using v_t = typename b_t::value;
379 return json_link_quick_map_type<
380 json_base::json_nullable<T, json_base::json_array<v_t, T>>>{ };
381 } else {
382 using v_t = typename mapped_type_t::value;
383 return json_link_quick_map_type<json_base::json_array<v_t, T>>{ };
384 }
385#if defined( DAW_JSON_HAS_REFLECTION )
386 } else if constexpr( refl_details::PotentiallyReflectable<T> ) {
387 return json_link_quick_map_type<
388 json_base::json_reflected_class<T>>{ };
389#endif
390 } else {
391 return json_link_quick_map_type<void, false>{ };
392 }
393#if defined( DAW_JSON_HAS_REFLECTION )
394 } else if constexpr( refl_details::PotentiallyReflectable<T> ) {
395 return json_link_quick_map_type<
396 json_base::json_reflected_class<T>>{ };
397#endif
398 } else {
399 return json_link_quick_map_type<void, false>{ };
400 }
401 }
402
404 template<typename T>
405 DAW_CPP20_CONCEPT has_json_link_quick_map_v =
406 decltype( json_link_quick_map<T>( ) )::value;
407
409 template<typename T>
410 using json_link_quick_map_t =
411 typename decltype( json_link_quick_map<T>( ) )::mapped_type;
412
413 template<typename JsonType>
414 struct json_class_map_type {
416 };
417
418 template<typename>
419 struct is_json_class_map : std::false_type {};
420
421 // This maybe specialized
422 template<typename T>
423 inline constexpr bool is_json_class_map_v =
424 has_json_data_contract_trait_v<T> and
425 is_json_class_map_v<json_data_contract_trait_t<T>>;
426
427 template<typename T>
428 DAW_ATTRIB_INLINE DAW_CONSTEVAL auto json_deduced_type_impl( ) noexcept {
429 if constexpr( is_a_basic_json_value<T> ) {
430 return daw::traits::identity<json_base::json_raw<T>>{ };
431 } else if constexpr( is_an_ordered_member_v<T> ) {
432 using type = T;
433 return daw::traits::identity<type>{ };
434 } else if constexpr( has_json_data_contract_trait_v<T> ) {
435 static_assert( not std::is_same_v<T, void> );
436
437 using type = json_base::json_class<T>;
438
439 static_assert( not std::is_same_v<daw::remove_cvref_t<type>, void>,
440 "Detection failure" );
441 static_assert( not is_nonesuch_v<remove_cvref_t<type>>,
442 "Detection failure" );
443 return daw::traits::identity<type>{ };
444 } else if constexpr( has_json_link_quick_map_v<T> ) {
445 static_assert( not std::is_same_v<T, void> );
446 using type = json_link_quick_map_t<T>;
447 using rcvref_type = remove_cvref_t<type>;
448 static_assert( not std::is_same_v<rcvref_type, void>,
449 "Detection failure" );
450 static_assert( not is_nonesuch_v<rcvref_type>, "Detection failure" );
451 return daw::traits::identity<type>{ };
452 } else if constexpr( is_a_json_type_v<T> ) {
453 static_assert( not std::is_same_v<T, void> );
454 using type =
455 typename daw::conditional_t<is_json_class_map_v<T>,
456 json_class_map_type<T>,
457 daw::traits::identity<T>>::type;
458 static_assert( not std::is_same_v<daw::remove_cvref_t<type>, void>,
459 "Detection failure" );
460 static_assert( not is_nonesuch_v<remove_cvref_t<type>>,
461 "Detection failure" );
462 return daw::traits::identity<type>{ };
463 } else if constexpr( concepts::is_nullable_value_v<T> ) {
464 using value_type = concepts::nullable_value_type_t<T>;
465 using sub_type =
466 typename decltype( json_deduced_type_impl<value_type>( ) )::type;
467 using type = json_base::json_nullable<T, sub_type>;
468 return daw::traits::identity<type>{ };
469 } else if constexpr( concepts::is_container_v<T> ) {
470 using type = json_base::json_array<typename T::value_type, T>;
471 return daw::traits::identity<type>{ };
472 } else if constexpr( std::is_empty_v<T> and
473 std::is_default_constructible_v<T> ) {
474 // Allow empty/default constructible types to work without mapping
475 using type = json_details::json_empty_class<T>;
476 return daw::traits::identity<type>{ };
477#if not defined( DAW_JSON_HAS_REFLECTION )
478 } else if constexpr( can_convert_to_tuple_v<T> ) {
479 using type = json_base::json_tuple<T>;
480 return daw::traits::identity<type>{ };
481#endif
482 } else {
483 static_assert( daw::deduced_false_v<T>,
484 "Could not deduced data contract type and there is no "
485 "json_data_contract_specialization" );
486 }
487 }
488
489 template<typename T>
490 using json_deduced_type =
491 typename DAW_TYPEOF( json_deduced_type_impl<T>( ) )::type;
492
493 template<typename T>
494 DAW_CPP20_CONCEPT has_json_deduced_type_v =
495 not std::is_same_v<json_deduced_type<T>,
497
498 template<typename... Ts>
499 DAW_CPP20_CONCEPT all_have_deduced_type_v =
500 ( has_json_deduced_type_v<Ts> and ... );
501
502 template<typename JsonElement, typename Container, typename Constructor>
503 struct json_constructor<
504 json_base::json_array<JsonElement, Container, Constructor>> {
505 using json_element_t = json_deduced_type<JsonElement>;
506 using json_element_parse_to_t = json_result_t<json_element_t>;
507
508 using container_t =
509 daw::conditional_t<std::is_same_v<Container, use_default>,
510 std::vector<json_element_parse_to_t>, Container>;
511
512 using type =
513 daw::conditional_t<std::is_same_v<use_default, Constructor>,
515
516 static_assert(
517 daw::is_callable_v<type, json_element_parse_to_t const *,
518 json_element_parse_to_t const *>,
519 "Constructor must support copy and/or move construction" );
520 };
521
522 template<typename JsonElement, typename Container, typename Constructor>
523 struct json_result<
524 json_base::json_array<JsonElement, Container, Constructor>> {
525 using constructor_t = typename json_constructor<
526 json_base::json_array<JsonElement, Container, Constructor>>::type;
527 using json_element_t = json_deduced_type<JsonElement>;
528 using json_element_parse_to_t =
529 typename json_result<json_element_t>::type;
530 using type =
531 std::invoke_result_t<constructor_t, json_element_parse_to_t const *,
532 json_element_parse_to_t const *>;
533 };
534
535 template<typename T>
536 DAW_CPP20_CONCEPT has_unnamed_default_type_mapping_v =
537 has_json_deduced_type_v<T>;
538
539 template<typename JsonMember>
540 using from_json_result_t = json_result_t<json_deduced_type<JsonMember>>;
541
542 template<typename Constructor, typename... Members>
543 using json_class_parse_result_impl2 =
544 std::invoke_result_t<Constructor, json_result_t<Members>...>;
545
546 template<typename Constructor, typename... Members>
547 using json_class_parse_result_impl =
548 daw::detected_t<json_class_parse_result_impl2, Constructor, Members...>;
549
550 template<typename Constructor, typename... Members>
551 struct could_not_construct_from_members_error;
552
553 template<typename Constructor, typename... Members>
554 using json_class_parse_result_t = typename daw::conditional_t<
555 daw::is_callable_v<Constructor, json_result_t<Members>...>,
556 std::invoke_result<Constructor, json_result_t<Members>...>,
557 daw::traits::identity<could_not_construct_from_members_error<
558 Constructor, Members...>>>::type;
559
560 template<typename JsonMember>
561 using dependent_member_t = typename JsonMember::dependent_member;
562
563 template<typename JsonMember, typename = void>
564 inline constexpr bool has_dependent_member_v = false;
565
566 template<typename JsonMember>
567 inline constexpr bool has_dependent_member_v<
568 JsonMember, std::void_t<dependent_member_t<JsonMember>>> = true;
569
570 DAW_JSON_MAKE_REQ_TYPE_ALIAS_TRAIT( has_nullable_dependent_member_v,
571 T::member_type::dependent_member );
572
573 template<typename JsonMember>
574 DAW_REQUIRES( is_json_nullable_v<JsonMember> )
575 inline constexpr bool has_dependent_member_v<
576 JsonMember DAW_ENABLEIF_S( is_json_nullable_v<JsonMember> )> =
577 has_nullable_dependent_member_v<JsonMember>;
578
579 template<typename Constructor>
580 [[nodiscard]] DAW_ATTRIB_INLINE constexpr auto
581 construct_nullable_empty( ) {
582 if constexpr( daw::is_callable_v<
583 Constructor,
584 concepts::construct_nullable_with_empty_t> ) {
585 return Constructor{ }( concepts::construct_nullable_with_empty );
586 } else {
587 return Constructor{ }( );
588 }
589 }
590 } // namespace json_details
591 } // namespace DAW_JSON_VER
592} // 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