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