DAW JSON Link
Loading...
Searching...
No Matches
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
12
15
16#include <daw/daw_bind_args_at.h>
17#include <daw/daw_concepts.h>
18#include <daw/daw_move.h>
19#include <daw/daw_pipelines.h>
20
21#include <cstddef>
22#include <meta>
23#include <string_view>
24#include <tuple>
25#include <type_traits>
26#include <utility>
27#include <vector>
28
29#if defined( DAW_JSON_HAS_REFLECTION )
30
31namespace daw::json::inline DAW_JSON_VER {
32 inline namespace experimental {
33 namespace refl_details {
34 template<EnumType E, json_options_t Options = json_custom_opts_def>
35 struct enum_string;
36
37 struct refl_map_as {
38 std::meta::info type;
39 };
40
41 struct refl_rename {
42 char const *name;
43 };
44
45 struct refl_ignored_base {
46 refl_ignored_base( ) = default;
47 };
48
49 template<typename D>
50 struct refl_ignored_value : refl_ignored_base {
51 constexpr refl_ignored_value( D value )
52 : default_value( value ) {}
53 D default_value;
54 template<typename T>
55 constexpr operator T( ) const {
56 if constexpr( std::is_convertible_v<D, T> ) {
57 return default_value;
58 } else if constexpr( requires( D v ) {
59 { v( ) }->std::convertible_to<T>;
60 } ) {
61 return default_value( );
62 }
63 }
64 };
65
66 struct refl_ignored : refl_ignored_base {
67 template<typename T>
68 static consteval auto operator( )( T &&rhs ) {
69 return refl_ignored_value<T>{ DAW_FWD( rhs ) };
70 }
71
72 template<typename T>
73 consteval operator T( ) const {
74 return T{ };
75 }
76 };
77
78 struct refl_enum_string {
79 json_options_t Options;
80 };
81
83 consteval std::vector<std::meta::info>
84 pub_nsdm_of( std::meta::info type_class ) {
85 return nonstatic_data_members_of(
86 type_class, std::meta::access_context::unprivileged( ) );
87 }
88
89 template<typename T, T... Vals>
90 inline constexpr std::array<T, sizeof...( Vals )> constant_fixed_array = {
91 Vals... };
92
93 template<std::ranges::input_range R>
94 requires( std::is_constructible_v<std::ranges::range_value_t<R>,
95 std::ranges::range_reference_t<R>> )
96 consteval auto as_stdarray( R &&elems ) {
97
98 auto args = std::vector{ ^^std::ranges::range_value_t<R> };
99 for( const auto &V : elems ) {
100 args.push_back( reflect_constant( V ) );
101 }
102 return substitute( ^^constant_fixed_array, args );
103 }
104
105#if defined( __clang__ )
106 consteval std::vector<std::meta::info>
107 annotations_of_with_type( std::meta::info item, std::meta::info type ) {
108 auto result = std::vector<std::meta::info>{ };
109 for( auto annot : annotations_of( item ) ) {
110 if( type_of( annot ) == type ) {
111 result.push_back( annot );
112 }
113 }
114 return result;
115 }
116#endif
117
118 // Checks for an annotation of a specific type and returns it if it exists
119 template<typename AnnotationType, std::meta::info r>
120 consteval std::optional<AnnotationType> get_annotation( ) {
121 auto annotations = annotations_of_with_type( r, ^^AnnotationType );
122 if( annotations.empty( ) ) {
123 return std::nullopt;
124 }
125 return extract<AnnotationType>( annotations.front( ) );
126 }
127
128 template<typename AnnotationType, typename T>
129 consteval bool has_annotation( ) {
130 return not annotations_of_with_type( ^^T, ^^AnnotationType ).empty( );
131 }
132
133 struct member_reflection_t {};
134
135 consteval auto annotations_of_with_base_type( std::meta::info item,
136 std::meta::info type ) {
137 auto result = std::vector<std::meta::info>{ };
138 for( auto annotation : annotations_of( item ) ) {
139 if( is_base_of_type( type, type_of( annotation ) ) ) {
140 result.push_back( annotation );
141 }
142 }
143 return result;
144 }
145
146 template<typename T>
147 consteval std::vector<std::meta::info>
148 get_non_ignored_reflectible_members( ) {
149 using namespace daw::pipelines;
150 auto result = std::vector<std::meta::info>{ };
151 auto const members = pub_nsdm_of( ^^T );
152 for( auto const member : members ) {
153 if( annotations_of_with_base_type( member, ^^refl_ignored_base )
154 .empty( ) ) {
155 result.push_back( member );
156 }
157 }
158 return result;
159 }
160
161 template<typename T>
162 constexpr auto to_tuple( T const &value ) {
163 static constexpr auto
164 members = [:as_stdarray(
165 get_non_ignored_reflectible_members<T>( ) ):];
166
167 /* This currently fails to compile
168 static constexpr auto [... Is] =
169 std::make_index_sequence<members.size( )>{ };
170 return daw::forward_nonrvalue_as_tuple( value.[:members[Is]:]... );
171 */
172 return [&]<std::size_t... Is>( std::index_sequence<Is...> ) {
173 return daw::forward_nonrvalue_as_tuple( value.[:members[Is]:]... );
174 }( std::make_index_sequence<members.size( )>{ } );
175 }
176
177 template<typename T>
178 using to_tuple_t = DAW_TYPEOF( to_tuple( std::declval<T>( ) ) );
179
180 template<JSONNAMETYPE Name, typename T>
181 using deduce_t = typename json_details::ensure_mapped_t<
182 json_details::json_deduced_type<T>>::template with_name<Name>;
183
184 template<typename T, std::size_t Idx>
185 using submember_type_t = std::tuple_element_t<Idx, to_tuple_t<T>>;
186
187 template<auto member_info, json_name name>
188 consteval std::optional<refl_map_as> get_map_as_annotation( ) {
189 static constexpr auto refl_map_as_annot =
190 get_annotation<refl_map_as, member_info>( );
191
192 static constexpr auto refl_enum_string_annot =
193 get_annotation<refl_enum_string, member_info>( );
194
195 if constexpr( refl_map_as_annot ) {
196 static_assert( not refl_enum_string_annot,
197 "Do not use reflect.enum_string and reflect.map_as "
198 "at the same time" );
199 return refl_map_as_annot;
200 } else if constexpr( refl_enum_string_annot ) {
201 using json_member_no_name = enum_string<
202 typename[:type_of( member_info ):],
203 refl_enum_string_annot->Options>;
204 static constexpr auto info =
205 ^^typename json_member_no_name::template with_name<name>;
206 return refl_map_as{ info };
207 } else {
208 return std::nullopt;
209 }
210 }
211
212 template<auto member_info>
213 consteval std::meta::info get_member_link_func( ) {
214 static constexpr auto annot_rename =
215 get_annotation<refl_rename, member_info>( );
216
217 static constexpr std::string_view svname =
218 annot_rename ? std::string_view( annot_rename->name )
219 : identifier_of( member_info );
220 static constexpr auto name = json_name<svname.size( ) + 1>(
221 svname.data( ), std::make_index_sequence<svname.size( ) + 1>{ } );
222 static_assert( not name.empty( ), "Unexpected empty name" );
223
224 static constexpr auto annot_map_as =
225 get_map_as_annotation<member_info, name>( );
226
227 if constexpr( annot_map_as ) {
228 static_assert(
229 not annot_rename,
230 "Do not use reflect.rename and reflect.map_as at the same time" );
231 return annot_map_as->type;
232 } else {
233 return ^^deduce_t<name, typename[:type_of( member_info ):]>;
234 }
235 }
236
237 template<typename T, std::size_t Idx>
238 using get_member_link_t =
239 typename[:get_member_link_func<
240 get_non_ignored_reflectible_members<T>( )[Idx]>( ):];
241
242 template<EnumType E>
243 constexpr E enum_from_string( std::string_view name ) {
244 template for( constexpr auto enumerator : enumerators_of( ^^E ) ) {
245 // TODO add name formatting e.g lower/upper/first capital
246 if( name == identifier_of( enumerator ) ) {
247 return [:enumerator:];
248 }
249 }
250 daw_json_error( ErrorReason::InvalidString );
251 }
252
253 template<EnumType E>
254 constexpr std::string_view enum_to_string( E value ) {
255 static constexpr auto enums =
256 reflect_constant_array( enumerators_of( ^^E ) );
257 template for( constexpr auto enumerator : [:enums:] ) {
258 if( value == [:enumerator:] ) {
259 return identifier_of( enumerator );
260 }
261 }
262 return std::string_view{ };
263 }
264
265 template<EnumType E>
266 struct reflect_enum_as_string {
267 static constexpr E operator( )( std::string_view name ) {
268 return enum_from_string<E>( name );
269 }
270
271 static constexpr std::string_view operator( )( E value ) {
272 return enum_to_string( value );
273 }
274 };
275
276 template<typename T, std::size_t... Idx>
277 consteval std::meta::info
278 get_json_members_list_impl( std::index_sequence<Idx...> ) {
279 return ^^json_member_list<get_member_link_t<T, Idx>...>;
280 }
281
282 template<typename T>
283 consteval std::meta::info get_json_member_list( ) {
284 static constexpr auto sz =
285 get_non_ignored_reflectible_members<T>( ).size( );
286 return get_json_members_list_impl<T>( std::make_index_sequence<sz>{ } );
287 }
288
289 template<EnumType E, json_options_t Options>
290 struct enum_string
291 : json_custom_no_name<E, reflect_enum_as_string<E>,
292 reflect_enum_as_string<E>, Options> {};
293
294 template<typename result_t, std::meta::info annotation>
295 struct DefaultReturn {
296 static constexpr auto operator( )( auto &&... ) {
297 static constexpr auto const ignored_default = [:constant_of(
298 annotation ):];
299 return static_cast<result_t>( ignored_default );
300 }
301 };
302
303 // Function to return the Nth parsed JSON member
304 template<typename result_t, std::size_t index>
305 struct ArgReturn {
306 static constexpr auto operator( )( auto &&...arguments ) {
307 return static_cast<result_t>( DAW_FWD( arguments...[index] ) );
308 }
309 };
310
311 template<std::meta::info members_i, std::meta::info arg_indexes_i,
312 typename... Args, typename C>
313 consteval auto fn_maker( C ) {
314 static constexpr auto members = [:members_i:];
315 static constexpr auto index = C::value;
316 static constexpr auto member = members[index];
317 static constexpr auto annotations = [:as_stdarray(
318 annotations_of_with_base_type(
319 member,
320 ^^refl_ignored_base ) ):];
321 using result_t = [:type_of( member ):];
322 if constexpr( annotations.empty( ) ) {
323 static constexpr auto arg_indexes = [:arg_indexes_i:];
324 static constexpr auto i = arg_indexes[index];
325 static_assert( i != daw::max_value<std::size_t> );
326 static constexpr auto fn = ArgReturn<result_t, i>{ };
327 return fn;
328 } else {
329 static_assert( annotations.size( ) == 1 );
330 static constexpr std::meta::info annotation = annotations.front( );
331 static constexpr auto fn = DefaultReturn<result_t, annotation>{ };
332 return fn;
333 }
334 }
335
336 template<std::meta::info members_i, std::meta::info arg_indexes_i,
337 typename... Args, std::size_t... Is>
338 consteval auto make_member_fns( std::index_sequence<Is...> ) {
339 return std::tuple{ fn_maker<members_i, arg_indexes_i, Args...>(
340 std::integral_constant<std::size_t, Is>{ } )... };
341 }
342
343 template<std::meta::info members_i>
344 consteval auto make_arg_indexes( ) {
345 static constexpr auto members = [:members_i:];
346 auto r = std::array<std::size_t, members.size( )>{ };
347 for( auto const [index, member] :
348 daw::pipelines::Enumerate( members ) ) {
349 if( not annotations_of_with_base_type( member, ^^refl_ignored_base )
350 .empty( ) ) {
351 r[index] = daw::max_value<std::size_t>;
352 } else {
353 r[index] = static_cast<std::size_t>(
354 std::count_if( r.data( ),
355 r.data( ) + static_cast<std::ptrdiff_t>( index ),
356 []( std::size_t s ) {
357 return s != daw::max_value<std::size_t>;
358 } ) );
359 }
360 }
361 return r;
362 };
363
364 template<typename T, typename... Fns>
365 struct construct_t {
366 static constexpr T operator( )( auto &&fns, auto &&...args ) {
367 auto const &[... member_fns] = fns;
368 return T{ member_fns( DAW_FWD( args )... )... };
369 }
370 };
371
372 template<typename T>
373 struct reflected_constructor {
374 static constexpr auto members_b = [:as_stdarray( pub_nsdm_of( ^^T ) ):];
375 static constexpr auto arg_indexes = make_arg_indexes<^^members_b>( );
376 using result_t = std::remove_cvref_t<T>;
377
378 template<typename... Args>
379 static constexpr result_t operator( )( Args &&...args ) {
380 static constexpr auto const member_fns =
381 make_member_fns<^^members_b, ^^arg_indexes, Args...>(
382 std::make_index_sequence<members_b.size( )>{ } );
383 return construct_t<result_t>{ }( member_fns, DAW_FWD( args )... );
384 }
385 };
386
387 template<typename, typename...>
388 inline constexpr bool construction_test_v = false;
389
390 template<typename T, typename... Ts>
391 inline constexpr bool construction_test_v<T, std::tuple<Ts...>> =
392 requires( Ts... ts ) {
393 reflected_constructor<T>{ }( ts... );
394 };
395
396 template<typename T>
397 concept Reflectable =
398 not std::is_empty_v<T> and std::is_class_v<T> and requires( T v ) {
399 to_tuple( v );
400 }
401 and construction_test_v<T, to_tuple_t<T>>;
402
403 // Trait that specifies a type is to be reflected on for parse info
404
405 } // namespace refl_details
406
410 struct reflect_t {
413 template<json_name Name>
414 static constexpr auto rename = refl_details::refl_rename{ Name.m_data };
415
416 template<typename JsonMember>
417 static constexpr auto map_as = refl_details::refl_map_as{ ^^JsonMember };
418
422 static constexpr auto ignored = refl_details::refl_ignored{ };
423
425 template<json_options_t Options>
426 static constexpr auto enum_string_with_opt =
427 refl_details::refl_enum_string{ Options };
428
429 static constexpr auto enum_string =
430 enum_string_with_opt<json_custom_opts_def>;
431 };
432
433 inline constexpr auto reflect = reflect_t{ };
434
435 } // namespace experimental
436 template<typename T>
437 inline constexpr bool enable_reflection_for = false;
438
439 template<typename T>
440 concept ReflectionEnabled =
441 enable_reflection_for<T> or refl_details::has_annotation<reflect_t, T>( );
442
443 template<ReflectionEnabled T>
444 struct json_data_contract<T> {
445 using constructor_t = refl_details::reflected_constructor<T>;
446
447 using type = typename[:refl_details::get_json_member_list<T>( ):];
448
449 DAW_ATTRIB_INLINE static constexpr auto to_json_data( T const &value ) {
450 return refl_details::to_tuple( value );
451 }
452 };
453} // namespace daw::json::inline DAW_JSON_VER
454
455#endif
DAW_ATTRIB_NOINLINE void daw_json_error(ErrorReason reason)
#define DAW_JSON_VER
The version string used in namespace definitions. Must be a valid namespace name.
Definition version.h:20