Add STLExtras apply_tuple

Zachary Turner via llvm-commits llvm-commits at lists.llvm.org
Thu Sep 29 15:33:30 PDT 2016


Given a tuple of type Ts..., invokes a user-supplied functor passing Ts...
variadically as the parameter list, allowing the functor to return a new
tuple (of a possibly different type).

I believe this is standard in C++17, so I'm adding it early.  In one of the
test cases I demonstrate some useful use cases, including that of iterating
efficiently iterating and de-referencing multiple ranges in parallel.

>From 46117057cdbd3558cb542a3fd6969b0dcde304ab Mon Sep 17 00:00:00 2001
From: Zachary Turner <zturner at google.com>
Date: Thu, 29 Sep 2016 15:16:35 -0700
Subject: [PATCH] apply_tuple

---
 include/llvm/ADT/STLExtras.h    | 13 +++++++++
 unittests/ADT/STLExtrasTest.cpp | 63
+++++++++++++++++++++++++++++++++++++++++
 2 files changed, 76 insertions(+)

diff --git a/include/llvm/ADT/STLExtras.h b/include/llvm/ADT/STLExtras.h
index 63f919d..5ebba37 100644
--- a/include/llvm/ADT/STLExtras.h
+++ b/include/llvm/ADT/STLExtras.h
@@ -24,6 +24,7 @@
 #include <functional>
 #include <iterator>
 #include <memory>
+#include <tuple>
 #include <utility> // for std::pair

 #include "llvm/ADT/Optional.h"
@@ -680,6 +681,18 @@ template <typename R> auto enumerate(R &&Range) {
   return detail::enumerator_impl<I, V>(std::begin(Range), std::end(Range));
 }

+template <typename F, typename Tuple, std::size_t... I>
+auto apply_tuple_impl(F &&f, Tuple &&t, index_sequence<I...>) {
+  return std::forward<F>(f)(std::get<I>(std::forward<Tuple>(t))...);
+}
+
+template <typename F, typename Tuple> auto apply_tuple(F &&f, Tuple &&t) {
+  using Indices =
+      build_index_impl<std::tuple_size<std::decay<Tuple>::type>::value>;
+
+  return apply_tuple_impl(std::forward<F>(f), std::forward<Tuple>(t),
+                          Indices());
+}
 } // End llvm namespace

 #endif
diff --git a/unittests/ADT/STLExtrasTest.cpp
b/unittests/ADT/STLExtrasTest.cpp
index d7457f5..26953cb 100644
--- a/unittests/ADT/STLExtrasTest.cpp
+++ b/unittests/ADT/STLExtrasTest.cpp
@@ -79,4 +79,67 @@ TEST(STLExtrasTest, EnumerateModify) {
   EXPECT_EQ('c', foo[1]);
   EXPECT_EQ('d', foo[2]);
 }
+
+TEST(STLExtrasTest, ApplyTuple) {
+  auto T = std::make_tuple(1, 2, 3, 4);
+  auto U = llvm::apply_tuple(
+      [](int A, int B, int C, int D) {
+        return std::make_tuple(A - B, B - C, C - D, D - A);
+      },
+      T);
+
+  EXPECT_EQ(-1, std::get<0>(U));
+  EXPECT_EQ(-1, std::get<1>(U));
+  EXPECT_EQ(-1, std::get<2>(U));
+  EXPECT_EQ(3, std::get<3>(U));
+
+  auto V = llvm::apply_tuple(
+      [](int A, int B, int C, int D) {
+        return std::make_tuple(std::make_pair(A, char('A' + A)),
+                               std::make_pair(B, char('A' + B)),
+                               std::make_pair(C, char('A' + C)), D);
+      },
+      T);
+
+  EXPECT_EQ(std::make_pair(1, 'B'), std::get<0>(V));
+  EXPECT_EQ(std::make_pair(2, 'C'), std::get<1>(V));
+  EXPECT_EQ(std::make_pair(3, 'D'), std::get<2>(V));
+  EXPECT_EQ(4, std::get<3>(V));
+}
+
+struct deref_iterator_tuple {
+  template <typename... Iters> auto operator()(Iters &&... Items) {
+    return std::make_tuple(*Items...);
+  }
+};
+
+struct increment_iterator_tuple {
+  template <typename... Iters> auto operator()(Iters &&... Items) {
+    return std::make_tuple(++Items...);
+  }
+};
+
+TEST(STLExtrasTest, ApplyTupleVariadic) {
+  std::vector<int> A = {1, 2, 3};
+  std::vector<char> B = {'A', 'B', 'C'};
+  std::vector<std::string> C = {"A", "B", "C"};
+
+  auto Iters = std::make_tuple(A.begin(), B.begin(), C.begin());
+  auto Values = apply_tuple(deref_iterator_tuple(), Iters);
+
+  static_assert(
+      std::is_same<std::tuple<int, char, std::string>,
decltype(Values)>::value,
+      "Incorrect tuple type!");
+
+  EXPECT_EQ(1, std::get<0>(Values));
+  EXPECT_EQ('A', std::get<1>(Values));
+  EXPECT_EQ("A", std::get<2>(Values));
+
+  Iters = apply_tuple(increment_iterator_tuple(), Iters);
+  Values = apply_tuple(deref_iterator_tuple(), Iters);
+
+  EXPECT_EQ(2, std::get<0>(Values));
+  EXPECT_EQ('B', std::get<1>(Values));
+  EXPECT_EQ("B", std::get<2>(Values));
+}
 }
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.llvm.org/pipermail/llvm-commits/attachments/20160929/d925bd17/attachment.html>


More information about the llvm-commits mailing list