{"id":239,"date":"2026-02-09T22:38:02","date_gmt":"2026-02-09T21:38:02","guid":{"rendered":"https:\/\/www.arnorehn.de\/blog\/?p=239"},"modified":"2026-02-11T07:05:08","modified_gmt":"2026-02-11T06:05:08","slug":"qfuture-c-coroutines","status":"publish","type":"post","link":"https:\/\/www.arnorehn.de\/blog\/2026\/02\/09\/qfuture-c-coroutines\/","title":{"rendered":"QFuture &#x2764;&#xfe0f; C++ coroutines"},"content":{"rendered":"\n<p>Ever since C++20 introduced coroutine support, I was wondering how this could integrate with Qt. Apparently I wasn&#8217;t the only one: before long, <a href=\"https:\/\/qcoro.dev\/\">QCoro<\/a> popped up. A really cool library! But it doesn&#8217;t use the existing future and promise types in Qt; instead it introduces its own types and mechanisms to support coroutines. I kept wondering why no-one just made QFuture and QPromise compatible &#8211; it would certainly be a more lightweight wrapper then.<\/p>\n\n\n\n<p>With a recent project at work being a gargantuan mess of <code>QFuture::then()<\/code> continuations (ever tried async looping constructs with continuations only? &#x1f974;) I had enough of a reason to finally sit down and implement this myself. The result: <a href=\"https:\/\/gitlab.com\/pumphaus\/qawaitablefuture\">https:\/\/gitlab.com\/pumphaus\/qawaitablefuture<\/a>.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Example<\/h2>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"cpp\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">#include &lt;qawaitablefuture\/qawaitablefuture.h>\n\nQFuture&lt;QByteArray> fetchUrl(const QUrl &amp;url)\n{\n    QNetworkAccessManager nam;\n    QNetworkRequest request(url);\n\n    QNetworkReply *reply = nam.get(request);\n\n    co_await QtFuture::connect(reply, &amp;QNetworkReply::finished);\n    reply->deleteLater();\n\n    if (reply->error()) {\n        throw std::runtime_error(reply->errorString().toStdString());\n    }\n    co_return reply->readAll();\n}<\/pre>\n\n\n\n<p>It looks a lot like what you&#8217;d write with QCoro, but it all fits in a single header and uses native <a href=\"https:\/\/doc.qt.io\/qt-6\/qfuture.html\">QFuture<\/a> features to &#8211; for example &#8211; connect to a signal. It&#8217;s really just syntax sugar around <code>QFuture::then()<\/code>. Well, that, and a bit of effort to propagate cancellation and exceptions. Cancellation propagation works both ways: if you <code>co_await<\/code> a canceled <code>QFuture<\/code>, the &#8220;outer&#8221; <code>QFuture<\/code> of coroutine will be canceled as well. If you <a href=\"https:\/\/doc.qt.io\/qt-6\/qfuture.html#cancelChain\">cancelChain()<\/a> a suspended coroutine-backed <code>QFuture<\/code>, cancellation will be propagated into the currently awaited <code>QFuture<\/code>.<\/p>\n\n\n\n<p>What&#8217;s especially neat: You can configure where your coroutine will be resumed with <code>co_await continueOn(...)<\/code>.  It supports the same arguments as <a href=\"https:\/\/doc.qt.io\/qt-6\/qfuture.html#then-1\">QFuture::then()<\/a>, so for example:<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">QFuture&lt;void> SomeClass::someMember()\n{\n    co_await QAwaitableFuture::continueOn(this);\n    co_await someLongRunningProcess();\n    \/\/ Due to continueOn(this), if \"this\" is destroyed during someLongRunningProcess(),\n    \/\/ the coroutine will be destroyed after the suspension point (-> outer QFuture will be canceled)\n    \/\/ and you won't access a dangling reference here.\n    co_return this->frobnicate();\n}\n\nQFuture&lt;int> multithreadedProcess()\n{\n    co_await QAwaitableFuture::continueOn(QtFuture::Launch::Async);\n\n    double result1 = co_await foo();\n    \/\/ resumes on a free thread in the thread pool\n    process(result1);\n\n    double result2 = co_await bar(result1);\n    \/\/ resumes on a free thread in the thread pool\n    double result3 = transmogrify(result2);\n\n    co_return co_await baz(result3);\n}<\/pre>\n\n\n\n<p>See the docs for <a href=\"https:\/\/doc.qt.io\/qt-6\/qfuture.html#then-3\">QFuture::then()<\/a> for details.<\/p>\n\n\n\n<p>Also, if you want to check the canceled flag or report progress, you can access the actual QPromise that&#8217;s backing the coroutine:<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">QFuture&lt;int> heavyComputation()\n{\n    QPromise&lt;int> &amp;promise = co_await QAwaitableFuture::promise();\n    promise.setProgressRange(0, 100);\n\n    double result = 0;\n\n    for (int i = 0; i &lt; 100; ++i) {\n        promise.setProgressValue(i);\n        if (promise.isCanceled()) {\n            co_return result;\n        }\n        frobnicationStep(&amp;result, i);\n    }\n    co_return result;\n} \n<\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">Outlook<\/h2>\n\n\n\n<p>I&#8217;m looking to upstream this. It&#8217;s too late for Qt 6.11 (already in feature freeze), but maybe 6.12? There have been some proposals for coroutine support on Qt&#8217;s Gerrit already, but none made it past the proof-of-concept stage. Hopefully this one will make it. Let&#8217;s see.<\/p>\n\n\n\n<p>Otherwise, just use the single header from the <a href=\"https:\/\/gitlab.com\/pumphaus\/qawaitablefuture\">qawaitablefuture repo<\/a>. It an be included as a git submodule, or you just vendor the header as-is.<\/p>\n\n\n\n<p>Happy hacking!<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Caveat: GCC &lt; 13<\/h2>\n\n\n\n<p>There was a nasty bug in GCC&#8217;s coroutine support: <a href=\"https:\/\/gcc.gnu.org\/bugzilla\/show_bug.cgi?id=101367\">https:\/\/gcc.gnu.org\/bugzilla\/show_bug.cgi?id=101367<\/a> It affects all GCC versions before 13.0.0 and effectively prevents you from writing <code>co_await foo([&amp;] { ... });<\/code> &#8211; i.e. you cannot await an expression involving a temporary lambda. You can rewrite this out as <code>auto f = foo([&amp;] { ... }); co_await f;<\/code> and it will work. But there&#8217;s no warning at compile time. As soon as the lambda with captures is a temporary expression inside the co_await, it will crash and burn at runtime. Fixed with GCC13+, but took me a while to figure out why things went haywire on Ubuntu 22.04 (defaults to GCC11).<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Ever since C++20 introduced coroutine support, I was wondering how this could integrate with Qt. Apparently I wasn&#8217;t the only one: before long, QCoro popped up. A really cool library! But it doesn&#8217;t use the existing future and promise types in Qt; instead it introduces its own types and mechanisms to support coroutines. I kept [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"closed","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[30,32,1],"tags":[37,39,40,42,41,22],"class_list":["post-239","post","type-post","status-publish","format-standard","hentry","category-programming","category-qt","category-uncategorized","tag-c-2","tag-c20","tag-coroutine","tag-gcc","tag-qfuture","tag-qt"],"_links":{"self":[{"href":"https:\/\/www.arnorehn.de\/blog\/wp-json\/wp\/v2\/posts\/239","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.arnorehn.de\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.arnorehn.de\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.arnorehn.de\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.arnorehn.de\/blog\/wp-json\/wp\/v2\/comments?post=239"}],"version-history":[{"count":10,"href":"https:\/\/www.arnorehn.de\/blog\/wp-json\/wp\/v2\/posts\/239\/revisions"}],"predecessor-version":[{"id":249,"href":"https:\/\/www.arnorehn.de\/blog\/wp-json\/wp\/v2\/posts\/239\/revisions\/249"}],"wp:attachment":[{"href":"https:\/\/www.arnorehn.de\/blog\/wp-json\/wp\/v2\/media?parent=239"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.arnorehn.de\/blog\/wp-json\/wp\/v2\/categories?post=239"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.arnorehn.de\/blog\/wp-json\/wp\/v2\/tags?post=239"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}