-
Notifications
You must be signed in to change notification settings - Fork 38
/
FunInfo.hpp
257 lines (216 loc) · 9.87 KB
/
FunInfo.hpp
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
/*
* This file is part of Empirical, https://github.com/devosoft/Empirical
* Copyright (C) Michigan State University, MIT Software license; see doc/LICENSE.md
* date: 2021
*/
/**
* @file
* @brief Wrap a function to provide more information about it.
* @note Status: ALPHA
*
* FunInfo will collect information about a provided function and facilitate
* manipulations.
*
*
* Developer Notes:
* - Will not currently handle return by reference!
*/
#ifndef EMP_META_FUNINFO_HPP_INCLUDE
#define EMP_META_FUNINFO_HPP_INCLUDE
#include <functional>
#include "TypePack.hpp"
#include "ValPack.hpp"
namespace emp {
// A generic base class that expands anything with operator()
template <typename T>
struct FunInfo : public FunInfo< decltype(&T::operator()) > {};
// Specialization for functions; redirect to function-object specialization.
template <typename RETURN_T, typename... PARAM_Ts>
struct FunInfo <RETURN_T(PARAM_Ts...)>
: public FunInfo< std::function<RETURN_T(PARAM_Ts...)> > {};
// Specialization for functions; redirect to function-object specialization.
template <typename RETURN_T, typename... PARAM_Ts>
struct FunInfo <RETURN_T(*)(PARAM_Ts...)>
: public FunInfo< std::function<RETURN_T(PARAM_Ts...)> > {};
// Specialization for function objects with AT LEAST ONE parameter...
template <typename CLASS_T, typename RETURN_T, typename PARAM1_T, typename... PARAM_Ts>
struct FunInfo <RETURN_T(CLASS_T::*)(PARAM1_T, PARAM_Ts...) const> {
private:
// template <typename T> struct is_templated_converter : std::false_type{};
// template <typename T>
// struct is_templated_converter<emp::decoy_t<T, decltype(std::declval<T&>().template operator()<int>(0))>> : std::true_type{};
/// Helper function to lock an argument at a designated position in a function.
template <typename BOUND_T, typename... BEFORE_Ts, typename... AFTER_Ts>
static auto BindAt_impl(CLASS_T fun, BOUND_T && bound,
TypePack<BEFORE_Ts...>, TypePack<AFTER_Ts...>) {
// If the function needs a reference for the parameter, send the supplied value through.
if constexpr (std::is_reference<PARAM1_T>()) {
return [fun, &bound](BEFORE_Ts &&... before_args, AFTER_Ts &&... after_args) {
return fun(std::forward<BEFORE_Ts>(before_args)...,
std::forward<BOUND_T>(bound),
std::forward<AFTER_Ts>(after_args)...);
};
}
// Otherwise, a copy is fine.
else {
return [fun, bound](BEFORE_Ts &&... before_args, AFTER_Ts &&... after_args) {
return fun(std::forward<BEFORE_Ts>(before_args)...,
bound,
std::forward<AFTER_Ts>(after_args)...);
};
}
}
public:
using fun_t = RETURN_T(PARAM1_T, PARAM_Ts...);
using return_t = RETURN_T;
using params_t = TypePack<PARAM1_T, PARAM_Ts...>;
template <size_t ID>
using arg_t = typename params_t::template get<ID>;
static constexpr size_t num_args = 1 + sizeof...(PARAM_Ts);
/// Test if this function can be called with a particular set of arguments.
template <typename ARG1, typename... ARG_Ts>
static constexpr bool InvocableWith(ARG1 &&, ARG_Ts &&...) {
return std::is_invocable<CLASS_T, ARG1, ARG_Ts...>();
}
/// Test if this function can be called with a particular set of argument TYPEs.
template <typename... ARG_Ts>
static constexpr bool InvocableWith() {
return std::is_invocable<CLASS_T, ARG_Ts...>();
}
/// Change a function's return type using a converter function.
template <typename FUN_T, typename CONVERTER_T>
static auto ChangeReturnType(FUN_T fun, CONVERTER_T convert_fun)
{
return [fun=fun, c=convert_fun](PARAM1_T && arg1, PARAM_Ts &&... args) {
return c( fun(std::forward<PARAM1_T>(arg1), std::forward<PARAM_Ts>(args)...) );
};
}
/// Change a function's arguments using a fixed converter function.
template <typename NEW_T, typename FUN_T, typename CONVERTER_T>
static auto ChangeParameterTypes(FUN_T fun, CONVERTER_T convert_fun)
{
return [fun=fun, c=convert_fun](NEW_T arg1, decoy_t<NEW_T, PARAM_Ts>... args) {
return fun(c(arg1), c(args)...);
};
}
/// Convert a function's arguments using a dynamic (tempalted) lambda function.
template <typename NEW_T, typename FUN_T, typename CONVERTER_T>
static auto ConvertParameterTypes(FUN_T fun, CONVERTER_T convert_lambda)
{
// If the converter can take two arguments, assume the second is for type.
if constexpr ( std::is_invocable<CONVERTER_T, NEW_T, PARAM1_T>()) {
return [fun=fun, c=convert_lambda](NEW_T arg1, decoy_t<NEW_T, PARAM_Ts>... args) {
return fun(c(arg1, std::decay_t<PARAM1_T>{}),
c(args, std::decay_t<PARAM_Ts>{})...);
};
}
// Otherwise assume that we are using a templated lambda (or similar object)
else {
return [fun=fun, c=convert_lambda](NEW_T arg1, decoy_t<NEW_T, PARAM_Ts>... args) {
return fun(c.template operator()<PARAM1_T>(arg1),
c.template operator()<PARAM_Ts>(args)...);
};
}
}
/// Lock in a specified argument of a function.
template <size_t ID, typename T>
static auto BindAt(CLASS_T fun, T && bound) {
using before_pack = typename params_t::template shrink<ID>;
using after_pack = typename params_t::template popN<ID+1>;
return BindAt_impl(fun, std::forward<T>(bound), before_pack(), after_pack());
}
/// Lock in multiple function arguments.
template <size_t ID1, size_t... IDs, typename T1, typename... Ts>
static auto Bind(CLASS_T fun, T1 && bound1, Ts &&... bound) {
static_assert(emp::ValPack<ID1,IDs...>::IsSorted() && emp::ValPack<ID1,IDs...>::IsUnique(),
"FunInfo::Bind must be given unique, sorted indicies.");
static_assert(sizeof...(IDs) == sizeof...(Ts),
"FunInfo::Bind must have exactly one ID per bound value.");
// Bind all LATER positions first, if there are any.
if constexpr (sizeof...(IDs) > 0) {
auto new_fun = Bind<IDs...>(fun, std::forward<Ts>(bound)...);
return FunInfo<decltype(new_fun)>::template BindAt<ID1>(new_fun, bound1);
}
// Otherwise just bind THIS position.
else return FunInfo<decltype(fun)>::template BindAt<ID1>(fun, bound1);
}
};
// Specialization for function objects with NO parameters...
template <typename CLASS_T, typename RETURN_T>
struct FunInfo <RETURN_T(CLASS_T::*)() const>
{
using fun_t = RETURN_T();
using return_t = RETURN_T;
using params_t = TypePack<>;
static constexpr size_t num_args = 0;
/// Test if this function can be called with a particular set of arguments.
template <typename... ARG_Ts>
static constexpr bool InvocableWith(ARG_Ts...) { return sizeof...(ARG_Ts) == 0; }
/// Change a function's return type using a converter function.
template <typename FUN_T, typename CONVERTER_T>
static auto ChangeReturnType(FUN_T fun, CONVERTER_T convert_fun)
{
return [fun=fun, c=convert_fun]() {
return c(fun());
};
}
/// Change a function's arguments using a converter function.
template <typename /*NEW_T*/, typename FUN_T, typename CONVERTER_T>
static auto ChangeParameterTypes(FUN_T fun, CONVERTER_T /*convert_fun*/)
{
// No parameters, so no changes to make.
return fun;
}
/// Convert a function's arguments using a dynamic (tempalted) lambda function.
template <typename NEW_T, typename FUN_T, typename CONVERTER_T>
static auto ConvertParameterTypes(FUN_T fun, CONVERTER_T /*convert_lambda*/)
{
// No parameters, so no conversions to make.
return fun;
}
};
// === Stand-alone helper functions ===
/// Change a function's return type using a converter function.
template <typename FUN_T, typename CONVERTER_T>
static auto ChangeReturnType(FUN_T fun, CONVERTER_T convert_fun)
{
return FunInfo<FUN_T>::ChangeReturnType(fun, convert_fun);
}
/// Change a function's arguments using a simple converter function.
template <typename NEW_T, typename FUN_T, typename CONVERTER_T>
static auto ChangeParameterTypes(FUN_T fun, CONVERTER_T convert_fun)
{
return FunInfo<FUN_T>::template ChangeParameterTypes<NEW_T>(fun, convert_fun);
}
/// Convert a function's arguments using a templated lambda.
/// @note: Will not work until C++20!!
template <typename NEW_T, typename FUN_T, typename CONVERTER_T>
static auto ConvertParameterTypes(FUN_T fun, CONVERTER_T convert_fun)
{
return FunInfo<FUN_T>::template ConvertParameterTypes<NEW_T>(fun, convert_fun);
}
/// Convert both return type AND parameter type.
/// Convert a function's arguments using a templated lambda.
template <typename NEW_T, typename FUN_T, typename R_CONVERTER_T, typename P_CONVERTER_T>
static auto ChangeTypes(FUN_T fun, R_CONVERTER_T ret_convert_fun, P_CONVERTER_T param_convert_fun)
{
auto partial = FunInfo<FUN_T>::template ChangeParameterTypes<NEW_T>(fun, param_convert_fun);
return FunInfo<decltype(partial)>::ChangeReturnType(partial, ret_convert_fun);
}
/// Lock in a specified argument of a function.
template <size_t POS, typename FUN_T, typename BOUND_T>
auto BindAt(FUN_T fun, BOUND_T && bound) {
return FunInfo<FUN_T>::template BindAt<POS>(fun, std::forward<BOUND_T>(bound));
}
/// Lock in the first argument of a function.
template <typename FUN_T, typename BOUND_T>
auto BindFirst(FUN_T fun, BOUND_T && bound) {
return FunInfo<FUN_T>::template BindAt<0>(fun, std::forward<BOUND_T>(bound));
}
/// Lock in a series of specified arguments to a function.
template <size_t... IDs, typename FUN_T, typename... Ts>
auto Bind(FUN_T fun, Ts &&... bound) {
return FunInfo<FUN_T>::template Bind<IDs...>(fun, std::forward<Ts>(bound)...);
}
}
#endif // #ifndef EMP_META_FUNINFO_HPP_INCLUDE