-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathsybpp.hpp
296 lines (249 loc) · 9.13 KB
/
sybpp.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
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
291
292
293
294
295
296
// Copyright (c) 2021 Christopher Taylor
// Copyright (c) 2020 Christopher Taylor
//
// SPDX-License-Identifier: BSL-1.0
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#pragma once
#ifndef __SYBPP_HPP__
#define __SYBPP_HPP__
#include <boost/fusion/include/sequence.hpp>
#include <boost/fusion/include/algorithm.hpp>
#include <boost/fusion/include/vector.hpp>
#include <boost/fusion/adapted/struct/adapt_struct.hpp>
#include <boost/fusion/include/adapt_struct.hpp>
#include <boost/type_traits.hpp>
#include <type_traits>
#include <functional>
#include <algorithm>
#include <variant>
#include <iterator>
#include <optional>
#include <utility>
#include <tuple>
namespace sybpp {
// is_iterable_trait
//
template<typename T, typename=void>
struct is_iterable : std::false_type {};
template<typename T>
struct is_iterable<T,
std::void_t<
decltype(std::begin(std::declval<T>())),
decltype(std::end(std::declval<T>()))
>
> : std::true_type {};
template<typename T>
constexpr bool is_iterable_v = is_iterable<T>::value;
// is variant trait
//
template<typename T, typename = void>
struct is_variant : std::false_type {};
template<typename T>
struct is_variant<T,
std::void_t<
decltype( std::declval<T>().index() ),
decltype( std::declval<T>().valueless_by_exception() )
>
> : std::true_type {};
// template variant_for_loop
//
template<typename WhenType, typename VariantType, std::size_t N>
struct when_variant_impl {
// cache the current variant_alternative to test
//
using var_type = typename std::variant_alternative<N, VariantType>::type;
// perform the variant_alternative test
//
enum { value = std::conditional<
std::is_same< WhenType, var_type >::value,
std::integral_constant<std::size_t, N>,
std::integral_constant<std::size_t, when_variant_impl< WhenType, VariantType, N-1>::value >
>::type::value
};
};
// recursion termination condition - sets value to std::variant_npos
//
template<typename WhenType, typename VariantType>
struct when_variant_impl<WhenType, VariantType, std::variant_npos> {
enum { value = std::variant_npos };
};
// entry point for recursion
//
template<typename WhenType, typename VariantType>
struct when_variant {
enum { value = when_variant_impl<WhenType, VariantType,
std::variant_size_v<VariantType>-1>::value
};
};
// pair support
//
template<typename T>
struct is_pair : std::false_type {};
template<typename T, typename F>
struct is_pair< std::pair<T, F> > : std::true_type {};
template<typename T>
struct is_pair_d : is_pair< typename std::decay<T>::type > {};
template<typename T>
inline constexpr bool is_pair_d_v = is_pair_d<T>::value;
// tuple support
//
template<typename T>
struct is_tuple : std::false_type {};
template<typename... T>
struct is_tuple< std::tuple<T...> > : std::true_type {};
template<typename T>
struct is_tuple_d : is_tuple< typename std::decay_t<T>::type > {};
template<typename T>
inline constexpr bool is_tuple_d_v = is_tuple<T>::value;
// optional support
//
template<typename T>
struct is_optional : std::false_type {};
template<typename T>
struct is_optional< std::optional<T> > : std::true_type {};
template<typename T>
struct is_optional_d : is_optional< typename std::decay_t<T>::type > {};
template<typename T>
inline constexpr bool is_optional_d_v = is_tuple<T>::value;
// sybpp - everywhere
//
template<typename ToModify, typename ModifyFn>
struct everywhere {
using to_modify_type = ToModify;
using modify_fn = ModifyFn;
using this_type = everywhere<ToModify, ModifyFn>;
using modify_fn_ref = ModifyFn &;
// reference to function
//
modify_fn_ref fn;
// wrapper type dynamically instantiated at compile
// time. type uses constexpr to insert execution
// of the function reference `to_modify_fn_ref fn`
// when the type we want to modify matches a
// type traversed using `boost::fusion::for_each`
//
struct func {
using this_type = func;
using to_modify_type_ref = to_modify_type &;
using fn_type = std::reference_wrapper<modify_fn>;
fn_type fn;
func() = delete;
func(modify_fn_ref fn_) : fn(fn_) {}
// at compile time, this method is `auto-magically` stamped
// out by the compiler, the constexpr inside acts as a
// template specialization technique
//
template<typename InputType>
void operator()(InputType & in) {
// matches types and determines when to insert function
// reference for execution
//
if constexpr(std::is_same<InputType, ToModify>::value) {
fn(in);
}
// recursion
//
else {
// compile-time check that InputType is a variant
//
if constexpr ( is_variant<InputType>::value ) {
constexpr const std::size_t variant_index =
when_variant<ToModify, InputType>::value;
// compile-time check to make sure the variant_index
// isn't variant_npos
//
if constexpr( variant_index != std::variant_npos) {
using variant_index_type =
typename std::variant_alternative<variant_index, InputType>::type;
if constexpr( !std::is_same< std::monostate, variant_index_type >::value ) {
// if all the compile-time checks clear, insert this runtime check
// to make sure the variant is initialized at the variant_index
//
if(in.index() == variant_index) {
// original implementation
//
// fn(std::get<variant_index>(in));
//
// using this implementation to get compile-time checking
//
fn(std::get<variant_index_type>(in));
}
}
}
}
// handle container types with iterator support
//
else if constexpr(is_iterable<InputType>::value) {
std::for_each(std::begin(in), std::end(in), [&](auto && value) {
// situation where members of the container type are,
// themselves, typelists (boost::fusion type sequences)
//
if constexpr(boost::fusion::traits::is_sequence<decltype(value)>::value) {
boost::fusion::for_each(value, (*this));
}
// situation where elements are integral types,
// string types, or struct/class types
//
else {
this_type fwdfn(fn);
fwdfn(value);
}
});
}
// optional support
//
else if constexpr(is_optional<std::decay_t<decltype(in)>>::value) {
if(in) {
this_type fwdfn(fn);
fwdfn(*in);
}
}
// pair support
//
else if constexpr( is_pair<std::decay_t<decltype(in)>>::value) {
this_type fwdfn(fn);
fwdfn(in.first);
fwdfn(in.second);
}
// tuple support
//
else if constexpr(is_tuple< std::decay_t<decltype(in)>>::value) {
this_type fwdfn(fn);
// C++17 fold expression
//
std::apply([&fwdfn](auto&&... args) { (fwdfn(args), ... ); }, in);
}
// compile-time check that InputType isn't an integral type
// or a possible std::(w)string type
//
else if constexpr( !std::is_same<InputType, std::string>::value &&
!std::is_same<InputType, std::wstring>::value &&
!std::is_integral<InputType>::value &&
!is_iterable<InputType>::value &&
!is_variant<InputType>::value ) {
boost::fusion::for_each(in, (*this));
}
}
}
};
everywhere(ModifyFn & fn_) : fn(fn_) {
}
template<typename T>
void operator()(T & in) {
if constexpr( std::is_same<T, to_modify_type>::value ) {
fn(in);
}
else {
func fwdfn(fn);
boost::fusion::for_each(in, fwdfn);
}
}
};
template<typename Transform, typename T>
void apply(Transform t, T & input) {
t(input);
}
} // end namespace sybpp
#endif