blob: a592704f080883a3a48433984e8fcb15e37d8d41 [file] [log] [blame]
/*
* Copyright 2015 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>
{};
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 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));
}
//////////////////////////////////////////////////////////////////////
}
#endif