Namespaces
Variants
Views
Actions

Difference between revisions of "cpp/coroutine/generator"

From cppreference.com
< cpp‎ | coroutine
(what's the point of these links?)
m
 
(27 intermediate revisions by 9 users not shown)
Line 7: Line 7:
 
     class Ref,
 
     class Ref,
 
     class V = void,
 
     class V = void,
     class Allocator = void
+
     class Allocator = void >
>
+
class generator
class generator : public ranges::view_interface<generator<Ref, V, Allocator>>
+
    : public ranges::view_interface<generator<Ref, V, Allocator>>
 
}}
 
}}
 
{{dcl|num=2|since=c++23|1=
 
{{dcl|num=2|since=c++23|1=
 
namespace pmr {
 
namespace pmr {
template< class Ref, class V = void >
+
    template< class Ref, class V = void >
using generator =
+
    using generator =
    std::generator<Ref, V, std::pmr::polymorphic_allocator<{{void}}Ref>>;
+
        std::generator<Ref, V, std::pmr::polymorphic_allocator<>>;
 
}
 
}
 
}}
 
}}
Line 22: Line 22:
 
@1@ The class template {{tt|std::generator}} presents a {{lconcept|view}} of the elements yielded by the evaluation of a {{lt|cpp/language/coroutines|coroutine}}.
 
@1@ The class template {{tt|std::generator}} presents a {{lconcept|view}} of the elements yielded by the evaluation of a {{lt|cpp/language/coroutines|coroutine}}.
  
@2@ Convenience alias template for the {{tt|generator}} using the [[cpp/memory/polymorphic_allocator|polymorphic allocator]].
+
@2@ Convenience alias template for the {{tt|generator}} using the {{lt|cpp/memory/polymorphic allocator}}.
  
 
A {{tt|std::generator}} generates a sequence of elements by repeatedly resuming the coroutine from which it was returned.
 
A {{tt|std::generator}} generates a sequence of elements by repeatedly resuming the coroutine from which it was returned.
Each time a {{c|co_yield}} statement is evaluated, the coroutine produces one element of the sequence.
+
Each time a {{c/core|co_yield}} statement is evaluated, the coroutine produces one element of the sequence.
When the {{c|co_yield}} statement is of the form {{c|co_yield ranges::elements_of(rng)}}, each element of the {{lconcept|range}} {{tt|rng}} is successively produced as an element of the sequence.
+
When the {{c/core|co_yield}} statement is of the form {{c|co_yield ranges::elements_of(rng)}}, each element of the {{lconcept|range}} {{c|rng}} is successively produced as an element of the sequence.
  
 
{{tt|std::generator}} models {{lconcept|view}} and {{lconcept|input_range}}.
 
{{tt|std::generator}} models {{lconcept|view}} and {{lconcept|input_range}}.
  
 
The behavior of a program that adds a specialization for {{tt|std::generator}} is undefined.
 
The behavior of a program that adds a specialization for {{tt|std::generator}} is undefined.
 
Internally, each active instance of {{tt|std::generator}} is associated with a stack.
 
 
* When {{rlt|begin}} is called, a new stack is created and the generator is added to the stack.
 
* When {{c|co_yield ranges::elements_of(rng)}} is evaluated in a generator body, {{c|rng}} is converted to a generator and added to the stack that contains the enclosing generator.
 
