<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en-us"><link rel="self" href="https://dvratil.cz/tag/kde//feed.xml" type="application/atom+xml"/><link rel="alternate" href="https://dvratil.cz/tag/kde/" tyle="text/html"/><id>https://dvratil.cz/tag/kde/</id><updated>2026-02-13T00:00:00+0000</updated><title>Daniel Vrátil's blog kde category feed</title><subtitle>Because opensource matters.</subtitle><author><name>Daniel Vrátil</name><email>me@dvratil.cz</email><uri>https://dvratil.cz/</uri></author><entry><title>QCoro 0.13.0 Release Announcement</title><link rel="alternate" href="https://dvratil.cz/2026/02/qcoro-0.13.0-release-announcement/"/><id>https://dvratil.cz/2026/02/qcoro-0.13.0-release-announcement/</id><published>2026-02-13T00:00:00+0000</published><updated>2026-02-13T00:00:00+0000</updated><category term="QCoro"/><summary>&lt;!--
SPDX-FileCopyrightText: 2026 Daniel Vrátil &lt;dvratil@kde.org&gt;
SPDX-License-Identifier: GFDL-1.3-or-later
--&gt;
&lt;p&gt;This release brings improvements to generators, better build system integration and
several bugfixes.&lt;/p&gt;
&lt;p&gt;As always, big thanks to everyone who reported issues and contributed to QCoro.
Your help is much appreciated!&lt;/p&gt;
&lt;h2 id="directly-awaiting-qt-types-in-asyncgenerator-coroutines"&gt;Directly Awaiting Qt Types in AsyncGenerator Coroutines&lt;/h2&gt;
&lt;p&gt;The biggest improvement in this release is that &lt;code&gt;QCoro::AsyncGenerator&lt;/code&gt; coroutines now support
directly &lt;code&gt;co_await&lt;/code&gt;ing Qt types without the &lt;code&gt;qCoro()&lt;/code&gt; wrapper, just like &lt;code&gt;QCoro::Task&lt;/code&gt; coroutines
already do (&lt;a href="https://github.com/qcoro/qcoro/issues/292"&gt;#292&lt;/a&gt;).&lt;/p&gt;</summary><content type="html" xml:base="https://dvratil.cz/2026/02/qcoro-0.13.0-release-announcement/">&lt;!--
SPDX-FileCopyrightText: 2026 Daniel Vrátil &lt;dvratil@kde.org&gt;
SPDX-License-Identifier: GFDL-1.3-or-later
--&gt;
&lt;p&gt;This release brings improvements to generators, better build system integration and
several bugfixes.&lt;/p&gt;
&lt;p&gt;As always, big thanks to everyone who reported issues and contributed to QCoro.
Your help is much appreciated!&lt;/p&gt;
&lt;h2 id="directly-awaiting-qt-types-in-asyncgenerator-coroutines"&gt;Directly Awaiting Qt Types in AsyncGenerator Coroutines&lt;/h2&gt;
&lt;p&gt;The biggest improvement in this release is that &lt;code&gt;QCoro::AsyncGenerator&lt;/code&gt; coroutines now support
directly &lt;code&gt;co_await&lt;/code&gt;ing Qt types without the &lt;code&gt;qCoro()&lt;/code&gt; wrapper, just like &lt;code&gt;QCoro::Task&lt;/code&gt; coroutines
already do (&lt;a href="https://github.com/qcoro/qcoro/issues/292"&gt;#292&lt;/a&gt;).&lt;/p&gt;
&lt;p&gt;Previously, if you wanted to await a &lt;code&gt;QNetworkReply&lt;/code&gt; inside an &lt;code&gt;AsyncGenerator&lt;/code&gt;, you had to
wrap it with &lt;code&gt;qCoro()&lt;/code&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-cpp" data-lang="cpp"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;QCoro&lt;span style="color:#f92672"&gt;::&lt;/span&gt;AsyncGenerator&lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt;QByteArray&lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt; fetchPages(QNetworkAccessManager &lt;span style="color:#f92672"&gt;&amp;amp;&lt;/span&gt;nam, QStringList urls) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;for&lt;/span&gt; (&lt;span style="color:#66d9ef"&gt;const&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;auto&lt;/span&gt; &lt;span style="color:#f92672"&gt;&amp;amp;&lt;/span&gt;url : urls) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;auto&lt;/span&gt; &lt;span style="color:#f92672"&gt;*&lt;/span&gt;reply &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;co_await&lt;/span&gt; qCoro(nam.get(QNetworkRequest{QUrl{url}}));
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;co_yield&lt;/span&gt; reply&lt;span style="color:#f92672"&gt;-&amp;gt;&lt;/span&gt;readAll();
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Starting with QCoro 0.13.0, you can &lt;code&gt;co_await&lt;/code&gt; directly, just like in &lt;code&gt;QCoro::Task&lt;/code&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-cpp" data-lang="cpp"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;QCoro&lt;span style="color:#f92672"&gt;::&lt;/span&gt;AsyncGenerator&lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt;QByteArray&lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt; fetchPages(QNetworkAccessManager &lt;span style="color:#f92672"&gt;&amp;amp;&lt;/span&gt;nam, QStringList urls) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;for&lt;/span&gt; (&lt;span style="color:#66d9ef"&gt;const&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;auto&lt;/span&gt; &lt;span style="color:#f92672"&gt;&amp;amp;&lt;/span&gt;url : urls) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;auto&lt;/span&gt; &lt;span style="color:#f92672"&gt;*&lt;/span&gt;reply &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;co_await&lt;/span&gt; nam.get(QNetworkRequest{QUrl{url}});
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;co_yield&lt;/span&gt; reply&lt;span style="color:#f92672"&gt;-&amp;gt;&lt;/span&gt;readAll();
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="other-features-and-changes"&gt;Other Features and Changes&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Generator&amp;rsquo;s &lt;code&gt;.end()&lt;/code&gt; method is now &lt;code&gt;const&lt;/code&gt; (and &lt;code&gt;constexpr&lt;/code&gt;), so it can be called on const generator objects (&lt;a href="https://github.com/qcoro/qcoro/issues/294"&gt;#294&lt;/a&gt;).&lt;/li&gt;
&lt;li&gt;&lt;code&gt;GeneratorIterator&lt;/code&gt; can now be constructed in an invalid state, allowing lazy initialization of iterators (&lt;a href="https://github.com/qcoro/qcoro/issues/318"&gt;#318&lt;/a&gt;).&lt;/li&gt;
&lt;li&gt;&lt;code&gt;qcoro.h&lt;/code&gt; now only includes QtNetwork and QtDBus headers when those features are actually enabled, resulting in cleaner builds when optional modules are disabled (&lt;a href="https://github.com/qcoro/qcoro/issues/280"&gt;#280&lt;/a&gt;).&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="bugfixes"&gt;Bugfixes&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Fixed memory leak in QFuture coro wrapper when a task is destroyed while awaiting on a QFuture (&lt;a href="https://github.com/qcoro/qcoro/issues/312"&gt;#312&lt;/a&gt;, Daniel Vrátil)&lt;/li&gt;
&lt;li&gt;Fixed include paths when using QCoro with CMake&amp;rsquo;s FetchContent (&lt;a href="https://github.com/qcoro/qcoro/issues/282"&gt;#282&lt;/a&gt;, Daniel Vrátil; &lt;a href="https://github.com/qcoro/qcoro/pull/310"&gt;#310&lt;/a&gt;, Nicolas Fella)&lt;/li&gt;
&lt;li&gt;Fixed QCoroNetworkReply test on Qt 6.10 (&lt;a href="https://github.com/qcoro/qcoro/pull/305"&gt;#305&lt;/a&gt;, Daniel Vrátil)&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="full-changelog"&gt;Full changelog&lt;/h2&gt;
&lt;p&gt;&lt;a href="https://github.com/danvratil/qcoro/releases/tag/v0.13.0"&gt;See changelog on Github&lt;/a&gt;&lt;/p&gt;
&lt;h2 id="support"&gt;Support&lt;/h2&gt;
&lt;p&gt;If you enjoy using QCoro, consider supporting its development on &lt;a href="https://github.com/sponsors/danvratil"&gt;GitHub Sponsors&lt;/a&gt; or buy me a coffee
on &lt;a href="https://ko-fi.com/danvratil"&gt;Ko-fi&lt;/a&gt; (after all, more coffee means more code, right?).&lt;/p&gt;</content></entry><entry><title>Old New Blog</title><link rel="alternate" href="https://dvratil.cz/2024/12/old-new-blog/"/><id>https://dvratil.cz/2024/12/old-new-blog/</id><published>2024-12-20T16:55:18+0100</published><updated>2024-12-20T16:55:18+0100</updated><category term="Random Blurbs"/><summary>&lt;p&gt;I started this blog back in 2010. Back then I used Wordpress and it worked reasonably well. In 2018 I decided to switch to a static generated site, mostly because the Wordpress blog felt slow to load and it was hassle to maintain. Back then the go-to static site generator was Jekyll, so I went with that. Lately I&amp;rsquo;ve been struggling with it though, because in order to keep all the plugins working, I needed to use older versions or Ruby, which meant I had to use Docker to build the blog locally. Overall, it felt like too much work and for the past few years I&amp;rsquo;ve been eyeing Hugo - more so since Carl and others migrated most of KDE websites to it. I mean, if it&amp;rsquo;s good enough for KDE, it&amp;rsquo;s good enough for me, right?&lt;/p&gt;</summary><content type="html" xml:base="https://dvratil.cz/2024/12/old-new-blog/">&lt;p&gt;I started this blog back in 2010. Back then I used Wordpress and it worked reasonably well. In 2018 I decided to switch to a static generated site, mostly because the Wordpress blog felt slow to load and it was hassle to maintain. Back then the go-to static site generator was Jekyll, so I went with that. Lately I&amp;rsquo;ve been struggling with it though, because in order to keep all the plugins working, I needed to use older versions or Ruby, which meant I had to use Docker to build the blog locally. Overall, it felt like too much work and for the past few years I&amp;rsquo;ve been eyeing Hugo - more so since Carl and others migrated most of KDE websites to it. I mean, if it&amp;rsquo;s good enough for KDE, it&amp;rsquo;s good enough for me, right?&lt;/p&gt;
&lt;p&gt;So this year I finally got around to do the switch. I migrated all the content from Jekyll. This time I actually went through every single post, converted it to proper Markdown, fixed formatting, images etc. It was a nice trip down the memory lane, reading all the old posts, remembering all the sprints and Akademies&amp;hellip; I also took the opportunity to clean up the tags and categories, so that they are more consistent and useful.&lt;/p&gt;
&lt;p&gt;Finally, I have rewritten the theme - I originally ported the template from Wordpress to Jekyll, but it was a bit of a mess, responsivity was &amp;ldquo;hacked&amp;rdquo; in via JavaScript. Web development (and my skills) has come a long way since then, so I was able to leverage more modern CSS and HTML features to make the site look the same, but be more responsive and accessible.&lt;/p&gt;
&lt;h2 id="comments"&gt;Comments&lt;/h2&gt;
&lt;p&gt;When I switched from Wordpress to Jekyll, I was looking for a way to preserve comments. I found &lt;a href="https://isso-comments.de/"&gt;Isso&lt;/a&gt;, which is basically a small CGI server backed with SQLite that you can run on the server and embed it into your static website through JavaScript. It could also natively import comments from Wordpress, so that&amp;rsquo;s the main reason why I went with it, I think. Isso was not perfect (although the development has picked up again in the past few years) and it kept breaking for me. I think it haven&amp;rsquo;t worked for the past few years on my blog and I just couldn&amp;rsquo;t be bothered to fix it. So, I decided to ditch it in favor of another solution&amp;hellip;&lt;/p&gt;
&lt;p&gt;I wanted to keep the comments for old posts by generating them as static HTML from the Isso&amp;rsquo;s SQLite database, alas the database file was empty. Looks like I lost all comments at some point in 2022. It sucks, but I guess it&amp;rsquo;s not the end of the world. Due to the nature of how Isso worked, not even the Wayback Machine was able to archive the comments, so I guess they are lost forever&amp;hellip;&lt;/p&gt;
&lt;p&gt;For this new blog, I decided to use Carl&amp;rsquo;s approach with &lt;a href="https://carlschwan.eu/2020/12/29/adding-comments-to-your-static-blog-with-mastodon/" title="Carl Schwan - Adding comments to your static blog with Mastodon"&gt;embedding replies to a Mastodon&lt;/a&gt;. I think it&amp;rsquo;s a neat idea and it&amp;rsquo;s probably the most reliable solution for comments on a static blog (that I don&amp;rsquo;t have to pay for, host myself or deal with privacy concerns or advertising).&lt;/p&gt;
&lt;p&gt;I have some more ideas regarding the comments system, but that&amp;rsquo;s for another post ;-) Hopefully I&amp;rsquo;ll get to blog more often now that I have a shiny new blog!&lt;/p&gt;
&lt;h2 id="happy-holidays-"&gt;Happy Holidays 🎄&lt;/h2&gt;
&lt;p&gt;Enjoy the holidays and see you in 2025 🥳!&lt;/p&gt;</content></entry><entry><title>QCoro 0.11.0 Release Announcement</title><link rel="alternate" href="https://dvratil.cz/2024/10/qcoro-0.11.0-release-announcement/"/><id>https://dvratil.cz/2024/10/qcoro-0.11.0-release-announcement/</id><published>2024-10-04T12:00:00+0000</published><updated>2024-10-04T12:00:00+0000</updated><category term="QCoro"/><summary>&lt;!--
SPDX-FileCopyrightText: 2024 Daniel Vrátil &lt;dvratil@kde.org&gt;
SPDX-License-Identifier: GFDL-1.3-or-later
--&gt;
&lt;p&gt;A long over-due release which has accumulated a bunch of bugfixes but also some
fancy new features&amp;hellip;read on!&lt;/p&gt;
&lt;p&gt;As always, big thanks to everyone who reported issues and contributed to QCoro.
Your help is much appreciated!&lt;/p&gt;
&lt;h2 id="qcorolazytaskt"&gt;&lt;code&gt;QCoro::LazyTask&amp;lt;T&amp;gt;&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;The biggest new features in this release is the brand-new &lt;a href="https://qcoro.dev/reference/coro/lazytask"&gt;&lt;code&gt;QCoro::LazyTask&amp;lt;T&amp;gt;&lt;/code&gt;&lt;/a&gt;.
It&amp;rsquo;s a new return type that you can use for your coroutines. It differs from &lt;code&gt;QCoro::Task&amp;lt;T&amp;gt;&lt;/code&gt;
in that, as the name suggest, the coroutine is evaluated lazily. What that means is when
you call a coroutine that returns &lt;code&gt;LazyTask&lt;/code&gt;, it will return imediately without executing
the body of the coroutine. The body will be executed only once you &lt;code&gt;co_await&lt;/code&gt; on the returned
&lt;code&gt;LazyTask&lt;/code&gt; object.&lt;/p&gt;</summary><content type="html" xml:base="https://dvratil.cz/2024/10/qcoro-0.11.0-release-announcement/">&lt;!--
SPDX-FileCopyrightText: 2024 Daniel Vrátil &lt;dvratil@kde.org&gt;
SPDX-License-Identifier: GFDL-1.3-or-later
--&gt;
&lt;p&gt;A long over-due release which has accumulated a bunch of bugfixes but also some
fancy new features&amp;hellip;read on!&lt;/p&gt;
&lt;p&gt;As always, big thanks to everyone who reported issues and contributed to QCoro.
Your help is much appreciated!&lt;/p&gt;
&lt;h2 id="qcorolazytaskt"&gt;&lt;code&gt;QCoro::LazyTask&amp;lt;T&amp;gt;&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;The biggest new features in this release is the brand-new &lt;a href="https://qcoro.dev/reference/coro/lazytask"&gt;&lt;code&gt;QCoro::LazyTask&amp;lt;T&amp;gt;&lt;/code&gt;&lt;/a&gt;.
It&amp;rsquo;s a new return type that you can use for your coroutines. It differs from &lt;code&gt;QCoro::Task&amp;lt;T&amp;gt;&lt;/code&gt;
in that, as the name suggest, the coroutine is evaluated lazily. What that means is when
you call a coroutine that returns &lt;code&gt;LazyTask&lt;/code&gt;, it will return imediately without executing
the body of the coroutine. The body will be executed only once you &lt;code&gt;co_await&lt;/code&gt; on the returned
&lt;code&gt;LazyTask&lt;/code&gt; object.&lt;/p&gt;
&lt;p&gt;This is different from the behavior of &lt;code&gt;QCoro::Task&amp;lt;T&amp;gt;&lt;/code&gt;, which is eager, meaning that it will
start executing the body immediately when called (like a regular function call).&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-cpp" data-lang="cpp"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;QCoro&lt;span style="color:#f92672"&gt;::&lt;/span&gt;LazyTask&lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;int&lt;/span&gt;&lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt; myWorker()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; qDebug() &lt;span style="color:#f92672"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;Starting worker&amp;#34;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;co_return&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;42&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;QCoro&lt;span style="color:#f92672"&gt;::&lt;/span&gt;Task&lt;span style="color:#f92672"&gt;&amp;lt;&amp;gt;&lt;/span&gt; mainCoroutine()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; qDebug() &lt;span style="color:#f92672"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;Creating worker&amp;#34;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;const&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;auto&lt;/span&gt; task &lt;span style="color:#f92672"&gt;=&lt;/span&gt; myWorker();
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; qDebug() &lt;span style="color:#f92672"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;Awaiting on worker&amp;#34;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;const&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;auto&lt;/span&gt; result &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;co_await&lt;/span&gt; task;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// do something with the result
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This will result in the following output:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-plain" data-lang="plain"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;mainCoroutine(): Creating worker
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;mainCoroutine(): Awaiting on worker
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;myWorker(): Starting worker
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;If &lt;code&gt;myWorker()&lt;/code&gt; were a &lt;code&gt;QCoro::Task&amp;lt;T&amp;gt;&lt;/code&gt; as we know it, the output would look like this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-plain" data-lang="plain"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;mainCoroutine(): Creating worker
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;myWorker(): Starting worker
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;mainCoroutine(): Awaiting on worker
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The fact that the body of a &lt;code&gt;QCoro::LazyTask&amp;lt;T&amp;gt;&lt;/code&gt; coroutine is only executed when &lt;code&gt;co_await&lt;/code&gt;ed has one
very important implication: &lt;strong&gt;it must not be used for Qt slots&lt;/strong&gt;, &lt;code&gt;Q_INVOKABLE&lt;/code&gt;s or, in general, for any
coroutine that may be executed directly by the Qt event loop. The reason is, that the Qt event loop
is not aware of coroutines (or QCoro), so it will never &lt;code&gt;co_await&lt;/code&gt; on the returned &lt;code&gt;QCoro::LazyTask&lt;/code&gt;
object - which means that the code inside the coroutine would never get executed. This is the
reason why the good old &lt;code&gt;QCoro::Task&amp;lt;T&amp;gt;&lt;/code&gt; is an eager coroutine - to ensure the body of the coroutine
gets executed even when called from the Qt event loop and not &lt;code&gt;co_await&lt;/code&gt;ed.&lt;/p&gt;
&lt;p&gt;For more details, see the &lt;a href="https://qcoro.dev/reference/coro/lazytask"&gt;documentation of &lt;code&gt;QCoro::LazyTask&amp;lt;T&amp;gt;&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id="defined-semantics-for-awaiting-default-constructed-and-moved-from-tasks"&gt;Defined Semantics for Awaiting Default-Constructed and Moved-From Tasks&lt;/h2&gt;
&lt;p&gt;This is something that wasn&amp;rsquo;t clearely defined until now (both in the docs and in the code), which is
what happens when you try to &lt;code&gt;co_await&lt;/code&gt; on a default-constructed &lt;code&gt;QCoro::Task&amp;lt;T&amp;gt;&lt;/code&gt; (or &lt;code&gt;QCoro::LazyTask&amp;lt;T&amp;gt;&lt;/code&gt;):&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-cpp" data-lang="cpp"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;co_await&lt;/span&gt; QCoro&lt;span style="color:#f92672"&gt;::&lt;/span&gt;Task&lt;span style="color:#f92672"&gt;&amp;lt;&amp;gt;&lt;/span&gt;(); &lt;span style="color:#75715e"&gt;// will hang indefinitely!
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Previously this would trigger a &lt;code&gt;Q_ASSERT&lt;/code&gt; in debug build and most likely a crash in production build.
Starting with QCoro 0.11, awaiting such task will print a &lt;code&gt;qWarning()&lt;/code&gt; and will hang indefinitely.&lt;/p&gt;
&lt;p&gt;The same applies to awaiting a moved-from task, which is identical to a default-constructed task:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-cpp" data-lang="cpp"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;QCoro&lt;span style="color:#f92672"&gt;::&lt;/span&gt;LazyTask&lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;int&lt;/span&gt;&lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt; task &lt;span style="color:#f92672"&gt;=&lt;/span&gt; myTask();
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;handleTask(std&lt;span style="color:#f92672"&gt;::&lt;/span&gt;move(task));
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;co_await&lt;/span&gt; task; &lt;span style="color:#75715e"&gt;// will hang indefinitely!`
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="compiler-support"&gt;Compiler Support&lt;/h2&gt;
&lt;p&gt;We have dropped official support for older compilers. Since QCoro 0.11, the officially supported compilers are:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;GCC &amp;gt;= 11&lt;/li&gt;
&lt;li&gt;Clang &amp;gt;= 15&lt;/li&gt;
&lt;li&gt;MSVC &amp;gt;= 19.40 (Visual Studio 17 2022)&lt;/li&gt;
&lt;li&gt;AppleClang &amp;gt;= 15 (Xcode 15.2)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;QCoro might still compile or work with older versions of those compilers, but we no longer test it and
do not guarantee that it will work correctly.&lt;/p&gt;
&lt;p&gt;The reason is that coroutine implementation in older versions of GCC and clang were buggy and behaved differently
than they do in newer versions, so making sure that QCoro behaves correctly across wide range of compilers was
getting more difficult as we implemented more and more complex and advanced features.&lt;/p&gt;
&lt;h2 id="other-features-and-changes"&gt;Other Features and Changes&lt;/h2&gt;
&lt;p&gt;A coroutine-friendly version of &lt;a href="https://doc.qt.io/qt-6/qfuture.html#takeResult"&gt;&lt;code&gt;QFuture::takeResult()&lt;/code&gt;&lt;/a&gt; is now available in the
form of &lt;a href="https://qcoro.dev/reference/core/qfuture#takeResult"&gt;&lt;code&gt;QCoroFuture::takeResult()&lt;/code&gt;&lt;/a&gt; when building QCoro against Qt 6 (&lt;a href="https://github.com/danvratil/qcoro/issues/217"&gt;#217&lt;/a&gt;).&lt;/p&gt;
&lt;p&gt;&lt;code&gt;QCoro::waitFor(QCoro::Task&amp;lt;T&amp;gt;)&lt;/code&gt; no longer requires that the task return type &lt;code&gt;T&lt;/code&gt; is default-constructible (&lt;a href="https://github.com/danvratil/qcoro/pulls/223"&gt;#223&lt;/a&gt;, Joey Richey)&lt;/p&gt;
&lt;h2 id="bugfixes"&gt;Bugfixes&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Suppress Clang error when building against Android NDK &amp;lt;= 25 (&lt;a href="https://github.com/danvratil/qcoro/issues/204"&gt;#204&lt;/a&gt;, Daniel Vrátil)&lt;/li&gt;
&lt;li&gt;Fixed missing QtGui dependency in QCoroQuick module (&lt;a href="https://github.com/danvratil/qcoro/pulls/209"&gt;#209&lt;/a&gt;, Andreas Sturmlechner)&lt;/li&gt;
&lt;li&gt;Fixed &lt;code&gt;QCoroIODevice::write()&lt;/code&gt; always returning 0 instead of bytes written (&lt;a href="https://github.com/danvratil/qcoro/issues/211"&gt;#211&lt;/a&gt;, Daniel Vrátil)&lt;/li&gt;
&lt;li&gt;Fixed unchecked &lt;code&gt;std::optional&lt;/code&gt; access in &lt;code&gt;QCoroIODevice::write&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Fixed awaiting on signal emission with &lt;code&gt;qCoro()&lt;/code&gt; would resume the awaiter in the sender&amp;rsquo;s thread context (&lt;a href="https://github.com/danvratil/qcoro/issues/213"&gt;#213&lt;/a&gt;, Daniel Vrátil)&lt;/li&gt;
&lt;li&gt;Fixed build wilth clang 18 due to missing &lt;code&gt;#include &amp;lt;exception&amp;gt;&lt;/code&gt; (&lt;a href="https://github.com/danvratil/qcoro/pulls/220"&gt;#220&lt;/a&gt;, Micah Terhaar)&lt;/li&gt;
&lt;li&gt;Fixed crash when &lt;code&gt;QNetworkAccessManager&lt;/code&gt; is destroyed from a coroutine awaiting on a network reply (&lt;a href="https://github.com/danvratil/qcoro/issues/231"&gt;#231&lt;/a&gt;, Daniel Vrátil)&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="full-changelog"&gt;Full changelog&lt;/h2&gt;
&lt;p&gt;&lt;a href="https://github.com/danvratil/qcoro/releases/tag/v0.11.0"&gt;See changelog on Github&lt;/a&gt;&lt;/p&gt;
&lt;h2 id="support"&gt;Support&lt;/h2&gt;
&lt;p&gt;If you enjoy using QCoro, consider supporting its development on &lt;a href="https://github.com/sponsors/danvratil"&gt;GitHub Sponsors&lt;/a&gt; or buy me a coffee
on &lt;a href="https://ko-fi.com/danvratil"&gt;Ko-fi&lt;/a&gt; (after all, more coffee means more code, right?).&lt;/p&gt;</content></entry><entry><title>KDE PIM Sprint 2024 Report</title><link rel="alternate" href="https://dvratil.cz/2024/06/kde-pim-sprint-2024-report/"/><id>https://dvratil.cz/2024/06/kde-pim-sprint-2024-report/</id><published>2024-06-19T20:00:00+0000</published><updated>2024-06-19T20:00:00+0000</updated><category term="kde"/><summary>&lt;p&gt;In 2021 I decided to &lt;a href="https://dvratil.cz/2021/05/taking-a-break/"&gt;take a break&lt;/a&gt; from contributing to KDE,
since I felt that I&amp;rsquo;ve been losing motivation and energy to contribute for a while…
But I&amp;rsquo;ve been slowly getting back to hacking on KDE stuff for the past year, which
ended in me going to Toulouse this year to attend the annual KDE PIM Sprint, my
first in 5 years.&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;m very happy to say that we have /a lot/ going on in PIM, and even though not
everything is in the best shape and the community is quite small (there were only
four of us at the sprint), we have great plans for the future, and I&amp;rsquo;m happy to be
part of it.&lt;/p&gt;</summary><content type="html" xml:base="https://dvratil.cz/2024/06/kde-pim-sprint-2024-report/">&lt;p&gt;In 2021 I decided to &lt;a href="https://dvratil.cz/2021/05/taking-a-break/"&gt;take a break&lt;/a&gt; from contributing to KDE,
since I felt that I&amp;rsquo;ve been losing motivation and energy to contribute for a while…
But I&amp;rsquo;ve been slowly getting back to hacking on KDE stuff for the past year, which
ended in me going to Toulouse this year to attend the annual KDE PIM Sprint, my
first in 5 years.&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;m very happy to say that we have /a lot/ going on in PIM, and even though not
everything is in the best shape and the community is quite small (there were only
four of us at the sprint), we have great plans for the future, and I&amp;rsquo;m happy to be
part of it.&lt;/p&gt;
&lt;h1 id="day-0"&gt;Day 0&lt;/h1&gt;
&lt;p&gt;The sprint was officially supposed to start on Saturday, but everyone arrived already
on Friday, so why wait? We wrote down the topics to discuss, put them on a whiteboard
and got to it.&lt;/p&gt;
&lt;figure&gt;&lt;img src="/2024/06/kde-pim-sprint-2024-report/images/whiteboard.png"
alt="Whiteboard with all discussion topics"&gt;
&lt;/figure&gt;
&lt;p&gt;We&amp;rsquo;ve managed to discuss some pretty important topics - how we want to proceed with
deprecation and removal of some components, how to improve our test coverage or how
to improve indexing and much much more.&lt;/p&gt;
&lt;p&gt;I arrived to the sprint with two big topics to discuss: milestones and testing:&lt;/p&gt;
&lt;h2 id="milestones"&gt;Milestones&lt;/h2&gt;
&lt;p&gt;The idea is to create milestones for all our bigger efforts that we work (or want
to work) on. The milestones should be concrete goals that are achievable within a
reasonable time frame and have clear definition of done. Each milestones should then
be split to smaller tasks that can be tackled by individuals. We hope that this
will help to make KDE PIM more attractive to new contributors, who can now clearly
see what is being worked on and can find very concrete, bite-sized tasks to work
on.&lt;/p&gt;
&lt;p&gt;As a result, we took all the ongoing tasks and turned most of them into
&lt;a href="https://invent.kde.org/groups/pim/-/milestones"&gt;milestones in Gitlab&lt;/a&gt;. It&amp;rsquo;s still very much work in progress,
we still need to break down many milestones to smaller tasks, but the general ideas
are out there.&lt;/p&gt;
&lt;h2 id="e2e-testing-of-resources"&gt;E2E Testing of Resources&lt;/h2&gt;
&lt;p&gt;Akonadi Resources provide &amp;ldquo;bridge&amp;rdquo; between Akonadi Server and individual services,
like IMAP servers, DAV servers, Google Calendar etc. But we have no tests to verify
that our Resources can talk to the services and vice versa. The plan is to create
a testing framework (in Python) so that we can have automated nightly tests to
verify that e.g. IMAP resource interfaces properly with common IMAP server
implementations, including major proprietary ones like Gmail or Office365. We want
to achieve decent coverage for all our resources. This is a big project, but I think
it&amp;rsquo;s a very exciting one as it includes not just programming, but also figuring out
and building some infrastructure to run e.g. Dovecot, NextCloud and others in
a Docker to test against.&lt;/p&gt;
&lt;h1 id="day-1"&gt;Day 1&lt;/h1&gt;
&lt;p&gt;On Saturday we started quite early, all the delicious french pastry is not going to
eat itself, is it? After breakfast we continued with discussions, we dicussed tags
support, how to improve our PR. But we also managed to produce some code. I
implemented syncing of iCal categories with Akonadi tags, so the tags are becoming
more useful. I also prepared Akonadi to be cleanly handle planned deprecation and
retirement of KJots, KNotes and their acompanying resources, as well as planned
removal of the Akonadi Kolab Resource (in favor of using IMAP+DAV).&lt;/p&gt;
&lt;p&gt;One of the tasks I want to look into is improving how we do database transactions in
the Akonadi Server. To get some data out of it, I shoved Prometheus exporter into
Akonadi, hooked it up to a local Prometheus service, thrown together a Grafana
dashboard, and here we are:&lt;/p&gt;
&lt;figure&gt;&lt;img src="/2024/06/kde-pim-sprint-2024-report/images/grafana.png"
alt="Grafana dashboard"&gt;
&lt;/figure&gt;
&lt;p&gt;We decided to order some pizzas for dinner and stayed at the venue hacking until
nearly 11 o&amp;rsquo;clock.&lt;/p&gt;
&lt;h1 id="day-2"&gt;Day 2&lt;/h1&gt;
&lt;p&gt;On the last day of the sprint we wrapped up on the discussions and focused on actually
implementing some of the ideas. I spent most of the time extending the Migration agent
to extract tags from all existing events and todos already stored in Akonadi and helped
to create some of the milestones on the Gitlab board. We also came up with a plan for
KDE PIM BoF on this years Akademy, where we want to present out progress on the
respective milestones and to give a chance to contributors to learn what are the biggest
hurdles they are facing when trying to contribute to KDE PIM and how we can help make
it easier for them to get involved.&lt;/p&gt;
&lt;h1 id="conclusion"&gt;Conclusion&lt;/h1&gt;
&lt;p&gt;I think it was a very productive sprint and I am really excited to be involved in PIM
again. Can&amp;rsquo;t wait to meet up with everyone again on Akademy in September.&lt;/p&gt;
&lt;p&gt;Go check out &lt;a href="https://ervin.ipsquad.net/blog/2024/06/16/report-from-kdepim-spring-sprint-2024/"&gt;Kevin&amp;rsquo;s&lt;/a&gt; and &lt;a href="https://carlschwan.eu/2024/06/16/kde-pim-sprint-2024-edition/"&gt;Carl&amp;rsquo;s&lt;/a&gt; reports to see what else
have they been up to during the sprint.&lt;/p&gt;
&lt;p&gt;Did some of the milestones caught your eye, or do you have have any questions? Come
talk to us in our &lt;a href="https://matrix.to/#/#kontact:kde.org"&gt;matrix channel&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Finally, many thanks to Kevin for organizing the sprint,
&lt;a href="https://www.etincelle-coworking.com/"&gt;Étincelle Coworking&lt;/a&gt; for providing us with nice and spacious
venue and &lt;a href="https://ev.kde.org/"&gt;KDE e.V.&lt;/a&gt; for supporting us on travel.&lt;/p&gt;
&lt;p&gt;Finally, if you like such meetings to happen in the future so that we can push forward
your favorite software, please consider &lt;a href="https://www.kde.org/community/donations/index.php"&gt;making a tax-deductible donation&lt;/a&gt;
to the &lt;a href="https://ev.kde.org/"&gt;KDE e.V. foundation&lt;/a&gt;.&lt;/p&gt;</content></entry><entry><title>QCoro 0.10.0 Release Announcement</title><link rel="alternate" href="https://dvratil.cz/2023/12/qcoro-0.10.0-release-announcement/"/><id>https://dvratil.cz/2023/12/qcoro-0.10.0-release-announcement/</id><published>2023-12-05T21:30:00+0000</published><updated>2023-12-05T21:30:00+0000</updated><category term="QCoro"/><summary>&lt;!--
SPDX-FileCopyrightText: 2023 Daniel Vrátil &lt;dvratil@kde.org&gt;
SPDX-License-Identifier: GFDL-1.3-or-later
--&gt;
&lt;p&gt;Thank you to everyone who reported issues and contributed to QCoro.
Your help is much appreciated!&lt;/p&gt;
&lt;h2 id="support-for-awaiting-qt-signals-with-qprivatesignal"&gt;Support for awaiting Qt signals with QPrivateSignal&lt;/h2&gt;
&lt;p&gt;Qt has a feature where signals can be made &amp;ldquo;private&amp;rdquo; (in the sense that only class
that defines the signal can emit it) by appending &lt;code&gt;QPrivateSignal&lt;/code&gt; argument to the
signal method:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-cpp" data-lang="cpp"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;class&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;MyObject&lt;/span&gt; &lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;public&lt;/span&gt; QObject {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; Q_OBJECT
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;...
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Q_SIGNALS:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;void&lt;/span&gt; error(&lt;span style="color:#66d9ef"&gt;int&lt;/span&gt; code, &lt;span style="color:#66d9ef"&gt;const&lt;/span&gt; QString &lt;span style="color:#f92672"&gt;&amp;amp;&lt;/span&gt;message, QPrivateSignal);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;};
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;code&gt;QPrivateSignal&lt;/code&gt; is a type that is defined inside the &lt;code&gt;Q_OBJECT&lt;/code&gt; macro, so it&amp;rsquo;s
private and as such only &lt;code&gt;MyObject&lt;/code&gt; class can emit the signal, since only &lt;code&gt;MyObject&lt;/code&gt;
can instantiate &lt;code&gt;QPrivateSignal&lt;/code&gt;:&lt;/p&gt;</summary><content type="html" xml:base="https://dvratil.cz/2023/12/qcoro-0.10.0-release-announcement/">&lt;!--
SPDX-FileCopyrightText: 2023 Daniel Vrátil &lt;dvratil@kde.org&gt;
SPDX-License-Identifier: GFDL-1.3-or-later
--&gt;
&lt;p&gt;Thank you to everyone who reported issues and contributed to QCoro.
Your help is much appreciated!&lt;/p&gt;
&lt;h2 id="support-for-awaiting-qt-signals-with-qprivatesignal"&gt;Support for awaiting Qt signals with QPrivateSignal&lt;/h2&gt;
&lt;p&gt;Qt has a feature where signals can be made &amp;ldquo;private&amp;rdquo; (in the sense that only class
that defines the signal can emit it) by appending &lt;code&gt;QPrivateSignal&lt;/code&gt; argument to the
signal method:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-cpp" data-lang="cpp"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;class&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;MyObject&lt;/span&gt; &lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;public&lt;/span&gt; QObject {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; Q_OBJECT
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;...
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Q_SIGNALS:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;void&lt;/span&gt; error(&lt;span style="color:#66d9ef"&gt;int&lt;/span&gt; code, &lt;span style="color:#66d9ef"&gt;const&lt;/span&gt; QString &lt;span style="color:#f92672"&gt;&amp;amp;&lt;/span&gt;message, QPrivateSignal);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;};
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;code&gt;QPrivateSignal&lt;/code&gt; is a type that is defined inside the &lt;code&gt;Q_OBJECT&lt;/code&gt; macro, so it&amp;rsquo;s
private and as such only &lt;code&gt;MyObject&lt;/code&gt; class can emit the signal, since only &lt;code&gt;MyObject&lt;/code&gt;
can instantiate &lt;code&gt;QPrivateSignal&lt;/code&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-cpp" data-lang="cpp"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;void&lt;/span&gt; MyObject&lt;span style="color:#f92672"&gt;::&lt;/span&gt;handleError(&lt;span style="color:#66d9ef"&gt;int&lt;/span&gt; code, &lt;span style="color:#66d9ef"&gt;const&lt;/span&gt; QString &lt;span style="color:#f92672"&gt;&amp;amp;&lt;/span&gt;message)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; Q_EMIT &lt;span style="color:#a6e22e"&gt;error&lt;/span&gt;(code, message, QPrivateSignal{});
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;QCoro has a feature that makes it possible to &lt;code&gt;co_await&lt;/code&gt; a signal emission and
returns the signals arguments as a tuple:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-cpp" data-lang="cpp"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;MyObject myObject;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;const&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;auto&lt;/span&gt; [code, message] &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;co_await&lt;/span&gt; qCoro(&lt;span style="color:#f92672"&gt;&amp;amp;&lt;/span&gt;myObject, &lt;span style="color:#f92672"&gt;&amp;amp;&lt;/span&gt;MyObject&lt;span style="color:#f92672"&gt;::&lt;/span&gt;handleError);
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;While it was possible to &lt;code&gt;co_await&lt;/code&gt; a &amp;ldquo;private&amp;rdquo; signal previously, it would get
return the &lt;code&gt;QPrivateSignal&lt;/code&gt; value as an additional value in the result tuple
and on some occasions would not compile at all.&lt;/p&gt;
&lt;p&gt;In QCoro 0.10, we can detect the &lt;code&gt;QPrivateSignal&lt;/code&gt; argument and drop it inside QCoro
so that it does not cause trouble and does not clutter the result type.&lt;/p&gt;
&lt;p&gt;Achieving this wasn&amp;rsquo;t simple, as it&amp;rsquo;s not really possible to detect the type (because
it&amp;rsquo;s private), e.g. code like this would fail to compile, because we are not allowed
to refer to &lt;code&gt;Obj::QPrivateSignal&lt;/code&gt;, since that type is private to &lt;code&gt;Obj&lt;/code&gt;.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-cpp" data-lang="cpp"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;template&lt;/span&gt;&lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;typename&lt;/span&gt; T, &lt;span style="color:#66d9ef"&gt;typename&lt;/span&gt; Obj&lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;constexpr&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;bool&lt;/span&gt; is_qprivatesignal &lt;span style="color:#f92672"&gt;=&lt;/span&gt; std&lt;span style="color:#f92672"&gt;::&lt;/span&gt;same_as_v&lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt;T, &lt;span style="color:#66d9ef"&gt;typename&lt;/span&gt; Obj&lt;span style="color:#f92672"&gt;::&lt;/span&gt;QPrivateSignal&lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;After many different attempts we ended up abusing &lt;code&gt;__PRETTY_FUNCTION__&lt;/code&gt;
(and &lt;code&gt;__FUNCSIG__&lt;/code&gt; on MSVC) and checking whether the function&amp;rsquo;s name contains
&lt;code&gt;QPrivateSignal&lt;/code&gt; string in the expected location. It&amp;rsquo;s a whacky hack, but hey - if it
works, it&amp;rsquo;s not stupid :). And thanks to improvements in compile-time evaluation in
C++20, the check is evaluated completely at compile-time, so there&amp;rsquo;s no runtime
overhead of obtaining current source location and doing string comparisons.&lt;/p&gt;
&lt;h2 id="source-code-reorganization-again"&gt;Source Code Reorganization (again!)&lt;/h2&gt;
&lt;p&gt;Big part of QCoro are template classes, so there&amp;rsquo;s a lot of code in headers. In my
opinion, some of the files (especially qcorotask.h) were getting hard to read and
navigate and it made it harder to just see the API of the class (like you get
with non-template classes), which is what users of a library are usually most
interested in.&lt;/p&gt;
&lt;p&gt;Therefore I decided to move definitions into separated files, so that they don&amp;rsquo;t
clutter the main include files.&lt;/p&gt;
&lt;p&gt;This change is completely source- and binary-compatible, so QCoro users don&amp;rsquo;t have
to make any changes to their code. The only difference is that the main QCoro
headers are much prettier to look at now.&lt;/p&gt;
&lt;h2 id="bugfixes"&gt;Bugfixes&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;QCoro::waitFor()&lt;/code&gt; now re-throws exceptions (&lt;a href="https://github.com/danvratil/qcoro/issues/172"&gt;#172&lt;/a&gt;, Daniel Vrátil)&lt;/li&gt;
&lt;li&gt;Replaced deprecated &lt;code&gt;QWebSocket::error&lt;/code&gt; with &lt;code&gt;QWbSocket::errorOccured&lt;/code&gt; in QCoroWebSockets module (&lt;a href="https://github.com/danvratil/qcoro/pulls/174"&gt;#174&lt;/a&gt;, Marius P)&lt;/li&gt;
&lt;li&gt;Fix &lt;code&gt;QCoro::connect()&lt;/code&gt; not working with lambdas (&lt;a href="https://github.com/danvratil/qcoro/pulls/179"&gt;#179&lt;/a&gt;, Johan Brüchert)&lt;/li&gt;
&lt;li&gt;Fix library name postfix for qmake compatibilty (&lt;a href="https://github.com/danvratil/qcoro/pulls/192"&gt;#192&lt;/a&gt;, Shantanu Tushar)&lt;/li&gt;
&lt;li&gt;Fix &lt;code&gt;std::coroutine_traits isn't a class template&lt;/code&gt; error with LLVM 16 (&lt;a href="https://github.com/danvratil/qcoro/pulls/196"&gt;#196&lt;/a&gt;, Rafael Sadowski)&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="full-changelog"&gt;Full changelog&lt;/h2&gt;
&lt;p&gt;&lt;a href="https://github.com/danvratil/qcoro/releases/tag/v0.10.0"&gt;See changelog on Github&lt;/a&gt;&lt;/p&gt;</content></entry><entry><title>QCoro 0.8.0 Release Announcement</title><link rel="alternate" href="https://dvratil.cz/2023/01/qcoro-0.8.0-release-announcement/"/><id>https://dvratil.cz/2023/01/qcoro-0.8.0-release-announcement/</id><published>2023-01-31T19:53:00+0000</published><updated>2023-01-31T19:53:00+0000</updated><category term="QCoro"/><summary>&lt;!--
SPDX-FileCopyrightText: 2022 Daniel Vrátil &lt;dvratil@kde.org&gt;
SPDX-License-Identifier: GFDL-1.3-or-later
--&gt;
&lt;p&gt;This is a rather small release with only two new features and one small improvement.&lt;/p&gt;
&lt;p&gt;Big thank you to &lt;a href="https://xstrahl.com"&gt;Xstrahl Inc.&lt;/a&gt; who sponsored development of
new features included in this release and of QCoro in general.&lt;/p&gt;
&lt;p&gt;And as always, thank you to everyone who reported issues and contributed to QCoro.
Your help is much appreciated!&lt;/p&gt;
&lt;p&gt;&lt;a href="https://qcoro.dvratil.cz/news/2023/2023-01-31-qcoro-0.8.0-announcement/"&gt;The original release announcement on qcoro.dvratil.cz&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id="improved-qcorowaitfor"&gt;Improved &lt;code&gt;QCoro::waitFor()&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;Up until this version, &lt;code&gt;QCoro::waitFor()&lt;/code&gt; was only usable for &lt;code&gt;QCoro::Task&amp;lt;T&amp;gt;&lt;/code&gt;.
Starting with QCoro 0.8.0, it is possible to use it with any type that satisfies
the &lt;code&gt;Awaitable&lt;/code&gt; concept. The concept has also been fixed to satisfies not just
types with the &lt;code&gt;await_resume()&lt;/code&gt;, &lt;code&gt;await_suspend()&lt;/code&gt; and &lt;code&gt;await_ready()&lt;/code&gt; member functions,
but also types with member &lt;code&gt;operator co_await()&lt;/code&gt; and non-member &lt;code&gt;operator co_await()&lt;/code&gt;
functions.&lt;/p&gt;</summary><content type="html" xml:base="https://dvratil.cz/2023/01/qcoro-0.8.0-release-announcement/">&lt;!--
SPDX-FileCopyrightText: 2022 Daniel Vrátil &lt;dvratil@kde.org&gt;
SPDX-License-Identifier: GFDL-1.3-or-later
--&gt;
&lt;p&gt;This is a rather small release with only two new features and one small improvement.&lt;/p&gt;
&lt;p&gt;Big thank you to &lt;a href="https://xstrahl.com"&gt;Xstrahl Inc.&lt;/a&gt; who sponsored development of
new features included in this release and of QCoro in general.&lt;/p&gt;
&lt;p&gt;And as always, thank you to everyone who reported issues and contributed to QCoro.
Your help is much appreciated!&lt;/p&gt;
&lt;p&gt;&lt;a href="https://qcoro.dvratil.cz/news/2023/2023-01-31-qcoro-0.8.0-announcement/"&gt;The original release announcement on qcoro.dvratil.cz&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id="improved-qcorowaitfor"&gt;Improved &lt;code&gt;QCoro::waitFor()&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;Up until this version, &lt;code&gt;QCoro::waitFor()&lt;/code&gt; was only usable for &lt;code&gt;QCoro::Task&amp;lt;T&amp;gt;&lt;/code&gt;.
Starting with QCoro 0.8.0, it is possible to use it with any type that satisfies
the &lt;code&gt;Awaitable&lt;/code&gt; concept. The concept has also been fixed to satisfies not just
types with the &lt;code&gt;await_resume()&lt;/code&gt;, &lt;code&gt;await_suspend()&lt;/code&gt; and &lt;code&gt;await_ready()&lt;/code&gt; member functions,
but also types with member &lt;code&gt;operator co_await()&lt;/code&gt; and non-member &lt;code&gt;operator co_await()&lt;/code&gt;
functions.&lt;/p&gt;
&lt;h2 id="qcorosleepfor-and-qcorosleepuntil"&gt;&lt;code&gt;QCoro::sleepFor()&lt;/code&gt; and &lt;code&gt;QCoro::sleepUntil()&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;Working both on QCoro codebase as well as some third-party code bases using QCoro
it&amp;rsquo;s clear that there&amp;rsquo;s a usecase for a simple coroutine that will sleep for
specified amount of time (or until a specified timepoint). It is especially useful
in tests, where simulating delays, especially in asynchronous code is common.&lt;/p&gt;
&lt;p&gt;Previously I used to create small coroutines like this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-cpp" data-lang="cpp"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;QCoro&lt;span style="color:#f92672"&gt;::&lt;/span&gt;Task&lt;span style="color:#f92672"&gt;&amp;lt;&amp;gt;&lt;/span&gt; timer(std&lt;span style="color:#f92672"&gt;::&lt;/span&gt;chrono&lt;span style="color:#f92672"&gt;::&lt;/span&gt;milliseconds timeout) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; QTimer timer;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; timer.setSingleShot(true);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; timer.start(timeout);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;co_await&lt;/span&gt; timer;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Now we can do the same simply by using &lt;code&gt;QCoro::sleepFor()&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Read the &lt;a href="https://qcoro.dvratil.cz/reference/core/qtimer/#qcorosleepfor"&gt;documentation for &lt;code&gt;QCoro::sleepFor()&lt;/code&gt;&lt;/a&gt;
and &lt;a href="https://qcoro.dvratil.cz/reference/core/qtimer/#qcorosleepuntil"&gt;&lt;code&gt;QCoro::sleepUntil()&lt;/code&gt;&lt;/a&gt; for more details.&lt;/p&gt;
&lt;h2 id="qcoromovetothread"&gt;&lt;code&gt;QCoro::moveToThread()&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;A small helper coroutine that allows a piece of function to be executed in the context
of another thread.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-cpp" data-lang="cpp"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;void&lt;/span&gt; App&lt;span style="color:#f92672"&gt;::&lt;/span&gt;runSlowOperation(QThread &lt;span style="color:#f92672"&gt;*&lt;/span&gt;helperThread) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// Still on the main thread
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; ui&lt;span style="color:#f92672"&gt;-&amp;gt;&lt;/span&gt;statusLabel.setText(tr(&lt;span style="color:#e6db74"&gt;&amp;#34;Running&amp;#34;&lt;/span&gt;));
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;const&lt;/span&gt; QString input &lt;span style="color:#f92672"&gt;=&lt;/span&gt; ui&lt;span style="color:#f92672"&gt;-&amp;gt;&lt;/span&gt;userInput.text();
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;co_await&lt;/span&gt; QCoro&lt;span style="color:#f92672"&gt;::&lt;/span&gt;moveToThread(helperThread);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// Now we are running in the context of the helper thread, the main thread is not blocked
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// It is safe to use `input` which was created in another thread
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; doSomeComplexCalculation(input);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// Move the execution back to the main thread
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;co_await&lt;/span&gt; QCoro&lt;span style="color:#f92672"&gt;::&lt;/span&gt;moveToThread(&lt;span style="color:#66d9ef"&gt;this&lt;/span&gt;&lt;span style="color:#f92672"&gt;-&amp;gt;&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;thread&lt;/span&gt;());
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// Runs on the main thread again
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; ui&lt;span style="color:#f92672"&gt;-&amp;gt;&lt;/span&gt;statusLabel.setText(tr(&lt;span style="color:#e6db74"&gt;&amp;#34;Done&amp;#34;&lt;/span&gt;));
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Read the &lt;a href="https://qcoro.dvratil.cz/reference/core/qthread#qcoromovetothread"&gt;documentation for &lt;code&gt;QCoro::moveToThread&lt;/code&gt;&lt;/a&gt; for more details.&lt;/p&gt;
&lt;h2 id="full-changelog"&gt;Full changelog&lt;/h2&gt;
&lt;p&gt;&lt;a href="https://github.com/danvratil/qcoro/releases/tag/v0.8.0"&gt;See changelog on Github&lt;/a&gt;&lt;/p&gt;</content></entry><entry><title>QCoro 0.7.0 Release Announcement</title><link rel="alternate" href="https://dvratil.cz/2022/11/qcoro-0.7.0-release-announcement/"/><id>https://dvratil.cz/2022/11/qcoro-0.7.0-release-announcement/</id><published>2022-11-20T23:52:00+0000</published><updated>2022-11-20T23:52:00+0000</updated><category term="QCoro"/><summary>&lt;!--
SPDX-FileCopyrightText: 2022 Daniel Vrátil &lt;dvratil@kde.org&gt;
SPDX-License-Identifier: GFDL-1.3-or-later
--&gt;
&lt;p&gt;The major new feature in this release is initial QML support, contributed by
Jonah Brüchert. Jonah also contributed &lt;code&gt;QObject::connect&lt;/code&gt; helper and
a coroutine version of QQuickImageProvider. As always, this release includes
some smaller enhancements and bugfixes, you can find a full list of them
on the Github release page.&lt;/p&gt;
&lt;p&gt;As always, big thank you to everyone who report issues and contributed to QCoro.
Your help is much appreciated!&lt;/p&gt;</summary><content type="html" xml:base="https://dvratil.cz/2022/11/qcoro-0.7.0-release-announcement/">&lt;!--
SPDX-FileCopyrightText: 2022 Daniel Vrátil &lt;dvratil@kde.org&gt;
SPDX-License-Identifier: GFDL-1.3-or-later
--&gt;
&lt;p&gt;The major new feature in this release is initial QML support, contributed by
Jonah Brüchert. Jonah also contributed &lt;code&gt;QObject::connect&lt;/code&gt; helper and
a coroutine version of QQuickImageProvider. As always, this release includes
some smaller enhancements and bugfixes, you can find a full list of them
on the Github release page.&lt;/p&gt;
&lt;p&gt;As always, big thank you to everyone who report issues and contributed to QCoro.
Your help is much appreciated!&lt;/p&gt;
&lt;h2 id="initial-qml-support"&gt;Initial QML Support&lt;/h2&gt;
&lt;p&gt;Jonah Brüchert has contributed initial support for QML. Unfortunately, we
cannot extend the QML engine to support the &lt;code&gt;async&lt;/code&gt; and &lt;code&gt;await&lt;/code&gt; keywords from
ES8, but we can make it possible to set a callback from QML that should be
called when the coroutine finishes.&lt;/p&gt;
&lt;p&gt;The problem with &lt;code&gt;QCoro::Task&lt;/code&gt; is that it is a template class so it cannot be
registered into the QML type system and used from inside QML. The solution
that Jonach has come up with is to introduce &lt;code&gt;QCoro::QmlTask&lt;/code&gt; class, which
can wrap any awaitable (be it &lt;code&gt;QCoro::Task&lt;/code&gt; or any generic awaitable type)
and provides a &lt;code&gt;then()&lt;/code&gt; method that can be called from QML and that takes
a JavaScript function as its only argument. The function will be invoked by
&lt;code&gt;QCoro::QmlTask&lt;/code&gt; when the wrapped awaitable has finished.&lt;/p&gt;
&lt;p&gt;The disadvantage of this approach is that in order to expose a class that
uses &lt;code&gt;QCoro::Task&amp;lt;T&amp;gt;&lt;/code&gt; as return types of its member functions into QML, we
need to create a wrapper class that converts those return types to
&lt;code&gt;QCoro::QmlTask&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Luckily, we should be able to provide a smoother user experience when using
QCoro in QML for Qt6 in a future QCoro release.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-cpp" data-lang="cpp"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;class&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;QmlCoroTimer&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;public&lt;/span&gt; QObject {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; Q_OBJECT
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;public&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;explicit&lt;/span&gt; QmlCoroTimer(QObject &lt;span style="color:#f92672"&gt;*&lt;/span&gt;parent &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;nullptr&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;:&lt;/span&gt; QObject(parent)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; {}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; Q_INVOCABLE QCoro&lt;span style="color:#f92672"&gt;::&lt;/span&gt;QmlTask start(&lt;span style="color:#66d9ef"&gt;int&lt;/span&gt; milliseconds) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// Implicitly wraps QCoro::Task&amp;lt;&amp;gt; into QCoro::QmlTask
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;waitFor&lt;/span&gt;(milliseconds);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;private&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// A simple coroutine that co_awaits a timer timeout
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; QCoro&lt;span style="color:#f92672"&gt;::&lt;/span&gt;Task&lt;span style="color:#f92672"&gt;&amp;lt;&amp;gt;&lt;/span&gt; waitFor(&lt;span style="color:#66d9ef"&gt;int&lt;/span&gt; milliseconds) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; QTimer timer;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; timer.start(milliseconds);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;co_await&lt;/span&gt; timer;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;};
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;...
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;QCoro&lt;span style="color:#f92672"&gt;::&lt;/span&gt;Qml&lt;span style="color:#f92672"&gt;::&lt;/span&gt;registerTypes();
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;qmlRegisterType&lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt;QmlCoroTimer&lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#34;cz.dvratil.qcoro.example&amp;#34;&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-qml" data-lang="qml"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;import&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;cz&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;dvratil&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;qcoro&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;example&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;1.0&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;Item&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;QmlCoroTimer&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;id: timer&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;Component.onCompleted:&lt;/span&gt; () {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// Attaches a callback to be called when the QmlCoroTimer::waitFor()
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// coroutine finishes.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;timer&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;start&lt;/span&gt;(&lt;span style="color:#ae81ff"&gt;1000&lt;/span&gt;).&lt;span style="color:#a6e22e"&gt;then&lt;/span&gt;(() &lt;span style="color:#f92672"&gt;=&amp;gt;&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;console&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;log&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#34;1 second elapsed!&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; });
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Read the &lt;a href="https://qcoro.dvratil.cz/reference/qml/qmltask"&gt;documentation for &lt;code&gt;QCoro::QmlTask&lt;/code&gt;&lt;/a&gt; for more details.&lt;/p&gt;
&lt;h2 id="qcoroconnect-helper"&gt;QCoro::connect Helper&lt;/h2&gt;
&lt;p&gt;The &lt;code&gt;QCoro::connect()&lt;/code&gt; helper is similar to &lt;code&gt;QObject::connect()&lt;/code&gt; - except you
you pass in a &lt;code&gt;QCoro::Task&amp;lt;T&amp;gt;&lt;/code&gt; instead of a sender and signal pointers. While
using the &lt;code&gt;.then()&lt;/code&gt; continuation can achieve similar results, the main
difference is that &lt;code&gt;QCoro::connect()&lt;/code&gt; takes a pointer to a context (receiver)
QObject. If the receiver is destroyed before the connected &lt;code&gt;QCoro::Task&amp;lt;T&amp;gt;&lt;/code&gt;
finishes, the slot is not invoked.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-cpp" data-lang="cpp"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;void&lt;/span&gt; MyClass&lt;span style="color:#f92672"&gt;::&lt;/span&gt;buttonClicked() {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; QCoro&lt;span style="color:#f92672"&gt;::&lt;/span&gt;Task&lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt;QByteArray&lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt; task &lt;span style="color:#f92672"&gt;=&lt;/span&gt; sendNetworkRequest();
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// If this object is deleted before the `task` completes,
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// the slot is not invoked.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; QCoro&lt;span style="color:#f92672"&gt;::&lt;/span&gt;connect(std&lt;span style="color:#f92672"&gt;::&lt;/span&gt;move(task), &lt;span style="color:#66d9ef"&gt;this&lt;/span&gt;, &lt;span style="color:#f92672"&gt;&amp;amp;&lt;/span&gt;handleNetworkReply);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;See the &lt;a href="https://qcoro.dvratil.cz/reference/coro/task/#interfacing-with-synchronous-functions"&gt;QCoro documentation&lt;/a&gt; for more details.&lt;/p&gt;
&lt;h2 id="full-changelog"&gt;Full changelog&lt;/h2&gt;
&lt;p&gt;&lt;a href="https://github.com/danvratil/qcoro/releases/tag/v0.7.0"&gt;See changelog on Github&lt;/a&gt;&lt;/p&gt;</content></entry><entry><title>QCoro 0.6.0 Release Announcement</title><link rel="alternate" href="https://dvratil.cz/2022/07/qcoro-0.6.0-release-announcement/"/><id>https://dvratil.cz/2022/07/qcoro-0.6.0-release-announcement/</id><published>2022-07-09T15:00:00+0000</published><updated>2022-07-09T15:00:00+0000</updated><category term="QCoro"/><summary>&lt;p&gt;I&amp;rsquo;m pleased to announce release 0.6.0 of QCoro, a library that allows using C++20
coroutines with Qt. This release brings several major new features alongside a bunch
of bugfixes and improvements inside QCoro.&lt;/p&gt;
&lt;p&gt;The four major features are:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Generator support&lt;/li&gt;
&lt;li&gt;New QCoroWebSockets module&lt;/li&gt;
&lt;li&gt;Deprecated task.h&lt;/li&gt;
&lt;li&gt;Clang-cl and apple-clang support&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;🎉 Starting with 0.6.0 I no longer consider this library to be experimental
(since clearly the experiment worked :-)) and its API to be stable enough for
general use. 🎉&lt;/p&gt;</summary><content type="html" xml:base="https://dvratil.cz/2022/07/qcoro-0.6.0-release-announcement/">&lt;p&gt;I&amp;rsquo;m pleased to announce release 0.6.0 of QCoro, a library that allows using C++20
coroutines with Qt. This release brings several major new features alongside a bunch
of bugfixes and improvements inside QCoro.&lt;/p&gt;
&lt;p&gt;The four major features are:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Generator support&lt;/li&gt;
&lt;li&gt;New QCoroWebSockets module&lt;/li&gt;
&lt;li&gt;Deprecated task.h&lt;/li&gt;
&lt;li&gt;Clang-cl and apple-clang support&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;🎉 Starting with 0.6.0 I no longer consider this library to be experimental
(since clearly the experiment worked :-)) and its API to be stable enough for
general use. 🎉&lt;/p&gt;
&lt;p&gt;As always, big thank you to everyone who report issues and contributed to QCoro.
Your help is much appreciated!&lt;/p&gt;
&lt;h3 id="generator-support"&gt;Generator support&lt;/h3&gt;
&lt;p&gt;Unlike regular functions (or &lt;code&gt;QCoro::Task&amp;lt;&amp;gt;&lt;/code&gt;-based coroutines) which can only ever
produce at most single result (through &lt;code&gt;return&lt;/code&gt; or &lt;code&gt;co_return&lt;/code&gt; statement), generators
can yield results repeatedly without terminating. In QCoro we have two types of generators:
synchronous and asynchronous. Synchronous means that the generator produces each value
synchronously. In QCoro those are implemented as &lt;code&gt;QCoro::Generator&amp;lt;T&amp;gt;&lt;/code&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-cpp" data-lang="cpp"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;// A generator that produces a sequence of numbers from 0 to `end`.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;QCoro&lt;span style="color:#f92672"&gt;::&lt;/span&gt;Generator&lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;int&lt;/span&gt;&lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt; sequence(&lt;span style="color:#66d9ef"&gt;int&lt;/span&gt; end) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;for&lt;/span&gt; (&lt;span style="color:#66d9ef"&gt;int&lt;/span&gt; i &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;; i &lt;span style="color:#f92672"&gt;&amp;lt;=&lt;/span&gt; end; &lt;span style="color:#f92672"&gt;++&lt;/span&gt;i) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// Produces current value of `i` and suspends.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;co_yield&lt;/span&gt; i;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// End the iterator
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;int&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;sumSequence&lt;/span&gt;(&lt;span style="color:#66d9ef"&gt;int&lt;/span&gt; end) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;int&lt;/span&gt; sum &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// Loops over the returned Generator, resuming the generator on each iterator
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// so it can produce a value that we then consume.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;for&lt;/span&gt; (&lt;span style="color:#66d9ef"&gt;int&lt;/span&gt; value : sequence(end)) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; sum &lt;span style="color:#f92672"&gt;+=&lt;/span&gt; value;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; sum;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The &lt;code&gt;Generator&lt;/code&gt; interface implements &lt;code&gt;begin()&lt;/code&gt; and &lt;code&gt;end()&lt;/code&gt; methods which produce an
iterator-like type. When the iterator is incremented, the generator is resumed to yield
a value and then suspended again. The iterator-like interface is not mandated by the C++
standard (the C++ standard provides no requirements for generators), but it is an
intentional design choice, since it makes it possible to use the generators with existing
language constructs as well as standard-library and Qt features.&lt;/p&gt;
&lt;p&gt;You can find more details about synchronous generators in the &lt;a href="https://qcoro.dvratil.cz/reference/coro/generator/"&gt;&lt;code&gt;QCoro::Generator&amp;lt;T&amp;gt;&lt;/code&gt;
documentation&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Asynchronous generators work in a similar way, but they produce value asynchronously,
that is the result of the generator must be &lt;code&gt;co_await&lt;/code&gt;ed by the caller.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-cpp" data-lang="cpp"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;QCoro&lt;span style="color:#f92672"&gt;::&lt;/span&gt;AsyncGenerator&lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt;QUrl&lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt; paginator(&lt;span style="color:#66d9ef"&gt;const&lt;/span&gt; QUrl &lt;span style="color:#f92672"&gt;&amp;amp;&lt;/span&gt;baseUrl) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; QUrl pageUrl &lt;span style="color:#f92672"&gt;=&lt;/span&gt; baseUrl;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; Q_FOREVER {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; pageUrl &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;co_await&lt;/span&gt; getNextPage(pageUrl); &lt;span style="color:#75715e"&gt;// co_awaits next page URL
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; (pageUrl.isNull()) { &lt;span style="color:#75715e"&gt;// if empty, we reached the last page
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;break&lt;/span&gt;; &lt;span style="color:#75715e"&gt;// leave the loop
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;co_yield&lt;/span&gt; pageUrl; &lt;span style="color:#75715e"&gt;// finally, yield the value and suspend
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// end the generator
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;QCoro&lt;span style="color:#f92672"&gt;::&lt;/span&gt;AsyncGenerator&lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt;QString&lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt; pageReader(&lt;span style="color:#66d9ef"&gt;const&lt;/span&gt; QUrl &lt;span style="color:#f92672"&gt;&amp;amp;&lt;/span&gt;baseUrl) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// Create a new generator
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;auto&lt;/span&gt; generator &lt;span style="color:#f92672"&gt;=&lt;/span&gt; paginator(baseUrl);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// Wait for the first value
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;auto&lt;/span&gt; it &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;co_await&lt;/span&gt; generator.begin();
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;auto&lt;/span&gt; end &lt;span style="color:#f92672"&gt;=&lt;/span&gt; generator.end();
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;while&lt;/span&gt; (it &lt;span style="color:#f92672"&gt;!=&lt;/span&gt; end) { &lt;span style="color:#75715e"&gt;// while the `it` iterator is valid...
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// Asynchronously retrieve the page content
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;const&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;auto&lt;/span&gt; content &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;co_await&lt;/span&gt; fetchPageContent(&lt;span style="color:#f92672"&gt;*&lt;/span&gt;it);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// Yield it to the caller, then suspend
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;co_yield&lt;/span&gt; content;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// When resumed, wait for the paginator generator to produce another value
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;co_await&lt;/span&gt; &lt;span style="color:#f92672"&gt;++&lt;/span&gt;it;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;QCoro&lt;span style="color:#f92672"&gt;::&lt;/span&gt;Task&lt;span style="color:#f92672"&gt;&amp;lt;&amp;gt;&lt;/span&gt; downloader(&lt;span style="color:#66d9ef"&gt;const&lt;/span&gt; QUrl &lt;span style="color:#f92672"&gt;&amp;amp;&lt;/span&gt;baseUrl) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;int&lt;/span&gt; page &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// `QCORO_FOREACH` is like `Q_FOREACH` for asynchronous iterators
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; QCORO_FOREACH(&lt;span style="color:#66d9ef"&gt;const&lt;/span&gt; QString &lt;span style="color:#f92672"&gt;&amp;amp;&lt;/span&gt;page, pageReader(baseUrl)) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// When value is finally produced, write it to a file
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; QFile &lt;span style="color:#a6e22e"&gt;file&lt;/span&gt;(QStringLiteral(&lt;span style="color:#e6db74"&gt;&amp;#34;page%1.html&amp;#34;&lt;/span&gt;).arg(page));
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; file.open(QIODevice&lt;span style="color:#f92672"&gt;::&lt;/span&gt;WriteOnly);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; file.write(page);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;++&lt;/span&gt;page;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Async generators also have &lt;code&gt;begin()&lt;/code&gt; and &lt;code&gt;end()&lt;/code&gt; methods which provide an asynchronous
iterator-like types. For one, the &lt;code&gt;begin()&lt;/code&gt; method itself is a coroutine and must be
&lt;code&gt;co_await&lt;/code&gt;ed to obtain the initial iterator. The increment operation of the iterator
must then be &lt;code&gt;co_await&lt;/code&gt;ed as well to obtain the iterator for the next value.
Unfortunately, asynchronous iterator cannot be used with ranged-based for loops, so
QCoro provides &lt;a href="https://qcoro.dvratil.cz/reference/coro/asyncgenerator/#qcoro_foreach"&gt;&lt;code&gt;QCORO_FOREACH&lt;/code&gt; macro&lt;/a&gt; to make using asynchronous generators simpler.&lt;/p&gt;
&lt;p&gt;Read the &lt;a href="https://qcoro.dvratil.cz/reference/coro/asyncgenerator"&gt;documentation for &lt;code&gt;QCoro::AsyncGenerator&amp;lt;T&amp;gt;&lt;/code&gt;&lt;/a&gt; for more details.&lt;/p&gt;
&lt;h3 id="new-qcorowebsockets-module"&gt;New QCoroWebSockets module&lt;/h3&gt;
&lt;p&gt;The QCoroWebSockets module provides QCoro wrappers for &lt;code&gt;QWebSocket&lt;/code&gt; and &lt;code&gt;QWebSocketServer&lt;/code&gt;
classes to make them usable with coroutines. Like the other modules, it&amp;rsquo;s a standalone
shared or static library that you must explicitly link against in order to be able to use
it, so you don&amp;rsquo;t have to worry that QCoro would pull websockets dependency into your
project if you don&amp;rsquo;t want to.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-cpp" data-lang="cpp"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;QCoro&lt;span style="color:#f92672"&gt;::&lt;/span&gt;Task&lt;span style="color:#f92672"&gt;&amp;lt;&amp;gt;&lt;/span&gt; ChatApp&lt;span style="color:#f92672"&gt;::&lt;/span&gt;handleNotifications(&lt;span style="color:#66d9ef"&gt;const&lt;/span&gt; QUrl &lt;span style="color:#f92672"&gt;&amp;amp;&lt;/span&gt;wsServer) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; (&lt;span style="color:#f92672"&gt;!&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;co_await&lt;/span&gt; qCoro(mWebSocket).open(wsServer)) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; qWarning() &lt;span style="color:#f92672"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;Failed to open websocket connection to&amp;#34;&lt;/span&gt; &lt;span style="color:#f92672"&gt;&amp;lt;&amp;lt;&lt;/span&gt; wsServer &lt;span style="color:#f92672"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;:&amp;#34;&lt;/span&gt; &lt;span style="color:#f92672"&gt;&amp;lt;&amp;lt;&lt;/span&gt; mWebSocket&lt;span style="color:#f92672"&gt;-&amp;gt;&lt;/span&gt;errorString();
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;co_return&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; qDebug() &lt;span style="color:#f92672"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;Connected to&amp;#34;&lt;/span&gt; &lt;span style="color:#f92672"&gt;&amp;lt;&amp;lt;&lt;/span&gt; wsServer;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// Loops whenever a message is received until the socket is disconnected
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; QCORO_FOREACH(&lt;span style="color:#66d9ef"&gt;const&lt;/span&gt; QString &lt;span style="color:#f92672"&gt;&amp;amp;&lt;/span&gt;rawMessage, qCoro(mWebSocket).textMessages()) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;const&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;auto&lt;/span&gt; message &lt;span style="color:#f92672"&gt;=&lt;/span&gt; parseMessage(rawMessage);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;switch&lt;/span&gt; (message.type) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;case&lt;/span&gt; MessageType&lt;span style="color:#f92672"&gt;::&lt;/span&gt;ChatMessage:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; handleChatMessage(message);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;break&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;case&lt;/span&gt; MessageType&lt;span style="color:#f92672"&gt;::&lt;/span&gt;PresenceChange:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; handlePresenceChange(message);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;break&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;case&lt;/span&gt; MessageType&lt;span style="color:#f92672"&gt;::&lt;/span&gt;Invalid:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; qWarning() &lt;span style="color:#f92672"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;Received an invalid message:&amp;#34;&lt;/span&gt; &lt;span style="color:#f92672"&gt;&amp;lt;&amp;lt;&lt;/span&gt; message.error;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;break&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The &lt;code&gt;textMessages()&lt;/code&gt; methods returns an asynchronous generator, which yields the message
whenever it arrives. The messages are received and enqueued as long as the generator
object exists. The difference between using a generator and just &lt;code&gt;co_await&lt;/code&gt;ing the next
emission of the &lt;code&gt;QWebSocket::textMessage()&lt;/code&gt; signal is that the generator holds a connection
to the signal for its entire lifetime, so no signal emission is lost. If we were only
&lt;code&gt;co_await&lt;/code&gt;ing a singal emission, any message that is received before we start &lt;code&gt;co_await&lt;/code&gt;ing
again after handling the current message would be lost.&lt;/p&gt;
&lt;p&gt;You can find more details about the &lt;code&gt;QCoroWebSocket&lt;/code&gt; and &lt;code&gt;QCoroWebSocketSever&lt;/code&gt;
in the &lt;a href="https://qcoro.dvratil.cz/reference/websockets/"&gt;QCoro&amp;rsquo;s websocket module documentation&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;You can build QCoro without the WebSockets module by passing &lt;code&gt;-DQCORO_WITH_QTWEBSOCKETS=OFF&lt;/code&gt;
to CMake.&lt;/p&gt;
&lt;h3 id="deprecated-tasksh-header"&gt;Deprecated tasks.h header&lt;/h3&gt;
&lt;p&gt;The &lt;code&gt;task.h&lt;/code&gt; header and it&amp;rsquo;s camelcase variant &lt;code&gt;Task&lt;/code&gt; been deprecated in QCoro 0.6.0
in favor of &lt;code&gt;qcorotask.h&lt;/code&gt; (and &lt;code&gt;QCoroTask&lt;/code&gt; camelcase version). The main reasons are to
avoid such a generic name in a library and to make the name consistent with the rest of
QCoro&amp;rsquo;s public headers which all start with &lt;code&gt;qcoro&lt;/code&gt; (or &lt;code&gt;QCoro&lt;/code&gt;) prefix.&lt;/p&gt;
&lt;p&gt;The old header is still present and fully functional, but including it will produce a
warning that you should port your code to use &lt;code&gt;qcorotask.h&lt;/code&gt;. You can suppress the warning
by defining &lt;code&gt;QCORO_NO_WARN_DEPRECATED_TASK_H&lt;/code&gt; in the compiler definitions:&lt;/p&gt;
&lt;p&gt;CMake:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-cmake" data-lang="cmake"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;add_compiler_definitions(&lt;span style="color:#e6db74"&gt;QCORO_NO_WARN_DEPRECATED_TASK_H&lt;/span&gt;)&lt;span style="color:#960050;background-color:#1e0010"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;QMake&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code class="language-qmake" data-lang="qmake"&gt;DEFINES += QCORO_NO_WARN_DEPRECATED_TASK_H
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The header file will be removed at some point in the future, at latest in the 1.0 release.&lt;/p&gt;
&lt;p&gt;You can also pass &lt;code&gt;-DQCORO_DISABLE_DEPRECATED_TASK_H=ON&lt;/code&gt; to CMake when compiling QCoro
to prevent it from installing the deprecated task.h header.&lt;/p&gt;
&lt;h3 id="clang-cl-and-apple-clang-support"&gt;Clang-cl and apple-clang support&lt;/h3&gt;
&lt;p&gt;The clang compiler is fully supported by QCoro since 0.4.0. This version of QCoro
intruduces supports for clang-cl and apple-clang.&lt;/p&gt;
&lt;p&gt;Clang-cl is a compiler-driver that provides MSVC-compatible command line options,
allowing to use clang and LLVM as a drop-in replacement for the MSVC toolchain.&lt;/p&gt;
&lt;p&gt;Apple-clang is the official build of clang provided by Apple on MacOS, which may be
different from the upstream clang releases.&lt;/p&gt;
&lt;h3 id="full-changelog"&gt;Full changelog&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Enable exceptions when compiling with clang-cl (&lt;a href="https://github.com/danvratil/qcoro/issues/90"&gt;#90&lt;/a&gt;, &lt;a href="https://github.com/danvratil/qcoro/pull/91"&gt;#91&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;Add option to generate code coverage report (&lt;a href="https://github.com/danvratil/qcoro/commit/0f0408ce927e50450ab847cf290dd229b2a6e12c"&gt;commit 0f0408c&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;Lower CMake requirement to 3.18.4 (&lt;a href="https://github.com/danvratil/qcoro/commit/deb80c13d9c9d866304fd5a64a33168adab34111"&gt;commit deb80c1&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;Add support for clang-cl (&lt;a href="https://github.com/danvratil/qcoro/issues/84"&gt;#84&lt;/a&gt;, &lt;a href="https://github.com/danvratil/qcoro/pull/86"&gt;#86&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;Avoid identifiers that begin with underscore and uppercase letter (&lt;a href="https://github.com/danvratil/qcoro/pull/83"&gt;#83&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;Add mising &lt;code&gt;&amp;lt;chrono&amp;gt;&lt;/code&gt; include (&lt;a href="https://github.com/danvratil/qcoro/pull/82"&gt;#82&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;New module: QCoroWebSockets (&lt;a href="https://github.com/danvratil/qcoro/pull/75"&gt;#75&lt;/a&gt;, &lt;a href="https://github.com/danvratil/qcoro/pull/88"&gt;#88&lt;/a&gt;, &lt;a href="https://github.com/danvratil/qcoro/pull/89"&gt;#89&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;Add &lt;code&gt;QCoroFwd&lt;/code&gt; header with forward-declarations of relevant types (&lt;a href="https://github.com/danvratil/qcoro/issues/71"&gt;#71&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;Deprecate &lt;code&gt;task.h&lt;/code&gt; header file in favor of &lt;code&gt;qcorotask.h&lt;/code&gt; (&lt;a href="https://github.com/danvratil/qcoro/pull/70"&gt;#70&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;Fix installing export headers (&lt;a href="https://github.com/danvratil/qcoro/pull/77"&gt;#77&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;Introduce support for generator coroutines (&lt;a href="https://github.com/danvratil/qcoro/pulls/69"&gt;#69&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;QCoro is now build with &amp;ldquo;modern Qt&amp;rdquo; compile definitions (&lt;a href="https://github.com/danvratil/qcoro/pull/66"&gt;#66&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;Export QCoro wrapper classes (&lt;a href="https://github.com/danvratil/qcoro/issues/63"&gt;#63&lt;/a&gt;, &lt;a href="https://github.com/danvratil/qcoro/pull/65"&gt;#65&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;Extended CI to include MSVC, apple-clang and multiple version of gcc and clang-cl (&lt;a href="https://github.com/danvratil/qcoro/pull/60"&gt;#60&lt;/a&gt;, &lt;a href="https://github.com/danvratil/qcoro/pull/61"&gt;#61&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;Fixed build with apple-clang&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id="download"&gt;Download&lt;/h2&gt;
&lt;p&gt;You can download QCoro 0.6.0 &lt;a href="https://github.com/danvratil/qcoro/releases/tag/v0.6.0"&gt;here&lt;/a&gt; or check the latest sources on &lt;a href="https://github.com/danvratil/qcoro"&gt;QCoro GitHub&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id="more-about-qcoro"&gt;More About QCoro&lt;/h2&gt;
&lt;p&gt;If you are interested in learning more about QCoro, go read the &lt;a href="https://qcoro.dvratil.cz/"&gt;documentation&lt;/a&gt;, look at the
&lt;a href="https://www.dvratil.cz/2021/08/first-qcoro-release"&gt;first release announcement&lt;/a&gt;, which contains a nice explanation and example or
watch &lt;a href="https://www.youtube.com/watch?v=KKVqFqbXJaU&amp;amp;list=PLsHpGlwPdtMq6pJ4mqBeYNWOanjdIIPTJ&amp;amp;index=20"&gt;recording of my talk about C++20 coroutines and QCoro&lt;/a&gt; this years&amp;rsquo; Akademy.&lt;/p&gt;</content></entry><entry><title>QCoro 0.5.0 Release Announcement</title><link rel="alternate" href="https://dvratil.cz/2022/04/qcoro-0.5.0-release-announcement/"/><id>https://dvratil.cz/2022/04/qcoro-0.5.0-release-announcement/</id><published>2022-04-25T22:00:00+0000</published><updated>2022-04-25T22:00:00+0000</updated><category term="QCoro"/><summary>&lt;p&gt;After another few months I&amp;rsquo;m happy to announce a new release of QCoro, which brings several new features and a bunch
of bugfixes.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;.then() continuation for &lt;code&gt;Task&amp;lt;T&amp;gt;&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;All asynchronous operations now return &lt;code&gt;Task&amp;lt;T&amp;gt;&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Timeouts for many operations&lt;/li&gt;
&lt;li&gt;Support for &lt;code&gt;QThread&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="then-continuation-for-task"&gt;.then() continuation for Task&lt;T&gt;&lt;/h2&gt;
&lt;p&gt;Sometimes it&amp;rsquo;s not possible to &lt;code&gt;co_await&lt;/code&gt; a coroutine - usually because you need to integrate with a 3rd party code
that is not coroutine-ready. A good example might be implementing &lt;code&gt;QAbstractItemModel&lt;/code&gt;, where none of the virtual
methods are coroutines and thus it&amp;rsquo;s not possible to use &lt;code&gt;co_await&lt;/code&gt; in them.&lt;/p&gt;</summary><content type="html" xml:base="https://dvratil.cz/2022/04/qcoro-0.5.0-release-announcement/">&lt;p&gt;After another few months I&amp;rsquo;m happy to announce a new release of QCoro, which brings several new features and a bunch
of bugfixes.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;.then() continuation for &lt;code&gt;Task&amp;lt;T&amp;gt;&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;All asynchronous operations now return &lt;code&gt;Task&amp;lt;T&amp;gt;&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Timeouts for many operations&lt;/li&gt;
&lt;li&gt;Support for &lt;code&gt;QThread&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="then-continuation-for-task"&gt;.then() continuation for Task&lt;T&gt;&lt;/h2&gt;
&lt;p&gt;Sometimes it&amp;rsquo;s not possible to &lt;code&gt;co_await&lt;/code&gt; a coroutine - usually because you need to integrate with a 3rd party code
that is not coroutine-ready. A good example might be implementing &lt;code&gt;QAbstractItemModel&lt;/code&gt;, where none of the virtual
methods are coroutines and thus it&amp;rsquo;s not possible to use &lt;code&gt;co_await&lt;/code&gt; in them.&lt;/p&gt;
&lt;p&gt;To still make it possible to all coroutines from such code, &lt;code&gt;QCoro::Task&amp;lt;T&amp;gt;&lt;/code&gt; now has a new method: &lt;code&gt;.then()&lt;/code&gt;,
which allows attaching a continuation callback that will be invoked by QCoro when the coroutine represented
by the &lt;code&gt;Task&lt;/code&gt; finishes.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-cpp" data-lang="cpp"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;void&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;notACoroutine&lt;/span&gt;() {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; someCoroutineReturningQString().then([](&lt;span style="color:#66d9ef"&gt;const&lt;/span&gt; QString &lt;span style="color:#f92672"&gt;&amp;amp;&lt;/span&gt;result) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// Will be invoked when the someCoroutine() finishes.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// The result of the coroutine is passed as an argument to the continuation.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; });
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The continuation itself might be a coroutine, and the result of the &lt;code&gt;.then()&lt;/code&gt; member function is again a &lt;code&gt;Task&amp;lt;R&amp;gt;&lt;/code&gt;
(where &lt;code&gt;R&lt;/code&gt; is the return type of the continuation callback), so it is possible to chain multiple continuations
as well as &lt;code&gt;co_await&lt;/code&gt;ing the entire chain.&lt;/p&gt;
&lt;h2 id="all-asynchronous-operations-now-return-taskt"&gt;All asynchronous operations now return &lt;code&gt;Task&amp;lt;T&amp;gt;&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;Up until now each operation from the QCoro wrapper types returned a special awaitable - for example,
&lt;code&gt;QCoroIODevice::read()&lt;/code&gt; returned &lt;code&gt;QCoro::detail::QCoroIODevice::ReadOperation&lt;/code&gt;. In most cases users of QCoro do
not need to concern themselves with that type, since they can still directly &lt;code&gt;co_await&lt;/code&gt; the returned awaitable.&lt;/p&gt;
&lt;p&gt;However, it unnecessarily leaks implementation details of QCoro into public API and it makes it harded to return
a coroutine from a non-coroutine function.&lt;/p&gt;
&lt;p&gt;As of QCoro 0.5.0, all the operations now return &lt;code&gt;Task&amp;lt;T&amp;gt;&lt;/code&gt;, which makes the API consistent. As a secondary effect,
all the operations can have a chained continuation using the &lt;code&gt;.then()&lt;/code&gt; continuation, as described above.&lt;/p&gt;
&lt;h2 id="timeout-support-for-many-operations"&gt;Timeout support for many operations&lt;/h2&gt;
&lt;p&gt;Qt doesn&amp;rsquo;t allow specifying timeout for many operations, because they are typically non-blocking. But the timeout
makes sense in most QCoro cases, because they are combination of wait + the non-blocking operation. Let&amp;rsquo;s take
&lt;code&gt;QIODevice::read()&lt;/code&gt; for example: the Qt version doesn&amp;rsquo;t have any timeout, because the call will never block - if
there&amp;rsquo;s nothing to read, it simply returns an empty &lt;code&gt;QByteArray&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;On the other hand, &lt;code&gt;QCoroIODevice::read()&lt;/code&gt; is an asynchronous operation, because under to hood, it&amp;rsquo;s a coroutine
that asynchronously calls a sequence of&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-cpp" data-lang="cpp"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;device&lt;span style="color:#f92672"&gt;-&amp;gt;&lt;/span&gt;waitForReadyRead();
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;device&lt;span style="color:#f92672"&gt;-&amp;gt;&lt;/span&gt;read();
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Since &lt;code&gt;QIODevice::waitForReadyRead()&lt;/code&gt; takes a timeout argument, it makes sense for &lt;code&gt;QCoroIODevice::read()&lt;/code&gt;
to also take (an optional) timeout argument. This and many other operations have gained support for timeout.&lt;/p&gt;
&lt;h2 id="support-for-qthread"&gt;Support for &lt;code&gt;QThread&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;It&amp;rsquo;s been a while since I added a new wrapper for a Qt class, so QCoro 0.5.0 adds wrapper for &lt;code&gt;QThread&lt;/code&gt;. It&amp;rsquo;s
now possible to &lt;code&gt;co_await&lt;/code&gt; thread start and end:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-cpp" data-lang="cpp"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;std&lt;span style="color:#f92672"&gt;::&lt;/span&gt;unique_ptr&lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt;QThread&lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;thread&lt;/span&gt;(QThread&lt;span style="color:#f92672"&gt;::&lt;/span&gt;create([]() {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; ...
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;});
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;ui&lt;span style="color:#f92672"&gt;-&amp;gt;&lt;/span&gt;setLabel(tr(&lt;span style="color:#e6db74"&gt;&amp;#34;Starting thread...&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;thread&lt;/span&gt;&lt;span style="color:#f92672"&gt;-&amp;gt;&lt;/span&gt;start();
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;co_await&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;qCoro&lt;/span&gt;(&lt;span style="color:#66d9ef"&gt;thread&lt;/span&gt;)&lt;span style="color:#f92672"&gt;-&amp;gt;&lt;/span&gt;waitForStarted();
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;ui&lt;span style="color:#f92672"&gt;-&amp;gt;&lt;/span&gt;setLabel(tr(&lt;span style="color:#e6db74"&gt;&amp;#34;Calculating...&amp;#34;&lt;/span&gt;));
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;co_await&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;qCoro&lt;/span&gt;(&lt;span style="color:#66d9ef"&gt;thread&lt;/span&gt;)&lt;span style="color:#f92672"&gt;-&amp;gt;&lt;/span&gt;waitForFinished();
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;ui&lt;span style="color:#f92672"&gt;-&amp;gt;&lt;/span&gt;setLabel(tr(&lt;span style="color:#e6db74"&gt;&amp;#34;Finished!&amp;#34;&lt;/span&gt;));
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="full-changelog"&gt;Full changelog&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;.then()&lt;/code&gt; continuation for &lt;code&gt;Task&amp;lt;T&amp;gt;&lt;/code&gt; (&lt;a href="https://github.com/danvratil/qcoro/pull/39"&gt;#39&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;Fixed namespace scoping (&lt;a href="https://github.com/danvratil/qcoro/pull/45"&gt;#45&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;Fixed &lt;code&gt;QCoro::waitFor()&lt;/code&gt; getting stuck when coroutine returns synchronously (&lt;a href="https://github.com/danvratil/qcoro/pull/46"&gt;#46&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;Fixed -pthread usage in CMake (&lt;a href="https://github.com/danvratil/qcoro/pull/47"&gt;#47&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;Produce QMake config files (.pri) for each module (&lt;a href="https://github.com/danvratil/qcoro/commit/e215616be8174438e907710025a7bd71e66a64b5"&gt;commit e215616&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;Fix build on platforms where -latomic must be linked explicitly (&lt;a href="https://github.com/danvratil/qcoro/pull/52"&gt;#52&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;Return &lt;code&gt;Task&amp;lt;T&amp;gt;&lt;/code&gt; from all operations (&lt;a href="https://github.com/danvratil/qcoro/pull/54"&gt;#54&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;Add QCoro wrapper for &lt;code&gt;QThread&lt;/code&gt; (&lt;a href="https://github.com/danvratil/qcoro/commit/832d931068312c906db6858493fc952b8d984b1c"&gt;commit 832d931&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;Many documentation updates&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Thanks to everyone who contributed to QCoro!&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="download"&gt;Download&lt;/h2&gt;
&lt;p&gt;You can download QCoro 0.5.0 &lt;a href="https://github.com/danvratil/qcoro/releases/tag/v0.5.0"&gt;here&lt;/a&gt; or check the latest sources on &lt;a href="https://github.com/danvratil/qcoro"&gt;QCoro GitHub&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id="more-about-qcoro"&gt;More About QCoro&lt;/h2&gt;
&lt;p&gt;If you are interested in learning more about QCoro, go read the &lt;a href="https://qcoro.dvratil.cz/"&gt;documentation&lt;/a&gt;, look at the
&lt;a href="https://www.dvratil.cz/2021/08/first-qcoro-release"&gt;first release announcement&lt;/a&gt;, which contains a nice explanation and example or
watch &lt;a href="https://www.youtube.com/watch?v=KKVqFqbXJaU&amp;amp;list=PLsHpGlwPdtMq6pJ4mqBeYNWOanjdIIPTJ&amp;amp;index=20"&gt;recording of my talk about C++20 coroutines and QCoro&lt;/a&gt; this years&amp;rsquo; Akademy.&lt;/p&gt;</content></entry><entry><title>QCoro 0.4.0 Release Announcement</title><link rel="alternate" href="https://dvratil.cz/2022/01/qcoro-0.4.0-release-announcement/"/><id>https://dvratil.cz/2022/01/qcoro-0.4.0-release-announcement/</id><published>2022-01-06T19:00:00+0000</published><updated>2022-01-06T19:00:00+0000</updated><category term="QCoro"/><summary>&lt;p&gt;It took a few months, but there&amp;rsquo;s a new release of QCoro with some new cool features. This change contains a breaking
change in CMake, wich requires QCoro users to adjust their CMakeLists.txt. I sincerely hope this is the last breaking
change for a very long time.&lt;/p&gt;
&lt;p&gt;Major highlights in this release:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Co-installability of Qt5 and Qt6 builds of QCoro&lt;/li&gt;
&lt;li&gt;Complete re-work of CMake configuration&lt;/li&gt;
&lt;li&gt;Support for compiling QCoro with Clang against libstdc++&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="co-installability-of-qt5-and-qt6-builds-of-qcoro"&gt;Co-installability of Qt5 and Qt6 builds of QCoro&lt;/h2&gt;
&lt;p&gt;This change mostly affects packagers of QCoro. It is now possible to install both Qt5 and Qt6 versions
of QCoro alongside each other without conflicting files. The shared libraries now contain the Qt version
number in their name (e.g. &lt;code&gt;libQCoro6Core.so&lt;/code&gt;) and header files are also located in dedicated subdirectories
(e.g. &lt;code&gt;/usr/include/qcoro6/{qcoro,QCoro}&lt;/code&gt;). User of QCoro should not need to do any changes to their codebase.&lt;/p&gt;</summary><content type="html" xml:base="https://dvratil.cz/2022/01/qcoro-0.4.0-release-announcement/">&lt;p&gt;It took a few months, but there&amp;rsquo;s a new release of QCoro with some new cool features. This change contains a breaking
change in CMake, wich requires QCoro users to adjust their CMakeLists.txt. I sincerely hope this is the last breaking
change for a very long time.&lt;/p&gt;
&lt;p&gt;Major highlights in this release:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Co-installability of Qt5 and Qt6 builds of QCoro&lt;/li&gt;
&lt;li&gt;Complete re-work of CMake configuration&lt;/li&gt;
&lt;li&gt;Support for compiling QCoro with Clang against libstdc++&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="co-installability-of-qt5-and-qt6-builds-of-qcoro"&gt;Co-installability of Qt5 and Qt6 builds of QCoro&lt;/h2&gt;
&lt;p&gt;This change mostly affects packagers of QCoro. It is now possible to install both Qt5 and Qt6 versions
of QCoro alongside each other without conflicting files. The shared libraries now contain the Qt version
number in their name (e.g. &lt;code&gt;libQCoro6Core.so&lt;/code&gt;) and header files are also located in dedicated subdirectories
(e.g. &lt;code&gt;/usr/include/qcoro6/{qcoro,QCoro}&lt;/code&gt;). User of QCoro should not need to do any changes to their codebase.&lt;/p&gt;
&lt;h2 id="complete-re-work-of-cmake-configuration"&gt;Complete re-work of CMake configuration&lt;/h2&gt;
&lt;p&gt;This change affects users of QCoro, as they will need to adjust CMakeLists.txt of their projects. First,
depending on whether they want to use Qt5 or Qt6 version of QCoro, a different package must be used.
Additionally, list of QCoro components to use must be specified:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;find_package(QCoro5 REQUIRED COMPONENTS Core Network DBus)
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Finally, the target names to use in &lt;code&gt;target_link_libraries&lt;/code&gt; have changed as well:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;QCoro::Core&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;QCoro::Network&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;QCoro::DBus&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The version-less &lt;code&gt;QCoro&lt;/code&gt; namespace can be used regardless of whether using Qt5 or Qt6 build of QCoro.
&lt;code&gt;QCoro5&lt;/code&gt; and &lt;code&gt;QCoro6&lt;/code&gt; namespaces are available as well, in case users need to combine both Qt5 and Qt6
versions in their codebase.&lt;/p&gt;
&lt;p&gt;This change brings QCoro CMake configuration system to the same style and behavior as Qt itself, so it
should now be easier to use QCoro, especially when supporting both Qt5 and Qt6.&lt;/p&gt;
&lt;h2 id="support-for-compiling-qcoro-with-clang-against-libstdc"&gt;Support for compiling QCoro with Clang against libstdc++&lt;/h2&gt;
&lt;p&gt;Until now, when the Clang compiler was detected, QCoro forced usage of LLVM&amp;rsquo;s libc++ standard library.
Coroutine support requires tight co-operation between the compiler and standard library. Because Clang
still considers their coroutine support experimental it expects all coroutine-related types in standard
library to be located in &lt;code&gt;std::experimental&lt;/code&gt; namespace. In GNU&amp;rsquo;s libstdc++, coroutines are fully supported
and thus implemented in the &lt;code&gt;std&lt;/code&gt; namespace. This requires a little bit of extra glue, which is now in place.&lt;/p&gt;
&lt;h3 id="full-changelog"&gt;Full changelog&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;QCoro can now be built with Clang against libstdc++ (&lt;a href="https://github.com/danvratil/qcoro/pull/38"&gt;#38&lt;/a&gt;, &lt;a href="https://github.com/danvratil/qcoro/issues/22"&gt;#22&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;Qt5 and Qt6 builds of QCoro are now co-installable (&lt;a href="https://github.com/danvratil/qcoro/issues/36"&gt;#36&lt;/a&gt;, &lt;a href="https://github.com/danvratil/qcoro/pull/37"&gt;#37&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;Fixed early co_return not resuming the caller (&lt;a href="https://github.com/danvratil/qcoro/issue/24"&gt;#24&lt;/a&gt;, &lt;a href="https://github.com/danvratil/qcoro/pull/35"&gt;#35&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;Fixed QProcess example (&lt;a href="https://github.com/danvratil/qcoro/pull/34"&gt;#34&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;Test suite has been improved and extended (&lt;a href="https://github.com/danvratil/qcoro/pull/29"&gt;#29&lt;/a&gt;, &lt;a href="https://github.com/danvratil/qcoro/pull/31"&gt;#31&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;Task move assignment operator checks for self-assignment (&lt;a href="https://github.com/danvratil/qcoro/pull/27"&gt;#27&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;QCoro can now be built as a subdirectory inside another CMake project (&lt;a href="https://github.com/danvratil/qcoro/pull/25"&gt;#25&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;Fixed QCoroCore/qcorocore.h header (&lt;a href="https://github.com/danvratil/qcoro/pull/23"&gt;#23&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;DBus is disabled by default on Windows, Mac and Android (&lt;a href="https://github.com/danvratil/qcoro/pull/21"&gt;#21&lt;/a&gt;)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Thanks to everyone who contributed to QCoro!&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="download"&gt;Download&lt;/h2&gt;
&lt;p&gt;You can download QCoro 0.4.0 &lt;a href="https://github.com/danvratil/qcoro/releases/tag/v0.4.0"&gt;here&lt;/a&gt; or check the latest sources on &lt;a href="https://github.com/danvratil/qcoro"&gt;QCoro GitHub&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id="more-about-qcoro"&gt;More About QCoro&lt;/h2&gt;
&lt;p&gt;If you are interested in learning more about QCoro, go read the &lt;a href="https://qcoro.dvratil.cz/"&gt;documentation&lt;/a&gt;, look at the
&lt;a href="https://www.dvratil.cz/2021/08/first-qcoro-release"&gt;first release announcement&lt;/a&gt;, which contains a nice explanation and example or
watch &lt;a href="https://www.youtube.com/watch?v=KKVqFqbXJaU&amp;amp;list=PLsHpGlwPdtMq6pJ4mqBeYNWOanjdIIPTJ&amp;amp;index=20"&gt;recording of my talk about C++20 coroutines and QCoro&lt;/a&gt; this years&amp;rsquo; Akademy.&lt;/p&gt;</content></entry></feed>