{"id":6109,"date":"2021-03-07T11:14:20","date_gmt":"2021-03-07T11:14:20","guid":{"rendered":"https:\/\/www.modernescpp.com\/index.php\/a-generic-data-stream-with-coroutines-in-c-20\/"},"modified":"2023-09-28T06:58:05","modified_gmt":"2023-09-28T06:58:05","slug":"a-generic-data-stream-with-coroutines-in-c-20","status":"publish","type":"post","link":"https:\/\/www.modernescpp.com\/index.php\/a-generic-data-stream-with-coroutines-in-c-20\/","title":{"rendered":"A Generic Data Stream with Coroutines in C++20"},"content":{"rendered":"<p>In my last post in this mini-series on coroutines from the practical perspective, I presented the workflow of &#8220;<a href=\"https:\/\/www.modernescpp.com\/index.php\/an-infinite-data-stream-with-coroutines-in-c-20\">An Infinite Data Stream with Coroutines in C++20<\/a>&#8220;. In this post, I use the generic potential of the data stream.<\/p>\n<p><!--more--><\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"alignnone wp-image-8406 size-full\" style=\"display: block; margin-left: auto; margin-right: auto;\" src=\"https:\/\/www.modernescpp.com\/wp-content\/uploads\/2021\/02\/TimelineCpp20Coroutines.png\" alt=\"\" width=\"1068\" height=\"383\" srcset=\"https:\/\/www.modernescpp.com\/wp-content\/uploads\/2021\/02\/TimelineCpp20Coroutines.png 1068w, https:\/\/www.modernescpp.com\/wp-content\/uploads\/2021\/02\/TimelineCpp20Coroutines-300x108.png 300w, https:\/\/www.modernescpp.com\/wp-content\/uploads\/2021\/02\/TimelineCpp20Coroutines-1030x369.png 1030w, https:\/\/www.modernescpp.com\/wp-content\/uploads\/2021\/02\/TimelineCpp20Coroutines-768x275.png 768w, https:\/\/www.modernescpp.com\/wp-content\/uploads\/2021\/02\/TimelineCpp20Coroutines-705x253.png 705w\" sizes=\"auto, (max-width: 1068px) 100vw, 1068px\" \/><\/p>\n<p>This post assumes that you know the previous post, &#8220;<a href=\"https:\/\/www.modernescpp.com\/index.php\/an-infinite-data-stream-with-coroutines-in-c-20\">An Infinite Data Stream with Coroutines in C++20<\/a>&#8220;, in which I explained very detailed the workflow of an infinite generator based on the new keyword<code> co_yield<\/code> So far, I have written about the new keywords<code> co_return<\/code>, and<code> co_yield, <\/code>which makes out of a function a coroutine. In the next post, I will have a closer look at the most challenging new keyword<code> co_await<\/code>.<\/p>\n<p><code>co_return<\/code>:<\/p>\n<ul>\n<li><a href=\"https:\/\/www.modernescpp.com\/index.php\/implementing-futures-with-coroutines\">Implementing Simple Futures With Coroutines<\/a><\/li>\n<li><a href=\"https:\/\/www.modernescpp.com\/index.php\/lazy-futures-with-coroutines-in-c-20\">Lazy Futures with Coroutines in C++20<\/a><\/li>\n<li><a href=\"https:\/\/www.modernescpp.com\/index.php\/executing-a-future-in-a-separate-thread-with-coroutines\">Executing a Future in a separate Thread with Coroutines<\/a><\/li>\n<\/ul>\n<p><code>co_yield:<\/code><\/p>\n<ul>\n<li><a href=\"https:\/\/www.modernescpp.com\/index.php\/an-infinite-data-stream-with-coroutines-in-c-20\">An Infinite Data Stream with Coroutines in C++20<\/a><\/li>\n<\/ul>\n<p>Finally, to something new.<\/p>\n<h2>Generalization of the Generator<\/h2>\n<p>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.<\/p>\n<p><code><\/code><\/p>\n<p><!-- HTML generated using hilite.me --><\/p>\n<div style=\"background: #f0f3f3; overflow: auto; width: auto; gray;border-width: .1em .1em .1em .8em;\">\n<pre style=\"margin: 0px; line-height: 125%;\"><span style=\"color: #0099ff; font-style: italic;\">\/\/ coroutineGetElements.cpp<\/span>\n\n<span style=\"color: #009999;\">#include &lt;coroutine&gt;<\/span>\n<span style=\"color: #009999;\">#include &lt;memory&gt;<\/span>\n<span style=\"color: #009999;\">#include &lt;iostream&gt;<\/span>\n<span style=\"color: #009999;\">#include &lt;string&gt;<\/span>\n<span style=\"color: #009999;\">#include &lt;vector&gt;<\/span>\n\n<span style=\"color: #006699; font-weight: bold;\">template<\/span><span style=\"color: #555555;\">&lt;<\/span><span style=\"color: #006699; font-weight: bold;\">typename<\/span> T<span style=\"color: #555555;\">&gt;<\/span>\n<span style=\"color: #006699; font-weight: bold;\">struct<\/span> Generator {\n    \n    <span style=\"color: #006699; font-weight: bold;\">struct<\/span> promise_type;\n    <span style=\"color: #006699; font-weight: bold;\">using<\/span> handle_type <span style=\"color: #555555;\">=<\/span> std<span style=\"color: #555555;\">::<\/span>coroutine_handle<span style=\"color: #555555;\">&lt;<\/span>promise_type<span style=\"color: #555555;\">&gt;<\/span>;\n    \n    Generator(handle_type h)<span style=\"color: #555555;\">:<\/span> coro(h) {}                      \n\n    handle_type coro;\n    \n    <span style=\"color: #555555;\">~<\/span>Generator() {  \n        <span style=\"color: #006699; font-weight: bold;\">if<\/span> ( coro ) coro.destroy();\n    }\n    Generator(<span style=\"color: #006699; font-weight: bold;\">const<\/span> Generator<span style=\"color: #555555;\">&amp;<\/span>) <span style=\"color: #555555;\">=<\/span> <span style=\"color: #006699; font-weight: bold;\">delete<\/span>;\n    Generator<span style=\"color: #555555;\">&amp;<\/span> <span style=\"color: #006699; font-weight: bold;\">operator<\/span> <span style=\"color: #555555;\">=<\/span> (<span style=\"color: #006699; font-weight: bold;\">const<\/span> Generator<span style=\"color: #555555;\">&amp;<\/span>) <span style=\"color: #555555;\">=<\/span> <span style=\"color: #006699; font-weight: bold;\">delete<\/span>;\n    Generator(Generator<span style=\"color: #555555;\">&amp;&amp;<\/span> oth)<span style=\"color: #555555;\">:<\/span> coro(oth.coro) {\n        oth.coro <span style=\"color: #555555;\">=<\/span> nullptr;\n    }\n    Generator<span style=\"color: #555555;\">&amp;<\/span> <span style=\"color: #006699; font-weight: bold;\">operator<\/span> <span style=\"color: #555555;\">=<\/span> (Generator<span style=\"color: #555555;\">&amp;&amp;<\/span> oth) {\n        coro <span style=\"color: #555555;\">=<\/span> oth.coro;\n        oth.coro <span style=\"color: #555555;\">=<\/span> nullptr;\n        <span style=\"color: #006699; font-weight: bold;\">return<\/span> <span style=\"color: #555555;\">*<\/span><span style=\"color: #006699; font-weight: bold;\">this<\/span>;\n    }\n    T getNextValue() {\n        coro.resume();\n        <span style=\"color: #006699; font-weight: bold;\">return<\/span> coro.promise().current_value;\n    }\n    <span style=\"color: #006699; font-weight: bold;\">struct<\/span> promise_type {\n        promise_type() {}                              \n          \n        <span style=\"color: #555555;\">~<\/span>promise_type() {}\n        \n        std<span style=\"color: #555555;\">::<\/span>suspend_always initial_suspend() {            \n            <span style=\"color: #006699; font-weight: bold;\">return<\/span> {};\n        }\n        std<span style=\"color: #555555;\">::<\/span>suspend_always final_suspend() noexcept {\n            <span style=\"color: #006699; font-weight: bold;\">return<\/span> {};\n        }\n        <span style=\"color: #006699; font-weight: bold;\">auto<\/span> get_return_object() {      \n            <span style=\"color: #006699; font-weight: bold;\">return<\/span> Generator{handle_type<span style=\"color: #555555;\">::<\/span>from_promise(<span style=\"color: #555555;\">*<\/span><span style=\"color: #006699; font-weight: bold;\">this<\/span>)};\n        }\n      \n        std<span style=\"color: #555555;\">::<\/span>suspend_always yield_value(<span style=\"color: #006699; font-weight: bold;\">const<\/span> T value) {    \n            current_value <span style=\"color: #555555;\">=<\/span> value;\n            <span style=\"color: #006699; font-weight: bold;\">return<\/span> {};\n        }\n         <span style=\"color: #007788; font-weight: bold;\">void<\/span> return_void() {}\n        <span style=\"color: #007788; font-weight: bold;\">void<\/span> unhandled_exception() {\n            std<span style=\"color: #555555;\">::<\/span>exit(<span style=\"color: #ff6600;\">1<\/span>);\n        }\n\n        T current_value;\n    };\n\n};\n\n<span style=\"color: #006699; font-weight: bold;\">template<\/span> <span style=\"color: #555555;\">&lt;<\/span><span style=\"color: #006699; font-weight: bold;\">typename<\/span> Cont<span style=\"color: #555555;\">&gt;<\/span>\nGenerator<span style=\"color: #555555;\">&lt;<\/span><span style=\"color: #006699; font-weight: bold;\">typename<\/span> Cont<span style=\"color: #555555;\">::<\/span>value_type<span style=\"color: #555555;\">&gt;<\/span> getNext(Cont cont) {\n    <span style=\"color: #006699; font-weight: bold;\">for<\/span> (<span style=\"color: #006699; font-weight: bold;\">auto<\/span> c<span style=\"color: #555555;\">:<\/span> cont) co_yield c;\n}\n\n<span style=\"color: #007788; font-weight: bold;\">int<\/span> main() {\n\n    std<span style=\"color: #555555;\">::<\/span>cout <span style=\"color: #555555;\">&lt;&lt;<\/span> <span style=\"color: #cc3300;\">'\\n'<\/span>;\n  \n    std<span style=\"color: #555555;\">::<\/span>string helloWorld <span style=\"color: #555555;\">=<\/span> <span style=\"color: #cc3300;\">\"Hello world\"<\/span>;\n    <span style=\"color: #006699; font-weight: bold;\">auto<\/span> gen <span style=\"color: #555555;\">=<\/span> getNext(helloWorld);                        <span style=\"color: #0099ff;\">\/\/ (1)<\/span>\n    <span style=\"color: #006699; font-weight: bold;\">for<\/span> (<span style=\"color: #007788; font-weight: bold;\">int<\/span> i <span style=\"color: #555555;\">=<\/span> <span style=\"color: #ff6600;\">0<\/span>; i <span style=\"color: #555555;\">&lt;<\/span> helloWorld.size(); <span style=\"color: #555555;\">++<\/span>i) {\n        std<span style=\"color: #555555;\">::<\/span>cout <span style=\"color: #555555;\">&lt;&lt;<\/span> gen.getNextValue() <span style=\"color: #555555;\">&lt;&lt;<\/span> <span style=\"color: #cc3300;\">\" \"<\/span>;            <span style=\"color: #0099ff;\">\/\/ (4)<\/span>\n    }\n\n    std<span style=\"color: #555555;\">::<\/span>cout <span style=\"color: #555555;\">&lt;&lt;<\/span> <span style=\"color: #cc3300;\">\"<\/span><span style=\"color: #cc3300; font-weight: bold;\">\\n\\n<\/span><span style=\"color: #cc3300;\">\"<\/span>;\n\n    <span style=\"color: #006699; font-weight: bold;\">auto<\/span> gen2 <span style=\"color: #555555;\">=<\/span> getNext(helloWorld);                       <span style=\"color: #0099ff;\">\/\/ (2)<\/span>\n    <span style=\"color: #006699; font-weight: bold;\">for<\/span> (<span style=\"color: #007788; font-weight: bold;\">int<\/span> i <span style=\"color: #555555;\">=<\/span> <span style=\"color: #ff6600;\">0<\/span>; i <span style=\"color: #555555;\">&lt;<\/span> <span style=\"color: #ff6600;\">5<\/span> ; <span style=\"color: #555555;\">++<\/span>i) {                         <span style=\"color: #0099ff;\">\/\/ (5)<\/span>\n        std<span style=\"color: #555555;\">::<\/span>cout <span style=\"color: #555555;\">&lt;&lt;<\/span> gen2.getNextValue() <span style=\"color: #555555;\">&lt;&lt;<\/span> <span style=\"color: #cc3300;\">\" \"<\/span>;\n    }\n\n    std<span style=\"color: #555555;\">::<\/span>cout <span style=\"color: #555555;\">&lt;&lt;<\/span> <span style=\"color: #cc3300;\">\"<\/span><span style=\"color: #cc3300; font-weight: bold;\">\\n\\n<\/span><span style=\"color: #cc3300;\">\"<\/span>;\n\n    std<span style=\"color: #555555;\">::<\/span>vector myVec{<span style=\"color: #ff6600;\">1<\/span>, <span style=\"color: #ff6600;\">2<\/span>, <span style=\"color: #ff6600;\">3<\/span>, <span style=\"color: #ff6600;\">4<\/span> ,<span style=\"color: #ff6600;\">5<\/span>};\n    <span style=\"color: #006699; font-weight: bold;\">auto<\/span> gen3 <span style=\"color: #555555;\">=<\/span> getNext(myVec);                           <span style=\"color: #0099ff;\">\/\/ (3)<\/span>\n    <span style=\"color: #006699; font-weight: bold;\">for<\/span> (<span style=\"color: #007788; font-weight: bold;\">int<\/span> i <span style=\"color: #555555;\">=<\/span> <span style=\"color: #ff6600;\">0<\/span>; i <span style=\"color: #555555;\">&lt;<\/span> myVec.size() ; <span style=\"color: #555555;\">++<\/span>i) {             <span style=\"color: #0099ff;\">\/\/ (6)<\/span>\n        std<span style=\"color: #555555;\">::<\/span>cout <span style=\"color: #555555;\">&lt;&lt;<\/span> gen3.getNextValue() <span style=\"color: #555555;\">&lt;&lt;<\/span> <span style=\"color: #cc3300;\">\" \"<\/span>;\n    }\n    \n    std<span style=\"color: #555555;\">::<\/span>cout <span style=\"color: #555555;\">&lt;&lt;<\/span> <span style=\"color: #cc3300;\">'\\n'<\/span>;\n\n}\n<\/pre>\n<\/div>\n<p>In this example, the generator is instantiated and used three times. In the first two cases,<code> gen<\/code> (line 1) and <code>gen2<\/code> (line 2) are initialized with <code>std::string helloWorld<\/code>, while <code>gen3<\/code> uses a <code>std::vector&lt;int&gt;<\/code> (line 3). The output of the program should not be surprising. Line 4 returns all characters of the string <code>helloWorld<\/code> successively, line 5 only the first five characters, and line 6 the elements of the<code> std::vector&lt;int&gt;<\/code>.<\/p>\n<div>\n<div>You\u00a0can\u00a0try\u00a0out\u00a0the\u00a0program\u00a0on <a href=\"https:\/\/godbolt.org\/z\/j9znva\">Compiler Explorer<\/a>.<\/div>\n<div><\/div>\n<div><img loading=\"lazy\" decoding=\"async\" class=\" size-full wp-image-6108\" style=\"display: block; margin-left: auto; margin-right: auto;\" src=\"https:\/\/www.modernescpp.com\/wp-content\/uploads\/2021\/03\/coroutineGetElements.png\" alt=\"coroutineGetElements\" width=\"250\" height=\"131\" srcset=\"https:\/\/www.modernescpp.com\/wp-content\/uploads\/2021\/03\/coroutineGetElements.png 304w, https:\/\/www.modernescpp.com\/wp-content\/uploads\/2021\/03\/coroutineGetElements-300x157.png 300w\" sizes=\"auto, (max-width: 250px) 100vw, 250px\" \/><\/div>\n<div><\/div>\n<div>To make it short. The implementation of\u00a0<code> Generator&lt;T&gt;<\/code> is almost identical to the previous one in the post\u00a0<a href=\"https:\/\/www.modernescpp.com\/index.php\/an-infinite-data-stream-with-coroutines-in-c-20\">An Infinite Data Stream with Coroutines in C++20<\/a>. The crucial difference with the previous program is the coroutine <code>getNext<\/code>.<\/div>\n<div><\/div>\n<div><\/div>\n<\/div>\n<p><!-- HTML generated using hilite.me --><\/p>\n<div style=\"background: #f0f3f3; overflow: auto; width: auto; gray;border-width: .1em .1em .1em .8em;\">\n<pre style=\"margin: 0; line-height: 125%;\"><span style=\"color: #006699; font-weight: bold;\">template<\/span> <span style=\"color: #555555;\">&lt;<\/span><span style=\"color: #006699; font-weight: bold;\">typename<\/span> Cont<span style=\"color: #555555;\">&gt;<\/span>\nGenerator<span style=\"color: #555555;\">&lt;<\/span><span style=\"color: #006699; font-weight: bold;\">typename<\/span> Cont<span style=\"color: #555555;\">::<\/span>value_type<span style=\"color: #555555;\">&gt;<\/span> getNext(Cont cont) {\n    <span style=\"color: #006699; font-weight: bold;\">for<\/span> (<span style=\"color: #006699; font-weight: bold;\">auto<\/span> c<span style=\"color: #555555;\">:<\/span> cont) co_yield c;\n}\n<\/pre>\n<\/div>\n<p><code>getNext<\/code> is a function template that takes a container as an argument and iterates in a range-based for loop through all container elements. After each iteration, the function template pauses. The return type <code>Generator&lt;typename Cont::value_type&gt;<\/code> may look surprising to you. <code>Cont::value_type<\/code> 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 <code>typename<\/code> in front of <code>Cont::value_type.<\/code><\/p>\n<h2>The Workflows<\/h2>\n<p>The compiler transforms your coroutine and runs the outer <strong>promise workflow<\/strong> and the inner <strong>awaiter workflow<\/strong>.<\/p>\n<h3>The Promise Workflow<\/h3>\n<p>So far, I have only written about the outer workflow, which is based on the member functions of the<code> promise_type<\/code>.<\/p>\n<p><!-- HTML generated using hilite.me --><\/p>\n<div style=\"background: #f0f3f3; overflow: auto; width: auto; gray;border-width: .1em .1em .1em .8em;\">\n<pre style=\"margin: 0; line-height: 125%;\">{\n    Promise prom;\n    co_await prom.initial_suspend();\n    try {\n        <span style=\"color: #555555;\">&lt;<\/span>function body having co_return, co_yield, or co_wait<span style=\"color: #555555;\">&gt;<\/span>\n    }\n    <span style=\"color: #006699; font-weight: bold;\">catch<\/span> (...) {\n        prom.unhandled_exception();\n    }\n<span style=\"color: #9999ff;\">FinalSuspend:<\/span>\n    co_await prom.final_suspend();\n}\n<\/pre>\n<\/div>\n<p>Following my previous post, this workflow should look familiar to you. You already know the components of this workflow, such as <code>prom.initial_suspend()<\/code>, the function body, and <code>prom.final_suspend().<\/code><\/p>\n<h3>The Awaiter Workflow<\/h3>\n<p>The outer workflow is based on the Awaitables, which return Awaiters. I intentionally simplified this explanation. You already know two predefined Awaitables:<\/p>\n<ul>\n<li><code>std::suspend_always<\/code><\/li>\n<\/ul>\n<p><!-- HTML generated using hilite.me --><\/p>\n<div style=\"background: #f0f3f3; overflow: auto; width: auto; gray;border-width: .1em .1em .1em .8em;\">\n<pre style=\"margin: 0; line-height: 125%;\"><span style=\"color: #006699; font-weight: bold;\">struct<\/span> suspend_always {\n    constexpr <span style=\"color: #007788; font-weight: bold;\">bool<\/span> await_ready() <span style=\"color: #006699; font-weight: bold;\">const<\/span> noexcept { <span style=\"color: #006699; font-weight: bold;\">return<\/span> <span style=\"color: #336666;\">false<\/span>; }\n    constexpr <span style=\"color: #007788; font-weight: bold;\">void<\/span> await_suspend(std<span style=\"color: #555555;\">::<\/span>coroutine_handle<span style=\"color: #555555;\">&lt;&gt;<\/span>) <span style=\"color: #006699; font-weight: bold;\">const<\/span> noexcept {}\n    constexpr <span style=\"color: #007788; font-weight: bold;\">void<\/span> await_resume() <span style=\"color: #006699; font-weight: bold;\">const<\/span> noexcept {}\n};\n<\/pre>\n<\/div>\n<ul>\n<li><code>std::suspend_never<\/code><\/li>\n<\/ul>\n<p><!-- HTML generated using hilite.me --><\/p>\n<div style=\"background: #f0f3f3; overflow: auto; width: auto; gray;border-width: .1em .1em .1em .8em;\">\n<pre style=\"margin: 0; line-height: 125%;\"><span style=\"color: #006699; font-weight: bold;\">struct<\/span> suspend_never {\n    constexpr <span style=\"color: #007788; font-weight: bold;\">bool<\/span> await_ready() <span style=\"color: #006699; font-weight: bold;\">const<\/span> noexcept { <span style=\"color: #006699; font-weight: bold;\">return<\/span> <span style=\"color: #336666;\">true<\/span>; }\n    constexpr <span style=\"color: #007788; font-weight: bold;\">void<\/span> await_suspend(std<span style=\"color: #555555;\">::<\/span>coroutine_handle<span style=\"color: #555555;\">&lt;&gt;<\/span>) <span style=\"color: #006699; font-weight: bold;\">const<\/span> noexcept {}\n    constexpr <span style=\"color: #007788; font-weight: bold;\">void<\/span> await_resume() <span style=\"color: #006699; font-weight: bold;\">const<\/span> noexcept {}\n};\n<\/pre>\n<\/div>\n<p>No, you may already guess which parts the awaiter workflow is based on. Right! On the member functions <code>await_ready()<\/code>, <code>await_suspend()<\/code>, and <code>await_resume()<\/code> of the Awaitable.<\/p>\n<p><!-- HTML generated using hilite.me --><\/p>\n<div style=\"background: #f0f3f3; overflow: auto; width: auto; gray;border-width: .1em .1em .1em .8em;\">\n<pre style=\"margin: 0; line-height: 125%;\">awaitable.await_ready() returns <span style=\"color: #336666;\">false<\/span><span style=\"color: #555555;\">:<\/span>\n    \n    suspend coroutine\n\t\n    awaitable.await_suspend(coroutineHandle) returns<span style=\"color: #555555;\">:<\/span> \n\t\n        <span style=\"color: #007788; font-weight: bold;\">void<\/span><span style=\"color: #555555;\">:<\/span>\n            awaitable.await_suspend(coroutineHandle);\n            coroutine keeps suspended\n            <span style=\"color: #006699; font-weight: bold;\">return<\/span> to caller\n\n        <span style=\"color: #9999ff;\">bool:<\/span>\n            <span style=\"color: #007788; font-weight: bold;\">bool<\/span> result <span style=\"color: #555555;\">=<\/span> awaitable.await_suspend(coroutineHandle);\n            <span style=\"color: #006699; font-weight: bold;\">if<\/span> result<span style=\"color: #555555;\">:<\/span> \n                coroutine keep suspended\n                <span style=\"color: #006699; font-weight: bold;\">return<\/span> to caller\n            <span style=\"color: #9999ff;\">else:<\/span> \n                go to resumptionPoint\n\n        another coroutine handle<span style=\"color: #555555;\">:<\/span>\t\n            <span style=\"color: #006699; font-weight: bold;\">auto<\/span> anotherCoroutineHandle <span style=\"color: #555555;\">=<\/span> awaitable.await_suspend(coroutineHandle);\n            anotherCoroutineHandle.resume();\n            <span style=\"color: #006699; font-weight: bold;\">return<\/span> to caller\n\t\n<span style=\"color: #9999ff;\">resumptionPoint:<\/span>\n\n<span style=\"color: #006699; font-weight: bold;\">return<\/span> awaitable.await_resume();\n<\/pre>\n<\/div>\n<p>I presented the awaiter workflow in a pseudo-language. Understanding the awaiter workflow is the final puzzle for having an intuition about the behavior of coroutines and how you can adapt them.<\/p>\n<h2>What&#8217;s next?<\/h2>\n<p>In my<a href=\"https:\/\/www.modernescpp.com\/index.php\/starting-jobs-with-coroutines\"> next post,<\/a> I dig deeper into the awaiter workflow based on the Awaitable. Be prepared for the double-edged sword. User-defined Awaitables give you great power but are challenging to understand.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>In my last post in this mini-series on coroutines from the practical perspective, I presented the workflow of &#8220;An Infinite Data Stream with Coroutines in C++20&#8220;. In this post, I use the generic potential of the data stream.<\/p>\n","protected":false},"author":21,"featured_media":8406,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[375],"tags":[445],"class_list":["post-6109","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-c-20","tag-coroutines"],"_links":{"self":[{"href":"https:\/\/www.modernescpp.com\/index.php\/wp-json\/wp\/v2\/posts\/6109","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.modernescpp.com\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.modernescpp.com\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.modernescpp.com\/index.php\/wp-json\/wp\/v2\/users\/21"}],"replies":[{"embeddable":true,"href":"https:\/\/www.modernescpp.com\/index.php\/wp-json\/wp\/v2\/comments?post=6109"}],"version-history":[{"count":2,"href":"https:\/\/www.modernescpp.com\/index.php\/wp-json\/wp\/v2\/posts\/6109\/revisions"}],"predecessor-version":[{"id":8413,"href":"https:\/\/www.modernescpp.com\/index.php\/wp-json\/wp\/v2\/posts\/6109\/revisions\/8413"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.modernescpp.com\/index.php\/wp-json\/wp\/v2\/media\/8406"}],"wp:attachment":[{"href":"https:\/\/www.modernescpp.com\/index.php\/wp-json\/wp\/v2\/media?parent=6109"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.modernescpp.com\/index.php\/wp-json\/wp\/v2\/categories?post=6109"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.modernescpp.com\/index.php\/wp-json\/wp\/v2\/tags?post=6109"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}