]> de.git.xonotic.org Git - xonotic/netradiant.git/blob - libs/generic/callback.h
Merge branch 'NateEag-master-patch-12920' into 'master'
[xonotic/netradiant.git] / libs / generic / callback.h
1 /*
2    Copyright (C) 2001-2006, William Joseph.
3    All Rights Reserved.
4
5    This file is part of GtkRadiant.
6
7    GtkRadiant is free software; you can redistribute it and/or modify
8    it under the terms of the GNU General Public License as published by
9    the Free Software Foundation; either version 2 of the License, or
10    (at your option) any later version.
11
12    GtkRadiant is distributed in the hope that it will be useful,
13    but WITHOUT ANY WARRANTY; without even the implied warranty of
14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15    GNU General Public License for more details.
16
17    You should have received a copy of the GNU General Public License
18    along with GtkRadiant; if not, write to the Free Software
19    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
20  */
21
22 #if !defined( INCLUDED_GENERIC_CLOSURE_H )
23 #define INCLUDED_GENERIC_CLOSURE_H
24
25 /// \file
26 /// \brief Type-safe techniques for binding the first argument of an opaque callback.
27
28 #include <cstddef>
29 #include "functional.h"
30
31 namespace detail {
32
33         template<typename Thunk_>
34         class CallbackBase {
35                 void *m_environment;
36                 Thunk_ m_thunk;
37         public:
38                 typedef Thunk_ Thunk;
39
40                 CallbackBase(void *environment, Thunk function) : m_environment(environment), m_thunk(function) {
41                 }
42
43                 void *getEnvironment() const {
44                         return m_environment;
45                 }
46
47                 Thunk getThunk() const {
48                         return m_thunk;
49                 }
50         };
51
52         template<typename Thunk>
53         inline bool operator==(const CallbackBase<Thunk> &self, const CallbackBase<Thunk> &other) {
54                 return self.getEnvironment() == other.getEnvironment() && self.getThunk() == other.getThunk();
55         }
56
57         template<typename Thunk>
58         inline bool operator!=(const CallbackBase<Thunk> &self, const CallbackBase<Thunk> &other) {
59                 return !(self == other);
60         }
61
62         template<typename Thunk>
63         inline bool operator<(const CallbackBase<Thunk> &self, const CallbackBase<Thunk> &other) {
64                 return self.getEnvironment() < other.getEnvironment() ||
65                            (!(other.getEnvironment() < self.getEnvironment()) && self.getThunk() < other.getThunk());
66         }
67
68 }
69
70 namespace detail {
71
72         template<class Type>
73         struct ConvertFromOpaque {
74         };
75
76         // reference
77
78         template<class T>
79         inline const void *convertToOpaque(const T &t) {
80                 return &t;
81         }
82
83         template<class T>
84         struct ConvertFromOpaque<const T &> {
85                 static T const &apply(void *p) {
86                         return *static_cast<const T *>(p);
87                 }
88         };
89
90         template<class T>
91         inline void *convertToOpaque(T &t) {
92                 return &t;
93         }
94
95         template<class T>
96         struct ConvertFromOpaque<T &> {
97                 static T &apply(void *p) {
98                         return *static_cast<T *>( p );
99                 }
100         };
101
102         // pointer
103
104         template<class T, class U = typename std::enable_if<!std::is_function<T>::value>::type>
105         inline const void *convertToOpaque(const T *t) {
106                 return t;
107         }
108
109         template<class T>
110         struct ConvertFromOpaque<const T *> {
111                 static const T *apply(void *p) {
112                         return static_cast<const T *>(p);
113                 }
114         };
115
116         template<class T, class U = typename std::enable_if<!std::is_function<T>::value>::type>
117         inline void *convertToOpaque(T *t) {
118                 return t;
119         }
120
121         template<class T>
122         struct ConvertFromOpaque<T *> {
123                 static T *apply(void *p) {
124                         return static_cast<T *>(p);
125                 }
126         };
127
128         // function pointer
129
130         template<class R, class... Ts>
131         inline const void *convertToOpaque(R(*const &t)(Ts...)) {
132                 return &t;
133         }
134
135         template<class R, class... Ts>
136         struct ConvertFromOpaque<R(*const &)(Ts...)> {
137                 using Type = R(*)(Ts...);
138
139                 static Type const &apply(void *p) {
140                         return *static_cast<Type *>(p);
141                 }
142         };
143
144     template<class R, class... Ts>
145     inline void *convertToOpaque(R(*&t)(Ts...)) {
146         return &t;
147     }
148
149     template<class R, class... Ts>
150     struct ConvertFromOpaque<R(*&)(Ts...)> {
151         using Type = R(*)(Ts...);
152
153         static Type &apply(void *p) {
154             return *static_cast<Type *>(p);
155         }
156     };
157
158         template<class Caller, class F>
159         class BindFirstOpaqueN;
160
161         template<class Caller, class R, class FirstBound, class... Ts>
162         class BindFirstOpaqueN<Caller, R(FirstBound, Ts...)> {
163                 FirstBound firstBound;
164         public:
165                 explicit BindFirstOpaqueN(FirstBound firstBound) : firstBound(firstBound) {
166                 }
167
168                 R operator()(Ts... args) const {
169                         return Caller::call(firstBound, args...);
170                 }
171
172                 FirstBound getBound() const {
173                         return firstBound;
174                 }
175
176                 static R thunk(void *environment, Ts... args) {
177                         return thunk_(detail::ConvertFromOpaque<FirstBound>::apply(environment), args...);
178                 }
179
180                 static R thunk_(FirstBound environment, Ts... args) {
181                         return Caller::call(environment, args...);
182                 }
183
184                 void *getEnvironment() const {
185                         return const_cast<void *>(detail::convertToOpaque(firstBound));
186                 }
187         };
188
189 }
190
191 template<class Caller>
192 using BindFirstOpaque = detail::BindFirstOpaqueN<Caller, get_func<Caller>>;
193
194 /// \brief Combines a void pointer with a pointer to a function which operates on a void pointer.
195 ///
196 /// Use with the callback constructors MemberCaller0, ConstMemberCaller0, ReferenceCaller0, ConstReferenceCaller0, PointerCaller0, ConstPointerCaller0 and FreeCaller0.
197 template<class F>
198 class Callback;
199
200 template<class R, class... Ts>
201 class Callback<R(Ts...)> : public detail::CallbackBase<R(*)(void *, Ts...)> {
202         using Base = detail::CallbackBase<R (*)(void *, Ts...)>;
203
204         static R nullThunk(void *, Ts...) {
205         }
206
207 public:
208         using func = R(Ts...);
209
210         Callback() : Base(0, nullThunk) {
211         }
212
213         template<typename Caller>
214         Callback(const BindFirstOpaque<Caller> &caller) : Base(caller.getEnvironment(), BindFirstOpaque<Caller>::thunk) {
215         }
216
217         Callback(void *environment, typename Base::Thunk function) : Base(environment, function) {
218         }
219
220         R operator()(Ts... args) const {
221                 return Base::getThunk()(Base::getEnvironment(), args...);
222         }
223 };
224
225 namespace detail {
226         template<class F>
227         struct Arglist;
228
229         template<class R, class Head, class... Ts>
230         struct Arglist<R(Head, Ts...)> {
231                 using type = R(Head, Ts...);
232
233                 template <class Unshift>
234                 using unshift = Arglist<R(Unshift, Head, Ts...)>;
235
236                 using shift = Arglist<R(Ts...)>;
237         };
238
239         template<class R, class... Ts>
240         struct Arglist<R(Ts...)> {
241                 using type = R(Ts...);
242
243                 template <class Unshift>
244                 using unshift = Arglist<R(Unshift, Ts...)>;
245         };
246
247         template<class F>
248         using ArgShift = typename detail::Arglist<F>::shift::type;
249
250         template<class F, class T>
251         using ArgUnshift = typename detail::Arglist<F>::template unshift<T>::type;
252 }
253
254 template<typename Caller>
255 inline Callback<detail::ArgShift<get_func<Caller>>> makeCallback(const Caller &caller, get_argument<Caller, 0> callee) {
256         return BindFirstOpaque<Caller>(callee);
257 }
258
259 template<class Caller, class F>
260 class CallerShiftFirst;
261
262 template<class Caller, class R, class FirstArgument, class... Ts>
263 class CallerShiftFirst<Caller, R(FirstArgument, Ts...)> {
264 public:
265         using func = R(FirstArgument, Ts...);
266
267         static R call(FirstArgument, Ts... args) {
268                 return Caller::call(args...);
269         }
270 };
271
272 template<typename Caller>
273 inline Callback<get_func<Caller>> makeStatelessCallback(const Caller &caller) {
274         return makeCallback(CallerShiftFirst<Caller, detail::ArgUnshift<get_func<Caller>, void *>>(), nullptr);
275 }
276
277 /// \brief Forms a Callback from a non-const Environment reference and a non-const Environment member-function.
278 template<class Environment, class F, MemberFunction<Environment, F> member>
279 using MemberCaller = BindFirstOpaque<Member<Environment, F, member>>;
280
281 /// \brief  Constructs a Callback1 from a non-const \p functor
282 ///
283 /// \param Functor Must define \c first_argument_type and \c operator()(first_argument_type).
284 template<typename Functor>
285 inline Callback<get_func<Functor>> makeCallback(Functor &functor) {
286         return MemberCaller<Functor, get_func<Functor>, &Functor::operator()>(functor);
287 }
288
289 /// \brief Forms a Callback from a const Environment reference and a const Environment member-function.
290 template<class Environment, class F, ConstMemberFunction<Environment, F> member>
291 using ConstMemberCaller = BindFirstOpaque<ConstMember<Environment, F, member>>;
292
293 /// \brief  Constructs a Callback1 from a const \p functor
294 ///
295 /// \param Functor Must define \c first_argument_type and const \c operator()(first_argument_type).
296 template<typename Functor>
297 inline Callback<get_func<Functor>> makeCallback(const Functor &functor) {
298         return ConstMemberCaller<Functor, get_func<Functor>, &Functor::operator()>(functor);
299 }
300
301 /// \brief Forms a Callback from a non-const Environment reference and a free function which operates on a non-const Environment reference.
302 template<class Environment, class F, detail::ArgUnshift<F, Environment &> *func>
303 using ReferenceCaller = BindFirstOpaque<Function<detail::ArgUnshift<F, Environment &>, func>>;
304
305 /// \brief Forms a Callback from a const Environment reference and a free function which operates on a const Environment reference.
306 template<class Environment, class F, detail::ArgUnshift<F, const Environment &> *func>
307 using ConstReferenceCaller = BindFirstOpaque<Function<detail::ArgUnshift<F, const Environment &>, func>>;
308
309 /// \brief Forms a Callback from a non-const Environment pointer and a free function which operates on a non-const Environment pointer.
310 template<class Environment, class F, detail::ArgUnshift<F, Environment *> *func>
311 using PointerCaller = BindFirstOpaque<Function<detail::ArgUnshift<F, Environment *>, func>>;
312
313 /// \brief Forms a Callback from a const Environment pointer and a free function which operates on a const Environment pointer.
314 template<class Environment, class F, detail::ArgUnshift<F, const Environment *> *func>
315 using ConstPointerCaller = BindFirstOpaque<Function<detail::ArgUnshift<F, const Environment *>, func>>;
316
317 namespace detail {
318         template<class Caller, class F>
319         class FreeCaller : public BindFirstOpaque<CallerShiftFirst<Caller, detail::ArgUnshift<F, void *>>> {
320         public:
321                 FreeCaller() : BindFirstOpaque<CallerShiftFirst<Caller, detail::ArgUnshift<F, void *>>>(nullptr) {
322                 }
323         };
324
325         template<class F>
326         struct FreeCallerWrapper;
327
328         template<class R, class... Ts>
329         struct FreeCallerWrapper<R(Ts...)> {
330                 using func = R(void *, Ts...);
331
332                 static R call(void *f, Ts... args) {
333                         // ideally, we'd get the implementation of the function type directly. Instead, it's passed in
334                         return reinterpret_cast<R(*)(Ts...)>(f)(args...);
335                 }
336         };
337 }
338
339 /// \brief Forms a Callback from a free function
340 template<class F, F *func>
341 using FreeCaller = detail::FreeCaller<Function<F, func>, F>;
342
343 template<class R, class... Ts>
344 inline Callback<R(Ts...)> makeCallbackF(R(*func)(Ts...)) {
345     void *pVoid = reinterpret_cast<void *>(func);
346     return BindFirstOpaque<detail::FreeCallerWrapper<R(Ts...)>>(pVoid);
347 }
348
349 #endif