Skip to content

QDBusPendingReply

ModuleQCoroDBus
Include
#include <QCoroDBusPendingReply>
CMake
target_link_libraries(myapp QCoro::QCoroDBus)

QDBusPendingReply on its own doesn't have any operation that could be awaited asynchronously, this is usually done through a helper class called QDBusPendingCallWatcher. To simplify the API, QCoro allows to directly co_await completion of the pending reply or use a wrapper class QCoroDBusPendingReply. To wrap a QDBusPendingReply into a QCoroDBusPendingReply, use qCoro():

template<typename ... Args>
QCoroDBusPendingCall qCoro(const QDBusPendingReply<Args ...> &);

QDBusPendingReply in Qt5 vs Qt6

QDBusPendingReply in Qt6 is a variadic template, meaning that it can take any amount of template arguments. In Qt5, however, QDBusPendingReply is a template class that accepts only up to 8 paremeters. In QCoro the QCoroDBusPendingReply wrapper is implemented as a variadic template for compatibility with Qt6, but when building against Qt5, the number of template parameters is artificially limited to 8 to mirror the limitation of Qt5 QDBusPendingReply limitation.

To await completion of the pending call without the qCoro wrapper, just use the pending call in a co_await expression. The behavior is identical to awaiting on result of QCoroDBusPendingReply::waitForFinished().

QDBusPendingReply<...> reply = interface.asyncCall(...);
co_await reply;
// Now the reply is finished and the result can be retrieved.

waitForFinished()

Waits until the DBus call is finished. This is equivalent to using QDBusPendingCallWatcher and waiting for it to emit the finished() signal.

Returns a QDBusMessage representing the received reply. If the reply is already finished or an error has occurred the coroutine will not suspend and will return a result immediatelly.

This is a coroutine-friendly equivalent to using QDBusPendingCallWatcher:

QDBusPendingCall call = interface.asyncCall(...);
QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(call);
QObject::connect(watcher, &QDBusPendingCallWatcher::finished,
                 this, [](QDBusPendingCallWatcher *watcher) {
                    watcher->deleteLater();
                    const QDBusReply<...> reply = *watcher;
                    ...
                });

It is also possible to just directly use a QDBusPendingReply in a co_await expression to await its completion:

QDBusPendingReply<...> pendingReply = interface.asyncCall(...);
const auto reply = co_await pendingReply;

The above is equivalent to:

QDBusPendingReply<...> pendingReply = interface.asyncCall(...);
const auto reply = co_await qCoro(pendingReply).waitForFinished();

Example

#include <QCoroDBus>

QCoro::Task<QString> PlayerControl::nextSong() {
    // Create a regular QDBusInterface representing the Spotify MPRIS interface
    QDBusInterface spotifyPlayer{QStringLiteral("org.mpris.MediaPlayer2.spotify"),
                                 QStringLiteral("/org/mpris/MediaPlayer2"),
                                 QStringLiteral("org.mpris.MediaPlayer2.Player")};
    // Call CanGoNext DBus method and co_await reply. During that the current coroutine is suspended.
    const QDBusReply<bool> canGoNext = co_await spotifyPlayer.asyncCall(QStringLiteral("CanGoNext"));
    // Response has arrived and coroutine is resumed. If the player can go to the next song,
    // do another async call to do so.
    if (static_cast<bool>(canGoNext)) {
        // co_await the call to finish, but throw away the result
        co_await spotifyPlayer.asyncCall(QStringLiteral("Next"));
    }

    // Finally, another async call to retrieve new track metadata. Once again, the coroutine
    // is suspended while we wait for the result.
    const QDBusReply<QVariantMap> metadata = co_await spotifyPlayer.asyncCall(QStringLiteral("Metadata"));
    // Since this function uses co_await, it is in fact a coroutine, so it must use co_return in order
    // to return our result. By definition, the result of this function can be co_awaited by the caller.
    co_return static_cast<const QVariantMap &>(metadata)[QStringLiteral("xesam:title")].toString();
}