---
title: QCoro 0.6.0 Release Announcement
date: "2022-07-09"
description: >
  This release brings several major new featrues alongside a bunch of bufixes and
  improvements inside QCoro. The four major features are generators support,
  new QCoroWebSocket module, deprecation of task.h header, and introduction of
  clang-cl and apple-clang support.
---

<!--
SPDX-FileCopyrightText: 2022 Daniel Vrátil <dvratil@kde.org>

SPDX-License-Identifier: GFDL-1.3-or-later
-->

# QCoro 0.6.0 Release Announcement

This release brings several major new features alongside a bunch of bugfixes and
improvements inside QCoro.

The four major features are:

* Generator support
* New QCoroWebSockets module
* Deprecated task.h
* Clang-cl and apple-clang support

🎉 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. 🎉

As always, big thank you to everyone who report issues and contributed to QCoro.
Your help is much appreciated!

## Generator support

Unlike regular functions (or `QCoro::Task<>`-based coroutines) which can only ever
produce at most single result (through `return` or `co_return` 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 `QCoro::Generator<T>`:

```cpp
// A generator that produces a sequence of numbers from 0 to `end`.
QCoro::Generator<int> sequence(int end) {
    for (int i = 0; i <= end; ++i) {
        // Produces current value of `i` and suspends.
        co_yield i;
    }
    // End the iterator
}

int sumSequence(int end) {
    int sum = 0;
    // Loops over the returned Generator, resuming the generator on each iterator
    // so it can produce a value that we then consume.
    for (int value : sequence(end)) {
        sum += value;
    }
    return sum;
}
```

The `Generator` interface implements `begin()` and `end()` 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.

You can find more details about synchronous generators in the [`QCoro::Generator<T>`
documentation](https://qcoro.dvratil.cz/reference/coro/generator/).

Asynchronous generators work in a similar way, but they produce value asynchronously,
that is the result of the generator must be `co_await`ed by the caller.

```cpp
QCoro::AsyncGenerator<QUrl> paginator(const QUrl &baseUrl) {
  QUrl pageUrl = baseUrl;
  Q_FOREVER {
    pageUrl = co_await getNextPage(pageUrl); // co_awaits next page URL
    if (pageUrl.isNull()) { // if empty, we reached the last page
      break; // leave the loop
    }
    co_yield pageUrl; // finally, yield the value and suspend
  }
  // end the generator
}

QCoro::AsyncGenerator<QString> pageReader(const QUrl &baseUrl) {
  // Create a new generator
  auto generator = paginator(baseUrl);
  // Wait for the first value
  auto it = co_await generator.begin();
  auto end = generator.end();
  while (it != end) { // while the `it` iterator is valid...
    // Asynchronously retrieve the page content
    const auto content = co_await fetchPageContent(*it);
    // Yield it to the caller, then suspend
    co_yield content;
    // When resumed, wait for the paginator generator to produce another value
    co_await ++it;
  }
}

QCoro::Task<> downloader(const QUrl &baseUrl) {
  int page = 1;
  // `QCORO_FOREACH` is like `Q_FOREACH` for asynchronous iterators
  QCORO_FOREACH(const QString &page, pageReader(baseUrl)) {
    // When value is finally produced, write it to a file
    QFile file(QStringLiteral("page%1.html").arg(page));
    file.open(QIODevice::WriteOnly);
    file.write(page);
    ++page;
  }
}
```

Async generators also have `begin()` and `end()` methods which provide an asynchronous
iterator-like types. For one, the `begin()` method itself is a coroutine and must be
`co_await`ed to obtain the initial iterator. The increment operation of the iterator
must then be `co_await`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 [`QCORO_FOREACH` macro](https://qcoro.dvratil.cz/reference/coro/asyncgenerator/#qcoro_foreach) to make using asynchronous generators simpler.

Read the [documentation for `QCoro::AsyncGenerator<T>`](https://qcoro.dvratil.cz/reference/coro/asyncgenerator) for more details.

## New QCoroWebSockets module

The QCoroWebSockets module provides QCoro wrappers for `QWebSocket` and `QWebSocketServer`
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.

```cpp
QCoro::Task<> ChatApp::handleNotifications(const QUrl &wsServer) {
  if (!co_await qCoro(mWebSocket).open(wsServer)) {
    qWarning() << "Failed to open websocket connection to" << wsServer << ":" << mWebSocket->errorString();
    co_return;
  }
  qDebug() << "Connected to" << wsServer;

  // Loops whenever a message is received until the socket is disconnected
  QCORO_FOREACH(const QString &rawMessage, qCoro(mWebSocket).textMessages()) {
    const auto message = parseMessage(rawMessage);
    switch (message.type) {
      case MessageType::ChatMessage:
        handleChatMessage(message);
        break;
      case MessageType::PresenceChange:
        handlePresenceChange(message);
        break;
      case MessageType::Invalid:
        qWarning() << "Received an invalid message:" << message.error;
        break;
    }
  }
}
```
The `textMessages()` 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 `co_await`ing the next
emission of the `QWebSocket::textMessage()` 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
`co_await`ing a singal emission, any message that is received before we start `co_await`ing
again after handling the current message would be lost.

You can find more details about the `QCoroWebSocket` and `QCoroWebSocketSever`
in the [QCoro's websocket module documentation](https://qcoro.dvratil.cz/reference/websockets/).

You can build QCoro without the WebSockets module by passing `-DQCORO_WITH_QTWEBSOCKETS=OFF`
to CMake.

## Deprecated tasks.h header

The `task.h` header and it's camelcase variant `Task` been deprecated in QCoro 0.6.0
in favor of `qcorotask.h` (and `QCoroTask` 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 `qcoro` (or `QCoro`) prefix.

The old header is still present and fully functional, but including it will produce a
warning that you should port your code to use `qcorotask.h`. You can suppress the warning
by defining `QCORO_NO_WARN_DEPRECATED_TASK_H` in the compiler definitions:

CMake:
```cmake
add_compiler_definitions(QCORO_NO_WARN_DEPRECATED_TASK_H)
```

QMake
```qmake
DEFINES += QCORO_NO_WARN_DEPRECATED_TASK_H
```

The header file will be removed at some point in the future, at latest in the 1.0 release.

You can also pass `-DQCORO_DISABLE_DEPRECATED_TASK_H=ON` to CMake when compiling QCoro
to prevent it from installing the deprecated task.h header.

## Clang-cl and apple-clang support

The clang compiler is fully supported by QCoro since 0.4.0. This version of QCoro
intruduces supports for clang-cl and apple-clang.

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.

Apple-clang is the official build of clang provided by Apple on MacOS, which may be
different from the upstream clang releases.

## Full changelog

[See changelog on Github](https://github.com/danvratil/qcoro/releases/tag/v0.6.0)
