GCC Code Coverage Report


Directory: ./
File: src/Process.cpp
Date: 2024-01-26 10:40:55
Exec Total Coverage
Lines: 71 302 23.5%
Branches: 47 334 14.1%

Line Branch Exec Source
1 /*****************************************************************************
2 *
3 * Copyright (C) 2009-2022 Florian Pose <fp@igh.de>
4 *
5 * This file is part of the QtPdCom library.
6 *
7 * The QtPdCom library is free software: you can redistribute it and/or modify
8 * it under the terms of the GNU Lesser General Public License as published by
9 * the Free Software Foundation, either version 3 of the License, or (at your
10 * option) any later version.
11 *
12 * The QtPdCom library is distributed in the hope that it will be useful, but
13 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
14 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
15 * License for more details.
16 *
17 * You should have received a copy of the GNU Lesser General Public License
18 * along with the QtPdCom Library. If not, see <http://www.gnu.org/licenses/>.
19 *
20 ****************************************************************************/
21
22 #include "Process.h"
23 #include "MessageManager.h"
24
25 #include <pdcom5/MessageManagerBase.h>
26 #include <pdcom5/Exception.h>
27
28 #ifdef QTPDCOM_HAS_LOGIN_MANAGER
29 #include "LoginManager_p.h"
30 #include "LoginManager.h"
31 #endif
32
33
34 #ifdef __WIN32__
35 #include <windows.h> // GetUserName(), gethostname()
36 #include <lmcons.h> // UNLEN
37 #else
38 #include <unistd.h> // getlogin()
39 #endif
40
41 #include <string>
42 #include <list>
43
44 #include <QTranslator>
45 #include <QFutureInterface>
46 #include <QQueue>
47 #include <QSslSocket>
48 #include <QSslKey>
49 #include <QSslCertificate>
50 #include <QSslConfiguration>
51
52 #include <QtPdCom1.h>
53 #include <git_revision_hash.h>
54
55 #define STR(x) #x
56 #define QUOTE(x) STR(x)
57
58 const char *const QtPdCom::qtpdcom_version_code =
59 QUOTE(QTPDCOM_MAJOR) "." QUOTE(QTPDCOM_MINOR) "." QUOTE(QTPDCOM_RELEASE);
60
61 const char *const QtPdCom::qtpdcom_full_version = GIT_REV;
62
63 #define DEBUG_DATA 0
64
65 using QtPdCom::Process;
66 using VariablePromise = QFutureInterface<PdCom::Variable>;
67 using ListPromise = QFutureInterface<QtPdCom::VariableList>;
68 using PingPromise = QFutureInterface<void>;
69 using ClientStatisticsPromise = QFutureInterface<std::vector<PdCom::ClientStatistics>>;
70
71 /****************************************************************************/
72
73 12 struct Process::Impl
74 {
75 12 Impl(Process *process):
76 process(process),
77 messageManager(),
78 appName("QtPdCom1"),
79 socket(),
80 defaultSslConfig(socket.sslConfiguration()),
81 socketValid(false),
82 connectionState(Disconnected),
83 rxBytes(0),
84
8/16
✓ Branch 2 taken 12 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 12 times.
✗ Branch 6 not taken.
✓ Branch 9 taken 12 times.
✗ Branch 10 not taken.
✓ Branch 14 taken 12 times.
✗ Branch 15 not taken.
✓ Branch 19 taken 12 times.
✗ Branch 20 not taken.
✓ Branch 22 taken 12 times.
✗ Branch 23 not taken.
✓ Branch 27 taken 12 times.
✗ Branch 28 not taken.
✓ Branch 31 taken 12 times.
✗ Branch 32 not taken.
12 txBytes(0)
85 {
86 // set the default process to the always last instance of process
87 12 defaultProcess = process;
88 12 }
89
90 void connectToHost(const QString &address, quint16 port);
91
92 Process * const process;
93
94 QtPdCom::MessageManager messageManager;
95
96 QString appName; /**< Our application name, that is announced to the
97 server. Default: QtPdCom1. */
98
99 QUrl url;
100 SslCaMode caMode = SslCaMode::NoTLS;
101 QList<QSslCertificate> caCertificates;
102 QSslKey privateKey;
103 QSslCertificate privateCert;
104 QSslSocket socket; /**< TCP socket to the process. */
105 // make backup from inital ssl config
106 const QSslConfiguration defaultSslConfig;
107
108 bool socketValid; /**< Connection state of the socket. */
109 ConnectionState connectionState; /**< The current connection state. */
110 QString errorString; /**< Error reason. Set, before error() is
111 emitted. */
112 quint64 rxBytes;
113 quint64 txBytes;
114
115 QQueue<VariablePromise> findVariableQueue;
116 VariablePromise currentFindVariablePromise;
117 QQueue<ListPromise> listVariableQueue;
118 ListPromise currentListPromise;
119 QQueue<PingPromise> pingQueue;
120 QQueue<ClientStatisticsPromise> clientStatisticsQueue;
121
122 static QtPdCom::Process *defaultProcess; /**< last created process
123 is the default process */
124
125 10 void setConnectionState(ConnectionState const state)
126 {
127
1/2
✓ Branch 0 taken 10 times.
✗ Branch 1 not taken.
10 if (connectionState != state) {
128 10 connectionState = state;
129 10 emit process->connectionStatusChanged();
130 }
131 10 }
132 };
133
134 /****************************************************************************/
135
136 QtPdCom::Process *QtPdCom::Process::Impl::defaultProcess = nullptr;
137
138 /*****************************************************************************
139 * Public implementation
140 ****************************************************************************/
141
142 /** Constructor.
143 */
144 12 Process::Process(QObject *parent):
145 QObject(parent),
146 PdCom::Process(),
147
3/6
✓ Branch 2 taken 12 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 12 times.
✗ Branch 6 not taken.
✓ Branch 8 taken 12 times.
✗ Branch 9 not taken.
12 impl{std::unique_ptr<Impl>{new Impl{this}}}
148 {
149
3/6
✓ Branch 2 taken 12 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 12 times.
✗ Branch 6 not taken.
✓ Branch 9 taken 12 times.
✗ Branch 10 not taken.
12 connect(&impl->socket, SIGNAL(connected()),
150 this, SLOT(socketConnected()));
151
3/6
✓ Branch 2 taken 12 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 12 times.
✗ Branch 6 not taken.
✓ Branch 9 taken 12 times.
✗ Branch 10 not taken.
12 connect(&impl->socket, SIGNAL(disconnected()),
152 this, SLOT(socketDisconnected()));
153 #if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0)
154 connect(&impl->socket, &QAbstractSocket::errorOccurred,
155 this, &Process::socketError);
156 #else
157
3/6
✓ Branch 2 taken 12 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 12 times.
✗ Branch 6 not taken.
✓ Branch 9 taken 12 times.
✗ Branch 10 not taken.
12 connect(&impl->socket, SIGNAL(error(QAbstractSocket::SocketError)),
158 this, SLOT(socketError()));
159 #endif
160
3/6
✓ Branch 2 taken 12 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 12 times.
✗ Branch 6 not taken.
✓ Branch 9 taken 12 times.
✗ Branch 10 not taken.
12 connect(&impl->socket, SIGNAL(readyRead()),
161 this, SLOT(socketRead()));
162
163
1/2
✓ Branch 2 taken 12 times.
✗ Branch 3 not taken.
12 setMessageManager(&impl->messageManager);
164 12 }
165
166 /****************************************************************************/
167
168 /** Destructor.
169 */
170 24 Process::~Process()
171 {
172 12 setMessageManager(nullptr);
173
174 // reset default process if this instance is the default
175
2/2
✓ Branch 1 taken 11 times.
✓ Branch 2 taken 1 times.
12 if (impl->defaultProcess == this) {
176 11 impl->defaultProcess = nullptr;
177 }
178
179 12 disconnectFromHost();
180 12 reset();
181 12 }
182
183 /****************************************************************************/
184
185 /** Sets the application name.
186 */
187 void Process::setApplicationName(const QString &name)
188 {
189 impl->appName = name;
190 }
191
192 QString Process::getApplicationName() const
193 {
194 return impl->appName;
195 }
196
197 /****************************************************************************/
198
199 /** Starts to connect to a process.
200 */
201 void Process::connectToHost(const QString &address, quint16 port)
202 {
203 impl->connectToHost(address, port);
204 }
205
206 void Process::Impl::connectToHost(const QString &address, quint16 port)
207 {
208 url.setHost(address);
209 url.setPort(port);
210 url.setScheme(caMode == SslCaMode::NoTLS ? "msr" : "msrs");
211 setConnectionState(Connecting);
212 if (caMode == SslCaMode::NoTLS) {
213 socket.connectToHost(address, port);
214 return;
215 }
216 auto ssl_config = defaultSslConfig;
217 ssl_config.setPeerVerifyMode(QSslSocket::VerifyPeer);
218 if (caMode == SslCaMode::CustomCAs) {
219 ssl_config.setCaCertificates(caCertificates);
220 } else if (caMode == SslCaMode::IgnoreCertificate) {
221 ssl_config.setPeerVerifyMode(QSslSocket::VerifyNone);
222 }
223 if (!privateCert.isNull() && !privateKey.isNull()) {
224 ssl_config.setPrivateKey(privateKey);
225 ssl_config.setLocalCertificate(privateCert);
226 }
227 socket.setSslConfiguration(ssl_config);
228 socket.connectToHostEncrypted(address, port);
229 }
230
231 /****************************************************************************/
232
233 /** Disconnects from a process.
234 */
235 12 void Process::disconnectFromHost()
236 {
237
2/2
✓ Branch 1 taken 5 times.
✓ Branch 2 taken 7 times.
12 switch (impl->connectionState) {
238 5 case Connecting:
239 case Connected:
240 5 impl->socketValid = false;
241 5 impl->setConnectionState(Disconnected);
242 5 asyncData();
243 5 impl->rxBytes = 0;
244 5 impl->txBytes = 0;
245 5 reset();
246 5 impl->socket.disconnectFromHost();
247 5 impl->socket.abort();
248 5 emit disconnected();
249 5 break;
250
251 7 default:
252 7 break;
253 }
254 12 }
255
256 /****************************************************************************/
257
258 /**
259 * \return The connection state.
260 */
261 Process::ConnectionState Process::getConnectionState() const
262 {
263 return impl->connectionState;
264 }
265
266 /****************************************************************************/
267
268 /**
269 * \return \a true, if the process is connected.
270 */
271 bool Process::isConnected() const
272 {
273 return impl->connectionState == Connected;
274 }
275
276 /****************************************************************************/
277
278 /**
279 * \return Error reason after the error() signal was emitted.
280 */
281 const QString &Process::getErrorString() const
282 {
283 return impl->errorString;
284 }
285
286 /****************************************************************************/
287
288 /**
289 * \return Host name of the process.
290 */
291 QString Process::getPeerName() const
292 {
293 return impl->socket.peerName();
294 }
295
296 /****************************************************************************/
297
298 /** Wrapper function for Process::findVariable.
299 */
300
301 3 QFuture<PdCom::Variable> Process::find(const QString &path)
302 {
303
1/2
✓ Branch 4 taken 3 times.
✗ Branch 5 not taken.
3 impl->currentFindVariablePromise = {};
304 3 impl->currentFindVariablePromise.reportStarted();
305 3 auto ans = impl->currentFindVariablePromise.future();
306
4/6
✓ Branch 2 taken 3 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 3 times.
✗ Branch 6 not taken.
✓ Branch 9 taken 1 times.
✓ Branch 10 taken 2 times.
3 if (!PdCom::Process::find(path.toStdString()))
307 // variable was not cached, findReply() will be called later
308
1/2
✓ Branch 3 taken 1 times.
✗ Branch 4 not taken.
1 impl->findVariableQueue.append(impl->currentFindVariablePromise);
309
2/4
✓ Branch 2 taken 3 times.
✗ Branch 3 not taken.
✓ Branch 6 taken 3 times.
✗ Branch 7 not taken.
3 impl->currentFindVariablePromise = {};
310 3 return ans;
311 }
312
313 /****************************************************************************/
314
315 /** Wrapper function for Process::list.
316 */
317
318 QFuture<QtPdCom::VariableList> Process::list(const QString& path)
319 {
320 impl->currentListPromise = {};
321 impl->currentListPromise.reportStarted();
322 auto ans = impl->currentListPromise.future();
323 if (!PdCom::Process::list(path.toStdString()))
324 impl->listVariableQueue.append(impl->currentListPromise);
325 impl->currentListPromise = {};
326 return ans;
327 }
328
329 /****************************************************************************/
330
331 QFuture<std::vector<PdCom::ClientStatistics>>
332 QtPdCom::Process::getClientStatisticsQt()
333 {
334 ClientStatisticsPromise ans;
335 impl->clientStatisticsQueue.append(ans);
336 ans.reportStarted();
337 PdCom::Process::getClientStatistics();
338 return ans.future();
339 }
340
341 /****************************************************************************/
342
343 /** Send a broadcast message.
344 */
345 void Process::sendBroadcast(const QString &msg, const QString &attr)
346 {
347 broadcast(msg.toStdString(), attr.toStdString());
348 }
349
350 /****************************************************************************/
351
352 quint64 Process::getRxBytes() const
353 {
354 return impl->rxBytes;
355 }
356
357 /****************************************************************************/
358
359 quint64 Process::getTxBytes() const
360 {
361 return impl->txBytes;
362 }
363
364 void QtPdCom::Process::setCaMode(SslCaMode mode)
365 {
366 if (impl->caMode != mode)
367 {
368 impl->caMode = mode;
369 impl->url.setScheme(mode == SslCaMode::NoTLS ? "msr" : "msrs");
370 emit sslCaModeChanged();
371 }
372 }
373
374 Process::SslCaMode Process::getCaMode() const
375 {
376 return impl->caMode;
377 }
378
379
380 void QtPdCom::Process::setClientCertificate(
381 const QSslCertificate &cert,
382 const QSslKey &key)
383 {
384 impl->privateKey = key;
385 impl->privateCert = cert;
386 }
387
388 void QtPdCom::Process::setCustomCAs(QList<QSslCertificate> cas)
389 {
390 impl->caCertificates = std::move(cas);
391 }
392
393 /****************************************************************************/
394
395 QtPdCom::Process *Process::getDefaultProcess()
396 {
397 return QtPdCom::Process::Impl::defaultProcess;
398 }
399
400 /****************************************************************************/
401
402 /** Set default process "manually"
403 */
404 void Process::setDefaultProcess(QtPdCom::Process *process)
405 {
406 QtPdCom::Process::Impl::defaultProcess = process;
407 }
408
409 /****************************************************************************/
410
411 10 PdCom::MessageManagerBase *Process::getMessageManager() const
412 {
413 10 return &impl->messageManager;
414 }
415
416 void QtPdCom::Process::setLoginManager(LoginManager *lm)
417 {
418 #ifdef QTPDCOM_HAS_LOGIN_MANAGER
419 if (lm) {
420 setAuthManager(lm->d_ptr.data());
421 } else {
422 setAuthManager(nullptr);
423 }
424 #else
425 throw PdCom::InvalidArgument("build without sasl support");
426 #endif
427 }
428
429 QtPdCom::LoginManager *Process::getLoginManager() const
430 {
431 #ifdef QTPDCOM_HAS_LOGIN_MANAGER
432 if (const auto p = getAuthManager()) {
433 auto *lm_p = static_cast<LoginManagerPrivate*>(p);
434 return lm_p->q_ptr;
435 }
436 return nullptr;
437 #else
438 throw PdCom::InvalidArgument("build without sasl support");
439 #endif
440 }
441
442 /*****************************************************************************
443 * private methods
444 ****************************************************************************/
445
446 std::string Process::applicationName() const
447 {
448 return impl->appName.toStdString();
449 }
450
451 /****************************************************************************/
452
453 std::string Process::hostname() const
454 {
455 char hostname[256];
456 if (!gethostname(hostname, sizeof(hostname))) {
457 return hostname;
458 }
459 return "";
460 }
461
462 /****************************************************************************/
463
464 /** Read data from the socket.
465 */
466 int Process::read(char *buf, int count)
467 {
468 if (impl->connectionState == Disconnected ||
469 impl->connectionState == ConnectedError)
470 return 0;
471 qint64 ret(impl->socket.read(buf, count));
472 if (ret > 0) {
473 impl->rxBytes += ret;
474 }
475 return ret;
476 }
477
478 /****************************************************************************/
479
480 /** Sends data via the socket.
481 *
482 * This is the implementation of the virtual PdCom::Process
483 * method to enable the Process object to send data to the process.
484 */
485 void Process::write(const char *buf, size_t count)
486 {
487 #if DEBUG_DATA
488 qDebug() << "Writing:" << count << QByteArray(buf, count);
489 #endif
490 while (count > 0) {
491 qint64 ret(impl->socket.write(buf, count));
492 if (ret <= 0) {
493 qWarning("write() failed.");
494 impl->socketValid = false;
495 impl->rxBytes = 0;
496 impl->txBytes = 0;
497 reset();
498 impl->socket.disconnectFromHost();
499 emit error();
500 return;
501 }
502 count -= ret;
503 buf += ret;
504 impl->txBytes += ret;
505 }
506 }
507
508 /****************************************************************************/
509
510 /** Flushed the socket.
511 */
512 void Process::flush()
513 {
514 #if DEBUG_DATA
515 qDebug() << "Flushing not implemented.";
516 #endif
517 }
518
519 /****************************************************************************/
520
521 /** The process is connected and ready.
522 *
523 * This virtual function from PdCom::Process has to be overloaded to let
524 * subclasses know about this event.
525 */
526 5 void Process::connected()
527 {
528 5 impl->setConnectionState(Connected);
529 5 emit processConnected();
530 5 }
531
532 /****************************************************************************/
533
534 /** Broadcast Reply.
535 */
536 void Process::broadcastReply(
537 const std::string &message,
538 const std::string &attr,
539 std::chrono::nanoseconds time_ns,
540 const std::string &user)
541
542 {
543 emit broadcastReceived(
544 QString::fromStdString(message),
545 QString::fromStdString(attr),
546 time_ns.count(),
547 QString::fromStdString(user));
548 }
549
550 /****************************************************************************/
551
552 /** Ping Reply.
553 */
554 void Process::pingReply()
555 {
556 if (impl->pingQueue.empty())
557 return;
558
559 auto promise = impl->pingQueue.dequeue();
560 promise.reportFinished();
561 }
562
563 /****************************************************************************/
564
565 QFuture<void> Process::pingQt()
566 {
567 PingPromise promise;
568 promise.reportStarted();
569 impl->pingQueue.push_back(promise);
570 PdCom::Process::ping();
571 return promise.future();
572 }
573
574 /****************************************************************************/
575
576 /** Resets the PdCom process.
577 */
578 17 void Process::reset()
579 {
580 try {
581
1/2
✓ Branch 1 taken 17 times.
✗ Branch 2 not taken.
17 PdCom::Process::reset();
582 } catch (std::exception &e) {
583 // do nothing
584 }
585
586 17 impl->messageManager.reset();
587 // cancel all pending futures
588
1/2
✗ Branch 2 not taken.
✓ Branch 3 taken 17 times.
17 while (!impl->pingQueue.empty())
589 impl->pingQueue.dequeue().reportCanceled();
590
2/2
✓ Branch 2 taken 1 times.
✓ Branch 3 taken 17 times.
1 while (!impl->findVariableQueue.empty())
591
1/2
✓ Branch 4 taken 1 times.
✗ Branch 5 not taken.
1 impl->findVariableQueue.dequeue().reportCanceled();
592
1/2
✗ Branch 2 not taken.
✓ Branch 3 taken 17 times.
17 if (impl->currentFindVariablePromise.isRunning()) {
593 impl->currentFindVariablePromise.reportCanceled();
594 impl->currentFindVariablePromise = {};
595 }
596
597
1/2
✗ Branch 2 not taken.
✓ Branch 3 taken 17 times.
17 while (!impl->listVariableQueue.empty())
598 impl->listVariableQueue.dequeue().reportCanceled();
599
1/2
✗ Branch 2 not taken.
✓ Branch 3 taken 17 times.
17 if (impl->currentListPromise.isRunning()) {
600 impl->currentListPromise.reportCanceled();
601 impl->currentListPromise = {};
602 }
603
1/2
✗ Branch 2 not taken.
✓ Branch 3 taken 17 times.
17 while (!impl->clientStatisticsQueue.empty())
604 impl->clientStatisticsQueue.dequeue().reportCanceled();
605 17 }
606
607 /****************************************************************************/
608
609 /** Socket connection established.
610 *
611 * This is called, when the pure socket connection was established and the
612 * Process object can start using it.
613 */
614 void Process::socketConnected()
615 {
616 impl->socketValid = true;
617 impl->socket.setSocketOption(QAbstractSocket::KeepAliveOption, 1);
618 }
619
620 /****************************************************************************/
621
622 /** Socket disconnected.
623 *
624 * The socket was closed and the process has to be told, that it is
625 * disconnected.
626 */
627 void Process::socketDisconnected()
628 {
629 switch (impl->connectionState) {
630 case Connecting:
631 impl->socketValid = false;
632 impl->setConnectionState(ConnectError);
633 impl->rxBytes = 0;
634 impl->txBytes = 0;
635 reset();
636 emit error();
637 break;
638
639 case Connected:
640 impl->socketValid = false;
641 impl->setConnectionState(Disconnected);
642 asyncData();
643 impl->rxBytes = 0;
644 impl->txBytes = 0;
645 reset();
646 emit disconnected();
647 break;
648
649 default:
650 break;
651 }
652 }
653
654 /****************************************************************************/
655
656 /** There was a socket error.
657 *
658 * The error could come up either while connecting the socket, or when the
659 * socket was already connected.
660 */
661 void Process::socketError()
662 {
663 impl->errorString = impl->socket.errorString();
664
665 switch (impl->connectionState) {
666 case Connecting:
667 impl->socketValid = false;
668 impl->setConnectionState(ConnectError);
669 impl->rxBytes = 0;
670 impl->txBytes = 0;
671 reset();
672 emit error();
673 break;
674
675 case Connected:
676 impl->socketValid = false;
677 impl->setConnectionState(ConnectedError);
678 asyncData();
679 impl->rxBytes = 0;
680 impl->txBytes = 0;
681 reset();
682 emit error();
683 break;
684
685 default:
686 break;
687 }
688 }
689
690 /****************************************************************************/
691
692 /** The socket has new data to read.
693 */
694 void Process::socketRead()
695 {
696 try {
697 while (impl->socket.bytesAvailable() > 0) {
698 asyncData();
699 }
700 }
701 catch (std::exception &e) {
702 impl->errorString = "Exception during asyncData(): ";
703 impl->errorString += e.what();
704 qCritical() << impl->errorString;
705 impl->socketValid = false;
706 if (impl->connectionState == Connected) {
707 impl->setConnectionState(ConnectedError);
708 } else {
709 impl->setConnectionState(ConnectError);
710 }
711 reset();
712 impl->socket.disconnectFromHost();
713 emit error();
714 }
715 catch (...) {
716 impl->errorString = "Unknown exception during asyncData()";
717 qCritical() << impl->errorString;
718 impl->socketValid = false;
719 if (impl->connectionState == Connected) {
720 impl->setConnectionState(ConnectedError);
721 } else {
722 impl->setConnectionState(ConnectError);
723 }
724 reset();
725 impl->socket.disconnectFromHost();
726 emit error();
727 }
728 }
729
730 /****************************************************************************/
731
732
733 2 void Process::findReply(const PdCom::Variable &var)
734 {
735
1/2
✓ Branch 2 taken 2 times.
✗ Branch 3 not taken.
2 if (impl->currentFindVariablePromise.isRunning()) {
736 // variable is cached, result of PdCom::Process::find() will be true
737 2 impl->currentFindVariablePromise.reportResult(var);
738 2 impl->currentFindVariablePromise.reportFinished();
739 }
740 else if (!impl->findVariableQueue.empty()) {
741 auto promise = impl->findVariableQueue.dequeue();
742 promise.reportResult(var);
743 promise.reportFinished();
744 }
745 2 }
746
747 void Process::listReply(const std::vector<PdCom::Variable> vars,
748 const std::vector<std::string> dirs)
749 {
750 ListPromise promise;
751 if (impl->currentListPromise.isRunning())
752 {
753 // list is cached, result of PdCom::Process::list() will be true
754 promise = impl->currentListPromise;
755 } else if (impl->listVariableQueue.empty())
756 return;
757 else
758 promise = impl->listVariableQueue.dequeue();
759 QVector<QString> qt_dirs;
760 qt_dirs.reserve(dirs.size());
761 for (const auto& s : dirs)
762 qt_dirs.append(QString::fromStdString(s));
763 QVector<PdCom::Variable> qt_vars;
764 qt_vars.resize(vars.size());
765 std::copy(vars.begin(), vars.end(), qt_vars.begin());
766 promise.reportResult(VariableList{std::move(qt_dirs), std::move(qt_vars)});
767 promise.reportFinished();
768 }
769
770 QUrl Process::getUrl() const
771 {
772 return impl->url;
773 }
774
775 int Process::getPort() const
776 {
777 return impl->url.port();
778 }
779
780 QString Process::getHost() const
781 {
782 return impl->url.host();
783 }
784
785 void QtPdCom::Process::clientStatisticsReply(
786 std::vector<PdCom::ClientStatistics> statistics)
787 {
788 if (impl->clientStatisticsQueue.empty())
789 return;
790
791 auto promise = impl->clientStatisticsQueue.dequeue();
792 promise.reportResult(statistics);
793 promise.reportFinished();
794 }
795
796 namespace {
797
798 using QtPdCom::details::takes_obj_as_first_parameter;
799
800 struct A {};
801
802 struct F1 {
803 int operator()(A& a, int);
804 };
805 struct F2 {
806 void operator()(int);
807 };
808
809 struct F3 {
810 void operator()();
811 };
812
813 struct F4 {
814 void operator()(A&);
815 };
816
817 template<class Fn, class Arg, class Param, class = void>
818 struct is_well_formed : std::false_type
819 {};
820
821 template<class Fn, class Arg, class Param>
822 struct is_well_formed<
823 Fn, Arg, Param,
824 typename std::enable_if<takes_obj_as_first_parameter<Fn, Arg, Param>() || true>::type
825 > : std::true_type
826 {};
827
828 static_assert(takes_obj_as_first_parameter<F1, A, int>(), "F1 takes object ref and int");
829 static_assert(!takes_obj_as_first_parameter<F2, A, int>(), "F2 only takes int");
830
831
832 static_assert(!takes_obj_as_first_parameter<F3, A>(), "F3 takes no arguments");
833 static_assert(takes_obj_as_first_parameter<F4, A>(), "F4 takes obj ref as argument");
834
835 static_assert(is_well_formed<F1, A, int>::value, "");
836 static_assert(is_well_formed<F2, A, int>::value, "");
837 static_assert(!is_well_formed<F3, A, int>::value,
838 "Mismatch beween expected arguments and actual lambda arguments is detected");
839
840 }
841