]> git.xonotic.org Git - xonotic/netradiant.git/blob - libs/generic/callback.h
Callback: remove fixed-arity wrappers
[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 MemberCaller0, ConstMemberCaller0, ReferenceCaller0, ConstReferenceCaller0, PointerCaller0, ConstPointerCaller0 and FreeCaller0.
155 template<class F>
156 class Callback;
157
158 template<class R, class... Ts>
159 class Callback<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         Callback() : Base(0, nullThunk) {
169         }
170
171         template<typename Caller>
172         Callback(const BindFirstOpaque<Caller> &caller) : Base(caller.getEnvironment(), BindFirstOpaque<Caller>::thunk) {
173         }
174
175         Callback(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 Callback<typename detail::Arglist<get_func<Caller>>::shift::type> makeCallback(const Caller &caller, get_argument<Caller, 0> callee) {
208         return Callback<typename detail::Arglist<get_func<Caller>>::shift::type>(BindFirstOpaque<Caller>(callee));
209 }
210
211 template<typename Caller>
212 inline Callback<get_func<Caller>> makeStatelessCallback(const Caller &caller) {
213         return makeCallback(CallerShiftFirst<Caller, typename detail::Arglist<get_func<Caller>>::template unshift<void *>::type>(), nullptr);
214 }
215
216 /// \brief Forms a Callback from a non-const Environment reference and a non-const Environment member-function.
217 ///
218 /// \dontinclude generic/callback.cpp
219 /// \skipline MemberCaller0 example
220 /// \until end example
221
222 template<class Environment, class F, MemberFunction<Environment, F> member>
223 using MemberCaller = BindFirstOpaque<typename MemberN<Environment, F>::template instance<member>>;
224
225 /// \brief Forms a Callback from a const Environment reference and a const Environment member-function.
226 ///
227 /// \dontinclude generic/callback.cpp
228 /// \skipline MemberCaller0 example
229 /// \until end example
230 template<class Environment, class F, ConstMemberFunction<Environment, F> member>
231 using ConstMemberCaller = BindFirstOpaque<typename ConstMemberN<Environment, F>::template instance<member>>;
232
233 /// \brief Forms a Callback from a non-const Environment reference and a free function which operates on a non-const Environment reference.
234 ///
235 /// \dontinclude generic/callback.cpp
236 /// \skipline ReferenceCaller0 example
237 /// \until end example
238 template<class Environment, class F, typename detail::Arglist<F>::template unshift<Environment &>::type *func>
239 using ReferenceCaller = BindFirstOpaque<typename FunctionN<typename detail::Arglist<F>::template unshift<Environment &>::type>::template instance<func>>;
240
241 /// \brief Forms a Callback from a const Environment reference and a free function which operates on a const Environment reference.
242 ///
243 /// \dontinclude generic/callback.cpp
244 /// \skipline ReferenceCaller0 example
245 /// \until end example
246 template<class Environment, class F, typename detail::Arglist<F>::template unshift<const Environment &>::type *func>
247 using ConstReferenceCaller = BindFirstOpaque<typename FunctionN<typename detail::Arglist<F>::template unshift<const Environment &>::type>::template instance<func>>;
248
249 /// \brief Forms a Callback from a non-const Environment pointer and a free function which operates on a non-const Environment pointer.
250 template<class Environment, class F, typename detail::Arglist<F>::template unshift<Environment *>::type *func>
251 using PointerCaller = BindFirstOpaque<typename FunctionN<typename detail::Arglist<F>::template unshift<Environment *>::type>::template instance<func>>;
252
253 /// \brief Forms a Callback from a const Environment pointer and a free function which operates on a const Environment pointer.
254 template<class Environment, class F, typename detail::Arglist<F>::template unshift<const Environment *>::type *func>
255 using ConstPointerCaller = BindFirstOpaque<typename FunctionN<typename detail::Arglist<F>::template unshift<const Environment *>::type>::template instance<func>>;
256
257 /// \brief Forms a Callback from a free function
258 template<class F, F *func>
259 class FreeCaller : public BindFirstOpaque<CallerShiftFirst<
260         typename FunctionN<F>::template instance<func>,
261         typename detail::Arglist<F>::template unshift<void *>::type
262 >> {
263 public:
264     FreeCaller()
265             : BindFirstOpaque<CallerShiftFirst<
266             typename FunctionN<F>::template instance<func>,
267             typename detail::Arglist<F>::template unshift<void *>::type
268     >>(nullptr) {
269     }
270 };
271
272 /// \brief  Constructs a Callback1 from a non-const \p functor
273 ///
274 /// \param Functor Must define \c first_argument_type and \c operator()(first_argument_type).
275 template<typename Functor>
276 inline Callback<get_func<Functor>> makeCallback(Functor &functor) {
277         return Callback<get_func<Functor>>(MemberCaller<Functor, get_func<Functor>, &Functor::operator()>(functor));
278 }
279
280 /// \brief  Constructs a Callback1 from a const \p functor
281 ///
282 /// \param Functor Must define \c first_argument_type and const \c operator()(first_argument_type).
283 template<typename Functor>
284 inline Callback<get_func<Functor>> makeCallback(const Functor &functor) {
285         return Callback<get_func<Functor>>(ConstMemberCaller<Functor, get_func<Functor>, &Functor::operator()>(functor));
286 }
287
288 using BoolImportCallback = Callback<void(bool)>;
289 using BoolExportCallback = Callback<void(const BoolImportCallback&)>;
290
291 using IntImportCallback = Callback<void(int)>;
292 using IntExportCallback = Callback<void(const IntImportCallback&)>;
293
294 using FloatImportCallback = Callback<void(float)>;
295 using FloatExportCallback = Callback<void(const FloatImportCallback&)>;
296
297 using StringImportCallback = Callback<void(const char*)>;
298 using StringExportCallback = Callback<void(const StringImportCallback&)>;
299
300 using SizeImportCallback = Callback<void(std::size_t)>;
301 using SizeExportCallback = Callback<void(const SizeImportCallback&)>;
302
303 #endif