{"id":6070,"date":"2021-01-14T21:30:04","date_gmt":"2021-01-14T21:30:04","guid":{"rendered":"https:\/\/www.modernescpp.com\/index.php\/semaphores-in-c-20\/"},"modified":"2023-06-26T09:34:32","modified_gmt":"2023-06-26T09:34:32","slug":"semaphores-in-c-20","status":"publish","type":"post","link":"https:\/\/www.modernescpp.com\/index.php\/semaphores-in-c-20\/","title":{"rendered":"Semaphores in C++20"},"content":{"rendered":"<p>Semaphores are a synchronization mechanism used to control concurrent access to a shared resource. They also allow it to play ping-pong.<\/p>\n<p><!--more--><\/p>\n<p>&nbsp;<img loading=\"lazy\" decoding=\"async\" class=\" size-full wp-image-5199\" src=\"https:\/\/www.modernescpp.com\/wp-content\/uploads\/2017\/02\/TimelineCpp20.png\" alt=\"TimelineCpp20\" width=\"650\" height=\"225\" style=\"display: block; margin-left: auto; margin-right: auto;\" srcset=\"https:\/\/www.modernescpp.com\/wp-content\/uploads\/2017\/02\/TimelineCpp20.png 894w, https:\/\/www.modernescpp.com\/wp-content\/uploads\/2017\/02\/TimelineCpp20-300x103.png 300w, https:\/\/www.modernescpp.com\/wp-content\/uploads\/2017\/02\/TimelineCpp20-768x265.png 768w\" sizes=\"auto, (max-width: 650px) 100vw, 650px\" \/><\/p>\n<p>&nbsp;<\/p>\n<p>A counting semaphore is a special semaphore with a counter bigger than zero. The counter is initialized in the constructor. Acquiring the semaphore decreases the counter, and releasing the semaphore increases the counter. If a thread tries to acquire the semaphore when the counter is zero, the thread will block until another thread increments the counter by releasing the semaphore.<\/p>\n<h2>Edsger W. Dijkstra Invented Semaphores<\/h2>\n<p>The Dutch computer scientist <a href=\"https:\/\/en.wikipedia.org\/wiki\/Edsger_W._Dijkstra\">Edsger W. Dijkstra<\/a> presented 1965 the concept of a semaphore. A semaphore is a data structure with a queue and a counter. The counter is initialized to a value equal to or greater than zero. It supports the two operations <code>wait<\/code> and <code>signal<\/code>. <code>wait<\/code> acquires the semaphore and decreases the counter; it blocks the thread acquiring it if the counter is zero. <code>signal<\/code> releases the semaphore and increases the counter. Blocked threads are added to the queue to avoid <a href=\"https:\/\/en.wikipedia.org\/wiki\/Starvation_(computer_science)\">starvation<\/a>.<\/p>\n<div>\n<p>Originally, a semaphore is a railway signal.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\" size-full wp-image-6065\" src=\"https:\/\/www.modernescpp.com\/wp-content\/uploads\/2021\/01\/semaphore.jpg\" alt=\"semaphore\" width=\"200\" height=\"300\" style=\"display: block; margin-left: auto; margin-right: auto;\" srcset=\"https:\/\/www.modernescpp.com\/wp-content\/uploads\/2021\/01\/semaphore.jpg 300w, https:\/\/www.modernescpp.com\/wp-content\/uploads\/2021\/01\/semaphore-200x300.jpg 200w\" sizes=\"auto, (max-width: 200px) 100vw, 200px\" \/><\/p>\n<p>&nbsp;<\/p>\n<div>\n<div style=\"text-align: center;\">The&nbsp;original&nbsp;uploader&nbsp;was&nbsp;AmosWolfe&nbsp;at&nbsp;English&nbsp;Wikipedia.&nbsp;&#8211;&nbsp;<a href=\"https:\/\/commons.wikimedia.org\/w\/index.php?curid=1972304\">Transferred from en.wikipedia to Commons., CC BY 2.0<\/a><\/div>\n<div>&nbsp;<\/div>\n<div><\/div>\n<\/div>\n<\/div>\n<h2 style=\"text-align: justify;\">Counting Semaphores in C++20<\/h2>\n<p>C++20 supports a <code>std::binary_semaphore<\/code>, which is an alias for a <code>std::counting_semaphore&lt;1&gt;<\/code>. In this case, the least maximal value is 1. <code>std::binary_semaphores<\/code> can be used to implement <a href=\"https:\/\/en.cppreference.com\/w\/cpp\/named_req\/BasicLockable\">locks<\/a>.<\/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: #006699; font-weight: bold;\">using<\/span> binary_semaphore <span style=\"color: #555555;\">=<\/span> std<span style=\"color: #555555;\">::<\/span>counting_semaphore<span style=\"color: #555555;\">&lt;<\/span><span style=\"color: #ff6600;\">1<\/span><span style=\"color: #555555;\">&gt;<\/span>;\r\n<\/pre>\n<\/div>\n<p>In contrast to a <code>std::mutex<\/code>, a <code>std::counting_semaphore<\/code> is not bound to a thread. This means that the acquire and release call of a semaphore can happen on different threads. The following table presents the interface of a <code>std::counting_semaphore<\/code>.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\" size-full wp-image-6066\" src=\"https:\/\/www.modernescpp.com\/wp-content\/uploads\/2021\/01\/semaphoreTable.png\" alt=\"semaphoreTable\" width=\"650\" height=\"282\" style=\"display: block; margin-left: auto; margin-right: auto;\" srcset=\"https:\/\/www.modernescpp.com\/wp-content\/uploads\/2021\/01\/semaphoreTable.png 819w, https:\/\/www.modernescpp.com\/wp-content\/uploads\/2021\/01\/semaphoreTable-300x130.png 300w, https:\/\/www.modernescpp.com\/wp-content\/uploads\/2021\/01\/semaphoreTable-768x334.png 768w\" sizes=\"auto, (max-width: 650px) 100vw, 650px\" \/><\/p>\n<div>\n<p>&nbsp;<\/p>\n<p>The constructor call <code>std::counting_semaphore&lt;10&gt; sem(5)<\/code> creates a semaphore sem with a maximal value of 10 and a counter of 5. The call <code>sem.max()<\/code> returns the least maximal value. <code>sem.try_aquire_for(relTime)<\/code> needs a relative time duration; the member function <code>sem.try_acquire_until(absTime)<\/code> needs an absolute time point. In my previous posts, you can read more about time durations and time points to the time library: <a href=\"https:\/\/www.modernescpp.com\/index.php\/tag\/time\">time<\/a>.&nbsp; The three calls <code>sem.try_acquire, sem.try_acquire_for<\/code>, and <code>sem.try_acquire_until<\/code> return a boolean indicating the success of the calls.<\/p>\n<p>Semaphores are typically used in sender-receiver workflows. For example, initializing the semaphore sem with 0 will block the receiver <code>sem.acquire()<\/code> call until the sender calls <code>sem.release()<\/code>. Consequently, the receiver waits for the notification of the sender. A one-time synchronization of threads can easily be implemented using semaphores.<\/p>\n<\/div>\n<p style=\"text-align: center;\">&nbsp;<\/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;\">\/\/ threadSynchronizationSemaphore.cpp<\/span>\r\n\r\n<span style=\"color: #009999;\">#include &lt;iostream&gt;<\/span>\r\n<span style=\"color: #009999;\">#include &lt;semaphore&gt;<\/span>\r\n<span style=\"color: #009999;\">#include &lt;thread&gt;<\/span>\r\n<span style=\"color: #009999;\">#include &lt;vector&gt;<\/span>\r\n\r\nstd<span style=\"color: #555555;\">::<\/span>vector<span style=\"color: #555555;\">&lt;<\/span><span style=\"color: #007788; font-weight: bold;\">int<\/span><span style=\"color: #555555;\">&gt;<\/span> myVec{};\r\n\r\nstd<span style=\"color: #555555;\">::<\/span>counting_semaphore<span style=\"color: #555555;\">&lt;<\/span><span style=\"color: #ff6600;\">1<\/span><span style=\"color: #555555;\">&gt;<\/span> prepareSignal(<span style=\"color: #ff6600;\">0<\/span>);              <span style=\"color: #0099ff; font-style: italic;\">\/\/ (1)<\/span>\r\n\r\n<span style=\"color: #007788; font-weight: bold;\">void<\/span> <span style=\"color: #cc00ff;\">prepareWork<\/span>() {\r\n\r\n    myVec.insert(myVec.end(), {<span style=\"color: #ff6600;\">0<\/span>, <span style=\"color: #ff6600;\">1<\/span>, <span style=\"color: #ff6600;\">0<\/span>, <span style=\"color: #ff6600;\">3<\/span>});\r\n    std<span style=\"color: #555555;\">::<\/span>cout <span style=\"color: #555555;\">&lt;&lt;<\/span> <span style=\"color: #cc3300;\">\"Sender: Data prepared.\"<\/span>  <span style=\"color: #555555;\">&lt;&lt;<\/span> <span style=\"color: #cc3300;\">'\\n'<\/span>;\r\n    prepareSignal.release();                              <span style=\"color: #0099ff; font-style: italic;\">\/\/ (2)<\/span>\r\n}\r\n\r\n<span style=\"color: #007788; font-weight: bold;\">void<\/span> <span style=\"color: #cc00ff;\">completeWork<\/span>() {\r\n\r\n    std<span style=\"color: #555555;\">::<\/span>cout <span style=\"color: #555555;\">&lt;&lt;<\/span> <span style=\"color: #cc3300;\">\"Waiter: Waiting for data.\"<\/span> <span style=\"color: #555555;\">&lt;&lt;<\/span> <span style=\"color: #cc3300;\">'\\n'<\/span>;\r\n    prepareSignal.acquire();                              <span style=\"color: #0099ff; font-style: italic;\">\/\/ (3)<\/span>\r\n    myVec[<span style=\"color: #ff6600;\">2<\/span>] <span style=\"color: #555555;\">=<\/span> <span style=\"color: #ff6600;\">2<\/span>;\r\n    std<span style=\"color: #555555;\">::<\/span>cout <span style=\"color: #555555;\">&lt;&lt;<\/span> <span style=\"color: #cc3300;\">\"Waiter: Complete the work.\"<\/span> <span style=\"color: #555555;\">&lt;&lt;<\/span> <span style=\"color: #cc3300;\">'\\n'<\/span>;\r\n    <span style=\"color: #006699; font-weight: bold;\">for<\/span> (<span style=\"color: #006699; font-weight: bold;\">auto<\/span> i<span style=\"color: #555555;\">:<\/span> myVec) std<span style=\"color: #555555;\">::<\/span>cout <span style=\"color: #555555;\">&lt;&lt;<\/span> i <span style=\"color: #555555;\">&lt;&lt;<\/span> <span style=\"color: #cc3300;\">\" \"<\/span>;\r\n    std<span style=\"color: #555555;\">::<\/span>cout <span style=\"color: #555555;\">&lt;&lt;<\/span> <span style=\"color: #cc3300;\">'\\n'<\/span>;\r\n    \r\n}\r\n\r\n<span style=\"color: #007788; font-weight: bold;\">int<\/span> <span style=\"color: #cc00ff;\">main<\/span>() {\r\n\r\n    std<span style=\"color: #555555;\">::<\/span>cout <span style=\"color: #555555;\">&lt;&lt;<\/span> <span style=\"color: #cc3300;\">'\\n'<\/span>;\r\n\r\n    std<span style=\"color: #555555;\">::<\/span><span style=\"color: #006699; font-weight: bold;\">thread<\/span> t1(prepareWork);\r\n    std<span style=\"color: #555555;\">::<\/span><span style=\"color: #006699; font-weight: bold;\">thread<\/span> t2(completeWork);\r\n\r\n    t1.join();\r\n    t2.join();\r\n\r\n    std<span style=\"color: #555555;\">::<\/span>cout <span style=\"color: #555555;\">&lt;&lt;<\/span> <span style=\"color: #cc3300;\">'\\n'<\/span>;\r\n  \r\n}\r\n<\/pre>\n<\/div>\n<p style=\"text-align: center;\">&nbsp;<\/p>\n<p>The <code>std::counting_semaphore prepareSignal<\/code> (1) can have the values 0 or 1. The concrete example initializes it with 0 (line 1). This means that the call <code>prepareSignal.release()<\/code> sets the value to 1 (line 2) and unblocks the call <code>prepareSignal.acquire()<\/code> (line 3).<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\" size-full wp-image-6067\" src=\"https:\/\/www.modernescpp.com\/wp-content\/uploads\/2021\/01\/threadSynchronizationSemaphore.png\" alt=\"threadSynchronizationSemaphore\" width=\"400\" height=\"457\" style=\"display: block; margin-left: auto; margin-right: auto;\" srcset=\"https:\/\/www.modernescpp.com\/wp-content\/uploads\/2021\/01\/threadSynchronizationSemaphore.png 499w, https:\/\/www.modernescpp.com\/wp-content\/uploads\/2021\/01\/threadSynchronizationSemaphore-263x300.png 263w\" sizes=\"auto, (max-width: 400px) 100vw, 400px\" \/><\/p>\n<p>Let me make a small performance test by playing ping-pong with semaphores.<\/p>\n<h2>A Ping-Pong Game<\/h2>\n<p>In my last post, &#8220;<a href=\"https:\/\/www.modernescpp.com\/index.php\/performancecomparison-of-condition-variables-and-atomics-in-c-20\">Performance Comparison of Condition Variables and Atomics in C++20<\/a>&#8220;, I implemented a ping-pong game. Here is the idea of the game: One thread executes a <code>ping<\/code> function and the other thread a <code>pong<\/code> function. <code><\/code> The ping thread waits for the notification of the pong thread and sends the notification back to the pong thread. The game stops after 1,000,000 ball changes. I perform each game five times to get comparable performance numbers. Let&#8217;s start the game:<\/p>\n<p>&nbsp;<\/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;\">\/\/ pingPongSemaphore.cpp<\/span>\r\n\r\n<span style=\"color: #009999;\">#include &lt;iostream&gt;<\/span>\r\n<span style=\"color: #009999;\">#include &lt;semaphore&gt;<\/span>\r\n<span style=\"color: #009999;\">#include &lt;thread&gt;<\/span>\r\n\r\nstd<span style=\"color: #555555;\">::<\/span>counting_semaphore<span style=\"color: #555555;\">&lt;<\/span><span style=\"color: #ff6600;\">1<\/span><span style=\"color: #555555;\">&gt;<\/span> signal2Ping(<span style=\"color: #ff6600;\">0<\/span>);       <span style=\"color: #0099ff; font-style: italic;\">\/\/ (1)<\/span>\r\nstd<span style=\"color: #555555;\">::<\/span>counting_semaphore<span style=\"color: #555555;\">&lt;<\/span><span style=\"color: #ff6600;\">1<\/span><span style=\"color: #555555;\">&gt;<\/span> signal2Pong(<span style=\"color: #ff6600;\">0<\/span>);       <span style=\"color: #0099ff; font-style: italic;\">\/\/ (2)<\/span>\r\n\r\nstd<span style=\"color: #555555;\">::<\/span>atomic<span style=\"color: #555555;\">&lt;<\/span><span style=\"color: #007788; font-weight: bold;\">int<\/span><span style=\"color: #555555;\">&gt;<\/span> counter{};\r\nconstexpr <span style=\"color: #007788; font-weight: bold;\">int<\/span> countlimit <span style=\"color: #555555;\">=<\/span> <span style=\"color: #ff6600;\">1<\/span><span style=\"color: #aa0000; background-color: #ffaaaa;\">'<\/span><span style=\"color: #ff6600;\">000<\/span><span style=\"color: #aa0000; background-color: #ffaaaa;\">'<\/span><span style=\"color: #ff6600;\">000<\/span>;\r\n\r\n<span style=\"color: #007788; font-weight: bold;\">void<\/span> <span style=\"color: #cc00ff;\">ping<\/span>() {\r\n    <span style=\"color: #006699; font-weight: bold;\">while<\/span>(counter <span style=\"color: #555555;\">&lt;=<\/span> countlimit) {\r\n        signal2Ping.acquire();                  <span style=\"color: #0099ff; font-style: italic;\">\/\/ (5)<\/span>\r\n        <span style=\"color: #555555;\">++<\/span>counter;\r\n        signal2Pong.release();\r\n    }\r\n}\r\n\r\n<span style=\"color: #007788; font-weight: bold;\">void<\/span> <span style=\"color: #cc00ff;\">pong<\/span>() {\r\n    <span style=\"color: #006699; font-weight: bold;\">while<\/span>(counter <span style=\"color: #555555;\">&lt;<\/span> countlimit) {\r\n        signal2Pong.acquire();\r\n        signal2Ping.release();                  <span style=\"color: #0099ff; font-style: italic;\">\/\/ (3)<\/span>\r\n    }\r\n}\r\n\r\n<span style=\"color: #007788; font-weight: bold;\">int<\/span> <span style=\"color: #cc00ff;\">main<\/span>() {\r\n\r\n    <span style=\"color: #006699; font-weight: bold;\">auto<\/span> start <span style=\"color: #555555;\">=<\/span> std<span style=\"color: #555555;\">::<\/span>chrono<span style=\"color: #555555;\">::<\/span>system_clock<span style=\"color: #555555;\">::<\/span>now();\r\n\r\n    signal2Ping.release();                     <span style=\"color: #0099ff; font-style: italic;\">\/\/ (4)<\/span>\r\n    std<span style=\"color: #555555;\">::<\/span><span style=\"color: #006699; font-weight: bold;\">thread<\/span> t1(ping);\r\n    std<span style=\"color: #555555;\">::<\/span><span style=\"color: #006699; font-weight: bold;\">thread<\/span> t2(pong);\r\n\r\n    t1.join();\r\n    t2.join();\r\n\r\n    std<span style=\"color: #555555;\">::<\/span>chrono<span style=\"color: #555555;\">::<\/span>duration<span style=\"color: #555555;\">&lt;<\/span><span style=\"color: #007788; font-weight: bold;\">double<\/span><span style=\"color: #555555;\">&gt;<\/span> dur <span style=\"color: #555555;\">=<\/span> std<span style=\"color: #555555;\">::<\/span>chrono<span style=\"color: #555555;\">::<\/span>system_clock<span style=\"color: #555555;\">::<\/span>now() <span style=\"color: #555555;\">-<\/span> start;\r\n    std<span style=\"color: #555555;\">::<\/span>cout <span style=\"color: #555555;\">&lt;&lt;<\/span> <span style=\"color: #cc3300;\">\"Duration: \"<\/span> <span style=\"color: #555555;\">&lt;&lt;<\/span> dur.count() <span style=\"color: #555555;\">&lt;&lt;<\/span> <span style=\"color: #cc3300;\">\" seconds\"<\/span> <span style=\"color: #555555;\">&lt;&lt;<\/span> <span style=\"color: #cc3300;\">'\\n'<\/span>;\r\n\r\n}\r\n<\/pre>\n<\/div>\n<p>&nbsp;<\/p>\n<p>The program<code> pingPongsemaphore.cpp<\/code> uses two semaphores: <code>signal2Ping<\/code> and <code>signal2Pong<\/code> (1 and 2). Both can have the two values 0 and 1 and are initialized with 0. This means when the value is 0 for the semaphore <code>signal2Ping,<\/code> a call <code>signal2Ping.release()<\/code> (3 and 4) set the value to 1 and is, therefore, a notification. A&nbsp; <code>signal2Ping.acquire()<\/code> (5) call blocks until the value becomes 1. The same argumentation holds for the second <code>semaphore signal2Pong<\/code>.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\" size-full wp-image-6068\" src=\"https:\/\/www.modernescpp.com\/wp-content\/uploads\/2021\/01\/pingPongSemaphore.png\" alt=\"pingPongSemaphore\" width=\"500\" height=\"415\" style=\"display: block; margin-left: auto; margin-right: auto;\" srcset=\"https:\/\/www.modernescpp.com\/wp-content\/uploads\/2021\/01\/pingPongSemaphore.png 1006w, https:\/\/www.modernescpp.com\/wp-content\/uploads\/2021\/01\/pingPongSemaphore-300x249.png 300w, https:\/\/www.modernescpp.com\/wp-content\/uploads\/2021\/01\/pingPongSemaphore-768x637.png 768w\" sizes=\"auto, (max-width: 500px) 100vw, 500px\" \/><\/p>\n<p>&nbsp;<\/p>\n<p>On average, the execution time is 0.33 seconds.<\/p>\n<p>Let me summarize the performance numbers for all ping-pong games. This includes the performance numbers of my last post, &#8220;<a href=\"https:\/\/www.modernescpp.com\/index.php\/performancecomparison-of-condition-variables-and-atomics-in-c-20\">Performance Comparison of Condition Variables and Atomics in C++20<\/a>&#8221; and this ping-pong game implemented with semaphores.<\/p>\n<h2>All Numbers<\/h2>\n<p>Condition variables are the slowest way, and atomic flag the fastest way to synchronize threads. The performance of a <code>std::atomic<\/code> is in between. There is one downside with <code>std::atomic<\/code>. <code>std::atomic_flag<\/code> is the only atomic data type that is always lock-free. Semaphores impressed me most because they are nearly as fast as atomic flags.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\" size-full wp-image-6069\" src=\"https:\/\/www.modernescpp.com\/wp-content\/uploads\/2021\/01\/PerformanceNumbers.png\" alt=\"PerformanceNumbers\" width=\"650\" height=\"102\" style=\"display: block; margin-left: auto; margin-right: auto;\" srcset=\"https:\/\/www.modernescpp.com\/wp-content\/uploads\/2021\/01\/PerformanceNumbers.png 825w, https:\/\/www.modernescpp.com\/wp-content\/uploads\/2021\/01\/PerformanceNumbers-300x47.png 300w, https:\/\/www.modernescpp.com\/wp-content\/uploads\/2021\/01\/PerformanceNumbers-768x121.png 768w\" sizes=\"auto, (max-width: 650px) 100vw, 650px\" \/><\/p>\n<h2>What&#8217;s next?<\/h2>\n<p>With latches and barriers, we have more convenient coordination types in C++20. Let me present them in my <a href=\"https:\/\/www.modernescpp.com\/index.php\/latches-in-c-20\">next post<\/a>.<\/p>\n<p>&nbsp;<\/p>\n<\/p>\n<p>&nbsp;<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Semaphores are a synchronization mechanism used to control concurrent access to a shared resource. They also allow it to play ping-pong.<\/p>\n","protected":false},"author":21,"featured_media":5199,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[375],"tags":[431],"class_list":["post-6070","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-c-20","tag-semaphores"],"_links":{"self":[{"href":"https:\/\/www.modernescpp.com\/index.php\/wp-json\/wp\/v2\/posts\/6070","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=6070"}],"version-history":[{"count":1,"href":"https:\/\/www.modernescpp.com\/index.php\/wp-json\/wp\/v2\/posts\/6070\/revisions"}],"predecessor-version":[{"id":6717,"href":"https:\/\/www.modernescpp.com\/index.php\/wp-json\/wp\/v2\/posts\/6070\/revisions\/6717"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.modernescpp.com\/index.php\/wp-json\/wp\/v2\/media\/5199"}],"wp:attachment":[{"href":"https:\/\/www.modernescpp.com\/index.php\/wp-json\/wp\/v2\/media?parent=6070"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.modernescpp.com\/index.php\/wp-json\/wp\/v2\/categories?post=6070"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.modernescpp.com\/index.php\/wp-json\/wp\/v2\/tags?post=6070"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}