Add option (enabled by default) to omit the sender from a slot's arguments. In most...
[qtgstreamer:qtgstreamer.git] / src / QGlib / closure.cpp
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 #include "closure.h"
18 #include "quark.h"
19 #include <glib-object.h>
20 #include <QtCore/QDebug>
21
22 namespace QGlib {
23
24 void Closure::ref()
25 {
26     g_closure_ref(static_cast<GClosure*>(m_object));
27 }
28
29 void Closure::unref()
30 {
31     g_closure_unref(static_cast<GClosure*>(m_object));
32 }
33
34
35 namespace Private {
36
37 static void c_marshaller(GClosure *closure, GValue *returnValue, uint paramValuesCount,
38                          const GValue *paramValues, void *hint, void *data)
39 {
40     Q_UNUSED(data);
41
42     ClosureDataBase *cdata = static_cast<ClosureDataBase*>(closure->data);
43
44     QList<Value> params;
45     //the signal sender is always the first argument. if we are instructed not to pass it
46     //as an argument to the slot, begin converting from paramValues[1]
47     for(uint i = cdata->passSender ? 0 : 1; i<paramValuesCount; ++i) {
48         params.append(Value(&paramValues[i]));
49     }
50
51     try {
52         SharedValue result(returnValue);
53         (*cdata->cppMarshaller)(result, params, cdata);
54     } catch (const std::logic_error & e) {
55         QString signalName;
56         if (hint != NULL) {
57             GSignalInvocationHint *ihint = static_cast<GSignalInvocationHint*>(hint);
58
59             GSignalQuery query;
60             g_signal_query(ihint->signal_id, &query);
61             signalName = QString::fromUtf8(query.signal_name);
62
63             if (ihint->detail != 0) {
64                 Quark q(ihint->detail);
65                 signalName.append(QLatin1String("::"));
66                 signalName.append(q.toString());
67             }
68         }
69
70         QString instanceName;
71         const Value & instanceValue = params.at(0);
72         if (instanceValue.type().isInstantiatable() && instanceValue.canTransformTo(Type::String)) {
73             //instances can be transformed to strings for debugging purposes
74             instanceName = instanceValue.transformTo(Type::String).get<QString>();
75         }
76
77         //attempt to determine the cause of the failure
78         QString msg;
79         try {
80             //dynamic_cast will throw an std::bad_cast if it fails
81             dynamic_cast<const ValueBase::InvalidTypeException &>(e);
82             //cast succeded, e is indeed an InvalidTypeException
83             msg = QLatin1String("One or more of the arguments of the signal are of different "
84                                 "type than the type that the closure expects");
85         } catch (...) {
86             try {
87                 dynamic_cast<const ValueBase::InvalidValueException &>(e);
88                 //cast succeded, e is indeed an InvalidValueException
89                 //this is most likely to happen because the signal returns void
90                 //but the closure returns something non-void. check this first.
91                 if (returnValue == NULL) {
92                     msg = QLatin1String("The signal is defined to return void but the "
93                                         "closure returns something non-void");
94                 } else {
95                     msg = QLatin1String("One of the arguments of the signal was not a valid GValue. "
96                                         "This is most likely a bug in the code that invoked the signal.");
97                 }
98             } catch (...) {
99                 msg = QString::fromAscii(e.what());
100             }
101         }
102
103         qCritical() << "Error during invocation of closure connected to signal"
104                     << signalName << "from object" << instanceName << ":" << msg;
105     }
106 }
107
108 static void closureDestroyNotify(void *data, GClosure *closure)
109 {
110     Q_UNUSED(data);
111     delete static_cast<ClosureDataBase*>(closure->data);
112 }
113
114 ClosurePtr createCppClosure(ClosureDataBase *closureData)
115 {
116     GClosure *closure = g_closure_new_simple(sizeof(GClosure), closureData);
117     g_closure_set_marshal(closure, &c_marshaller);
118     g_closure_add_finalize_notifier(closure, NULL, &closureDestroyNotify);
119     ClosurePtr result = ClosurePtr::wrap(closure);
120     g_closure_sink(closure);
121     return result;
122 }
123
124 } //namespace Private
125 } //namespace QGlib