Waitable QTimer with C++20 coroutines

Found an example of overriding co_await operator and was able to implement my own version:

#include <QTimer>
#include <QMetaObject>

#include <chrono>
#include <coroutine>

//QTimer accepts only milliseconds.
class TimeAwaitable
{
public:

    explicit TimeAwaitable(std::chrono::milliseconds d) : m_d(d) {}

    ~TimeAwaitable()
    {
        if (m_connection)
        {
            QObject::disconnect(m_connection);
        }
    }

    bool await_ready() const noexcept
    {
        return m_d <= std::chrono::milliseconds(0);
    }

    void await_suspend(std::coroutine_handle<> h) noexcept
    {
        m_connection = QObject::connect(&m_timer, &QTimer::timeout,
            [this, h]()
            {
                QObject::disconnect(m_connection);

                h.resume();
            });

        m_timer.setSingleShot(true);
        m_timer.start(m_d);
    }

    void await_resume() const noexcept {}

private:

    std::chrono::milliseconds m_d;
        
    QTimer m_timer;

    QMetaObject::Connection m_connection;
};

inline TimeAwaitable operator co_await(std::chrono::milliseconds d) noexcept
{
    return TimeAwaitable{ d };
}

For those who did not work with QT, QTimer::singleShot simply calls the lambda on the current thread after a given interval.

The usage is:

struct task
{
    struct promise_type
    {
        task get_return_object() { return {}; }
        std::suspend_never initial_suspend() { return {}; }
        std::suspend_never final_suspend() noexcept { return {}; }
        void return_void() {}
        void unhandled_exception() { std::terminate(); }
    };
};

task testAwait()
{
    using namespace std::chrono_literals;

    std::cout << "testAwait started.";

    co_await 5s;

    std::cout << "testAwait finished.";
}

Leave a Reply

Your email address will not be published. Required fields are marked *