Jekyll2023-12-05T14:43:33-06:00https://dvratil.cz/feed.xmlDaniel Vrátil’s blogDaniel Vrátil's personal blog.Daniel VrátilQCoro 0.10.0 Release Announcement2023-12-05T15:30:00-06:002023-12-05T15:30:00-06:00https://dvratil.cz/2023/12/qcoro-0.10.0-announcement<!--
SPDX-FileCopyrightText: 2023 Daniel Vrátil <dvratil@kde.org>
SPDX-License-Identifier: GFDL-1.3-or-later
-->
<h1 id="qcoro-0100-release-announcement">QCoro 0.10.0 Release Announcement</h1>
<p>Thank you to everyone who reported issues and contributed to QCoro.
Your help is much appreciated!</p>
<h2 id="support-for-awaiting-qt-signals-with-qprivatesignal">Support for awaiting Qt signals with QPrivateSignal</h2>
<p>Qt has a feature where signals can be made “private” (in the sense that only class
that defines the signal can emit it) by appending <code class="language-plaintext highlighter-rouge">QPrivateSignal</code> argument to the
signal method:</p>
<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">class</span> <span class="nc">MyObject</span> <span class="o">:</span> <span class="k">public</span> <span class="n">QObject</span> <span class="p">{</span>
<span class="n">Q_OBJECT</span>
<span class="p">...</span>
<span class="nl">Q_SIGNALS:</span>
<span class="kt">void</span> <span class="n">error</span><span class="p">(</span><span class="kt">int</span> <span class="n">code</span><span class="p">,</span> <span class="k">const</span> <span class="n">QString</span> <span class="o">&</span><span class="n">message</span><span class="p">,</span> <span class="n">QPrivateSignal</span><span class="p">);</span>
<span class="p">};</span>
</code></pre></div></div>
<p><code class="language-plaintext highlighter-rouge">QPrivateSignal</code> is a type that is defined inside the <code class="language-plaintext highlighter-rouge">Q_OBJECT</code> macro, so it’s
private and as such only <code class="language-plaintext highlighter-rouge">MyObject</code> class can emit the signal, since only <code class="language-plaintext highlighter-rouge">MyObject</code>
can instantiate <code class="language-plaintext highlighter-rouge">QPrivateSignal</code>:</p>
<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">void</span> <span class="n">MyObject</span><span class="o">::</span><span class="n">handleError</span><span class="p">(</span><span class="kt">int</span> <span class="n">code</span><span class="p">,</span> <span class="k">const</span> <span class="n">QString</span> <span class="o">&</span><span class="n">message</span><span class="p">)</span>
<span class="p">{</span>
<span class="n">Q_EMIT</span> <span class="n">error</span><span class="p">(</span><span class="n">code</span><span class="p">,</span> <span class="n">message</span><span class="p">,</span> <span class="n">QPrivateSignal</span><span class="p">{});</span>
<span class="p">}</span>
</code></pre></div></div>
<p>QCoro has a feature that makes it possible to <code class="language-plaintext highlighter-rouge">co_await</code> a signal emission and
returns the signals arguments as a tuple:</p>
<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code>
<span class="n">MyObject</span> <span class="n">myObject</span><span class="p">;</span>
<span class="k">const</span> <span class="k">auto</span> <span class="p">[</span><span class="n">code</span><span class="p">,</span> <span class="n">message</span><span class="p">]</span> <span class="o">=</span> <span class="n">co_await</span> <span class="nf">qCoro</span><span class="p">(</span><span class="o">&</span><span class="n">myObject</span><span class="p">,</span> <span class="o">&</span><span class="n">MyObject</span><span class="o">::</span><span class="n">handleError</span><span class="p">);</span>
</code></pre></div></div>
<p>While it was possible to <code class="language-plaintext highlighter-rouge">co_await</code> a “private” signal previously, it would get
return the <code class="language-plaintext highlighter-rouge">QPrivateSignal</code> value as an additional value in the result tuple
and on some occasions would not compile at all.</p>
<p>In QCoro 0.10, we can detect the <code class="language-plaintext highlighter-rouge">QPrivateSignal</code> argument and drop it inside QCoro
so that it does not cause trouble and does not clutter the result type.</p>
<p>Achieving this wasn’t simple, as it’s not really possible to detect the type (because
it’s private), e.g. code like this would fail to compile, because we are not allowed
to refer to <code class="language-plaintext highlighter-rouge">Obj::QPrivateSignal</code>, since that type is private to <code class="language-plaintext highlighter-rouge">Obj</code>.</p>
<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">template</span><span class="o"><</span><span class="k">typename</span> <span class="nc">T</span><span class="p">,</span> <span class="k">typename</span> <span class="nc">Obj</span><span class="p">></span>
<span class="k">constexpr</span> <span class="kt">bool</span> <span class="n">is_qprivatesignal</span> <span class="o">=</span> <span class="n">std</span><span class="o">::</span><span class="n">same_as_v</span><span class="o"><</span><span class="n">T</span><span class="p">,</span> <span class="k">typename</span> <span class="n">Obj</span><span class="o">::</span><span class="n">QPrivateSignal</span><span class="o">></span><span class="p">;</span>
</code></pre></div></div>
<p>After many different attempts we ended up abusing <code class="language-plaintext highlighter-rouge">__PRETTY_FUNCTION__</code>
(and <code class="language-plaintext highlighter-rouge">__FUNCSIG__</code> on MSVC) and checking whether the function’s name contains
<code class="language-plaintext highlighter-rouge">QPrivateSignal</code> string in the expected location. It’s a whacky hack, but hey - if it
works, it’s not stupid :). And thanks to improvements in compile-time evaluation in
C++20, the check is evaluated completely at compile-time, so there’s no runtime
overhead of obtaining current source location and doing string comparisons.</p>
<h2 id="source-code-reorganization-again">Source Code Reorganization (again!)</h2>
<p>Big part of QCoro are template classes, so there’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.</p>
<p>Therefore I decided to move definitions into separated files, so that they don’t
clutter the main include files.</p>
<p>This change is completely source- and binary-compatible, so QCoro users don’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.</p>
<h2 id="bugfixes">Bugfixes</h2>
<ul>
<li><code class="language-plaintext highlighter-rouge">QCoro::waitFor()</code> now re-throws exceptions (<a href="https://github.com/danvratil/qcoro/issues/172">#172</a>, Daniel Vrátil)</li>
<li>Replaced deprecated <code class="language-plaintext highlighter-rouge">QWebSocket::error</code> with <code class="language-plaintext highlighter-rouge">QWbSocket::errorOccured</code> in QCoroWebSockets module (<a href="https://github.com/danvratil/qcoro/pulls/174">#174</a>, Marius P)</li>
<li>Fix <code class="language-plaintext highlighter-rouge">QCoro::connect()</code> not working with lambdas (<a href="https://github.com/danvratil/qcoro/pulls/179">#179</a>, Johan Brüchert)</li>
<li>Fix library name postfix for qmake compatibilty (<a href="https://github.com/danvratil/qcoro/pulls/192">#192</a>, Shantanu Tushar)</li>
<li>Fix <code class="language-plaintext highlighter-rouge">std::coroutine_traits isn't a class template</code> error with LLVM 16 (<a href="https://github.com/danvratil/qcoro/pulls/196">#196</a>, Rafael Sadowski)</li>
</ul>
<h2 id="full-changelog">Full changelog</h2>
<p><a href="https://github.com/danvratil/qcoro/releases/tag/v0.10.0">See changelog on Github</a></p>Daniel Vrátil<!– SPDX-FileCopyrightText: 2023 Daniel Vrátil dvratil@kde.orgQCoro 0.8.0 Release Announcement2023-01-31T13:53:00-06:002023-01-31T13:53:00-06:00https://dvratil.cz/2023/01/qcoro-0.8.0-announcement<!--
SPDX-FileCopyrightText: 2022 Daniel Vrátil <dvratil@kde.org>
SPDX-License-Identifier: GFDL-1.3-or-later
-->
<h1 id="qcoro-080-release-announcement">QCoro 0.8.0 Release Announcement</h1>
<p>This is a rather small release with only two new features and one small improvement.</p>
<p>Big thank you to <a href="https://xstrahl.com">Xstrahl Inc.</a> who sponsored development of
new features included in this release and of QCoro in general.</p>
<p>And as always, thank you to everyone who reported issues and contributed to QCoro.
Your help is much appreciated!</p>
<p><a href="https://qcoro.dvratil.cz/news/2023/2023-01-31-qcoro-0.8.0-announcement/">The original release announcement on qcoro.dvratil.cz</a>.</p>
<h2 id="improved-qcorowaitfor">Improved <code class="language-plaintext highlighter-rouge">QCoro::waitFor()</code></h2>
<p>Up until this version, <code class="language-plaintext highlighter-rouge">QCoro::waitFor()</code> was only usable for <code class="language-plaintext highlighter-rouge">QCoro::Task<T></code>.
Starting with QCoro 0.8.0, it is possible to use it with any type that satisfies
the <code class="language-plaintext highlighter-rouge">Awaitable</code> concept. The concept has also been fixed to satisfies not just
types with the <code class="language-plaintext highlighter-rouge">await_resume()</code>, <code class="language-plaintext highlighter-rouge">await_suspend()</code> and <code class="language-plaintext highlighter-rouge">await_ready()</code> member functions,
but also types with member <code class="language-plaintext highlighter-rouge">operator co_await()</code> and non-member <code class="language-plaintext highlighter-rouge">operator co_await()</code>
functions.</p>
<h2 id="qcorosleepfor-and-qcorosleepuntil"><code class="language-plaintext highlighter-rouge">QCoro::sleepFor()</code> and <code class="language-plaintext highlighter-rouge">QCoro::sleepUntil()</code></h2>
<p>Working both on QCoro codebase as well as some third-party code bases using QCoro
it’s clear that there’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.</p>
<p>Previously I used to create small coroutines like this:</p>
<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">QCoro</span><span class="o">::</span><span class="n">Task</span><span class="o"><></span> <span class="n">timer</span><span class="p">(</span><span class="n">std</span><span class="o">::</span><span class="n">chrono</span><span class="o">::</span><span class="n">milliseconds</span> <span class="n">timeout</span><span class="p">)</span> <span class="p">{</span>
<span class="n">QTimer</span> <span class="n">timer</span><span class="p">;</span>
<span class="n">timer</span><span class="p">.</span><span class="n">setSingleShot</span><span class="p">(</span><span class="nb">true</span><span class="p">);</span>
<span class="n">timer</span><span class="p">.</span><span class="n">start</span><span class="p">(</span><span class="n">timeout</span><span class="p">);</span>
<span class="n">co_await</span> <span class="n">timer</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>
<p>Now we can do the same simply by using <code class="language-plaintext highlighter-rouge">QCoro::sleepFor()</code>.</p>
<p>Read the <a href="https://qcoro.dvratil.cz/reference/core/qtimer/#qcorosleepfor">documentation for <code class="language-plaintext highlighter-rouge">QCoro::sleepFor()</code></a>
and <a href="https://qcoro.dvratil.cz/reference/core/qtimer/#qcorosleepuntil"><code class="language-plaintext highlighter-rouge">QCoro::sleepUntil()</code></a> for more details.</p>
<h2 id="qcoromovetothread"><code class="language-plaintext highlighter-rouge">QCoro::moveToThread()</code></h2>
<p>A small helper coroutine that allows a piece of function to be executed in the context
of another thread.</p>
<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">void</span> <span class="n">App</span><span class="o">::</span><span class="n">runSlowOperation</span><span class="p">(</span><span class="n">QThread</span> <span class="o">*</span><span class="n">helperThread</span><span class="p">)</span> <span class="p">{</span>
<span class="c1">// Still on the main thread</span>
<span class="n">ui</span><span class="o">-></span><span class="n">statusLabel</span><span class="p">.</span><span class="n">setText</span><span class="p">(</span><span class="n">tr</span><span class="p">(</span><span class="s">"Running"</span><span class="p">));</span>
<span class="k">const</span> <span class="n">QString</span> <span class="n">input</span> <span class="o">=</span> <span class="n">ui</span><span class="o">-></span><span class="n">userInput</span><span class="p">.</span><span class="n">text</span><span class="p">();</span>
<span class="n">co_await</span> <span class="n">QCoro</span><span class="o">::</span><span class="n">moveToThread</span><span class="p">(</span><span class="n">helperThread</span><span class="p">);</span>
<span class="c1">// Now we are running in the context of the helper thread, the main thread is not blocked</span>
<span class="c1">// It is safe to use `input` which was created in another thread</span>
<span class="n">doSomeComplexCalculation</span><span class="p">(</span><span class="n">input</span><span class="p">);</span>
<span class="c1">// Move the execution back to the main thread</span>
<span class="n">co_await</span> <span class="n">QCoro</span><span class="o">::</span><span class="n">moveToThread</span><span class="p">(</span><span class="k">this</span><span class="o">-></span><span class="kr">thread</span><span class="p">());</span>
<span class="c1">// Runs on the main thread again</span>
<span class="n">ui</span><span class="o">-></span><span class="n">statusLabel</span><span class="p">.</span><span class="n">setText</span><span class="p">(</span><span class="n">tr</span><span class="p">(</span><span class="s">"Done"</span><span class="p">));</span>
<span class="p">}</span>
</code></pre></div></div>
<p>Read the <a href="https://qcoro.dvratil.cz/reference/core/qthread#qcoromovetothread">documentation for <code class="language-plaintext highlighter-rouge">QCoro::moveToThread</code></a> for more details.</p>
<h2 id="full-changelog">Full changelog</h2>
<p><a href="https://github.com/danvratil/qcoro/releases/tag/v0.8.0">See changelog on Github</a></p>Daniel Vrátil<!– SPDX-FileCopyrightText: 2022 Daniel Vrátil dvratil@kde.orgQCoro 0.7.0 Release Announcement2022-11-20T17:52:00-06:002022-11-20T17:52:00-06:00https://dvratil.cz/2022/11/qcoro-0.7.0-release-announcement<!--
SPDX-FileCopyrightText: 2022 Daniel Vrátil <dvratil@kde.org>
SPDX-License-Identifier: GFDL-1.3-or-later
-->
<h1 id="qcoro-070-release-announcement">QCoro 0.7.0 Release Announcement</h1>
<p>The major new feature in this release is initial QML support, contributed by
Jonah Brüchert. Jonah also contributed <code class="language-plaintext highlighter-rouge">QObject::connect</code> 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.</p>
<p>As always, big thank you to everyone who report issues and contributed to QCoro.
Your help is much appreciated!</p>
<h2 id="initial-qml-support">Initial QML Support</h2>
<p>Jonah Brüchert has contributed initial support for QML. Unfortunately, we
cannot extend the QML engine to support the <code class="language-plaintext highlighter-rouge">async</code> and <code class="language-plaintext highlighter-rouge">await</code> keywords from
ES8, but we can make it possible to set a callback from QML that should be
called when the coroutine finishes.</p>
<p>The problem with <code class="language-plaintext highlighter-rouge">QCoro::Task</code> 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 <code class="language-plaintext highlighter-rouge">QCoro::QmlTask</code> class, which
can wrap any awaitable (be it <code class="language-plaintext highlighter-rouge">QCoro::Task</code> or any generic awaitable type)
and provides a <code class="language-plaintext highlighter-rouge">then()</code> method that can be called from QML and that takes
a JavaScript function as its only argument. The function will be invoked by
<code class="language-plaintext highlighter-rouge">QCoro::QmlTask</code> when the wrapped awaitable has finished.</p>
<p>The disadvantage of this approach is that in order to expose a class that
uses <code class="language-plaintext highlighter-rouge">QCoro::Task<T></code> as return types of its member functions into QML, we
need to create a wrapper class that converts those return types to
<code class="language-plaintext highlighter-rouge">QCoro::QmlTask</code>.</p>
<p>Luckily, we should be able to provide a smoother user experience when using
QCoro in QML for Qt6 in a future QCoro release.</p>
<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">class</span> <span class="nc">QmlCoroTimer</span><span class="o">:</span> <span class="k">public</span> <span class="n">QObject</span> <span class="p">{</span>
<span class="n">Q_OBJECT</span>
<span class="nl">public:</span>
<span class="k">explicit</span> <span class="n">QmlCoroTimer</span><span class="p">(</span><span class="n">QObject</span> <span class="o">*</span><span class="n">parent</span> <span class="o">=</span> <span class="nb">nullptr</span><span class="p">)</span>
<span class="o">:</span> <span class="n">QObject</span><span class="p">(</span><span class="n">parent</span><span class="p">)</span>
<span class="p">{}</span>
<span class="n">Q_INVOCABLE</span> <span class="n">QCoro</span><span class="o">::</span><span class="n">QmlTask</span> <span class="n">start</span><span class="p">(</span><span class="kt">int</span> <span class="n">milliseconds</span><span class="p">)</span> <span class="p">{</span>
<span class="c1">// Implicitly wraps QCoro::Task<> into QCoro::QmlTask</span>
<span class="k">return</span> <span class="n">waitFor</span><span class="p">(</span><span class="n">milliseconds</span><span class="p">);</span>
<span class="p">}</span>
<span class="nl">private:</span>
<span class="c1">// A simple coroutine that co_awaits a timer timeout</span>
<span class="n">QCoro</span><span class="o">::</span><span class="n">Task</span><span class="o"><></span> <span class="n">waitFor</span><span class="p">(</span><span class="kt">int</span> <span class="n">milliseconds</span><span class="p">)</span> <span class="p">{</span>
<span class="n">QTimer</span> <span class="n">timer</span><span class="p">;</span>
<span class="n">timer</span><span class="p">.</span><span class="n">start</span><span class="p">(</span><span class="n">milliseconds</span><span class="p">);</span>
<span class="n">co_await</span> <span class="n">timer</span><span class="p">;</span>
<span class="p">}</span>
<span class="p">};</span>
<span class="p">...</span>
<span class="n">QCoro</span><span class="o">::</span><span class="n">Qml</span><span class="o">::</span><span class="n">registerTypes</span><span class="p">();</span>
<span class="n">qmlRegisterType</span><span class="o"><</span><span class="n">QmlCoroTimer</span><span class="o">></span><span class="p">(</span><span class="s">"cz.dvratil.qcoro.example"</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">1</span><span class="p">);</span>
</code></pre></div></div>
<div class="language-qml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">import</span> <span class="nx">cz</span><span class="p">.</span><span class="nx">dvratil</span><span class="p">.</span><span class="nx">qcoro</span><span class="p">.</span><span class="nx">example</span> <span class="mf">1.0</span>
<span class="kt">Item</span> <span class="p">{</span>
<span class="kt">QmlCoroTimer</span> <span class="p">{</span>
<span class="nl">id</span><span class="p">:</span> <span class="kd">timer</span>
<span class="p">}</span>
<span class="nl">Component.onCompleted</span><span class="p">:</span> <span class="p">()</span> <span class="p">{</span>
<span class="c1">// Attaches a callback to be called when the QmlCoroTimer::waitFor()</span>
<span class="c1">// coroutine finishes.</span>
<span class="nx">timer</span><span class="p">.</span><span class="nx">start</span><span class="p">(</span><span class="mi">1000</span><span class="p">).</span><span class="nx">then</span><span class="p">(()</span> <span class="o">=></span> <span class="p">{</span>
<span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="dl">"</span><span class="s2">1 second elapsed!</span><span class="dl">"</span><span class="p">);</span>
<span class="p">});</span>
<span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>
<p>Read the <a href="https://qcoro.dvratil.cz/reference/qml/qmltask">documentation for <code class="language-plaintext highlighter-rouge">QCoro::QmlTask</code></a> for more details.</p>
<h2 id="qcoroconnect-helper">QCoro::connect Helper</h2>
<p>The <code class="language-plaintext highlighter-rouge">QCoro::connect()</code> helper is similar to <code class="language-plaintext highlighter-rouge">QObject::connect()</code> - except you
you pass in a <code class="language-plaintext highlighter-rouge">QCoro::Task<T></code> instead of a sender and signal pointers. While
using the <code class="language-plaintext highlighter-rouge">.then()</code> continuation can achieve similar results, the main
difference is that <code class="language-plaintext highlighter-rouge">QCoro::connect()</code> takes a pointer to a context (receiver)
QObject. If the receiver is destroyed before the connected <code class="language-plaintext highlighter-rouge">QCoro::Task<T></code>
finishes, the slot is not invoked.</p>
<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">void</span> <span class="n">MyClass</span><span class="o">::</span><span class="n">buttonClicked</span><span class="p">()</span> <span class="p">{</span>
<span class="n">QCoro</span><span class="o">::</span><span class="n">Task</span><span class="o"><</span><span class="n">QByteArray</span><span class="o">></span> <span class="n">task</span> <span class="o">=</span> <span class="n">sendNetworkRequest</span><span class="p">();</span>
<span class="c1">// If this object is deleted before the `task` completes,</span>
<span class="c1">// the slot is not invoked.</span>
<span class="n">QCoro</span><span class="o">::</span><span class="n">connect</span><span class="p">(</span><span class="n">std</span><span class="o">::</span><span class="n">move</span><span class="p">(</span><span class="n">task</span><span class="p">),</span> <span class="k">this</span><span class="p">,</span> <span class="o">&</span><span class="n">handleNetworkReply</span><span class="p">);</span>
<span class="p">}</span>
</code></pre></div></div>
<p>See the <a href="https://qcoro.dvratil.cz/reference/coro/task/#interfacing-with-synchronous-functions">QCoro documentation</a> for more details.</p>
<h2 id="full-changelog">Full changelog</h2>
<p><a href="https://github.com/danvratil/qcoro/releases/tag/v0.7.0">See changelog on Github</a></p>Daniel Vrátil<!– SPDX-FileCopyrightText: 2022 Daniel Vrátil dvratil@kde.orgQCoro 0.6.0 Release Announcement2022-07-09T10:00:00-05:002022-07-09T10:00:00-05:00https://dvratil.cz/2022/07/qcoro-0.6.0-release-announcement<p>I’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.</p>
<p>The four major features are:</p>
<ul>
<li>Generator support</li>
<li>New QCoroWebSockets module</li>
<li>Deprecated task.h</li>
<li>Clang-cl and apple-clang support</li>
</ul>
<p>🎉 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. 🎉</p>
<p>As always, big thank you to everyone who report issues and contributed to QCoro.
Your help is much appreciated!</p>
<h3 id="generator-support">Generator support</h3>
<p>Unlike regular functions (or <code class="language-plaintext highlighter-rouge">QCoro::Task<></code>-based coroutines) which can only ever
produce at most single result (through <code class="language-plaintext highlighter-rouge">return</code> or <code class="language-plaintext highlighter-rouge">co_return</code> 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 <code class="language-plaintext highlighter-rouge">QCoro::Generator<T></code>:</p>
<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// A generator that produces a sequence of numbers from 0 to `end`.</span>
<span class="n">QCoro</span><span class="o">::</span><span class="n">Generator</span><span class="o"><</span><span class="kt">int</span><span class="o">></span> <span class="n">sequence</span><span class="p">(</span><span class="kt">int</span> <span class="n">end</span><span class="p">)</span> <span class="p">{</span>
<span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">i</span> <span class="o"><=</span> <span class="n">end</span><span class="p">;</span> <span class="o">++</span><span class="n">i</span><span class="p">)</span> <span class="p">{</span>
<span class="c1">// Produces current value of `i` and suspends.</span>
<span class="n">co_yield</span> <span class="n">i</span><span class="p">;</span>
<span class="p">}</span>
<span class="c1">// End the iterator</span>
<span class="p">}</span>
<span class="kt">int</span> <span class="nf">sumSequence</span><span class="p">(</span><span class="kt">int</span> <span class="n">end</span><span class="p">)</span> <span class="p">{</span>
<span class="kt">int</span> <span class="n">sum</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
<span class="c1">// Loops over the returned Generator, resuming the generator on each iterator</span>
<span class="c1">// so it can produce a value that we then consume.</span>
<span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">value</span> <span class="o">:</span> <span class="n">sequence</span><span class="p">(</span><span class="n">end</span><span class="p">))</span> <span class="p">{</span>
<span class="n">sum</span> <span class="o">+=</span> <span class="n">value</span><span class="p">;</span>
<span class="p">}</span>
<span class="k">return</span> <span class="n">sum</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>
<p>The <code class="language-plaintext highlighter-rouge">Generator</code> interface implements <code class="language-plaintext highlighter-rouge">begin()</code> and <code class="language-plaintext highlighter-rouge">end()</code> 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.</p>
<p>You can find more details about synchronous generators in the <a href="https://qcoro.dvratil.cz/reference/coro/generator/"><code class="language-plaintext highlighter-rouge">QCoro::Generator<T></code>
documentation</a>.</p>
<p>Asynchronous generators work in a similar way, but they produce value asynchronously,
that is the result of the generator must be <code class="language-plaintext highlighter-rouge">co_await</code>ed by the caller.</p>
<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">QCoro</span><span class="o">::</span><span class="n">AsyncGenerator</span><span class="o"><</span><span class="n">QUrl</span><span class="o">></span> <span class="n">paginator</span><span class="p">(</span><span class="k">const</span> <span class="n">QUrl</span> <span class="o">&</span><span class="n">baseUrl</span><span class="p">)</span> <span class="p">{</span>
<span class="n">QUrl</span> <span class="n">pageUrl</span> <span class="o">=</span> <span class="n">baseUrl</span><span class="p">;</span>
<span class="n">Q_FOREVER</span> <span class="p">{</span>
<span class="n">pageUrl</span> <span class="o">=</span> <span class="n">co_await</span> <span class="n">getNextPage</span><span class="p">(</span><span class="n">pageUrl</span><span class="p">);</span> <span class="c1">// co_awaits next page URL</span>
<span class="k">if</span> <span class="p">(</span><span class="n">pageUrl</span><span class="p">.</span><span class="n">isNull</span><span class="p">())</span> <span class="p">{</span> <span class="c1">// if empty, we reached the last page</span>
<span class="k">break</span><span class="p">;</span> <span class="c1">// leave the loop</span>
<span class="p">}</span>
<span class="n">co_yield</span> <span class="n">pageUrl</span><span class="p">;</span> <span class="c1">// finally, yield the value and suspend</span>
<span class="p">}</span>
<span class="c1">// end the generator</span>
<span class="p">}</span>
<span class="n">QCoro</span><span class="o">::</span><span class="n">AsyncGenerator</span><span class="o"><</span><span class="n">QString</span><span class="o">></span> <span class="n">pageReader</span><span class="p">(</span><span class="k">const</span> <span class="n">QUrl</span> <span class="o">&</span><span class="n">baseUrl</span><span class="p">)</span> <span class="p">{</span>
<span class="c1">// Create a new generator</span>
<span class="k">auto</span> <span class="n">generator</span> <span class="o">=</span> <span class="n">paginator</span><span class="p">(</span><span class="n">baseUrl</span><span class="p">);</span>
<span class="c1">// Wait for the first value</span>
<span class="k">auto</span> <span class="n">it</span> <span class="o">=</span> <span class="n">co_await</span> <span class="n">generator</span><span class="p">.</span><span class="n">begin</span><span class="p">();</span>
<span class="k">auto</span> <span class="n">end</span> <span class="o">=</span> <span class="n">generator</span><span class="p">.</span><span class="n">end</span><span class="p">();</span>
<span class="k">while</span> <span class="p">(</span><span class="n">it</span> <span class="o">!=</span> <span class="n">end</span><span class="p">)</span> <span class="p">{</span> <span class="c1">// while the `it` iterator is valid...</span>
<span class="c1">// Asynchronously retrieve the page content</span>
<span class="k">const</span> <span class="k">auto</span> <span class="n">content</span> <span class="o">=</span> <span class="n">co_await</span> <span class="n">fetchPageContent</span><span class="p">(</span><span class="o">*</span><span class="n">it</span><span class="p">);</span>
<span class="c1">// Yield it to the caller, then suspend</span>
<span class="n">co_yield</span> <span class="n">content</span><span class="p">;</span>
<span class="c1">// When resumed, wait for the paginator generator to produce another value</span>
<span class="n">co_await</span> <span class="o">++</span><span class="n">it</span><span class="p">;</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="n">QCoro</span><span class="o">::</span><span class="n">Task</span><span class="o"><></span> <span class="n">downloader</span><span class="p">(</span><span class="k">const</span> <span class="n">QUrl</span> <span class="o">&</span><span class="n">baseUrl</span><span class="p">)</span> <span class="p">{</span>
<span class="kt">int</span> <span class="n">page</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span>
<span class="c1">// `QCORO_FOREACH` is like `Q_FOREACH` for asynchronous iterators</span>
<span class="n">QCORO_FOREACH</span><span class="p">(</span><span class="k">const</span> <span class="n">QString</span> <span class="o">&</span><span class="n">page</span><span class="p">,</span> <span class="n">pageReader</span><span class="p">(</span><span class="n">baseUrl</span><span class="p">))</span> <span class="p">{</span>
<span class="c1">// When value is finally produced, write it to a file</span>
<span class="n">QFile</span> <span class="n">file</span><span class="p">(</span><span class="n">QStringLiteral</span><span class="p">(</span><span class="s">"page%1.html"</span><span class="p">).</span><span class="n">arg</span><span class="p">(</span><span class="n">page</span><span class="p">));</span>
<span class="n">file</span><span class="p">.</span><span class="n">open</span><span class="p">(</span><span class="n">QIODevice</span><span class="o">::</span><span class="n">WriteOnly</span><span class="p">);</span>
<span class="n">file</span><span class="p">.</span><span class="n">write</span><span class="p">(</span><span class="n">page</span><span class="p">);</span>
<span class="o">++</span><span class="n">page</span><span class="p">;</span>
<span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>
<p>Async generators also have <code class="language-plaintext highlighter-rouge">begin()</code> and <code class="language-plaintext highlighter-rouge">end()</code> methods which provide an asynchronous
iterator-like types. For one, the <code class="language-plaintext highlighter-rouge">begin()</code> method itself is a coroutine and must be
<code class="language-plaintext highlighter-rouge">co_await</code>ed to obtain the initial iterator. The increment operation of the iterator
must then be <code class="language-plaintext highlighter-rouge">co_await</code>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 <a href="https://qcoro.dvratil.cz/reference/coro/asyncgenerator/#qcoro_foreach"><code class="language-plaintext highlighter-rouge">QCORO_FOREACH</code> macro</a> to make using asynchronous generators simpler.</p>
<p>Read the <a href="https://qcoro.dvratil.cz/reference/coro/asyncgenerator">documentation for <code class="language-plaintext highlighter-rouge">QCoro::AsyncGenerator<T></code></a> for more details.</p>
<h3 id="new-qcorowebsockets-module">New QCoroWebSockets module</h3>
<p>The QCoroWebSockets module provides QCoro wrappers for <code class="language-plaintext highlighter-rouge">QWebSocket</code> and <code class="language-plaintext highlighter-rouge">QWebSocketServer</code>
classes to make them usable with coroutines. Like the other modules, it’s a standalone
shared or static library that you must explicitly link against in order to be able to use
it, so you don’t have to worry that QCoro would pull websockets dependency into your
project if you don’t want to.</p>
<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">QCoro</span><span class="o">::</span><span class="n">Task</span><span class="o"><></span> <span class="n">ChatApp</span><span class="o">::</span><span class="n">handleNotifications</span><span class="p">(</span><span class="k">const</span> <span class="n">QUrl</span> <span class="o">&</span><span class="n">wsServer</span><span class="p">)</span> <span class="p">{</span>
<span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">co_await</span> <span class="n">qCoro</span><span class="p">(</span><span class="n">mWebSocket</span><span class="p">).</span><span class="n">open</span><span class="p">(</span><span class="n">wsServer</span><span class="p">))</span> <span class="p">{</span>
<span class="n">qWarning</span><span class="p">()</span> <span class="o"><<</span> <span class="s">"Failed to open websocket connection to"</span> <span class="o"><<</span> <span class="n">wsServer</span> <span class="o"><<</span> <span class="s">":"</span> <span class="o"><<</span> <span class="n">mWebSocket</span><span class="o">-></span><span class="n">errorString</span><span class="p">();</span>
<span class="n">co_return</span><span class="p">;</span>
<span class="p">}</span>
<span class="n">qDebug</span><span class="p">()</span> <span class="o"><<</span> <span class="s">"Connected to"</span> <span class="o"><<</span> <span class="n">wsServer</span><span class="p">;</span>
<span class="c1">// Loops whenever a message is received until the socket is disconnected</span>
<span class="n">QCORO_FOREACH</span><span class="p">(</span><span class="k">const</span> <span class="n">QString</span> <span class="o">&</span><span class="n">rawMessage</span><span class="p">,</span> <span class="n">qCoro</span><span class="p">(</span><span class="n">mWebSocket</span><span class="p">).</span><span class="n">textMessages</span><span class="p">())</span> <span class="p">{</span>
<span class="k">const</span> <span class="k">auto</span> <span class="n">message</span> <span class="o">=</span> <span class="n">parseMessage</span><span class="p">(</span><span class="n">rawMessage</span><span class="p">);</span>
<span class="k">switch</span> <span class="p">(</span><span class="n">message</span><span class="p">.</span><span class="n">type</span><span class="p">)</span> <span class="p">{</span>
<span class="k">case</span> <span class="n">MessageType</span><span class="o">::</span><span class="n">ChatMessage</span><span class="p">:</span>
<span class="n">handleChatMessage</span><span class="p">(</span><span class="n">message</span><span class="p">);</span>
<span class="k">break</span><span class="p">;</span>
<span class="k">case</span> <span class="n">MessageType</span><span class="o">::</span><span class="n">PresenceChange</span><span class="p">:</span>
<span class="n">handlePresenceChange</span><span class="p">(</span><span class="n">message</span><span class="p">);</span>
<span class="k">break</span><span class="p">;</span>
<span class="k">case</span> <span class="n">MessageType</span><span class="o">::</span><span class="n">Invalid</span><span class="p">:</span>
<span class="n">qWarning</span><span class="p">()</span> <span class="o"><<</span> <span class="s">"Received an invalid message:"</span> <span class="o"><<</span> <span class="n">message</span><span class="p">.</span><span class="n">error</span><span class="p">;</span>
<span class="k">break</span><span class="p">;</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>
<p>The <code class="language-plaintext highlighter-rouge">textMessages()</code> 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 <code class="language-plaintext highlighter-rouge">co_await</code>ing the next
emission of the <code class="language-plaintext highlighter-rouge">QWebSocket::textMessage()</code> 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
<code class="language-plaintext highlighter-rouge">co_await</code>ing a singal emission, any message that is received before we start <code class="language-plaintext highlighter-rouge">co_await</code>ing
again after handling the current message would be lost.</p>
<p>You can find more details about the <code class="language-plaintext highlighter-rouge">QCoroWebSocket</code> and <code class="language-plaintext highlighter-rouge">QCoroWebSocketSever</code>
in the <a href="https://qcoro.dvratil.cz/reference/websockets/">QCoro’s websocket module documentation</a>.</p>
<p>You can build QCoro without the WebSockets module by passing <code class="language-plaintext highlighter-rouge">-DQCORO_WITH_QTWEBSOCKETS=OFF</code>
to CMake.</p>
<h3 id="deprecated-tasksh-header">Deprecated tasks.h header</h3>
<p>The <code class="language-plaintext highlighter-rouge">task.h</code> header and it’s camelcase variant <code class="language-plaintext highlighter-rouge">Task</code> been deprecated in QCoro 0.6.0
in favor of <code class="language-plaintext highlighter-rouge">qcorotask.h</code> (and <code class="language-plaintext highlighter-rouge">QCoroTask</code> 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’s public headers which all start with <code class="language-plaintext highlighter-rouge">qcoro</code> (or <code class="language-plaintext highlighter-rouge">QCoro</code>) prefix.</p>
<p>The old header is still present and fully functional, but including it will produce a
warning that you should port your code to use <code class="language-plaintext highlighter-rouge">qcorotask.h</code>. You can suppress the warning
by defining <code class="language-plaintext highlighter-rouge">QCORO_NO_WARN_DEPRECATED_TASK_H</code> in the compiler definitions:</p>
<p>CMake:</p>
<div class="language-cmake highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nf">add_compiler_definitions</span><span class="p">(</span>QCORO_NO_WARN_DEPRECATED_TASK_H<span class="p">)</span>
</code></pre></div></div>
<p>QMake</p>
<pre><code class="language-qmake">DEFINES += QCORO_NO_WARN_DEPRECATED_TASK_H
</code></pre>
<p>The header file will be removed at some point in the future, at latest in the 1.0 release.</p>
<p>You can also pass <code class="language-plaintext highlighter-rouge">-DQCORO_DISABLE_DEPRECATED_TASK_H=ON</code> to CMake when compiling QCoro
to prevent it from installing the deprecated task.h header.</p>
<h3 id="clang-cl-and-apple-clang-support">Clang-cl and apple-clang support</h3>
<p>The clang compiler is fully supported by QCoro since 0.4.0. This version of QCoro
intruduces supports for clang-cl and apple-clang.</p>
<p>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.</p>
<p>Apple-clang is the official build of clang provided by Apple on MacOS, which may be
different from the upstream clang releases.</p>
<h3 id="full-changelog">Full changelog</h3>
<ul>
<li>Enable exceptions when compiling with clang-cl (<a href="https://github.com/danvratil/qcoro/issues/90">#90</a>, <a href="https://github.com/danvratil/qcoro/pull/91">#91</a>)</li>
<li>Add option to generate code coverage report (<a href="https://github.com/danvratil/qcoro/commit/0f0408ce927e50450ab847cf290dd229b2a6e12c">commit 0f0408c</a>)</li>
<li>Lower CMake requirement to 3.18.4 (<a href="https://github.com/danvratil/qcoro/commit/deb80c13d9c9d866304fd5a64a33168adab34111">commit deb80c1</a>)</li>
<li>Add support for clang-cl (<a href="https://github.com/danvratil/qcoro/issues/84">#84</a>, <a href="https://github.com/danvratil/qcoro/pull/86">#86</a>)</li>
<li>Avoid identifiers that begin with underscore and uppercase letter (<a href="https://github.com/danvratil/qcoro/pull/83">#83</a>)</li>
<li>Add mising <code class="language-plaintext highlighter-rouge"><chrono></code> include (<a href="https://github.com/danvratil/qcoro/pull/82">#82</a></li>
<li>New module: QCoroWebSockets (<a href="https://github.com/danvratil/qcoro/pull/75">#75</a>, <a href="https://github.com/danvratil/qcoro/pull/88">#88</a>, <a href="https://github.com/danvratil/qcoro/pull/89">#89</a>)</li>
<li>Add <code class="language-plaintext highlighter-rouge">QCoroFwd</code> header with forward-declarations of relevant types (<a href="https://github.com/danvratil/qcoro/issues/71">#71</a>)</li>
<li>Deprecate <code class="language-plaintext highlighter-rouge">task.h</code> header file in favor of <code class="language-plaintext highlighter-rouge">qcorotask.h</code> (<a href="https://github.com/danvratil/qcoro/pull/70">#70</a>)</li>
<li>Fix installing export headers (<a href="https://github.com/danvratil/qcoro/pull/77">#77</a>)</li>
<li>Introduce support for generator coroutines (<a href="https://github.com/danvratil/qcoro/pulls/69">#69</a>)</li>
<li>QCoro is now build with “modern Qt” compile definitions (<a href="https://github.com/danvratil/qcoro/pull/66">#66</a>)</li>
<li>Export QCoro wrapper classes (<a href="https://github.com/danvratil/qcoro/issues/63">#63</a>, <a href="https://github.com/danvratil/qcoro/pull/65">#65</a>)</li>
<li>Extended CI to include MSVC, apple-clang and multiple version of gcc and clang-cl (<a href="https://github.com/danvratil/qcoro/pull/60">#60</a>, <a href="https://github.com/danvratil/qcoro/pull/61">#61</a>)</li>
<li>Fixed build with apple-clang</li>
</ul>
<hr />
<h2 id="download">Download</h2>
<p>You can download QCoro 0.6.0 <a href="https://github.com/danvratil/qcoro/releases/tag/v0.6.0">here</a> or check the latest sources on <a href="https://github.com/danvratil/qcoro">QCoro GitHub</a>.</p>
<h2 id="more-about-qcoro">More About QCoro</h2>
<p>If you are interested in learning more about QCoro, go read the <a href="https://qcoro.dvratil.cz/">documentation</a>, look at the
<a href="https://www.dvratil.cz/2021/08/first-qcoro-release">first release announcement</a>, which contains a nice explanation and example or
watch <a href="https://www.youtube.com/watch?v=KKVqFqbXJaU&list=PLsHpGlwPdtMq6pJ4mqBeYNWOanjdIIPTJ&index=20">recording of my talk about C++20 coroutines and QCoro</a> this years’ Akademy.</p>Daniel VrátilI’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.QCoro 0.5.0 Release Announcement2022-04-25T17:00:00-05:002022-04-25T17:00:00-05:00https://dvratil.cz/2022/04/qcoro-0.5.0-release-announcement<p>After another few months I’m happy to announce a new release of QCoro, which brings several new features and a bunch
of bugfixes.</p>
<ul>
<li>.then() continuation for <code class="language-plaintext highlighter-rouge">Task<T></code></li>
<li>All asynchronous operations now return <code class="language-plaintext highlighter-rouge">Task<T></code></li>
<li>Timeouts for many operations</li>
<li>Support for <code class="language-plaintext highlighter-rouge">QThread</code></li>
</ul>
<h2 id="then-continuation-for-task">.then() continuation for Task<T></T></h2>
<p>Sometimes it’s not possible to <code class="language-plaintext highlighter-rouge">co_await</code> a coroutine - usually because you need to integrate with a 3rd party code
that is not coroutine-ready. A good example might be implementing <code class="language-plaintext highlighter-rouge">QAbstractItemModel</code>, where none of the virtual
methods are coroutines and thus it’s not possible to use <code class="language-plaintext highlighter-rouge">co_await</code> in them.</p>
<p>To still make it possible to all coroutines from such code, <code class="language-plaintext highlighter-rouge">QCoro::Task<T></code> now has a new method: <code class="language-plaintext highlighter-rouge">.then()</code>,
which allows attaching a continuation callback that will be invoked by QCoro when the coroutine represented
by the <code class="language-plaintext highlighter-rouge">Task</code> finishes.</p>
<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">void</span> <span class="nf">notACoroutine</span><span class="p">()</span> <span class="p">{</span>
<span class="n">someCoroutineReturningQString</span><span class="p">().</span><span class="n">then</span><span class="p">([](</span><span class="k">const</span> <span class="n">QString</span> <span class="o">&</span><span class="n">result</span><span class="p">)</span> <span class="p">{</span>
<span class="c1">// Will be invoked when the someCoroutine() finishes.</span>
<span class="c1">// The result of the coroutine is passed as an argument to the continuation.</span>
<span class="p">});</span>
<span class="p">}</span>
</code></pre></div></div>
<p>The continuation itself might be a coroutine, and the result of the <code class="language-plaintext highlighter-rouge">.then()</code> member function is again a <code class="language-plaintext highlighter-rouge">Task<R></code>
(where <code class="language-plaintext highlighter-rouge">R</code> is the return type of the continuation callback), so it is possible to chain multiple continuations
as well as <code class="language-plaintext highlighter-rouge">co_await</code>ing the entire chain.</p>
<h2 id="all-asynchronous-operations-now-return-taskt">All asynchronous operations now return <code class="language-plaintext highlighter-rouge">Task<T></code></h2>
<p>Up until now each operation from the QCoro wrapper types returned a special awaitable - for example,
<code class="language-plaintext highlighter-rouge">QCoroIODevice::read()</code> returned <code class="language-plaintext highlighter-rouge">QCoro::detail::QCoroIODevice::ReadOperation</code>. In most cases users of QCoro do
not need to concern themselves with that type, since they can still directly <code class="language-plaintext highlighter-rouge">co_await</code> the returned awaitable.</p>
<p>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.</p>
<p>As of QCoro 0.5.0, all the operations now return <code class="language-plaintext highlighter-rouge">Task<T></code>, which makes the API consistent. As a secondary effect,
all the operations can have a chained continuation using the <code class="language-plaintext highlighter-rouge">.then()</code> continuation, as described above.</p>
<h2 id="timeout-support-for-many-operations">Timeout support for many operations</h2>
<p>Qt doesn’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’s take
<code class="language-plaintext highlighter-rouge">QIODevice::read()</code> for example: the Qt version doesn’t have any timeout, because the call will never block - if
there’s nothing to read, it simply returns an empty <code class="language-plaintext highlighter-rouge">QByteArray</code>.</p>
<p>On the other hand, <code class="language-plaintext highlighter-rouge">QCoroIODevice::read()</code> is an asynchronous operation, because under to hood, it’s a coroutine
that asynchronously calls a sequence of</p>
<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">device</span><span class="o">-></span><span class="n">waitForReadyRead</span><span class="p">();</span>
<span class="n">device</span><span class="o">-></span><span class="n">read</span><span class="p">();</span>
</code></pre></div></div>
<p>Since <code class="language-plaintext highlighter-rouge">QIODevice::waitForReadyRead()</code> takes a timeout argument, it makes sense for <code class="language-plaintext highlighter-rouge">QCoroIODevice::read()</code>
to also take (an optional) timeout argument. This and many other operations have gained support for timeout.</p>
<h2 id="support-for-qthread">Support for <code class="language-plaintext highlighter-rouge">QThread</code></h2>
<p>It’s been a while since I added a new wrapper for a Qt class, so QCoro 0.5.0 adds wrapper for <code class="language-plaintext highlighter-rouge">QThread</code>. It’s
now possible to <code class="language-plaintext highlighter-rouge">co_await</code> thread start and end:</p>
<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">std</span><span class="o">::</span><span class="n">unique_ptr</span><span class="o"><</span><span class="n">QThread</span><span class="o">></span> <span class="kr">thread</span><span class="p">(</span><span class="n">QThread</span><span class="o">::</span><span class="n">create</span><span class="p">([]()</span> <span class="p">{</span>
<span class="p">...</span>
<span class="p">});</span>
<span class="n">ui</span><span class="o">-></span><span class="n">setLabel</span><span class="p">(</span><span class="n">tr</span><span class="p">(</span><span class="s">"Starting thread..."</span><span class="p">);</span>
<span class="kr">thread</span><span class="o">-></span><span class="n">start</span><span class="p">();</span>
<span class="n">co_await</span> <span class="nf">qCoro</span><span class="p">(</span><span class="kr">thread</span><span class="p">)</span><span class="o">-></span><span class="n">waitForStarted</span><span class="p">();</span>
<span class="n">ui</span><span class="o">-></span><span class="n">setLabel</span><span class="p">(</span><span class="n">tr</span><span class="p">(</span><span class="s">"Calculating..."</span><span class="p">));</span>
<span class="n">co_await</span> <span class="nf">qCoro</span><span class="p">(</span><span class="kr">thread</span><span class="p">)</span><span class="o">-></span><span class="n">waitForFinished</span><span class="p">();</span>
<span class="n">ui</span><span class="o">-></span><span class="n">setLabel</span><span class="p">(</span><span class="n">tr</span><span class="p">(</span><span class="s">"Finished!"</span><span class="p">));</span>
</code></pre></div></div>
<h2 id="full-changelog">Full changelog</h2>
<ul>
<li><code class="language-plaintext highlighter-rouge">.then()</code> continuation for <code class="language-plaintext highlighter-rouge">Task<T></code> (<a href="https://github.com/danvratil/qcoro/pull/39">#39</a>)</li>
<li>Fixed namespace scoping (<a href="https://github.com/danvratil/qcoro/pull/45">#45</a>)</li>
<li>Fixed <code class="language-plaintext highlighter-rouge">QCoro::waitFor()</code> getting stuck when coroutine returns synchronously (<a href="https://github.com/danvratil/qcoro/pull/46">#46</a>)</li>
<li>Fixed -pthread usage in CMake (<a href="https://github.com/danvratil/qcoro/pull/47">#47</a>)</li>
<li>Produce QMake config files (.pri) for each module (<a href="https://github.com/danvratil/qcoro/commit/e215616be8174438e907710025a7bd71e66a64b5">commit e215616</a>)</li>
<li>Fix build on platforms where -latomic must be linked explicitly (<a href="https://github.com/danvratil/qcoro/pull/52">#52</a>)</li>
<li>Return <code class="language-plaintext highlighter-rouge">Task<T></code> from all operations (<a href="https://github.com/danvratil/qcoro/pull/54">#54</a>)</li>
<li>Add QCoro wrapper for <code class="language-plaintext highlighter-rouge">QThread</code> (<a href="https://github.com/danvratil/qcoro/commit/832d931068312c906db6858493fc952b8d984b1c">commit 832d931</a>)</li>
<li>Many documentation updates</li>
</ul>
<p>Thanks to everyone who contributed to QCoro!</p>
<hr />
<h2 id="download">Download</h2>
<p>You can download QCoro 0.5.0 <a href="https://github.com/danvratil/qcoro/releases/tag/v0.5.0">here</a> or check the latest sources on <a href="https://github.com/danvratil/qcoro">QCoro GitHub</a>.</p>
<h2 id="more-about-qcoro">More About QCoro</h2>
<p>If you are interested in learning more about QCoro, go read the <a href="https://qcoro.dvratil.cz/">documentation</a>, look at the
<a href="https://www.dvratil.cz/2021/08/first-qcoro-release">first release announcement</a>, which contains a nice explanation and example or
watch <a href="https://www.youtube.com/watch?v=KKVqFqbXJaU&list=PLsHpGlwPdtMq6pJ4mqBeYNWOanjdIIPTJ&index=20">recording of my talk about C++20 coroutines and QCoro</a> this years’ Akademy.</p>Daniel VrátilAfter another few months I’m happy to announce a new release of QCoro, which brings several new features and a bunch of bugfixes.QCoro 0.4.0 Release Announcement2022-01-06T13:00:00-06:002022-01-06T13:00:00-06:00https://dvratil.cz/2022/01/qcoro-0.4.0-release-announcement<p>It took a few months, but there’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.</p>
<p>Major highlights in this release:</p>
<ul>
<li>Co-installability of Qt5 and Qt6 builds of QCoro</li>
<li>Complete re-work of CMake configuration</li>
<li>Support for compiling QCoro with Clang against libstdc++</li>
</ul>
<h2 id="co-installability-of-qt5-and-qt6-builds-of-qcoro">Co-installability of Qt5 and Qt6 builds of QCoro</h2>
<p>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. <code class="language-plaintext highlighter-rouge">libQCoro6Core.so</code>) and header files are also located in dedicated subdirectories
(e.g. <code class="language-plaintext highlighter-rouge">/usr/include/qcoro6/{qcoro,QCoro}</code>). User of QCoro should not need to do any changes to their codebase.</p>
<h2 id="complete-re-work-of-cmake-configuration">Complete re-work of CMake configuration</h2>
<p>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:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>find_package(QCoro5 REQUIRED COMPONENTS Core Network DBus)
</code></pre></div></div>
<p>Finally, the target names to use in <code class="language-plaintext highlighter-rouge">target_link_libraries</code> have changed as well:</p>
<ul>
<li><code class="language-plaintext highlighter-rouge">QCoro::Core</code></li>
<li><code class="language-plaintext highlighter-rouge">QCoro::Network</code></li>
<li><code class="language-plaintext highlighter-rouge">QCoro::DBus</code></li>
</ul>
<p>The version-less <code class="language-plaintext highlighter-rouge">QCoro</code> namespace can be used regardless of whether using Qt5 or Qt6 build of QCoro.
<code class="language-plaintext highlighter-rouge">QCoro5</code> and <code class="language-plaintext highlighter-rouge">QCoro6</code> namespaces are available as well, in case users need to combine both Qt5 and Qt6
versions in their codebase.</p>
<p>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.</p>
<h2 id="support-for-compiling-qcoro-with-clang-against-libstdc">Support for compiling QCoro with Clang against libstdc++</h2>
<p>Until now, when the Clang compiler was detected, QCoro forced usage of LLVM’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 <code class="language-plaintext highlighter-rouge">std::experimental</code> namespace. In GNU’s libstdc++, coroutines are fully supported
and thus implemented in the <code class="language-plaintext highlighter-rouge">std</code> namespace. This requires a little bit of extra glue, which is now in place.</p>
<h3 id="full-changelog">Full changelog</h3>
<ul>
<li>QCoro can now be built with Clang against libstdc++ (<a href="https://github.com/danvratil/qcoro/pull/38">#38</a>, <a href="https://github.com/danvratil/qcoro/issues/22">#22</a>)</li>
<li>Qt5 and Qt6 builds of QCoro are now co-installable (<a href="https://github.com/danvratil/qcoro/issues/36">#36</a>, <a href="https://github.com/danvratil/qcoro/pull/37">#37</a>)</li>
<li>Fixed early co_return not resuming the caller (<a href="https://github.com/danvratil/qcoro/issue/24">#24</a>, <a href="https://github.com/danvratil/qcoro/pull/35">#35</a>)</li>
<li>Fixed QProcess example (<a href="https://github.com/danvratil/qcoro/pull/34">#34</a>)</li>
<li>Test suite has been improved and extended (<a href="https://github.com/danvratil/qcoro/pull/29">#29</a>, <a href="https://github.com/danvratil/qcoro/pull/31">#31</a>)</li>
<li>Task move assignment operator checks for self-assignment (<a href="https://github.com/danvratil/qcoro/pull/27">#27</a>)</li>
<li>QCoro can now be built as a subdirectory inside another CMake project (<a href="https://github.com/danvratil/qcoro/pull/25">#25</a>)</li>
<li>Fixed QCoroCore/qcorocore.h header (<a href="https://github.com/danvratil/qcoro/pull/23">#23</a>)</li>
<li>DBus is disabled by default on Windows, Mac and Android (<a href="https://github.com/danvratil/qcoro/pull/21">#21</a>)</li>
</ul>
<p>Thanks to everyone who contributed to QCoro!</p>
<hr />
<h2 id="download">Download</h2>
<p>You can download QCoro 0.4.0 <a href="https://github.com/danvratil/qcoro/releases/tag/v0.4.0">here</a> or check the latest sources on <a href="https://github.com/danvratil/qcoro">QCoro GitHub</a>.</p>
<h2 id="more-about-qcoro">More About QCoro</h2>
<p>If you are interested in learning more about QCoro, go read the <a href="https://qcoro.dvratil.cz/">documentation</a>, look at the
<a href="https://www.dvratil.cz/2021/08/first-qcoro-release">first release announcement</a>, which contains a nice explanation and example or
watch <a href="https://www.youtube.com/watch?v=KKVqFqbXJaU&list=PLsHpGlwPdtMq6pJ4mqBeYNWOanjdIIPTJ&index=20">recording of my talk about C++20 coroutines and QCoro</a> this years’ Akademy.</p>Daniel VrátilIt took a few months, but there’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.QCoro 0.2.0 Release Announcement2021-09-08T09:00:00-05:002021-09-08T09:00:00-05:00https://dvratil.cz/2021/09/qcoro-0.2.0-release-announcement<p>Just about a month after the first official release of QCoro, a library that provides C++ coroutine support for Qt,
here’s 0.2.0 with some big changes. While the API is backwards compatible, users updating from 0.1.0 will have
to adjust their <code class="language-plaintext highlighter-rouge">#include</code> statements when including QCoro headers.</p>
<p>QCoro 0.2.0 brings the following changes:</p>
<h2 id="library-modularity">Library modularity</h2>
<p>The code has been reorganized into three modules (and thus three standalone libraries): QCoroCore, QCoroDBus and
QCoroNetwork. QCoroCore contains the elementary QCoro tools (<code class="language-plaintext highlighter-rouge">QCoro::Task</code>, <code class="language-plaintext highlighter-rouge">qCoro()</code> wrapper etc.) and coroutine
support for some QtCore types. The QCoroDBus module contains coroutine support for types from the QtDBus module
and equally the QCoroNetwork module contains coroutine support for types from the QtNetwork module. The latter two
modules are also optional, the library can be built without them. It also means that an application that only uses
let’s say QtNetwork and has no DBus dependency will no longer get QtDBus pulled in through QCoro, as long as it
only links against <code class="language-plaintext highlighter-rouge">libQCoroCore</code> and <code class="language-plaintext highlighter-rouge">libQCoroNetwork</code>. The reorganization will also allow for future
support of additional Qt modules.</p>
<h2 id="headers-clean-up">Headers clean up</h2>
<p>The include headers in QCoro we a bit of a mess and in 0.2.0 they all got a unified form. All public header files
now start with <code class="language-plaintext highlighter-rouge">qcoro</code> (e.g. <code class="language-plaintext highlighter-rouge">qcorotimer.h</code>, <code class="language-plaintext highlighter-rouge">qcoronetworkreply.h</code> etc.), and QCoro also provides CamelCase headers
now. Thus users should simply do <code class="language-plaintext highlighter-rouge">#include <QCoroTimer></code> if they want coroutine support for <code class="language-plaintext highlighter-rouge">QTimer</code>.</p>
<p>The reorganization of headers makes QCoro 0.2.0 incompatible with previous versions and any users of QCoro will
have to update their <code class="language-plaintext highlighter-rouge">#include</code> statements. I’m sorry about this extra hassle, but with this brings much needed
sanity into the header organization and naming scheme.</p>
<h2 id="docs-update">Docs update</h2>
<p>The documentation has been updated to reflect the reorganization as well as some internal changes. It should be
easier to understand now and hopefully will make it easier for users to start with QCoro now.</p>
<h2 id="internal-api-cleanup-and-code-de-duplication">Internal API cleanup and code de-duplication</h2>
<p>Historically, certain types types which can be directly <code class="language-plaintext highlighter-rouge">co_await</code>ed with QCoro, for instance <code class="language-plaintext highlighter-rouge">QTimer</code> has their
coroutine support implemented differently than types that have multiple asynchronous operations and thus have
a coroutine-friendly wrapper classes (like <code class="language-plaintext highlighter-rouge">QIODevice</code> and it’s <code class="language-plaintext highlighter-rouge">QCoroIODevice</code> wrapper). In 0.2.0 I have unified
the code so that even the coroutine support for simple types like <code class="language-plaintext highlighter-rouge">QTimer</code> are implemented through wrapper classes
(so there’s <code class="language-plaintext highlighter-rouge">QCoroTimer</code> now)</p>
<hr />
<h2 id="download">Download</h2>
<p>You can download QCoro 0.2.0 <a href="https://github.com/danvratil/qcoro/releases/tag/v0.2.0">here</a> or check the latest sources on <a href="https://github.com/danvratil/qcoro">QCoro GitHub</a>.</p>
<h2 id="more-about-qcoro">More About QCoro</h2>
<p>If you are interested in learning more about QCoro, go read the <a href="https://qcoro.dvratil.cz/">documentation</a>, look at the
<a href="https://www.dvratil.cz/2021/08/first-qcoro-release">first release announcement</a>, which contains a nice explanation and example or
watch <a href="https://www.youtube.com/watch?v=KKVqFqbXJaU&list=PLsHpGlwPdtMq6pJ4mqBeYNWOanjdIIPTJ&index=20">recording of my talk about C++20 coroutines and QCoro</a> this years’ Akademy.</p>Daniel VrátilJust about a month after the first official release of QCoro, a library that provides C++ coroutine support for Qt, here’s 0.2.0 with some big changes. While the API is backwards compatible, users updating from 0.1.0 will have to adjust their #include statements when including QCoro headers.Initial release of QCoro2021-08-16T05:00:00-05:002021-08-16T05:00:00-05:00https://dvratil.cz/2021/08/first-qcoro-release<p>I’m happy to announce first release of QCoro, a library that provides C++ coroutine support for Qt.</p>
<p>You can download QCoro 0.1.0 <a href="https://github.com/danvratil/qcoro/releases/tag/v0.1.0">here</a> or check the latest sources on <a href="https://github.com/danvratil/qcoro">QCoro GitHub</a>.</p>
<p>I have talked about QCoro (and C++ coroutines in general) recently at KDE Akademy, you can view the
<a href="https://www.youtube.com/watch?v=KKVqFqbXJaU&list=PLsHpGlwPdtMq6pJ4mqBeYNWOanjdIIPTJ&index=20">recording of my talk on YouTube</a>.</p>
<p>In general, QCoro provides coroutine support for various asynchronous operations provided by Qt. Since
Qt doesn’t support coroutines by default, QCoro provides the necessary “glue” between native Qt types
and the C++ coroutine machinery, making it possible to use Qt types with coroutines easily.</p>
<p>QCoro provides coroutine support for asynchronous operations of <code class="language-plaintext highlighter-rouge">QIODevice</code>, <code class="language-plaintext highlighter-rouge">QNetworkReply</code>, <code class="language-plaintext highlighter-rouge">QProcess</code>,
<code class="language-plaintext highlighter-rouge">QDBusPendingReply</code>, <code class="language-plaintext highlighter-rouge">QTimer</code> and more. Take a look at the documentation for detailed description and list
of all currently supported Qt types.</p>
<p>A brief example from <a href="https://qcoro.dvratil.cz/">our documentation</a> that demonstrates how using coroutines makes handling asynchronous
operations in Qt simpler:</p>
<p>This is a (simplified) example of how we do network requests with Qt normally, using signals and slots:</p>
<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">QNetworkAccessManager</span> <span class="o">*</span><span class="n">manager</span> <span class="o">=</span> <span class="k">new</span> <span class="nf">QNetworkAccessManager</span><span class="p">(</span><span class="k">this</span><span class="p">);</span>
<span class="n">QNetworkReply</span> <span class="o">*</span><span class="n">reply</span> <span class="o">=</span> <span class="n">manager</span><span class="o">-></span><span class="n">get</span><span class="p">(</span><span class="n">url</span><span class="p">);</span>
<span class="n">connect</span><span class="p">(</span><span class="n">reply</span><span class="p">,</span> <span class="o">&</span><span class="n">QNetworkReply</span><span class="o">::</span><span class="n">finished</span><span class="p">,</span> <span class="k">this</span><span class="p">,</span>
<span class="p">[</span><span class="k">this</span><span class="p">,</span> <span class="n">reply</span><span class="p">]()</span> <span class="p">{</span>
<span class="k">const</span> <span class="k">auto</span> <span class="n">data</span> <span class="o">=</span> <span class="n">reply</span><span class="o">-></span><span class="n">readAll</span><span class="p">();</span>
<span class="n">doSomethingWithData</span><span class="p">(</span><span class="n">data</span><span class="p">);</span>
<span class="n">reply</span><span class="o">-></span><span class="n">deleteLater</span><span class="p">();</span>
<span class="p">});</span>
</code></pre></div></div>
<p>And this is the same code, written using C++ coroutines:</p>
<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">QNetworkAccessManager</span> <span class="n">networkAccessManager</span><span class="p">;</span>
<span class="n">QNetworkReply</span> <span class="o">*</span><span class="n">reply</span> <span class="o">=</span> <span class="n">co_await</span> <span class="n">networkAccessManager</span><span class="p">.</span><span class="n">get</span><span class="p">(</span><span class="n">url</span><span class="p">);</span>
<span class="k">const</span> <span class="k">auto</span> <span class="n">data</span> <span class="o">=</span> <span class="n">reply</span><span class="o">-></span><span class="n">readAll</span><span class="p">();</span>
<span class="n">doSomethingWithData</span><span class="p">(</span><span class="n">data</span><span class="p">);</span>
<span class="n">reply</span><span class="o">-></span><span class="n">deleteLater</span><span class="p">();</span>
</code></pre></div></div>
<p>The <code class="language-plaintext highlighter-rouge">co_await</code> keyword here is the key here: it asynchronously waits for the reply to finish. During the wait,
the execution returns to the caller, which could be the Qt event loop, which means that even if this code <em>looks</em>
synchronous, in fact it won’t block the event loop while keeping the code simple to read and understand.</p>Daniel VrátilI’m happy to announce first release of QCoro, a library that provides C++ coroutine support for Qt.Building RC LEGO with Arduino and Qt2021-06-16T09:00:00-05:002021-06-16T09:00:00-05:00https://dvratil.cz/2021/06/rc-lego-part-1-design<p>Recently my 4 year-old stepson saw a kid with an RC racing car in a park. He really wanted
his own, but with Christmas and his birthday still being a long way away, I decided to
solve the “problem” by combining three things I’m really passionate about: LEGO, electronics
and programming.</p>
<p>In this short series of blogs I’ll describe how to build one such car using LEGO, Arduino and
a bit of C++ (and Qt, of course!).</p>
<h2 id="lego">LEGO</h2>
<p>Obviously, we will need some LEGO to build the car. Luckily, I bought LEGO Technic <a href="https://www.lego.com/en-us/product/mercedes-benz-arocs-3245-42043">Mercedes Benz Arocs 3245
(40243)</a> last year. It’s a big build with lots of cogs, one electric engine and bunch of pneumatics.
I can absolutely recommend it - building the set was a lot of fun and thanks to the Power Functions it has
a high play-value as well. There’s also fair amount of <a href="https://rebrickable.com/search/?q=42043&search_type=all">really good MOCs</a>, especially
the <a href="https://rebrickable.com/mocs/MOC-6060/M_longer/42043-alternate-mobile-crane/#details">MOC 6060 - Mobile Crane by M_longer</a> is really good. But I’m digressing here. :)</p>
<p><a href="https://www.lego.com/en-us/product/mercedes-benz-arocs-3245-42043" class="image left" target="_blank"><img src="https://dvratil.cz/assets/rc-lego/lego-40243-arocs.jpg" alt="Mercedes Benz Arocs 3245 (40243)" title="Mercedes Benz Arocs 3245 (40243)" /></a>
<a href="https://www.lego.com/en-us/product/mercedes-benz-arocs-3245-42043" class="image right" target="_blank"><img src="https://dvratil.cz/assets/rc-lego/lego-40243-arocs-box.jpg" alt="Mercedes Benz Arocs 3245 (40243)" title="Mercedes Benz Arocs 3245 (40243)" /></a></p>
<div class="clear"></div>
<p>The problem with Arocs is that it only has a single Power Functions engine (<a href="https://rebrickable.com/parts/99499/electric-power-functions-large-motor-with-dark-bluish-gray-bottom/">99499 Electric Power Functions Large Motor</a>)
and we will need at least two: one for driving and one for steering. So I bought a second one. I bought the same one,
but a smaller one would probably do just fine for the steering.</p>
<p><a href="https://rebrickable.com/parts/99499/electric-power-functions-large-motor-with-dark-bluish-gray-bottom/" class="image-small" target="_blank"><img src="https://dvratil.cz/assets/rc-lego/99499-engine.jpg" alt="LEGO Power Functions engine (99499)" title="LEGO Power Functions engine (99499)" /></a></p>
<p>I started by prototyping the car and the drive train, especially how to design the gear ratios to not overload
the engine when accelerating while keeping the car moving at reasonable speed.</p>
<p><img src="https://dvratil.cz/assets/rc-lego/prototype-01.jpg" alt="First prototype of engine-powered LEGO car" title="First prototype of engine-powered LEGO car" /></p>
<p>Turns out the <a href="https://rebrickable.com/parts/76244/technic-gear-24-tooth-clutch-with-dark-bluish-gray-center/">76244 Technic Gear 24 Tooth Clutch</a> is really important as it prevents the gear
teeth skipping when the engine stops suddenly, or when the car gets pushed around by hand.</p>
<p><a href="https://rebrickable.com/parts/76244/technic-gear-24-tooth-clutch-with-dark-bluish-gray-center/" class="image-small" target="_blank"><img src="https://dvratil.cz/assets/rc-lego/76244-clutch.webp" alt="76244 Technic Gear 24 Tooth Clutch" title="76244 Technic Gear 24 Tooth Clutch" class="center" /></a></p>
<p>Initially I thought I would base the build of the car on some existing designs but in the end I just started building
and I ended up with this skeleton:</p>
<p><a href="https://dvratil.cz/assets/rc-lego/full/remotebrick-skelet.jpg" target="_blank"><img src="https://dvratil.cz/assets/rc-lego/remotebrick-skelet.jpg" alt="Skelet of first version of the RC car" title="Skelet of first version of the RC cart" /></a></p>
<p>The two engines are in the middle - rear one powers the wheels, the front one handles the steering using the
<a href="https://rebrickable.com/parts/61927b/technic-linear-actuator-with-dark-bluish-gray-ends-improved-version/">61927b Technic Linear Actuator</a>. I’m not entirely happy with the steering, so I might rework
that in the future. I recently got <a href="https://www.lego.com/en-us/product/ford-mustang-10265">Ford Mustang (10265)</a> which has a really interesting steering
mechanism and I think I’ll try to rebuild the steering this way.</p>
<h2 id="wires">Wires</h2>
<p class="left" style="width: calc(30% - 15px);">
<a href="https://rebrickable.com/parts/16511/battery-box-power-functions-4-x-11-x-7-with-orange-switch-and-dark-bluish-gray-covers-new-version/" target="_blank"><img src="https://dvratil.cz/assets/rc-lego/58118-wire.jpg" alt="58118 Eletric Power Functions Extension Wires" title="58118 Eletric Power Functions Extension Wire" /></a>
</p>
<p class="right" style="width: calc(70% - 15px);">
We will control the engines from Arduino. But how to connect the LEGO Power Functions to an Arduino? Well, you
just need to buy a bunch of those <a href="https://rebrickable.com/parts/58118/electric-power-functions-extension-wire-with-one-light-bluish-gray-end-50cm/">58118 Electric Power Functions Extension Wires</a>, cut them and
connect them with DuPont cables that can be connected to a breadboard. Make sure to buy the “with one Light Bluish
Gray End” version - I accidentally bought cables which had both ends light bluish, but those can’t be connected to the
<a href="https://rebrickable.com/parts/16511/battery-box-power-functions-4-x-11-x-7-with-orange-switch-and-dark-bluish-gray-covers-new-version/">16511 Battery Box</a>.</p>
<div class="clear"></div>
<p>We will need 3 of those half-cut PF cables in total: two for the engines and one to connect to the battery box. You
probably noticed that there are 4 connectors and 4 wires in each cable. Wires <em>1</em> and <em>4</em> are always
GND and 9V, respectively, regardless of what position is the switch on the battery pack. Wires <em>2</em> and <em>3</em>
are 0V and 9V or vice versa, depending on the position of the battery pack switch. This way we can control the engine
rotation direction.</p>
<p><img src="https://dvratil.cz/assets/rc-lego/pf-wires-schema.png" alt="Schematics of PF wires" title="Schematics of PF wires" class="center" /></p>
<p>For the two cables that will control the engines we need all 4 wires connected to the DuPont cable. For the one cable
that will be connected to the battery pack we only need the outter wires to be connected, since we will only use the
battery pack to provide the power - we will control the engines using Arduino and an integrated circuit.</p>
<p>I used the glue gun to connect the PF wires and the DuPont cables, which works fairly well. You could use a solder
if you have one, but the glue also works as an isolator to prevent the wires from short-circuiting.</p>
<p><a href="https://dvratil.cz/assets/rc-lego/full/pf-wires-dupont.jpg" target="_blank"><img src="https://dvratil.cz/assets/rc-lego/pf-wires-dupont.jpg" alt="LEGO PF cable connected to DuPont wires" title="LEGO PF cable connected to DuPont wires" class="center" /></a></p>
<p>This completes the LEGO part of this guide. Next comes the electronics :)</p>
<h2 id="arduino">Arduino</h2>
<p>To remotely control the car we need some electronics on board. I used the following components:</p>
<ul>
<li>Arduino UNO - to run the software, obviously</li>
<li>HC-06 Bluetooth module - for remote control</li>
<li>400 pin bread board - to connect the wiring</li>
<li>L293D integrated circuit - to control the engines</li>
<li>1 kΩ and 2 kΩ resistors - to reduce voltage between Arduino and BT module</li>
<li>9V battery box - to power the Arduino board once on board the car</li>
<li>M-M DuPont cables - to wire everything together</li>
</ul>
<p>The total price of those components is about €30, which is still less than what I paid for the LEGO engine and PF wires.</p>
<p>Let’s start with the Bluetooth module. There are some really nice guides online how to use them, I’ll try to describe
it quickly here. The module has 4 pins: <code>RX</code>, <code>TX</code>, <code>GND</code> and <code>VCC</code>.
<code>GND</code> can be connected directly to Arduino’s <code>GND</code> pin. <code>VCC</code> is power supply for the
bluetooth module. You can connect it to the <code>5V</code> pin on Arduino. Now for <code>TX</code> and <code>RX</code>
pins. You could connect them to the <code>RX</code> and <code>TX</code> pins on the Arduino board, but that makes it
hard to debug the program later, since all output from the program will go to the bluetooth module rather than our
computer. Instead connect it to pins <code>2</code> and <code>3</code>. <em>Warning</em>: you need to use a voltage
divider for the <code> RX</code> pin, because Arduino operates on 5V, but the HC-06 module operates on 3.3V. You can
do it by putting a 1kΩ resistor between Arduino pin <code>3</code> and HC-06 <code>RX</code> and 2kΩ resistor between
Arduino <code>GND</code> and HC-06 <code>RX</code> pins.</p>
<p>Next comes up the L293D integrated circuit. This circuit will allow us to control the engines. While in theory we
could hook up the engines directly to the Arduino board (there’s enough free pins), in practice it’s a bad idea. The
engines need 9V to operate, which is a lot of power drain for the Arduino circuitry. Additionally, it would mean that
the Arduino board and the engines would both be drawing power from the single 9V battery used to power the Arduino.</p>
<p>Instead, we use the L293D IC, where you connect external power source (the LEGO Battery pack in our case) to it as well
as the engines and use only a low voltage signal from the Arduino to control the current from the external power
source to the engines (very much like a transistor). The advantage of the L293D is that it can control up to 2 separate
engines and it can also reverse the polarity, allowing to control direction of each engine.</p>
<p>Here’s schematics of the L293D:</p>
<p class="left" style="width: calc(25% - 15px);">
<img src="https://dvratil.cz/assets/rc-lego/l293d-schema.png" alt="L293D schematics" title="L293D Schematics" />
</p>
<p class="right" style="width: calc(75% - 15px);">
To sum it up, <code>pin 1 (Enable 1,2)</code> turns on the left half of the IC, <code>pin 9 (Enable 3,4)</code> turns
on the right half of the IC. Hook it up to Arduino's 5V pin. Do the same with <code>pin 16 (VCC1)</code>, which powers
the overall integrated circuit. The external power source (the 9V from the LEGO Battery pack) is connected to
<code>pin 8 (VCC2)</code>. <code>Pin 2 (Input 1)</code> and <code>pin 7 (Input 2)</code> are connected to Arduino and
are used to control the engines. <code>Pin 3 (Output 1)</code> and <code>pin 6 (Output 2)</code> are output pins that
are connected to one of the LEGO engines. On the other side of the circuit, <code>pin 10 (Input 3)</code> and
<code>pin 15 (Input 4)</code> are used to control the other LEGO engine, which is connected to <code>pin 11 (Output 3)
</code> and <code>pin 14 (Output 4)</code>. The remaining four pins in the middle (<code>4</code>, <code>5</code>,
<code>12</code> and <code>13</code> double as ground and heat sink, so connect them to GND (ideally both Arduino and
the LEGO battery GND).</p>
<div class="clear"></div>
<p>Since we have 9V LEGO Battery pack connected to <code>VCC2</code>, sending 5V from Arduino to <code>Input 1</code> and
0V to <code>Input 2</code> will cause 9V on <code>Output 1</code> and 0V on <code>Output 2</code> (the engine will spin
clockwise). Sending 5V from Arduino to <code>Input 2</code> and 0V to <code>Input 1</code> will cause 9V to be on
<code>Output 2</code> and 0V on <code>Output 1</code>, making the engine rotate counterclockwise. Same goes for the
other side of the IC. Simple!</p>
<p><a href="https://dvratil.cz/assets/rc-lego/full/arduino-wires-1.jpg" class="image left" target="_blank"><img src="https://dvratil.cz/assets/rc-lego/arduino-wires-1.jpg" alt="Photo of all electronic components wired together" title="Photo of all electronic components wired together." /></a>
<a href="https://dvratil.cz/assets/rc-lego/full/arduino-wires-2.jpg" class="image right" target="_blank"><img src="https://dvratil.cz/assets/rc-lego/arduino-wires-2.jpg" alt="Photo of all electronic components wired together" title="Photo of all electronic components wired together." /></a></p>
<div class="clear"></div>
<h2 id="conclusion">Conclusion</h2>
<p>I also built a LEGO casing for the Arduino board and the breadboard to attach them to the car. With some effort I
could probably rebuild the chassis to allow the casing to “sink” lower into the construction.</p>
<p><a href="https://dvratil.cz/assets/rc-lego/full/remotebrick-arduino.jpg" target="_blank"><img src="https://dvratil.cz/assets/rc-lego/remotebrick-arduino.jpg" alt="Photo of LEGO car with the electronics on board" title="Photo of LEGO car with the electronics on board." class="center" /></a></p>
<p>The batterry packs (the LEGO Battery box and the 9V battery case for Arduino) are nicely hidden in the middle
of the car on the sides next to the engines.</p>
<p><a href="https://dvratil.cz/assets/rc-lego/full/remotebrick-battery-1.jpg" class="image left" target="_blank"><img src="https://dvratil.cz/assets/rc-lego/remotebrick-battery-1.jpg" alt="Photo of LEGO Battery Box" title="Photo of the LEGO Battery Box" /></a>
<a href="https://dvratil.cz/assets/rc-lego/full/remotebrick-battery-2.jpg" class="image right" target="_blank"><img src="https://dvratil.cz/assets/rc-lego/remotebrick-battery-2.jpg" alt="Photo of Arduino 9V battery case" title="Photo of the Arduino 9V battery case" /></a></p>
<div class="clear"></div>
<p>Now we are done with the hardware side - we have a LEGO car with two engines and all the electronics wired together
and hooked up to the engines and battery. In the next part we will start writing software for the Arduino board so
that we can control the LEGO engines programmatically. Stay tuned!</p>Daniel VrátilRecently my 4 year-old stepson saw a kid with an RC racing car in a park. He really wanted his own, but with Christmas and his birthday still being a long way away, I decided to solve the “problem” by combining three things I’m really passionate about: LEGO, electronics and programming.Taking a break2021-05-03T03:00:00-05:002021-05-03T03:00:00-05:00https://dvratil.cz/2021/05/taking-a-break<p>I’ve seen lots of posts like this in the past, never thought I’d be writing one myself.</p>
<p>I haven’t been very actively contributing to KDE for the past months. It’s been rather frustrating,
because I felt like I <em>have</em> to contribute something, fix some bugs, finish some feature…but
whenever I had the time to work on PIM, I just couldn’t bring myself to do anything. Instead I
found myself running away to other projects or just playing games.</p>
<p>It took me a while to realize that the problem was that I was putting pressure on myself to contribute
even though I did not feel like it. It turned from hobby and passion into a duty, and that’s wrong.</p>
<p>I think the main frustration comes from the feeling that I cannot innovate - I’m bound by various
restrictions - libraries and languages I can use, APIs I must preserve/conform to, legacy behavior
to not break anything for existing users… This has been taking away the fun. I have enough of this in
my dayjob, thank you. So….</p>
<p>I decided to take a break from KDE PIM for a while. I’m sure I’ll be back at some point. But right now
I feel like I gave it all I could and it’s still not where I’d like it to be and it’s no longer
fun for me. What makes me very happy is the number of new contributors that have appeared over the past year
or so.</p>
<p>Instead of hacking on KDE PIM I went back to some of my older projects - I improved <a href="https://github.com/danvratil/harbour-passilic">Passilic</a>,
the <a href="https://www.passwordstore.org/">Pass</a> password manager frontend for Sailfish OS and revived my old Android app to sync <a href="https://github.com/danvratil/fbeventsync">Facebook
events with Android calenar</a>.</p>
<p>I also started playing with C++20 coroutines and how they could be used with Qt. The result is the
<a href="https://danvratil.github.io/qcoro/">QCoro library</a>. I’ll blog about that soon and, hopefully, will talk about it in more depth in
two months on Akademy (see, I’m not leaving completely 😉).</p>
<p>Finally, I spent the past week building a remote-controlled car using Lego, Arduino and a mobile app
I wrote (with Qt, of course 😉). I’ll blog about that as well (spoiler alert: it’s fun!).</p>
<p>See y’all around!</p>
<p>/Dan</p>Daniel VrátilI’ve seen lots of posts like this in the past, never thought I’d be writing one myself.