DAW JSON Link
daw_json_reflection.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/daw_pipelines.h"
12 #include "daw_json_link.h"
13 
14 #include <cstddef>
15 #include <experimental/meta>
16 #include <tuple>
17 #include <type_traits>
18 #include <utility>
19 #include <vector>
20 
21 #define DAW_REFL( ... ) ^^__VA_ARGS__
22 #define DAW_SPLICE( ... ) [:__VA_ARGS__:]
23 
25  inline namespace experimental {
26  template<typename E, json_options_t Options = json_custom_opts_def>
27  requires std::is_enum_v<E> struct enum_string;
28 
29  struct refl_map_as {
30  std::meta::info type;
31  };
32 
33  struct refl_rename {
34  char const *name;
35  };
36 
39  };
40 
42 
43  template<typename T>
45  T value;
46  };
47 
48  namespace refl_details {
49  template<auto... vals>
50  struct replicator_type {
51  template<typename F>
52  constexpr void operator>>( F body ) const {
53  ( body.template operator( )<vals>( ), ... );
54  }
55  };
56 
57  template<auto... vals>
58  replicator_type<vals...> replicator = { };
59 
61  consteval std::vector<std::meta::info>
62  pub_nsdm_of( std::meta::info type_class ) {
63  auto members = std::meta::nonstatic_data_members_of( type_class );
64  std::erase_if( members, []( std::meta::info const &i ) {
65  return not std::meta::is_public( i );
66  } );
67  return members;
68  }
69 
70  template<typename R>
71  consteval auto expand( R range ) {
72  auto args = std::vector<std::meta::info>( );
73  for( auto r : range ) {
74  args.push_back( std::meta::reflect_value( r ) );
75  }
76  return std::meta::substitute( DAW_REFL( replicator ), args );
77  }
78 
79  template<typename T>
80  consteval std::optional<T> get_annotaion( std::meta::info r ) {
81  for( std::meta::info a : std::meta::annotations_of( r ) ) {
82  if( std::meta::type_of( a ) == DAW_REFL( T ) ) {
83  return std::meta::extract<T>( a );
84  }
85  }
86  return std::nullopt;
87  }
88 
89  template<typename T>
90  consteval bool has_annotation( std::meta::info r, T const &value ) {
91  auto expected = std::meta::reflect_value( value );
92  for( std::meta::info a : std::meta::annotations_of( r ) ) {
93  if( value_of( a ) == expected ) {
94  return true;
95  }
96  }
97  return false;
98  }
99 
100  template<typename T, std::size_t... Is>
101  consteval auto to_tuple( T const &value, std::index_sequence<Is...> )
102  -> decltype( std::tuple(
103  value.DAW_SPLICE( pub_nsdm_of( DAW_REFL( T ) )[Is] )... ) ) {
104  return std::tuple(
105  value.DAW_SPLICE( pub_nsdm_of( DAW_REFL( T ) )[Is] )... );
106  }
107 
108  template<typename T>
109  consteval auto
110  to_tuple( T const &value ) -> decltype( refl_details::to_tuple(
111  value,
112  std::make_index_sequence<pub_nsdm_of( DAW_REFL( T ) ).size( )>{ } ) ) {
113  return refl_details::to_tuple(
114  value,
115  std::make_index_sequence<pub_nsdm_of( DAW_REFL( T ) ).size( )>{ } );
116  }
117 
118  template<JSONNAMETYPE Name, typename T>
119  using deduce_t = typename json_details::ensure_mapped_t<
120  json_details::json_deduced_type<T>>::template with_name<Name>;
121 
122  template<typename T, std::size_t Idx>
123  using submember_type_t =
124  std::tuple_element_t<Idx, DAW_TYPEOF( refl_details::to_tuple(
125  std::declval<T>( ) ) )>;
126 
127  template<typename T, std::size_t Idx>
128  consteval auto get_member_link_func( ) {
129  static constexpr auto member_info = pub_nsdm_of( DAW_REFL( T ) )[Idx];
130  static constexpr auto annot_rename =
131  get_annotaion<daw::json::refl_rename>( member_info );
132 
133  static constexpr std::string_view name =
134  annot_rename ? std::string_view( annot_rename->name )
135  : std::meta::identifier_of( member_info );
136  static_assert( not name.empty( ), "Unexpected empty name" );
137 
138  static constexpr auto annot_map_as = [] {
139  static constexpr auto refl_map_as_annot =
140  get_annotaion<refl_map_as>( member_info );
141  static constexpr auto refl_enum_string_annot =
142  get_annotaion<refl_enum_string>( member_info );
143  if constexpr( refl_map_as_annot ) {
144  static_assert( not refl_enum_string_annot,
145  "Do not use reflect.enum_string and reflect.map_as "
146  "at the same time" );
147  return refl_map_as_annot;
148  } else if constexpr( refl_enum_string_annot ) {
149  using json_member_no_name =
150  daw::json::enum_string<DAW_SPLICE(
151  std::meta::type_of( member_info ) ),
152  refl_enum_string_annot->Options>;
153  static constexpr auto info =
154  DAW_REFL( typename json_member_no_name::template with_name<
155  json_name<name.size( ) + 1>(
156  name.data( ),
157  std::make_index_sequence<name.size( ) + 1>{ } )> );
158  return std::optional<refl_map_as>{ refl_map_as{ info } };
159  } else {
160  return false;
161  }
162  }( );
163 
164  if constexpr( annot_map_as ) {
165  static_assert(
166  not annot_rename,
167  "Do not use reflect.rename and reflect.map_as at the same time" );
168  static constexpr auto result =
169  daw::traits::identity<DAW_SPLICE( annot_map_as->type )>{ };
170  return result;
171  } else {
172  return daw::traits::identity<deduce_t<
173  json_name<name.size( ) + 1>(
174  name.data( ), std::make_index_sequence<name.size( ) + 1>{ } ),
175  submember_type_t<T, Idx>>>{ };
176  }
177  }
178 
179  template<typename T, std::size_t Idx>
180  using get_member_link_t =
181  typename DAW_TYPEOF( get_member_link_func<T, Idx>( ) )::type;
182 
183  template<typename, typename>
184  struct make_data_contract;
185 
186  template<typename, typename...>
187  inline constexpr bool construction_test_v = false;
188 
189  template<typename T, typename... Ts>
190  inline constexpr bool construction_test_v<T, std::tuple<Ts...>> =
191  requires {
192  T{ std::declval<Ts>( )... };
193  };
194 
195  template<typename T>
196  concept Reflectable =
197  not std::is_empty_v<T> and std::is_class_v<T> and requires {
198  refl_details::to_tuple( std::declval<T>( ) );
199  }
200  and construction_test_v<T, DAW_TYPEOF( refl_details::to_tuple(
201  std::declval<T>( ) ) )>;
202 
203  template<Reflectable T, std::size_t... Is>
204  struct make_data_contract<T, std::index_sequence<Is...>> {
205  using type = json_member_list<get_member_link_t<T, Is>...>;
206 
207  DAW_ATTRIB_INLINE static constexpr auto to_json_data( T const &value ) {
208  return daw::forward_nonrvalue_as_tuple(
209  value.DAW_SPLICE( pub_nsdm_of( DAW_REFL( T ) )[Is] )... );
210  }
211  };
212 
213  template<typename E>
214  requires std::is_enum_v<E> constexpr E
215  enum_from_string( std::string_view name ) {
216  template for( constexpr auto enumerator :
217  std::meta::enumerators_of( DAW_REFL( E ) ) ) {
218  if( name == std::meta::identifier_of( enumerator ) ) {
219  return DAW_SPLICE( enumerator );
220  }
221  }
222  daw_json_error( ErrorReason::InvalidString );
223  }
224 
225  /*
226  template<typename E>
227  requires std::is_enum_v<E>
228  constexpr std::string_view enum_to_string( E value ) {
229  template for( constexpr auto enumerator: std::meta::enumerators_of(
230  DAW_REFL( E ) ) ) { if( value == DAW_SPLICE( enumerator ) ) { return
231  std::meta::identifier_of( enumerator );
232  }
233  }
234  return std::string_view{ };
235  }
236  */
237  template<typename E>
238  requires std::is_enum_v<E> constexpr std::string_view
239  enum_to_string( E value ) {
240  auto result = std::string_view{ };
241  DAW_SPLICE( expand( std::meta::enumerators_of( DAW_REFL( E ) ) ) ) >>
242  [&]<auto e> {
243  if( value == DAW_SPLICE( e ) ) {
244  result = std::meta::identifier_of( e );
245  }
246  };
247  return result;
248  }
249 
250  template<typename E>
251  requires std::is_enum_v<E> struct reflect_enum_as_string {
252  static constexpr E operator( )( std::string_view name ) {
253  return enum_from_string<E>( name );
254  }
255 
256  static constexpr std::string_view operator( )( E value ) {
257  return enum_to_string( value );
258  }
259  };
260  } // namespace refl_details
261 
262  template<typename E, json_options_t Options>
263  requires std::is_enum_v<E> struct enum_string
264  : json_custom_no_name<E, refl_details::reflect_enum_as_string<E>,
265  refl_details::reflect_enum_as_string<E>, Options> {
266  };
267 
268  inline constexpr struct reflect_t {
269  static consteval refl_rename rename( char const *name ) {
270  return refl_rename{ name };
271  }
272 
273  template<typename JsonMember>
274  static constexpr auto map_as = refl_map_as{ DAW_REFL( JsonMember ) };
275 
276  static constexpr auto ignore_with_default =
277  daw::json::refl_ignore_with_default{ };
278 
279  template<typename T>
280  static consteval auto ignore_with_value( T &&v ) {
281  return refl_ignore_with_value{ DAW_FWD( v ) };
282  }
283 
284  template<json_options_t Options>
285  static constexpr auto enum_string_with_opt =
286  daw::json::refl_enum_string{ Options };
287 
288  static constexpr auto enum_string =
289  enum_string_with_opt<json_custom_opts_def>;
290  } reflect{ };
291 
292  // Trait that specifies a type is to be reflected on for parse info
293  template<refl_details::Reflectable T>
294  inline constexpr bool is_reflectible_type_v =
295  refl_details::has_annotation( DAW_REFL( T ), reflect );
296  } // namespace experimental
297 
298  template<typename T>
299  requires is_reflectible_type_v<T> //
301  : refl_details::make_data_contract<
302  T, std::make_index_sequence<
303  refl_details::pub_nsdm_of( DAW_REFL( T ) ).size( )>> {};
304 
305 } // namespace daw::json::inline DAW_JSON_VER
306 
307 #undef DAW_REFL
308 #undef DAW_SPLICE
#define DAW_SPLICE(...)
#define DAW_REFL(...)
DAW_ATTRIB_NOINLINE void daw_json_error(ErrorReason reason)
constexpr struct daw::json::inline::experimental::reflect_t reflect
requires(std::is_aggregate_v< T > and std::is_class_v< T > and not std::is_empty_v< T >) inline const expr bool use_boost_pfr
Enable Boost PFR mappings for a type.
Mapping class for JSON data structures to C++. It must be specialized in order to parse to a user cla...
static consteval refl_rename rename(char const *name)
static consteval auto ignore_with_value(T &&v)
#define DAW_JSON_VER
The version string used in namespace definitions. Must be a valid namespace name.
Definition: version.h:25