DAW JSON Link
Loading...
Searching...
No Matches
daw_json_reflection_helpers.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
14
15#if defined( DAW_JSON_HAS_REFLECTION )
20
21#include <daw/daw_concepts.h>
22
23#include <meta>
24#include <optional>
25#include <type_traits>
26#include <vector>
27
28namespace daw::json::inline DAW_JSON_VER {
29 struct reflect_base_t;
30} // namespace daw::json::inline DAW_JSON_VER
31
32namespace daw::json::inline DAW_JSON_VER::refl_details {
34 consteval std::vector<std::meta::info>
35 pub_nsdm_of( std::meta::info type_class ) {
36 return nonstatic_data_members_of(
37 type_class, std::meta::access_context::unprivileged( ) );
38 }
39
40 consteval std::vector<std::meta::info>
41 all_nsdm_of( std::meta::info type_class ) {
42 return nonstatic_data_members_of( type_class,
43 std::meta::access_context::unchecked( ) );
44 }
45
46 template<typename T>
47 concept PublicMembersOnly = all_nsdm_of( remove_cvref( ^^T ) ).size( ) ==
48 pub_nsdm_of( remove_cvref( ^^T ) ).size( );
49 // The type may not be reflectable but we can error later
50 template<typename T>
51 concept PotentiallyReflectable =
52 not std::is_empty_v<T> and std::is_class_v<T> and std::is_aggregate_v<T> and
53 PublicMembersOnly<T>;
54
55 template<EnumType E, json_options_t Options = json_custom_opts_def>
56 struct enum_string;
57
58 template<EnumType E, json_options_t NumberOptions = number_opts_def>
59 struct enum_string_or_number;
60
61 struct refl_ignored_base {
62 consteval refl_ignored_base( ) = default;
63 };
64
65 struct refl_annotation_base {
66 consteval refl_annotation_base( ) = default;
67 };
68
69 struct refl_map_as : refl_annotation_base {
70 std::meta::info type;
71
72 explicit consteval refl_map_as( std::meta::info i )
73 : type( i ) {}
74 };
75
76 struct refl_enum_string : refl_annotation_base {
77 json_options_t Options;
78
79 explicit consteval refl_enum_string( json_options_t opts )
80 : Options( opts ) {}
81 };
82
83 struct refl_enum_string_or_number : refl_annotation_base {
84 json_options_t NumberOptions;
85
86 explicit consteval refl_enum_string_or_number( json_options_t opts )
87 : NumberOptions( opts ) {}
88 };
89
90 struct refl_rename : refl_annotation_base {
91 char const *name;
92
93 explicit consteval refl_rename( char const *Name )
94 : name( Name ) {}
95 };
96
97 consteval std::vector<std::meta::info>
98 annotations_of_with_base_type( std::meta::info item, std::meta::info type ) {
99 auto result = std::vector<std::meta::info>{ };
100 for( auto annotation : annotations_of( item ) ) {
101 if( is_base_of_type( type, type_of( annotation ) ) ) {
102 result.push_back( annotation );
103 }
104 }
105 return result;
106 }
107
109 template<typename T>
110 concept TypeAnnotatable =
111 std::is_class_v<T> or std::is_enum_v<T> or std::is_union_v<T>;
112
115 template<typename Annotation, typename T>
116 consteval std::optional<Annotation> get_type_annotation( ) {
117 if constexpr( TypeAnnotatable<T> ) {
118 for( auto annot : annotations_of( ^^T ) ) {
119 if( remove_cv( type_of( annot ) ) == ^^Annotation ) {
120 return extract<Annotation>( annot );
121 }
122 }
123 }
124 return std::nullopt;
125 }
126
127 template<typename AnnotationBase, typename T>
128 consteval bool has_type_annotation_with_base( ) {
129 if constexpr( TypeAnnotatable<T> ) {
130 return not annotations_of_with_base_type( ^^T, ^^AnnotationBase )
131 .empty( );
132 } else {
133 return false;
134 }
135 }
136
140 template<typename T>
141 concept HasTypeLevelAnnotation =
142 has_type_annotation_with_base<refl_annotation_base, T>( ) or
143 has_type_annotation_with_base<refl_ignored_base, T>( ) or
144 ( std::is_enum_v<T> and has_type_annotation_with_base<reflect_base_t, T>( ) );
145
148 template<typename E>
149 concept EnumStringAnnotated =
150 std::is_enum_v<E> and
151 get_type_annotation<refl_enum_string, E>( ).has_value( );
152
156 template<typename E>
157 concept EnumStringOrNumberAnnotated =
158 std::is_enum_v<E> and
159 get_type_annotation<refl_enum_string_or_number, E>( ).has_value( );
160
163 template<typename T>
164 concept MapAsAnnotated =
165 get_type_annotation<refl_map_as, T>( ).has_value( );
166
169 template<HasTypeLevelAnnotation T>
170 consteval auto type_annotation_mapping( ) {
171 static_assert( not( std::is_enum_v<T> and
172 has_type_annotation_with_base<reflect_base_t, T>( ) ),
173 "reflect is only valid on class types. Use "
174 "reflect.enum_string to map an enum as a string" );
175 static_assert( not get_type_annotation<refl_rename, T>( ),
176 "reflect.rename is only valid on members, not types" );
177 static_assert( not has_type_annotation_with_base<refl_ignored_base, T>( ),
178 "reflect.ignored is only valid on members, not types" );
179 static_assert( std::is_enum_v<T> or
180 not get_type_annotation<refl_enum_string, T>( ),
181 "reflect.enum_string is only valid on enum types or "
182 "members of enum type" );
183 static_assert( std::is_enum_v<T> or
184 not get_type_annotation<refl_enum_string_or_number, T>( ),
185 "reflect.enum_string_or_number is only valid on enum "
186 "types or members of enum type" );
187 static_assert( not( EnumStringAnnotated<T> and MapAsAnnotated<T> ),
188 "Do not use reflect.enum_string and reflect.map_as "
189 "at the same time" );
190 static_assert( not( EnumStringOrNumberAnnotated<T> and MapAsAnnotated<T> ),
191 "Do not use reflect.enum_string_or_number and "
192 "reflect.map_as at the same time" );
193 static_assert(
194 not( EnumStringOrNumberAnnotated<T> and EnumStringAnnotated<T> ),
195 "Do not use reflect.enum_string_or_number and reflect.enum_string at "
196 "the same time" );
197 if constexpr( EnumStringAnnotated<T> ) {
198 static constexpr auto annot = get_type_annotation<refl_enum_string, T>( );
199 return daw::traits::identity<enum_string<T, annot->Options>>{ };
200 } else if constexpr( EnumStringOrNumberAnnotated<T> ) {
201 static constexpr auto annot =
202 get_type_annotation<refl_enum_string_or_number, T>( );
203 return daw::traits::identity<
204 enum_string_or_number<T, annot->NumberOptions>>{ };
205 } else if constexpr( MapAsAnnotated<T> ) {
206 static constexpr auto annot = get_type_annotation<refl_map_as, T>( );
207 using mapping_t = typename[:annot->type:];
208 static_assert( json_details::is_a_json_type_v<mapping_t>,
209 "A type level reflect.map_as<JsonType> requires a JSON "
210 "type, e.g. json_number_no_name<int>" );
211 static_assert( json_details::is_no_name_v<mapping_t>,
212 "A type level reflect.map_as<JsonType> requires a no "
213 "name JSON type, e.g. json_number_no_name<int>. The "
214 "member name is supplied where the type is used" );
215 return daw::traits::identity<mapping_t>{ };
216 } else {
217 // Unreachable, the checks above will have failed
218 return daw::traits::identity<void>{ };
219 }
220 }
221
222 template<HasTypeLevelAnnotation T>
223 using type_annotation_mapping_t =
224 typename decltype( type_annotation_mapping<T>( ) )::type;
225} // namespace daw::json::inline DAW_JSON_VER::refl_details
226#endif
#define DAW_JSON_VER
The version string used in namespace definitions. Must be a valid namespace name.
Definition version.h:20