A Generic Data Stream with Coroutines in C++20


In my last post in this mini-series to coroutines from the practical perspective, I presented the workflow of "An Infinite Data Stream with Coroutines in C++20". In this post, I use the generic potential of the data stream.


This post assumes that you know the previous post "An Infinite Data Stream with Coroutines in C++20", in which I explain very detailed the workflow of an infinite generator, based on the new keyword co_yield So far, I have written about the new keywords co_return, and co_yield, which makes out of a function a coroutine. In the next post, I will have a closer look at the most challenging new keyword co_await.




 Finally, to something new.

Generalization of the Generator

You may wonder why I never used the full generic potential of Generator in my last post. Let me adjust its implementation to produce the successive elements of an arbitrary container of the Standard Template Library.

// coroutineGetElements.cpp

#include <coroutine>
#include <memory>
#include <iostream>
#include <string>
#include <vector>

template<typename T>
struct Generator {
    struct promise_type;
    using handle_type = std::coroutine_handle<promise_type>;
    Generator(handle_type h): coro(h) {}                      

    handle_type coro;
    ~Generator() {  
        if ( coro ) coro.destroy();
    Generator(const Generator&) = delete;
    Generator& operator = (const Generator&) = delete;
    Generator(Generator&& oth): coro(oth.coro) {
        oth.coro = nullptr;
    Generator& operator = (Generator&& oth) {
        coro = oth.coro;
        oth.coro = nullptr;
        return *this;
    T getNextValue() {
        return coro.promise().current_value;
    struct promise_type {
        promise_type() {}                              
        ~promise_type() {}
        std::suspend_always initial_suspend() {            
            return {};
        std::suspend_always final_suspend() noexcept {
            return {};
        auto get_return_object() {      
            return Generator{handle_type::from_promise(*this)};
        std::suspend_always yield_value(const T value) {    
            current_value = value;
            return {};
         void return_void() {}
        void unhandled_exception() {

        T current_value;


template <typename Cont>
Generator<typename Cont::value_type> getNext(Cont cont) {
    for (auto c: cont) co_yield c;

int main() {

    std::cout << '\n';
    std::string helloWorld = "Hello world";
    auto gen = getNext(helloWorld);                        // (1)
    for (int i = 0; i < helloWorld.size(); ++i) {
        std::cout << gen.getNextValue() << " ";            // (4)

    std::cout << "\n\n";

    auto gen2 = getNext(helloWorld);                       // (2)
    for (int i = 0; i < 5 ; ++i) {                         // (5)
        std::cout << gen2.getNextValue() << " ";

    std::cout << "\n\n";

    std::vector myVec{1, 2, 3, 4 ,5};
    auto gen3 = getNext(myVec);                           // (3)
    for (int i = 0; i < myVec.size() ; ++i) {             // (6)
        std::cout << gen3.getNextValue() << " ";
    std::cout << '\n';



In this example, the generator is instantiated and used three times. In the first two cases, gen (line 1) and gen2 (line 2) are initialized with std::string helloWorld, while gen3 uses a std::vector<int> (line 3). The output of the program should not be surprising. Line 4 returns all characters of the string helloWorld successively, line 5 only the first five characters, and line 6 the elements of the std::vector<int>.

You can try out the program on the Compiler Explorer.
To make it short. The implementation of the Generator<T> is almost identical to the previous one in the post An Infinite Data Stream with Coroutines in C++20. The crucial difference with the previous program is the coroutine getNext.
template <typename Cont>
Generator<typename Cont::value_type> getNext(Cont cont) {
    for (auto c: cont) co_yield c;


getNext is a function template that takes a container as an argument and iterates in a range-based for loop through all elements of the container. After each iteration, the function template pauses. The return type Generator<typename Cont::value_type> may look surprising to you. Cont::value_type is a dependent template parameter, for which the parser needs a hint. By default, the compiler assumes a non-type if it could be interpreted as a type or a non-type. For this reason, I have to put typename in front of Cont::value_type.

The Workflows

The compiler transforms your coroutine and runs two workflows: the outer promise workflow and the inner awaiter workflow.

The Promise Workflow

So far, I have only written about the outer workflow, which is based on the member functions of the promise_type.

    Promise prom;
    co_await prom.initial_suspend();
    try {
        <function body having co_return, co_yield, or co_wait>
    catch (...) {
    co_await prom.final_suspend();


When you followed my previous post, this workflow should look familiar to you. You already know the components of this workflow such as prom.initial_suspend(), the function body, and prom.final_suspend().

The Awaiter Workflow

The outer workflow is base on the Awaitables, which return Awaiters. I intentionally simplified this explanation. You already know two predefined Awaitables:

  • std::suspend_always
struct suspend_always {
    constexpr bool await_ready() const noexcept { return false; }
    constexpr void await_suspend(std::coroutine_handle<>) const noexcept {}
    constexpr void await_resume() const noexcept {}


  • std::suspend_never
struct suspend_never {
    constexpr bool await_ready() const noexcept { return true; }
    constexpr void await_suspend(std::coroutine_handle<>) const noexcept {}
    constexpr void await_resume() const noexcept {}


No, you may already guess on which parts the awaiter workflow is based on? Right! On the member functions await_ready(), await_suspend(), and await_resume() of the Awaitable.


awaitable.await_ready() returns false:
    suspend coroutine
    awaitable.await_suspend(coroutineHandle) returns: 
            coroutine keeps suspended
            return to caller

            bool result = awaitable.await_suspend(coroutineHandle);
            if result: 
                coroutine keep suspended
                return to caller
                go to resumptionPoint

        another coroutine handle:	
            auto anotherCoroutineHandle = awaitable.await_suspend(coroutineHandle);
            return to caller

return awaitable.await_resume();


I presented the awaiter workflow in a pseudo-language. Understanding the awaiter workflow is the final puzzle piece for having an intuition about the behaviour of coroutines and how you can adapt them.

What's next?

In my next post, I dig deeper into the awaiter workflow, which is based on the Awaitable. Be prepared for the double-edged sword. User-defined Awaitables give you great power but are challenging to understand.


Thanks a lot to my Patreon Supporters: Matt Braun, Roman Postanciuc, Tobias Zindl, Marko, G Prvulovic, Reinhold Dröge, Abernitzke, Frank Grimm, Sakib, Broeserl, António Pina, Sergey Agafyin, Андрей Бурмистров, Jake, GS, Lawton Shoemake, Animus24, Jozo Leko, John Breland, espkk, Louis St-Amour, Venkat Nandam, Jose Francisco, Douglas Tinkham, Kuchlong Kuchlong, Robert Blanch, Truels Wissneth, Kris Kafka, Mario Luoni, Neil Wang, Friedrich Huber, lennonli, Pramod Tikare Muralidhara, Peter Ware, Tobi Heideman, Daniel Hufschläger, Red Trip, Alexander Schwarz, Tornike Porchxidze, Alessandro Pezzato, Evangelos Denaxas, Bob Perry, Satish Vangipuram, Andi Ireland, Richard Ohnemus, Michael Dunsky, Dimitrov Tsvetomir, Leo Goodstadt, Eduardo Velasquez, John Wiederhirn, Yacob Cohen-Arazi, Florian Tischler, Robin Furness, and Michael Young.


Thanks in particular to Jon Hess, Lakshman, Christian Wittenhorst, Sherhy Pyton, Dendi Suhubdy, Sudhakar Belagurusamy, Richard Sargeant, and Rusty Fleming.



My special thanks to Embarcadero CBUIDER STUDIO FINAL ICONS 1024 Small



I'm happy to give online seminars or face-to-face seminars worldwide. Please call me if you have any questions.

Bookable (Online)


Standard Seminars (English/German)

Here is a compilation of my standard seminars. These seminars are only meant to give you a first orientation.


Contact Me

Modernes C++,


My Newest E-Books

Course: Modern C++ Concurrency in Practice

Course: C++ Standard Library including C++14 & C++17

Course: Embedded Programming with Modern C++

Course: Generic Programming (Templates)

Course: C++ Fundamentals for Professionals

Interactive Course: The All-in-One Guide to C++20

Subscribe to the newsletter (+ pdf bundle)

Blog archive

Source Code


Today 542

Yesterday 7029

Week 40866

Month 107532

All 7375372

Currently are 134 guests and no members online

Kubik-Rubik Joomla! Extensions

Latest comments