Thank you to everyone who reported issues and contributed to QCoro. Your help is much appreciated!

Support for awaiting Qt signals with QPrivateSignal

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 QPrivateSignal argument to the signal method:

class MyObject : public QObject {
    Q_OBJECT
...
Q_SIGNALS:
    void error(int code, const QString &message, QPrivateSignal);
};

QPrivateSignal is a type that is defined inside the Q_OBJECT macro, so it’s private and as such only MyObject class can emit the signal, since only MyObject can instantiate QPrivateSignal:

void MyObject::handleError(int code, const QString &message)
{
    Q_EMIT error(code, message, QPrivateSignal{});
}

QCoro has a feature that makes it possible to co_await a signal emission and returns the signals arguments as a tuple:


MyObject myObject;
const auto [code,  message] = co_await qCoro(&myObject, &MyObject::handleError);

While it was possible to co_await a “private” signal previously, it would get return the QPrivateSignal value as an additional value in the result tuple and on some occasions would not compile at all.

In QCoro 0.10, we can detect the QPrivateSignal argument and drop it inside QCoro so that it does not cause trouble and does not clutter the result type.

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 Obj::QPrivateSignal, since that type is private to Obj.

template<typename T, typename Obj>
constexpr bool is_qprivatesignal = std::same_as_v<T, typename Obj::QPrivateSignal>;

After many different attempts we ended up abusing __PRETTY_FUNCTION__ (and __FUNCSIG__ on MSVC) and checking whether the function’s name contains QPrivateSignal 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.

Source Code Reorganization (again!)

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.

Therefore I decided to move definitions into separated files, so that they don’t clutter the main include files.

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.

Bugfixes

  • QCoro::waitFor() now re-throws exceptions (#172, Daniel Vrátil)
  • Replaced deprecated QWebSocket::error with QWbSocket::errorOccured in QCoroWebSockets module (#174, Marius P)
  • Fix QCoro::connect() not working with lambdas (#179, Johan Brüchert)
  • Fix library name postfix for qmake compatibilty (#192, Shantanu Tushar)
  • Fix std::coroutine_traits isn't a class template error with LLVM 16 (#196, Rafael Sadowski)

Full changelog

See changelog on Github