Skip to content

QCoro::Task

template<typename T> class QCoro::Task

Any coroutine that wants to co_await one of the types supported by the QCoro library must have return type QCoro::Task<T>, where T is the type of the "regular" coroutine return value.

There's no need by the user to interact with or construct QCoro::Task manually, the object is constructed automatically by the compiler before the user code is executed. To return a value from a coroutine, use co_return, which will store the result in the Task object and leave the coroutine.

QCoro::Task<QString> getUserName(UserID userId) {
    ...

    // Obtain a QString by co_awaiting another coroutine
    const QString result = co_await fetchUserNameFromDb(userId);

    ...

    // Return the QString from the coroutine as you would from a regular function,
    // just use `co_return` instead of `return` keyword.
    co_return result;
}

To obtain the result of a coroutine that returns QCoro::Task<T>, the result must be co_awaited. When the coroutine co_returns a result, the result is stored in the Task object and the co_awaiting coroutine is resumed. The result is obtained from the returned Task object and returned as a result of the co_await call.

QCoro::Task<void> getUserDetails(UserID userId) {
    ...

    const QString name = co_await getUserName(userId);

    ...
}

Exception Propagation

When coroutines throws an unhandled exception, the exception is stored in the Task object and is re-thrown from the co_await call in the awaiting coroutine.

Blocking wait

Sometimes it's necessary to wait for a coroutine in a blocking manner - this is especially useful in tests where possibly no event loop is running. QCoro has QCoro::waitFor() function which takes QCoro::Task<T> (that is, result of calling any QCoro-based coroutine) and blocks until the coroutine finishes. If the coroutine has a non-void return value, the value is returned from waitFor().

QCoro::Task<int> computeAnswer() {
    std::this_thread::sleep_for(std::chrono::year{7'500'000});
    co_return 42;
}

void nonCoroutineFunction() {
    // The following line will block as if computeAnswer were not a coroutine.
    const int answer = QCoro::waitFor(computeAnswer());
    std::cout << "The answer is: " << answer << std::endl;
}

Event loops

The implementation internally uses a QEventLoop to wait for the coroutine to be completed. This means that a QCoreApplication instance must exist, although it does not need to be executed. Usual warnings about using a nested event loop apply here as well.