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