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