DAW JSON Link
Loading...
Searching...
No Matches
daw_json_reflection_impl.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
18
19#if defined( DAW_JSON_HAS_REFLECTION )
21
22#include <daw/daw_bind_args_at.h>
23#include <daw/daw_concepts.h>
24#include <daw/daw_move.h>
25#include <daw/daw_pipelines.h>
26
27#include <cstddef>
28#include <meta>
29#include <string_view>
30#include <tuple>
31#include <type_traits>
32#include <utility>
33#include <vector>
34
35namespace daw::json::inline DAW_JSON_VER::refl_details {
36 struct reflect_all_t {};
37
38 template<EnumType E, json_options_t Options = json_custom_opts_def>
39 struct enum_string;
40
41 struct refl_ignored_base {
42 consteval refl_ignored_base( ) = default;
43 };
44
45 struct refl_annotation_base {
46 consteval refl_annotation_base( ) = default;
47 };
48
49 struct refl_map_as : refl_annotation_base {
50 std::meta::info type;
51
52 explicit consteval refl_map_as( std::meta::info i )
53 : type( i ) {}
54 };
55
56 struct refl_enum_string : refl_annotation_base {
57 json_options_t Options;
58
59 explicit consteval refl_enum_string( json_options_t opts )
60 : Options( opts ) {}
61 };
62
63 struct refl_rename : refl_annotation_base {
64 char const *name;
65
66 explicit consteval refl_rename( char const *Name )
67 : name( Name ) {}
68 };
69
70 template<typename T, T... Vals>
71 inline constexpr std::array<T, sizeof...( Vals )> constant_fixed_array = {
72 Vals... };
73
74 template<std::ranges::input_range R>
75 requires( std::is_constructible_v<std::ranges::range_value_t<R>,
76 std::ranges::range_reference_t<R>> )
77 consteval auto as_stdarray( R &&elems ) {
78
79 auto args = std::vector{ ^^std::ranges::range_value_t<R> };
80 for( const auto &V : elems ) {
81 args.push_back( reflect_constant( V ) );
82 }
83 return substitute( ^^constant_fixed_array, args );
84 }
85
86 consteval std::vector<std::meta::info>
87 annotations_of_with_base_type( std::meta::info item, std::meta::info type ) {
88 auto result = std::vector<std::meta::info>{ };
89 for( auto annotation : annotations_of( item ) ) {
90 if( is_base_of_type( type, type_of( annotation ) ) ) {
91 result.push_back( annotation );
92 }
93 }
94 return result;
95 }
96#if defined( __clang__ )
97 consteval std::vector<std::meta::info>
98 annotations_of_with_type( std::meta::info item, std::meta::info type ) {
99 auto result = std::vector<std::meta::info>{ };
100 for( auto annot : annotations_of( item ) ) {
101 if( type_of( annot ) == type ) {
102 result.push_back( annot );
103 }
104 }
105 return result;
106 }
107#endif
108
109 template<typename AnnotationType, typename T>
110 consteval bool has_annotation( ) {
111 return not annotations_of_with_base_type( ^^T, ^^AnnotationType ).empty( );
112 }
113
114 template<typename T>
115 consteval std::vector<std::meta::info> get_reflectible_members( ) {
116 using namespace daw::pipelines;
117 auto result = std::vector<std::meta::info>{ };
118 if( not annotations_of_with_base_type( ^^T, ^^reflect_all_t ).empty( ) ) {
119 return all_nsdm_of( ^^T );
120 }
121 return pub_nsdm_of( ^^T );
122 }
123
124 template<typename T>
125 consteval std::vector<std::meta::info>
126 get_non_ignored_reflectible_members( ) {
127 using namespace daw::pipelines;
128 auto result = std::vector<std::meta::info>{ };
129 auto const members = get_reflectible_members<T>( );
130 for( auto const member : members ) {
131 if( annotations_of_with_base_type( member, ^^refl_ignored_base )
132 .empty( ) ) {
133 result.push_back( member );
134 }
135 }
136 return result;
137 }
138
139 template<typename T>
140 constexpr auto to_tuple( T const &value ) {
141 static constexpr auto
142 members = [:as_stdarray( get_non_ignored_reflectible_members<T>( ) ):];
143 /* This currently fails to compile
144 static constexpr auto [... Is] =
145 std::make_index_sequence<members.size( )>{ };
146 return daw::forward_nonrvalue_as_tuple( value.[:members[Is]:]... );
147 */
148 return [&]<std::size_t... Is>( std::index_sequence<Is...> ) {
149 return daw::forward_nonrvalue_as_tuple( value.[:members[Is]:]... );
150 }( std::make_index_sequence<members.size( )>{ } );
151 }
152
153 template<typename T>
154 using to_tuple_t = DAW_TYPEOF( to_tuple( std::declval<T>( ) ) );
155
156 template<std::meta::info members_i>
157 consteval auto make_arg_indexes( ) {
158 static constexpr auto members = [:members_i:];
159 auto r = std::array<std::size_t, members.size( )>{ };
160 for( auto const [index, member] : daw::pipelines::Enumerate( members ) ) {
161 if( not annotations_of_with_base_type( member, ^^refl_ignored_base )
162 .empty( ) ) {
163 r[index] = daw::max_value<std::size_t>;
164 } else {
165 r[index] = static_cast<std::size_t>(
166 std::count_if( r.data( ),
167 r.data( ) + static_cast<std::ptrdiff_t>( index ),
168 []( std::size_t s ) {
169 return s != daw::max_value<std::size_t>;
170 } ) );
171 }
172 }
173 return r;
174 };
175
176 template<typename T, typename... Fns>
177 struct construct_t {
178 static constexpr T operator( )( auto &&fns, auto &&...args ) {
179 auto const &[... member_fns] = fns;
180 return T{ member_fns( DAW_FWD( args )... )... };
181 }
182 };
183
184 // Function to return the Nth parsed JSON member
185 template<typename result_t, std::size_t index>
186 struct ArgReturn {
187 static constexpr result_t operator( )( auto &&...arguments ) {
188 return static_cast<result_t>( DAW_FWD( arguments...[index] ) );
189 }
190 };
191
192 template<typename result_t, std::meta::info annotation>
193 struct DefaultReturn {
194 static constexpr result_t operator( )( auto &&... ) {
195 static constexpr auto const ignored_default = [:constant_of(
196 annotation ):];
197 return static_cast<result_t>( ignored_default );
198 }
199 };
200
201 template<std::meta::info members_i, std::meta::info arg_indexes_i,
202 typename... Args, typename C>
203 consteval auto fn_maker( C ) {
204 static constexpr auto members = [:members_i:];
205 static constexpr auto index = C::value;
206 static constexpr auto member = members[index];
207 static constexpr auto annotations = [:as_stdarray(
208 annotations_of_with_base_type(
209 member, ^^refl_ignored_base ) ):];
210 using result_t = [:type_of( member ):];
211 if constexpr( annotations.empty( ) ) {
212 static constexpr auto arg_indexes = [:arg_indexes_i:];
213 static constexpr auto i = arg_indexes[index];
214 static_assert( i != daw::max_value<std::size_t> );
215 static constexpr auto fn = ArgReturn<result_t, i>{ };
216 return fn;
217 } else {
218 static_assert( annotations.size( ) == 1 );
219 static constexpr std::meta::info annotation = annotations.front( );
220 static constexpr auto fn = DefaultReturn<result_t, annotation>{ };
221 return fn;
222 }
223 }
224
225 template<std::meta::info members_i, std::meta::info arg_indexes_i,
226 typename... Args, std::size_t... Is>
227 consteval auto make_member_fns( std::index_sequence<Is...> ) {
228 return std::tuple{ fn_maker<members_i, arg_indexes_i, Args...>(
229 std::integral_constant<std::size_t, Is>{ } )... };
230 }
231
232 template<typename T>
233 struct reflected_constructor {
234 static constexpr auto members_b = [:as_stdarray(
235 get_reflectible_members<T>( ) ):];
236 static constexpr auto arg_indexes = make_arg_indexes<^^members_b>( );
237 using result_t = std::remove_cvref_t<T>;
238
239 template<typename... Args>
240 static constexpr result_t operator( )( Args &&...args ) {
241 static constexpr auto const member_fns =
242 make_member_fns<^^members_b, ^^arg_indexes, Args...>(
243 std::make_index_sequence<members_b.size( )>{ } );
244 return construct_t<result_t>{ }( member_fns, DAW_FWD( args )... );
245 }
246 };
247
248 template<typename, typename...>
249 inline constexpr bool construction_test_v = false;
250
251 template<typename T, typename... Ts>
252 inline constexpr bool construction_test_v<T, std::tuple<Ts...>> =
253 requires( Ts... ts ) {
254 reflected_constructor<T>{ }( ts... );
255 };
256
257 template<typename T>
258 concept Reflectable =
259 not std::is_empty_v<T> and std::is_class_v<T> and requires( T v ) {
260 to_tuple( v );
261 }
262 and construction_test_v<T, to_tuple_t<T>>;
263
264 // Checks for an annotation of a specific type and returns it if it exists
265 template<typename AnnotationType, std::meta::info r>
266 consteval std::optional<AnnotationType> get_annotation( ) {
267 auto annotations = annotations_of_with_type( r, ^^AnnotationType );
268 if( annotations.empty( ) ) {
269 return std::nullopt;
270 }
271 return extract<AnnotationType>( annotations.front( ) );
272 }
273
274 template<auto member_info, json_name name>
275 consteval std::optional<refl_map_as> get_map_as_annotation( ) {
276 static constexpr auto refl_map_as_annot =
277 get_annotation<refl_map_as, member_info>( );
278
279 static constexpr auto refl_enum_string_annot =
280 get_annotation<refl_enum_string, member_info>( );
281
282 if constexpr( refl_map_as_annot ) {
283 static_assert( not refl_enum_string_annot,
284 "Do not use reflect.enum_string and reflect.map_as "
285 "at the same time" );
286 return refl_map_as_annot;
287 } else if constexpr( refl_enum_string_annot ) {
288 using json_member_no_name = enum_string<
289 typename[:type_of( member_info ):], refl_enum_string_annot->Options>;
290 static constexpr auto info =
291 ^^typename json_member_no_name::template with_name<name>;
292 return refl_map_as{ info };
293 } else {
294 return std::nullopt;
295 }
296 }
297
298 template<JSONNAMETYPE Name, typename T>
299 using deduce_t = typename json_details::ensure_mapped_t<
300 json_details::json_deduced_type<T>>::template with_name<Name>;
301
302 template<auto member_info>
303 consteval std::meta::info get_member_link_func( ) {
304 static constexpr auto annot_rename =
305 get_annotation<refl_rename, member_info>( );
306
307 static constexpr std::string_view svname =
308 annot_rename ? std::string_view( annot_rename->name )
309 : identifier_of( member_info );
310 static constexpr auto name = json_name<svname.size( ) + 1>(
311 svname.data( ), std::make_index_sequence<svname.size( ) + 1>{ } );
312 static_assert( not name.empty( ), "Unexpected empty name" );
313
314 static constexpr auto annot_map_as =
315 get_map_as_annotation<member_info, name>( );
316
317 if constexpr( annot_map_as ) {
318 static_assert(
319 not annot_rename,
320 "Do not use reflect.rename and reflect.map_as at the same time" );
321 return annot_map_as->type;
322 } else {
323 return ^^deduce_t<name, typename[:type_of( member_info ):]>;
324 }
325 }
326
327 template<typename T, std::size_t Idx>
328 using get_member_link_t = typename
329 [:get_member_link_func<get_non_ignored_reflectible_members<T>( )[Idx]>( ):];
330
331} // namespace daw::json::inline DAW_JSON_VER::refl_details
332
333#endif
#define DAW_JSON_VER
The version string used in namespace definitions. Must be a valid namespace name.
Definition version.h:20