Add option (enabled by default) to omit the sender from a slot's arguments. In most...
[qtgstreamer:qtgstreamer.git] / src / QGlib / signalimpl_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_SIGNAL_H
20 #  error "This file must not be included directly"
21 # endif
22
23 # include "value.h"
24 # include "string.h"
25 # include <QtCore/QList>
26 # include <QtCore/QDebug>
27 # include <boost/type_traits.hpp>
28 # include <stdexcept>
29
30
31 namespace QGlib {
32 namespace Private {
33
34 template <typename Signature>
35 struct EmitImpl {};
36
37 } //namespace Private
38 } //namespace QGlib
39
40
41 # if QGLIB_HAVE_CXX0X
42
43 namespace QGlib {
44 namespace Private {
45
46 inline QList<Value> packArguments()
47 {
48     return QList<Value>();
49 }
50
51 template <typename Arg1, typename... Args>
52 QList<Value> packArguments(Arg1 && a1, Args&&... args)
53 {
54     typedef typename boost::add_const<
55                 typename boost::add_reference<Arg1>::type
56             >::type ConstArg1Ref;
57
58     QList<Value> result = packArguments(args...);
59     //prepend, since we are packing from the last to the first argument
60     result.prepend(Value(static_cast<ConstArg1Ref>(a1)));
61     return result;
62 }
63
64 template <typename R, typename... Args>
65 struct EmitImpl<R (Args...)>
66 {
67     static inline R emit(void *instance, const char *detailedSignal, Args&&... args)
68     {
69         Value && returnValue = Signal::emit(instance, detailedSignal, packArguments(args...));
70
71         try {
72             return returnValue.get<R>();
73         } catch(const std::logic_error &) {
74             qCritical() << "Error during emission of signal" << detailedSignal
75                         << "The returned value from emit is of different type than the one requested";
76             return R();
77         }
78     }
79 };
80
81 template <typename... Args>
82 struct EmitImpl<void (Args...)>
83 {
84     static inline void emit(void *instance, const char *detailedSignal, Args&&... args)
85     {
86         Value && returnValue = Signal::emit(instance, detailedSignal, packArguments(args...));
87
88         if (returnValue.isValid()) {
89             qWarning() << "Ignoring return value from emission of signal" << detailedSignal;
90         }
91     }
92 };
93
94 template <typename T, typename R, typename... Args>
95 class MemberFunction
96 {
97 public:
98     inline MemberFunction(R (T::*fn)(Args...), T *obj)
99         : m_function(fn), m_object(obj) {}
100
101     inline R operator()(Args&&... args) const
102     {
103         return (m_object->*m_function)(args...);
104     }
105
106 private:
107     R (T::*m_function)(Args...);
108     T *m_object;
109 };
110
111 template <typename T, typename R, typename... Args>
112 MemberFunction<T, R, Args...> mem_fn(R (T::*fn)(Args...), T *obj)
113 {
114     return MemberFunction<T, R, Args...>(fn, obj);
115 }
116
117 } //namespace Private
118
119 template <typename R, typename... Args>
120 R Signal::emit(void *instance, const char *detailedSignal, Args&&... args)
121 {
122     return QGlib::Private::EmitImpl<R (Args...)>::emit(instance, detailedSignal, args...);
123 }
124
125 template <typename T, typename R, typename... Args>
126 SignalHandler Signal::connect(void *instance, const char *detailedSignal,
127                               T *receiver, R (T::*slot)(Args...), ConnectFlags flags)
128 {
129     typedef QGlib::Private::MemberFunction<T, R, Args...> F;
130
131     F && f = QGlib::Private::mem_fn(slot, receiver);
132     ClosurePtr && closure = QGlib::Private::CppClosure<R (Args...), F>::create(f, flags & PassSender);
133     return connect(instance, detailedSignal, closure, flags);
134 }
135
136 } //namespace QGlib
137
138 # else //QGLIB_HAVE_CXX0X
139
140 // include the second part of this file as many times as QGLIB_SIGNAL_MAX_ARGS specifies
141 #  define BOOST_PP_ITERATION_PARAMS_1 (3,(0, QGLIB_SIGNAL_MAX_ARGS, "QGlib/signalimpl_p.h"))
142 #  include BOOST_PP_ITERATE()
143 #  undef BOOST_PP_ITERATION_PARAMS_1
144
145 # endif //QGLIB_HAVE_CXX0X
146
147
148 #else // !defined(BOOST_PP_IS_ITERATING) || !BOOST_PP_IS_ITERATING
149
150 /*
151     This part is included from BOOST_PP_ITERATE(). It defines specializations of struct EmitImpl
152     with different number of arguments as well as the multiple implementations of the non-variadic
153     Signal::emit and Signal::connect. This part is included multiple times (QGLIB_SIGNAL_MAX_ARGS
154     defines how many), and each time it defines those classes and functions with different number
155     of arguments. The concept is based on the implementation of boost::function.
156 */
157
158 # define QGLIB_SIGNAL_IMPL_NUM_ARGS  BOOST_PP_ITERATION()
159
160 # define QGLIB_SIGNAL_IMPL_TEMPLATE_PARAMS \
161         BOOST_PP_ENUM_PARAMS(QGLIB_SIGNAL_IMPL_NUM_ARGS, typename A)
162
163 # define QGLIB_SIGNAL_IMPL_TEMPLATE_ARGS \
164         BOOST_PP_ENUM_PARAMS(QGLIB_SIGNAL_IMPL_NUM_ARGS, A)
165
166 # define QGLIB_SIGNAL_IMPL_FUNCTION_PARAMS \
167         BOOST_PP_ENUM_TRAILING_BINARY_PARAMS(QGLIB_SIGNAL_IMPL_NUM_ARGS, A, a)
168
169 # define QGLIB_SIGNAL_IMPL_FUNCTION_ARGS \
170         BOOST_PP_ENUM_TRAILING_PARAMS(QGLIB_SIGNAL_IMPL_NUM_ARGS, a)
171
172 # if QGLIB_SIGNAL_IMPL_NUM_ARGS > 0
173 #  define QGLIB_SIGNAL_IMPL_COMMA ,
174 # else
175 #  define QGLIB_SIGNAL_IMPL_COMMA
176 # endif
177
178 # define QGLIB_SIGNAL_IMPL_PACK_ARGS_STEP(z, n, list) \
179         list.append(Value(static_cast< typename boost::add_const< typename boost::add_reference<A ##n>::type >::type >(a ##n)));
180
181 # define QGLIB_SIGNAL_IMPL_PACK_ARGS(list) \
182         BOOST_PP_REPEAT(QGLIB_SIGNAL_IMPL_NUM_ARGS, QGLIB_SIGNAL_IMPL_PACK_ARGS_STEP, list)
183
184 # define QGLIB_SIGNAL_IMPL_BIND_ARGS \
185         BOOST_PP_ENUM_SHIFTED_PARAMS(BOOST_PP_INC(QGLIB_SIGNAL_IMPL_NUM_ARGS), _)
186
187 namespace QGlib {
188 namespace Private {
189
190 template <typename R QGLIB_SIGNAL_IMPL_COMMA
191                      QGLIB_SIGNAL_IMPL_TEMPLATE_PARAMS>
192 struct EmitImpl<R (QGLIB_SIGNAL_IMPL_TEMPLATE_ARGS)>
193 {
194     static inline R emit(void *instance, const char *detailedSignal
195                          QGLIB_SIGNAL_IMPL_FUNCTION_PARAMS)
196     {
197         QList<Value> values;
198         QGLIB_SIGNAL_IMPL_PACK_ARGS(values)
199         Value returnValue = Signal::emit(instance, detailedSignal, values);
200
201         try {
202             return returnValue.get<R>();
203         } catch(const std::logic_error &) {
204             qCritical() << "Error during emission of signal" << detailedSignal
205                         << "The returned value from emit is of different type than the one requested";
206             return R();
207         }
208     }
209 };
210
211 template <QGLIB_SIGNAL_IMPL_TEMPLATE_PARAMS>
212 struct EmitImpl<void (QGLIB_SIGNAL_IMPL_TEMPLATE_ARGS)>
213 {
214     static inline void emit(void *instance, const char *detailedSignal
215                             QGLIB_SIGNAL_IMPL_FUNCTION_PARAMS)
216     {
217         QList<Value> values;
218         QGLIB_SIGNAL_IMPL_PACK_ARGS(values)
219         Value returnValue = Signal::emit(instance, detailedSignal, values);
220
221         if (returnValue.isValid()) {
222             qWarning() << "Ignoring return value from emission of signal" << detailedSignal;
223         }
224     }
225 };
226
227 } //namespace Private
228
229 template <typename R QGLIB_SIGNAL_IMPL_COMMA
230                      QGLIB_SIGNAL_IMPL_TEMPLATE_PARAMS>
231 R Signal::emit(void *instance, const char *detailedSignal QGLIB_SIGNAL_IMPL_FUNCTION_PARAMS)
232 {
233     return QGlib::Private::EmitImpl<R (QGLIB_SIGNAL_IMPL_TEMPLATE_ARGS)>
234                              ::emit(instance, detailedSignal QGLIB_SIGNAL_IMPL_FUNCTION_ARGS);
235 }
236
237 template <typename T, typename R QGLIB_SIGNAL_IMPL_COMMA
238                                  QGLIB_SIGNAL_IMPL_TEMPLATE_PARAMS>
239 SignalHandler Signal::connect(void *instance, const char *detailedSignal,
240                               T *receiver, R (T::*slot)(QGLIB_SIGNAL_IMPL_TEMPLATE_ARGS),
241                               ConnectFlags flags)
242 {
243     boost::function<R (QGLIB_SIGNAL_IMPL_TEMPLATE_ARGS)> f
244                 = boost::bind(slot, receiver QGLIB_SIGNAL_IMPL_COMMA
245                                              QGLIB_SIGNAL_IMPL_BIND_ARGS);
246
247     ClosurePtr closure = QGlib::Private::CppClosure<
248                                         R (QGLIB_SIGNAL_IMPL_TEMPLATE_ARGS),
249                                         boost::function<R (QGLIB_SIGNAL_IMPL_TEMPLATE_ARGS)>
250                                                    >::create(f, flags & PassSender);
251     return connect(instance, detailedSignal, closure, flags);
252 }
253
254 } //namespace QGlib
255
256 # undef QGLIB_SIGNAL_IMPL_BIND_ARGS
257 # undef QGLIB_SIGNAL_IMPL_PACK_ARGS
258 # undef QGLIB_SIGNAL_IMPL_PACK_ARGS_STEP
259 # undef QGLIB_SIGNAL_IMPL_COMMA
260 # undef QGLIB_SIGNAL_IMPL_FUNCTION_ARGS
261 # undef QGLIB_SIGNAL_IMPL_FUNCTION_PARAMS
262 # undef QGLIB_SIGNAL_IMPL_TEMPLATE_ARGS
263 # undef QGLIB_SIGNAL_IMPL_TEMPLATE_PARAMS
264 # undef QGLIB_SIGNAL_IMPL_NUM_ARGS
265
266 #endif // !defined(BOOST_PP_IS_ITERATING) || !BOOST_PP_IS_ITERATING