std::coroutine_handle, std::noop_coroutine_handle

From cppreference.com
< cpp‎ | coroutine
 
 
Utilities library
General utilities
Date and time
Function objects
Formatting library (C++20)
(C++11)
Relational operators (deprecated in C++20)
Integer comparison functions
(C++20)(C++20)(C++20)   
(C++20)
Swap and type operations
(C++14)
(C++11)
(C++11)
(C++11)
(C++17)
Common vocabulary types
(C++11)
(C++17)
(C++17)
(C++17)
(C++11)
(C++17)
(C++23)
Elementary string conversions
(C++17)
(C++17)
 
Coroutine support
Coroutine traits
Coroutine handle
coroutine_handle
(C++20)
No-op coroutines
Trivial awaitables
 
 
Defined in header <coroutine>
template< class Promise = void > struct coroutine_handle;
(1) (since C++20)
template<> struct coroutine_handle<void>;
(2) (since C++20)
template<> struct coroutine_handle<std::noop_coroutine_promise>;
(3) (since C++20)
using noop_coroutine_handle =
    std::coroutine_handle<std::noop_coroutine_promise>;
(4) (since C++20)

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, and holds a pointer to the coroutine state as its only non-static data member.

The behavior of a program that adds specializations for coroutine_handle is undefined.

Member functions

constructs a coroutine_handle object
(public member function)
(C++20)
assigns the coroutine_handle object
(public member function)
Conversion
obtains a type-erased coroutine_handle
(public member function)
Observers
(C++20)
checks if the coroutine has completed
(public member function)
checks if the handle represents a coroutine
(public member function)
Control
resumes execution of the coroutine
(public member function)
(C++20)
destroys a coroutine
(public member function)
Promise Access
(C++20)
access the promise of a coroutine
(public member function)
[static] (C++20)
creates a coroutine_handle from the promise object of a coroutine
(public static member function)
Export/Import
(C++20)
exports the underlying address, i.e. the pointer backing the coroutine
(public member function)
[static] (C++20)
imports a coroutine from a pointer
(public static member function)

Non-member functions

compares two coroutine_handle objects
(function)

Helper classes

hash support for std::coroutine_handle
(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

#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.

DR Applied to Behavior as published Correct behavior
LWG 3460 C++20 the public base class of coroutine_handle could leave it in an undesired state inheritance removed