]> git.xonotic.org Git - xonotic/netradiant.git/blob - libs/generic/callback.h
Callback: work at any arity
[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 template<typename Type>
32 inline void* convertToOpaque( Type* t ){
33         return t;
34 }
35 template<typename Type>
36 inline void* convertToOpaque( const Type* t ){
37         return const_cast<Type*>( t );
38 }
39 template<typename Type>
40 inline void* convertToOpaque( Type& t ){
41         return &t;
42 }
43 template<typename Type>
44 inline void* convertToOpaque( const Type& t ){
45         return const_cast<Type*>( &t );
46 }
47
48
49 template<typename Type>
50 class ConvertFromOpaque
51 {
52 };
53
54 template<typename Type>
55 class ConvertFromOpaque<Type&>
56 {
57 public:
58 static Type& apply( void* p ){
59         return *static_cast<Type*>( p );
60 }
61 };
62
63 template<typename Type>
64 class ConvertFromOpaque<const Type&>
65 {
66 public:
67 static const Type& apply( void* p ){
68         return *static_cast<Type*>( p );
69 }
70 };
71
72
73 template<typename Type>
74 class ConvertFromOpaque<Type*>
75 {
76 public:
77 static Type* apply( void* p ){
78         return static_cast<Type*>( p );
79 }
80 };
81
82 template<typename Type>
83 class ConvertFromOpaque<const Type*>
84 {
85 public:
86 static const Type* apply( void* p ){
87         return static_cast<Type*>( p );
88 }
89 };
90
91 template<typename Thunk_>
92 class CallbackBase
93 {
94 void* m_environment;
95 Thunk_ m_thunk;
96 public:
97 typedef Thunk_ Thunk;
98 CallbackBase( void* environment, Thunk function ) : m_environment( environment ), m_thunk( function ){
99 }
100 void* getEnvironment() const {
101         return m_environment;
102 }
103 Thunk getThunk() const {
104         return m_thunk;
105 }
106 };
107
108 template<typename Thunk>
109 inline bool operator==( const CallbackBase<Thunk>& self, const CallbackBase<Thunk>& other ){
110         return self.getEnvironment() == other.getEnvironment() && self.getThunk() == other.getThunk();
111 }
112 template<typename Thunk>
113 inline bool operator!=( const CallbackBase<Thunk>& self, const CallbackBase<Thunk>& other ){
114         return !( self == other );
115 }
116 template<typename Thunk>
117 inline bool operator<( const CallbackBase<Thunk>& self, const CallbackBase<Thunk>& other ){
118         return self.getEnvironment() < other.getEnvironment() ||
119                    ( !( other.getEnvironment() < self.getEnvironment() ) && self.getThunk() < other.getThunk() );
120 }
121
122 template<class Caller, class F>
123 class BindFirstOpaqueN;
124
125 template<class Caller, class R, class FirstBound, class... Ts>
126 class BindFirstOpaqueN<Caller, R(FirstBound, Ts...)> {
127         FirstBound firstBound;
128 public:
129         explicit BindFirstOpaqueN(FirstBound firstBound) : firstBound(firstBound) {
130         }
131
132         R operator()(Ts... args) const {
133                 return Caller::call(firstBound, args...);
134         }
135
136         FirstBound getBound() const {
137                 return firstBound;
138         }
139
140         static R thunk(void *environment, Ts... args) {
141                 return Caller::call(ConvertFromOpaque<FirstBound>::apply(environment), args...);
142         }
143
144         void *getEnvironment() const {
145                 return convertToOpaque(firstBound);
146         }
147 };
148
149 template<class Caller>
150 using BindFirstOpaque = BindFirstOpaqueN<Caller, get_func<Caller>>;
151
152 /// \brief Combines a void pointer with a pointer to a function which operates on a void pointer.
153 ///
154 /// Use with the callback constructors MemberCaller, ConstMemberCaller, ReferenceCaller, ConstReferenceCaller, PointerCaller, ConstPointerCaller and FreeCaller.
155 template<class F>
156 class CallbackN;
157
158 template<class R, class... Ts>
159 class CallbackN<R(Ts...)> : public CallbackBase<R(*)(void *, Ts...)> {
160         using Base = CallbackBase<R (*)(void *, Ts...)>;
161
162         static R nullThunk(void *, Ts...) {
163         }
164
165 public:
166         using func = R(Ts...);
167
168         CallbackN() : Base(0, nullThunk) {
169         }
170
171         template<typename Caller>
172         CallbackN(const BindFirstOpaque<Caller> &caller) : Base(caller.getEnvironment(), BindFirstOpaque<Caller>::thunk) {
173         }
174
175         CallbackN(void *environment, typename Base::Thunk function) : Base(environment, function) {
176         }
177
178         R operator()(Ts... args) const {
179                 return Base::getThunk()(Base::getEnvironment(), args...);
180         }
181 };
182
183 namespace detail {
184         template<class F>
185         struct Arglist;
186
187         template<class R, class Head, class... Ts>
188         struct Arglist<R(Head, Ts...)> {
189                 using type = R(Head, Ts...);
190
191                 template <class Unshift>
192                 using unshift = Arglist<R(Unshift, Head, Ts...)>;
193
194                 using shift = Arglist<R(Ts...)>;
195         };
196
197         template<class R, class... Ts>
198         struct Arglist<R(Ts...)> {
199                 using type = R(Ts...);
200
201                 template <class Unshift>
202                 using unshift = Arglist<R(Unshift, Ts...)>;
203         };
204 }
205
206 template<typename Caller>
207 inline CallbackN<typename detail::Arglist<get_func<Caller>>::shift::type> makeCallbackN(const Caller &caller, get_argument<Caller, 0> callee) {
208         return CallbackN<typename detail::Arglist<get_func<Caller>>::shift::type>(BindFirstOpaque<Caller>(callee));
209 }
210
211 template<typename Caller>
212 inline CallbackN<get_func<Caller>> makeStatelessCallbackN(const Caller &caller) {
213         return makeCallbackN(CallerShiftFirst<Caller, typename detail::Arglist<get_func<Caller>>::template unshift<void *>::type>(), nullptr);
214 }
215
216 namespace detail {
217         template<class Object, class F>
218         struct MemberFunction;
219
220         template<class Object, class R, class... Ts>
221         struct MemberFunction<Object, R(Ts...)> {
222                 using type = R(Object::*)(Ts...);
223                 using type_const = R(Object::*)(Ts...) const;
224         };
225 }
226
227 template<class Object, class F>
228 using MemberFunction = typename detail::MemberFunction<Object, F>::type;
229
230 template<class Object, class F>
231 using ConstMemberFunction = typename detail::MemberFunction<Object, F>::type_const;
232
233 /// \brief Forms a Callback from a non-const Environment reference and a non-const Environment member-function.
234 ///
235 /// \dontinclude generic/callback.cpp
236 /// \skipline MemberCaller example
237 /// \until end example
238
239 template<class Environment, class F, MemberFunction<Environment, F> member>
240 using MemberCallerN = BindFirstOpaque<typename MemberN<Environment, F>::template instance<member>>;
241
242 /// \brief Forms a Callback from a const Environment reference and a const Environment member-function.
243 ///
244 /// \dontinclude generic/callback.cpp
245 /// \skipline MemberCaller example
246 /// \until end example
247 template<class Environment, class F, ConstMemberFunction<Environment, F> member>
248 using ConstMemberCallerN = BindFirstOpaque<typename ConstMemberN<Environment, F>::template instance<member>>;
249
250 /// \brief Forms a Callback from a non-const Environment reference and a free function which operates on a non-const Environment reference.
251 ///
252 /// \dontinclude generic/callback.cpp
253 /// \skipline ReferenceCaller example
254 /// \until end example
255 template<class Environment, class F, typename detail::Arglist<F>::template unshift<Environment &>::type *func>
256 using ReferenceCallerN = BindFirstOpaque<typename FunctionN<typename detail::Arglist<F>::template unshift<Environment &>::type>::template instance<func>>;
257
258 /// \brief Forms a Callback from a const Environment reference and a free function which operates on a const Environment reference.
259 ///
260 /// \dontinclude generic/callback.cpp
261 /// \skipline ReferenceCaller example
262 /// \until end example
263 template<class Environment, class F, typename detail::Arglist<F>::template unshift<const Environment &>::type *func>
264 using ConstReferenceCallerN = BindFirstOpaque<typename FunctionN<typename detail::Arglist<F>::template unshift<const Environment &>::type>::template instance<func>>;
265
266 /// \brief Forms a Callback from a non-const Environment pointer and a free function which operates on a non-const Environment pointer.
267 template<class Environment, class F, typename detail::Arglist<F>::template unshift<Environment *>::type *func>
268 using PointerCallerN = BindFirstOpaque<typename FunctionN<typename detail::Arglist<F>::template unshift<Environment *>::type>::template instance<func>>;
269
270 /// \brief Forms a Callback from a const Environment pointer and a free function which operates on a const Environment pointer.
271 template<class Environment, class F, typename detail::Arglist<F>::template unshift<const Environment *>::type *func>
272 using ConstPointerCallerN = BindFirstOpaque<typename FunctionN<typename detail::Arglist<F>::template unshift<const Environment *>::type>::template instance<func>>;
273
274 /// \brief Forms a Callback from a free function
275 template<class F, F *func>
276 class FreeCallerN : public BindFirstOpaque<CallerShiftFirst<
277         typename FunctionN<F>::template instance<func>,
278         typename detail::Arglist<F>::template unshift<void *>::type
279 >> {
280 public:
281     FreeCallerN()
282             : BindFirstOpaque<CallerShiftFirst<
283             typename FunctionN<F>::template instance<func>,
284             typename detail::Arglist<F>::template unshift<void *>::type
285     >>(nullptr) {
286     }
287 };
288
289 /// \brief  Constructs a Callback1 from a non-const \p functor
290 ///
291 /// \param Functor Must define \c first_argument_type and \c operator()(first_argument_type).
292 template<typename Functor>
293 inline CallbackN<get_func<Functor>> makeCallbackN(Functor &functor) {
294         return CallbackN<get_func<Functor>>(MemberCallerN<Functor, get_func<Functor>, &Functor::operator()>(functor));
295 }
296
297 /// \brief  Constructs a Callback1 from a const \p functor
298 ///
299 /// \param Functor Must define \c first_argument_type and const \c operator()(first_argument_type).
300 template<typename Functor>
301 inline CallbackN<get_func<Functor>> makeCallbackN(const Functor &functor) {
302         return CallbackN<get_func<Functor>>(ConstMemberCallerN<Functor, get_func<Functor>, &Functor::operator()>(functor));
303 }
304
305 // todo: inline
306
307 #define makeCallback makeCallbackN
308
309 using Callback = CallbackN<void()>;
310
311 template<class Result>
312 using Callback0 = CallbackN<Result()>;
313
314 template<class FirstArgument, class Result = void>
315 using Callback1 = CallbackN<Result(FirstArgument)>;
316
317 template<typename FirstArgument, typename SecondArgument, typename Result = void>
318 using Callback2 = CallbackN<Result(FirstArgument, SecondArgument)>;
319
320 template<typename FirstArgument, typename SecondArgument, typename ThirdArgument, typename Result = void>
321 using Callback3 = CallbackN<Result(FirstArgument, SecondArgument, ThirdArgument)>;
322
323 #define makeCallback0 makeCallbackN
324 #define makeStatelessCallback0 makeStatelessCallbackN
325
326 #define makeCallback1 makeCallbackN
327 #define makeStatelessCallback1 makeStatelessCallbackN
328
329 #define makeCallback2 makeCallbackN
330 #define makeStatelessCallback2 makeStatelessCallbackN
331
332 #define makeCallback3 makeCallbackN
333 #define makeStatelessCallback3 makeStatelessCallbackN
334
335 template<class Environment, void(Environment::*member)()>
336 using MemberCaller = MemberCallerN<Environment, void(), member>;
337
338 template<class Environment, void(Environment::*member)() const>
339 using ConstMemberCaller = ConstMemberCallerN<Environment, void(), member>;
340
341 template<class Environment, class FirstArgument, void(Environment::*member)(FirstArgument)>
342 using MemberCaller1 = MemberCallerN<Environment, void(FirstArgument), member>;
343
344 template<class Environment, class FirstArgument, void(Environment::*member)(FirstArgument) const>
345 using ConstMemberCaller1 = ConstMemberCallerN<Environment, void(FirstArgument), member>;
346
347 template<class Environment, void(*func)(Environment &)>
348 using ReferenceCaller = ReferenceCallerN<Environment, void(), func>;
349
350 template<class Environment, void(*func)(const Environment &)>
351 using ConstReferenceCaller = ConstReferenceCallerN<Environment, void(), func>;
352
353 /// \brief Forms a Callback from a non-const Environment reference and a free function which operates on a non-const Environment reference and one other argument.
354 template<class Environment, class FirstArgument, void(*func)(Environment &, FirstArgument)>
355 using ReferenceCaller1 = ReferenceCallerN<Environment, void(FirstArgument), func>;
356
357 /// \brief Forms a Callback from a const Environment reference and a free function which operates on a const Environment reference and one other argument.
358 template<class Environment, class FirstArgument, void(*func)(const Environment &, FirstArgument)>
359 using ConstReferenceCaller1 = ConstReferenceCallerN<Environment, void(FirstArgument), func>;
360
361 /// \brief Forms a Callback from a non-const Environment pointer and a free function which operates on a non-const Environment pointer.
362 template<class Environment, void(*func)(Environment *)>
363 using PointerCaller = PointerCallerN<Environment, void(), func>;
364
365 /// \brief Forms a Callback from a const Environment pointer and a free function which operates on a const Environment pointer.
366 template<class Environment, void(*func)(const Environment *)>
367 using ConstPointerCaller = ConstPointerCallerN<Environment, void(), func>;
368
369 /// \brief Forms a Callback from a non-const Environment pointer and a free function which operates on a non-const Environment pointer and one other argument.
370 template<class Environment, class FirstArgument, void(*func)(Environment *, FirstArgument)>
371 using PointerCaller1 = PointerCallerN<Environment, void(FirstArgument), func>;
372
373 /// \brief Forms a Callback from a const Environment pointer and a free function which operates on a const Environment pointer and one other argument.
374 template<class Environment, class FirstArgument, void(*func)(const Environment *, FirstArgument)>
375 using ConstPointerCaller1 = ConstPointerCallerN<Environment, void(FirstArgument), func>;
376
377 template<void(*func)()>
378 using FreeCaller = FreeCallerN<void(), func>;
379
380 template<class FirstArgument, void(*func)(FirstArgument)>
381 using FreeCaller1 = FreeCallerN<void(FirstArgument), func>;
382
383 typedef Callback1<bool> BoolImportCallback;
384 typedef Callback1<const BoolImportCallback&> BoolExportCallback;
385
386 typedef Callback1<int> IntImportCallback;
387 typedef Callback1<const IntImportCallback&> IntExportCallback;
388
389 typedef Callback1<float> FloatImportCallback;
390 typedef Callback1<const FloatImportCallback&> FloatExportCallback;
391
392 typedef Callback1<const char*> StringImportCallback;
393 typedef Callback1<const StringImportCallback&> StringExportCallback;
394
395 typedef Callback1<std::size_t> SizeImportCallback;
396 typedef Callback1<const SizeImportCallback&> SizeExportCallback;
397
398 #endif