* When a generator iterator is {{rl|iterator#increment|incremented}}, the coroutine at the top of the associated stack is resumed.
 
* When a generator finishes (i.e. when {{rlt|promise_type/final_suspend|final_suspend}} is called), it is removed from the stack.
 
  
 
===Template parameters===
 
===Template parameters===
 
{{par begin}}
 
{{par begin}}
{{par|Ref|a reference type}}
+
{{par|Ref|the reference type ({{lc|ranges::range_reference_t}}) of the generator. If {{tt|V}} is {{c/core|void}}, both the reference type and the value type are inferred from {{tt|Ref}}}}
{{par|V|a value type or {{c|void}}}}
+
{{par|V|the value type ({{lc|ranges::range_value_t}}) of the generator, or {{c/core|void}}}}
{{par|Allocator|an allocator type or {{c|void}}}}
+
{{par|Allocator|an allocator type or {{c/core|void}}}}
 
{{par end}}
 
{{par end}}
  
If {{tt|Allocator}} is not {{c|void}}, then:
+
If {{tt|Allocator}} is not {{c/core|void}}, then the behavior is undefined if {{tt|Allocator}} does not meet the {{named req|Allocator}} requirements.
* {{c|std::allocator_traits<Allocator>::pointer}} is a pointer type;
+
* {{tt|Allocator}} meets the {{named req|LegacyAllocator}} requirements.
+
  
 
===Member types===
 
===Member types===
 
{{dsc begin}}
 
{{dsc begin}}
{{dsc hitem|Member type|Definition}}
+
{{dsc hitem|Member|Definition}}
{{dsc|{{tt|yielded}} {{mark since c++23}}|{{c|std::conditional_t<std::is_reference_v<T>, T, const T&>}}}}
+
{{dsc expos mem type|value|private=yes|{{c/core|std::conditional_t<std::is_void_v<V>, std::remove_cvref_t<{{void}}Ref>, V>;}}}}
{{dsc|{{c|/*value*/}} {{mark|private}}{{mark since c++23}}|{{c|std::conditional_t<std::is_void_v<V>, std::remove_cvref_t<{{void}}Ref>, V>;}}.<br>{{c|/*value*/}} is a cv-unqualified object type. The name is for exposition only.}}
+
{{dsc expos mem type|reference|private=yes|{{c/core|std::conditional_t<std::is_void_v<V>, Ref&&, Ref>;}}}}
{{dsc|{{c|/*reference*/}} {{mark|private}}{{mark since c++23}}|{{c|std::conditional_t<std::is_void_v<V>, Ref&&, Ref>;}}.<br>{{c|/*reference*/}} is either a reference type, or a cv-unqualified object type that models {{lconcept|copy_constructible}}. The name is for exposition only.}}
+
{{dsc|{{tt|yielded}}|{{box/core|{{c/core|std::conditional_t<std::is_reference_v<}}{{tti|reference}}{{sep}}{{c/core|>,}}{{nbspt|1}}{{tti|reference}}{{c/core|, const}}{{nbspt|1}}{{tti|reference}}{{sep}}{{c/core|&>}}}}}}
 
{{dsc end}}
 
{{dsc end}}
  
Let {{tt|RRef}} denote {{c|std::remove_reference_t</*reference*/>&&}} if {{c|/*reference*/}} is a reference type, and {{c|/*reference*/}} otherwise.
+
{{par begin}}
The following concepts are modeled:
+
{{par hreq}}
* {{c|std::common_reference_with</*reference*/&&, /*value*/&>}},
+
{{par req|{{c/core|std::allocator_traits<Allocator>::pointer}} is a pointer type.}}
* {{c|std::common_reference_with</*reference*/&&, RRef&&>}}, and
+
{{par req|{{tti|value}} is a cv-unqualified object type.}}
* {{c|std::common_reference_with<RRef&&, const /*value*/&>}}.
+
{{par req|{{tti|reference}} is either a reference type, or a cv-unqualified object type that models {{lconcept|copy_constructible}}.}}
 +
{{par req|Let {{tti|RRef}} denote {{box/core|{{c/core|std::remove_reference_t<}}{{tti|reference}}{{sep}}{{c/core|>&&}}}}, if {{tti|reference}} is a reference type, and {{tti|reference}} otherwise.
 +
* {{box/core|{{c/core|std::common_reference_with<}}{{tti|reference}}{{sep}}{{c/core|&&,}}{{nbspt|1}}{{tti|value}}{{sep}}{{c/core|&>}}}} is modeled.
 +
* {{box/core|{{c/core|std::common_reference_with<}}{{tti|reference}}{{sep}}{{c/core|&&,}}{{nbspt|1}}{{tti|RRef}}{{sep}}{{c/core|&&>}}}} is modeled.
 +
* {{box/core|{{c/core|std::common_reference_with<}}{{tti|RRef}}{{sep}}{{c/core|&&, const}}{{nbspt|1}}{{tti|value}}{{sep}}{{c/core|&>}}}} is modeled.
 +
}}
 +
{{par end}}
 +
 
 +
The program is ill-formed if any of these type requirements is not satisfied.
 +
 
 +
===Data members===
 +
{{dsc begin}}
 +
{{dsc hitem|Member|Definition}}
 +
{{dsc expos mem obj|active_|private=yes|
 +
Internally, each active instance of {{tt|std::generator}} is associated with a stack (handled as if by object of type {{c/core|std::unique_ptr<std::stack<std::coroutine_handle<>>>}}).
 +
* When {{rlt|begin}} is called, a new stack is created and the generator is added to the stack.
 +
* When {{c|co_yield ranges::elements_of(rng)}} is evaluated in a generator body, {{c|rng}} is converted to a generator and added to the stack that contains the enclosing generator.
 +
* When a generator iterator is {{rl|iterator#increment|incremented}}, the coroutine at the top of the associated stack is resumed.
 +
* When a generator finishes (i.e. when {{l2tt|cpp/coroutine/generator/promise_type/final_suspend}} is called), it is removed from the stack.}}
 +
{{dsc expos mem obj|coroutine_|private=yes|a handle of type {{c/core|std::coroutine_handle<promise_type>}}}}
 +
{{dsc end}}
  
 
===Member functions===
 
===Member functions===
Line 76: Line 87:
 
===Nested classes===
 
===Nested classes===
 
{{dsc begin}}
 
{{dsc begin}}
{{dsc mem class|cpp/coroutine/generator/promise|the promise type|notes={{mark c++23}}}}
+
{{dsc mem class|cpp/coroutine/generator/promise_type|the promise type}}
{{dsc expos mem class|cpp/coroutine/generator/iterator|the iterator type|notes={{mark c++23}}}}
+
{{dsc expos mem class|cpp/coroutine/generator/iterator|the iterator type}}
 
{{dsc end}}
 
{{dsc end}}
  
Line 87: Line 98:
 
|code=
 
|code=
 
#include <generator>
 
#include <generator>
#include <ranges>
 
 
#include <iostream>
 
#include <iostream>
  
std::generator<char> letters(char first)
+
template<typename T>
 +
struct Tree
 
{
 
{
     for (;; co_yield first++);
+
     T value;
}
+
    Tree *left{}, *right{};
 +
 
 +
    std::generator<const T&> traverse_inorder() const
 +
    {
 +
        if (left)
 +
            co_yield std::ranges::elements_of(left->traverse_inorder());
 +
 
 +
        co_yield value;
 +
 
 +
        if (right)
 +
            co_yield std::ranges::elements_of(right->traverse_inorder());
 +
    }
 +
};
  
 
int main()
 
int main()
 
{
 
{
     for (const char ch : letters('a') {{!}} std::views::take(032))
+
     Tree<char> tree[]
         std::cout << ch << ' ';
+
    {
 +
                                    {'D', tree + 1, tree + 2},
 +
        //                            │
 +
        //            ┌───────────────┴────────────────┐
 +
        //            │                                │
 +
                    {'B', tree + 3, tree + 4},      {'F', tree + 5, tree + 6},
 +
        //            │                                │
 +
        //  ┌─────────┴─────────────┐      ┌───────────┴─────────────┐
 +
        //  │                      │      │                        │
 +
          {'A'},                  {'C'}, {'E'},                    {'G'}
 +
    };
 +
 
 +
    for (char x : tree->traverse_inorder())
 +
         std::cout << x << ' ';
 
     std::cout << '\n';
 
     std::cout << '\n';
 
}
 
}
 
|output=
 
|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
+
A B C D E F G
 
}}
 
}}
 +
 +
