DAW JSON Link
daw_json_parse_class.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
11#include "../daw_json_exception.h"
12#include "daw_json_assert.h"
14#include "daw_json_name.h"
17#include "daw_json_skip.h"
18#include "version.h"
19
20#include <daw/daw_consteval.h>
21#include <daw/daw_constinit.h>
22#include <daw/daw_fwd_pack_apply.h>
23#include <daw/daw_likely.h>
24#include <daw/daw_traits.h>
25
26#include <ciso646>
27#include <cstddef>
28#include <exception>
29#include <type_traits>
30
31namespace daw::json {
32 inline namespace DAW_JSON_VER {
33 namespace json_details {
34
47 template<typename JsonMember, typename ParseState>
48 [[nodiscard]] DAW_ATTRIB_INLINE constexpr json_result<JsonMember>
49 parse_ordered_class_member( template_param<JsonMember>,
50 std::size_t &member_index,
51 ParseState &parse_state ) {
52
53 using json_member_t = ordered_member_subtype_t<JsonMember>;
54
55 parse_state.move_next_member_or_end( );
56 /***
57 * Some members specify their index so there may be gaps between
58 * member data elements in the array.
59 */
60 if constexpr( is_an_ordered_member_v<JsonMember> ) {
61 pocm_details::maybe_skip_members<is_json_nullable_v<json_member_t>>(
62 parse_state, member_index, JsonMember::member_index );
63 } else {
65 ErrorReason::UnexpectedEndOfData, parse_state );
66 }
67
68 // this is an out value, get position ready
69 ++member_index;
70
71 if( DAW_UNLIKELY( parse_state.front( ) == ']' ) ) {
72 if constexpr( is_json_nullable_v<json_member_t> ) {
73 using constructor_t = typename json_member_t::constructor_t;
74 return construct_value(
75 template_args<json_result<json_member_t>, constructor_t>,
77 } else {
78 daw_json_error( missing_member( "ordered_class_member" ),
80 }
81 }
82 return parse_value<without_name<json_member_t>>(
84 }
85
86 /***
87 * Parse a member from a json_class
88 * @tparam member_position position in json_class member list
89 * @tparam JsonMember type description of member to parse
90 * @tparam N Number of members in json_class
91 * @tparam ParseState see IteratorRange
92 * @param locations location info for members
93 * @param parse_state JSON data
94 * @return parsed value from JSON data
95 */
96 template<std::size_t member_position, typename JsonMember,
97 AllMembersMustExist must_exist, bool NeedsClassPositions,
98 typename ParseState, std::size_t N, typename CharT, bool B>
99 [[nodiscard]] DAW_ATTRIB_INLINE constexpr json_result<JsonMember>
101 locations_info_t<N, CharT, B> &locations ) {
102 parse_state.move_next_member_or_end( );
103
104 daw_json_assert_weak( parse_state.is_at_next_class_member( ),
105 ErrorReason::MissingMemberNameOrEndOfClass,
106 parse_state );
107
108 auto [loc, known] = find_class_member<member_position, must_exist>(
109 parse_state, locations, is_json_nullable_v<JsonMember>,
110 JsonMember::name );
111
112 // If the member was found loc will have it's position
113 if( not known ) {
114 if constexpr( NeedsClassPositions ) {
115 auto const cf = parse_state.class_first;
116 auto const cl = parse_state.class_last;
117 if constexpr( use_direct_construction_v<
118 ParseState, without_name<JsonMember>> ) {
119 auto const after_parse = daw::on_scope_exit( [&] {
120 parse_state.class_first = cf;
121 parse_state.class_last = cl;
122 } );
123 return parse_value<without_name<JsonMember>>(
125 } else {
126 auto result = parse_value<without_name<JsonMember>>(
128 parse_state.class_first = cf;
129 parse_state.class_last = cl;
130 return result;
131 }
132 } else {
133 return parse_value<without_name<JsonMember>>(
135 }
136 }
137 // We cannot find the member, check if the member is nullable
138 if( loc.is_null( ) ) {
139 if constexpr( is_json_nullable_v<JsonMember> ) {
140 return parse_value<without_name<JsonMember>, true>(
142 } else {
143 daw_json_error( missing_member( std::string_view(
144 std::data( JsonMember::name ),
145 std::size( JsonMember::name ) ) ),
146 parse_state );
147 }
148 }
149
150 // Member was previously skipped
151 return parse_value<without_name<JsonMember>, true>(
153 }
154
155 inline namespace {
156 template<bool IsExactClass, typename ParseState, typename OldClassPos>
157 DAW_ATTRIB_INLINE inline constexpr void
158
159 class_cleanup_now( ParseState &parse_state,
160 OldClassPos const &old_class_pos ) {
162 ErrorReason::UnexpectedEndOfData, parse_state );
163 parse_state.move_next_member_or_end( );
164 // If we fulfill the contract before all values are parses
165 parse_state.move_to_next_class_member( );
166 if constexpr( IsExactClass ) {
167 daw_json_assert_weak( parse_state.front( ) == '}',
168 ErrorReason::UnknownMember, parse_state );
169 parse_state.remove_prefix( );
170 } else {
171 (void)parse_state.skip_class( );
172 // Yes this must be checked. We maybe at the end of document. After
173 // the 2nd try, give up
174 }
175 parse_state.trim_left_checked( );
176 parse_state.set_class_position( old_class_pos );
177 }
178
179 template<bool AllMembersMustExist, typename ParseState,
180 typename OldClassPos>
181 struct class_cleanup {
182 ParseState &parse_state;
183 OldClassPos const &old_class_pos;
184
185 DAW_ATTRIB_INLINE
186 CPP20CONSTEXPR inline ~class_cleanup( ) noexcept( false ) {
187#if defined( DAW_HAS_CONSTEXPR_SCOPE_GUARD )
188 if( DAW_IS_CONSTANT_EVALUATED( ) ) {
189 class_cleanup_now<AllMembersMustExist>( parse_state,
191 } else {
192#endif
193 if( std::uncaught_exceptions( ) == 0 ) {
194 class_cleanup_now<AllMembersMustExist>( parse_state,
196 }
197#if defined( DAW_HAS_CONSTEXPR_SCOPE_GUARD )
198 }
199#endif
200 }
201 };
202 } // namespace
203
204#if not defined( _MSC_VER ) or defined( __clang__ )
205 template<typename ParseState, typename... JsonMembers>
206 inline constexpr auto
207 known_locations_v = make_locations_info<ParseState, JsonMembers...>( );
208#endif
209
210 /***
211 * Parse to the user supplied class. The parser will run left->right if
212 * it can when the JSON document's order matches that of the order of
213 * the supplied classes ctor. If there is an order mismatch, store the
214 * start/finish of JSON members we are interested in and return that to
215 * the members parser when needed.
216 */
217 template<typename JsonClass, typename... JsonMembers, typename ParseState,
218 std::size_t... Is>
219 [[nodiscard]] constexpr json_result<JsonClass>
220 parse_json_class( ParseState &parse_state, std::index_sequence<Is...> ) {
221 static_assert( is_a_json_type<JsonClass>::value );
222 using T = typename JsonClass::base_type;
223 using Constructor = typename JsonClass::constructor_t;
224 static_assert( has_json_data_contract_trait_v<T>, "Unexpected type" );
225 using must_exist = daw::constant<(
226 json_details::all_json_members_must_exist_v<T, ParseState>
229
230 parse_state.trim_left( );
231 // TODO, use member name
232 daw_json_assert_weak( parse_state.is_opening_brace_checked( ),
233 ErrorReason::InvalidClassStart, parse_state );
234
235 auto const old_class_pos = parse_state.get_class_position( );
236 parse_state.set_class_position( );
237 parse_state.remove_prefix( );
238 parse_state.trim_left( );
239
240 if constexpr( sizeof...( JsonMembers ) == 0 ) {
241 // Clang-CL with MSVC has issues if we don't do empties this way
242 class_cleanup_now<
243 json_details::all_json_members_must_exist_v<T, ParseState>>(
245
246 if constexpr( use_direct_construction_v<ParseState, JsonClass> ) {
247 return T{ };
248 } else {
249 return construct_value( template_args<T, Constructor>,
250 parse_state );
251 }
252 } else {
253 using NeedClassPositions = std::bool_constant<(
254 ( JsonMembers::must_be_class_member or ... ) )>;
255
256#if defined( _MSC_VER ) and not defined( __clang__ )
257 auto known_locations =
258 make_locations_info<ParseState, JsonMembers...>( );
259#else
260 auto known_locations = known_locations_v<ParseState, JsonMembers...>;
261#endif
262
263 if constexpr( use_direct_construction_v<ParseState, JsonClass> ) {
264 auto const run_after_parse = class_cleanup<
265 json_details::all_json_members_must_exist_v<T, ParseState>,
266 ParseState, decltype( old_class_pos )>{ parse_state,
268 (void)run_after_parse;
269
270 /*
271 * Rather than call directly use apply/tuple to evaluate
272 * left->right
273 */
274 if constexpr( force_aggregate_construction_v<T> ) {
275 return T{ parse_class_member<
276 Is, traits::nth_type<Is, JsonMembers...>, must_exist::value,
277 NeedClassPositions::value>( parse_state, known_locations )... };
278 } else {
279 return construct_value_tp<T, Constructor>(
281 Is, traits::nth_type<Is, JsonMembers...>,
282 must_exist::value, NeedClassPositions::value>(
283 parse_state, known_locations )... } );
284 }
285 } else {
286 if constexpr( force_aggregate_construction_v<T> ) {
287 auto result = T{ parse_class_member<
288 Is, traits::nth_type<Is, JsonMembers...>, must_exist::value,
289 NeedClassPositions::value>( parse_state, known_locations )... };
290
291 class_cleanup_now<
292 json_details::all_json_members_must_exist_v<T, ParseState>>(
294 return result;
295 } else {
296 auto result = construct_value_tp<T, Constructor>(
298 Is, traits::nth_type<Is, JsonMembers...>,
299 must_exist::value, NeedClassPositions::value>(
300 parse_state, known_locations )... } );
301
302 class_cleanup_now<
303 json_details::all_json_members_must_exist_v<T, ParseState>>(
305 return result;
306 }
307 }
308 }
309 }
310
311 /***
312 * Parse to a class where the members are constructed from the values of
313 * a JSON array. Often this is used for geometric types like Point
314 */
315 template<typename JsonClass, typename... JsonMembers, typename ParseState>
316 [[nodiscard]] static constexpr json_result<JsonClass>
317 parse_json_tuple_class( template_params<JsonClass, JsonMembers...>,
318 ParseState &parse_state ) {
319 static_assert( is_a_json_type<JsonClass>::value );
320 using T = typename JsonClass::base_type;
321 using Constructor = typename JsonClass::constructor_t;
322 static_assert( has_json_data_contract_trait_v<T>, "Unexpected type" );
323 static_assert(
324 std::is_invocable<Constructor,
325 typename JsonMembers::parse_to_t...>::value,
326 "Supplied types cannot be used for construction of this type" );
327
328 parse_state.trim_left( ); // Move to array start '['
329 daw_json_assert_weak( parse_state.is_opening_bracket_checked( ),
330 ErrorReason::InvalidArrayStart, parse_state );
331 auto const old_class_pos = parse_state.get_class_position( );
332 parse_state.set_class_position( );
333 parse_state.remove_prefix( );
334 parse_state.trim_left( );
335
336 size_t current_idx = 0;
337
338 if constexpr( use_direct_construction_v<ParseState, JsonClass> ) {
339 auto const run_after_parse = ordered_class_cleanup<
340 json_details::all_json_members_must_exist_v<T, ParseState>,
341 ParseState, decltype( old_class_pos )>{ parse_state,
343 (void)run_after_parse;
344 if constexpr( force_aggregate_construction_v<T> ) {
346 template_arg<JsonMembers>, current_idx, parse_state )... };
347 } else {
348 return construct_value_tp<T, Constructor>(
351 template_arg<JsonMembers>, current_idx, parse_state )... } );
352 }
353 } else {
354 auto result = [&] {
355 if constexpr( force_aggregate_construction_v<T> ) {
357 template_arg<JsonMembers>, current_idx, parse_state )... };
358 } else {
359 return construct_value_tp<T, Constructor>(
362 template_arg<JsonMembers>, current_idx, parse_state )... } );
363 }
364 }( );
366 T, ParseState> ) {
367 parse_state.trim_left( );
368 daw_json_assert_weak( parse_state.front( ) == ']',
369 ErrorReason::UnknownMember, parse_state );
370 parse_state.remove_prefix( );
371 parse_state.trim_left( );
372 } else {
373 (void)parse_state.skip_array( );
374 }
375 parse_state.set_class_position( old_class_pos );
376 return result;
377 }
378 }
379 } // namespace json_details
380 } // namespace DAW_JSON_VER
381} // namespace daw::json
#define daw_json_assert_weak(Bool,...)
Definition: daw_json_assert.h:190
OldClassPos const & old_class_pos
Definition: daw_json_parse_class.h:183
ParseState & parse_state
Definition: daw_json_parse_class.h:182
#define CPP20CONSTEXPR
Definition: daw_json_parse_value.h:40
DAW_ATTRIB_FLATINLINE DAW_JSON_MAKE_LOC_INFO_CONSTEVAL auto make_locations_info()
Definition: daw_json_location_info.h:168
constexpr json_result< JsonClass > parse_json_class(ParseState &parse_state, std::index_sequence< Is... >)
Definition: daw_json_parse_class.h:220
typename JsonMember::without_name without_name
Definition: daw_json_traits.h:104
static constexpr json_result< JsonClass > parse_json_tuple_class(template_params< JsonClass, JsonMembers... >, ParseState &parse_state)
Definition: daw_json_parse_class.h:317
std::bool_constant< daw::is_detected_v< json_type_t, T > > is_a_json_type
Definition: daw_json_traits.h:446
typename daw::detected_or_t< T, ordered_member_subtype_test, T > ordered_member_subtype_t
Definition: daw_json_parse_common.h:51
constexpr bool all_json_members_must_exist_v
Definition: daw_json_traits.h:641
constexpr DAW_ATTRIB_INLINE json_result< JsonMember > parse_ordered_class_member(template_param< JsonMember >, std::size_t &member_index, ParseState &parse_state)
Parse a class member in an ordered json class(class as array). These are often referred to as JSON tu...
Definition: daw_json_parse_class.h:49
AllMembersMustExist
Definition: daw_json_location_info.h:206
static constexpr DAW_ATTRIB_FLATINLINE auto construct_value(template_params< Value, Constructor >, ParseState &parse_state, Args &&...args)
Definition: daw_json_parse_common.h:60
typename JsonMember::parse_to_t json_result
Definition: daw_json_parse_common.h:200
constexpr bool use_direct_construction_v
Definition: daw_json_parse_common.h:959
constexpr DAW_ATTRIB_INLINE json_result< JsonMember > parse_class_member(ParseState &parse_state, locations_info_t< N, CharT, B > &locations)
Definition: daw_json_parse_class.h:100
daw::constant< v > ParseTag
Definition: daw_json_enums.h:119
DAW_ATTRIB_NOINLINE void daw_json_error(ErrorReason reason)
Definition: daw_json_assert.h:39
Definition: daw_from_json.h:22
Definition: daw_json_location_info.h:99
#define DAW_JSON_VER
The version string used in namespace definitions. Must be a valid namespace name.
Definition: version.h:16