1 /*
2  * Copyright (C) 2018 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #ifndef IORAP_COMMON_INTROSPECTION_H
18 #define IORAP_COMMON_INTROSPECTION_H
19 
20 /*
21  * Provide zero-cost compile-time introspection of struct member fields.
22  *
23  * Example:
24  *
25  * // Declaration
26  * struct PackageEvent {
27  *
28  *   int type;
29  *   std::string package_uri;
30  *   std::string package_name;
31  * };
32  *
33  * IORAP_INTROSPECT_ADAPT_STRUCT(PackageEvent, type, package_uri, package_name);
34  *
35  * // Usage
36  * {
37  *   std::stringstream str;
38  *   for_each_member_field(PackageEvent{123,"hello","world"}, [&](auto&& val) {
39  *     str << val << ",";
40  *   }
41  *   CHECK_EQ("123,hello,world,"s, str.str());
42  * }
43  */
44 
45 #include "common/macros.h"
46 #include "common/type.h"
47 
48 #include <tuple>
49 
50 namespace iorap {
51 namespace introspect {
52 
53 template <auto value>
54 struct member_type;
55 
56 // Compile-time introspection data for a member-to-pointer.
57 //
58 // Example:
59 //   using package_uri_member_type = member_type<&PackageEvent::&package_uri>
60 //   int type = package_uri_member_type::value(PackageEvent{123,"hello","world"});
61 //   CHECK_EQ(type, 123);
62 template <typename T, typename F, F T::*member>
63 struct member_type<member> {
64   // The type of the struct this field is located in, e.g. 'struct XYZ {...}' -> XYZ.
65   static constexpr auto struct_t = type_c<T>;
66   // The type of the field, e.g. 'struct XYZ { int x; }' -> int.
67   static constexpr auto type = type_c<F>;
68 
69   // Allow a 'const U', 'volatile U', 'U&' etc here.
70   // Returns the value inside of 'U'.
71   template <typename U>
72   static constexpr decltype(auto) value(U&& v) {
73     static_assert(std::is_same_v<T, std::decay_t<U>>, "U must be cvref of T");
74 
75     using U_noref = std::remove_reference_t<U>;
76 
77     // This casts from the regular non-const pointer-to-member to a potentially const/volatile
78     // pointer-to-member.
79     F U_noref::*safer_member = member;
80 
81     // Now dereference it,
82     return v.*safer_member;
83     // TODO: are we properly returning && for rvalue, & for lvalue refs, etc?
84   }
85 
86   static constexpr void set_value(typename decltype(struct_t)::type& s,
87                                   typename decltype(type)::type&& value) {
88     s.*member = std::forward<typename decltype(type)::type>(value);
89   }
90 };
91 
92 // Given a self : T, where T has introspection-enabled support, T has some
93 // members m1, m2, m3, ... , mN.
94 //
95 // Invokes fun(self.*m1); fun(self.*m2); fun(self.*m3); ... ; fun(self.*mN).
96 template <typename T, typename F>
97 static constexpr void for_each_member_field_value(T&& self, F&& fun) {
98   constexpr auto members = introspect_members(type_c<std::decay_t<T>>);
99   // std::tuple<member_type<A>, member_type<B>, ...>
100 
101   // Warning: Don't use 'v=std::forward<V>(v)' as that actually captures-by-value.
102   for_each(members, [&fun, &self](auto&& type) mutable {
103     // Note that 'type' is a member_type
104     fun(type.value(std::forward<T>(self)));
105   });
106 }
107 
108 // Given a self : T, where T has introspection-enabled support, T has some
109 // members m1, m2, m3, ... , mN. The basic_type of each member is t1, t2, t3, ..., tN.
110 //
111 // Invokes
112 //   self.*m1 = fun(self, t1);
113 //   self.*m2 = fun(self, t2);
114 //   self.*m3 = fun(self, t3);
115 //   ...;
116 //   self.*mN = fun(self, tN).
117 template <typename T, typename F>
118 static constexpr void for_each_member_field_set_value(T&& self, F&& fun) {
119   constexpr auto members = introspect_members(type_c<std::decay_t<T>>);
120   // std::tuple<member_type<A>, member_type<B>, ...>
121 
122   // Warning: Don't use 'v=std::forward<V>(v)' as that actually captures-by-value.
123   for_each(members, [&fun, &self](auto&& type) mutable {
124     // Note that 'type' is a member_type
125     type.set_value(std::forward<T>(self), fun(type.type));
126   });
127 }
128 
129 }
130 }
131 
132 // Add compile-time introspection capabilities to a pre-existing struct or class.
133 //
134 // Arguments: Name, [Member1, Member2, ... MemberN]
135 //
136 // Example:
137 //
138 //   struct Rectangle {
139 //     int height;
140 //     int width;
141 //   };
142 //
143 //   IORAP_INTROSPECT_ADAPT_STRUCT(Rectangle, height, width);
144 //
145 // See also for_each_member_field_value.
146 #define IORAP_INTROSPECT_ADAPT_STRUCT(/*name, [member1, member2, member3, ...]*/...) \
147   IORAP_INTROSPECT_ADAPT_STRUCT_IMPL(IORAP_PP_NARG(__VA_ARGS__), __VA_ARGS__)
148 
149 #define IORAP_INTROSPECT_ADAPT_STRUCT_IMPL(N, ...) \
150   IORAP_PP_CONCAT(IORAP_INTROSPECT_ADAPT_STRUCT_IMPL_, N)(__VA_ARGS__)
151 
152 // This simple implementation relies on the 'introspect_members' function being overloaded
153 // for the type<T> values. ADL is then applied to resolve the exact overload for any T,
154 // thus allowing this function definition to be in any namespace.
155 
156 // The auto signature must conform to:
157 //   introspect_members(type<T>) -> std::tuple<member_type1, member_type_2, ...>
158 
159 // TODO: it would be nice to capture the name of the member as a string literal.
160 #define IORAP_INTROSPECT_ADAPT_STRUCT_IMPL_1(TYPE) \
161   static constexpr auto introspect_members(::iorap::introspect::type<TYPE>) { \
162     return std::make_tuple();\
163   }
164 #define IORAP_INTROSPECT_ADAPT_STRUCT_IMPL_2(TYPE, m1) \
165   static constexpr auto introspect_members(::iorap::introspect::type<TYPE>) { \
166     return std::make_tuple(::iorap::introspect::member_type<&TYPE::m1>{}\
167     );\
168   }
169 
170 #define IORAP_INTROSPECT_ADAPT_STRUCT_IMPL_3(TYPE, m1, m2) \
171   static constexpr auto introspect_members(::iorap::introspect::type<TYPE>) { \
172     return std::make_tuple(::iorap::introspect::member_type<&TYPE::m1>{},\
173                            ::iorap::introspect::member_type<&TYPE::m2>{}\
174     ); \
175   }
176 
177 #define IORAP_INTROSPECT_ADAPT_STRUCT_IMPL_4(TYPE, m1, m2, m3) \
178   static constexpr auto introspect_members(::iorap::introspect::type<TYPE>) { \
179     return std::make_tuple(::iorap::introspect::member_type<&TYPE::m1>{},\
180                            ::iorap::introspect::member_type<&TYPE::m2>{},\
181                            ::iorap::introspect::member_type<&TYPE::m3>{}\
182     ); \
183   }
184 
185 // TODO: Consider using IORAP_PP_MAP
186 
187 
188 #endif  // IORAP_COMMON_INTROSPECTION_H