Apache Mesos
lambda.hpp
Go to the documentation of this file.
1 // Licensed under the Apache License, Version 2.0 (the "License");
2 // you may not use this file except in compliance with the License.
3 // You may obtain a copy of the License at
4 //
5 // http://www.apache.org/licenses/LICENSE-2.0
6 //
7 // Unless required by applicable law or agreed to in writing, software
8 // distributed under the License is distributed on an "AS IS" BASIS,
9 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10 // See the License for the specific language governing permissions and
11 // limitations under the License.
12 
13 #ifndef __STOUT_LAMBDA_HPP__
14 #define __STOUT_LAMBDA_HPP__
15 
16 #include <algorithm>
17 #include <functional>
18 #include <memory>
19 #include <type_traits>
20 #include <utility>
21 #include <vector>
22 
23 #include <glog/logging.h>
24 
25 #include <stout/cpp14.hpp>
26 #include <stout/cpp17.hpp>
27 #include <stout/result_of.hpp>
28 
29 namespace lambda {
30 
31 using std::bind;
32 using std::cref;
33 using std::function;
34 using std::ref;
35 
36 using namespace std::placeholders;
37 
38 
39 template <
40  template <typename...> class Iterable,
41  typename F,
42  typename U,
43  typename V = typename result_of<F(U)>::type,
44  typename... Us>
45 Iterable<V> map(F&& f, const Iterable<U, Us...>& input)
46 {
47  Iterable<V> output;
49  input.begin(),
50  input.end(),
51  std::inserter(output, output.begin()),
52  std::forward<F>(f));
53  return output;
54 }
55 
56 
57 template <
58  template <typename...> class OutputIterable,
59  template <typename...> class InputIterable,
60  typename F,
61  typename U,
62  typename V = typename result_of<F(U)>::type,
63  typename... Us>
64 OutputIterable<V> map(F&& f, const InputIterable<U, Us...>& input)
65 {
66  OutputIterable<V> output;
68  input.begin(),
69  input.end(),
70  std::inserter(output, output.begin()),
71  std::forward<F>(f));
72  return output;
73 }
74 
75 
76 template <
77  template <typename...> class Iterable,
78  typename F,
79  typename U,
80  typename V = typename result_of<F(U)>::type,
81  typename = typename std::enable_if<
82  !std::is_same<U, V>::value>::type,
83  typename... Us>
84 Iterable<V> map(F&& f, Iterable<U, Us...>&& input)
85 {
86  Iterable<V> output;
88  std::make_move_iterator(input.begin()),
89  std::make_move_iterator(input.end()),
90  std::inserter(output, output.begin()),
91  std::forward<F>(f));
92  return output;
93 }
94 
95 
96 template <
97  template <typename...> class Iterable,
98  typename F,
99  typename U,
100  typename = typename std::enable_if<
102  typename... Us>
103 Iterable<U, Us...>&& map(F&& f, Iterable<U, Us...>&& iterable)
104 {
106  std::make_move_iterator(iterable.begin()),
107  std::make_move_iterator(iterable.end()),
108  iterable.begin(),
109  std::forward<F>(f));
110  return std::move(iterable);
111 }
112 
113 
114 template <
115  template <typename...> class OutputIterable,
116  template <typename...> class InputIterable,
117  typename F,
118  typename U,
119  typename V = typename result_of<F(U)>::type,
120  typename... Us>
121 OutputIterable<V> map(F&& f, InputIterable<U, Us...>&& input)
122 {
123  OutputIterable<V> output;
125  std::make_move_iterator(input.begin()),
126  std::make_move_iterator(input.end()),
127  std::inserter(output, output.begin()),
128  std::forward<F>(f));
129  return output;
130 }
131 
132 
133 template <
134  template <typename...> class OutputIterable,
135  typename F,
136  typename U,
137  typename V = typename result_of<F(U)>::type>
138 OutputIterable<V> map(F&& f, std::initializer_list<U> input)
139 {
140  OutputIterable<V> output;
142  input.begin(),
143  input.end(),
144  std::inserter(output, output.begin()),
145  std::forward<F>(f));
146  return output;
147 }
148 
149 
150 template <
151  typename F,
152  typename U,
153  typename V = typename result_of<F(U)>::type>
154 std::vector<V> map(F&& f, std::initializer_list<U> input)
155 {
156  std::vector<V> output;
158  input.begin(),
159  input.end(),
160  std::inserter(output, output.begin()),
161  std::forward<F>(f));
162  return output;
163 }
164 
165 
166 #define RETURN(...) -> decltype(__VA_ARGS__) { return __VA_ARGS__; }
167 
168 
169 namespace internal {
170 
171 // The `int` specializations here for `is_placeholder<T>::value`.
172 // `is_placeholder<T>::value` returns a `0` for non-placeholders,
173 // and I > 0 for placeholders where I indicates the placeholder
174 // value. e.g., `is_placeholder<decltype(_1)>::value == 1`
175 
176 template <int I>
177 struct Expand
178 {
179  // Bound argument is a placeholder.
180  template <typename T, typename Args>
181  auto operator()(T&&, Args&& args) const
182  RETURN(std::get<I - 1>(std::forward<Args>(args)))
183 };
184 
185 
186 template <>
187 struct Expand<0>
188 {
189  // Bound argument is not a placeholder.
190  template <typename T, typename Args>
191  auto operator()(T&& t, Args&&) const
192  RETURN(std::forward<T>(t))
193 };
194 
195 
196 template <typename F, typename... BoundArgs>
197 class Partial
198 {
199  F f;
200  std::tuple<BoundArgs...> bound_args;
201 
202  template <typename T, typename Args>
203  static auto expand(T&& t, Args&& args)
204  RETURN(Expand<std::is_placeholder<typename std::decay<T>::type>::value>{}(
205  std::forward<T>(t), std::forward<Args>(args)))
206 
207  // Invoke the given function `f` with bound arguments expanded. If a bound
208  // argument is a placeholder, we use the index `I` of the placeholder to
209  // pass the `I`th argument out of `args` along. Otherwise, we pass the bound
210  // argument through preserving its value category. That is, passing the bound
211  // argument as an lvalue-ref or rvalue-ref depending correspondingly on
212  // whether the `Partial` itself is an lvalue or rvalue.
213  template <typename F_, typename BoundArgs_, typename Args, std::size_t... Is>
214  static auto invoke_expand(
215  F_&& f,
216  BoundArgs_&& bound_args,
218  Args&& args)
219  RETURN(cpp17::invoke(
220  std::forward<F_>(f),
221  expand(
222  std::get<Is>(std::forward<BoundArgs_>(bound_args)),
223  std::forward<Args>(args))...))
224 
225 public:
226  template <typename... BoundArgs_>
227  explicit Partial(const F& f, BoundArgs_&&... args)
228  : f(f), bound_args(std::forward<BoundArgs_>(args)...) {}
229 
230  template <typename... BoundArgs_>
231  explicit Partial(F&& f, BoundArgs_&&... args)
232  : f(std::move(f)), bound_args(std::forward<BoundArgs_>(args)...) {}
233 
234  Partial(const Partial&) = default;
235  Partial(Partial&&) = default;
236 
237  Partial& operator=(const Partial&) = default;
238  Partial& operator=(Partial&&) = default;
239 
240  template <typename... Args>
241  auto operator()(Args&&... args) &
242  RETURN(invoke_expand(
243  f,
244  bound_args,
245  cpp14::make_index_sequence<sizeof...(BoundArgs)>(),
246  std::forward_as_tuple(std::forward<Args>(args)...)))
247 
248  template <typename... Args>
249  auto operator()(Args&&... args) const &
250  RETURN(invoke_expand(
251  f,
252  bound_args,
253  cpp14::make_index_sequence<sizeof...(BoundArgs)>(),
254  std::forward_as_tuple(std::forward<Args>(args)...)))
255 
256  template <typename... Args>
257  auto operator()(Args&&... args) &&
258  RETURN(invoke_expand(
259  std::move(f),
260  std::move(bound_args),
261  cpp14::make_index_sequence<sizeof...(BoundArgs)>(),
262  std::forward_as_tuple(std::forward<Args>(args)...)))
263 
264  template <typename... Args>
265  auto operator()(Args&&... args) const &&
266  RETURN(invoke_expand(
267  std::move(f),
268  std::move(bound_args),
269  cpp14::make_index_sequence<sizeof...(BoundArgs)>(),
270  std::forward_as_tuple(std::forward<Args>(args)...)))
271 };
272 
273 } // namespace internal {
274 
275 
276 // Performs partial function application, similar to `std::bind`. However,
277 // it supports moving the bound arguments through, unlike `std::bind`.
278 // To do so, the `operator()` must be invoked on a rvalue `lambda::partial`.
279 //
280 // Unsupported `std::bind` features:
281 // - There is no special treatment for nested bind expressions. When calling
282 // `operator()` on partial, call parameters will not be passed to nested
283 // bind expression. Instead, bind expression will be passed as-is to the
284 // wrapped function object. This behavior is intentional, for simplicity
285 // reasons, and is in sync with C++20's `std::bind_front`.
286 // - Passing `std::reference_wrapper` is not implemented.
287 template <typename F, typename... Args>
289  typename std::decay<F>::type,
290  typename std::decay<Args>::type...>
291 partial(F&& f, Args&&... args)
292 {
293  using R = internal::Partial<
294  typename std::decay<F>::type,
295  typename std::decay<Args>::type...>;
296  return R(std::forward<F>(f), std::forward<Args>(args)...);
297 }
298 
299 
300 #undef RETURN
301 
302 
303 namespace internal {
304 
305 // Helper for invoking functional objects.
306 // It needs specialization for `void` return type to ignore potentialy
307 // non-`void` return value from `cpp17::invoke(f, args...)`.
308 template <typename R>
309 struct Invoke
310 {
311  template <typename F, typename... Args>
312  R operator()(F&& f, Args&&... args)
313  {
314  return cpp17::invoke(std::forward<F>(f), std::forward<Args>(args)...);
315  }
316 };
317 
318 
319 template <>
320 struct Invoke<void>
321 {
322  template <typename F, typename... Args>
323  void operator()(F&& f, Args&&... args)
324  {
325  cpp17::invoke(std::forward<F>(f), std::forward<Args>(args)...);
326  }
327 };
328 
329 } // namespace internal {
330 
331 
332 // This is similar to `std::function`, but it can only be called once.
333 // The "called once" semantics is enforced by having rvalue-ref qualifier
334 // on `operator()`, so instances of `CallableOnce` must be `std::move`'d
335 // in order to be invoked. Similar to `std::function`, this has heap
336 // allocation overhead due to type erasure.
337 //
338 // Note: Heap allocation can be avoided in some cases by implementing
339 // small buffer optimization. This is currently not implemented.
340 template <typename F>
342 
343 
344 template <typename R, typename... Args>
345 class CallableOnce<R(Args...)>
346 {
347 public:
348  template <
349  typename F,
350  typename std::enable_if<
351  !std::is_same<F, CallableOnce>::value &&
352  (std::is_same<R, void>::value ||
353  std::is_convertible<
354  decltype(
355  cpp17::invoke(std::declval<F>(), std::declval<Args>()...)),
356  R>::value),
357  int>::type = 0>
359  : f(new CallableFn<typename std::decay<F>::type>(std::forward<F>(f))) {}
360 
361  CallableOnce(CallableOnce&&) = default;
362  CallableOnce(const CallableOnce&) = delete;
363 
364  CallableOnce& operator=(CallableOnce&&) = default;
365  CallableOnce& operator=(const CallableOnce&) = delete;
366 
367  R operator()(Args... args) &&
368  {
369  CHECK(f != nullptr);
370  return std::move(*f)(std::forward<Args>(args)...);
371  }
372 
373 private:
374  struct Callable
375  {
376  virtual ~Callable() = default;
377  virtual R operator()(Args&&...) && = 0;
378  };
379 
380  template <typename F>
381  struct CallableFn : Callable
382  {
383  F f;
384 
385  CallableFn(const F& f) : f(f) {}
386  CallableFn(F&& f) : f(std::move(f)) {}
387 
388  virtual R operator()(Args&&... args) &&
389  {
390  return internal::Invoke<R>{}(std::move(f), std::forward<Args>(args)...);
391  }
392  };
393 
394  std::unique_ptr<Callable> f;
395 };
396 
397 } // namespace lambda {
398 
399 
400 namespace std {
401 
402 template <typename F, typename... Args>
403 struct is_bind_expression<lambda::internal::Partial<F, Args...>>
404  : true_type {};
405 
406 } // namespace std {
407 
408 #endif // __STOUT_LAMBDA_HPP__
F && f
Definition: defer.hpp:270
CallableOnce(F &&f)
Definition: lambda.hpp:358
internal::Partial< typename std::decay< F >::type, typename std::decay< Args >::type...> partial(F &&f, Args &&...args)
Definition: lambda.hpp:291
Definition: cpp14.hpp:31
void operator()(F &&f, Args &&...args)
Definition: lambda.hpp:323
R operator()(F &&f, Args &&...args)
Definition: lambda.hpp:312
Definition: lambda.hpp:197
Definition: lambda.hpp:177
Iterable< V > map(F &&f, const Iterable< U, Us...> &input)
Definition: lambda.hpp:45
make_integer_sequence< std::size_t, N > make_index_sequence
Definition: cpp14.hpp:61
process::Future< Nothing > transform(process::Owned< Reader< T >> &&reader, const std::function< std::string(const T &)> &func, process::http::Pipe::Writer writer)
This is a helper function that reads records from a Reader, applies a transformation to the records a...
Definition: recordio.hpp:112
R operator()(Args...args)&&
Definition: lambda.hpp:367
Try< uint32_t > type(const std::string &path)
Definition: lambda.hpp:309
Try< Nothing > bind(int_fd s, const Address &address)
Definition: network.hpp:46
#define RETURN(...)
Definition: lambda.hpp:166
Definition: lambda.hpp:341