{"id":5470,"date":"2018-07-06T12:43:33","date_gmt":"2018-07-06T12:43:33","guid":{"rendered":"https:\/\/www.modernescpp.com\/index.php\/c-core-guidelines-the-remaining-rules-to-lock-free-programming\/"},"modified":"2023-06-26T11:48:39","modified_gmt":"2023-06-26T11:48:39","slug":"c-core-guidelines-the-remaining-rules-to-lock-free-programming","status":"publish","type":"post","link":"https:\/\/www.modernescpp.com\/index.php\/c-core-guidelines-the-remaining-rules-to-lock-free-programming\/","title":{"rendered":"C++ Core Guidelines: The Remaining Rules about Lock-Free Programming"},"content":{"rendered":"<p>Today, I will finish my story on concurrency and lock-free programming. There are four rules to lock-free programming in the C++ core guidelines left.<\/p>\n<p><!--more--><\/p>\n<p>&nbsp;<img loading=\"lazy\" decoding=\"async\" class=\" size-full wp-image-5468\" src=\"https:\/\/www.modernescpp.com\/wp-content\/uploads\/2018\/07\/padlock.png\" alt=\"padlock\" width=\"300\" height=\"257\" style=\"display: block; margin-left: auto; margin-right: auto;\" srcset=\"https:\/\/www.modernescpp.com\/wp-content\/uploads\/2018\/07\/padlock.png 1280w, https:\/\/www.modernescpp.com\/wp-content\/uploads\/2018\/07\/padlock-300x257.png 300w, https:\/\/www.modernescpp.com\/wp-content\/uploads\/2018\/07\/padlock-1024x877.png 1024w, https:\/\/www.modernescpp.com\/wp-content\/uploads\/2018\/07\/padlock-768x658.png 768w\" sizes=\"auto, (max-width: 300px) 100vw, 300px\" \/><\/p>\n<p>First of all, here are the rules for the current post.<\/p>\n<ul>\n<li><a href=\"http:\/\/isocpp.github.io\/CppCoreGuidelines\/CppCoreGuidelines#Rconc-distrust\">CP.101: Distrust your hardware\/compiler combination<\/a><\/li>\n<li><a href=\"http:\/\/isocpp.github.io\/CppCoreGuidelines\/CppCoreGuidelines#Rconc-literature\">CP.102: Carefully study the literature<\/a><\/li>\n<li><a href=\"http:\/\/isocpp.github.io\/CppCoreGuidelines\/CppCoreGuidelines#Rconc-double\">CP.110: Do not write your own double-checked locking for initialization<\/a><\/li>\n<li><a href=\"http:\/\/isocpp.github.io\/CppCoreGuidelines\/CppCoreGuidelines#Rconc-double-pattern\">CP.111: Use a conventional pattern if you really need double-checked locking<\/a><\/li>\n<\/ul>\n<p>I must admit I annoyed a few German readers with my two last posts about lock-free programming. My readers got the impression that I wouldn&#8217;t say I like lock-free programming. Wrong!. I&#8217;m inquisitive about lock-free programming, but before you use it, you have to answer two questions.<\/p>\n<ol>\n<li>Does lock-free programming solve my performance bottleneck?<\/li>\n<li>Do I understand lock-free programming well enough to use it?<\/li>\n<\/ol>\n<p>Before you can not answer these two questions with a big yes, you should continue with the rule CP.102<\/p>\n<h3><a href=\"http:\/\/isocpp.github.io\/CppCoreGuidelines\/CppCoreGuidelines#Rconc-distrust\">CP.101: Distrust your hardware\/compiler combination<\/a><\/h3>\n<p>What does that mean: distrust your hardware\/compiler combination. Let me put it in another way: When you break the sequential consistency, you will also break your with high probability your intuition. Here is my example:<\/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: #009999;\">#include &lt;atomic&gt;<\/span>\r\n<span style=\"color: #009999;\">#include &lt;iostream&gt;<\/span>\r\n<span style=\"color: #009999;\">#include &lt;thread&gt;<\/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> x{<span style=\"color: #ff6600;\">0<\/span>};\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> y{<span style=\"color: #ff6600;\">0<\/span>};\r\n\r\n<span style=\"color: #007788; font-weight: bold;\">void<\/span> <span style=\"color: #cc00ff;\">writing<\/span>(){  \r\n  x.store(<span style=\"color: #ff6600;\">2000<\/span>);                        <span style=\"color: #0099ff; font-style: italic;\">\/\/ (1)<\/span>\r\n  y.store(<span style=\"color: #ff6600;\">11<\/span>);                          <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;\">reading<\/span>(){  \r\n  std<span style=\"color: #555555;\">::<\/span>cout <span style=\"color: #555555;\">&lt;&lt;<\/span> y.load() <span style=\"color: #555555;\">&lt;&lt;<\/span> <span style=\"color: #cc3300;\">\" \"<\/span>;         <span style=\"color: #0099ff; font-style: italic;\">\/\/ (3)<\/span>\r\n  std<span style=\"color: #555555;\">::<\/span>cout <span style=\"color: #555555;\">&lt;&lt;<\/span> x.load() <span style=\"color: #555555;\">&lt;&lt;<\/span> std<span style=\"color: #555555;\">::<\/span>endl;   <span style=\"color: #0099ff; font-style: italic;\">\/\/ (4)<\/span>\r\n}\r\n\r\n<span style=\"color: #007788; font-weight: bold;\">int<\/span> <span style=\"color: #cc00ff;\">main<\/span>(){\r\n  std<span style=\"color: #555555;\">::<\/span><span style=\"color: #006699; font-weight: bold;\">thread<\/span> thread1(writing);\r\n  std<span style=\"color: #555555;\">::<\/span><span style=\"color: #006699; font-weight: bold;\">thread<\/span> thread2(reading);\r\n  thread1.join();\r\n  thread2.join();\r\n}\r\n<\/pre>\n<\/div>\n<p>&nbsp;<\/p>\n<p>I have a question for the short example. Which values f\u00fcr <span style=\"font-family: courier new, courier;\">y<\/span> and <span style=\"font-family: courier new, courier;\">x<\/span> are possible in lines (3) and (4).<span style=\"font-family: courier new, courier;\"> x<\/span> and <span style=\"font-family: courier new, courier;\">y<\/span> are atomic, therefore, no data race is possible. I further don&#8217;t specify the memory ordering. Therefore, sequential consistency applies. Sequential consistency means:<\/p>\n<ul>\n<ul>\n<li>Each thread performs its operation in the specified sequence: line (1) happens before line (2), and line (3) happens before line(4).<\/li>\n<li>There is a global order of all operations on all threads.<\/li>\n<\/ul>\n<\/ul>\n<p>If you combine these two properties of sequential consistency, only one combination of x and y not possible: <span style=\"font-family: courier new, courier;\">y == 11<\/span> and <span style=\"font-family: courier new, courier;\">x == 0<\/span>.<\/p>\n<p>Now, let me break the sequential consistency and maybe your intuition. Here is the weakest of all memory orderings: the relaxed semantics.<\/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: #009999;\">#include &lt;atomic&gt;<\/span>\r\n<span style=\"color: #009999;\">#include &lt;iostream&gt;<\/span>\r\n<span style=\"color: #009999;\">#include &lt;thread&gt;<\/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> x{<span style=\"color: #ff6600;\">0<\/span>};\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> y{<span style=\"color: #ff6600;\">0<\/span>};\r\n\r\n<span style=\"color: #007788; font-weight: bold;\">void<\/span> <span style=\"color: #cc00ff;\">writing<\/span>(){  \r\n  x.store(<span style=\"color: #ff6600;\">2000<\/span>, std<span style=\"color: #555555;\">::<\/span>memory_order_relaxed);   <span style=\"color: #0099ff; font-style: italic;\">\/\/ (1)<\/span>\r\n  y.store(<span style=\"color: #ff6600;\">11<\/span>, std<span style=\"color: #555555;\">::<\/span>memory_order_relaxed);     <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;\">reading<\/span>(){  \r\n  std<span style=\"color: #555555;\">::<\/span>cout <span style=\"color: #555555;\">&lt;&lt;<\/span> y.load(std<span style=\"color: #555555;\">::<\/span>memory_order_relaxed) <span style=\"color: #555555;\">&lt;&lt;<\/span> <span style=\"color: #cc3300;\">\" \"<\/span>;        <span style=\"color: #0099ff; font-style: italic;\">\/\/ (3)<\/span>\r\n  std<span style=\"color: #555555;\">::<\/span>cout <span style=\"color: #555555;\">&lt;&lt;<\/span> x.load(std<span style=\"color: #555555;\">::<\/span>memory_order_relaxed) <span style=\"color: #555555;\">&lt;&lt;<\/span> std<span style=\"color: #555555;\">::<\/span>endl;  <span style=\"color: #0099ff; font-style: italic;\">\/\/ (4) <\/span>\r\n}\r\n\r\n<span style=\"color: #007788; font-weight: bold;\">int<\/span> <span style=\"color: #cc00ff;\">main<\/span>(){\r\n  std<span style=\"color: #555555;\">::<\/span><span style=\"color: #006699; font-weight: bold;\">thread<\/span> thread1(writing);\r\n  std<span style=\"color: #555555;\">::<\/span><span style=\"color: #006699; font-weight: bold;\">thread<\/span> thread2(reading);\r\n  thread1.join();\r\n  thread2.join();\r\n}\r\n<\/pre>\n<\/div>\n<p>&nbsp;<\/p>\n<p>Two unintuitive phenomena can happen. First, <span style=\"font-family: 'courier new', courier;\">thread2<\/span> can see the operations of <span style=\"font-family: 'courier new', courier;\">thread1<\/span> in a different sequence. Second, <span style=\"font-family: 'courier new', courier;\">thread1<\/span> can reorder its instruction because they are not performed on the same atomic.&nbsp; What does that mean for the possible values of x and y: <span style=\"font-family: courier new, courier;\">y == 11<\/span> and <span style=\"font-family: courier new, courier;\">x == 0 <\/span>is a valid result? I want to be a little bit more specific. Which result is possible depends on your hardware.<\/p>\n<p>For example, operation recording is quite conservative on x86 or AMD64, stores can be reordered after loads, but on Alpha, IA64, or RISC (ARM) architectures, all four possible reorderings of stores and loads operations are allowed.<\/p>\n<p>&nbsp;<img loading=\"lazy\" decoding=\"async\" class=\" size-full wp-image-5469\" src=\"https:\/\/www.modernescpp.com\/wp-content\/uploads\/2018\/07\/LoadStore.png\" alt=\"LoadStore\" width=\"500\" height=\"88\" style=\"display: block; margin-left: auto; margin-right: auto;\" srcset=\"https:\/\/www.modernescpp.com\/wp-content\/uploads\/2018\/07\/LoadStore.png 887w, https:\/\/www.modernescpp.com\/wp-content\/uploads\/2018\/07\/LoadStore-300x52.png 300w, https:\/\/www.modernescpp.com\/wp-content\/uploads\/2018\/07\/LoadStore-768x134.png 768w\" sizes=\"auto, (max-width: 500px) 100vw, 500px\" \/><\/p>\n<p>&nbsp;<\/p>\n<p>If you don&#8217;t believe me, I suggest you&nbsp;read the following rule CP.102.<\/p>\n<\/p>\n<h3><a href=\"http:\/\/isocpp.github.io\/CppCoreGuidelines\/CppCoreGuidelines#Rconc-literature\">CP.102: Carefully study the literature<\/a><\/h3>\n<p>There is not much to add to this rule. At least I can provide links to the literature.<\/p>\n<ul>\n<li><a href=\"https:\/\/www.manning.com\/books\/c-plus-plus-concurrency-in-action-second-edition\">Anthony Williams: C++ concurrency in action. Manning Publications.<\/a><\/li>\n<li><a href=\"https:\/\/queue.acm.org\/detail.cfm?searchterm=T&amp;id=2088916\">Boehm, Adve, You Don\u2019t Know Jack About Shared Variables or Memory Models, Communications of the ACM, Feb 2012.<\/a><\/li>\n<li><a href=\"http:\/\/www.hpl.hp.com\/techreports\/2009\/HPL-2009-259html.html\">Boehm, \u201cThreads Basics\u201d, HPL TR 2009-259.<\/a><\/li>\n<li><a href=\"https:\/\/cacm.acm.org\/magazines\/2010\/8\/96610-memory-models-a-case-for-rethinking-parallel-languages-and-hardware\/fulltext\">Adve, Boehm, \u201cMemory Models: A Case for Rethinking Parallel Languages and Hardware\u201d, Communications of the ACM, August 2010.<\/a><\/li>\n<li><a href=\"http:\/\/www.hpl.hp.com\/techreports\/2008\/HPL-2008-56.pdf\">Boehm, Adve, \u201cFoundations of the C++ Concurrency Memory Model\u201d, PLDI 08.<\/a><\/li>\n<li><a href=\"https:\/\/www.cl.cam.ac.uk\/~pes20\/cpp\/popl085ap-sewell.pdf\">Mark Batty, Scott Owens, Susmit Sarkar, Peter Sewell, and Tjark Weber, \u201cMathematizing C++ Concurrency\u201d, POPL 2011.<\/a><\/li>\n<li><a href=\"http:\/\/www.stroustrup.com\/isorc2010.pdf\">Damian Dechev, Peter Pirkelbauer, and Bjarne Stroustrup: Understanding and Effectively Preventing the ABA Problem in Descriptor-based Lock-free Designs. 13th IEEE Computer Society ISORC 2010 Symposium. May 2010<\/a>.<\/li>\n<li><a href=\"http:\/\/www.stroustrup.com\/oopsla09-nonblocking.pdf\">Damian Dechev and Bjarne Stroustrup: Scalable Non-blocking Concurrent Objects for Mission Critical Code. ACM OOPSLA\u201909. October 2009<\/a><\/li>\n<li><a href=\"http:\/\/www.stroustrup.com\/sec09.pdf\">Damian Dechev, Peter Pirkelbauer, Nicolas Rouquette, and Bjarne Stroustrup: Semantically Enhanced Containers for Concurrent Real-Time Systems. Proc. 16th Annual IEEE International Conference and Workshop on the Engineering of Computer Based Systems (IEEE ECBS). April 2009<\/a><\/li>\n<\/ul>\n<h3><a href=\"http:\/\/isocpp.github.io\/CppCoreGuidelines\/CppCoreGuidelines#Rconc-double\">CP.110: Do not write your own double-checked locking for initialization, <\/a>and<a href=\"http:\/\/isocpp.github.io\/CppCoreGuidelines\/CppCoreGuidelines#Rconc-double\"> <\/a><a href=\"http:\/\/isocpp.github.io\/CppCoreGuidelines\/CppCoreGuidelines#Rconc-double-pattern\">CP.111: Use a conventional pattern if you really need double-checked locking<\/a><\/h3>\n<p>I know I should not write about the singleton pattern, but the double-checked locking pattern is infamous for initializing a singleton in a thread-safe way. Here we are:<\/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%;\">std::mutex myMutex;\r\n\r\n<span style=\"color: #006699; font-weight: bold;\">class<\/span> <span style=\"color: #00aa88; font-weight: bold;\">MySingleton<\/span>{\r\n<span style=\"color: #9999ff;\">public:<\/span>  \r\n  <span style=\"color: #006699; font-weight: bold;\">static<\/span> MySingleton<span style=\"color: #555555;\">&amp;<\/span> getInstance(){    \r\n    std::lock_guard<span style=\"color: #555555;\">&lt;std::<\/span>mutex<span style=\"color: #555555;\">&gt;<\/span> myLock(myMutex);       <span style=\"color: #0099ff; font-style: italic;\">\/\/ (1)  <\/span>\r\n    <span style=\"color: #006699; font-weight: bold;\">if<\/span>( <span style=\"color: #555555;\">!<\/span>instance ) instance<span style=\"color: #555555;\">=<\/span> <span style=\"color: #006699; font-weight: bold;\">new<\/span> MySingleton();    \r\n    <span style=\"color: #006699; font-weight: bold;\">return<\/span> <span style=\"color: #555555;\">*<\/span>instance;  \r\n  }\r\n<span style=\"color: #9999ff;\">private:<\/span>  \r\n  MySingleton();  \r\n  <span style=\"color: #555555;\">~<\/span>MySingleton();  \r\n  MySingleton(<span style=\"color: #006699; font-weight: bold;\">const<\/span> MySingleton<span style=\"color: #555555;\">&amp;<\/span>)<span style=\"color: #555555;\">=<\/span> <span style=\"color: #006699; font-weight: bold;\">delete<\/span>;  \r\n  MySingleton<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> MySingleton<span style=\"color: #555555;\">&amp;<\/span>)<span style=\"color: #555555;\">=<\/span> <span style=\"color: #006699; font-weight: bold;\">delete<\/span>;\r\n  <span style=\"color: #006699; font-weight: bold;\">static<\/span> MySingleton<span style=\"color: #555555;\">*<\/span> instance;\r\n};\r\nMySingleton<span style=\"color: #555555;\">::<\/span>MySingleton()<span style=\"color: #555555;\">=<\/span> <span style=\"color: #006699; font-weight: bold;\">default<\/span>;\r\nMySingleton<span style=\"color: #555555;\">::~<\/span>MySingleton()<span style=\"color: #555555;\">=<\/span> <span style=\"color: #006699; font-weight: bold;\">default<\/span>;\r\nMySingleton<span style=\"color: #555555;\">*<\/span> MySingleton<span style=\"color: #555555;\">::<\/span>instance<span style=\"color: #555555;\">=<\/span> nullptr;\r\n<\/pre>\n<\/div>\n<p>&nbsp;<\/p>\n<p>This singleton pattern implementation is thread-safe because each access to the instance is protected by a std<span style=\"font-family: courier new, courier;\">::lock_guard <\/span>(line (1)). The implementation is correct but to expensive, because a heavy-weight lock guards each reading access of the singleton. Besides the initialization of the singleton, no synchronization is necessary. Here comes the double-checked locking pattern to our rescue.<\/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;\">static<\/span> MySingleton<span style=\"color: #555555;\">&amp;<\/span> getInstance(){    \r\n  <span style=\"color: #006699; font-weight: bold;\">if<\/span> ( <span style=\"color: #555555;\">!<\/span>instance ){                              <span style=\"color: #0099ff; font-style: italic;\">\/\/ (1)               <\/span>\r\n    lock_guard<span style=\"color: #555555;\">&lt;<\/span>mutex<span style=\"color: #555555;\">&gt;<\/span> myLock(myMutex);           <span style=\"color: #0099ff; font-style: italic;\">\/\/ (2)   <\/span>\r\n    <span style=\"color: #006699; font-weight: bold;\">if<\/span>( <span style=\"color: #555555;\">!<\/span>instance ) instance<span style=\"color: #555555;\">=<\/span> <span style=\"color: #006699; font-weight: bold;\">new<\/span> MySingleton(); <span style=\"color: #0099ff; font-style: italic;\">\/\/ (3)<\/span>\r\n  }  \r\n  <span style=\"color: #006699; font-weight: bold;\">return<\/span> <span style=\"color: #555555;\">*<\/span>instance; \r\n}\r\n<\/pre>\n<\/div>\n<p>&nbsp;<\/p>\n<p>The <span style=\"font-family: courier new, courier;\">getInstance<\/span> method uses an inexpensive pointer comparison in line (1) instead of an expensive lock. Only if the pointer is a <span style=\"font-family: courier new, courier;\">nullptr,<\/span> an expensive lock is used (line (2)). Because there is the possibility that another thread will initialize the singleton between the pointer comparison (line (1)) and the lock (line (2)), an additional pointer comparison in line (3) is necessary. So the name is prominent. Two times a check and one time a lock.<\/p>\n<p>Smart? Yes! Thread-safe? No!<\/p>\n<p>What is the problem? The call <span style=\"font-family: courier new, courier;\">instance= new MySingleton()<\/span> inline (3) consists of at least three steps.<\/p>\n<ol>\n<li>Allocate memory for <span style=\"font-family: 'courier new', courier;\">MySingleton<\/span><\/li>\n<li>Create the <span style=\"font-family: 'courier new', courier;\">MySingleton<\/span> object in the memory<\/li>\n<li>Let <span style=\"font-family: courier new, courier;\">instance<\/span> refer to the <span style=\"font-family: courier new, courier;\">MySingleton<\/span> object<\/li>\n<\/ol>\n<p>The problem is that there is no guarantee about the sequence of these three steps. For example, the processor can reorder the steps to sequence 1,3, and 2. So, in the first step, the memory will be allocated; in the second step, the<span style=\"font-family: courier new, courier;\">&nbsp;instance<\/span> refers to the singleton. If another thread tries to access the singleton at that time, it compares the pointer and assumes that the singleton is fully initialized. <span style=\"font-family: courier new, courier;\"><\/span><\/p>\n<p>The consequence is simple: the program has undefined behavior.<\/p>\n<p>I have already written a quite emotionally discussed post to the thread-safe singleton pattern. This included different implementations with <span style=\"font-family: courier new, courier;\">std::lock_guard<\/span>, <span style=\"font-family: courier new, courier;\">std::call_once<\/span>, and <span style=\"font-family: courier new, courier;\">std::once_flag,<\/span> the Meyers singleton, and atomic versions based on the double-checked locking pattern. You can read the details of these implementations and their different performance characteristics on Linux and Windows here:&nbsp; <a href=\"https:\/\/www.modernescpp.com\/index.php\/thread-safe-initialization-of-a-singleton\">Thread-Safe Initialization of a Singleton<\/a>.<\/p>\n<h2>What&#8217;s next?<\/h2>\n<p>As I promised, I&#8217;m done with the rules of concurrency. The <a href=\"https:\/\/goo.gl\/h5XXGq\">next post<\/a> is about the rules for error handling in the C++ core guidelines.<\/p>\n<p>&nbsp;<\/p>\n<p>&nbsp;<\/p>\n<p>&nbsp;<\/p>\n<p>&nbsp;<\/p>\n<p>&nbsp;<\/p>\n<p>&nbsp;<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Today, I will finish my story on concurrency and lock-free programming. There are four rules to lock-free programming in the C++ core guidelines left.<\/p>\n","protected":false},"author":21,"featured_media":5468,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[372],"tags":[483],"class_list":["post-5470","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-modern-c","tag-lock-free"],"_links":{"self":[{"href":"https:\/\/www.modernescpp.com\/index.php\/wp-json\/wp\/v2\/posts\/5470","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=5470"}],"version-history":[{"count":1,"href":"https:\/\/www.modernescpp.com\/index.php\/wp-json\/wp\/v2\/posts\/5470\/revisions"}],"predecessor-version":[{"id":6820,"href":"https:\/\/www.modernescpp.com\/index.php\/wp-json\/wp\/v2\/posts\/5470\/revisions\/6820"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.modernescpp.com\/index.php\/wp-json\/wp\/v2\/media\/5468"}],"wp:attachment":[{"href":"https:\/\/www.modernescpp.com\/index.php\/wp-json\/wp\/v2\/media?parent=5470"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.modernescpp.com\/index.php\/wp-json\/wp\/v2\/categories?post=5470"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.modernescpp.com\/index.php\/wp-json\/wp\/v2\/tags?post=5470"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}