GCC Code Coverage Report


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