{"id":1076,"date":"2015-04-06T09:31:48","date_gmt":"2015-04-06T16:31:48","guid":{"rendered":"http:\/\/www.elbeno.com\/blog\/?p=1076"},"modified":"2015-06-30T21:16:05","modified_gmt":"2015-07-01T04:16:05","slug":"c-tuples-the-missing-functionality","status":"publish","type":"post","link":"https:\/\/www.elbeno.com\/blog\/?p=1076","title":{"rendered":"C++ Tuples: the missing functionality"},"content":{"rendered":"<p>C++ provides a strange mix of compile-time and runtime functionality for dealing with tuples. There are some interesting parts, like <code>std::tie<\/code> to destructure a tuple, and <code>std::tuple_cat<\/code> to join together several tuples into one. So there is evidence that the standard has been influenced by some functional programming ideas, but I don&#8217;t think the full power of tuples has been realized (in both senses), and I found myself thinking about some missing parts.<\/p>\n<p>Like why do we have <code>std::tuple_cat<\/code> and <code>std::get&lt;0&gt;<\/code> (aka <code>std::tuple_head<\/code>), but not <code>std::tuple_cons<\/code> or <code>std::tuple_tail<\/code>? So here are some possible implementations.<\/p>\n<p>First, something missing from <code>type_traits<\/code> which will help us constrain things:<\/p>\n<pre lang=\"cpp\">\r\ntemplate <typename T>\r\nstruct is_tuple : public std::false_type {};\r\n\r\ntemplate <typename... Ts>\r\nstruct is_tuple<std::tuple<Ts...>> : public std::true_type {};\r\n<\/pre>\n<p>Both <code>tuple_cons<\/code> and <code>tuple_tail<\/code> can make use of an expansion of <code>std::index_sequence<\/code> to work their magic, with a user-facing function that provides the appropriate <code>std::index_sequence<\/code> to an overload.<\/p>\n<p>For <code>tuple_cons<\/code>, we just call <code>std::make_tuple<\/code> with the new element and the result of expanding <code>std::get<\/code> across the other tuple:<\/p>\n<pre lang=\"cpp\">\r\ntemplate <typename U, typename T, std::size_t ...Is>\r\nauto tuple_cons(U&& u, T&& t, std::index_sequence<Is...>,\r\n    std::enable_if_t<is_tuple<std::decay_t<T>>::value>* = nullptr)\r\n{\r\n  return std::make_tuple(std::forward<U>(u), \r\n                         std::get<Is>(std::forward<T>(t))...);\r\n}\r\n\r\ntemplate <typename U, typename T>\r\nauto tuple_cons(U&& u, T&& t,\r\n    std::enable_if_t<is_tuple<std::decay_t<T>>::value>* = nullptr)\r\n{\r\n  using Tuple = std::decay_t<T>;\r\n  return tuple_cons(std::forward<U>(u), std::forward<T>(t),\r\n      std::make_index_sequence<std::tuple_size<Tuple>::value>());\r\n}\r\n<\/pre>\n<p>And for <code>tuple_tail<\/code>, we construct a <code>std::index_sequence<\/code> of length n-1, then offset it by one in the expansion to obtain the tail:<\/p>\n<pre lang=\"cpp\">\r\ntemplate <typename T, std::size_t ...Is>\r\nauto tuple_tail(T&& t, std::index_sequence<Is...>,\r\n    std::enable_if_t<is_tuple<std::decay_t<T>>::value>* = nullptr)\r\n{\r\n  return std::make_tuple(std::get<Is + 1>(std::forward<T>(t))...);\r\n}\r\n\r\ntemplate <typename T>\r\nauto tuple_tail(T&& t, \r\n    std::enable_if_t<is_tuple<std::decay_t<T>>::value>* = nullptr)\r\n{\r\n  using Tuple = std::decay_t<T>;\r\n  return tuple_tail(std::forward<T>(t),\r\n      std::make_index_sequence<std::tuple_size<Tuple>::value - 1>());\r\n}\r\n<\/pre>\n","protected":false},"excerpt":{"rendered":"<p>C++ provides a strange mix of compile-time and runtime functionality for dealing with tuples. There are some interesting parts, like std::tie to destructure a tuple, and std::tuple_cat to join together several tuples into one. So there is evidence that the standard has been influenced by some functional programming ideas, but&#8230;<\/p>\n","protected":false},"author":2,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[22,8],"tags":[],"class_list":["post-1076","post","type-post","status-publish","format-standard","hentry","category-cpp","category-programming"],"_links":{"self":[{"href":"https:\/\/www.elbeno.com\/blog\/index.php?rest_route=\/wp\/v2\/posts\/1076","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.elbeno.com\/blog\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.elbeno.com\/blog\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.elbeno.com\/blog\/index.php?rest_route=\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/www.elbeno.com\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=1076"}],"version-history":[{"count":4,"href":"https:\/\/www.elbeno.com\/blog\/index.php?rest_route=\/wp\/v2\/posts\/1076\/revisions"}],"predecessor-version":[{"id":1080,"href":"https:\/\/www.elbeno.com\/blog\/index.php?rest_route=\/wp\/v2\/posts\/1076\/revisions\/1080"}],"wp:attachment":[{"href":"https:\/\/www.elbeno.com\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=1076"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.elbeno.com\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=1076"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.elbeno.com\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=1076"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}