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
25namespace folly {
26
27namespace detail {
28namespace partial {
29
30// helper type to make sure that the templated constructor in Partial does
31// not accidentally act as copy or move constructor
32struct PartialConstructFromCallable {};
33
34template <typename F, typename Tuple>
35class 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 */
118template <typename F, typename... Args>
119auto 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