/*
|
* 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
|