folly/ApplyTuple.h: fix const-correctness & other issues, simplify
authorSven Over <over@fb.com>
Thu, 18 Feb 2016 13:31:22 +0000 (05:31 -0800)
committerfacebook-github-bot-4 <folly-bot@fb.com>
Thu, 18 Feb 2016 14:20:26 +0000 (06:20 -0800)
Summary:The existing implementation of folly::applyTuple does not support
mutable callables (such as mutable lambdas and other functor objects
that only implement non-const operator()).

This commit adds a few more unit tests and changes the implementation
so that new and existing tests pass.

Reviewed By: yfeldblum

Differential Revision: D2942622

fb-gh-sync-id: 82478f290e9fd2020358ff79ef0a6bcf8a43738c
shipit-source-id: 82478f290e9fd2020358ff79ef0a6bcf8a43738c

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

index b2dff7422cb0a6df2601dcde09af2b4f5bd1f43d..3a8e14eea2255c913371d1aa3ce632cba8300d44 100644 (file)
  *    ASSERT(x == 24);
  */
 
-#ifndef FOLLY_APPLYTUPLE_H_
-#define FOLLY_APPLYTUPLE_H_
+#pragma once
 
-#include <tuple>
 #include <functional>
-#include <type_traits>
+#include <utility>
 
 namespace folly {
 
 //////////////////////////////////////////////////////////////////////
 
 namespace detail {
+namespace apply_tuple {
+
+template <std::size_t...>
+struct IndexSequence {};
+
+template <std::size_t N, std::size_t... Is>
+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>;
 
 // 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)) {
+template <class F>
+inline constexpr F&& makeCallable(F&& f) {
+  return std::forward<F>(f);
+}
+template <class M, class C>
+inline constexpr auto makeCallable(M(C::*d)) -> decltype(std::mem_fn(d)) {
   return std::mem_fn(d);
 }
 
-template<class Tuple>
-struct DerefSize
-  : std::tuple_size<typename std::remove_reference<Tuple>::type>
-{};
-
-template<class Tuple, class ...Unpacked> struct ExprDoUnpack {
-  enum {
-    value = sizeof...(Unpacked) < DerefSize<Tuple>::value
-  };
-};
-
-template<class Tuple, class ...Unpacked> struct ExprIsUnpacked {
-  enum {
-    value = sizeof...(Unpacked) == DerefSize<Tuple>::value
-  };
-};
-
-// 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<ExprDoUnpack<Tuple, Unpacked...>::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<ExprIsUnpacked<Tuple, Unpacked...>::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 F, class Tuple, std::size_t... Indexes>
+inline constexpr auto call(F&& f, Tuple&& t, IndexSequence<Indexes...>)
+    -> decltype(
+        std::forward<F>(f)(std::get<Indexes>(std::forward<Tuple>(t))...)) {
+  return std::forward<F>(f)(std::get<Indexes>(std::forward<Tuple>(t))...);
 }
 
+} // namespace apply_tuple
+} // namespace detail
+
 //////////////////////////////////////////////////////////////////////
 
-template<class Callable, class Tuple>
-typename detail::ReturnValue<
-  typename std::decay<Callable>::type,
-  typename std::decay<Tuple>::type
->::type
-applyTuple(const Callable& c, Tuple&& t) {
-  typedef typename detail::ReturnValue<
-    typename std::decay<Callable>::type,
-    typename std::decay<Tuple>::type
-  >::type RetT;
-  return detail::CallTuple<RetT>::call(c, std::forward<Tuple>(t));
+template <class F, class Tuple>
+inline constexpr auto applyTuple(F&& f, Tuple&& t)
+    -> decltype(detail::apply_tuple::call(
+        detail::apply_tuple::makeCallable(std::forward<F>(f)),
+        std::forward<Tuple>(t),
+        detail::apply_tuple::MakeIndexSequenceFromTuple<Tuple>{})) {
+  return detail::apply_tuple::call(
+      detail::apply_tuple::makeCallable(std::forward<F>(f)),
+      std::forward<Tuple>(t),
+      detail::apply_tuple::MakeIndexSequenceFromTuple<Tuple>{});
 }
 
 //////////////////////////////////////////////////////////////////////
-
 }
-
-#endif
index 7219590c6407ce80814ca5ed8c771ee30cc09bf6..395cdfed1ea7409522395e2b4068914c8ecf8896 100644 (file)
@@ -166,3 +166,123 @@ TEST(ApplyTuple, Test) {
   const auto tuple3 = std::make_tuple(1, 2, 3.0);
   folly::applyTuple(func, tuple3);
 }
+
+TEST(ApplyTuple, Mutable) {
+  auto argsTuple = std::make_tuple(1, 2, 3.0);
+
+  folly::applyTuple([](int a, int b, double c) mutable { func(a, b, c); },
+                    argsTuple);
+}
+
+TEST(ApplyTuple, ConstOverloads) {
+  struct ConstOverloaded {
+    ConstOverloaded() {}
+    int operator()() { return 101; }
+    int operator()() const { return 102; }
+  };
+
+  ConstOverloaded covl;
+
+  // call operator()()
+  EXPECT_EQ(folly::applyTuple(covl, std::make_tuple()), 101);
+  EXPECT_EQ(folly::applyTuple(std::ref(covl), std::make_tuple()), 101);
+  EXPECT_EQ(folly::applyTuple(std::move(covl), std::make_tuple()), 101);
+
+  // call operator()() const
+  EXPECT_EQ(folly::applyTuple(const_cast<ConstOverloaded const&>(covl),
+                              std::make_tuple()),
+            102);
+  EXPECT_EQ(folly::applyTuple(std::cref(covl), std::make_tuple()), 102);
+}
+
+TEST(ApplyTuple, RefOverloads) {
+  struct RefOverloaded {
+    RefOverloaded() {}
+    int operator()() & { return 201; }
+    int operator()() const & { return 202; }
+    int operator()() && { return 203; }
+  };
+
+  RefOverloaded rovl;
+
+  // call operator()() &
+  EXPECT_EQ(folly::applyTuple(rovl, std::make_tuple()), 201);
+  EXPECT_EQ(folly::applyTuple(std::ref(rovl), std::make_tuple()), 201);
+
+  // call operator()() const &
+  EXPECT_EQ(folly::applyTuple(const_cast<RefOverloaded const&>(rovl),
+                              std::make_tuple()),
+            202);
+  EXPECT_EQ(folly::applyTuple(std::cref(rovl), std::make_tuple()), 202);
+
+  // call operator()() &&
+  EXPECT_EQ(folly::applyTuple(std::move(rovl), std::make_tuple()), 203);
+}
+
+struct MemberFunc {
+  int x;
+  int getX() const { return x; }
+  void setX(int xx) { x = xx; }
+};
+
+TEST(ApplyTuple, MemberFunction) {
+  MemberFunc mf;
+  mf.x = 123;
+
+  // call getter
+  EXPECT_EQ(folly::applyTuple(&MemberFunc::getX, std::make_tuple(&mf)), 123);
+
+  // call setter
+  folly::applyTuple(&MemberFunc::setX, std::make_tuple(&mf, 234));
+  EXPECT_EQ(mf.x, 234);
+  EXPECT_EQ(folly::applyTuple(&MemberFunc::getX, std::make_tuple(&mf)), 234);
+}
+
+TEST(ApplyTuple, MemberFunctionWithRefWrapper) {
+  MemberFunc mf;
+  mf.x = 234;
+
+  EXPECT_EQ(folly::applyTuple(&MemberFunc::getX, std::make_tuple(std::ref(mf))),
+            234);
+}
+
+TEST(ApplyTuple, MemberFunctionWithConstPointer) {
+  MemberFunc mf;
+  mf.x = 234;
+
+  EXPECT_EQ(
+      folly::applyTuple(&MemberFunc::getX,
+                        std::make_tuple(const_cast<MemberFunc const*>(&mf))),
+      234);
+}
+
+TEST(ApplyTuple, MemberFunctionWithSharedPtr) {
+  MemberFunc mf;
+  mf.x = 234;
+
+  EXPECT_EQ(
+      folly::applyTuple(&MemberFunc::getX,
+                        std::make_tuple(std::make_shared<MemberFunc>(mf))),
+      234);
+}
+
+TEST(ApplyTuple, MemberFunctionWithUniquePtr) {
+  MemberFunc mf;
+  mf.x = 234;
+
+  EXPECT_EQ(folly::applyTuple(&MemberFunc::getX,
+                              std::make_tuple(std::unique_ptr<MemberFunc>(
+                                  new MemberFunc(mf)))),
+            234);
+}
+
+TEST(ApplyTuple, Array) {
+  folly::applyTuple(func, std::array<int, 3>{{1, 2, 3}});
+  folly::applyTuple(func, std::array<double, 3>{{1, 2, 3}});
+}
+
+TEST(ApplyTuple, Pair) {
+  auto add = [](int x, int y) { return x + y; };
+
+  EXPECT_EQ(folly::applyTuple(add, std::pair<int, int>{1200, 34}), 1234);
+}