{"id":103903,"date":"2020-06-25T07:00:00","date_gmt":"2020-06-25T14:00:00","guid":{"rendered":"https:\/\/devblogs.microsoft.com\/oldnewthing\/?p=103890"},"modified":"2020-07-02T06:50:16","modified_gmt":"2020-07-02T13:50:16","slug":"20200625-00-2","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20200625-00\/?p=103903","title":{"rendered":"Mundane std::tuple tricks: Creating interesting index sequences"},"content":{"rendered":"<p>We saw last time that <a href=\"https:\/\/devblogs.microsoft.com\/oldnewthing\/20200624-00\/?p=103886\"> manipulating tuples boils down to the <code>index_sequence<\/code><\/a>. The problem is that the C++ standard library doesn&#8217;t provide very much in the way of helpers to manipulate these index sequences.<\/p>\n<p>The only ones that come with the standard library are <code>make_integer_sequence<\/code>, a helper for generating the integer sequence <code>0, 1, 2, \u2026, N-1<\/code>, and its close relative <code>make_index_sequence<\/code>, which does the same thing for <code>size_t<\/code>.<\/p>\n<p>Note that the template parameter to <code>make_index_sequence<\/code> is the size of the resulting index sequence, not the highest value in the index sequence. The highest value is one less than the size since the sequence starts at zero.<\/p>\n<p>Even with only index sequences that start at zero, we can do something interesting.<\/p>\n<pre>template&lt;typename Tuple&gt;\r\nauto remove_last(Tuple&amp;&amp; tuple)\r\n{\r\n  constexpr auto size = std::tuple_size_v&lt;Tuple&gt;;\r\n  using indices = std::make_index_sequence&lt;size-1&gt;;\r\n  return select_tuple(std::forward&lt;Tuple&gt;(tuple), indices{});\r\n}\r\n<\/pre>\n<p>The <code>remove_last<\/code> function removes the last element from the tuple and returns what&#8217;s left. We do this by extracting the size of the source tuple, subtracting one, and using that to generate a new index sequence that goes from 0 to <code>size<\/code> \u2212 2, for a total of <code>size<\/code> \u2212 1 elements.<\/p>\n<p>Okay, but what about other index sequences? We&#8217;ll have to build those ourselves.<\/p>\n<pre>template&lt;std::size_t N, typename Seq&gt; struct offset_sequence;\r\n\r\ntemplate&lt;std::size_t N, std::size_t... Ints&gt;\r\nstruct offset_sequence&lt;N, std::index_sequence&lt;Ints...&gt;&gt;\r\n{\r\n using type = std::index_sequence&lt;Ints + N...&gt;;\r\n};\r\ntemplate&lt;std::size_t N, typename Seq&gt;\r\nusing offset_sequence_t = typename offset_sequence&lt;N, Seq&gt;::type;\r\n\r\n\/\/ example = index_sequence&lt;3, 4, 5, 6&gt;\r\nusing example = offset_sequence_t&lt;3, std::make_index_sequence&lt;4&gt;&gt;;\r\n<\/pre>\n<p>To offset an index sequence, we generate a new index sequence whose integers are the old integers plus the provided offset value <var>N<\/var>. The magic happens in the template parameter pack expansion:<\/p>\n<pre> using type = std::index_sequence&lt;Ints + N...&gt;;\r\n<\/pre>\n<p>This takes each of the integers in the original index sequence, adds <var>N<\/var> to each one, and uses the results to build a new index sequence.<\/p>\n<p>Now we can remove the <i>first<\/i> element from the tuple.<\/p>\n<pre>template&lt;typename Tuple&gt;\r\nauto remove_first(Tuple&amp;&amp; tuple)\r\n{\r\n  constexpr auto size = std::tuple_size_v&lt;Tuple&gt;;\r\n  using indices = offset_sequence_t&lt;1,\r\n                    std::make_index_sequence&lt;size-1&gt;&gt;;\r\n  return select_tuple(std::forward&lt;Tuple&gt;(tuple), indices{});\r\n}\r\n<\/pre>\n<p>And in fact, we can remove the <var>N<\/var>th element.<\/p>\n<pre>template&lt;std::size_t N, typename Tuple&gt;\r\nauto remove_Nth(Tuple&amp;&amp; tuple)\r\n{\r\n  constexpr auto size = std::tuple_size_v&lt;Tuple&gt;;\r\n  using first = std::make_index_sequence&lt;N&gt;;\r\n  using rest = offset_sequence_t&lt;N+1,\r\n                std::make_index_sequence&lt;size-N-1&gt;&gt;;\r\n  return std::tuple_cat(\r\n    select_tuple(std::forward&lt;Tuple&gt;(tuple), first{}),\r\n    select_tuple(std::forward&lt;Tuple&gt;(tuple), rest{}));\r\n}\r\n<\/pre>\n<p>What we want to do is extract the first <var>N<\/var> elements, skip element <var>N<\/var>, and then extract elements <var>N<\/var> + 1 to the end.<\/p>\n<p>Extracting the first <var>N<\/var> is easy: We select from the index sequence that goes from 0 to <var>N<\/var> \u2212 1.<\/p>\n<p>Extracting the rest of the elements is trickier: We want to start at <var>N<\/var> + 1 and continue until <code>size<\/code> \u2212 1, for a length of (<code>size<\/code> \u2212 1) \u2212 (<var>N<\/var> + 1) + 1 = <code>size<\/code> \u2212 <var>N<\/var> \u2212 1. We generate an index sequence of length <code>size<\/code> \u2212 <var>N<\/var> \u2212 1 (starting at zero), then add <var>N<\/var> + 1 to each element, to bring us to the desired sequence.<\/p>\n<p>We call <code>select_tuple<\/code> twice, once to get the front part, once to get the back part, and then use <code>tuple_cat<\/code> to put them together.<\/p>\n<p>Another way to do this is with a single selection. For that, we need a way to concatenate two index sequences.<\/p>\n<pre>template&lt;typename Seq1, typename Seq&gt; struct cat_sequence;\r\n\r\ntemplate&lt;std::size_t... Ints1, std::size_t... Ints2&gt;\r\nstruct cat_sequence&lt;std::index_sequence&lt;Ints1...&gt;,\r\n                    std::index_sequence&lt;Ints2...&gt;&gt;\r\n{\r\n using type = std::index_sequence&lt;Ints1..., Ints2...&gt;;\r\n};\r\ntemplate&lt;typename Seq1, typename Seq2&gt;\r\nusing cat_sequence_t = typename cat_sequence&lt;Seq1, Seq2&gt;::type;\r\n\r\n\/\/ example = index_sequence&lt;3, 1, 4, 1, 5, 9&gt;\r\nusing example = cat_sequence_t&lt;std::index_sequence&lt;3, 1, 4&gt;,\r\n                               std::index_sequence&lt;1, 5, 9&gt;&gt;;\r\n<\/pre>\n<p>The magic happens at<\/p>\n<pre> using type = std::index_sequence&lt;Ints1..., Ints2...&gt;;\r\n<\/pre>\n<p>which takes the two sequences and places them one after the other into a single index sequence.<\/p>\n<p>We can now use this alternate formulation:<\/p>\n<pre>template&lt;std::size_t N, typename Tuple&gt;\r\nauto remove_Nth(Tuple&amp;&amp; tuple)\r\n{\r\n  constexpr auto size = std::tuple_size_v&lt;Tuple&gt;;\r\n  using first = std::make_index_sequence&lt;N&gt;;\r\n  using rest = offset_sequence_t&lt;N+1,\r\n                std::make_index_sequence&lt;size-N-1&gt;&gt;;\r\n  <span style=\"color: blue;\">using indices = cat_sequence_t&lt;first, rest&gt;;\r\n  return select_tuple(std::forward&lt;Tuple&gt;(tuple), indices{});<\/span>\r\n}\r\n<\/pre>\n<p>We&#8217;ll continue manipulating index sequences next time.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Building our own little template language.<\/p>\n","protected":false},"author":1069,"featured_media":111744,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[1],"tags":[25],"class_list":["post-103903","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-code"],"acf":[],"blog_post_summary":"<p>Building our own little template language.<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/103903","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/users\/1069"}],"replies":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/comments?post=103903"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/103903\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/media\/111744"}],"wp:attachment":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/media?parent=103903"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=103903"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=103903"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}