{"id":5168,"date":"2017-02-05T17:03:48","date_gmt":"2017-02-05T17:03:48","guid":{"rendered":"https:\/\/www.modernescpp.com\/index.php\/the-new-ranges-library\/"},"modified":"2023-06-26T12:24:26","modified_gmt":"2023-06-26T12:24:26","slug":"the-new-ranges-library","status":"publish","type":"post","link":"https:\/\/www.modernescpp.com\/index.php\/the-new-ranges-library\/","title":{"rendered":"The New Ranges Library"},"content":{"rendered":"<p>A small-time jump, and we are in the year 2020. C++ will get &#8211; as far as the future is predictable &#8211; the new ranges library. Thanks to Eric Nieblers library, working with the Standard Template Library (STL) will become more comfortable and powerful.<\/p>\n<p>&nbsp;<\/p>\n<p><!--more--><\/p>\n<p>&nbsp;<\/p>\n<p>Much more comfortable because the algorithm of the STL can directly work on the containers and need no begin and end iterator. We get more powerful with the ranges library lazy evaluation, improved function composition, and range comprehension in C++20.<\/p>\n<p>Firstly, let me talk about the point more comfortably.<\/p>\n<h2>More comfortable<\/h2>\n<p>Iterators are the glue between the generic algorithms and containers of the STL. Therefore, each container returns on request an iterator pointing to the first element and an iterator pointing just behind the last element of the container. The half-open range interval defines the range of the algorithm. Thanks to the ranges library, you no longer need a half-open interval in C++20. You can directly apply the algorithm to the container:<\/p>\n<p>&nbsp;<\/p>\n<p><!-- HTML generated using hilite.me --><\/p>\n<div style=\"background: #ffffff; overflow: auto; width: auto; gray;border-width: .1em .1em .1em .8em;\">\n<pre style=\"margin: 0; line-height: 125%;\">std::vector&lt;<span style=\"color: #2b91af;\">int<\/span>&gt; vec{2, 5, 3, 1, 4};\r\nstd::sort(vec.begin(), vec.end());   \r\nstd::sort(vec);                   <span style=\"color: #008000;\">\/\/ C++20<br \/><\/span>\r\n<\/pre>\n<\/div>\n<p>&nbsp;<\/p>\n<p>Now, I will write about the functional features in the ranges library.<\/p>\n<\/p>\n<h2>Lazy evaluation<\/h2>\n<p>Haskell is, by default, lazy (see <a href=\"https:\/\/www.modernescpp.com\/index.php\/recursion-list-manipulation-and-lazy-evaluation\">Recursion, List Manipulation, and Lazy Evaluation<\/a>). Due to lazy evaluation, you can define in the Haskell algorithm on infinite data structures if you only ask for a finite number of elements at runtime. Therefore, you can separate the algorithm for calculating the infinite data structure from its usage.<\/p>\n<p>Lazy evaluation is one of the characteristics of functional programming. With C++20, we will get it. Thanks to lazy evaluation, I can directly translate the Haskell code in the example into C++:<\/p>\n<p>&nbsp;<\/p>\n<p><!-- HTML generated using hilite.me --><\/p>\n<div style=\"background: #ffffff; overflow: auto; width: auto; gray;border-width: .1em .1em .1em .8em;\">\n<table>\n<tbody>\n<tr>\n<td>\n<pre style=\"margin: 0; line-height: 125%;\"> 1\r\n 2\r\n 3\r\n 4\r\n 5\r\n 6\r\n 7\r\n 8\r\n 9\r\n10\r\n11\r\n12\r\n13\r\n14\r\n15\r\n16\r\n17\r\n18\r\n19\r\n20\r\n21\r\n22\r\n23\r\n24\r\n25\r\n26<\/pre>\n<\/td>\n<td>\n<pre style=\"margin: 0; line-height: 125%;\"><span style=\"color: #008000;\">\/\/ lazyEvaluation.cpp<\/span>\r\n\r\n<span style=\"color: #0000ff;\">#include &lt;range\/v3\/all.hpp&gt;<\/span>\r\n<span style=\"color: #0000ff;\">#include &lt;iostream&gt;<\/span>\r\n<span style=\"color: #0000ff;\">#include &lt;tuple&gt;<\/span>\r\n\r\n<span style=\"color: #0000ff;\">using<\/span> <span style=\"color: #0000ff;\">namespace<\/span> ranges;\r\n\r\n<span style=\"color: #2b91af;\">int<\/span> main(){\r\n\r\n  std::cout &lt;&lt; std::endl;\r\n\r\n  <span style=\"color: #008000;\">\/\/ take 5 [1..]  -- [1,2,3,4,5]<\/span>\r\n\r\n  <span style=\"color: #0000ff;\">auto<\/span> num = view::take(view::ints(1), 5);\r\n  ranges::for_each(num, [](<span style=\"color: #2b91af;\">int<\/span> i){ std::cout &lt;&lt; i &lt;&lt; <span style=\"color: #a31515;\">\" \"<\/span>; });\r\n\r\n  std::cout &lt;&lt; <span style=\"color: #a31515;\">\"\\n\\n\"<\/span>;\r\n\r\n  <span style=\"color: #0000ff;\">auto<\/span> pairs= view::zip_with([](<span style=\"color: #2b91af;\">int<\/span> i, <span style=\"color: #2b91af;\">char<\/span> c){ <span style=\"color: #0000ff;\">return<\/span> std::make_pair(i, c); } , view::ints(0), <span style=\"color: #a31515;\">\"ranges\"<\/span>);\r\n\r\n  ranges::for_each(pairs, [](std::pair&lt;<span style=\"color: #2b91af;\">int<\/span>,<span style=\"color: #2b91af;\">char<\/span>&gt; p){ std::cout &lt;&lt; <span style=\"color: #a31515;\">\"(\"<\/span> &lt;&lt;  p.first &lt;&lt; <span style=\"color: #a31515;\">\":\"<\/span> &lt;&lt; p.second &lt;&lt; <span style=\"color: #a31515;\">\")\"<\/span>; });\r\n\r\n  std::cout &lt;&lt; <span style=\"color: #a31515;\">\"\\n\\n\"<\/span>;\r\n\r\n}\r\n<\/pre>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<\/div>\n<p>&nbsp;<\/p>\n<p><span style=\"font-family: courier new,courier;\">view::ints(1)<\/span> in line 15 creates an infinite sequence of numbers starting with 1. The key is that I&#8217;m only asking for 5 of them. Using a lambda function, the numbers will be displayed in the following for_each loop. Admittedly, that is not so elegant. Firstly, I can implement the algorithm more elegantly with function composition: <span style=\"font-family: courier new,courier;\">auto num= view::ints(1) | view::take(5)<\/span>. The details will follow in a few seconds. Secondly, the upcoming C++20 standard will support the output of ranges with the range-based for-loop: <span style=\"font-family: courier new,courier;\">for (n: num) std::cout &lt;&lt; num &lt;&lt; &#8221; &#8220;.<\/span><\/p>\n<p>The functional world-known function <span style=\"font-family: courier new,courier;\">view::zip_with <\/span>takes lists and a lambda function and zips the list with the help of a lambda function to a new list. Therefore, the lambda function in line 22 zips the infinite sequence of numbers starting with 0 with the finite string <span style=\"font-family: courier new,courier;\">&#8220;ranges&#8221;.<\/span> The result is a finite tuple. So you can refer to elements of the pairs by using the <span style=\"font-family: courier new,courier;\">first<\/span> or <span style=\"font-family: courier new,courier;\">second.<\/span><\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\" size-full wp-image-5164\" src=\"https:\/\/www.modernescpp.com\/wp-content\/uploads\/2017\/02\/lazy.png\" alt=\"lazy\" style=\"margin: 15px;\" width=\"353\" height=\"210\" srcset=\"https:\/\/www.modernescpp.com\/wp-content\/uploads\/2017\/02\/lazy.png 353w, https:\/\/www.modernescpp.com\/wp-content\/uploads\/2017\/02\/lazy-300x178.png 300w\" sizes=\"auto, (max-width: 353px) 100vw, 353px\" \/><\/p>\n<h2>Function composition<\/h2>\n<p>Function composition in Haskell feels like putting Lego stones together. The newly created functions express their functionality very concisely and are readable for the used programmer, like prose. In Haskell, the power of function composition is based on three pillars:<\/p>\n<p>Haskell&#8217;s functions<\/p>\n<ol>\n<li>perform <strong>one task,<\/strong><\/li>\n<li>use the <strong>list as the essential data structure <\/strong>and<\/li>\n<li>use the <strong>(.) for function composition.<\/strong><\/li>\n<\/ol>\n<p>The story is similar to the ranges library. The library has a <a href=\"https:\/\/ericniebler.github.io\/range-v3\/index.html#range-views\">rich set of functions<\/a>. These functions are inspired by Haskell, which works on the key data structure range and uses the, from the Unix shell or Windows PowerShell known, pipe symbol (|) for function composition.<\/p>\n<p>Once more, I show how you can implement Haskell code in C++20:<\/p>\n<p>&nbsp;<\/p>\n<p><!-- HTML generated using hilite.me --><\/p>\n<div style=\"background: #ffffff; overflow: auto; width: auto; gray;border-width: .1em .1em .1em .8em;\">\n<table>\n<tbody>\n<tr>\n<td>\n<pre style=\"margin: 0; line-height: 125%;\"> 1\r\n 2\r\n 3\r\n 4\r\n 5\r\n 6\r\n 7\r\n 8\r\n 9\r\n10\r\n11\r\n12\r\n13\r\n14\r\n15\r\n16\r\n17\r\n18\r\n19\r\n20\r\n21\r\n22\r\n23\r\n24\r\n25\r\n26\r\n27\r\n28\r\n29\r\n30\r\n31\r\n32\r\n33\r\n34\r\n35<\/pre>\n<\/td>\n<td>\n<pre style=\"margin: 0; line-height: 125%;\"><span style=\"color: #008000;\">\/\/ functionCompositionRanges.cpp<\/span>\r\n\r\n<span style=\"color: #0000ff;\">#include &lt;range\/v3\/all.hpp&gt;<\/span>\r\n<span style=\"color: #0000ff;\">#include &lt;numeric&gt;<\/span>\r\n<span style=\"color: #0000ff;\">#include &lt;iostream&gt;<\/span>\r\n\r\n<span style=\"color: #0000ff;\">using<\/span> <span style=\"color: #0000ff;\">namespace<\/span> ranges;\r\n\r\n<span style=\"color: #2b91af;\">int<\/span> main(){\r\n\r\n  std::cout &lt;&lt; std::endl;\r\n\r\n  <span style=\"color: #008000;\">\/\/ odds= takeWhile (&lt; 1000) . filter odd . map (^2)<\/span>\r\n  <span style=\"color: #008000;\">\/\/ odds [1..]                 -- [1,9,25, ... , 841,961]<\/span>\r\n\r\n  <span style=\"color: #0000ff;\">auto<\/span> odds= view::transform([](<span style=\"color: #2b91af;\">int<\/span> i){ <span style=\"color: #0000ff;\">return<\/span> i*i;} ) |\r\n             view::remove_if([](<span style=\"color: #2b91af;\">int<\/span> i){ <span style=\"color: #0000ff;\">return<\/span> i % 2 == 0; } ) |\r\n             view::take_while([](<span style=\"color: #2b91af;\">int<\/span> i){ <span style=\"color: #0000ff;\">return<\/span> i &lt; 1000;} );\r\n  <span style=\"color: #0000ff;\">auto<\/span> oddNumbers= view::ints(1) | odds;\r\n\r\n  ranges::for_each(oddNumbers,[](<span style=\"color: #2b91af;\">int<\/span> i){ std::cout &lt;&lt; i &lt;&lt; <span style=\"color: #a31515;\">\" \"<\/span>; });\r\n\r\n  std::cout &lt;&lt; <span style=\"color: #a31515;\">\"\\n\\n\"<\/span>;\r\n\r\n  <span style=\"color: #008000;\">\/\/ total= sum $ take 100 $ map (\\x -&gt; x*x) [100..1000] -- 2318350<\/span>\r\n\r\n  <span style=\"color: #0000ff;\">auto<\/span> total= ranges::accumulate(view::ints(100, 1000) |\r\n                                 view::transform([](<span style=\"color: #2b91af;\">int<\/span> x){ <span style=\"color: #0000ff;\">return<\/span> x*x; }) |\r\n                                 view::take(100), 0);\r\n\r\n  std::cout &lt;&lt; <span style=\"color: #a31515;\">\"total: \"<\/span> &lt;&lt; total &lt;&lt; std::endl;\r\n\r\n  std::cout &lt;&lt; std::endl;\r\n\r\n}\r\n<\/pre>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<\/div>\n<p>&nbsp;<\/p>\n<p>The evaluation order of function composition is in Haskell (line 13) from right to left; in C++ (lines 16 &#8211; 19) from left to right. The C++ expression in lines 27 &#8211; 29 is not so easy to digest. It accumulates (<span style=\"font-family: courier new,courier;\">ranges::accumulate<\/span>) the first 100 numbers (<span style=\"font-family: courier new,courier;\">view::take(100)<\/span>) from the numbers from 100 &#8211; 1000 (<span style=\"font-family: courier new,courier;\">view::ints(100,1000<\/span>)) by mapping each number to its square (<span style=\"font-family: courier new,courier;\">view::transform([](int x){ return x*x; }<\/span>). The start value is 0.<\/p>\n<p>Here is the output of the program.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\" size-full wp-image-5165\" src=\"https:\/\/www.modernescpp.com\/wp-content\/uploads\/2017\/02\/composition.png\" alt=\"composition\" style=\"margin: 15px;\" width=\"501\" height=\"210\" srcset=\"https:\/\/www.modernescpp.com\/wp-content\/uploads\/2017\/02\/composition.png 501w, https:\/\/www.modernescpp.com\/wp-content\/uploads\/2017\/02\/composition-300x126.png 300w\" sizes=\"auto, (max-width: 501px) 100vw, 501px\" \/><\/p>\n<h2>Range comprehension<\/h2>\n<p>List comprehension is <a href=\"https:\/\/en.wikipedia.org\/wiki\/Syntactic_sugar\">syntactic sugar<\/a> of the sweetest kind for the functional algorithm <a href=\"https:\/\/www.modernescpp.com\/index.php\/higher-order-functions\">map and filter <\/a>and allows you to create new lists at runtime. The functional programming language Miranda introduced list comprehension; Haskell made them known. It is not an accident that list comprehension looks like the mathematical notation for set comprehension because Haskell is based on mathematical concepts.<\/p>\n<p>The figure compares set comprehension in Mathematics with list comprehension in Haskell.<\/p>\n<p>&nbsp;<img loading=\"lazy\" decoding=\"async\" class=\" size-full wp-image-5166\" src=\"https:\/\/www.modernescpp.com\/wp-content\/uploads\/2017\/02\/setListComprehension.png\" alt=\"setListComprehension\" width=\"700\" height=\"96\" style=\"margin: 15px;\" srcset=\"https:\/\/www.modernescpp.com\/wp-content\/uploads\/2017\/02\/setListComprehension.png 820w, https:\/\/www.modernescpp.com\/wp-content\/uploads\/2017\/02\/setListComprehension-300x41.png 300w, https:\/\/www.modernescpp.com\/wp-content\/uploads\/2017\/02\/setListComprehension-768x105.png 768w\" sizes=\"auto, (max-width: 700px) 100vw, 700px\" \/><\/p>\n<p>The ranges library supports range comprehension. It&#8217;s not as sweet as list comprehension, but the functionality is comparable.<\/p>\n<p>&nbsp;<\/p>\n<p><!-- HTML generated using hilite.me --><\/p>\n<div style=\"background: #ffffff; overflow: auto; width: auto; gray;border-width: .1em .1em .1em .8em;\">\n<table>\n<tbody>\n<tr>\n<td>\n<pre style=\"margin: 0; line-height: 125%;\"> 1\r\n 2\r\n 3\r\n 4\r\n 5\r\n 6\r\n 7\r\n 8\r\n 9\r\n10\r\n11\r\n12\r\n13\r\n14\r\n15\r\n16\r\n17\r\n18\r\n19\r\n20\r\n21\r\n22\r\n23<\/pre>\n<\/td>\n<td>\n<pre style=\"margin: 0; line-height: 125%;\"><span style=\"color: #008000;\">\/\/ rangeComprehension.cpp<\/span>\r\n\r\n<span style=\"color: #0000ff;\">#include &lt;range\/v3\/all.hpp&gt;<\/span>\r\n<span style=\"color: #0000ff;\">#include &lt;iostream&gt;<\/span>\r\n\r\n<span style=\"color: #0000ff;\">using<\/span> <span style=\"color: #0000ff;\">namespace<\/span> ranges;\r\n\r\n<span style=\"color: #2b91af;\">int<\/span> main(){\r\n\r\n  std::cout &lt;&lt; std::endl;\r\n\r\n  <span style=\"color: #008000;\">\/\/ odds= [ x*x | x &lt;-[1..] , odd x  ]<\/span>\r\n  <span style=\"color: #008000;\">\/\/ takeWhile (&lt;1000) odds --  -- [1,9,25, ... , 841,961]<\/span>\r\n\r\n  <span style=\"color: #0000ff;\">auto<\/span> odds= view::ints(1) | view::for_each([](<span style=\"color: #2b91af;\">int<\/span> i){ <span style=\"color: #0000ff;\">return<\/span> yield_if(i%2 == 1, i*i); });\r\n\r\n  ranges::for_each(odds | view::take_while([](<span style=\"color: #2b91af;\">int<\/span> i){ <span style=\"color: #0000ff;\">return<\/span> i &lt; 1000; }) , [](<span style=\"color: #2b91af;\">int<\/span> i){\r\n    std::cout &lt;&lt; i &lt;&lt; <span style=\"color: #a31515;\">\" \"<\/span>;\r\n  });\r\n\r\n  std::cout &lt;&lt; <span style=\"color: #a31515;\">\"\\n\\n\"<\/span>;\r\n\r\n}\r\n<\/pre>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<\/div>\n<p>&nbsp;<\/p>\n<p>The list comprehension in Haskell (line 12) is very concise. I request the integers starting with 1(<span style=\"font-family: courier new,courier;\">x&lt;-[1..]<\/span>), keep the odd values (<span style=\"font-family: courier new,courier;\">odd x<\/span>), and map them to their square (<span style=\"font-family: courier new,courier;\">x*x<\/span>). The expression <span style=\"font-family: courier new,courier;\">odd<\/span> corresponds to the filter function and <span style=\"font-family: courier new,courier;\">x*x<\/span> to the map function. The list will be evaluated in line 13 as long as their elements are smaller than 1000 (<span style=\"font-family: courier new,courier;\">takeWhile(&lt;1000)<\/span>).<\/p>\n<p>I apply the same strategy in C++. The first argument of the <span style=\"font-family: courier new,courier;\">yield_if<\/span> function is the filter expression, and the second one is the map expression.<\/p>\n<p>In the end, the output of the program.<br \/><span style=\"font-family: courier new,courier;\"><\/span><\/p>\n<p>&nbsp;<img loading=\"lazy\" decoding=\"async\" class=\" size-full wp-image-5167\" src=\"https:\/\/www.modernescpp.com\/wp-content\/uploads\/2017\/02\/rangeComprhension.png\" alt=\"rangeComprhension\" style=\"margin: 15px;\" width=\"530\" height=\"183\" srcset=\"https:\/\/www.modernescpp.com\/wp-content\/uploads\/2017\/02\/rangeComprhension.png 530w, https:\/\/www.modernescpp.com\/wp-content\/uploads\/2017\/02\/rangeComprhension-300x104.png 300w\" sizes=\"auto, (max-width: 530px) 100vw, 530px\" \/><\/p>\n<p>If I use more generators for integers or filter expressions in a range of comprehension, the expression will become very difficult to understand. I present in the following example the Pythagorean Triple in Haskell and C++. The Pythagorean triple consists of three positive integers <span class=\"texhtml\">x<\/span>, <span class=\"texhtml\">y<\/span>, and <span class=\"texhtml\">z<\/span>, such that <span class=\"texhtml\">x<sup>2<\/sup> + y<sup>2<\/sup> = z<sup>2<\/sup><\/span>.<\/p>\n<p>&nbsp;<\/p>\n<p><!-- HTML generated using hilite.me --><\/p>\n<div style=\"background: #ffffff; overflow: auto; width: auto; gray;border-width: .1em .1em .1em .8em;\">\n<pre style=\"margin: 0; line-height: 125%;\">triples =[(x, y, z)|z &lt;-[1..], x &lt;-[1..z],y &lt;-[x..z] ,x^2 + y^2 == z^2]\r\n\r\n<span style=\"color: #0000ff;\">auto<\/span> triples = \r\n  view::for_each(view::ints(1),[](<span style=\"color: #2b91af;\">int<\/span> z){<br \/><span style=\"color: #0000ff;\">    return<\/span> view::for_each(view::ints(1, z),[=](<span style=\"color: #2b91af;\">int<\/span> x){\r\n      <span style=\"color: #0000ff;\">return<\/span> view::for_each(view::ints(x, z),[=](<span style=\"color: #2b91af;\">int<\/span> y){\r\n        <span style=\"color: #0000ff;\">return<\/span> yield_if(x*x + y*y == z*z, std::make_tuple(x, y, z));\r\n      });\r\n    });\r\n  });\r\n<\/pre>\n<\/div>\n<p>&nbsp;<\/p>\n<p>&nbsp;<\/p>\n<p>The <strong>view <\/strong>algorithms, such as <span style=\"font-family: courier new,courier;\">view::for_each<\/span> of the ranges library, are standard in that they are lightweight wrappers for the underlying range. They will only evaluate its arguments on request and can not change them. With <strong>actions<\/strong> such as (<span style=\"font-family: courier new,courier;\">action::remove_if<\/span>), the ranges library has a bunch of additional algorithms that can change their arguments and create new ranges. Contrary to the view algorithms, the action algorithms are not lazy. They are eager.<\/p>\n<h2>What&#8217;s next?<\/h2>\n<p>Eric Niebler used the type-traits library to make the range library&#8217;s algorithm type-safe. That is no longer necessary with C++20 because we get concepts. Now you know what I will write about in my <a href=\"https:\/\/www.modernescpp.com\/index.php\/concepts-lite\">next post<\/a>.<\/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>A small-time jump, and we are in the year 2020. C++ will get &#8211; as far as the future is predictable &#8211; the new ranges library. Thanks to Eric Nieblers library, working with the Standard Template Library (STL) will become more comfortable and powerful. &nbsp;<\/p>\n","protected":false},"author":21,"featured_media":5164,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[365],"tags":[508,413],"class_list":["post-5168","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-functional","tag-haskell","tag-ranges"],"_links":{"self":[{"href":"https:\/\/www.modernescpp.com\/index.php\/wp-json\/wp\/v2\/posts\/5168","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=5168"}],"version-history":[{"count":1,"href":"https:\/\/www.modernescpp.com\/index.php\/wp-json\/wp\/v2\/posts\/5168\/revisions"}],"predecessor-version":[{"id":6891,"href":"https:\/\/www.modernescpp.com\/index.php\/wp-json\/wp\/v2\/posts\/5168\/revisions\/6891"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.modernescpp.com\/index.php\/wp-json\/wp\/v2\/media\/5164"}],"wp:attachment":[{"href":"https:\/\/www.modernescpp.com\/index.php\/wp-json\/wp\/v2\/media?parent=5168"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.modernescpp.com\/index.php\/wp-json\/wp\/v2\/categories?post=5168"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.modernescpp.com\/index.php\/wp-json\/wp\/v2\/tags?post=5168"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}