]> de.git.xonotic.org Git - xonotic/netradiant.git/blob - libs/generic/callback.h
26772880385d04336583bf1d09ef281b32d6f35c
[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<typename Type>
73         inline void *convertToOpaque(Type *t) {
74                 return t;
75         }
76
77         template<typename Type>
78         inline void *convertToOpaque(const Type *t) {
79                 return const_cast<Type *>( t );
80         }
81
82         template<typename Type>
83         inline void *convertToOpaque(Type &t) {
84                 return &t;
85         }
86
87         template<typename Type>
88         inline void *convertToOpaque(const Type &t) {
89                 return const_cast<Type *>( &t );
90         }
91
92
93         template<typename Type>
94         class ConvertFromOpaque {
95         };
96
97         template<typename Type>
98         class ConvertFromOpaque<Type &> {
99         public:
100                 static Type &apply(void *p) {
101                         return *static_cast<Type *>( p );
102                 }
103         };
104
105         template<typename Type>
106         class ConvertFromOpaque<const Type &> {
107         public:
108                 static const Type &apply(void *p) {
109                         return *static_cast<Type *>( p );
110                 }
111         };
112
113
114         template<typename Type>
115         class ConvertFromOpaque<Type *> {
116         public:
117                 static Type *apply(void *p) {
118                         // illegal cast
119                         return reinterpret_cast<Type *>( p );
120                 }
121         };
122
123         template<typename Type>
124         class ConvertFromOpaque<const Type *> {
125         public:
126                 static const Type *apply(void *p) {
127                         return static_cast<Type *>( p );
128                 }
129         };
130
131         template<class Caller, class F>
132         class BindFirstOpaqueN;
133
134         template<class Caller, class R, class FirstBound, class... Ts>
135         class BindFirstOpaqueN<Caller, R(FirstBound, Ts...)> {
136                 FirstBound firstBound;
137         public:
138                 explicit BindFirstOpaqueN(FirstBound firstBound) : firstBound(firstBound) {
139                 }
140
141                 R operator()(Ts... args) const {
142                         return Caller::call(firstBound, args...);
143                 }
144
145                 FirstBound getBound() const {
146                         return firstBound;
147                 }
148
149                 static R thunk(void *environment, Ts... args) {
150                         return Caller::call(detail::ConvertFromOpaque<FirstBound>::apply(environment), args...);
151                 }
152
153                 void *getEnvironment() const {
154                         return detail::convertToOpaque(firstBound);
155                 }
156         };
157
158 }
159
160 template<class Caller>
161 using BindFirstOpaque = detail::BindFirstOpaqueN<Caller, get_func<Caller>>;
162
163 /// \brief Combines a void pointer with a pointer to a function which operates on a void pointer.
164 ///
165 /// Use with the callback constructors MemberCaller0, ConstMemberCaller0, ReferenceCaller0, ConstReferenceCaller0, PointerCaller0, ConstPointerCaller0 and FreeCaller0.
166 template<class F>
167 class Callback;
168
169 template<class R, class... Ts>
170 class Callback<R(Ts...)> : public detail::CallbackBase<R(*)(void *, Ts...)> {
171         using Base = detail::CallbackBase<R (*)(void *, Ts...)>;
172
173         static R nullThunk(void *, Ts...) {
174         }
175
176 public:
177         using func = R(Ts...);
178
179         Callback() : Base(0, nullThunk) {
180         }
181
182         template<typename Caller>
183         Callback(const BindFirstOpaque<Caller> &caller) : Base(caller.getEnvironment(), BindFirstOpaque<Caller>::thunk) {
184         }
185
186         Callback(void *environment, typename Base::Thunk function) : Base(environment, function) {
187         }
188
189         R operator()(Ts... args) const {
190                 return Base::getThunk()(Base::getEnvironment(), args...);
191         }
192 };
193
194 namespace detail {
195         template<class F>
196         struct Arglist;
197
198         template<class R, class Head, class... Ts>
199         struct Arglist<R(Head, Ts...)> {
200                 using type = R(Head, Ts...);
201
202                 template <class Unshift>
203                 using unshift = Arglist<R(Unshift, Head, Ts...)>;
204
205                 using shift = Arglist<R(Ts...)>;
206         };
207
208         template<class R, class... Ts>
209         struct Arglist<R(Ts...)> {
210                 using type = R(Ts...);
211
212                 template <class Unshift>
213                 using unshift = Arglist<R(Unshift, Ts...)>;
214         };
215
216         template<class F>
217         using ArgShift = typename detail::Arglist<F>::shift::type;
218
219         template<class F, class T>
220         using ArgUnshift = typename detail::Arglist<F>::template unshift<T>::type;
221 }
222
223 template<typename Caller>
224 inline Callback<detail::ArgShift<get_func<Caller>>> makeCallback(const Caller &caller, get_argument<Caller, 0> callee) {
225         return BindFirstOpaque<Caller>(callee);
226 }
227
228 template<class Caller, class F>
229 class CallerShiftFirst;
230
231 template<class Caller, class R, class FirstArgument, class... Ts>
232 class CallerShiftFirst<Caller, R(FirstArgument, Ts...)> {
233 public:
234         using func = R(FirstArgument, Ts...);
235
236         static R call(FirstArgument, Ts... args) {
237                 return Caller::call(args...);
238         }
239 };
240
241 template<typename Caller>
242 inline Callback<get_func<Caller>> makeStatelessCallback(const Caller &caller) {
243         return makeCallback(CallerShiftFirst<Caller, detail::ArgUnshift<get_func<Caller>, void *>>(), nullptr);
244 }
245
246 /// \brief Forms a Callback from a non-const Environment reference and a non-const Environment member-function.
247 template<class Environment, class F, MemberFunction<Environment, F> member>
248 using MemberCaller = BindFirstOpaque<Member<Environment, F, member>>;
249
250 /// \brief  Constructs a Callback1 from a non-const \p functor
251 ///
252 /// \param Functor Must define \c first_argument_type and \c operator()(first_argument_type).
253 template<typename Functor>
254 inline Callback<get_func<Functor>> makeCallback(Functor &functor) {
255         return MemberCaller<Functor, get_func<Functor>, &Functor::operator()>(functor);
256 }
257
258 /// \brief Forms a Callback from a const Environment reference and a const Environment member-function.
259 template<class Environment, class F, ConstMemberFunction<Environment, F> member>
260 using ConstMemberCaller = BindFirstOpaque<ConstMember<Environment, F, member>>;
261
262 /// \brief  Constructs a Callback1 from a const \p functor
263 ///
264 /// \param Functor Must define \c first_argument_type and const \c operator()(first_argument_type).
265 template<typename Functor>
266 inline Callback<get_func<Functor>> makeCallback(const Functor &functor) {
267         return ConstMemberCaller<Functor, get_func<Functor>, &Functor::operator()>(functor);
268 }
269
270 /// \brief Forms a Callback from a non-const Environment reference and a free function which operates on a non-const Environment reference.
271 template<class Environment, class F, detail::ArgUnshift<F, Environment &> *func>
272 using ReferenceCaller = BindFirstOpaque<Function<detail::ArgUnshift<F, Environment &>, func>>;
273
274 /// \brief Forms a Callback from a const Environment reference and a free function which operates on a const Environment reference.
275 template<class Environment, class F, detail::ArgUnshift<F, const Environment &> *func>
276 using ConstReferenceCaller = BindFirstOpaque<Function<detail::ArgUnshift<F, const Environment &>, func>>;
277
278 /// \brief Forms a Callback from a non-const Environment pointer and a free function which operates on a non-const Environment pointer.
279 template<class Environment, class F, detail::ArgUnshift<F, Environment *> *func>
280 using PointerCaller = BindFirstOpaque<Function<detail::ArgUnshift<F, Environment *>, func>>;
281
282 /// \brief Forms a Callback from a const Environment pointer and a free function which operates on a const Environment pointer.
283 template<class Environment, class F, detail::ArgUnshift<F, const Environment *> *func>
284 using ConstPointerCaller = BindFirstOpaque<Function<detail::ArgUnshift<F, const Environment *>, func>>;
285
286 namespace detail {
287         template<class Caller, class F>
288         class FreeCaller : public BindFirstOpaque<CallerShiftFirst<Caller, detail::ArgUnshift<F, void *>>> {
289         public:
290                 FreeCaller() : BindFirstOpaque<CallerShiftFirst<Caller, detail::ArgUnshift<F, void *>>>(nullptr) {
291                 }
292         };
293
294         template <class F>
295         struct freecallwrapper;
296
297         template <class R, class... Ts>
298         struct freecallwrapper<R(Ts...)> {
299                 using func = R(R(Ts...), Ts...);
300                 static R call(R(*f)(Ts...), Ts... args) {
301                         // ideally, we'd get the implementation of the function type directly. Instead, it's passed in
302                         return f(args...);
303                 }
304         };
305 }
306
307 /// \brief Forms a Callback from a free function
308 template<class F, F *func>
309 using FreeCaller = detail::FreeCaller<Function<F, func>, F>;
310
311 template<class F>
312 inline Callback<F> makeCallbackF(F *func) {
313         // illegal cast
314         return Callback<F>(reinterpret_cast<void *>(func), BindFirstOpaque<detail::freecallwrapper<F>>::thunk);
315 }
316
317 template <class T>
318 struct ImportExportCallback {
319         using Import_t = Callback<void(T)>;
320         Import_t Import;
321         using Export_t = Callback<void(const Callback<void(T)> &)>;
322     Export_t Export;
323 };
324
325 #endif