Let applyTuple accept any number of tuples
authorSven Over <over@fb.com>
Thu, 11 Aug 2016 08:40:38 +0000 (01:40 -0700)
committerFacebook Github Bot 3 <facebook-github-bot-3-bot@fb.com>
Thu, 11 Aug 2016 08:53:31 +0000 (01:53 -0700)
Summary:
Instead of passing exactly one tuple of arguments to applyTuple,
this diff allows to pass any number (zero or more) of tuples.

This can be very handy when piecing together arguments for
a function call.

Reviewed By: yfeldblum

Differential Revision: D3681499

fbshipit-source-id: a75a448636759f71db8d303e9dccada5b559af54

folly/ApplyTuple.h
folly/test/ApplyTupleTest.cpp

index 3a8e14eea2255c913371d1aa3ce632cba8300d44..0288f567c2c02ab642ead3c842fddc2b5f6c226b 100644 (file)
@@ -28,6 +28,7 @@
 #pragma once
 
 #include <functional>
+#include <tuple>
 #include <utility>
 
 namespace folly {
@@ -46,9 +47,17 @@ struct MakeIndexSequence : MakeIndexSequence<N - 1, N - 1, Is...> {};
 template <std::size_t... Is>
 struct MakeIndexSequence<0, Is...> : IndexSequence<Is...> {};
 
-template <class Tuple>
-using MakeIndexSequenceFromTuple =
-    MakeIndexSequence<std::tuple_size<typename std::decay<Tuple>::type>::value>;
+inline constexpr std::size_t sum() {
+  return 0;
+}
+template <typename... Args>
+inline constexpr std::size_t sum(std::size_t v1, Args... vs) {
+  return v1 + sum(vs...);
+}
+
+template <class... Tuple>
+using MakeIndexSequenceFromTuple = MakeIndexSequence<sum(
+    std::tuple_size<typename std::decay<Tuple>::type>::value...)>;
 
 // This is to allow using this with pointers to member functions,
 // where the first argument in the tuple will be the this pointer.
@@ -68,21 +77,42 @@ inline constexpr auto call(F&& f, Tuple&& t, IndexSequence<Indexes...>)
   return std::forward<F>(f)(std::get<Indexes>(std::forward<Tuple>(t))...);
 }
 
+template <class Tuple, std::size_t... Indexes>
+inline constexpr auto forwardTuple(Tuple&& t, IndexSequence<Indexes...>)
+    -> decltype(
+        std::forward_as_tuple(std::get<Indexes>(std::forward<Tuple>(t))...)) {
+  return std::forward_as_tuple(std::get<Indexes>(std::forward<Tuple>(t))...);
+}
+
 } // namespace apply_tuple
 } // namespace detail
 
 //////////////////////////////////////////////////////////////////////
 
-template <class F, class Tuple>
-inline constexpr auto applyTuple(F&& f, Tuple&& t)
+/**
+ * Invoke a callable object with a set of arguments passed as a tuple, or a
+ *     series of tuples
+ *
+ * Example: the following lines are equivalent
+ *     func(1, 2, 3, "foo");
+ *     applyTuple(func, std::make_tuple(1, 2, 3, "foo"));
+ *     applyTuple(func, std::make_tuple(1, 2), std::make_tuple(3, "foo"));
+ */
+
+template <class F, class... Tuples>
+inline constexpr auto applyTuple(F&& f, Tuples&&... t)
     -> decltype(detail::apply_tuple::call(
         detail::apply_tuple::makeCallable(std::forward<F>(f)),
-        std::forward<Tuple>(t),
-        detail::apply_tuple::MakeIndexSequenceFromTuple<Tuple>{})) {
+        std::tuple_cat(detail::apply_tuple::forwardTuple(
+            std::forward<Tuples>(t),
+            detail::apply_tuple::MakeIndexSequenceFromTuple<Tuples>{})...),
+        detail::apply_tuple::MakeIndexSequenceFromTuple<Tuples...>{})) {
   return detail::apply_tuple::call(
       detail::apply_tuple::makeCallable(std::forward<F>(f)),
-      std::forward<Tuple>(t),
-      detail::apply_tuple::MakeIndexSequenceFromTuple<Tuple>{});
+      std::tuple_cat(detail::apply_tuple::forwardTuple(
+          std::forward<Tuples>(t),
+          detail::apply_tuple::MakeIndexSequenceFromTuple<Tuples>{})...),
+      detail::apply_tuple::MakeIndexSequenceFromTuple<Tuples...>{});
 }
 
 //////////////////////////////////////////////////////////////////////
index 92ebac3fa1a8e919b293a4aded073d1b0f184bdd..e29c9914759c5dbc2d29d25604448e8331e9180b 100644 (file)
@@ -287,3 +287,30 @@ TEST(ApplyTuple, Pair) {
 
   EXPECT_EQ(folly::applyTuple(add, std::pair<int, int>{1200, 34}), 1234);
 }
+
+TEST(ApplyTuple, MultipleTuples) {
+  auto add = [](int x, int y, int z) { return x * 100 + y * 10 + z; };
+
+  EXPECT_EQ(123, folly::applyTuple(add, std::make_tuple(1, 2, 3)));
+  EXPECT_EQ(
+      123, folly::applyTuple(add, std::make_tuple(1, 2, 3), std::make_tuple()));
+  EXPECT_EQ(
+      123, folly::applyTuple(add, std::make_tuple(1, 2), std::make_tuple(3)));
+  EXPECT_EQ(
+      123, folly::applyTuple(add, std::make_tuple(1), std::make_tuple(2, 3)));
+  EXPECT_EQ(
+      123, folly::applyTuple(add, std::make_tuple(), std::make_tuple(1, 2, 3)));
+
+  EXPECT_EQ(
+      123,
+      folly::applyTuple(
+          add, std::make_tuple(1, 2, 3), std::make_tuple(), std::make_tuple()));
+  EXPECT_EQ(
+      123,
+      folly::applyTuple(
+          add, std::make_tuple(1), std::make_tuple(2), std::make_tuple(3)));
+  EXPECT_EQ(
+      123,
+      folly::applyTuple(
+          add, std::make_tuple(1), std::make_tuple(), std::make_tuple(2, 3)));
+}