std::corout...tine_handle(3)

πŸ“¦ version 2024.07.05, in nixpkgs unstable, opensuse-leap 16.0

πŸ’– NAME πŸ’–

okay so bestie, the page is std::coroutine_handle,std::noop_coroutine_handle β€” and yes, the NAME line literally repeats it: std::coroutine_handle,std::noop_coroutine_handle. This is from the C++ Standard Libary manual (typo preserved, we don’t fix the header), and the page header is std::corout...tine_handle(3) β€” the truncated corout...tine_handle is iconic, we respect it.


🌸 Synopsis 🌸

Defined in header <coroutine>:

template< class Promise = void >                        (1) (since C++20)
struct coroutine_handle;
template<>                                              (2) (since C++20)
struct coroutine_handle<void>;
template<>                                              (3) (since C++20)
struct coroutine_handle<std::noop_coroutine_promise>;
using noop_coroutine_handle =                           (4) (since C++20)
    std::coroutine_handle<std::noop_coroutine_promise>;

The class template coroutine_handle can be used to refer to a suspended or executing coroutine. Every specialization of coroutine_handle is a LiteralType.

  1. Primary template, can be created from the promise object of type Promise.
  2. Specialization std::coroutine_handle<void> erases the promise type. It is convertible from other specializations.
  3. Specialization std::coroutine_handle<std::noop_coroutine_promise> refers to no-op coroutines. It cannot be created from a promise object.

On typical implementations, every specialization of std::coroutine_handle is TriviallyCopyable.

🚨 If the program adds specializations for std::coroutine_handle, the behavior is undefined.

🧚 Member functions 🧚

  • constructor β€” constructs a coroutine_handle object (C++20) (public member function)
  • operator= β€” assigns the coroutine_handle object (C++20) (public member function)

Conversion

  • operator coroutine_handle<> β€” obtains a type-erased coroutine_handle (C++20) (public member function)

Observers

  • done β€” checks if the coroutine has completed (C++20) (public member function)
  • operator bool β€” checks if the handle represents a coroutine (C++20) (public member function)

Control

  • operator() β€” resumes execution of the coroutine (C++20) (public member function)
  • resume β€” resumes execution of the coroutine (C++20) (public member function)
  • destroy β€” destroys a coroutine (C++20) (public member function)

Promise Access

  • promise β€” access the promise of a coroutine (C++20) (public member function)
  • from_promise β€” creates a coroutine_handle from the promise object of a coroutine; [static] (C++20) (public static member function)

Export/Import

  • address β€” exports the underlying address, i.e. the pointer backing the coroutine (C++20) (public member function)
  • from_address β€” imports a coroutine from a pointer; [static] (C++20) (public static member function)

🫢 Non-member functions 🫢

  • operator== β€” compares two coroutine_handle objects (function)
  • operator<=> β€” (function) (C++20)

✨ Helper classes ✨

  • std::hash<std::coroutine_handle> β€” hash support for std::coroutine_handle (C++20) (class template specialization)

🌈 Notes 🌈

A coroutine_handle may be dangling, in which case the coroutine_handle must be used carefully in order to avoid undefined behavior.


πŸŽ€ Example πŸŽ€

// Run this code

#include <coroutine>
#include <iostream>
#include <optional>

template<std::movable T>
class Generator
{
public:
    struct promise_type
    {
        Generator<T> get_return_object()
        {
            return Generator{Handle::from_promise(*this)};
        }
        static std::suspend_always initial_suspend() noexcept
        {
            return {};
        }
        static std::suspend_always final_suspend() noexcept
        {
            return {};
        }
        std::suspend_always yield_value(T value) noexcept
        {
            current_value = std::move(value);
            return {};
        }
        // Disallow co_await in generator coroutines.
        void await_transform() = delete;
        [[noreturn]]
        static void unhandled_exception() { throw; }

        std::optional<T> current_value;
    };

    using Handle = std::coroutine_handle<promise_type>;

    explicit Generator(const Handle coroutine) :
        m_coroutine{coroutine}
    {}

    Generator() = default;
    ~Generator()
    {
        if (m_coroutine)
            m_coroutine.destroy();
    }

    Generator(const Generator&) = delete;
    Generator& operator=(const Generator&) = delete;

    Generator(Generator&& other) noexcept :
        m_coroutine{other.m_coroutine}
    {
        other.m_coroutine = {};
    }
    Generator& operator=(Generator&& other) noexcept
    {
        if (this != &other)
        {
            if (m_coroutine)
                m_coroutine.destroy();
            m_coroutine = other.m_coroutine;
            other.m_coroutine = {};
        }
        return *this;
    }

    // Range-based for loop support.
    class Iter
    {
    public:
        void operator++()
        {
            m_coroutine.resume();
        }
        const T& operator*() const
        {
            return *m_coroutine.promise().current_value;
        }
        bool operator==(std::default_sentinel_t) const
        {
            return !m_coroutine || m_coroutine.done();
        }

        explicit Iter(const Handle coroutine) :
            m_coroutine{coroutine}
        {}

    private:
        Handle m_coroutine;
    };

    Iter begin()
    {
        if (m_coroutine)
            m_coroutine.resume();
        return Iter{m_coroutine};
    }

    std::default_sentinel_t end() { return {}; }

private:
    Handle m_coroutine;
};

template<std::integral T>
Generator<T> range(T first, const T last)
{
    while (first < last)
        co_yield first++;
}

int main()
{
    for (const char i : range(65, 91))
        std::cout << i << ' ';
    std::cout << '\n';
}

Output:

A B C D E F G H I J K L M N O P Q R S T U V W X Y Z

🩹 Defect reports 🩹

The following behavior-changing defect reports were applied retroactively to previously published C++ standards.

  • LWG 3460 β€” applied to C++20; behavior as published: the public base class of coroutine_handle could leave it in an undesired state; correct behavior: inheritance removed.

πŸ‘‘ See also πŸ‘‘

  • generator β€” a view that represents synchronous coroutine generator (C++23) (class template)

πŸ’– source/version: http://cppreference.com β€” date: 2024.06.10 β€” that’s the footer, bestie, and no cap it’s exactly where the page ends 🫢✨

πŸ“¦ where this page comes from