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
29
31
33 using std::cref;
34 using std::function;
35 using std::ref;
36
38
39
40 template <
41 template <typename...> class Iterable,
42 typename F,
43 typename U,
45 typename... Us>
46 Iterable<V>
map(F&&
f,
const Iterable<U, Us...>& input)
47 {
48 Iterable<V> output;
50 input.begin(),
51 input.end(),
52 std::inserter(output, output.begin()),
54 return output;
55 }
56
57
58 template <
59 template <typename...> class OutputIterable,
60 template <typename...> class InputIterable,
61 typename F,
62 typename U,
64 typename... Us>
65 OutputIterable<V>
map(F&&
f,
const InputIterable<U, Us...>& input)
66 {
67 OutputIterable<V> output;
69 input.begin(),
70 input.end(),
71 std::inserter(output, output.begin()),
73 return output;
74 }
75
76
77 template <
78 template <typename...> class Iterable,
79 typename F,
80 typename U,
82 typename = typename std::enable_if<
83 !std::is_same<U, V>::value>
::type,
84 typename... Us>
85 Iterable<V>
map(F&&
f, Iterable<U, Us...>&& input)
86 {
87 Iterable<V> output;
89 std::make_move_iterator(input.begin()),
90 std::make_move_iterator(input.end()),
91 std::inserter(output, output.begin()),
93 return output;
94 }
95
96
97 template <
98 template <typename...> class Iterable,
99 typename F,
100 typename U,
101 typename = typename std::enable_if<
103 typename... Us>
104 Iterable<U, Us...>&&
map(F&&
f, Iterable<U, Us...>&& iterable)
105 {
107 std::make_move_iterator(iterable.begin()),
108 std::make_move_iterator(iterable.end()),
109 iterable.begin(),
111 return std::move(iterable);
112 }
113
114
115 template <
116 template <typename...> class OutputIterable,
117 template <typename...> class InputIterable,
118 typename F,
119 typename U,
121 typename... Us>
122 OutputIterable<V>
map(F&&
f, InputIterable<U, Us...>&& input)
123 {
124 OutputIterable<V> output;
126 std::make_move_iterator(input.begin()),
127 std::make_move_iterator(input.end()),
128 std::inserter(output, output.begin()),
130 return output;
131 }
132
133
134 template <
135 template <typename...> class OutputIterable,
136 typename F,
137 typename U,
139 OutputIterable<V>
map(F&&
f, std::initializer_list<U> input)
140 {
141 OutputIterable<V> output;
143 input.begin(),
144 input.end(),
145 std::inserter(output, output.begin()),
147 return output;
148 }
149
150
151 template <
152 typename F,
153 typename U,
155 std::vector<V>
map(F&&
f, std::initializer_list<U> input)
156 {
157 std::vector<V> output;
159 input.begin(),
160 input.end(),
161 std::inserter(output, output.begin()),
163 return output;
164 }
165
166
167 // TODO(arojas): Make this generic enough such that an arbitrary
168 // number of inputs can be used.
169 // It would be nice to be able to choose between `std::pair` and
170 // `std::tuple` or other heterogeneous compilers.
171 template <
172 template <typename...> class OutputIterable,
173 template <typename...> class InputIterable1,
174 template <typename...> class InputIterable2,
175 typename U1,
176 typename U2,
177 typename... U1s,
178 typename... U2s>
179 OutputIterable<std::pair<U1, U2>>
zipto(
180 const InputIterable1<U1, U1s...>& input1,
181 const InputIterable2<U2, U2s...>& input2)
182 {
183 OutputIterable<std::pair<U1, U2>> output;
184
185 auto iterator1 = input1.begin();
186 auto iterator2 = input2.begin();
187
188 auto inserter = std::inserter(output, output.begin());
189
190 // We zip only as many elements as we can.
191 while (iterator1 != input1.end() && iterator2 != input2.end()) {
192 inserter = std::make_pair(*iterator1, *iterator2);
193 iterator1++;
194 iterator2++;
195 }
196
197 return output;
198 }
199
200
201 // TODO(arojas): Make this generic enough such that the output
202 // container type can be parametrized, i.e. `hashmap`, `std::unordered_map`,
203 // `std::map`, `std::vector<std::pair<U1, U2>`.
204 // NOTE: by default we zip into a `hashmap`. See the `zip()` overload
205 // for zipping into another iterable as `std::pair`.
206 template <
207 template <typename...> class InputIterable1,
208 template <typename...> class InputIterable2,
209 typename U1,
210 typename U2,
211 typename... U1s,
212 typename... U2s>
214 const InputIterable1<U1, U1s...>& input1,
215 const InputIterable2<U2, U2s...>& input2)
216 {
217 // TODO(benh): Use the overload of `zip()`, something like:
218 // std::vector<std::pair<U1, U2>> vector = zip<std::vector>(input1, input2);
219 // return hashmap<U1, U2>(
220 // std::make_move_iterator(vector.begin()),
221 // std::make_move_iterator(vector.end()));
222
224
225 auto iterator1 = input1.begin();
226 auto iterator2 = input2.begin();
227
228 // We zip only as many elements as we can.
229 while (iterator1 != input1.end() && iterator2 != input2.end()) {
230 output.
put(*iterator1, *iterator2);
231 iterator1++;
232 iterator2++;
233 }
234
235 return output;
236 }
237
238
239 #define RETURN(...) -> decltype(__VA_ARGS__) { return __VA_ARGS__; } 240
241
243
244 // The `int` specializations here for `is_placeholder<T>::value`.
245 // `is_placeholder<T>::value` returns a `0` for non-placeholders,
246 // and I > 0 for placeholders where I indicates the placeholder
247 // value. e.g., `is_placeholder<decltype(_1)>::value == 1`
248
249 template <int I>
251 {
252 // Bound argument is a placeholder.
253 template <typename T, typename Args>
254 auto operator()(T&&, Args&& args) const
255 RETURN(std::get<I - 1>(std::forward<Args>(args)))
256 };
257
258
259 template <>
261 {
262 // Bound argument is not a placeholder.
263 template <typename T, typename Args>
264 auto operator()(T&& t, Args&&) const
265 RETURN(std::forward<T>(t))
266 };
267
268
269 template <typename F, typename... BoundArgs>
271 {
273 std::tuple<BoundArgs...> bound_args;
274
275 template <typename T, typename Args>
276 static auto expand(T&& t, Args&& args)
278 std::forward<T>(t), std::forward<Args>(args)))
279
280 // Invoke the given function `f` with bound arguments expanded. If a bound
281 // argument is a placeholder, we use the index `I` of the placeholder to
282 // pass the `I`th argument out of `args` along. Otherwise, we pass the bound
283 // argument through preserving its value category. That is, passing the bound
284 // argument as an lvalue-ref or rvalue-ref depending correspondingly on
285 // whether the `Partial` itself is an lvalue or rvalue.
286 template <typename F_, typename BoundArgs_, typename Args, std::size_t... Is>
287 static auto invoke_expand(
288 F_&& f,
289 BoundArgs_&& bound_args,
291 Args&& args)
293 std::forward<F_>(f),
294 expand(
295 std::get<Is>(std::forward<BoundArgs_>(bound_args)),
296 std::forward<Args>(args))...))
297
298 public:
299 template <typename... BoundArgs_>
300 explicit Partial(
const F& f, BoundArgs_&&... args)
301 :
f(f), bound_args(std::forward<BoundArgs_>(args)...) {}
302
303 template <typename... BoundArgs_>
304 explicit Partial(F&& f, BoundArgs_&&... args)
305 :
f(std::move(f)), bound_args(std::forward<BoundArgs_>(args)...) {}
306
307 Partial(const Partial&) = default;
308 Partial(Partial&&) = default;
309
310 Partial& operator=(const Partial&) = default;
311 Partial& operator=(Partial&&) = default;
312
313 template <typename... Args>
314 auto operator()(Args&&... args) &
316 f,
317 bound_args,
319 std::forward_as_tuple(std::forward<Args>(args)...)))
320
321 template <typename... Args>
322 auto operator()(Args&&... args) const &
324 f,
325 bound_args,
327 std::forward_as_tuple(std::forward<Args>(args)...)))
328
329 template <typename... Args>
330 auto operator()(Args&&... args) &&
332 std::move(f),
333 std::move(bound_args),
335 std::forward_as_tuple(std::forward<Args>(args)...)))
336
337 template <typename... Args>
338 auto operator()(Args&&... args) const &&
340 std::move(f),
341 std::move(bound_args),
343 std::forward_as_tuple(std::forward<Args>(args)...)))
344 };
345
346 } // namespace internal {
347
348
349 // Performs partial function application, similar to `std::bind`. However,
350 // it supports moving the bound arguments through, unlike `std::bind`.
351 // To do so, the `operator()` must be invoked on a rvalue `lambda::partial`.
352 //
353 // Unsupported `std::bind` features:
354 // - There is no special treatment for nested bind expressions. When calling
355 // `operator()` on partial, call parameters will not be passed to nested
356 // bind expression. Instead, bind expression will be passed as-is to the
357 // wrapped function object. This behavior is intentional, for simplicity
358 // reasons, and is in sync with C++20's `std::bind_front`.
359 // - Passing `std::reference_wrapper` is not implemented.
360 template <typename F, typename... Args>
365 {
367 typename std::decay<F>::type,
369 return R(std::forward<F>(
f), std::forward<Args>(args)...);
370 }
371
372
373 #undef RETURN
374
375
377
378 // Helper for invoking functional objects.
379 // It needs specialization for `void` return type to ignore potentialy
380 // non-`void` return value from `cpp17::invoke(f, args...)`.
381 template <typename R>
383 {
384 template <typename F, typename... Args>
386 {
387 return cpp17::invoke(std::forward<F>(
f), std::forward<Args>(args)...);
388 }
389 };
390
391
392 template <>
394 {
395 template <typename F, typename... Args>
397 {
398 cpp17::invoke(std::forward<F>(
f), std::forward<Args>(args)...);
399 }
400 };
401
402 } // namespace internal {
403
404
405 // This is similar to `std::function`, but it can only be called once.
406 // The "called once" semantics is enforced by having rvalue-ref qualifier
407 // on `operator()`, so instances of `CallableOnce` must be `std::move`'d
408 // in order to be invoked. Similar to `std::function`, this has heap
409 // allocation overhead due to type erasure.
410 //
411 // Note: Heap allocation can be avoided in some cases by implementing
412 // small buffer optimization. This is currently not implemented.
413 template <typename F>
415
416
417 template <typename R, typename... Args>
419 {
420 public:
421 template <
422 typename F,
423 typename std::enable_if<
424 !std::is_same<F, CallableOnce>::value &&
425 (std::is_same<R, void>::value ||
426 std::is_convertible<
427 decltype(
428 cpp17::invoke(std::declval<F>(), std::declval<Args>()...)),
429 R>::value),
432 :
f(
new CallableFn<
typename std::decay<F>::type>(std::forward<F>(
f))) {}
433
436
439
441 {
443 return std::move(*
f)(std::forward<Args>(args)...);
444 }
445
446 private:
447 struct Callable
448 {
449 virtual ~Callable() = default;
450 virtual R operator()(Args&&...) && = 0;
451 };
452
453 template <typename F>
454 struct CallableFn : Callable
455 {
457
458 CallableFn(
const F& f) :
f(f) {}
459 CallableFn(F&& f) :
f(std::move(f)) {}
460
461 R operator()(Args&&... args) && override
462 {
464 }
465 };
466
467 std::unique_ptr<Callable>
f;
468 };
469
470 } // namespace lambda {
471
472
474
475 template <typename F, typename... Args>
476 struct is_bind_expression<
lambda::internal::Partial<F, Args...>>
477 : true_type {};
478
479 } // namespace std {
480
481 #endif // __STOUT_LAMBDA_HPP__
F && f
Definition: defer.hpp:270
CallableOnce(F &&f)
Definition: lambda.hpp:431
hashmap< U1, U2 > zip(const InputIterable1< U1, U1s... > &input1, const InputIterable2< U2, U2s... > &input2)
Definition: lambda.hpp:213
internal::Partial< typename std::decay< F >::type, typename std::decay< Args >::type... > partial(F &&f, Args &&...args)
Definition: lambda.hpp:364
Definition: type_utils.hpp:619
Definition: lambda.hpp:30
void operator()(F &&f, Args &&...args)
Definition: lambda.hpp:396
R operator()(F &&f, Args &&...args)
Definition: lambda.hpp:385
Definition: hashmap.hpp:38
Definition: lambda.hpp:270
Definition: lambda.hpp:250
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
void put(const Key &key, Value &&value)
Definition: hashmap.hpp:104
Iterable< V > map(F &&f, const Iterable< U, Us... > &input)
Definition: lambda.hpp:46
Definition: attributes.hpp:24
R operator()(Args...args)&&
Definition: lambda.hpp:440
Try< uint32_t > type(const std::string &path)
OutputIterable< std::pair< U1, U2 > > zipto(const InputIterable1< U1, U1s... > &input1, const InputIterable2< U2, U2s... > &input2)
Definition: lambda.hpp:179
Definition: lambda.hpp:382
Try< Nothing > bind(int_fd s, const Address &address)
Definition: network.hpp:46
#define RETURN(...)
Definition: lambda.hpp:239
Definition: lambda.hpp:414