From 9aa33f6ae63248816b9b5d7286f2d1740027d1d3 Mon Sep 17 00:00:00 2001 From: Jordan DeLong Date: Sat, 14 Jul 2012 15:57:32 -0700 Subject: [PATCH] Add folly::applyTuple utility 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 | 111 +++++++++++++++++++++++ folly/test/ApplyTupleTest.cpp | 163 ++++++++++++++++++++++++++++++++++ 2 files changed, 274 insertions(+) create mode 100644 folly/ApplyTuple.h create mode 100644 folly/test/ApplyTupleTest.cpp diff --git a/folly/ApplyTuple.h b/folly/ApplyTuple.h new file mode 100644 index 00000000..5f535b9e --- /dev/null +++ b/folly/ApplyTuple.h @@ -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(), std::make_tuple(12, 12)); + * ASSERT(x == 24); + */ + +#ifndef FOLLY_APPLYTUPLE_H_ +#define FOLLY_APPLYTUPLE_H_ + +#include +#include +#include + +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 F& makeCallable(F& f) { return f; } +template +auto makeCallable(R (C::*d)(A...)) -> decltype(std::mem_fn(d)) { + return std::mem_fn(d); +} + +template +struct DerefSize + : std::tuple_size::type> +{}; + +// CallTuple recursively unpacks tuple arguments so we can forward +// them into the function. +template +struct CallTuple { + template + static typename std::enable_if< + (sizeof...(Unpacked) < DerefSize::value), + Ret + >::type call(const F& f, Tuple&& t, Unpacked&&... unp) { + typedef typename std::tuple_element< + sizeof...(Unpacked), + typename std::remove_reference::type + >::type ElementType; + return CallTuple::call(f, std::forward(t), + std::forward(unp)..., + std::forward(std::get(t)) + ); + } + + template + static typename std::enable_if< + (sizeof...(Unpacked) == DerefSize::value), + Ret + >::type call(const F& f, Tuple&& t, Unpacked&&... unp) { + return makeCallable(f)(std::forward(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 struct ReturnValue {}; +template +struct ReturnValue> { + typedef typename std::result_of::type type; +}; + +} + +////////////////////////////////////////////////////////////////////// + +template +typename detail::ReturnValue< + typename std::decay::type, + typename std::remove_reference::type +>::type +applyTuple(const Callable& c, Tuple&& t) { + typedef typename detail::ReturnValue< + typename std::decay::type, + typename std::remove_reference::type + >::type RetT; + return detail::CallTuple::call(c, std::forward(t)); +} + +////////////////////////////////////////////////////////////////////// + +} + +#endif diff --git a/folly/test/ApplyTupleTest.cpp b/folly/test/ApplyTupleTest.cpp new file mode 100644 index 00000000..51faf457 --- /dev/null +++ b/folly/test/ApplyTupleTest.cpp @@ -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 + +#include "folly/ApplyTuple.h" +#include + +#include +#include + +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 makeFunc() { + return &func; +} + +struct GuardObjBase { + GuardObjBase(GuardObjBase&&) {} + GuardObjBase() {} + GuardObjBase(GuardObjBase const&) = delete; + GuardObjBase& operator=(GuardObjBase const&) = delete; +}; +typedef GuardObjBase const& Guard; + +template +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 +GuardObj::type,std::tuple> +guard(F&& f, Args&&... args) { + return GuardObj::type,std::tuple>( + std::forward(f), + std::tuple(std::forward(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(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(&Overloaded::func), + std::make_tuple(&ovl, 12))); + EXPECT_EQ(true, + folly::applyTuple( + static_cast(&Overloaded::func), + std::make_tuple(&ovl, false))); + + int x = folly::applyTuple(std::plus(), std::make_tuple(12, 12)); + EXPECT_EQ(24, x); + + Mover m; + folly::applyTuple(move_only_func, + std::forward_as_tuple(std::forward(Mover()))); +} -- 2.34.1