{"id":5872,"date":"2020-04-03T06:31:15","date_gmt":"2020-04-03T06:31:15","guid":{"rendered":"https:\/\/www.modernescpp.com\/index.php\/c-20-an-infinite-data-stream-with-coroutines\/"},"modified":"2023-09-28T07:44:45","modified_gmt":"2023-09-28T07:44:45","slug":"c-20-an-infinite-data-stream-with-coroutines","status":"publish","type":"post","link":"https:\/\/www.modernescpp.com\/index.php\/c-20-an-infinite-data-stream-with-coroutines\/","title":{"rendered":"C++20: An Infinite Data Stream with Coroutines"},"content":{"rendered":"<p>My story to coroutines in C++20 goes on. Today I dive deep into the coroutines framework to create an infinite data stream. You have to read the two previous posts, &#8220;<a href=\"https:\/\/bit.ly\/3dl4VGE\">C++20: Coroutines &#8211; A First Overview<\/a>&#8220;, and &#8220;<a href=\"https:\/\/bit.ly\/CoroutinesMoreDetails\">C++20: More Details to Coroutines<\/a>&#8221; to be prepared.<\/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>The framework for writing coroutines consists of more than 20 functions that you partially have\u00a0to implement and partially could overwrite. Therefore, you can tailor the coroutine to your needs. In the end, you can, for example, create a generator <span style=\"font-family: 'courier new', courier;\">Generator&lt;int&gt;<\/span> for an infinite data stream such as the following one:<\/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%;\">Generator<span style=\"color: #555555;\">&lt;<\/span><span style=\"color: #007788; font-weight: bold;\">int<\/span><span style=\"color: #555555;\">&gt;<\/span> getNext(<span style=\"color: #007788; font-weight: bold;\">int<\/span> start <span style=\"color: #555555;\">=<\/span> <span style=\"color: #ff6600;\">0<\/span>, <span style=\"color: #007788; font-weight: bold;\">int<\/span> step <span style=\"color: #555555;\">=<\/span> <span style=\"color: #ff6600;\">1<\/span>) {\n    <span style=\"color: #006699; font-weight: bold;\">auto<\/span> value <span style=\"color: #555555;\">=<\/span> start;\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>;; <span style=\"color: #555555;\">++<\/span>i) {\n        co_yield value;\n        value <span style=\"color: #555555;\">+=<\/span> step;\n    }\n}\n<\/pre>\n<\/div>\n<p>Now, we know the destiny of our job. Let&#8217;s start.<\/p>\n<h2>The Framework<\/h2>\n<p>A coroutine comprises three parts: the promise object, the coroutine handle, and the frame.<\/p>\n<ul>\n<li><strong>Promise object<\/strong>: The promise object is manipulated from inside the coroutine and delivers its\u00a0result via the promise object.<\/li>\n<li><strong>Coroutine handle<\/strong>: The coroutine handle is a non-owning handle to resume or destroy the\u00a0coroutine frame from the outside.<\/li>\n<li><strong>Coroutine frame<\/strong>: The coroutine frame is an internal, typically heap-allocated state. It consists of\u00a0the already mentioned promise object, the copied parameters of the coroutine, the representation of\u00a0the suspension points, local variables whose lifetime ends before the current suspension point, and\u00a0local variables whose lifetime exceeds the current suspension point.<\/li>\n<\/ul>\n<h3>The Simplified Workflow<\/h3>\n<p>When you use <span style=\"font-family: 'courier new', courier;\">co_yield<\/span>, <span style=\"font-family: 'courier new', courier;\">co_await<\/span>, or <span style=\"font-family: 'courier new', courier;\">co_return<\/span> in a function, the function becomes a coroutine,\u00a0and the compiler transforms its body to something equivalent to the following lines.<\/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 promise;\n  co_await promise.initial_suspend();\n  try\n  {\n    <span style=\"color: #555555;\">&lt;<\/span>function body<span style=\"color: #555555;\">&gt;<\/span>\n  }\n  <span style=\"color: #006699; font-weight: bold;\">catch<\/span> (...)\n  {\n    promise.unhandled_exception();\n  }\n  <span style=\"color: #9999ff;\">FinalSuspend:<\/span>\n    co_await promise.final_suspend();\n}\n<\/pre>\n<\/div>\n<p><span style=\"font-family: 'courier new', courier;\">&lt;function body&gt;<\/span> stands for the original function body. The simplified workflow of the coroutine consists of the following phases.<\/p>\n<p>The coroutine begins the execution<\/p>\n<ul>\n<li>Allocates the coroutine frame<\/li>\n<li>Copies all function parameters to the coroutine frame<\/li>\n<li>Creates the promise object <span style=\"font-family: 'courier new', courier;\">promise<\/span><\/li>\n<li>Calls <span style=\"font-family: 'courier new', courier;\">promise.get_return_object()<\/span>\u00a0to create the coroutine handle and keep it in a local variable. The result of the call will be returned to the caller when the coroutine first suspends.<\/li>\n<li>Calls <span style=\"font-family: 'courier new', courier;\">promise.initial_suspend()<\/span>, and <span style=\"font-family: 'courier new', courier;\">co_await<\/span>&#8216;s its result. The promise type typically returns <span style=\"font-family: 'courier new', courier;\">std::suspend_never<\/span>\u00a0for eagerly-started coroutines or <span style=\"font-family: 'courier new', courier;\">std::suspend_always<\/span>\u00a0for lazily-started coroutines.<\/li>\n<li>The body of the coroutine is executed when <span style=\"font-family: 'courier new', courier;\">co_await promise.initial_suspend()<\/span>\u00a0resumes<\/li>\n<\/ul>\n<p>The coroutine reaches a suspension point<\/p>\n<ul>\n<li>The coroutine handle (<span style=\"font-family: 'courier new', courier;\">promise.get_return_object()<\/span>) is returned to the caller, which resumes the coroutine<\/li>\n<\/ul>\n<p>The coroutine reaches <span style=\"font-family: 'courier new', courier;\">co_return<\/span><\/p>\n<ul>\n<li>Calls <span style=\"font-family: 'courier new', courier;\">promise.return_void()<\/span>\u00a0for <span style=\"font-family: 'courier new', courier;\">co_return<\/span>\u00a0or <span style=\"font-family: 'courier new', courier;\">co_return expression<\/span>, where <span style=\"font-family: 'courier new', courier;\">expression<\/span>\u00a0has type <span style=\"font-family: 'courier new', courier;\">void<\/span><\/li>\n<li>Calls <span style=\"font-family: 'courier new', courier;\">promise.return_value(expression)<\/span>\u00a0for <span style=\"font-family: 'courier new', courier;\">co_return expression<\/span>, where <span style=\"font-family: 'courier new', courier;\">expression<\/span>\u00a0has non-type <span style=\"font-family: 'courier new', courier;\">void<\/span><\/li>\n<li>Destroys all stack-created variables<\/li>\n<li>Calls <span style=\"font-family: 'courier new', courier;\">promise.final_suspend()<\/span>\u00a0and <span style=\"font-family: 'courier new', courier;\">co_await<\/span>&#8216;s its result<\/li>\n<\/ul>\n<p>The coroutine is destroyed (by terminating via <span style=\"font-family: 'courier new', courier;\">co_retur<\/span>n, an uncaught exception, or the coroutine handle)<\/p>\n<ul>\n<li>Calls the destruction of the promise object<\/li>\n<li>Calls the destructor of the function parameters<\/li>\n<li>Frees the memory used by the coroutine frame<\/li>\n<li>Transfers control back to the caller<\/li>\n<\/ul>\n<p>Let&#8217;s put the theory into praxis.<\/p>\n<h3>An Infinite Data Stream with <span style=\"font-family: 'courier new', courier;\">co_yield<\/span><\/h3>\n<p>The following program produces an infinite data stream. The coroutine <span style=\"font-family: 'courier new', courier;\">getNext<\/span> uses <span style=\"font-family: 'courier new', courier;\">co_yield<\/span> to\u00a0create a data stream that starts at <span style=\"font-family: 'courier new', courier;\">start<\/span> and gives on request the next value, incremented by <span style=\"font-family: 'courier new', courier;\">step<\/span>.<\/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%;\"><span style=\"color: #0099ff; font-style: italic;\">\/\/ infiniteDataStream.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\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) {}                         <span style=\"color: #0099ff; font-style: italic;\">\/\/ (3)<\/span>\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) noexcept <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) noexcept {\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 getValue() {\n        <span style=\"color: #006699; font-weight: bold;\">return<\/span> coro.promise().current_value;\n    }\n    <span style=\"color: #007788; font-weight: bold;\">bool<\/span> next() {                                                <span style=\"color: #0099ff; font-style: italic;\">\/\/ (5)<\/span>\n        coro.resume();\n        <span style=\"color: #006699; font-weight: bold;\">return<\/span> not coro.done();\n    }\n    <span style=\"color: #006699; font-weight: bold;\">struct<\/span> promise_type {\n        promise_type()  <span style=\"color: #555555;\">=<\/span> <span style=\"color: #006699; font-weight: bold;\">default<\/span>;                               <span style=\"color: #0099ff; font-style: italic;\">\/\/ (1)<\/span>\n          \n        <span style=\"color: #555555;\">~<\/span>promise_type() <span style=\"color: #555555;\">=<\/span> <span style=\"color: #006699; font-weight: bold;\">default<\/span>;\n        \n        <span style=\"color: #006699; font-weight: bold;\">auto<\/span> <span style=\"color: #cc00ff;\">initial_suspend<\/span>() {                                 <span style=\"color: #0099ff; font-style: italic;\">\/\/ (4)<\/span>\n            <span style=\"color: #006699; font-weight: bold;\">return<\/span> std<span style=\"color: #555555;\">::<\/span>suspend_always{};\n        }\n        <span style=\"color: #006699; font-weight: bold;\">auto<\/span> <span style=\"color: #cc00ff;\">final_suspend<\/span>() {\n            <span style=\"color: #006699; font-weight: bold;\">return<\/span> std<span style=\"color: #555555;\">::<\/span>suspend_always{};\n        }\n        <span style=\"color: #006699; font-weight: bold;\">auto<\/span> <span style=\"color: #cc00ff;\">get_return_object<\/span>() {                               <span style=\"color: #0099ff; font-style: italic;\">\/\/ (2)<\/span>\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        <span style=\"color: #006699; font-weight: bold;\">auto<\/span> <span style=\"color: #cc00ff;\">return_void<\/span>() {\n            <span style=\"color: #006699; font-weight: bold;\">return<\/span> std<span style=\"color: #555555;\">::<\/span>suspend_never{};\n        }\n      \n        <span style=\"color: #006699; font-weight: bold;\">auto<\/span> <span style=\"color: #cc00ff;\">yield_value<\/span>(<span style=\"color: #006699; font-weight: bold;\">const<\/span> T value) {                        <span style=\"color: #0099ff; font-style: italic;\">\/\/ (6) <\/span>\n            current_value <span style=\"color: #555555;\">=<\/span> value;\n            <span style=\"color: #006699; font-weight: bold;\">return<\/span> std<span style=\"color: #555555;\">::<\/span>suspend_always{};\n        }\n        <span style=\"color: #007788; font-weight: bold;\">void<\/span> <span style=\"color: #cc00ff;\">unhandled_exception<\/span>() {\n            std<span style=\"color: #555555;\">::<\/span>exit(<span style=\"color: #ff6600;\">1<\/span>);\n        }\n        T current_value;\n    };\n\n};\n\nGenerator<span style=\"color: #555555;\">&lt;<\/span><span style=\"color: #007788; font-weight: bold;\">int<\/span><span style=\"color: #555555;\">&gt;<\/span> getNext(<span style=\"color: #007788; font-weight: bold;\">int<\/span> start <span style=\"color: #555555;\">=<\/span> <span style=\"color: #ff6600;\">0<\/span>, <span style=\"color: #007788; font-weight: bold;\">int<\/span> step <span style=\"color: #555555;\">=<\/span> <span style=\"color: #ff6600;\">1<\/span>) noexcept {\n    <span style=\"color: #006699; font-weight: bold;\">auto<\/span> value <span style=\"color: #555555;\">=<\/span> start;\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>;; <span style=\"color: #555555;\">++<\/span>i){\n        co_yield value;\n        value <span style=\"color: #555555;\">+=<\/span> step;\n    }\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> std<span style=\"color: #555555;\">::<\/span>endl;\n  \n    std<span style=\"color: #555555;\">::<\/span>cout <span style=\"color: #555555;\">&lt;&lt;<\/span> <span style=\"color: #cc3300;\">\"getNext():\"<\/span>;\n    <span style=\"color: #006699; font-weight: bold;\">auto<\/span> gen <span style=\"color: #555555;\">=<\/span> getNext();\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;\">10<\/span>; <span style=\"color: #555555;\">++<\/span>i) {\n        gen.next();\n        std<span style=\"color: #555555;\">::<\/span>cout <span style=\"color: #555555;\">&lt;&lt;<\/span> <span style=\"color: #cc3300;\">\" \"<\/span> <span style=\"color: #555555;\">&lt;&lt;<\/span> gen.getValue();                      <span style=\"color: #0099ff; font-style: italic;\">\/\/ (7)<\/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>cout <span style=\"color: #555555;\">&lt;&lt;<\/span> <span style=\"color: #cc3300;\">\"getNext(100, -10):\"<\/span>;\n    <span style=\"color: #006699; font-weight: bold;\">auto<\/span> gen2 <span style=\"color: #555555;\">=<\/span> getNext(<span style=\"color: #ff6600;\">100<\/span>, <span style=\"color: #555555;\">-<\/span><span style=\"color: #ff6600;\">10<\/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;\">20<\/span>; <span style=\"color: #555555;\">++<\/span>i) {\n        gen2.next();\n        std<span style=\"color: #555555;\">::<\/span>cout <span style=\"color: #555555;\">&lt;&lt;<\/span> <span style=\"color: #cc3300;\">\" \"<\/span> <span style=\"color: #555555;\">&lt;&lt;<\/span> gen2.getValue();\n    }\n    \n    std<span style=\"color: #555555;\">::<\/span>cout <span style=\"color: #555555;\">&lt;&lt;<\/span> std<span style=\"color: #555555;\">::<\/span>endl;\n    \n}\n<\/pre>\n<\/div>\n<p>The main function creates two coroutines. The first <code>gen<\/code> returns the values from 0 to 10, and the second <code>gen2<\/code> from 100 to -100. Before I dive into the workflow,\u00a0thanks to <a href=\"https:\/\/godbolt.org\/z\/jTS9BR\">Compiler Explorer<\/a> and GCC 10, here is the program&#8217;s output.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\" size-full wp-image-5871\" style=\"display: block; margin-left: auto; margin-right: auto;\" src=\"https:\/\/www.modernescpp.com\/wp-content\/uploads\/2020\/04\/infiniteDataStream.png\" alt=\"infiniteDataStream\" width=\"650\" height=\"82\" srcset=\"https:\/\/www.modernescpp.com\/wp-content\/uploads\/2020\/04\/infiniteDataStream.png 968w, https:\/\/www.modernescpp.com\/wp-content\/uploads\/2020\/04\/infiniteDataStream-300x38.png 300w, https:\/\/www.modernescpp.com\/wp-content\/uploads\/2020\/04\/infiniteDataStream-768x97.png 768w\" sizes=\"auto, (max-width: 650px) 100vw, 650px\" \/><\/p>\n<p>The numbers in the program<span style=\"font-family: 'courier new', courier;\"> infiniteDataStream.cpp<\/span> stand for the steps in the first iteration of the workflow.<\/p>\n<ol>\n<li>Creates the promise<\/li>\n<li>Calls <span style=\"font-family: 'courier new', courier;\">promise.get_return_object()<\/span>\u00a0and keep the result in a local variable<\/li>\n<li>Creates the generator<\/li>\n<li>Calls <span style=\"font-family: 'courier new', courier;\">promise.initial_suspend()<\/span>. The generator is lazy and, therefore, suspends always.<\/li>\n<li>Asks for the next value and returns if the generator is consumed<\/li>\n<li>Triggered by the <span style=\"font-family: 'courier new', courier;\">co_yield<\/span>\u00a0call. The next value is afterward available.<\/li>\n<li>Gets the next value<\/li>\n<\/ol>\n<p>In additional iterations, only steps 5 to 7 are performed.<\/p>\n<p>It is pretty challenging to understand the underlying framework of coroutines. Playing with existing coroutines and observing the changed behavior may be the easiest way to grasp them. The presented coroutine that creates an infinite data stream is a good starting point for your first experiments: use the link to the executable program on\u00a0<a href=\"https:\/\/godbolt.org\/z\/jTS9BR\">Compiler Explorer<\/a>.<\/p>\n<h2>What&#8217;s next?<\/h2>\n<p>I used <code>co_yield<\/code> to create an infinite data stream in today&#8217;s post. My <a href=\"https:\/\/www.modernescpp.com\/index.php\/c-20-thread-synchronization-with-coroutines\">next post <\/a>is about thread synchronization with <span style=\"font-family: 'courier new', courier;\">co_await<\/span>.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>My story to coroutines in C++20 goes on. Today I dive deep into the coroutines framework to create an infinite data stream. You have to read the two previous posts, &#8220;C++20: Coroutines &#8211; A First Overview&#8220;, and &#8220;C++20: More Details to Coroutines&#8221; to be prepared.<\/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-5872","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\/5872","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=5872"}],"version-history":[{"count":2,"href":"https:\/\/www.modernescpp.com\/index.php\/wp-json\/wp\/v2\/posts\/5872\/revisions"}],"predecessor-version":[{"id":8424,"href":"https:\/\/www.modernescpp.com\/index.php\/wp-json\/wp\/v2\/posts\/5872\/revisions\/8424"}],"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=5872"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.modernescpp.com\/index.php\/wp-json\/wp\/v2\/categories?post=5872"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.modernescpp.com\/index.php\/wp-json\/wp\/v2\/tags?post=5872"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}