1
+ //
2
+ // Copyright (c) 2014-2016 Pavel Medvedev. All rights reserved.
3
+ //
4
+ // Distributed under the MIT software license, see the accompanying
5
+ // file LICENSE
6
+
7
+ #ifndef FIXED_SIZE_FUNCTION_HPP_INCLUDED
8
+ #define FIXED_SIZE_FUNCTION_HPP_INCLUDED
9
+
10
+ #include < functional>
11
+ #include < stdexcept>
12
+ #include < tuple>
13
+ #include < type_traits>
14
+
15
+ enum class construct_type {
16
+ none,
17
+ copy,
18
+ move,
19
+ copy_and_move,
20
+ };
21
+
22
+ namespace details {
23
+
24
+ // V-table implementation
25
+ template <typename Ret, typename ... Args>
26
+ struct fixed_function_vtable_base {
27
+ Ret (*call)(void *, Args&&...) = nullptr ;
28
+ void (*destroy)(void *) = nullptr ;
29
+ };
30
+
31
+ template <construct_type ConstructStrategy, typename Ret, typename ... Args>
32
+ struct fixed_function_vtable ;
33
+
34
+ template <typename Ret, typename ... Args>
35
+ struct fixed_function_vtable <construct_type::none, Ret, Args...>
36
+ : fixed_function_vtable_base<Ret, Args...> {};
37
+
38
+ template <typename Ret, typename ... Args>
39
+ struct fixed_function_vtable <construct_type::copy, Ret, Args...>
40
+ : fixed_function_vtable_base<Ret, Args...> {
41
+ void (*copy)(const void *, void *) = nullptr ;
42
+ };
43
+
44
+ template <typename Ret, typename ... Args>
45
+ struct fixed_function_vtable <construct_type::move, Ret, Args...>
46
+ : fixed_function_vtable_base<Ret, Args...> {
47
+ void (*move)(void *, void *) = nullptr ;
48
+ };
49
+
50
+ template <typename Ret, typename ... Args>
51
+ struct fixed_function_vtable <construct_type::copy_and_move, Ret, Args...>
52
+ : fixed_function_vtable_base<Ret, Args...> {
53
+ void (*copy)(const void *, void *) = nullptr ;
54
+ void (*move)(void *, void *) = nullptr ;
55
+ };
56
+
57
+ } // namespace details
58
+
59
+ template <typename Function,
60
+ size_t MaxSize = 128 ,
61
+ construct_type ConstructStrategy = construct_type::copy_and_move>
62
+ class fixed_size_function ;
63
+
64
+ template <typename Ret,
65
+ typename ... Args,
66
+ size_t MaxSize,
67
+ construct_type ConstructStrategy>
68
+ class fixed_size_function <Ret(Args...), MaxSize, ConstructStrategy> {
69
+ public:
70
+ // Compile-time information
71
+
72
+ using is_copyable =
73
+ std::integral_constant<bool ,
74
+ ConstructStrategy == construct_type::copy ||
75
+ ConstructStrategy ==
76
+ construct_type::copy_and_move>;
77
+ using is_movable =
78
+ std::integral_constant<bool ,
79
+ ConstructStrategy == construct_type::move ||
80
+ ConstructStrategy ==
81
+ construct_type::copy_and_move>;
82
+
83
+ using result_type = Ret;
84
+
85
+ static const std::size_t arity = sizeof ...(Args);
86
+
87
+ template <std::size_t N>
88
+ struct argument {
89
+ static_assert (N < arity, " invalid argument index" );
90
+ using type = typename std::tuple_element<N, std::tuple<Args...>>::type;
91
+ };
92
+
93
+ public:
94
+ template <typename F, size_t S, construct_type C>
95
+ fixed_size_function (fixed_size_function<F, S, C> const &) = delete ;
96
+ template <typename F, size_t S, construct_type C>
97
+ fixed_size_function (fixed_size_function<F, S, C>&) = delete ;
98
+ template <typename F, size_t S, construct_type C>
99
+ fixed_size_function (fixed_size_function<F, S, C>&&) = delete ;
100
+ template <typename F, size_t S, construct_type C>
101
+ fixed_size_function& operator =(fixed_size_function<F, S, C> const &) = delete ;
102
+ template <typename F, size_t S, construct_type C>
103
+ fixed_size_function& operator =(fixed_size_function<F, S, C>&) = delete ;
104
+ template <typename F, size_t S, construct_type C>
105
+ fixed_size_function& operator =(fixed_size_function<F, S, C>&&) = delete ;
106
+ template <typename F, size_t S, construct_type C>
107
+ void assign (fixed_size_function<F, S, C> const &) = delete;
108
+ template <typename F, size_t S, construct_type C>
109
+ void assign (fixed_size_function<F, S, C>&) = delete;
110
+ template <typename F, size_t S, construct_type C>
111
+ void assign (fixed_size_function<F, S, C>&&) = delete;
112
+
113
+ fixed_size_function () {}
114
+
115
+ ~fixed_size_function () { reset (); }
116
+
117
+ fixed_size_function (std::nullptr_t ) {}
118
+
119
+ fixed_size_function& operator =(std::nullptr_t ) {
120
+ reset ();
121
+ return *this ;
122
+ }
123
+
124
+ fixed_size_function (fixed_size_function const & src) { copy (src); }
125
+
126
+ fixed_size_function& operator =(fixed_size_function const & src) {
127
+ assign (src);
128
+ return *this ;
129
+ }
130
+
131
+ fixed_size_function (fixed_size_function& src) { copy (src); }
132
+
133
+ fixed_size_function& operator =(fixed_size_function& src) {
134
+ assign (src);
135
+ return *this ;
136
+ }
137
+
138
+ fixed_size_function (fixed_size_function&& src) {
139
+ move (std::move (src), is_movable ());
140
+ }
141
+
142
+ fixed_size_function& operator =(fixed_size_function&& src) {
143
+ assign (std::move (src));
144
+ return *this ;
145
+ }
146
+
147
+ template <typename Functor>
148
+ fixed_size_function (Functor&& f) {
149
+ create (std::forward<Functor>(f));
150
+ }
151
+
152
+ template <typename Functor>
153
+ fixed_size_function& operator =(Functor&& f) {
154
+ assign (std::forward<Functor>(f));
155
+ return *this ;
156
+ }
157
+
158
+ void assign (fixed_size_function const & src) {
159
+ reset ();
160
+ copy (src);
161
+ }
162
+
163
+ void assign (fixed_size_function& src) {
164
+ reset ();
165
+ copy (src);
166
+ }
167
+
168
+ void assign (fixed_size_function&& src) {
169
+ reset ();
170
+ move (std::move (src), is_movable ());
171
+ }
172
+
173
+ template <typename Functor>
174
+ void assign (Functor&& f) {
175
+ reset ();
176
+ create (std::forward<Functor>(f));
177
+ }
178
+
179
+ void reset () {
180
+ auto destroy = vtable_.destroy ;
181
+ if (destroy) {
182
+ vtable_ = vtable ();
183
+ destroy (&storage_);
184
+ }
185
+ }
186
+
187
+ explicit operator bool () const { return vtable_.call != nullptr ; }
188
+
189
+ Ret operator ()(Args... args) {
190
+ return vtable_.call ? vtable_.call (&storage_, std::forward<Args>(args)...)
191
+ : (Ret) nullptr ;
192
+ }
193
+
194
+ void swap (fixed_size_function& other) {
195
+ fixed_size_function tmp = std::move (other);
196
+ other = std::move (*this );
197
+ *this = std::move (tmp);
198
+ }
199
+
200
+ friend void swap (fixed_size_function& lhs, fixed_size_function& rhs) {
201
+ lhs.swap (rhs);
202
+ }
203
+
204
+ friend bool operator ==(std::nullptr_t , fixed_size_function const & f) {
205
+ return !f;
206
+ }
207
+
208
+ friend bool operator ==(fixed_size_function const & f, std::nullptr_t ) {
209
+ return !f;
210
+ }
211
+
212
+ friend bool operator !=(std::nullptr_t , fixed_size_function const & f) {
213
+ return f;
214
+ }
215
+
216
+ friend bool operator !=(fixed_size_function const & f, std::nullptr_t ) {
217
+ return f;
218
+ }
219
+
220
+ private:
221
+ template <typename Functor>
222
+ void create (Functor&& f) {
223
+ using functor_type = typename std::decay<Functor>::type;
224
+ static_assert (sizeof (functor_type) <= StorageSize,
225
+ " Functor must be smaller than storage buffer" );
226
+
227
+ new (&storage_) functor_type (std::forward<Functor>(f));
228
+
229
+ vtable_.call = &call_impl<functor_type>;
230
+ vtable_.destroy = &destroy_impl<functor_type>;
231
+ init_copy<functor_type>(is_copyable ());
232
+ init_move<functor_type>(is_movable ());
233
+ }
234
+
235
+ void copy (fixed_size_function const & src) {
236
+ if (src.vtable_ .copy ) {
237
+ src.vtable_ .copy (&src.storage_ , &storage_);
238
+ vtable_ = src.vtable_ ;
239
+ }
240
+ }
241
+
242
+ void move (fixed_size_function&& src, std::true_type movable) {
243
+ if (src.vtable_ .move ) {
244
+ src.vtable_ .move (&src.storage_ , &storage_);
245
+ vtable_ = src.vtable_ ;
246
+ src.reset ();
247
+ }
248
+ }
249
+
250
+ void move (fixed_size_function const & src, std::false_type movable) {
251
+ copy (src);
252
+ }
253
+
254
+ private:
255
+ template <typename Functor>
256
+ static Ret call_impl (void * functor, Args&&... args) {
257
+ return (*static_cast <Functor*>(functor))(std::forward<Args>(args)...);
258
+ }
259
+
260
+ template <typename Functor>
261
+ static void destroy_impl (void * functor) {
262
+ static_cast <Functor*>(functor)->~Functor ();
263
+ }
264
+
265
+ template <typename Functor>
266
+ static void copy_impl (void const * functor, void * dest) {
267
+ new (dest) Functor (*static_cast <Functor const *>(functor));
268
+ }
269
+
270
+ template <typename Functor>
271
+ static void move_impl (void * functor, void * dest) {
272
+ new (dest) Functor (std::move (*static_cast <Functor*>(functor)));
273
+ }
274
+
275
+ template <typename Functor>
276
+ void init_copy (std::true_type /* copyable*/ ) {
277
+ vtable_.copy = ©_impl<Functor>;
278
+ }
279
+
280
+ template <typename Functor>
281
+ void init_copy (std::false_type /* copyable*/ ) {}
282
+
283
+ template <typename Functor>
284
+ void init_move (std::true_type /* movable*/ ) {
285
+ vtable_.move = &move_impl<Functor>;
286
+ }
287
+
288
+ template <typename Functor>
289
+ void init_move (std::false_type /* movable*/ ) {}
290
+
291
+ private:
292
+ using vtable =
293
+ details::fixed_function_vtable<ConstructStrategy, Ret, Args...>;
294
+ static const size_t StorageSize = MaxSize - sizeof (vtable);
295
+ using storage = typename std::aligned_storage<StorageSize>::type;
296
+
297
+ vtable vtable_;
298
+ storage storage_;
299
+ };
300
+
301
+ #endif // FIXED_SIZE_FUNCTION_HPP_INCLUDED
0 commit comments