1 |
|
|
#pragma once |
2 |
|
|
|
3 |
|
|
#include <concepts> |
4 |
|
|
#include <tuple> |
5 |
|
|
#include <string_view> |
6 |
|
|
|
7 |
|
|
namespace meta { |
8 |
|
|
|
9 |
|
|
#pragma region type_name |
10 |
|
|
|
11 |
|
|
template <typename T> |
12 |
|
|
constexpr auto type_name() noexcept { |
13 |
|
|
std::string_view name, prefix, suffix; |
14 |
|
|
#ifdef __clang__ |
15 |
|
|
name = __PRETTY_FUNCTION__; |
16 |
|
|
prefix = "auto type_name() [T = "; |
17 |
|
|
suffix = "]"; |
18 |
|
|
#elif defined(__GNUC__) |
19 |
|
|
name = __PRETTY_FUNCTION__; |
20 |
|
|
prefix = "constexpr auto type_name() [with T = "; |
21 |
|
|
suffix = "]"; |
22 |
|
|
#elif defined(_MSC_VER) |
23 |
|
|
name = __FUNCSIG__; |
24 |
|
|
prefix = "auto __cdecl type_name<"; |
25 |
|
|
suffix = ">(void) noexcept"; |
26 |
|
|
#endif |
27 |
|
|
name.remove_prefix(prefix.size()); |
28 |
|
|
name.remove_suffix(suffix.size()); |
29 |
|
|
return name; |
30 |
|
|
} |
31 |
|
|
|
32 |
|
|
#pragma endregion |
33 |
|
|
|
34 |
|
|
#pragma region Helper functions for CRTP |
35 |
|
|
|
36 |
|
|
template <typename T> struct crtp { |
37 |
|
160 |
T &underlying() { return static_cast<T &>(*this); } |
38 |
|
|
T const &underlying() const { return static_cast<T const &>(*this); } |
39 |
|
|
}; |
40 |
|
|
|
41 |
|
|
#pragma endregion |
42 |
|
|
|
43 |
|
|
#pragma region Concept Function, Function_object, callable |
44 |
|
|
|
45 |
|
|
// std::invoke<F,Args...> is a way to check if F is a Function or Function-like object. However |
46 |
|
|
// it requires 'Args...', which may not always be available. 'callable<F>' is a concept that identifies |
47 |
|
|
// if 'F' is a regular Function, lambda, or Function object. |
48 |
|
|
|
49 |
|
|
template<typename F> |
50 |
|
|
concept FunctionObject = requires (F) { |
51 |
|
|
&F::operator(); |
52 |
|
|
}; |
53 |
|
|
|
54 |
|
|
template<typename F> |
55 |
|
|
struct is_function_trait : public std::false_type { }; |
56 |
|
|
|
57 |
|
|
template<typename Ret, typename... Args> |
58 |
|
|
struct is_function_trait<Ret(Args...)> : public std::true_type { }; |
59 |
|
|
|
60 |
|
|
template<typename F> |
61 |
|
|
concept Function = is_function_trait<F>::value; |
62 |
|
|
|
63 |
|
|
template<typename F> |
64 |
|
|
concept Callable = Function<F> || FunctionObject<F>; |
65 |
|
|
|
66 |
|
|
#pragma endregion |
67 |
|
|
|
68 |
|
|
#pragma region Concept predicate |
69 |
|
|
|
70 |
|
|
// std::predicate<F,Args> requires both Function type and argument types. The latter is redundent. |
71 |
|
|
// predicate<F> operates solely on F and works as expected in case 'F' is a regular Function, lambda |
72 |
|
|
// or Function object |
73 |
|
|
|
74 |
|
|
template<typename F> |
75 |
|
|
struct is_predicate_trait : public std::false_type { }; |
76 |
|
|
|
77 |
|
|
template<typename... Args> |
78 |
|
|
struct is_predicate_trait<bool(Args...)> : public std::true_type {}; |
79 |
|
|
|
80 |
|
|
template<typename F, typename... Args> |
81 |
|
|
struct is_predicate_trait<bool(F::*)(Args...)> : public std::true_type { }; |
82 |
|
|
|
83 |
|
|
template<typename F, typename... Args> |
84 |
|
|
struct is_predicate_trait<bool(F::*)(Args...) const> : public std::true_type { }; |
85 |
|
|
|
86 |
|
|
template<FunctionObject F> |
87 |
|
|
struct is_predicate_trait<F> : is_predicate_trait<decltype(&F::operator())> { }; |
88 |
|
|
|
89 |
|
|
template<typename F> |
90 |
|
|
concept Predicate = is_predicate_trait<F>::value; |
91 |
|
|
|
92 |
|
|
#pragma endregion |
93 |
|
|
|
94 |
|
|
#pragma region Function signature introspection |
95 |
|
|
|
96 |
|
|
template<typename Ret, typename... Args> |
97 |
|
|
struct SignatureInterface { |
98 |
|
|
static constexpr std::size_t nargs = sizeof...(Args); |
99 |
|
|
|
100 |
|
|
using return_arg_t = Ret; |
101 |
|
|
using input_arg_t = std::tuple<Args...>; |
102 |
|
|
}; |
103 |
|
|
|
104 |
|
|
// primary template - should never be instantiated |
105 |
|
|
template<typename F> |
106 |
|
|
struct Signature : SignatureInterface<void,void> { |
107 |
|
|
}; |
108 |
|
|
|
109 |
|
|
template<typename Ret, typename... Args> |
110 |
|
|
struct Signature<Ret(Args...)> : public SignatureInterface<Ret, Args...> {}; |
111 |
|
|
|
112 |
|
|
template<typename Ret, typename F, typename... Args> |
113 |
|
|
struct Signature<Ret(F::*)(Args...)> : public SignatureInterface<Ret, Args...> {}; |
114 |
|
|
|
115 |
|
|
template<typename Ret, typename F, typename... Args> |
116 |
|
|
struct Signature<Ret(F::*)(Args...) const> : public SignatureInterface<Ret, Args...> {}; |
117 |
|
|
|
118 |
|
|
template<FunctionObject F> |
119 |
|
|
struct Signature<F> : public Signature<decltype(&F::operator())> {}; |
120 |
|
|
|
121 |
|
|
#pragma endregion |
122 |
|
|
|
123 |
|
|
} |
124 |
|
|
|
125 |
|
|
|
126 |
|
|
|
127 |
|
|
|
128 |
|
|
|
129 |
|
|
|
130 |
|
|
|
131 |
|
|
|
132 |
|
|
|
133 |
|
|
|
134 |
|
|
|
135 |
|
|
|
136 |
|
|
|
137 |
|
|
|
138 |
|
|
|
139 |
|
|
|
140 |
|
|
|
141 |
|
|
|
142 |
|
|
|
143 |
|
|
// #pragma region meta type |
144 |
|
|
// |
145 |
|
|
// // extract the N'th argument type of a parameter pack |
146 |
|
|
// template<size_t N, typename... Ts> |
147 |
|
|
// using argument_t = typename std::tuple_element<N, std::tuple<Ts...>>::type; |
148 |
|
|
// |
149 |
|
|
// // extract the first argument type of a parameter pack |
150 |
|
|
// template<typename... Ts > |
151 |
|
|
// using first_argument_t = argument_t<0, Ts...>; |
152 |
|
|
// |
153 |
|
|
// // extract the last argument type of a parameter pack |
154 |
|
|
// template<typename... Ts > |
155 |
|
|
// using last_argument_t = argument_t<sizeof...(Ts) - 1, Ts...>; |
156 |
|
|
// |
157 |
|
|
// // extract the N'th argument of a parameter pack |
158 |
|
|
// template <int I, class... Ts> |
159 |
|
|
// argument_t<I, Ts...> get(Ts&&... ts) { |
160 |
|
|
// return std::get<I>(std::forward_as_tuple(ts...)); |
161 |
|
|
// } |
162 |
|
|
// |
163 |
|
|
// // extract the first argument of a parameter pack |
164 |
|
|
// template <class... Ts> |
165 |
|
|
// first_argument_t<Ts...> first(Ts&&... ts) { |
166 |
|
|
// return std::get<0>(std::forward_as_tuple(ts...)); |
167 |
|
|
// } |
168 |
|
|
// |
169 |
|
|
// // extract the last argument of a parameter pack |
170 |
|
|
// template <class... Ts> |
171 |
|
|
// last_argument_t<Ts...> last(Ts&&... ts) { |
172 |
|
|
// return std::get<(sizeof...(Ts) - 1)>(std::forward_as_tuple(ts...)); |
173 |
|
|
// } |
174 |
|
|
// |
175 |
|
|
// #pragma endregion |