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