{"id":6102,"date":"2021-03-06T10:39:31","date_gmt":"2021-03-06T10:39:31","guid":{"rendered":"https:\/\/www.modernescpp.com\/index.php\/executing-a-future-in-a-separate-thread-with-coroutines\/"},"modified":"2023-09-28T06:57:07","modified_gmt":"2023-09-28T06:57:07","slug":"executing-a-future-in-a-separate-thread-with-coroutines","status":"publish","type":"post","link":"https:\/\/www.modernescpp.com\/index.php\/executing-a-future-in-a-separate-thread-with-coroutines\/","title":{"rendered":"Executing a Future in a Separate Thread with Coroutines"},"content":{"rendered":"<p>This post concludes my posts about co_return in C++20. I started with an eager future, and continued with a lazy future. Today, I execute the future in a separate thread using coroutines as an implementation detail.<\/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>Before I continue, I want to emphasize this. The reason for this mini-series about coroutines in C++20 is simple: I want to help you to build an intuition about the complicated workflow of coroutines. This is what has happened so far in this mini-series. Each post is based on the previous ones.<\/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<\/a><\/li>\n<\/ul>\n<p>Now,\u00a0 I want to execute the coroutine on a separate thread.<\/p>\n<h2>Execution on Another Thread<\/h2>\n<p>The coroutine in the previous example, &#8220;<a href=\"https:\/\/www.modernescpp.com\/index.php\/lazy-futures-with-coroutines-in-c-20\">Lazy Futures with Coroutines in C++20<\/a>&#8221;\u00a0 was fully suspended before entering the coroutine body of <code>createFuture<\/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%;\">MyFuture<span style=\"color: #555555;\">&lt;<\/span><span style=\"color: #007788; font-weight: bold;\">int<\/span><span style=\"color: #555555;\">&gt;<\/span> createFuture() {\n    std<span style=\"color: #555555;\">::<\/span>cout <span style=\"color: #555555;\">&lt;&lt;<\/span> <span style=\"color: #cc3300;\">\"createFuture\"<\/span> <span style=\"color: #555555;\">&lt;&lt;<\/span> <span style=\"color: #cc3300;\">'\\n'<\/span>;\n    co_return <span style=\"color: #ff6600;\">2021<\/span>;\n}\n<\/pre>\n<\/div>\n<p>The reason was that the function<code> initial_suspend<\/code> of the promise returns<code> std::suspend_always<\/code>. This means that the coroutine is at first suspended and can, hence,\u00a0 be executed on a separate thread<\/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;\">\/\/ lazyFutureOnOtherThread.cpp<\/span>\n\n<span style=\"color: #009999;\">#include &lt;coroutine&gt;<\/span>\n<span style=\"color: #009999;\">#include &lt;iostream&gt;<\/span>\n<span style=\"color: #009999;\">#include &lt;memory&gt;<\/span>\n<span style=\"color: #009999;\">#include &lt;thread&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> MyFuture {\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    handle_type coro;\n\n    MyFuture(handle_type h)<span style=\"color: #555555;\">:<\/span> coro(h) {}\n    <span style=\"color: #555555;\">~<\/span>MyFuture() {\n        <span style=\"color: #006699; font-weight: bold;\">if<\/span> ( coro ) coro.destroy();\n    }\n\n    T get() {                                           <span style=\"color: #0099ff; font-style: italic;\">\/\/ (1)<\/span>\n        std<span style=\"color: #555555;\">::<\/span>cout <span style=\"color: #555555;\">&lt;&lt;<\/span> <span style=\"color: #cc3300;\">\"    MyFuture::get:  \"<\/span> \n                  <span style=\"color: #555555;\">&lt;&lt;<\/span> <span style=\"color: #cc3300;\">\"std::this_thread::get_id(): \"<\/span> \n                  <span style=\"color: #555555;\">&lt;&lt;<\/span> std<span style=\"color: #555555;\">::<\/span>this_thread<span style=\"color: #555555;\">::<\/span>get_id() <span style=\"color: #555555;\">&lt;&lt;<\/span> <span style=\"color: #cc3300;\">'\\n'<\/span>;\n        \n        std<span style=\"color: #555555;\">::<\/span><span style=\"color: #006699; font-weight: bold;\">thread<\/span> t([<span style=\"color: #006699; font-weight: bold;\">this<\/span>] { coro.resume(); });       <span style=\"color: #0099ff; font-style: italic;\">\/\/ (2)<\/span>\n        t.join();\n        <span style=\"color: #006699; font-weight: bold;\">return<\/span> coro.promise().result;\n    }\n\n    <span style=\"color: #006699; font-weight: bold;\">struct<\/span> promise_type {\n        promise_type(){ \n            std<span style=\"color: #555555;\">::<\/span>cout <span style=\"color: #555555;\">&lt;&lt;<\/span> <span style=\"color: #cc3300;\">\"        promise_type::promise_type:  \"<\/span> \n                      <span style=\"color: #555555;\">&lt;&lt;<\/span> <span style=\"color: #cc3300;\">\"std::this_thread::get_id(): \"<\/span> \n                      <span style=\"color: #555555;\">&lt;&lt;<\/span> std<span style=\"color: #555555;\">::<\/span>this_thread<span style=\"color: #555555;\">::<\/span>get_id() <span style=\"color: #555555;\">&lt;&lt;<\/span> <span style=\"color: #cc3300;\">'\\n'<\/span>;\n        }\n        <span style=\"color: #555555;\">~<\/span>promise_type(){ \n            std<span style=\"color: #555555;\">::<\/span>cout <span style=\"color: #555555;\">&lt;&lt;<\/span> <span style=\"color: #cc3300;\">\"        promise_type::~promise_type:  \"<\/span> \n                      <span style=\"color: #555555;\">&lt;&lt;<\/span> <span style=\"color: #cc3300;\">\"std::this_thread::get_id(): \"<\/span> \n                      <span style=\"color: #555555;\">&lt;&lt;<\/span> std<span style=\"color: #555555;\">::<\/span>this_thread<span style=\"color: #555555;\">::<\/span>get_id() <span style=\"color: #555555;\">&lt;&lt;<\/span> <span style=\"color: #cc3300;\">'\\n'<\/span>;\n        }\n\n        T result;\n        <span style=\"color: #006699; font-weight: bold;\">auto<\/span> <span style=\"color: #cc00ff;\">get_return_object<\/span>() {\n            <span style=\"color: #006699; font-weight: bold;\">return<\/span> MyFuture{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: #007788; font-weight: bold;\">void<\/span> <span style=\"color: #cc00ff;\">return_value<\/span>(T v) {\n            std<span style=\"color: #555555;\">::<\/span>cout <span style=\"color: #555555;\">&lt;&lt;<\/span> <span style=\"color: #cc3300;\">\"        promise_type::return_value:  \"<\/span> \n                      <span style=\"color: #555555;\">&lt;&lt;<\/span> <span style=\"color: #cc3300;\">\"std::this_thread::get_id(): \"<\/span> \n                      <span style=\"color: #555555;\">&lt;&lt;<\/span> std<span style=\"color: #555555;\">::<\/span>this_thread<span style=\"color: #555555;\">::<\/span>get_id() <span style=\"color: #555555;\">&lt;&lt;<\/span> <span style=\"color: #cc3300;\">'\\n'<\/span>;\n            std<span style=\"color: #555555;\">::<\/span>cout <span style=\"color: #555555;\">&lt;&lt;<\/span> v <span style=\"color: #555555;\">&lt;&lt;<\/span> std<span style=\"color: #555555;\">::<\/span>endl;\n            result <span style=\"color: #555555;\">=<\/span> v;\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            std<span style=\"color: #555555;\">::<\/span>cout <span style=\"color: #555555;\">&lt;&lt;<\/span> <span style=\"color: #cc3300;\">\"        promise_type::final_suspend:  \"<\/span> \n                      <span style=\"color: #555555;\">&lt;&lt;<\/span> <span style=\"color: #cc3300;\">\"std::this_thread::get_id(): \"<\/span> \n                      <span style=\"color: #555555;\">&lt;&lt;<\/span> std<span style=\"color: #555555;\">::<\/span>this_thread<span style=\"color: #555555;\">::<\/span>get_id() <span style=\"color: #555555;\">&lt;&lt;<\/span> <span style=\"color: #cc3300;\">'\\n'<\/span>;\n            <span style=\"color: #006699; font-weight: bold;\">return<\/span> {};\n        }\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};\n\nMyFuture<span style=\"color: #555555;\">&lt;<\/span><span style=\"color: #007788; font-weight: bold;\">int<\/span><span style=\"color: #555555;\">&gt;<\/span> createFuture() {\n    co_return <span style=\"color: #ff6600;\">2021<\/span>;\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>cout <span style=\"color: #555555;\">&lt;&lt;<\/span> <span style=\"color: #cc3300;\">\"main:  \"<\/span> \n              <span style=\"color: #555555;\">&lt;&lt;<\/span> <span style=\"color: #cc3300;\">\"std::this_thread::get_id(): \"<\/span> \n              <span style=\"color: #555555;\">&lt;&lt;<\/span> std<span style=\"color: #555555;\">::<\/span>this_thread<span style=\"color: #555555;\">::<\/span>get_id() <span style=\"color: #555555;\">&lt;&lt;<\/span> <span style=\"color: #cc3300;\">'\\n'<\/span>;\n\n    <span style=\"color: #006699; font-weight: bold;\">auto<\/span> fut <span style=\"color: #555555;\">=<\/span> createFuture();\n    <span style=\"color: #006699; font-weight: bold;\">auto<\/span> res <span style=\"color: #555555;\">=<\/span> fut.get();\n    std<span style=\"color: #555555;\">::<\/span>cout <span style=\"color: #555555;\">&lt;&lt;<\/span> <span style=\"color: #cc3300;\">\"res: \"<\/span> <span style=\"color: #555555;\">&lt;&lt;<\/span> res <span style=\"color: #555555;\">&lt;&lt;<\/span> <span style=\"color: #cc3300;\">'\\n'<\/span>;\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>I added a few comments to the program that show the id of the running thread. The program<code> lazyFutureOnOtherThread.cpp<\/code> is quite similar to the previous program <code>lazyFuture.cpp<\/code> in the post &#8220;<a href=\"https:\/\/www.modernescpp.com\/index.php\/lazy-futures-with-coroutines-in-c-20\">Lazy Futures with Coroutines in C++20<\/a>&#8220;. The client calls <code>get<\/code> (line 1) for the next value. The call <code>std::thread t([this] { coro.resume(); });<\/code> (line 2) resumes the coroutine on another thread.<\/p>\n<p>You can try out the program on the\u00a0<a href=\"https:\/\/wandbox.org\/permlink\/jFVVj80Gxu6bnNkc\">Wandbox<\/a> online compiler.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\" size-full wp-image-6100\" style=\"display: block; margin-left: auto; margin-right: auto;\" src=\"https:\/\/www.modernescpp.com\/wp-content\/uploads\/2021\/03\/lazyFutureOnOtherThread.png\" alt=\"lazyFutureOnOtherThread\" width=\"650\" height=\"165\" srcset=\"https:\/\/www.modernescpp.com\/wp-content\/uploads\/2021\/03\/lazyFutureOnOtherThread.png 1102w, https:\/\/www.modernescpp.com\/wp-content\/uploads\/2021\/03\/lazyFutureOnOtherThread-300x76.png 300w, https:\/\/www.modernescpp.com\/wp-content\/uploads\/2021\/03\/lazyFutureOnOtherThread-1024x260.png 1024w, https:\/\/www.modernescpp.com\/wp-content\/uploads\/2021\/03\/lazyFutureOnOtherThread-768x195.png 768w\" sizes=\"auto, (max-width: 650px) 100vw, 650px\" \/><\/p>\n<p>I want to add a few additional remarks about the member function <code>get<\/code>. It is crucial that the promise is resumed in a separate thread, and finishes before it returns <code>coro.promise().result;<\/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%;\">T <span style=\"color: #cc00ff;\">get<\/span>() {\n    std<span style=\"color: #555555;\">::<\/span><span style=\"color: #006699; font-weight: bold;\">thread<\/span> t([<span style=\"color: #006699; font-weight: bold;\">this<\/span>] { coro.resume(); });\n    t.join();\n    <span style=\"color: #006699; font-weight: bold;\">return<\/span> coro.promise().result;\n}\n<\/pre>\n<\/div>\n<p>When I join the thread<code> t<\/code> after the call return<code> coro.promise().result<\/code>, the program would have undefined behavior. In the following implementation of the function <code>get<\/code>I use a <code>std::jthread<\/code>. Here is my post about std::jthread in C++20: &#8220;<a href=\"https:\/\/www.modernescpp.com\/index.php\/an-improved-thread-with-c-20\">An Improved Thread with C++20<\/a>&#8220;. Since <code>std::jthread<\/code> automatically joins when it goes out of scope. This is too late.<\/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%;\">T <span style=\"color: #cc00ff;\">get<\/span>() {   \n    std<span style=\"color: #555555;\">::<\/span>jthread t([<span style=\"color: #006699; font-weight: bold;\">this<\/span>] { coro.resume(); });\n    <span style=\"color: #006699; font-weight: bold;\">return<\/span> coro.promise().result;\n}\n<\/pre>\n<\/div>\n<p>In this case, the client likely gets its result before the promise prepares it using the member function<code> return_value<\/code>. Now, <code>result<\/code> has an arbitrary value, and therefore so does <code>res<\/code>.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\" size-full wp-image-6101\" style=\"display: block; margin-left: auto; margin-right: auto;\" src=\"https:\/\/www.modernescpp.com\/wp-content\/uploads\/2021\/03\/lazyFutureOnOtherThreadLateJoin.png\" alt=\"lazyFutureOnOtherThreadLateJoin\" width=\"650\" height=\"164\" srcset=\"https:\/\/www.modernescpp.com\/wp-content\/uploads\/2021\/03\/lazyFutureOnOtherThreadLateJoin.png 1108w, https:\/\/www.modernescpp.com\/wp-content\/uploads\/2021\/03\/lazyFutureOnOtherThreadLateJoin-300x76.png 300w, https:\/\/www.modernescpp.com\/wp-content\/uploads\/2021\/03\/lazyFutureOnOtherThreadLateJoin-1024x259.png 1024w, https:\/\/www.modernescpp.com\/wp-content\/uploads\/2021\/03\/lazyFutureOnOtherThreadLateJoin-768x194.png 768w\" sizes=\"auto, (max-width: 650px) 100vw, 650px\" \/><\/p>\n<div><\/div>\n<div>Other possibilities exist to ensure the thread is done before the return call.<\/div>\n<div><\/div>\n<ul>\n<li><code>std::jthread<\/code> has its scope<\/li>\n<\/ul>\n<div><\/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%;\">T <span style=\"color: #cc00ff;\">get<\/span>() {\n    {\n        std<span style=\"color: #555555;\">::<\/span>jthread t([<span style=\"color: #006699; font-weight: bold;\">this<\/span>] { coro.resume(); });\n    }\n    <span style=\"color: #006699; font-weight: bold;\">return<\/span> coro.promise().result;\n}\n<\/pre>\n<\/div>\n<ul>\n<li>Make<code> std::jthread<\/code> a temporary object<\/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%;\">T <span style=\"color: #cc00ff;\">get<\/span>() {\n    std<span style=\"color: #555555;\">::<\/span>jthread([<span style=\"color: #006699; font-weight: bold;\">this<\/span>] { coro.resume(); });\n    <span style=\"color: #006699; font-weight: bold;\">return<\/span> coro.promise().result;\n}<\/pre>\n<\/div>\n<p>In particular, I don&#8217;t like the last solution because it may take you a few seconds to recognize that I just called the constructor of <code>std::jthread<\/code>.<\/p>\n<p>Now, it is the right time to add more theories about coroutines.<\/p>\n<h2><code>promise_type<\/code><\/h2>\n<p>You may wonder that the coroutine, such as <code>MyFuture<\/code> always has inner type <code>promise_type<\/code>. This name is required. Alternatively, you can specialize <code><a href=\"https:\/\/en.cppreference.com\/w\/cpp\/coroutine\/coroutine_traits\">std::coroutines_traits<\/a>\u00a0<\/code>on <code>MyFuture <\/code>and define a public <code>promise_type<\/code> in it. I mentioned this point explicitly because I know a few people, including me who already fall into this trap.<\/p>\n<p>Here is another trap I fall into on Windows.<\/p>\n<h2><code>return_void<\/code> and <code>return_value<\/code><\/h2>\n<p>The promise needs either the member function<code> return_void<\/code> or <code>return_value.<\/code><\/p>\n<ul>\n<li>The promise needs a <code>return_void<\/code> member function if\n<ul>\n<li>the coroutine has no <code>co_return<\/code> statement.<\/li>\n<li>the coroutine has a <code>co_return<\/code> statement without argument.<\/li>\n<li>the coroutine has a <code>co_return expression<\/code>\u00a0a statement where the expression has type<code> void.<\/code><\/li>\n<\/ul>\n<\/li>\n<li>The promise needs a <code>return_value<\/code> member function if it returns<code> co_return<\/code> expression statement where expression must not have the type <code>void<\/code><\/li>\n<\/ul>\n<p>Falling off the end of a void-returning coroutine without a <code>return_void<\/code>\u00a0a member function is an undefined behavior. Interestingly, Microsoft, but not the GCC compiler, requires a member function <code>return_void<\/code> if the coroutine is always suspended at its final suspension point and, therefore, does not fail at the end: <code> std<span style=\"color: #555555;\">::<\/span>suspend_always final_suspend() noexcept; <\/code>From my perspective, the C++20 standard is not clear, and I always add a member function<code> void return_void() {}<\/code> to my promise type.<\/p>\n<h2>What&#8217;s next?<\/h2>\n<p>After I discussed the new keyword <code>co_return<\/code>, I want to continue with <code>co_yield<\/code>. <code>co_yield<\/code> enables you to create infinite data streams. I will show in my <a href=\"https:\/\/www.modernescpp.com\/index.php\/an-infinite-data-stream-with-coroutines-in-c-20\">next post<\/a>, how.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>This post concludes my posts about co_return in C++20. I started with an eager future, and continued with a lazy future. Today, I execute the future in a separate thread using coroutines as an implementation detail.<\/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,446],"class_list":["post-6102","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-c-20","tag-coroutines","tag-tasks"],"_links":{"self":[{"href":"https:\/\/www.modernescpp.com\/index.php\/wp-json\/wp\/v2\/posts\/6102","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=6102"}],"version-history":[{"count":2,"href":"https:\/\/www.modernescpp.com\/index.php\/wp-json\/wp\/v2\/posts\/6102\/revisions"}],"predecessor-version":[{"id":8410,"href":"https:\/\/www.modernescpp.com\/index.php\/wp-json\/wp\/v2\/posts\/6102\/revisions\/8410"}],"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=6102"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.modernescpp.com\/index.php\/wp-json\/wp\/v2\/categories?post=6102"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.modernescpp.com\/index.php\/wp-json\/wp\/v2\/tags?post=6102"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}