-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathfunction_traits.h
More file actions
290 lines (244 loc) · 13.9 KB
/
Copy pathfunction_traits.h
File metadata and controls
290 lines (244 loc) · 13.9 KB
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
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
#pragma once
#include <type_traits>
#include <functional>
#include <tuple>
#include <array>
#include <optional>
/**
* @brief Type traits for inspecting callable signatures and member pointers.
*
* Sub-namespace CallableFn provides function_signature, a trait that extracts
* return type, argument tuple and arity from any callable (free function,
* std::function, lambda, member function — including all cv/ref/noexcept
* combinations).
*
* The outer namespace provides general-purpose traits for member pointers,
* enums, optional, and char-array detection.
*/
namespace FunctionTraitsNs {
// -------------------------------------------------------------------------
// remove_cvref_t backport
// -------------------------------------------------------------------------
#if __cplusplus < 202002L
/// @brief Backport of std::remove_cvref_t for C++17.
template <class T>
using remove_cvref_t = std::remove_cv_t<std::remove_reference_t<T>>;
#else
template <class T>
using remove_cvref_t = std::remove_cvref_t<T>;
#endif
// =========================================================================
// CallableFn — signature introspection
// =========================================================================
namespace CallableFn {
// Primary: forward to operator() for lambdas and function objects.
template <typename T>
struct function_signature : function_signature<decltype(&T::operator())> {};
// Free function pointer
template <typename R, typename... Args>
struct function_signature<R(*)(Args...)> {
using class_type = void;
using return_type = R;
using input_type = std::tuple<Args...>;
static constexpr std::size_t arity = sizeof...(Args);
};
template <typename R, typename... Args>
struct function_signature<R(*)(Args...) noexcept> {
using class_type = void;
using return_type = R;
using input_type = std::tuple<Args...>;
static constexpr std::size_t arity = sizeof...(Args);
};
// std::function
template <typename R, typename... Args>
struct function_signature<std::function<R(Args...)>> {
using class_type = void;
using return_type = R;
using input_type = std::tuple<Args...>;
static constexpr std::size_t arity = sizeof...(Args);
};
// Generates a function_signature specialization for member functions with
// the given cv-qualifier, ref-qualifier and noexcept specifier.
#define FTRAITS_MEMBER_SIG(cv, ref, noex) \
template <typename C, typename R, typename... Args> \
struct function_signature<R(C::*)(Args...) cv ref noex> { \
using class_type = C; \
using return_type = R; \
using input_type = std::tuple<Args...>; \
static constexpr std::size_t arity = sizeof...(Args); \
};
FTRAITS_MEMBER_SIG( , , )
FTRAITS_MEMBER_SIG( , , noexcept )
FTRAITS_MEMBER_SIG(const , , )
FTRAITS_MEMBER_SIG(const , , noexcept )
FTRAITS_MEMBER_SIG(volatile , , )
FTRAITS_MEMBER_SIG(volatile , , noexcept )
FTRAITS_MEMBER_SIG(const volatile, , )
FTRAITS_MEMBER_SIG(const volatile, , noexcept )
#if __cplusplus >= 201703L
FTRAITS_MEMBER_SIG( , & , )
FTRAITS_MEMBER_SIG( , & , noexcept )
FTRAITS_MEMBER_SIG( , &&, )
FTRAITS_MEMBER_SIG( , &&, noexcept )
FTRAITS_MEMBER_SIG(const , & , )
FTRAITS_MEMBER_SIG(const , & , noexcept )
FTRAITS_MEMBER_SIG(const , &&, )
FTRAITS_MEMBER_SIG(const , &&, noexcept )
#endif
#undef FTRAITS_MEMBER_SIG
// --- Convenience aliases ---
/// @brief Class of callable F.
template <typename F>
using class_t = typename function_signature<std::decay_t<F>>::class_type;
/// @brief Argument tuple of callable F.
template <typename F>
using input_t = typename function_signature<std::decay_t<F>>::input_type;
/// @brief Return type of callable F.
template <typename F>
using return_t = typename function_signature<std::decay_t<F>>::return_type;
/// @brief Argument count of callable F.
template <typename F>
constexpr std::size_t arity_v = function_signature<std::decay_t<F>>::arity;
/// @brief Class of a member function pointer passed as a value.
template <auto MemberFnPtr>
using member_class_t = class_t<decltype(MemberFnPtr)>;
/// @brief Argument tuple of a member function pointer passed as a value.
template <auto MemberFnPtr>
using member_input_t = input_t<decltype(MemberFnPtr)>;
/// @brief Return type of a member function pointer passed as a value.
template <auto MemberFnPtr>
using member_return_t = return_t<decltype(MemberFnPtr)>;
/// @brief Argument count of a member function pointer passed as a value.
template <auto MemberFnPtr>
constexpr std::size_t member_arity_v = arity_v<decltype(MemberFnPtr)>;
// Safe tuple element: yields void when Idx is out of range.
template <typename Tuple, std::size_t Idx, bool IsValid>
struct tuple_element_safe { using type = void; };
template <typename Tuple, std::size_t Idx>
struct tuple_element_safe<Tuple, Idx, true> { using type = std::tuple_element_t<Idx, Tuple>; };
/// @brief std::tuple_element_t<Idx, Tuple>, or void if Idx >= tuple size.
template <typename Tuple, std::size_t Idx>
using tuple_element_safe_t = typename tuple_element_safe<
Tuple, Idx, (Idx < std::tuple_size_v<Tuple>)>::type;
} // namespace CallableFn
// =========================================================================
// Member pointer traits
// =========================================================================
/// @brief Checks that F is a member function pointer of class C.
template <typename C, typename F> struct is_member_of_class : std::false_type {};
template <typename C, typename R, typename... Args>
struct is_member_of_class<C, R(C::*)(Args...)> : std::true_type {};
template <typename C, typename F>
inline constexpr bool is_member_of_class_v = is_member_of_class<C, F>::value; // was ::value() — compile error
/// @brief Checks that M is a member object pointer of class C.
template <typename C, typename M> struct is_member_object_of_class : std::false_type {};
template <typename C, typename T>
struct is_member_object_of_class<C, T C::*> : std::true_type {};
template <typename C, typename M>
inline constexpr bool is_member_object_of_class_v = is_member_object_of_class<C, M>::value;
/// @brief Extracts the pointed-to type from a data member pointer.
template <typename M> struct member_object_type;
template <typename C, typename T>
struct member_object_type<T C::*> { using type = T; };
template <typename M>
using member_object_type_t = typename member_object_type<std::remove_cv_t<M>>::type;
// =========================================================================
// Enum / optional helpers
// =========================================================================
/// @brief For enum types: the underlying integer type. For all others: T itself.
template <typename U, bool IsEnum = std::is_enum_v<U>>
struct get_convert_type_for_possible_enum { using type = U; };
template <typename U>
struct get_convert_type_for_possible_enum<U, true> { using type = std::underlying_type_t<U>; };
template <typename U>
using get_convert_type_for_possible_enum_t = typename get_convert_type_for_possible_enum<U>::type;
/// @brief Detects std::optional<T>.
template <typename T> struct is_std_optional : std::false_type {};
template <typename T> struct is_std_optional<std::optional<T>> : std::true_type {};
template <typename T>
inline constexpr bool is_std_optional_v = is_std_optional<std::decay_t<T>>::value;
/// @brief Unwraps std::optional<T> → T; passthrough for non-optional types.
template <typename T> struct optional_value { using type = remove_cvref_t<T>; };
template <typename U> struct optional_value<std::optional<U>> { using type = U; };
template <typename T>
using optional_value_t = typename optional_value<remove_cvref_t<T>>::type;
// =========================================================================
// Callable tuple traits
// =========================================================================
/// @brief True if every callable in the tuple is invocable with no arguments.
template <typename Tuple> struct is_nullary_invocable_tuple;
template <typename... Fns>
struct is_nullary_invocable_tuple<std::tuple<Fns...>> {
static constexpr bool value = (std::is_invocable_v<Fns> && ...);
};
/// @brief True if every callable in Tuple is invocable with Args...
template <typename Tuple, typename... Args> struct is_invocable_tuple;
template <typename... Fns, typename... Args>
struct is_invocable_tuple<std::tuple<Fns...>, Args...> {
static constexpr bool value = (std::is_invocable_v<Fns, Args...> && ...);
};
template <typename Tuple, typename... Args>
inline constexpr bool is_nullary_invocable_tuple_v = is_nullary_invocable_tuple<Tuple>::value;
template <typename Tuple, typename... Args>
inline constexpr bool is_invocable_tuple_v = is_invocable_tuple<Tuple, Args...>::value;
/// @brief True if FirstFn and SecondFn accept the same argument types.
template <typename FirstFn, typename SecondFn>
struct are_connectable_for_input {
static constexpr bool value = std::is_same_v<CallableFn::input_t<FirstFn>,
CallableFn::input_t<SecondFn>>;
};
template <typename FirstFn, typename SecondFn>
inline constexpr bool are_connectable_for_input_v = are_connectable_for_input<FirstFn, SecondFn>::value;
/// @brief Underlying integer type for enums; T itself for non-enum types.
template <typename T, bool IsEnum = std::is_enum_v<T>>
struct underlying_or_self_impl { using type = T; };
template <typename T>
struct underlying_or_self_impl<T, true> { using type = typename std::underlying_type<T>::type; };
template <typename T>
using underlying_or_self_t = typename underlying_or_self_impl<T>::type;
// =========================================================================
// Array detection
// =========================================================================
/**
* @brief Detects whether T (cvref-stripped) is a @c std::array<E,N>
* specialisation for any element type E and any compile-time size N.
*
* cv-qualifiers and references are stripped before the check via
* @c remove_cvref_t, so @c const std::array<int,3>& satisfies the
* trait just as well as the plain @c std::array<int,3>.
*
* Use the companion variable template @c is_std_array_v as the
* everyday shorthand.
*
* @code
* static_assert( FunctionTraitsNs::is_std_array_v<std::array<int, 4>>);
* static_assert(!FunctionTraitsNs::is_std_array_v<std::vector<int>>);
* static_assert( FunctionTraitsNs::is_std_array_v<const std::array<float, 2>&>);
* @endcode
*/
template <typename T> struct is_std_array : std::false_type {};
template <typename T, std::size_t N>
struct is_std_array<std::array<T, N>> : std::true_type {}; ///< Partial specialisation — matches any std::array<E,N>.
/// @brief Variable-template shorthand: @c true iff T (cvref-stripped) is a std::array.
template <typename T>
inline constexpr bool is_std_array_v = is_std_array<remove_cvref_t<T>>::value;
// =========================================================================
// Char-array detection
// =========================================================================
/// @brief True if T (reference-stripped) is a C-style char array.
template <typename T> struct is_c_array_of_char : std::false_type {};
template <std::size_t N> struct is_c_array_of_char<char[N]> : std::true_type {};
template <std::size_t N> struct is_c_array_of_char<const char[N]> : std::true_type {};
template <std::size_t N> struct is_c_array_of_char<volatile char[N]> : std::true_type {};
template <std::size_t N> struct is_c_array_of_char<const volatile char[N]> : std::true_type {};
template <typename T>
inline constexpr bool is_c_array_of_char_v = is_c_array_of_char<std::remove_reference_t<T>>::value;
/// @brief True if T is std::array<char, N> (any cv-qualification).
template <typename T> struct is_std_array_char : std::false_type {};
template <std::size_t N> struct is_std_array_char<std::array<char, N>> : std::true_type {};
template <std::size_t N> struct is_std_array_char<const std::array<char, N>> : std::true_type {};
template <std::size_t N> struct is_std_array_char<volatile std::array<char, N>> : std::true_type {};
template <std::size_t N> struct is_std_array_char<const volatile std::array<char, N>> : std::true_type {};
template <typename T>
inline constexpr bool is_std_array_char_v = is_std_array_char<std::remove_reference_t<T>>::value;
} // namespace FunctionTraitsNs