JH-Toolkit v1.4.1
An engineering-oriented C++20 toolkit with duck-typed concepts, static design, async coroutines, and semantic containers — header-only, RTTI-free, and concurrency-friendly.
Loading...
Searching...
No Matches
jh::async::generator< T, U > Class Template Referencefinal

Coroutine-based generator supporting both yielding and receiving values. More...

#include <jh/asynchronous/generator.h>

Classes

struct  iterator
struct  promise_type

Public Types

using value_type = T
 Type alias for the value type produced by the generator.
using send_type = U

Public Member Functions

 generator (const generator &)=delete
 Deleted copy constructor.
generatoroperator= (const generator &)=delete
 Deleted copy assignment operator.
 generator (generator &&other) noexcept
 Move constructor.
generatoroperator= (generator &&other) noexcept
 Move assignment operator.
 generator (std::coroutine_handle< promise_type > h)
 Constructs a generator directly from its coroutine handle.
 ~generator ()
 Destroys the coroutine handle if it exists.
bool next ()
 Advances the generator to the next value.
bool done () const noexcept
 Checks if the generator has completed execution.
bool send (send_type value)
 Sends a value to the generator and resumes execution.
bool send_ite (send_type value)
 Advances the generator and sends a value in one step.
std::optional< value_typevalue () const noexcept
 Retrieves the currently yielded value.
std::optional< send_type > last_sent_value () const noexcept
 Retrieves the last value sent to the generator.
void stop ()
 Stops the generator and destroys the coroutine.
iterator begin ()
 Returns an iterator for ranged-for loops.
iterator begin () const =delete
 Deleted const overload of begin().

Static Public Member Functions

static iterator end ()
 Returns a sentinel iterator representing the end of the generator sequence.

Public Attributes

std::coroutine_handle< promise_typeco_ro
 Handle to the coroutine.

Detailed Description

template<typename T, typename U = typed::monostate>
requires std::is_copy_constructible_v<T>
class jh::async::generator< T, U >

Coroutine-based generator supporting both yielding and receiving values.

This class implements a coroutine-driven sequence producer, conceptually equivalent to Python's Generator[T, U, None]. It provides a clear and type-safe interface for two-way coroutine communication:

  • Yield — values are produced via co_yield.
  • Await — inputs are received via co_await, corresponding to send().

A generator is a consumable object — each call to next() or send() advances its internal coroutine state. Once advanced, previously yielded values cannot be revisited.

Values produced by co_yield are retrieved via .value(), which returns std::optional<T>. Before the first next() call or after completion, this optional contains std::nullopt.

When consuming generator outputs:

Template Parameters
TThe yielded value type (produced by co_yield). Must be copy-constructible since it is stored within std::optional<T>. Prefer trivially copyable or POD-like types for best performance:
  • POD types such as int, float, jh::pod::pair.
  • Trivial objects such as std::array, std::string.
UThe input type sent to the generator (via send() or send_ite()). Corresponds to values received by co_await inside the coroutine. Defaults to typed::monostate, making the generator a pure output sequence.
Note
Move-only types (e.g. std::unique_ptr<T>) are not supported by default because the implementation relies on std::optional<T>. To support them, implement a custom buffering or ownership model.

Member Typedef Documentation

◆ value_type

template<typename T, typename U = typed::monostate>
using jh::async::generator< T, U >::value_type = T

Type alias for the value type produced by the generator.

Type alias for the value type sent to the generator.

Constructor & Destructor Documentation

◆ generator() [1/3]

template<typename T, typename U = typed::monostate>
jh::async::generator< T, U >::generator ( const generator< T, U > & )
delete

Deleted copy constructor.

Since generator<T, U> manages a coroutine handle (std::coroutine_handle<promise_type>), copying the generator would lead to double ownership issues.

To prevent accidental copies, the copy constructor is explicitly deleted.

◆ generator() [2/3]

template<typename T, typename U = typed::monostate>
jh::async::generator< T, U >::generator ( generator< T, U > && other)
inlinenoexcept

Move constructor.

Transfers ownership of the coroutine handle from other to this.

  • The other generator is set to nullptr to prevent double destruction.
  • This ensures safe movement of generator instances.
Parameters
otherThe generator to move from.

◆ generator() [3/3]

template<typename T, typename U = typed::monostate>
jh::async::generator< T, U >::generator ( std::coroutine_handle< promise_type > h)
inlineexplicit

Constructs a generator directly from its coroutine handle.

This constructor is the linkage point between the coroutine's promise_type and its corresponding jh::async::generator<T, U> object. It is invoked automatically by the compiler when a coroutine function returning a generator is defined and called.

This enables Python-like semantics for defining and using coroutine generators:

  • jh::async::generator<T, U> Func(Args...) { scope_with_co_yield(); } — defines a coroutine generator.
  • Func(args...) — directly obtains a generator instance, without explicitly handling std::coroutine_handle.
  • jh::to_range([...] { Func(args...); }) — wraps the generator-producing function into a reusable, re-entrant range.

Thus, jh::async::generator<T, U> aligns closely with Python's Generator[T, U, None] semantics, making coroutine-based data pipelines natural and concise in C++.

Parameters
hThe coroutine handle to be managed by this generator.

Member Function Documentation

◆ begin() [1/2]

template<typename T, typename U = typed::monostate>
iterator jh::async::generator< T, U >::begin ( )
inline

Returns an iterator for ranged-for loops.

