QCoro 库提供了一组工具来将 C++20 协程与 Qt 结合使用。
看看下面的例子,你就会知道协程是多么神奇的事情:
QNetworkAccessManager networkAccessManager;// co_await 回复 - 协程被挂起,直到 QNetworkReply 完成。 // 当协程挂起时,*Qt 事件循环照常运行*.const QNetworkReply *reply = co_await networkAccessManager.get(url); // 回复完成后,您的代码将在此处恢复,就好像没有发生任何令人惊奇的事情一样 ;-)const auto data =reply->readAll();
它需要一个支持协程 TS 的编译器,请参阅文档以获取支持的编译器和版本的列表。
文档
QCoro 提供了在 Qt 中轻松使用 C++20 协程所需的工具。该库的基石是QCoro::Task<T>
,它表示一个已执行的协程,并允许其调用者异步等待协程的结果。此外,QCoro 还为常见的 Qt 类型提供了一组包装器,例如QTimer
、 QNetworkReply
、 QDBusPendingCall
、 QFuture
等,允许直接co_await
异步操作。
此外,还有一个神奇的qCoro()
函数,可以包装许多本机 Qt 函数和类型,使它们对协程友好。
请查看文档以获取所有支持的功能和 Qt 类型的完整列表。
QDBusPendingCall
QCoro 可以等待异步 D-Bus 调用完成。无需将QDBusPendingCallWatcher
与 QCoro 一起使用 - 只需co_await
结果即可。在 co_awaiting 期间,Qt 事件循环照常运行。
QDBusInterface remoteServiceInterface{serviceName, objectPath, 接口};const QDBusReply<bool> isReady = co_await remoteServiceInterface.asyncCall(QStringLiteral("isReady"));
完整文档在这里。
QFuture
QFuture 表示异步任务的结果。通常,您必须使用QFutureWatcher
才能在未来准备就绪时收到通知。有了 QCoro,您就可以co_await
了!
const QFuture<int> task1 = QtConcurrent::run(....);const QFuture<int> task2 = QtConcurrent::run(....);const int a = co_await task1;const int b = co_await task2; co_return a + b;
完整文档在这里。
QNetworkReply
使用 Qt 执行网络请求可能会很乏味 - 信号/槽方法会破坏代码流程。链接请求和错误处理很快就会变得混乱,并且您的代码被分解为许多函数。但 QCoro 则不然,您可以简单地co_await
QNetworkReply
完成:
QNetworkAccessManager qnam; QNetworkReply *回复 = qnam.get(QStringLiteral("https://github.com/qcoro/qcoro"));const auto 内容 = co_await 回复; 回复->deleteLater();if (回复->error() != QNetworkReply::NoError) {co_return handleError(reply); }const 自动链接 = findLinkInReturnedHtmlCode(内容); 回复 = qnam.get(link);const auto data = co_await 回复; 回复->deleteLater();if (回复->error() != QNetworkReply::NoError) {co_return handleError(reply); } ...
完整文档在这里。
QTimer
也许您想延迟执行代码一秒钟,也许您想以重复的间隔执行某些代码。这对于co_await
来说变得非常简单:
QTimer定时器; 定时器.setInterval(1s); timer.start();for (int i = 1; i <= 100; ++i) {co_await timer;qDebug() << "等待 " << i << " 秒..."; }qDebug() << "完成!";
完整文档在这里。
QIODevice
QIODevice
是 Qt 中许多类的基类,允许异步写入和读取数据。如何知道有数据可供读取?您可以连接到QIODevice::readyRead()
信号,或者您可以使用 QCoro 和co_await
对象:
socket->write("PING");// 等待“pong” const auto data = co_await socket;co_returncalculateLatency(data);
完整文档在这里。
请查看完整文档以了解更多信息。
有时不可能使用co_await
来处理协程的结果 - 通常是在与不支持协程的第三方代码交互时。在这些场景中,可以将连续回调链接到协程,当协程完成时,该回调将被异步调用。
void RegularFunction() {someCoroutineReturningInt().then([](int result) {// 处理结果}); }
延续回调也可以是协程,整个表达式的结果是 Task,其中 T 是延续的返回类型。因此,可以co_await
整个链,或链接多个.then()
延续。
完整文档在这里。
生成器是一个延迟生成多个值的协程。虽然没有任何特定于 Qt 的内容,但 QCoro 为用户提供了必要的工具,以便在其 Qt 应用程序中创建自定义生成器。
QCoro 为同步生成器 ( QCoro::Generator<T>
) 和异步生成器 ( QCoro::AsyncGenerator<T>
) 提供 API。两个生成器都提供类似容器的 API: begin()
和end()
成员函数返回类似迭代器的对象,这是众所周知且已建立的 API,并使生成器与现有算法兼容。
QCoro::Generator<int> 斐波那契() { quint64 a = 0,b = 0; Q_FOREVER {co_yield b;const auto tmp = b; a = b; b += tmp; } }void printFib(quint64 max) {for (auto fib : fibonacci()) {if (fib > max) {break; } std::cout << fib << std::endl; } }
完整文档在这里。
MIT License
Copyright (c) 2022 Daniel Vrátil <[email protected]>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.