{"id":9718,"date":"2024-07-15T08:43:25","date_gmt":"2024-07-15T08:43:25","guid":{"rendered":"https:\/\/www.modernescpp.com\/?p=9718"},"modified":"2024-07-15T08:43:26","modified_gmt":"2024-07-15T08:43:26","slug":"cooperative-interruption-of-a-thread-in-c20-callbacks","status":"publish","type":"post","link":"https:\/\/www.modernescpp.com\/index.php\/cooperative-interruption-of-a-thread-in-c20-callbacks\/","title":{"rendered":"Cooperative Interruption of a Thread in C++20: Callbacks"},"content":{"rendered":"\n<p> I introduced in my last post &#8220;<a href=\"https:\/\/www.modernescpp.com\/index.php\/cooperative-interruption-of-a-thread-in-c-20\/\">Cooperative Interruption of a Thread in C++20<\/a>&#8221; callbacks. Today, I dive deeper.<\/p>\n\n\n\n<figure class=\"wp-block-image aligncenter size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"912\" height=\"321\" src=\"https:\/\/www.modernescpp.com\/wp-content\/uploads\/2024\/07\/TimelineCpp20Interruption.png\" alt=\"\" class=\"wp-image-9720\" srcset=\"https:\/\/www.modernescpp.com\/wp-content\/uploads\/2024\/07\/TimelineCpp20Interruption.png 912w, https:\/\/www.modernescpp.com\/wp-content\/uploads\/2024\/07\/TimelineCpp20Interruption-300x106.png 300w, https:\/\/www.modernescpp.com\/wp-content\/uploads\/2024\/07\/TimelineCpp20Interruption-768x270.png 768w, https:\/\/www.modernescpp.com\/wp-content\/uploads\/2024\/07\/TimelineCpp20Interruption-705x248.png 705w\" sizes=\"auto, (max-width: 912px) 100vw, 912px\" \/><\/figure>\n\n\n\n<p>First , here&#8217;s a short reminder.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Reminder<\/h2>\n\n\n\n<p> In my last post &#8220;<a href=\"https:\/\/www.modernescpp.com\/index.php\/cooperative-interruption-of-a-thread-in-c-20\/\">Cooperative Interruption of a Thread in C++20<\/a>&#8220;, I presented the following program.<\/p>\n\n\n\n<!-- HTML generated using hilite.me --><div style=\"background: #f0f3f3; overflow:auto;width:auto;gray;border-width:.1em .1em .1em .8em\"><pre style=\"margin: 0; line-height: 125%\"><span style=\"color: #0099FF; font-style: italic\">\/\/ invokeCallback.cpp<\/span>\n\n<span style=\"color: #009999\">#include &lt;chrono&gt;<\/span>\n<span style=\"color: #009999\">#include &lt;iostream&gt;<\/span>\n<span style=\"color: #009999\">#include &lt;thread&gt;<\/span>\n<span style=\"color: #009999\">#include &lt;vector&gt;<\/span>\n\n<span style=\"color: #006699; font-weight: bold\">using<\/span> <span style=\"color: #006699; font-weight: bold\">namespace<\/span><span style=\"color: #555555\">::<\/span>std<span style=\"color: #555555\">::<\/span>literals;\n\n<span style=\"color: #006699; font-weight: bold\">auto<\/span> func <span style=\"color: #555555\">=<\/span> [](std<span style=\"color: #555555\">::<\/span>stop_token stoken) {                             <span style=\"color: #0099FF; font-style: italic\">\/\/ (1)<\/span>\n        <span style=\"color: #007788; font-weight: bold\">int<\/span> counter{<span style=\"color: #FF6600\">0<\/span>};\n        <span style=\"color: #006699; font-weight: bold\">auto<\/span> thread_id <span style=\"color: #555555\">=<\/span> std<span style=\"color: #555555\">::<\/span>this_thread<span style=\"color: #555555\">::<\/span>get_id();\n        std<span style=\"color: #555555\">::<\/span>stop_callback callBack(stoken, [<span style=\"color: #555555\">&amp;<\/span>counter, thread_id] {  <span style=\"color: #0099FF; font-style: italic\">\/\/ (2)<\/span>\n            std<span style=\"color: #555555\">::<\/span>cout <span style=\"color: #555555\">&lt;&lt;<\/span> <span style=\"color: #CC3300\">&quot;Thread id: &quot;<\/span> <span style=\"color: #555555\">&lt;&lt;<\/span> thread_id \n                      <span style=\"color: #555555\">&lt;&lt;<\/span> <span style=\"color: #CC3300\">&quot;; counter: &quot;<\/span> <span style=\"color: #555555\">&lt;&lt;<\/span> counter <span style=\"color: #555555\">&lt;&lt;<\/span> <span style=\"color: #CC3300\">&#39;\\n&#39;<\/span>;\n        });\n        <span style=\"color: #006699; font-weight: bold\">while<\/span> (counter <span style=\"color: #555555\">&lt;<\/span> <span style=\"color: #FF6600\">10<\/span>) {\n            std<span style=\"color: #555555\">::<\/span>this_thread<span style=\"color: #555555\">::<\/span>sleep_for(<span style=\"color: #FF6600\">0.2<\/span>s);\n            <span style=\"color: #555555\">++<\/span>counter;\n        }\n    };\n\n<span style=\"color: #007788; font-weight: bold\">int<\/span> <span style=\"color: #CC00FF\">main<\/span>() {\n    \n    std<span style=\"color: #555555\">::<\/span>cout <span style=\"color: #555555\">&lt;&lt;<\/span> <span style=\"color: #CC3300\">&#39;\\n&#39;<\/span>;\n    \n    std<span style=\"color: #555555\">::<\/span>vector<span style=\"color: #555555\">&lt;<\/span>std<span style=\"color: #555555\">::<\/span>jthread<span style=\"color: #555555\">&gt;<\/span> vecThreads(<span style=\"color: #FF6600\">10<\/span>);\n    <span style=\"color: #006699; font-weight: bold\">for<\/span>(<span style=\"color: #006699; font-weight: bold\">auto<\/span><span style=\"color: #555555\">&amp;<\/span> thr<span style=\"color: #555555\">:<\/span> vecThreads) thr <span style=\"color: #555555\">=<\/span> std<span style=\"color: #555555\">::<\/span>jthread(func);\n    \n    std<span style=\"color: #555555\">::<\/span>this_thread<span style=\"color: #555555\">::<\/span>sleep_for(<span style=\"color: #FF6600\">1<\/span>s);                              <span style=\"color: #0099FF; font-style: italic\">\/\/ (3)<\/span>\n    \n    <span style=\"color: #006699; font-weight: bold\">for<\/span>(<span style=\"color: #006699; font-weight: bold\">auto<\/span><span style=\"color: #555555\">&amp;<\/span> thr<span style=\"color: #555555\">:<\/span> vecThreads) thr.request_stop();                <span style=\"color: #0099FF; font-style: italic\">\/\/ (4)<\/span>\n\n    std<span style=\"color: #555555\">::<\/span>cout <span style=\"color: #555555\">&lt;&lt;<\/span> <span style=\"color: #CC3300\">&#39;\\n&#39;<\/span>;\n    \n}\n<\/pre><\/div>\n\n\n\n<p>Each of the ten threads invokes the lambda function<code> func<\/code> (1). The callback (2) displays the thread <code>id<\/code> and the <code>counter<\/code>. Due to the one-second sleeping of the main thread (3) and the sleeping of the child threads, the counter is 4 when the callbacks are invoked. The call <code>thr.request_stop()<\/code> triggers the callback on each thread. <\/p>\n\n\n\n<figure class=\"wp-block-image aligncenter size-full is-resized\"><img loading=\"lazy\" decoding=\"async\" width=\"842\" height=\"584\" src=\"https:\/\/www.modernescpp.com\/wp-content\/uploads\/2024\/07\/invokeCallback.png\" alt=\"\" class=\"wp-image-9724\" style=\"width:450px\" srcset=\"https:\/\/www.modernescpp.com\/wp-content\/uploads\/2024\/07\/invokeCallback.png 842w, https:\/\/www.modernescpp.com\/wp-content\/uploads\/2024\/07\/invokeCallback-300x208.png 300w, https:\/\/www.modernescpp.com\/wp-content\/uploads\/2024\/07\/invokeCallback-768x533.png 768w, https:\/\/www.modernescpp.com\/wp-content\/uploads\/2024\/07\/invokeCallback-705x489.png 705w\" sizes=\"auto, (max-width: 842px) 100vw, 842px\" \/><\/figure>\n\n\n\n<p>One question was not answered in my last post:<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Where does the callback run?<\/h3>\n\n\n\n<p>The <code>std::stop_callback<\/code> constructor registers the callback function for the <code>std::stop_token<\/code> given by the associated <code>std::stop_source.<\/code> This callback function is either invoked in the thread invoking <code>request_stop() <\/code>or the thread constructing the <code>std::stop_callback.<\/code> If the request to stop happens prior to the registration of the <code>std::stop_callback<\/code>, the callback is invoked in the thread constructing the <code>std::stop_callback.<\/code> Otherwise, the callback is invoked in the thread invoking <code>request_stop.<\/code> &nbsp;If the call <code>request_stop()<\/code>happens after the execution of the thread constructing the <code>std::stop_callback<\/code>, the registered callback will never be called. &nbsp;<\/p>\n\n\n\n<p>You can register more than one callback for one or more threads using the same <code>std::stop_token.<\/code> The C++ standard does not guarantee the order in which they are executed.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">More than one callback<\/h3>\n\n\n\n<!-- HTML generated using hilite.me --><div style=\"background: #f0f3f3; overflow:auto;width:auto;gray;border-width:.1em .1em .1em .8em\"><pre style=\"margin: 0; line-height: 125%\"><span style=\"color: #0099FF; font-style: italic\">\/\/ invokeCallbacks.cpp<\/span>\n\n<span style=\"color: #009999\">#include &lt;chrono&gt;<\/span>\n<span style=\"color: #009999\">#include &lt;iostream&gt;<\/span>\n<span style=\"color: #009999\">#include &lt;thread&gt;<\/span>\n\n<span style=\"color: #006699; font-weight: bold\">using<\/span> <span style=\"color: #006699; font-weight: bold\">namespace<\/span> std<span style=\"color: #555555\">::<\/span>literals;\n\n<span style=\"color: #007788; font-weight: bold\">void<\/span> <span style=\"color: #CC00FF\">func<\/span>(std<span style=\"color: #555555\">::<\/span>stop_token stopToken) {\n    std<span style=\"color: #555555\">::<\/span>this_thread<span style=\"color: #555555\">::<\/span>sleep_for(<span style=\"color: #FF6600\">100<\/span>ms);\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\">9<\/span>; <span style=\"color: #555555\">++<\/span>i) {\n       std<span style=\"color: #555555\">::<\/span>stop_callback cb(stopToken, [i] { std<span style=\"color: #555555\">::<\/span>cout <span style=\"color: #555555\">&lt;&lt;<\/span> i; });\n    }\n    std<span style=\"color: #555555\">::<\/span>cout <span style=\"color: #555555\">&lt;&lt;<\/span> <span style=\"color: #CC3300\">&#39;\\n&#39;<\/span>;\n}\n\n<span style=\"color: #007788; font-weight: bold\">int<\/span> <span style=\"color: #CC00FF\">main<\/span>() {\n    \n    std<span style=\"color: #555555\">::<\/span>cout <span style=\"color: #555555\">&lt;&lt;<\/span> <span style=\"color: #CC3300\">&#39;\\n&#39;<\/span>;\n    \n    std<span style=\"color: #555555\">::<\/span>jthread thr1 <span style=\"color: #555555\">=<\/span> std<span style=\"color: #555555\">::<\/span>jthread(func);\n    std<span style=\"color: #555555\">::<\/span>jthread thr2 <span style=\"color: #555555\">=<\/span> std<span style=\"color: #555555\">::<\/span>jthread(func);\n    thr1.request_stop();\n    thr2.request_stop();\n\n    std<span style=\"color: #555555\">::<\/span>cout <span style=\"color: #555555\">&lt;&lt;<\/span> <span style=\"color: #CC3300\">&#39;\\n&#39;<\/span>;\n    \n}\n<\/pre><\/div>\n\n\n\n<figure class=\"wp-block-image aligncenter size-full is-resized\"><img loading=\"lazy\" decoding=\"async\" width=\"400\" height=\"235\" src=\"https:\/\/www.modernescpp.com\/wp-content\/uploads\/2024\/07\/invokeCallbacks.png\" alt=\"\" class=\"wp-image-9729\" style=\"width:400px\" srcset=\"https:\/\/www.modernescpp.com\/wp-content\/uploads\/2024\/07\/invokeCallbacks.png 400w, https:\/\/www.modernescpp.com\/wp-content\/uploads\/2024\/07\/invokeCallbacks-300x176.png 300w\" sizes=\"auto, (max-width: 400px) 100vw, 400px\" \/><\/figure>\n\n\n\n<h2 class=\"wp-block-heading\">A General Mechanism to Send Signals<\/h2>\n\n\n\n<p>The pair <code>std::stop_source <\/code>and <code>std::stop_token<\/code> can be considered as a general mechanism to send a signal. By copying the <code>std::stop_token<\/code>, you can send the signal to any entity executing something. In the following example, I use <code>std::async,<\/code> <code>std::promise, std::thread<\/code>, and<code> std::jthread <\/code>in various combinations.<\/p>\n\n\n\n<!-- HTML generated using hilite.me --><div style=\"background: #f0f3f3; overflow:auto;width:auto;gray;border-width:.1em .1em .1em .8em\"><pre style=\"margin: 0; line-height: 125%\"><span style=\"color: #0099FF; font-style: italic\">\/\/ signalStopRequests.cpp<\/span>\n\n<span style=\"color: #009999\">#include &lt;iostream&gt;<\/span>\n<span style=\"color: #009999\">#include &lt;thread&gt;<\/span>\n<span style=\"color: #009999\">#include &lt;future&gt;<\/span>\n\n<span style=\"color: #006699; font-weight: bold\">using<\/span> <span style=\"color: #006699; font-weight: bold\">namespace<\/span> std<span style=\"color: #555555\">::<\/span>literals;\n\n<span style=\"color: #007788; font-weight: bold\">void<\/span> <span style=\"color: #CC00FF\">function1<\/span>(std<span style=\"color: #555555\">::<\/span>stop_token stopToken, <span style=\"color: #006699; font-weight: bold\">const<\/span> std<span style=\"color: #555555\">::<\/span>string<span style=\"color: #555555\">&amp;<\/span> str){\n    std<span style=\"color: #555555\">::<\/span>this_thread<span style=\"color: #555555\">::<\/span>sleep_for(<span style=\"color: #FF6600\">1<\/span>s);\n    <span style=\"color: #006699; font-weight: bold\">if<\/span> (stopToken.stop_requested()) std<span style=\"color: #555555\">::<\/span>cout <span style=\"color: #555555\">&lt;&lt;<\/span> str <span style=\"color: #555555\">&lt;&lt;<\/span> <span style=\"color: #CC3300\">&quot;: Stop requested<\/span><span style=\"color: #CC3300; font-weight: bold\">\\n<\/span><span style=\"color: #CC3300\">&quot;<\/span>;\n}\n\n<span style=\"color: #007788; font-weight: bold\">void<\/span> <span style=\"color: #CC00FF\">function2<\/span>(std<span style=\"color: #555555\">::<\/span>promise<span style=\"color: #555555\">&lt;<\/span><span style=\"color: #007788; font-weight: bold\">void<\/span><span style=\"color: #555555\">&gt;<\/span> prom, \n               std<span style=\"color: #555555\">::<\/span>stop_token stopToken, <span style=\"color: #006699; font-weight: bold\">const<\/span> std<span style=\"color: #555555\">::<\/span>string<span style=\"color: #555555\">&amp;<\/span> str) {\n    std<span style=\"color: #555555\">::<\/span>this_thread<span style=\"color: #555555\">::<\/span>sleep_for(<span style=\"color: #FF6600\">1<\/span>s);\n    std<span style=\"color: #555555\">::<\/span>stop_callback callBack(stopToken, [<span style=\"color: #555555\">&amp;<\/span>str] { \n        std<span style=\"color: #555555\">::<\/span>cout <span style=\"color: #555555\">&lt;&lt;<\/span> str <span style=\"color: #555555\">&lt;&lt;<\/span> <span style=\"color: #CC3300\">&quot;: Stop requested<\/span><span style=\"color: #CC3300; font-weight: bold\">\\n<\/span><span style=\"color: #CC3300\">&quot;<\/span>; \n    });\n    prom.set_value();\n}\n\n<span style=\"color: #007788; font-weight: bold\">int<\/span> <span style=\"color: #CC00FF\">main<\/span>() {\n\n    std<span style=\"color: #555555\">::<\/span>cout <span style=\"color: #555555\">&lt;&lt;<\/span> <span style=\"color: #CC3300\">&#39;\\n&#39;<\/span>;\n\n    std<span style=\"color: #555555\">::<\/span>stop_source stopSource;                                                <span style=\"color: #0099FF; font-style: italic\">\/\/ (1)<\/span>\n\n    std<span style=\"color: #555555\">::<\/span>stop_token stopToken <span style=\"color: #555555\">=<\/span> std<span style=\"color: #555555\">::<\/span>stop_token(stopSource.get_token());        <span style=\"color: #0099FF; font-style: italic\">\/\/ (2)<\/span>\n\n    std<span style=\"color: #555555\">::<\/span><span style=\"color: #006699; font-weight: bold\">thread<\/span> thr1 <span style=\"color: #555555\">=<\/span> std<span style=\"color: #555555\">::<\/span><span style=\"color: #006699; font-weight: bold\">thread<\/span>(function1, stopToken, <span style=\"color: #CC3300\">&quot;std::thread&quot;<\/span>);        <span style=\"color: #0099FF; font-style: italic\">\/\/ (3)<\/span>\n    \n    std<span style=\"color: #555555\">::<\/span>jthread jthr <span style=\"color: #555555\">=<\/span> std<span style=\"color: #555555\">::<\/span>jthread(function1, stopToken, <span style=\"color: #CC3300\">&quot;std::jthread&quot;<\/span>);     <span style=\"color: #0099FF; font-style: italic\">\/\/ (4)<\/span>\n    \n    <span style=\"color: #006699; font-weight: bold\">auto<\/span> fut1 <span style=\"color: #555555\">=<\/span> std<span style=\"color: #555555\">::<\/span>async([stopToken] {                                        <span style=\"color: #0099FF; font-style: italic\">\/\/ (5)<\/span>\n        std<span style=\"color: #555555\">::<\/span>this_thread<span style=\"color: #555555\">::<\/span>sleep_for(<span style=\"color: #FF6600\">1<\/span>s);\n        <span style=\"color: #006699; font-weight: bold\">if<\/span> (stopToken.stop_requested()) std<span style=\"color: #555555\">::<\/span>cout <span style=\"color: #555555\">&lt;&lt;<\/span> <span style=\"color: #CC3300\">&quot;std::async: Stop requested<\/span><span style=\"color: #CC3300; font-weight: bold\">\\n<\/span><span style=\"color: #CC3300\">&quot;<\/span>;\n    });\n\n    std<span style=\"color: #555555\">::<\/span>promise<span style=\"color: #555555\">&lt;<\/span><span style=\"color: #007788; font-weight: bold\">void<\/span><span style=\"color: #555555\">&gt;<\/span> prom;                                                    <span style=\"color: #0099FF; font-style: italic\">\/\/ (6)<\/span>\n    <span style=\"color: #006699; font-weight: bold\">auto<\/span> fut2 <span style=\"color: #555555\">=<\/span> prom.get_future();\n    std<span style=\"color: #555555\">::<\/span><span style=\"color: #006699; font-weight: bold\">thread<\/span> thr2(function2, std<span style=\"color: #555555\">::<\/span>move(prom), stopToken, <span style=\"color: #CC3300\">&quot;std::promise&quot;<\/span>);\n\n    stopSource.request_stop();                                                  <span style=\"color: #0099FF; font-style: italic\">\/\/ (7)<\/span>\n    <span style=\"color: #006699; font-weight: bold\">if<\/span> (stopToken.stop_requested()) std<span style=\"color: #555555\">::<\/span>cout <span style=\"color: #555555\">&lt;&lt;<\/span> <span style=\"color: #CC3300\">&quot;main: Stop requested<\/span><span style=\"color: #CC3300; font-weight: bold\">\\n<\/span><span style=\"color: #CC3300\">&quot;<\/span>;      <span style=\"color: #0099FF; font-style: italic\">\/\/ (8)<\/span>\n\n    thr1.join();\n    thr2.join();\n\n    std<span style=\"color: #555555\">::<\/span>cout <span style=\"color: #555555\">&lt;&lt;<\/span> <span style=\"color: #CC3300\">&#39;\\n&#39;<\/span>;\n\n}\n<\/pre><\/div>\n\n\n\n<p>Thanks to the <code>stopSource<\/code> (line 1), I can create the<code> stopToken <\/code>(line 2) for each running entity, such as <code>std::thread <\/code>(line 3),<code> std::jthread <\/code>(line 4),<code> std::async<\/code> (line 5), or <code>std::promise<\/code> (line 6). A <code>std::stop_token<\/code> is cheap to copy. Line 7 triggers <code>stopSource.request_stop. <\/code>Also, the main thread (line 8) gets the signal. I use in this example <code>std::jthread. std::jthread<\/code>, and <code>std::condition_variable_any <\/code>has explicit member functions to deal with cooperative interruption more conveniently. Read more about it in the following post: &#8220;<a href=\"https:\/\/www.modernescpp.com\/index.php\/an-improved-thread-with-c-20\/\">An Improved Thread with C++20<\/a>&#8220;.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">What&#8217;s next?<\/h2>\n\n\n\n<p>I will take a break in the next two weeks. Afterward, I will jump again in C++23 and add the first time in C++26.<\/p>\n\n\n\n<p><\/p>\n\n\n\n<p><\/p>\n\n\n\n<p><\/p>\n","protected":false},"excerpt":{"rendered":"<p>I introduced in my last post &#8220;Cooperative Interruption of a Thread in C++20&#8221; callbacks. Today, I dive deeper. First , here&#8217;s a short reminder. Reminder In my last post &#8220;Cooperative Interruption of a Thread in C++20&#8220;, I presented the following program. \/\/ invokeCallback.cpp #include &lt;chrono&gt; #include &lt;iostream&gt; #include &lt;thread&gt; #include &lt;vector&gt; using namespace::std::literals; auto func [&hellip;]<\/p>\n","protected":false},"author":21,"featured_media":9720,"comment_status":"closed","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[375],"tags":[],"class_list":["post-9718","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-c-20"],"_links":{"self":[{"href":"https:\/\/www.modernescpp.com\/index.php\/wp-json\/wp\/v2\/posts\/9718","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=9718"}],"version-history":[{"count":25,"href":"https:\/\/www.modernescpp.com\/index.php\/wp-json\/wp\/v2\/posts\/9718\/revisions"}],"predecessor-version":[{"id":9746,"href":"https:\/\/www.modernescpp.com\/index.php\/wp-json\/wp\/v2\/posts\/9718\/revisions\/9746"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.modernescpp.com\/index.php\/wp-json\/wp\/v2\/media\/9720"}],"wp:attachment":[{"href":"https:\/\/www.modernescpp.com\/index.php\/wp-json\/wp\/v2\/media?parent=9718"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.modernescpp.com\/index.php\/wp-json\/wp\/v2\/categories?post=9718"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.modernescpp.com\/index.php\/wp-json\/wp\/v2\/tags?post=9718"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}