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_convertible_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 } else if constexpr( can_convert_to_tuple_v<T> ) {
471 using type = json_base::json_tuple<T>;
472 return daw::traits::identity<type>{ };
473 } else {
474 static_assert( daw::deduced_false_v<T>,
475 "Could not deduced data contract type and there is no "
476 "json_data_contract_specialization" );
477 }
478 }
479
480 template<typename T>
481 using json_deduced_type =
482 typename DAW_TYPEOF( json_deduced_type_impl<T>( ) )::type;
483
484 template<typename T>
485 DAW_CPP20_CONCEPT has_json_deduced_type_v =
486 not std::is_same_v<json_deduced_type<T>,
488
489 template<typename... Ts>
490 DAW_CPP20_CONCEPT all_have_deduced_type_v =
491 ( has_json_deduced_type_v<Ts> and ... );
492
493 template<typename JsonElement, typename Container, typename Constructor>
494 struct json_constructor<
495 json_base::json_array<JsonElement, Container, Constructor>> {
496 using json_element_t = json_deduced_type<JsonElement>;
497 using json_element_parse_to_t = json_result_t<json_element_t>;
498
499 using container_t =
500 daw::conditional_t<std::is_same_v<Container, use_default>,
501 std::vector<json_element_parse_to_t>, Container>;
502
503 using type =
504 daw::conditional_t<std::is_same_v<use_default, Constructor>,
506
507 static_assert(
508 daw::is_callable_v<type, json_element_parse_to_t const *,
509 json_element_parse_to_t const *>,
510 "Constructor must support copy and/or move construction" );
511 };
512
513 template<typename JsonElement, typename Container, typename Constructor>
514 struct json_result<
515 json_base::json_array<JsonElement, Container, Constructor>> {
516 using constructor_t = typename json_constructor<
517 json_base::json_array<JsonElement, Container, Constructor>>::type;
518 using json_element_t = json_deduced_type<JsonElement>;
519 using json_element_parse_to_t =
520 typename json_result<json_element_t>::type;
521 using type =
522 std::invoke_result_t<constructor_t, json_element_parse_to_t const *,
523 json_element_parse_to_t const *>;
524 };
525
526 template<typename T>
527 DAW_CPP20_CONCEPT has_unnamed_default_type_mapping_v =
528 has_json_deduced_type_v<T>;
529
530 template<typename JsonMember>
531 using from_json_result_t = json_result_t<json_deduced_type<JsonMember>>;
532
533 template<typename Constructor, typename... Members>
534 using json_class_parse_result_impl2 =
535 std::invoke_result_t<Constructor, json_result_t<Members>...>;
536
537 template<typename Constructor, typename... Members>
538 using json_class_parse_result_impl =
539 daw::detected_t<json_class_parse_result_impl2, Constructor, Members...>;
540
541 template<typename Constructor, typename... Members>
542 struct could_not_construct_from_members_error;
543
544 template<typename Constructor, typename... Members>
545 using json_class_parse_result_t = typename daw::conditional_t<
546 daw::is_callable_v<Constructor, json_result_t<Members>...>,
547 std::invoke_result<Constructor, json_result_t<Members>...>,
548 daw::traits::identity<could_not_construct_from_members_error<
549 Constructor, Members...>>>::type;
550
551 template<typename JsonMember>
552 using dependent_member_t = typename JsonMember::dependent_member;
553
554 template<typename JsonMember, typename = void>
555 inline constexpr bool has_dependent_member_v = false;
556
557 template<typename JsonMember>
558 inline constexpr bool has_dependent_member_v<
559 JsonMember, std::void_t<dependent_member_t<JsonMember>>> = true;
560
561 DAW_JSON_MAKE_REQ_TYPE_ALIAS_TRAIT( has_nullable_dependent_member_v,
562 T::member_type::dependent_member );
563
564 template<typename JsonMember>
565 DAW_REQUIRES( is_json_nullable_v<JsonMember> )
566 inline constexpr bool has_dependent_member_v<
567 JsonMember DAW_ENABLEIF_S( is_json_nullable_v<JsonMember> )> =
568 has_nullable_dependent_member_v<JsonMember>;
569
570 template<typename Constructor>
571 [[nodiscard]] DAW_ATTRIB_INLINE constexpr auto
572 construct_nullable_empty( ) {
573 if constexpr( daw::is_callable_v<
574 Constructor,
575 concepts::construct_nullable_with_empty_t> ) {
576 return Constructor{ }( concepts::construct_nullable_with_empty );
577 } else {
578 return Constructor{ }( );
579 }
580 }
581 } // namespace json_details
582 } // namespace DAW_JSON_VER
583} // 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