Add option (enabled by default) to omit the sender from a slot's arguments. In most...
[qtgstreamer:qtgstreamer.git] / src / QGlib / closureimpl_p.h
1 /*
2     Copyright (C) 2010  George Kiagiadakis <kiagiadakis.george@gmail.com>
3
4     This library is free software; you can redistribute it and/or modify
5     it under the terms of the GNU Lesser General Public License as published
6     by the Free Software Foundation; either version 2.1 of the License, or
7     (at your option) any later version.
8
9     This program is distributed in the hope that it will be useful,
10     but WITHOUT ANY WARRANTY; without even the implied warranty of
11     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12     GNU General Public License for more details.
13
14     You should have received a copy of the GNU Lesser General Public License
15     along with this program.  If not, see <http://www.gnu.org/licenses/>.
16 */
17 #if !defined(BOOST_PP_IS_ITERATING) || !BOOST_PP_IS_ITERATING
18
19 # ifndef IN_QGLIB_CLOSURE_H
20 #  error "This file must not be included directly"
21 # endif
22
23 # include "value.h"
24 # include <QtCore/QList>
25 # include <stdexcept>
26 # include <boost/type_traits.hpp>
27
28
29 namespace QGlib {
30 namespace Private {
31
32 class ClosureDataBase
33 {
34 public:
35     inline virtual ~ClosureDataBase() {}
36
37     typedef void (*CppMarshaller)(SharedValue &, const QList<Value> &, ClosureDataBase*);
38     CppMarshaller cppMarshaller;
39     bool passSender; //whether to pass the sender instance as the first slot argument
40
41 protected:
42     inline ClosureDataBase(CppMarshaller marshal, bool passSender)
43         : cppMarshaller(marshal), passSender(passSender) {}
44 };
45
46
47 /*! This is the method that glues the c++ closure system with the GObject world.
48  * It creates a GClosure, sets its data, marshaller and finalize notifier accordingly
49  * and returns a ClosurePtr. This is the only exported symbol from this library that
50  * allows you to create a GClosure and it should only be used through the
51  * template CppClosure class.
52  */
53 ClosurePtr createCppClosure(ClosureDataBase *data); //implemented in closure.cpp
54
55
56 template <typename Function, typename R>
57 struct invoker
58 {
59     static inline void invoke(const Function & f, SharedValue & result) { result.set<R>(f()); }
60 };
61
62 template <typename Function>
63 struct invoker<Function, void>
64 {
65     static inline void invoke(const Function & f, SharedValue &) { f(); }
66 };
67
68
69 template <typename Signature, typename Function>
70 struct CppClosure {};
71
72 } //namespace Private
73 } //namespace QGlib
74
75
76 # if QGLIB_HAVE_CXX0X
77
78 namespace QGlib {
79 namespace Private {
80
81 template <typename ParentFunction, typename R, typename Arg1, typename... Args>
82 class BoundArgumentFunction
83 {
84 public:
85     inline BoundArgumentFunction(ParentFunction && fn, Arg1 && arg)
86         : m_function(fn), m_firstArg(arg) {}
87
88     inline R operator()(Args&&... args) const
89     {
90         return m_function(m_firstArg, args...);
91     }
92
93 private:
94     ParentFunction && m_function;
95     Arg1 && m_firstArg;
96 };
97
98 template <typename F, typename R, typename Arg1, typename... Args>
99 inline BoundArgumentFunction<F, R, Arg1, Args...> partial_bind(F && f, Arg1 && a1)
100 {
101     return BoundArgumentFunction<F, R, Arg1, Args...>(f, a1);
102 }
103
104
105 template <typename F, typename R>
106 inline void unpackAndInvoke(F && function, SharedValue & result,
107                             QList<Value>::const_iterator &&,
108                             QList<Value>::const_iterator &&)
109 {
110     invoker<F, R>::invoke(function, result);
111 }
112
113 template <typename F, typename R, typename Arg1, typename... Args>
114 inline void unpackAndInvoke(F && function, SharedValue & result,
115                             QList<Value>::const_iterator && argsBegin,
116                             QList<Value>::const_iterator && argsEnd)
117 {
118     typedef typename boost::remove_const<
119                 typename boost::remove_reference<Arg1>::type
120             >::type CleanArg1;
121     typedef BoundArgumentFunction<F, R, Arg1, Args...> F1;
122
123     CleanArg1 && boundArg = argsBegin->get<CleanArg1>();
124     F1 && f = partial_bind<F, R, Arg1, Args...>(function, boundArg);
125     unpackAndInvoke< F1, R, Args... >(f, result, ++argsBegin, argsEnd);
126 }
127
128
129 template <typename F, typename R, typename... Args>
130 struct CppClosure<R (Args...), F>
131 {
132     class ClosureData : public ClosureDataBase
133     {
134     public:
135         inline ClosureData(CppMarshaller marshal, const F & func, bool passSender)
136             : ClosureDataBase(marshal, passSender), function(func) {}
137         F function;
138     };
139
140     static inline ClosurePtr create(const F & function, bool passSender)
141     {
142         return createCppClosure(new ClosureData(&marshaller, function, passSender));
143     }
144
145     static inline void marshaller(SharedValue & result, const QList<Value> & params, ClosureDataBase *data)
146     {
147         if (static_cast<unsigned int>(params.size()) < sizeof...(Args)) {
148             throw std::logic_error("The signal provides less arguments than what the closure expects");
149         }
150
151         ClosureData *cdata = static_cast<ClosureData*>(data);
152         unpackAndInvoke<F, R, Args...>(cdata->function, result, params.constBegin(), params.constEnd());
153     }
154 };
155
156 } //namespace Private
157 } //namespace QGlib
158
159 # else //QGLIB_HAVE_CXX0X
160
161 #  if !defined(QGLIB_CLOSURE_MAX_ARGS)
162 #   define QGLIB_CLOSURE_MAX_ARGS 9
163 #  endif
164
165 #  include <boost/function.hpp>
166 #  include <boost/preprocessor.hpp>
167 #  include <boost/bind.hpp>
168
169 // include the second part of this file as many times as QGLIB_CLOSURE_MAX_ARGS specifies
170 #  define BOOST_PP_ITERATION_PARAMS_1 (3,(0, QGLIB_CLOSURE_MAX_ARGS, "QGlib/closureimpl_p.h"))
171 #  include BOOST_PP_ITERATE()
172
173 #  undef BOOST_PP_ITERATION_PARAMS_1
174 #  undef QGLIB_CLOSURE_MAX_ARGS
175
176 # endif //QGLIB_HAVE_CXX0X
177
178
179 #else // !defined(BOOST_PP_IS_ITERATING) || !BOOST_PP_IS_ITERATING
180
181 /*
182     This part is included from BOOST_PP_ITERATE(). It defines a CppClosureN class
183     (where N is the number of template arguments it takes) and a specialization for class
184     CppClosure, so that the CppClosure<R (Args...), F> syntax is supported. This part is
185     included multiple times (QGLIB_CLOSURE_MAX_ARGS defines how many), and each time
186     it defines those classes with different number of arguments.
187     The concept is based on the implementation of boost::function.
188 */
189
190 # define QGLIB_CLOSURE_IMPL_NUM_ARGS  BOOST_PP_ITERATION()
191
192 # define QGLIB_CLOSURE_IMPL_TEMPLATE_PARAMS \
193         BOOST_PP_ENUM_PARAMS(QGLIB_CLOSURE_IMPL_NUM_ARGS, typename A)
194
195 # define QGLIB_CLOSURE_IMPL_TEMPLATE_ARGS \
196         BOOST_PP_ENUM_PARAMS(QGLIB_CLOSURE_IMPL_NUM_ARGS, A)
197
198 # define QGLIB_CLOSURE_IMPL_CPPCLOSUREN \
199         BOOST_PP_CAT(CppClosure, QGLIB_CLOSURE_IMPL_NUM_ARGS)
200
201 # if QGLIB_CLOSURE_IMPL_NUM_ARGS > 0
202 #  define QGLIB_CLOSURE_IMPL_COMMA ,
203 # else
204 #  define QGLIB_CLOSURE_IMPL_COMMA
205 # endif
206
207 # define QGLIB_CLOSURE_IMPL_UNPACK_ARGS_STEP(z, n, list) \
208     ,list.at(n).get< typename boost::remove_const< typename boost::remove_reference<A ##n>::type >::type >()
209
210 # define QGLIB_CLOSURE_IMPL_UNPACK_ARGS(list) \
211     BOOST_PP_REPEAT(QGLIB_CLOSURE_IMPL_NUM_ARGS, QGLIB_CLOSURE_IMPL_UNPACK_ARGS_STEP, list)
212
213 namespace QGlib {
214 namespace Private {
215
216 template <typename F, typename R QGLIB_CLOSURE_IMPL_COMMA
217                                  QGLIB_CLOSURE_IMPL_TEMPLATE_PARAMS>
218 struct QGLIB_CLOSURE_IMPL_CPPCLOSUREN
219 {
220     class ClosureData : public ClosureDataBase
221     {
222     public:
223         inline ClosureData(CppMarshaller marshal, const F & func, bool passSender)
224             : ClosureDataBase(marshal, passSender), function(func) {}
225         F function;
226     };
227
228     static ClosurePtr create(const F & function, bool passSender)
229     {
230         return createCppClosure(new ClosureData(&marshaller, function, passSender));
231     }
232
233     static void marshaller(SharedValue & result, const QList<Value> & params, ClosureDataBase *data)
234     {
235         if (params.size() < QGLIB_CLOSURE_IMPL_NUM_ARGS) {
236             throw std::logic_error("The signal provides less arguments than what the closure expects");
237         }
238
239         ClosureData *cdata = static_cast<ClosureData*>(data);
240 # if QGLIB_CLOSURE_IMPL_NUM_ARGS > 0
241         boost::function<R ()> callback = boost::bind<R>(cdata->function
242                                                         QGLIB_CLOSURE_IMPL_UNPACK_ARGS(params));
243         invoker< boost::function<R ()>, R >::invoke(callback, result);
244 # else
245         invoker< F, R >::invoke(cdata->function, result);
246 # endif
247     }
248 };
249
250 //partial specialization of struct CppClosure to support the CppClosure<R (Args...), F> syntax
251 template <typename F, typename R  QGLIB_CLOSURE_IMPL_COMMA
252                                   QGLIB_CLOSURE_IMPL_TEMPLATE_PARAMS>
253 struct CppClosure<R (QGLIB_CLOSURE_IMPL_TEMPLATE_ARGS), F>
254     : public QGLIB_CLOSURE_IMPL_CPPCLOSUREN< F, R  QGLIB_CLOSURE_IMPL_COMMA
255                                                    QGLIB_CLOSURE_IMPL_TEMPLATE_ARGS >
256 {
257 };
258
259 } //namespace Private
260 } //namespace QGlib
261
262 # undef QGLIB_CLOSURE_IMPL_UNPACK_ARGS
263 # undef QGLIB_CLOSURE_IMPL_UNPACK_ARGS_STEP
264 # undef QGLIB_CLOSURE_IMPL_COMMA
265 # undef QGLIB_CLOSURE_IMPL_CPPCLOSUREN
266 # undef QGLIB_CLOSURE_IMPL_TEMPLATE_ARGS
267 # undef QGLIB_CLOSURE_IMPL_TEMPLATE_PARAMS
268 # undef QGLIB_CLOSURE_IMPL_NUM_ARGS
269
270 #endif // !defined(BOOST_PP_IS_ITERATING) || !BOOST_PP_IS_ITERATING