-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathdraup.hpp
228 lines (195 loc) · 5.29 KB
/
draup.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
#ifndef DRAUP_DRAUP_HPP
#define DRAUP_DRAUP_HPP
#include <cstddef>
#include <type_traits>
namespace draup {
template <class T>
struct type_c
{
using type = T;
};
namespace util {
template <class... Contents>
struct set
{
template <class T>
constexpr static bool contains();
template <class NullaryFunction>
static void for_each(NullaryFunction f) noexcept;
template <class T>
using add = set<T, Contents...>;
};
template <class Content, class... Rest>
struct set<Content, Rest...>
{
template <class NullaryFunction>
static void
for_each(NullaryFunction f) noexcept
{
f(type_c<Content>{});
set<Rest...>::for_each(f);
}
template <class T>
constexpr static bool
contains()
{
if constexpr (std::is_same_v<T, Content>) {
return true;
} else {
return set<Rest...>::template contains<T>();
}
}
template <class T>
using add = set<T, Content, Rest...>;
};
template <>
struct set<>
{
template <class NullaryFunction>
static void
for_each(NullaryFunction f) noexcept
{
(void)f;
}
template <class T>
constexpr static bool
contains()
{
return false;
}
template <class T>
using add = set<T>;
};
template <class T>
constexpr auto
make_set()
{
return set<T>{};
}
template <class T, class TheSet>
constexpr auto
set_insert()
{
if constexpr (TheSet::template contains<T>()) {
return TheSet{};
} else {
return typename TheSet::template add<T>{};
}
}
} // namespace util
template <class>
struct sfinae_true : std::true_type
{
};
/* This template will be specialized by every plugin with its own integer.
* The operator() of the respective struct should return a hana::set of all the
* classes registered so far. */
template <unsigned int>
struct registry_hook;
/* Methods to test whether a registry_hook with a specified number is already
* defined via SFINAE. */
template <class Dummy, unsigned int testN>
constexpr auto test_existence(int)
-> sfinae_true<decltype(registry_hook<testN>{})>;
template <class Dummy, unsigned int testN>
constexpr auto test_existence(long) -> std::false_type;
/* Returns the smallest number N for which registry_hook<N> is not defined yet.
* A per-plugin unique dummy class is necessary s.t. this struct is
* re-instantiated for every plugin.
*/
template <class Dummy, unsigned int testN = 0>
constexpr unsigned int
get_free_N()
{
/* The 'constexpr' is essential here. Otherwise, the template in the
* else-branch must be instantiated even if the condition evaluates to true,
* leading to infinite recursion. */
if constexpr (std::is_same<decltype(test_existence<Dummy, testN>(0)),
std::false_type>::value) {
return testN;
} else {
return get_free_N<Dummy, testN + 1>();
}
}
/* Helper struct / method to chain together the registered classes */
template <class ClassToRegister, unsigned int myN>
struct register_class
{
/* General case: myN > 0. I.e., other classes have already been registered.
* We recursively get the set of these classes and append our own. */
auto
operator()()
{
return util::set_insert<ClassToRegister,
typeof(registry_hook<myN - 1>{}())>();
}
};
template <class ClassToRegister>
struct register_class<ClassToRegister, 0>
{
/* Special case: myN == 0. No other classes have been registered. Create a new
* set. */
auto
operator()()
{
return util::make_set<ClassToRegister>();
}
};
class GetterDummyClass {
};
template <class T>
struct plugin_getter
{
auto
operator()()
{
constexpr unsigned int next_N = get_free_N<T>();
if constexpr (next_N == 0) {
return util::set<>{};
} else {
// get the set of registered classes
auto registered_classes = registry_hook<next_N - 1>{}();
return registered_classes;
}
}
};
/*
* A struct + variable template to provide a callable for_each symbol.
*/
template <class Dummy>
struct for_each
{
template <class NullaryFunction>
void
operator()(NullaryFunction f)
{
using TheSet = typeof(plugin_getter<Dummy>{}());
TheSet::for_each(f);
}
};
template <std::size_t>
struct DraupDummy
{
};
} // namespace draup
/*
* Macro to do the necessary incantation to register a new plugin
*/
#define DRAUP_REGISTER(classname) \
namespace draup { \
template <> \
struct registry_hook<draup::get_free_N<classname>()> \
{ \
constexpr static unsigned int my_N = draup::get_free_N<classname>(); \
auto \
operator()() \
{ \
return draup::register_class<classname, my_N>{}(); \
} \
}; \
}
#define DRAUP_GET_REGISTERED() \
::draup::plugin_getter<::draup::GetterDummyClass>{}()
#define DRAUP_FOR_EACH(f) \
::draup::for_each<::draup::DraupDummy<__COUNTER__>>{}(f)
#endif // DRAUP_DRAUP_HPP