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