===References===
 +
{{ref std c++23}}
 +
{{ref std|title=Range generators|id=coro.generator|section=26.8}}
 +
{{ref std end}}
  
 
===See also===
 
===See also===

Latest revision as of 21:38, 12 November 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

[edit] 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 (ranges::range_value_t) of the generator, or void
Allocator - an allocator type or void

If Allocator is not void, then the behavior is undefined if Allocator does not meet the Allocator requirements.

[edit] Member types

Member Definition
value (private) std::conditional_t<std::is_void_v<V>, std::remove_cvref_t<Ref>, V>;
(exposition-only member type*)
reference (private) std::conditional_t<std::is_void_v<V>, Ref&&, Ref>;
(exposition-only member type*)
yielded std::conditional_t<std::is_reference_v<reference >, reference, const reference &>
Type requirements
-
std::allocator_traits<Allocator>::pointer is a pointer type.
-
value is a cv-unqualified object type.
-
reference is either a reference type, or a cv-unqualified object type that models copy_constructible.
-
Let RRef denote std::remove_reference_t<reference >&&, if reference is a reference type, and reference otherwise.

The program is ill-formed if any of these type requirements is not satisfied.

[edit] Data members

Member Definition
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 promise_type::final_suspend is called), it is removed from the stack.
    (exposition-only member object*)
coroutine_ (private) a handle of type std::coroutine_handle<promise_type>
(exposition-only member object*)

[edit] 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]

[edit] Nested classes

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

[edit] Notes

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

[edit] Example

#include <generator>
#include <iostream>
 
template<typename T>
struct Tree
{
    T value;
    Tree *left{}, *right{};
 
    std::generator<const T&> traverse_inorder() const
    {
        if (left)
            co_yield std::ranges::elements_of(left->traverse_inorder());
 
        co_yield value;
 
        if (right)
            co_yield std::ranges::elements_of(right->traverse_inorder());
    }
};
 
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_inorder())
        std::cout << x << ' ';
    std::cout << '\n';
}

Output:

A B C D E F G

[edit] References

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

[edit] See also

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