Enables use of the generator in a C++ range-based loop: for (auto x : gen). This overload is available only when U == typed::monostate, meaning the generator does not expect any input values.

Each iteration step advances the coroutine and consumes its internal state. Unlike standard ranges, a generator cannot be treated as a view or re-iterated, because iteration directly resumes and mutates the underlying coroutine frame.

A const version of begin() is intentionally deleted because invoking iteration on a constant generator would violate logical immutability: advancing the coroutine inherently modifies its promise object and execution context.

Returns
An iterator positioned at the beginning of the generator sequence.
See also
generator::end

◆ begin() [2/2]

template<typename T, typename U = typed::monostate>
iterator jh::async::generator< T, U >::begin ( ) const
delete

Deleted const overload of begin().

Generator iteration is a stateful and consuming operation. Allowing a const overload would incorrectly imply immutability, even though every iteration step mutates the coroutine's suspended frame.

This deletion enforces the invariant that jh::async::generator<T, U> may only be iterated when held as a mutable instance.

◆ done()

template<typename T, typename U = typed::monostate>
bool jh::async::generator< T, U >::done ( ) const
inlinenodiscardnoexcept

Checks if the generator has completed execution.

Returns
true if the generator has finished, false otherwise.

◆ end()

template<typename T, typename U = typed::monostate>
iterator jh::async::generator< T, U >::end ( )
inlinestatic

Returns a sentinel iterator representing the end of the generator sequence.

This function provides the canonical past-the-end sentinel for use in range-based iteration. Unlike begin(), calling end() never resumes or interacts with the underlying coroutine; it simply returns a default-constructed iterator object used to mark the termination of iteration.

Because it performs no coroutine access, end() is idempotent — it can be safely invoked multiple times without affecting the generator state.

This overload is available only when U == typed::monostate, meaning the generator is purely output-driven and does not require input through send().

Returns
A default-constructed iterator serving as the logical end sentinel.
See also
generator::begin

◆ last_sent_value()

template<typename T, typename U = typed::monostate>
std::optional< send_type > jh::async::generator< T, U >::last_sent_value ( ) const
inlinenoexcept

Retrieves the last value sent to the generator.

Returns
An optional containing the most recent input value transmitted via send() or send_ite().

This accessor returns the most recent value (U) that was sent into the coroutine through send() or send_ite(). The stored value is preserved until the next input or coroutine resumption. If no input has been sent yet, the returned std::optional is empty.

The returned value is a copy of the last sent element. This guarantees safety after coroutine resumption. If pointer semantics or shared ownership are required, use copyable reference-counted types such as std::shared_ptr instead of move-only handles.

When U == typed::monostate, the generator does not consume inputs at all. In such cases, this accessor has no semantic meaning and always yields an empty std::optional.

◆ next()

template<typename T, typename U = typed::monostate>
bool jh::async::generator< T, U >::next ( )
inline

Advances the generator to the next value.

Returns
true if a new value is available, false if the coroutine has finished.

◆ operator=() [1/2]

template<typename T, typename U = typed::monostate>
generator & jh::async::generator< T, U >::operator= ( const generator< T, U > & )
delete

Deleted copy assignment operator.

Like the copy constructor, the copy assignment operator is deleted to ensure that the coroutine handle is not duplicated, which would lead to undefined behavior.

◆ operator=() [2/2]

template<typename T, typename U = typed::monostate>
generator & jh::async::generator< T, U >::operator= ( generator< T, U > && other)
inlinenoexcept

Move assignment operator.

  1. First, it stops the current coroutine if it exists.
  2. Then, it transfers ownership of the coroutine handle from other to this.
  3. The other generator is set to nullptr to prevent double destruction.
  4. This ensures safe assignment of generator instances.
Parameters
otherThe generator to move from.
Returns
Reference to this generator after assignment.

◆ send()

template<typename T, typename U = typed::monostate>
bool jh::async::generator< T, U >::send ( send_type value)
inline

Sends a value to the generator and resumes execution.

Parameters
valueThe value to send.
Returns
true if the coroutine is still active, false otherwise.
Note
When U == typed::monostate, this function becomes a no-op. Since there is no co_await to receive input, the call does not advance the coroutine or affect its state. Use next() or send_ite() instead to progress the generator.

◆ send_ite()

template<typename T, typename U = typed::monostate>
bool jh::async::generator< T, U >::send_ite ( send_type value)
inline

Advances the generator and sends a value in one step.

This function combines next() and send(), eliminating the need for a separate next() call. It first advances the generator, and if successful, sends the provided value.

Parameters
valueThe value to send to the generator.
Returns
true if the generator successfully advances and accepts the value, false if the generator has finished.
Note
When U == typed::monostate, this behaves identically to next(), since no input is transmitted and the send stage is inert.

◆ value()

template<typename T, typename U = typed::monostate>
std::optional< value_type > jh::async::generator< T, U >::value ( ) const
inlinenoexcept

Retrieves the currently yielded value.

Returns
An optional containing the current value.
Note
The returned value is a copy of the last yielded element. This ensures that each access is memory-safe and independent of coroutine resumption, avoiding dangling references.
If your generator needs to yield pointer-like data, use a copyable smart pointer such as std::shared_ptr<T> or any equivalent reference-counted handle type.
Warning
std::unique_ptr<T> and other move-only types are not supported because the generator requires T to be copy-constructible for std::optional<T> storage.

The documentation for this class was generated from the following file: