| /* |
| * Copyright (C) 2018 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| #ifndef IORAP_COMMON_INTROSPECTION_H |
| #define IORAP_COMMON_INTROSPECTION_H |
| |
| /* |
| * Provide zero-cost compile-time introspection of struct member fields. |
| * |
| * Example: |
| * |
| * // Declaration |
| * struct PackageEvent { |
| * |
| * int type; |
| * std::string package_uri; |
| * std::string package_name; |
| * }; |
| * |
| * IORAP_INTROSPECT_ADAPT_STRUCT(PackageEvent, type, package_uri, package_name); |
| * |
| * // Usage |
| * { |
| * std::stringstream str; |
| * for_each_member_field(PackageEvent{123,"hello","world"}, [&](auto&& val) { |
| * str << val << ","; |
| * } |
| * CHECK_EQ("123,hello,world,"s, str.str()); |
| * } |
| */ |
| |
| #include "common/macros.h" |
| #include "common/type.h" |
| |
| #include <tuple> |
| |
| namespace iorap { |
| namespace introspect { |
| |
| template <auto value> |
| struct member_type; |
| |
| // Compile-time introspection data for a member-to-pointer. |
| // |
| // Example: |
| // using package_uri_member_type = member_type<&PackageEvent::&package_uri> |
| // int type = package_uri_member_type::value(PackageEvent{123,"hello","world"}); |
| // CHECK_EQ(type, 123); |
| template <typename T, typename F, F T::*member> |
| struct member_type<member> { |
| // The type of the struct this field is located in, e.g. 'struct XYZ {...}' -> XYZ. |
| static constexpr auto struct_t = type_c<T>; |
| // The type of the field, e.g. 'struct XYZ { int x; }' -> int. |
| static constexpr auto type = type_c<F>; |
| |
| // Allow a 'const U', 'volatile U', 'U&' etc here. |
| // Returns the value inside of 'U'. |
| template <typename U> |
| static constexpr decltype(auto) value(U&& v) { |
| static_assert(std::is_same_v<T, std::decay_t<U>>, "U must be cvref of T"); |
| |
| using U_noref = std::remove_reference_t<U>; |
| |
| // This casts from the regular non-const pointer-to-member to a potentially const/volatile |
| // pointer-to-member. |
| F U_noref::*safer_member = member; |
| |
| // Now dereference it, |
| return v.*safer_member; |
| // TODO: are we properly returning && for rvalue, & for lvalue refs, etc? |
| } |
| |
| static constexpr void set_value(typename decltype(struct_t)::type& s, |
| typename decltype(type)::type&& value) { |
| s.*member = std::forward<typename decltype(type)::type>(value); |
| } |
| }; |
| |
| // Given a self : T, where T has introspection-enabled support, T has some |
| // members m1, m2, m3, ... , mN. |
| // |
| // Invokes fun(self.*m1); fun(self.*m2); fun(self.*m3); ... ; fun(self.*mN). |
| template <typename T, typename F> |
| static constexpr void for_each_member_field_value(T&& self, F&& fun) { |
| constexpr auto members = introspect_members(type_c<std::decay_t<T>>); |
| // std::tuple<member_type<A>, member_type<B>, ...> |
| |
| // Warning: Don't use 'v=std::forward<V>(v)' as that actually captures-by-value. |
| for_each(members, [&fun, &self](auto&& type) mutable { |
| // Note that 'type' is a member_type |
| fun(type.value(std::forward<T>(self))); |
| }); |
| } |
| |
| // Given a self : T, where T has introspection-enabled support, T has some |
| // members m1, m2, m3, ... , mN. The basic_type of each member is t1, t2, t3, ..., tN. |
| // |
| // Invokes |
| // self.*m1 = fun(self, t1); |
| // self.*m2 = fun(self, t2); |
| // self.*m3 = fun(self, t3); |
| // ...; |
| // self.*mN = fun(self, tN). |
| template <typename T, typename F> |
| static constexpr void for_each_member_field_set_value(T&& self, F&& fun) { |
| constexpr auto members = introspect_members(type_c<std::decay_t<T>>); |
| // std::tuple<member_type<A>, member_type<B>, ...> |
| |
| // Warning: Don't use 'v=std::forward<V>(v)' as that actually captures-by-value. |
| for_each(members, [&fun, &self](auto&& type) mutable { |
| // Note that 'type' is a member_type |
| type.set_value(std::forward<T>(self), fun(type.type)); |
| }); |
| } |
| |
| } |
| } |
| |
| // Add compile-time introspection capabilities to a pre-existing struct or class. |
| // |
| // Arguments: Name, [Member1, Member2, ... MemberN] |
| // |
| // Example: |
| // |
| // struct Rectangle { |
| // int height; |
| // int width; |
| // }; |
| // |
| // IORAP_INTROSPECT_ADAPT_STRUCT(Rectangle, height, width); |
| // |
| // See also for_each_member_field_value. |
| #define IORAP_INTROSPECT_ADAPT_STRUCT(/*name, [member1, member2, member3, ...]*/...) \ |
| IORAP_INTROSPECT_ADAPT_STRUCT_IMPL(IORAP_PP_NARG(__VA_ARGS__), __VA_ARGS__) |
| |
| #define IORAP_INTROSPECT_ADAPT_STRUCT_IMPL(N, ...) \ |
| IORAP_PP_CONCAT(IORAP_INTROSPECT_ADAPT_STRUCT_IMPL_, N)(__VA_ARGS__) |
| |
| // This simple implementation relies on the 'introspect_members' function being overloaded |
| // for the type<T> values. ADL is then applied to resolve the exact overload for any T, |
| // thus allowing this function definition to be in any namespace. |
| |
| // The auto signature must conform to: |
| // introspect_members(type<T>) -> std::tuple<member_type1, member_type_2, ...> |
| |
| // TODO: it would be nice to capture the name of the member as a string literal. |
| #define IORAP_INTROSPECT_ADAPT_STRUCT_IMPL_1(TYPE) \ |
| static constexpr auto introspect_members(::iorap::introspect::type<TYPE>) { \ |
| return std::make_tuple();\ |
| } |
| #define IORAP_INTROSPECT_ADAPT_STRUCT_IMPL_2(TYPE, m1) \ |
| static constexpr auto introspect_members(::iorap::introspect::type<TYPE>) { \ |
| return std::make_tuple(::iorap::introspect::member_type<&TYPE::m1>{}\ |
| );\ |
| } |
| |
| #define IORAP_INTROSPECT_ADAPT_STRUCT_IMPL_3(TYPE, m1, m2) \ |
| static constexpr auto introspect_members(::iorap::introspect::type<TYPE>) { \ |
| return std::make_tuple(::iorap::introspect::member_type<&TYPE::m1>{},\ |
| ::iorap::introspect::member_type<&TYPE::m2>{}\ |
| ); \ |
| } |
| |
| #define IORAP_INTROSPECT_ADAPT_STRUCT_IMPL_4(TYPE, m1, m2, m3) \ |
| static constexpr auto introspect_members(::iorap::introspect::type<TYPE>) { \ |
| return std::make_tuple(::iorap::introspect::member_type<&TYPE::m1>{},\ |
| ::iorap::introspect::member_type<&TYPE::m2>{},\ |
| ::iorap::introspect::member_type<&TYPE::m3>{}\ |
| ); \ |
| } |
| |
| // TODO: Consider using IORAP_PP_MAP |
| |
| |
| #endif // IORAP_COMMON_INTROSPECTION_H |