QCoro
C Coroutine Library for Qt5 and Qt6
Overview
QCoro is a C library that provide set of tools to make use of C 20 coroutines in connection with certain asynchronous Qt actions.
Take a look at the example below to see what an amazing thing coroutines are:
QNetworkAccessManager networkAccessManager;
// co_await the reply - the coroutine is suspended until the QNetworkReply is finished.
// While the coroutine is suspended, *the Qt event loop runs as usual*.
const QNetworkReply *reply = co_await networkAccessManager.get(url);
// Once the reply is finished, your code resumes here as if nothing amazing has just happened ;-)
const auto data = reply->readAll();
This library has only one class and one function that the user must be aware of: the class is
QCoro::Task
and must be used as a return type for any coroutine that co_await
s
a Qt type. The function is qCoro()
and it provides coroutine-friendly
wrappers for Qt types that have multiple asynchronous operations that the user may want to co_await
(for example QProcess
). All the other code (basically everything in the
QCoro::detail
namespace) is here to provide the cogs and gears for the C coroutine machinery,
making it possible to use Qt types with coroutines.
The major benefit of using coroutines with Qt types is that it allows writing asynchronous code as if it
were synchronous and, most importantly, while the coroutine is co_await
ing, the Qt event loop runs
as usual, meaning that your application remains responsive.
This is a rather experimental library that I started working on to better understand coroutines in C . After reading numerous articles and blog posts about coroutines, it still wasn't exactly clear to me how the whole thing works, so I started working on this library to get a better idea about coroutines.
Coroutines
Coroutines are regular functions, except that they can be suspended and resumed again. When
a coroutine is suspended, it returns sort of a promise to the caller and the caller continues
executing their code. At some point, the caller can use the newly introduced co_await
keyword
to wait for the returned promise to be fulfilled. When that happens, the caller is suspended
and instead the coroutine is resumed.
This allows writing asynchronous code as if it were synchronous, making it much easier to read and understand.
That's not all that coroutines can do, you can read more about it in the 'Coroutines' section of this documentation.
Supported Qt Versions
QCoro supports compiling for both Qt5 and Qt6. Minimum supported versions are:
- Qt5 >= 5.15.2
- Qt6 >= 6.2.0
Pass -DUSE_QT_VERSION=5
or -DUSE_QT_VERSION=6
to CMake to force building QCoro with respective
major version of Qt. QCoro will default to Qt6 when available and fallback to Qt5 otherwise.
Supported Compilers
This library requires a compiler that supports the Coroutine TS (obviously). Currently GCC, Clang and MSVC are supported.
Officially supported compilers are:
- GCC >= 11 (April 2021)
- Clang >= 15 (September 2022)
- MSVC >= 19.40 (Visual Studio 17 2022)
- AppleClang >= 15.0.0 (Xcode 15.2)
In both GCC and Clang, coroutine support must be explicitly enabled.
GCC
To enable coroutines support in GCC, add -fcoroutines
to CXX_FLAGS
.
CMake:
set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fcoroutines")
Alternatively, just use qcoro_enable_coroutines()
CMake macro provided by QCoro to set the
flags automatically.
Clang
In Clang coroutines are still considered experimental (unlike in GCC).
Coroutines are enabled by adding -fcoroutines-ts
to CMAKE_CXX_FLAGS
.
CMake:
set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fcoroutines-ts")
Alternatively, just use qcoro_enable_coroutines()
CMake macro provided by QCoro to set the
flags automatically.
MSVC
Coroutine support in MSVC is enabled automatically by CMake when C 20 standard is specified
in CMAKE_CXX_STANDARD
:
set(CMAKE_CXX_STANDARD 20)