Namespaces
Variants
Views
Actions

Difference between revisions of "cpp/coroutine/generator"

From cppreference.com
< cpp‎ | coroutine
(Example: a data structure iteration I think is a compelling demo of the utility of generator, especially a recursive data structure)
m (Example: reverse the tree.)
Line 93: Line 93:
 
===Example===
 
===Example===
 
{{example
 
{{example
 +
|Can be tried in [https://godbolt.org/z/6K6Kr34a8 Compiler Explorer]
 
|code=
 
|code=
 
#include <generator>
 
#include <generator>
Line 115: Line 116:
 
     }
 
     }
 
};
 
};
 
  
 
int main()
 
int main()
 
{
 
{
     Tree<int> tree[]
+
     Tree<char> tree[]
 
     {
 
     {
        {0},                     {2}, {4},                      {6},  
+
                                    {'D', tree + 1, tree + 2},
           {1, tree + 0, tree + 1},      {5, tree + 2, tree + 3},  
+
        //                            │
                            {3, tree + 4, tree + 5}
+
        //           ┌───────────────┴────────────────┐
 +
        //            │                                │
 +
                    {'B', tree + 3, tree + 4},      {'F', tree + 5, tree + 6},
 +
        //            │                                │
 +
        //  ┌─────────┴─────────────┐      ┌───────────┴─────────────┐
 +
        //  │                      │      │                        │
 +
          {'A'},                 {'C'}, {'E'},                    {'G'}
 
     };
 
     };
  
     for (int x : tree[6].traverse_preorder())
+
     for (char x : tree->traverse_preorder())
 
         std::cout << x << ' ';
 
         std::cout << x << ' ';
 
 
     std::cout << '\n';
 
     std::cout << '\n';
 
}
 
}
 
|output=
 
|output=
0 1 2 3 4 5 6
+
A B C D E F G
 
}}
 
}}
  

Revision as of 12:47, 20 January 2024

 
 
Utilities library
General utilities
Relational operators (deprecated in C++20)
 
Coroutine support
Coroutine traits
Coroutine handle
No-op coroutines
Trivial awaitables
Range generators
generator
(C++23)
 
Ranges library
Range adaptors
 
 
Defined in header <generator>
template<

    class Ref,
    class V = void,
    class Allocator = void >
class generator

    : public ranges::view_interface<generator<Ref, V, Allocator>>
(1) (since C++23)
namespace pmr {

    template< class Ref, class V = void >
    using generator =
        std::generator<Ref, V, std::pmr::polymorphic_allocator<>>;

}
(2) (since C++23)
1) The class template std::generator presents a view of the elements yielded by the evaluation of a coroutine.
2) Convenience alias template for the generator using the polymorphic allocator.

A std::generator generates a sequence of elements by repeatedly resuming the coroutine from which it was returned. Each time a co_yield statement is evaluated, the coroutine produces one element of the sequence. When the co_yield statement is of the form co_yield ranges::elements_of(rng), each element of the range rng is successively produced as an element of the sequence.

std::generator models view and input_range.

The behavior of a program that adds a specialization for std::generator is undefined.

Contents

Data members

Name Description
active_ (private)

Internally, each active instance of std::generator is associated with a stack (handled as if by object of type std::unique_ptr<std::stack<std::coroutine_handle<>>>).

  • When begin is called, a new stack is created and the generator is added to the stack.
  • When co_yield ranges::elements_of(rng) is evaluated in a generator body, rng is converted to a generator and added to the stack that contains the enclosing generator.
  • When a generator iterator is incremented, the coroutine at the top of the associated stack is resumed.
  • When a generator finishes (i.e. when final_suspend is called), it is removed from the stack.
    (exposition-only member object*)
coroutine_ (private) std::coroutine_handle<promise_type>
(exposition-only member object*)

Template parameters

Ref - the reference type (ranges::range_reference_t) of the generator. If V is void, both the reference type and the value type are inferred from Ref
V - the value type (range_value_t) of the generator, or void
Allocator - an allocator type or void

If Allocator is not void, then:

Member types

Member type Definition
value (private) std::conditional_t<std::is_void_v<V>, std::remove_cvref_t<Ref>, V>;.
value is a cv-unqualified object type.
(exposition-only member type*)
reference (private) std::conditional_t<std::is_void_v<V>, Ref&&, Ref>;.
reference is either a reference type, or a cv-unqualified object type that models copy_constructible.
(exposition-only member type*)
yielded std::conditional_t<std::is_reference_v<reference>, reference, const reference&>.

Let /*RRef*/ denote:

The following concepts are modeled:

Member functions

constructs a generator object
(public member function) [edit]
effectively destroys the entire stack of yielded generators
(public member function) [edit]
assigns a generator object
(public member function) [edit]
resumes the initially suspended coroutine and returns an iterator to its handle
(public member function) [edit]
returns std::default_sentinel
(public member function) [edit]
Inherited from std::ranges::view_interface
returns whether the derived view is empty. Provided if it satisfies sized_range or forward_range.
(public member function of std::ranges::view_interface<D>) [edit]
(C++23)
returns a constant iterator to the beginning of the range.
(public member function of std::ranges::view_interface<D>) [edit]
(C++23)
returns a sentinel for the constant iterator of the range.
(public member function of std::ranges::view_interface<D>) [edit]
returns whether the derived view is not empty. Provided if ranges::empty is applicable to it.
(public member function of std::ranges::view_interface<D>) [edit]

Nested classes

the promise type
(public member class)
the iterator type
(exposition-only member class*)

Notes

Feature-test macro Value Std Feature
__cpp_lib_generator 202207L (C++23) std::generator – synchronous coroutine generator for ranges

Example

Can be tried in Compiler Explorer

#include <generator>
#include <iostream>
 
template<typename T>
struct Tree
{
    T value;
    Tree *left{}, *right{};
 
    std::generator<const T&> traverse_preorder() const
    {
        if (left)
            for (const T& x : left->traverse_preorder())
                co_yield x;
 
        co_yield value;
        if (right)
            for (const T& x : right->traverse_preorder())
                co_yield x;
    }
};
 
int main()
{
    Tree<char> tree[]
    {
                                    {'D', tree + 1, tree + 2},
        //                            │
        //            ┌───────────────┴────────────────┐
        //            │                                │
                    {'B', tree + 3, tree + 4},       {'F', tree + 5, tree + 6},
        //            │                                │
        //  ┌─────────┴─────────────┐      ┌───────────┴─────────────┐
        //  │                       │      │                         │
          {'A'},                  {'C'}, {'E'},                    {'G'}
    };
 
    for (char x : tree->traverse_preorder())
        std::cout << x << ' ';
    std::cout << '\n';
}

Output:

A B C D E F G

References

  • C++23 standard (ISO/IEC 14882:2024):
  • 26.8 Range generators [coro.generator]

See also

creates a coroutine handle that has no observable effects when resumed or destroyed
(function) [edit]