Add folly::applyTuple utility
authorJordan DeLong <jdelong@fb.com>
Sat, 14 Jul 2012 22:57:32 +0000 (15:57 -0700)
committerJordan DeLong <jdelong@fb.com>
Thu, 2 Aug 2012 08:54:23 +0000 (01:54 -0700)
Summary: Applies a function to a std::tuple of arguments.

Test Plan: Unit test.

Reviewed By: tjackson@fb.com

FB internal diff: D520190

folly/ApplyTuple.h [new file with mode: 0644]
folly/test/ApplyTupleTest.cpp [new file with mode: 0644]

diff --git a/folly/ApplyTuple.h b/folly/ApplyTuple.h
new file mode 100644 (file)
index 0000000..5f535b9
--- /dev/null
@@ -0,0 +1,111 @@
+/*
+ * Copyright 2012 Facebook, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * Defines a function folly::applyTuple, which takes a function and a
+ * std::tuple of arguments and calls the function with those
+ * arguments.
+ *
+ * Example:
+ *
+ *    int x = folly::applyTuple(std::plus<int>(), std::make_tuple(12, 12));
+ *    ASSERT(x == 24);
+ */
+
+#ifndef FOLLY_APPLYTUPLE_H_
+#define FOLLY_APPLYTUPLE_H_
+
+#include <tuple>
+#include <functional>
+#include <type_traits>
+
+namespace folly {
+
+//////////////////////////////////////////////////////////////////////
+
+namespace detail {
+
+// This is to allow using this with pointers to member functions,
+// where the first argument in the tuple will be the this pointer.
+template<class F> F& makeCallable(F& f) { return f; }
+template<class R, class C, class ...A>
+auto makeCallable(R (C::*d)(A...)) -> decltype(std::mem_fn(d)) {
+  return std::mem_fn(d);
+}
+
+template<class Tuple>
+struct DerefSize
+  : std::tuple_size<typename std::remove_reference<Tuple>::type>
+{};
+
+// CallTuple recursively unpacks tuple arguments so we can forward
+// them into the function.
+template<class Ret>
+struct CallTuple {
+  template<class F, class Tuple, class ...Unpacked>
+  static typename std::enable_if<
+    (sizeof...(Unpacked) < DerefSize<Tuple>::value),
+    Ret
+  >::type call(const F& f, Tuple&& t, Unpacked&&... unp) {
+    typedef typename std::tuple_element<
+      sizeof...(Unpacked),
+      typename std::remove_reference<Tuple>::type
+    >::type ElementType;
+    return CallTuple<Ret>::call(f, std::forward<Tuple>(t),
+      std::forward<Unpacked>(unp)...,
+      std::forward<ElementType>(std::get<sizeof...(Unpacked)>(t))
+    );
+  }
+
+  template<class F, class Tuple, class ...Unpacked>
+  static typename std::enable_if<
+    (sizeof...(Unpacked) == DerefSize<Tuple>::value),
+    Ret
+  >::type call(const F& f, Tuple&& t, Unpacked&&... unp) {
+    return makeCallable(f)(std::forward<Unpacked>(unp)...);
+  }
+};
+
+// The point of this meta function is to extract the contents of the
+// tuple as a parameter pack so we can pass it into std::result_of<>.
+template<class F, class Args> struct ReturnValue {};
+template<class F, class ...Args>
+struct ReturnValue<F,std::tuple<Args...>> {
+  typedef typename std::result_of<F (Args...)>::type type;
+};
+
+}
+
+//////////////////////////////////////////////////////////////////////
+
+template<class Callable, class Tuple>
+typename detail::ReturnValue<
+  typename std::decay<Callable>::type,
+  typename std::remove_reference<Tuple>::type
+>::type
+applyTuple(const Callable& c, Tuple&& t) {
+  typedef typename detail::ReturnValue<
+    typename std::decay<Callable>::type,
+    typename std::remove_reference<Tuple>::type
+  >::type RetT;
+  return detail::CallTuple<RetT>::call(c, std::forward<Tuple>(t));
+}
+
+//////////////////////////////////////////////////////////////////////
+
+}
+
+#endif
diff --git a/folly/test/ApplyTupleTest.cpp b/folly/test/ApplyTupleTest.cpp
new file mode 100644 (file)
index 0000000..51faf45
--- /dev/null
@@ -0,0 +1,163 @@
+/*
+ * Copyright 2012 Facebook, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <iostream>
+
+#include "folly/ApplyTuple.h"
+#include <gtest/gtest.h>
+
+#include <iostream>
+#include <memory>
+
+namespace {
+
+void func(int a, int b, double c) {
+  EXPECT_EQ(a, 1);
+  EXPECT_EQ(b, 2);
+  EXPECT_EQ(c, 3.0);
+}
+
+struct Wat {
+  void func(int a, int b, double c) {
+    ::func(a, b, c);
+  }
+
+  double retVal(int a, double b) {
+    return a + b;
+  }
+
+  Wat() {}
+  Wat(Wat const&) = delete;
+
+  int foo;
+};
+
+struct Overloaded {
+  int func(int) { return 0; }
+  bool func(bool) { return true; }
+};
+
+struct Func {
+  int operator()() const {
+    return 1;
+  }
+};
+
+struct CopyCount {
+  CopyCount() {}
+  CopyCount(CopyCount const&) {
+    std::cout << "copy count copy ctor\n";
+  }
+};
+
+void anotherFunc(CopyCount const&) {}
+
+std::function<void (int, int, double)> makeFunc() {
+  return &func;
+}
+
+struct GuardObjBase {
+  GuardObjBase(GuardObjBase&&) {}
+  GuardObjBase() {}
+  GuardObjBase(GuardObjBase const&) = delete;
+  GuardObjBase& operator=(GuardObjBase const&) = delete;
+};
+typedef GuardObjBase const& Guard;
+
+template<class F, class Tuple>
+struct GuardObj : GuardObjBase {
+  explicit GuardObj(F&& f, Tuple&& args)
+    : f_(std::move(f))
+    , args_(std::move(args))
+  {}
+  GuardObj(GuardObj&& g)
+    : GuardObjBase(std::move(g))
+    , f_(std::move(g.f_))
+    , args_(std::move(g.args_))
+  {}
+
+  ~GuardObj() {
+    folly::applyTuple(f_, args_);
+  }
+
+  GuardObj(const GuardObj&) = delete;
+  GuardObj& operator=(const GuardObj&) = delete;
+
+private:
+  F f_;
+  Tuple args_;
+};
+
+template<class F, class ...Args>
+GuardObj<typename std::decay<F>::type,std::tuple<Args...>>
+guard(F&& f, Args&&... args) {
+  return GuardObj<typename std::decay<F>::type,std::tuple<Args...>>(
+    std::forward<F>(f),
+    std::tuple<Args...>(std::forward<Args>(args)...)
+  );
+}
+
+struct Mover {
+  Mover() {}
+  Mover(Mover&&) {}
+  Mover(const Mover&) = delete;
+  Mover& operator=(const Mover&) = delete;
+};
+
+void move_only_func(Mover&&) {}
+
+}
+
+TEST(ApplyTuple, Test) {
+  auto argsTuple = std::make_tuple(1, 2, 3.0);
+  auto func2 = func;
+  folly::applyTuple(func2, argsTuple);
+  folly::applyTuple(func, argsTuple);
+  folly::applyTuple(func, std::make_tuple(1, 2, 3.0));
+  folly::applyTuple(makeFunc(), std::make_tuple(1, 2, 3.0));
+  folly::applyTuple(makeFunc(), argsTuple);
+
+  std::unique_ptr<Wat> wat(new Wat);
+  folly::applyTuple(&Wat::func, std::make_tuple(wat.get(), 1, 2, 3.0));
+  auto argsTuple2 = std::make_tuple(wat.get(), 1, 2, 3.0);
+  folly::applyTuple(&Wat::func, argsTuple2);
+
+  EXPECT_EQ(10.0,
+            folly::applyTuple(&Wat::retVal,
+                              std::make_tuple(wat.get(), 1, 9.0)));
+
+  auto test = guard(func, 1, 2, 3.0);
+  CopyCount cpy;
+  auto test2 = guard(anotherFunc, cpy);
+  auto test3 = guard(anotherFunc, std::cref(cpy));
+
+  Overloaded ovl;
+  EXPECT_EQ(0,
+            folly::applyTuple(
+              static_cast<int (Overloaded::*)(int)>(&Overloaded::func),
+              std::make_tuple(&ovl, 12)));
+  EXPECT_EQ(true,
+            folly::applyTuple(
+              static_cast<bool (Overloaded::*)(bool)>(&Overloaded::func),
+              std::make_tuple(&ovl, false)));
+
+  int x = folly::applyTuple(std::plus<int>(), std::make_tuple(12, 12));
+  EXPECT_EQ(24, x);
+
+  Mover m;
+  folly::applyTuple(move_only_func,
+                    std::forward_as_tuple(std::forward<Mover>(Mover())));
+}