1/*
2 * Copyright 2012-present Facebook, Inc.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#pragma once
18
19#include <functional>
20#include <tuple>
21#include <utility>
22
23#include <folly/Traits.h>
24#include <folly/Utility.h>
25#include <folly/functional/Invoke.h>
26
27namespace folly {
28
29//////////////////////////////////////////////////////////////////////
30
31/**
32 * Helper to generate an index sequence from a tuple like type
33 */
34template <typename Tuple>
35using index_sequence_for_tuple =
36 make_index_sequence<std::tuple_size<Tuple>::value>;
37
38namespace detail {
39namespace apply_tuple {
40namespace adl {
41using std::get;
42
43struct ApplyInvoke {
44 template <typename T>
45 using seq = index_sequence_for_tuple<std::remove_reference_t<T>>;
46
47 template <typename F, typename T, std::size_t... I>
48 static constexpr auto invoke_(F&& f, T&& t, index_sequence<I...>) noexcept(
49 is_nothrow_invocable<F&&, decltype(get<I>(std::declval<T>()))...>::value)
50 -> invoke_result_t<F&&, decltype(get<I>(std::declval<T>()))...> {
51 return invoke(static_cast<F&&>(f), get<I>(static_cast<T&&>(t))...);
52 }
53};
54
55template <
56 typename Tuple,
57 std::size_t... Indices,
58 typename ReturnTuple =
59 std::tuple<decltype(get<Indices>(std::declval<Tuple>()))...>>
60auto forward_tuple(Tuple&& tuple, index_sequence<Indices...>) -> ReturnTuple {
61 return ReturnTuple{get<Indices>(std::forward<Tuple>(tuple))...};
62}
63} // namespace adl
64} // namespace apply_tuple
65} // namespace detail
66
67struct ApplyInvoke : private detail::apply_tuple::adl::ApplyInvoke {
68 public:
69 template <typename F, typename T>
70 constexpr auto operator()(F&& f, T&& t) const noexcept(
71 noexcept(invoke_(static_cast<F&&>(f), static_cast<T&&>(t), seq<T>{})))
72 -> decltype(invoke_(static_cast<F&&>(f), static_cast<T&&>(t), seq<T>{})) {
73 return invoke_(static_cast<F&&>(f), static_cast<T&&>(t), seq<T>{});
74 }
75};
76
77//////////////////////////////////////////////////////////////////////
78
79// libc++ v3.9 has std::apply
80// android ndk r15c libc++ claims to be v3.9 but is missing std::apply
81#if __cpp_lib_apply >= 201603 || \
82 (((__ANDROID__ && _LIBCPP_VERSION > 3900) || \
83 (!__ANDROID__ && _LIBCPP_VERSION > 3800)) && \
84 _LIBCPP_STD_VER > 14) || \
85 (_MSC_VER && _HAS_CXX17)
86
87/* using override */ using std::apply;
88
89#else // __cpp_lib_apply >= 201603
90
91// mimic: std::apply, C++17
92template <typename F, typename Tuple>
93constexpr decltype(auto) apply(F&& func, Tuple&& tuple) {
94 return ApplyInvoke{}(static_cast<F&&>(func), static_cast<Tuple&&>(tuple));
95}
96
97#endif // __cpp_lib_apply >= 201603
98
99/**
100 * Get a tuple of references from the passed tuple, forwarding will be applied
101 * on the individual types of the tuple based on the value category of the
102 * passed tuple
103 *
104 * For example
105 *
106 * forward_tuple(std::make_tuple(1, 2))
107 *
108 * Returns a std::tuple<int&&, int&&>,
109 *
110 * auto tuple = std::make_tuple(1, 2);
111 * forward_tuple(tuple)
112 *
113 * Returns a std::tuple<int&, int&>
114 */
115template <typename Tuple>
116auto forward_tuple(Tuple&& tuple) noexcept
117 -> decltype(detail::apply_tuple::adl::forward_tuple(
118 std::declval<Tuple>(),
119 std::declval<
120 index_sequence_for_tuple<std::remove_reference_t<Tuple>>>())) {
121 return detail::apply_tuple::adl::forward_tuple(
122 std::forward<Tuple>(tuple),
123 index_sequence_for_tuple<std::remove_reference_t<Tuple>>{});
124}
125
126/**
127 * Mimic the invoke suite of traits for tuple based apply invocation
128 */
129template <typename F, typename Tuple>
130struct apply_result : invoke_result<ApplyInvoke, F, Tuple> {};
131template <typename F, typename Tuple>
132using apply_result_t = invoke_result_t<ApplyInvoke, F, Tuple>;
133template <typename F, typename Tuple>
134struct is_applicable : is_invocable<ApplyInvoke, F, Tuple> {};
135template <typename R, typename F, typename Tuple>
136struct is_applicable_r : is_invocable_r<R, ApplyInvoke, F, Tuple> {};
137template <typename F, typename Tuple>
138struct is_nothrow_applicable : is_nothrow_invocable<ApplyInvoke, F, Tuple> {};
139template <typename R, typename F, typename Tuple>
140struct is_nothrow_applicable_r
141 : is_nothrow_invocable_r<R, ApplyInvoke, F, Tuple> {};
142
143namespace detail {
144namespace apply_tuple {
145
146template <class F>
147class Uncurry {
148 public:
149 explicit Uncurry(F&& func) : func_(std::move(func)) {}
150 explicit Uncurry(const F& func) : func_(func) {}
151
152 template <class Tuple>
153 auto operator()(Tuple&& tuple) const
154 -> decltype(apply(std::declval<F>(), std::forward<Tuple>(tuple))) {
155 return apply(func_, std::forward<Tuple>(tuple));
156 }
157
158 private:
159 F func_;
160};
161} // namespace apply_tuple
162} // namespace detail
163
164/**
165 * Wraps a function taking N arguments into a function which accepts a tuple of
166 * N arguments. Note: This function will also accept an std::pair if N == 2.
167 *
168 * For example, given the below code:
169 *
170 * std::vector<std::tuple<int, int, int>> rows = ...;
171 * auto test = [](std::tuple<int, int, int>& row) {
172 * return std::get<0>(row) * std::get<1>(row) * std::get<2>(row) == 24;
173 * };
174 * auto found = std::find_if(rows.begin(), rows.end(), test);
175 *
176 *
177 * 'test' could be rewritten as:
178 *
179 * auto test =
180 * folly::uncurry([](int a, int b, int c) { return a * b * c == 24; });
181 *
182 */
183template <class F>
184auto uncurry(F&& f)
185 -> detail::apply_tuple::Uncurry<typename std::decay<F>::type> {
186 return detail::apply_tuple::Uncurry<typename std::decay<F>::type>(
187 std::forward<F>(f));
188}
189
190#if __cpp_lib_make_from_tuple || (_MSC_VER >= 1910 && _MSVC_LANG > 201402)
191
192/* using override */ using std::make_from_tuple;
193
194#else
195
196namespace detail {
197namespace apply_tuple {
198template <class T>
199struct Construct {
200 template <class... Args>
201 constexpr T operator()(Args&&... args) const {
202 return T(std::forward<Args>(args)...);
203 }
204};
205} // namespace apply_tuple
206} // namespace detail
207
208// mimic: std::make_from_tuple, C++17
209template <class T, class Tuple>
210constexpr T make_from_tuple(Tuple&& t) {
211 return apply(detail::apply_tuple::Construct<T>(), std::forward<Tuple>(t));
212}
213
214#endif
215
216//////////////////////////////////////////////////////////////////////
217} // namespace folly
218