huangcm
2025-08-30 0269911b91ed7e03c24005924cc6423abf245fb8
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
/*
 * 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