Cpp / Qt: Reduce boost usage (memory and tuple)
[gpgme.git] / lang / qt / src / threadedjobmixin.h
1 /*
2     threadedjobmixin.h
3
4     This file is part of qgpgme, the Qt API binding for gpgme
5     Copyright (c) 2008 Klarälvdalens Datakonsult AB
6     Copyright (c) 2016 Intevation GmbH
7
8     Libkleopatra is free software; you can redistribute it and/or
9     modify it under the terms of the GNU General Public License as
10     published by the Free Software Foundation; either version 2 of the
11     License, or (at your option) any later version.
12
13     Libkleopatra is distributed in the hope that it will be useful,
14     but WITHOUT ANY WARRANTY; without even the implied warranty of
15     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16     General Public License for more details.
17
18     You should have received a copy of the GNU General Public License
19     along with this program; if not, write to the Free Software
20     Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
21
22     In addition, as a special exception, the copyright holders give
23     permission to link the code of this program with any edition of
24     the Qt library by Trolltech AS, Norway (or with modified versions
25     of Qt that use the same license as Qt), and distribute linked
26     combinations including the two.  You must obey the GNU General
27     Public License in all respects for all of the code used other than
28     Qt.  If you modify this file, you may extend this exception to
29     your version of the file, but you are not obligated to do so.  If
30     you do not wish to do so, delete this exception statement from
31     your version.
32 */
33
34 #ifndef __QGPGME_THREADEDJOBMIXING_H__
35 #define __QGPGME_THREADEDJOBMIXING_H__
36
37 #include <QMutex>
38 #include <QMutexLocker>
39 #include <QThread>
40 #include <QString>
41 #include <QIODevice>
42
43 #ifdef BUILDING_QGPGME
44 # include "context.h"
45 # include "interfaces/progressprovider.h"
46 #else
47 # include <gpgme++/context.h>
48 # include <gpgme++/interfaces/progressprovider.h>
49 #endif
50
51 #include <boost/shared_ptr.hpp>
52 #include <boost/weak_ptr.hpp>
53 #include <boost/bind.hpp>
54 #include <boost/function.hpp>
55 #include <boost/tuple/tuple.hpp>
56 #include <boost/utility/enable_if.hpp>
57 #include <boost/type_traits/is_same.hpp>
58
59 #include <cassert>
60
61 namespace QGpgME
62 {
63 namespace _detail
64 {
65
66 QString audit_log_as_html(GpgME::Context *ctx, GpgME::Error &err);
67
68 class PatternConverter
69 {
70     const QList<QByteArray> m_list;
71     mutable const char **m_patterns;
72 public:
73     explicit PatternConverter(const QByteArray &ba);
74     explicit PatternConverter(const QString &s);
75     explicit PatternConverter(const QList<QByteArray> &lba);
76     explicit PatternConverter(const QStringList &sl);
77     ~PatternConverter();
78
79     const char **patterns() const;
80 };
81
82 class ToThreadMover
83 {
84     QObject *const m_object;
85     QThread *const m_thread;
86 public:
87     ToThreadMover(QObject *o, QThread *t) : m_object(o), m_thread(t) {}
88     ToThreadMover(QObject &o, QThread *t) : m_object(&o), m_thread(t) {}
89     ToThreadMover(const std::shared_ptr<QObject> &o, QThread *t) : m_object(o.get()), m_thread(t) {}
90     ~ToThreadMover()
91     {
92         if (m_object && m_thread) {
93             m_object->moveToThread(m_thread);
94         }
95     }
96 };
97
98 template <typename T_result>
99 class Thread : public QThread
100 {
101 public:
102     explicit Thread(QObject *parent = Q_NULLPTR) : QThread(parent) {}
103
104     void setFunction(const boost::function<T_result()> &function)
105     {
106         const QMutexLocker locker(&m_mutex);
107         m_function = function;
108     }
109
110     T_result result() const
111     {
112         const QMutexLocker locker(&m_mutex);
113         return m_result;
114     }
115
116 private:
117     void run() Q_DECL_OVERRIDE {
118         const QMutexLocker locker(&m_mutex);
119         m_result = m_function();
120     }
121 private:
122     mutable QMutex m_mutex;
123     boost::function<T_result()> m_function;
124     T_result m_result;
125 };
126
127 template <typename T_base, typename T_result = std::tuple<GpgME::Error, QString, GpgME::Error> >
128 class ThreadedJobMixin : public T_base, public GpgME::ProgressProvider
129 {
130 public:
131     typedef ThreadedJobMixin<T_base, T_result> mixin_type;
132     typedef T_result result_type;
133
134 protected:
135     BOOST_STATIC_ASSERT((std::tuple_size<T_result>::value > 2));
136     BOOST_STATIC_ASSERT((
137                             std::is_same <
138                             typename std::tuple_element <
139                             std::tuple_size<T_result>::value - 2,
140                             T_result
141                             >::type,
142                             QString
143                             >::value
144                         ));
145     BOOST_STATIC_ASSERT((
146                             std::is_same <
147                             typename std::tuple_element <
148                             std::tuple_size<T_result>::value - 1,
149                             T_result
150                             >::type,
151                             GpgME::Error
152                             >::value
153                         ));
154
155     explicit ThreadedJobMixin(GpgME::Context *ctx)
156         : T_base(0), m_ctx(ctx), m_thread(), m_auditLog(), m_auditLogError()
157     {
158
159     }
160
161     void lateInitialization()
162     {
163         assert(m_ctx);
164         QObject::connect(&m_thread, SIGNAL(finished()), this, SLOT(slotFinished()));
165         m_ctx->setProgressProvider(this);
166     }
167
168     template <typename T_binder>
169     void run(const T_binder &func)
170     {
171         m_thread.setFunction(boost::bind(func, this->context()));
172         m_thread.start();
173     }
174     template <typename T_binder>
175     void run(const T_binder &func, const std::shared_ptr<QIODevice> &io)
176     {
177         if (io) {
178             io->moveToThread(&m_thread);
179         }
180         // the arguments passed here to the functor are stored in a QThread, and are not
181         // necessarily destroyed (living outside the UI thread) at the time the result signal
182         // is emitted and the signal receiver wants to clean up IO devices.
183         // To avoid such races, we pass std::weak_ptr's to the functor.
184         m_thread.setFunction(boost::bind(func, this->context(), this->thread(), std::weak_ptr<QIODevice>(io)));
185         m_thread.start();
186     }
187     template <typename T_binder>
188     void run(const T_binder &func, const std::shared_ptr<QIODevice> &io1, const std::shared_ptr<QIODevice> &io2)
189     {
190         if (io1) {
191             io1->moveToThread(&m_thread);
192         }
193         if (io2) {
194             io2->moveToThread(&m_thread);
195         }
196         // the arguments passed here to the functor are stored in a QThread, and are not
197         // necessarily destroyed (living outside the UI thread) at the time the result signal
198         // is emitted and the signal receiver wants to clean up IO devices.
199         // To avoid such races, we pass std::weak_ptr's to the functor.
200         m_thread.setFunction(boost::bind(func, this->context(), this->thread(), std::weak_ptr<QIODevice>(io1), std::weak_ptr<QIODevice>(io2)));
201         m_thread.start();
202     }
203     GpgME::Context *context() const
204     {
205         return m_ctx.get();
206     }
207
208     virtual void resultHook(const result_type &) {}
209
210     void slotFinished()
211     {
212         const T_result r = m_thread.result();
213         m_auditLog = std::get < std::tuple_size<T_result>::value - 2 > (r);
214         m_auditLogError = std::get < std::tuple_size<T_result>::value - 1 > (r);
215         resultHook(r);
216         Q_EMIT this->done();
217         doEmitResult(r);
218         this->deleteLater();
219     }
220     void slotCancel() Q_DECL_OVERRIDE {
221         if (m_ctx)
222         {
223             m_ctx->cancelPendingOperation();
224         }
225     }
226     QString auditLogAsHtml() const Q_DECL_OVERRIDE
227     {
228         return m_auditLog;
229     }
230     GpgME::Error auditLogError() const Q_DECL_OVERRIDE
231     {
232         return m_auditLogError;
233     }
234     void showProgress(const char *what, int type, int current, int total) Q_DECL_OVERRIDE {
235         // will be called from the thread exec'ing the operation, so
236         // just bounce everything to the owning thread:
237         // ### hope this is thread-safe (meta obj is const, and
238         // ### portEvent is thread-safe, so should be ok)
239         QMetaObject::invokeMethod(this, "progress", Qt::QueuedConnection,
240         // TODO port
241         Q_ARG(QString, QString()),
242         Q_ARG(int, current),
243         Q_ARG(int, total));
244     }
245 private:
246     template <typename T1, typename T2>
247     void doEmitResult(const std::tuple<T1, T2> &tuple)
248     {
249         Q_EMIT this->result(std::get<0>(tuple), std::get<1>(tuple));
250     }
251
252     template <typename T1, typename T2, typename T3>
253     void doEmitResult(const std::tuple<T1, T2, T3> &tuple)
254     {
255         Q_EMIT this->result(std::get<0>(tuple), std::get<1>(tuple), std::get<2>(tuple));
256     }
257
258     template <typename T1, typename T2, typename T3, typename T4>
259     void doEmitResult(const std::tuple<T1, T2, T3, T4> &tuple)
260     {
261         Q_EMIT this->result(std::get<0>(tuple), std::get<1>(tuple), std::get<2>(tuple), std::get<3>(tuple));
262     }
263
264     template <typename T1, typename T2, typename T3, typename T4, typename T5>
265     void doEmitResult(const std::tuple<T1, T2, T3, T4, T5> &tuple)
266     {
267         Q_EMIT this->result(std::get<0>(tuple), std::get<1>(tuple), std::get<2>(tuple), std::get<3>(tuple), std::get<4>(tuple));
268     }
269
270 private:
271     std::shared_ptr<GpgME::Context> m_ctx;
272     Thread<T_result> m_thread;
273     QString m_auditLog;
274     GpgME::Error m_auditLogError;
275 };
276
277 }
278 }
279
280 #endif /* __QGPGME_THREADEDJOBMIXING_H__ */