1 | /* |
2 | * Copyright 2016-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 <folly/Utility.h> |
20 | #include <folly/functional/Invoke.h> |
21 | |
22 | #include <tuple> |
23 | #include <utility> |
24 | |
25 | namespace folly { |
26 | |
27 | namespace detail { |
28 | namespace partial { |
29 | |
30 | // helper type to make sure that the templated constructor in Partial does |
31 | // not accidentally act as copy or move constructor |
32 | struct PartialConstructFromCallable {}; |
33 | |
34 | template <typename F, typename Tuple> |
35 | class Partial { |
36 | using Indexes = make_index_sequence<std::tuple_size<Tuple>{}>; |
37 | |
38 | template <typename Self, std::size_t... I, typename... Args> |
39 | static auto invokeForward(Self&& self, index_sequence<I...>, Args&&... args) |
40 | -> decltype(invoke( |
41 | std::declval<Self>().f_, |
42 | std::get<I>(std::declval<Self>().stored_args_)..., |
43 | std::declval<Args>()...)) { |
44 | return invoke( |
45 | std::forward<Self>(self).f_, |
46 | std::get<I>(std::forward<Self>(self).stored_args_)..., |
47 | std::forward<Args>(args)...); |
48 | } |
49 | |
50 | public: |
51 | template <typename Callable, typename... Args> |
52 | Partial(PartialConstructFromCallable, Callable&& callable, Args&&... args) |
53 | : f_(std::forward<Callable>(callable)), |
54 | stored_args_(std::forward<Args>(args)...) {} |
55 | |
56 | template <typename... CArgs> |
57 | auto operator()(CArgs&&... cargs) & -> decltype(invokeForward( |
58 | std::declval<Partial&>(), |
59 | Indexes{}, |
60 | std::declval<CArgs>()...)) { |
61 | return invokeForward(*this, Indexes{}, std::forward<CArgs>(cargs)...); |
62 | } |
63 | template <typename... CArgs> |
64 | auto operator()(CArgs&&... cargs) const& -> decltype(invokeForward( |
65 | std::declval<const Partial&>(), |
66 | Indexes{}, |
67 | std::declval<CArgs>()...)) { |
68 | return invokeForward(*this, Indexes{}, std::forward<CArgs>(cargs)...); |
69 | } |
70 | template <typename... As> |
71 | auto operator()(As&&... a) && -> decltype(invokeForward( |
72 | std::declval<Partial&&>(), |
73 | Indexes{}, |
74 | std::declval<As>()...)) { |
75 | return invokeForward(std::move(*this), Indexes{}, std::forward<As>(a)...); |
76 | } |
77 | template <typename... As> |
78 | auto operator()(As&&... as) const&& -> decltype(invokeForward( |
79 | std::declval<const Partial&&>(), |
80 | Indexes{}, |
81 | std::declval<As>()...)) { |
82 | return invokeForward(std::move(*this), Indexes{}, std::forward<As>(as)...); |
83 | } |
84 | |
85 | private: |
86 | // the stored callable |
87 | F f_; |
88 | // the stored arguments, these will be forwarded along with the actual |
89 | // argumnets to the callable above |
90 | Tuple stored_args_; |
91 | }; |
92 | |
93 | } // namespace partial |
94 | } // namespace detail |
95 | |
96 | /** |
97 | * Partially applies arguments to a callable |
98 | * |
99 | * `partial` takes a callable and zero or more additional arguments and returns |
100 | * a callable object, which when called with zero or more arguments, will invoke |
101 | * the original callable with the additional arguments passed to `partial`, |
102 | * followed by those passed to the call. |
103 | * |
104 | * E.g. `partial(Foo, 1, 2)(3)` is equivalent to `Foo(1, 2, 3)`. |
105 | * |
106 | * `partial` can be used to bind a class method to an instance: |
107 | * `partial(&Foo::method, foo_pointer)` returns a callable object that can be |
108 | * invoked in the same way as `foo_pointer->method`. In case the first |
109 | * argument in a call to `partial` is a member pointer, the second argument |
110 | * can be a reference, pointer or any object that can be dereferenced to |
111 | * an object of type Foo (like `std::shared_ptr` or `std::unique_ptr`). |
112 | * |
113 | * `partial` is similar to `std::bind`, but you don't have to use placeholders |
114 | * to have arguments passed on. Any number of arguments passed to the object |
115 | * returned by `partial` when called will be added to those passed to `partial` |
116 | * and passed to the original callable. |
117 | */ |
118 | template <typename F, typename... Args> |
119 | auto partial(F&& f, Args&&... args) -> detail::partial::Partial< |
120 | typename std::decay<F>::type, |
121 | std::tuple<typename std::decay<Args>::type...>> { |
122 | return {detail::partial::PartialConstructFromCallable{}, |
123 | std::forward<F>(f), |
124 | std::forward<Args>(args)...}; |
125 | } |
126 | |
127 | } // namespace folly |
128 | |