2 * Copyright 2015 Facebook, Inc.
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
17 #ifndef FOLLY_GEN_BASE_H
18 #error This file may only be included from folly/gen/Base.h
21 // Ignore shadowing warnings within this file, so includers can use -Wshadow.
22 #pragma GCC diagnostic push
23 #pragma GCC diagnostic ignored "-Wshadow"
25 namespace folly { namespace gen {
28 * ArgumentReference - For determining ideal argument type to receive a value.
31 struct ArgumentReference
32 : public std::conditional<
33 std::is_reference<T>::value,
34 T, // T& -> T&, T&& -> T&&, const T& -> const T&
35 typename std::conditional<std::is_const<T>::value,
36 T&, // const int -> const int&
40 template <class Key, class Value>
41 class Group : public GenImpl<Value&&, Group<Key, Value>> {
43 static_assert(!std::is_reference<Key>::value &&
44 !std::is_reference<Value>::value,
45 "Key and Value must be decayed types");
47 typedef std::vector<Value> VectorType;
49 typedef Value ValueType;
51 Group(Key key, VectorType values)
52 : key_(std::move(key)), values_(std::move(values)) {}
54 const Key& key() const { return key_; }
56 size_t size() const { return values_.size(); }
57 const VectorType& values() const { return values_; }
58 VectorType& values() { return values_; }
60 VectorType operator|(const detail::Collect<VectorType>&) const {
64 VectorType operator|(const detail::CollectTemplate<std::vector>&) const {
69 void foreach(Body&& body) const {
70 for (auto& value : values_) {
71 body(std::move(value));
75 template <class Handler>
76 bool apply(Handler&& handler) const {
77 for (auto& value : values_) {
78 if (!handler(std::move(value))) {
87 mutable VectorType values_;
93 * ReferencedSource - Generate values from an STL-like container using
94 * iterators from .begin() until .end(). Value type defaults to the type of
95 * *container->begin(). For std::vector<int>, this would be int&. Note that the
96 * value here is a reference, so the values in the vector will be passed by
97 * reference to downstream operators.
99 * This type is primarily used through the 'from' helper method, like:
101 * string& longestName = from(names)
102 * | maxBy([](string& s) { return s.size() });
104 template<class Container,
106 class ReferencedSource :
107 public GenImpl<Value, ReferencedSource<Container, Value>> {
108 Container* container_;
110 explicit ReferencedSource(Container* container)
111 : container_(container) {}
114 void foreach(Body&& body) const {
115 for (auto& value : *container_) {
116 body(std::forward<Value>(value));
120 template<class Handler>
121 bool apply(Handler&& handler) const {
122 for (auto& value : *container_) {
123 if (!handler(std::forward<Value>(value))) {
132 * CopiedSource - For producing values from eagerly from a sequence of values
133 * whose storage is owned by this class. Useful for preparing a generator for
134 * use after a source collection will no longer be available, or for when the
135 * values are specified literally with an initializer list.
137 * This type is primarily used through the 'fromCopy' function, like:
139 * auto sourceCopy = fromCopy(makeAVector());
140 * auto sum = sourceCopy | sum;
141 * auto max = sourceCopy | max;
143 * Though it is also used for the initializer_list specialization of from().
145 template<class StorageType,
148 public GenImpl<const StorageType&,
149 CopiedSource<StorageType, Container>> {
151 !std::is_reference<StorageType>::value, "StorageType must be decayed");
153 // Generator objects are often copied during normal construction as they are
154 // encapsulated by downstream generators. It would be bad if this caused
155 // a copy of the entire container each time, and since we're only exposing a
156 // const reference to the value, it's safe to share it between multiple
159 !std::is_reference<Container>::value,
160 "Can't copy into a reference");
161 std::shared_ptr<const Container> copy_;
163 typedef Container ContainerType;
165 template<class SourceContainer>
166 explicit CopiedSource(const SourceContainer& container)
167 : copy_(new Container(begin(container), end(container))) {}
169 explicit CopiedSource(Container&& container) :
170 copy_(new Container(std::move(container))) {}
172 // To enable re-use of cached results.
173 CopiedSource(const CopiedSource<StorageType, Container>& source)
174 : copy_(source.copy_) {}
177 void foreach(Body&& body) const {
178 for (const auto& value : *copy_) {
183 template<class Handler>
184 bool apply(Handler&& handler) const {
185 // The collection may be reused by others, we can't allow it to be changed.
186 for (const auto& value : *copy_) {
187 if (!handler(value)) {
196 * RangeSource - For producing values from a folly::Range. Useful for referring
197 * to a slice of some container.
199 * This type is primarily used through the 'from' function, like:
201 * auto rangeSource = from(folly::range(v.begin(), v.end()));
202 * auto sum = rangeSource | sum;
204 * Reminder: Be careful not to invalidate iterators when using ranges like this.
206 template<class Iterator>
207 class RangeSource : public GenImpl<typename Range<Iterator>::reference,
208 RangeSource<Iterator>> {
209 Range<Iterator> range_;
211 RangeSource() = default;
212 explicit RangeSource(Range<Iterator> range)
213 : range_(std::move(range))
216 template<class Handler>
217 bool apply(Handler&& handler) const {
218 for (auto& value : range_) {
219 if (!handler(value)) {
227 void foreach(Body&& body) const {
228 for (auto& value : range_) {
235 * Sequence - For generating values from beginning value, incremented along the
236 * way with the ++ and += operators. Iteration may continue indefinitely.
237 * Value type specified explicitly.
239 * This type is primarily used through the 'seq' and 'range' function, like:
241 * int total = seq(1, 10) | sum;
242 * auto indexes = range(0, 10);
243 * auto endless = seq(0); // 0, 1, 2, 3, ...
245 template<class Value, class SequenceImpl>
246 class Sequence : public GenImpl<const Value&, Sequence<Value, SequenceImpl>> {
247 static_assert(!std::is_reference<Value>::value &&
248 !std::is_const<Value>::value, "Value mustn't be const or ref.");
252 explicit Sequence(Value start, SequenceImpl impl)
253 : start_(std::move(start)), impl_(std::move(impl)) { }
255 template<class Handler>
256 bool apply(Handler&& handler) const {
257 for (Value current = start_; impl_.test(current); impl_.step(current)) {
258 if (!handler(current)) {
266 void foreach(Body&& body) const {
267 for (Value current = start_; impl_.test(current); impl_.step(current)) {
274 * Sequence implementations (range, sequence, infinite, with/without step)
276 template<class Value>
280 explicit RangeImpl(Value end) : end_(std::move(end)) { }
281 bool test(const Value& current) const { return current < end_; }
282 void step(Value& current) const { ++current; }
285 template<class Value, class Distance>
286 class RangeWithStepImpl {
290 explicit RangeWithStepImpl(Value end, Distance step)
291 : end_(std::move(end)), step_(std::move(step)) { }
292 bool test(const Value& current) const { return current < end_; }
293 void step(Value& current) const { current += step_; }
296 template<class Value>
300 explicit SeqImpl(Value end) : end_(std::move(end)) { }
301 bool test(const Value& current) const { return current <= end_; }
302 void step(Value& current) const { ++current; }
305 template<class Value, class Distance>
306 class SeqWithStepImpl {
310 explicit SeqWithStepImpl(Value end, Distance step)
311 : end_(std::move(end)), step_(std::move(step)) { }
312 bool test(const Value& current) const { return current <= end_; }
313 void step(Value& current) const { current += step_; }
316 template<class Value>
319 bool test(const Value& current) const { return true; }
320 void step(Value& current) const { ++current; }
324 * GenratorBuilder - Helper for GENERTATOR macro.
326 template<class Value>
327 struct GeneratorBuilder {
328 template<class Source,
329 class Yield = detail::Yield<Value, Source>>
330 Yield operator+(Source&& source) {
331 return Yield(std::forward<Source>(source));
336 * Yield - For producing values from a user-defined generator by way of a
339 template<class Value, class Source>
340 class Yield : public GenImpl<Value, Yield<Value, Source>> {
343 explicit Yield(Source source)
344 : source_(std::move(source)) {
347 template<class Handler>
348 bool apply(Handler&& handler) const {
350 auto body = [&](Value value) {
351 if (!handler(std::forward<Value>(value))) {
364 void foreach(Body&& body) const {
365 source_(std::forward<Body>(body));
369 template<class Value>
370 class Empty : public GenImpl<Value, Empty<Value>> {
372 template <class Handler>
373 bool apply(Handler&&) const {
377 template <class Body>
378 void foreach(Body&&) const {}
381 template <class Value>
382 class SingleReference : public GenImpl<Value&, SingleReference<Value>> {
383 static_assert(!std::is_reference<Value>::value,
384 "SingleReference requires non-ref types");
387 explicit SingleReference(Value& ref) : ptr_(&ref) {}
389 template <class Handler>
390 bool apply(Handler&& handler) const {
391 return handler(*ptr_);
394 template <class Body>
395 void foreach(Body&& body) const {
400 template <class Value>
401 class SingleCopy : public GenImpl<const Value&, SingleCopy<Value>> {
402 static_assert(!std::is_reference<Value>::value,
403 "SingleCopy requires non-ref types");
406 explicit SingleCopy(Value value) : value_(std::forward<Value>(value)) {}
408 template <class Handler>
409 bool apply(Handler&& handler) const {
410 return handler(value_);
413 template <class Body>
414 void foreach(Body&& body) const {
424 * Map - For producing a sequence of values by passing each value from a source
425 * collection through a predicate.
427 * This type is usually used through the 'map' or 'mapped' helper function:
429 * auto squares = seq(1, 10) | map(square) | asVector;
431 template<class Predicate>
432 class Map : public Operator<Map<Predicate>> {
437 explicit Map(Predicate pred)
438 : pred_(std::move(pred))
441 template<class Value,
443 class Result = typename ArgumentReference<
444 typename std::result_of<Predicate(Value)>::type
447 public GenImpl<Result, Generator<Value, Source, Result>> {
451 explicit Generator(Source source, const Predicate& pred)
452 : source_(std::move(source)), pred_(pred) {}
455 void foreach(Body&& body) const {
456 source_.foreach([&](Value value) {
457 body(pred_(std::forward<Value>(value)));
461 template<class Handler>
462 bool apply(Handler&& handler) const {
463 return source_.apply([&](Value value) {
464 return handler(pred_(std::forward<Value>(value)));
468 static constexpr bool infinite = Source::infinite;
471 template<class Source,
473 class Gen = Generator<Value, Source>>
474 Gen compose(GenImpl<Value, Source>&& source) const {
475 return Gen(std::move(source.self()), pred_);
478 template<class Source,
480 class Gen = Generator<Value, Source>>
481 Gen compose(const GenImpl<Value, Source>& source) const {
482 return Gen(source.self(), pred_);
487 * Filter - For filtering values from a source sequence by a predicate.
489 * This type is usually used through the 'filter' helper function, like:
491 * auto nonEmpty = from(strings)
492 * | filter([](const string& str) -> bool {
493 * return !str.empty();
496 * Note that if no predicate is provided, the values are casted to bool and
497 * filtered based on that. So if pointers is a vector of pointers,
499 * auto nonNull = from(pointers) | filter();
501 * will give a vector of all the pointers != nullptr.
503 template<class Predicate>
504 class Filter : public Operator<Filter<Predicate>> {
508 explicit Filter(Predicate pred)
509 : pred_(std::move(pred))
512 template<class Value,
514 class Generator : public GenImpl<Value, Generator<Value, Source>> {
518 explicit Generator(Source source, const Predicate& pred)
519 : source_(std::move(source)), pred_(pred) {}
522 void foreach(Body&& body) const {
523 source_.foreach([&](Value value) {
524 if (pred_(std::forward<Value>(value))) {
525 body(std::forward<Value>(value));
530 template<class Handler>
531 bool apply(Handler&& handler) const {
532 return source_.apply([&](Value value) -> bool {
533 if (pred_(std::forward<Value>(value))) {
534 return handler(std::forward<Value>(value));
540 static constexpr bool infinite = Source::infinite;
543 template<class Source,
545 class Gen = Generator<Value, Source>>
546 Gen compose(GenImpl<Value, Source>&& source) const {
547 return Gen(std::move(source.self()), pred_);
550 template<class Source,
552 class Gen = Generator<Value, Source>>
553 Gen compose(const GenImpl<Value, Source>& source) const {
554 return Gen(source.self(), pred_);
559 * Until - For producing values from a source until a predicate is satisfied.
561 * This type is usually used through the 'until' helper function, like:
563 * auto best = from(sortedItems)
564 * | until([](Item& item) { return item.score > 100; })
567 template<class Predicate>
568 class Until : public Operator<Until<Predicate>> {
572 explicit Until(Predicate pred)
573 : pred_(std::move(pred))
576 template<class Value,
578 class Generator : public GenImpl<Value, Generator<Value, Source>> {
582 explicit Generator(Source source, const Predicate& pred)
583 : source_(std::move(source)), pred_(pred) {}
585 template<class Handler>
586 bool apply(Handler&& handler) const {
587 bool cancelled = false;
588 source_.apply([&](Value value) -> bool {
589 if (pred_(value)) { // un-forwarded to disable move
592 if (!handler(std::forward<Value>(value))) {
602 template<class Source,
604 class Gen = Generator<Value, Source>>
605 Gen compose(GenImpl<Value, Source>&& source) const {
606 return Gen(std::move(source.self()), pred_);
609 template<class Source,
611 class Gen = Generator<Value, Source>>
612 Gen compose(const GenImpl<Value, Source>& source) const {
613 return Gen(source.self(), pred_);
616 // Theoretically an 'until' might stop an infinite
617 static constexpr bool infinite = false;
621 * Take - For producing up to N values from a source.
623 * This type is usually used through the 'take' helper function, like:
625 * auto best = from(docs)
626 * | orderByDescending(scoreDoc)
629 class Take : public Operator<Take> {
632 explicit Take(size_t count)
635 template<class Value,
638 public GenImpl<Value, Generator<Value, Source>> {
642 explicit Generator(Source source, size_t count)
643 : source_(std::move(source)) , count_(count) {}
645 template<class Handler>
646 bool apply(Handler&& handler) const {
647 if (count_ == 0) { return false; }
649 bool cancelled = false;
650 source_.apply([&](Value value) -> bool {
651 if (!handler(std::forward<Value>(value))) {
661 template<class Source,
663 class Gen = Generator<Value, Source>>
664 Gen compose(GenImpl<Value, Source>&& source) const {
665 return Gen(std::move(source.self()), count_);
668 template<class Source,
670 class Gen = Generator<Value, Source>>
671 Gen compose(const GenImpl<Value, Source>& source) const {
672 return Gen(source.self(), count_);
677 * Stride - For producing every Nth value from a source.
679 * This type is usually used through the 'stride' helper function, like:
681 * auto half = from(samples)
684 class Stride : public Operator<Stride> {
688 explicit Stride(size_t stride) : stride_(stride) {
690 throw std::invalid_argument("stride must not be 0");
694 template <class Value, class Source>
695 class Generator : public GenImpl<Value, Generator<Value, Source>> {
699 Generator(Source source, size_t stride)
700 : source_(std::move(source)), stride_(stride) {}
702 template <class Handler>
703 bool apply(Handler&& handler) const {
704 size_t distance = stride_;
705 return source_.apply([&](Value value)->bool {
706 if (++distance >= stride_) {
707 if (!handler(std::forward<Value>(value))) {
716 template <class Body>
717 void foreach(Body&& body) const {
718 size_t distance = stride_;
719 source_.foreach([&](Value value) {
720 if (++distance >= stride_) {
721 body(std::forward<Value>(value));
728 template <class Source, class Value, class Gen = Generator<Value, Source>>
729 Gen compose(GenImpl<Value, Source>&& source) const {
730 return Gen(std::move(source.self()), stride_);
733 template <class Source, class Value, class Gen = Generator<Value, Source>>
734 Gen compose(const GenImpl<Value, Source>& source) const {
735 return Gen(source.self(), stride_);
740 * Sample - For taking a random sample of N elements from a sequence
741 * (without replacement).
743 template<class Random>
744 class Sample : public Operator<Sample<Random>> {
748 explicit Sample(size_t count, Random rng)
749 : count_(count), rng_(std::move(rng)) {}
751 template<class Value,
754 class StorageType = typename std::decay<Value>::type>
756 public GenImpl<StorageType&&,
757 Generator<Value, Source, Rand, StorageType>> {
758 static_assert(!Source::infinite, "Cannot sample infinite source!");
759 // It's too easy to bite ourselves if random generator is only 16-bit
760 static_assert(Random::max() >= std::numeric_limits<int32_t>::max() - 1,
761 "Random number generator must support big values");
766 explicit Generator(Source source, size_t count, Random rng)
767 : source_(std::move(source)) , count_(count), rng_(std::move(rng)) {}
769 template<class Handler>
770 bool apply(Handler&& handler) const {
771 if (count_ == 0) { return false; }
772 std::vector<StorageType> v;
774 // use reservoir sampling to give each source value an equal chance
775 // of appearing in our output.
777 source_.foreach([&](Value value) -> void {
778 if (v.size() < count_) {
779 v.push_back(std::forward<Value>(value));
781 // alternatively, we could create a std::uniform_int_distribution
782 // instead of using modulus, but benchmarks show this has
783 // substantial overhead.
784 size_t index = rng_() % n;
785 if (index < v.size()) {
786 v[index] = std::forward<Value>(value);
792 // output is unsorted!
794 if (!handler(std::move(val))) {
802 template<class Source,
804 class Gen = Generator<Value, Source, Random>>
805 Gen compose(GenImpl<Value, Source>&& source) const {
806 return Gen(std::move(source.self()), count_, rng_);
809 template<class Source,
811 class Gen = Generator<Value, Source, Random>>
812 Gen compose(const GenImpl<Value, Source>& source) const {
813 return Gen(source.self(), count_, rng_);
818 * Skip - For skipping N items from the beginning of a source generator.
820 * This type is usually used through the 'skip' helper function, like:
822 * auto page = from(results)
823 * | skip(pageSize * startPage)
826 class Skip : public Operator<Skip> {
829 explicit Skip(size_t count)
832 template<class Value,
835 public GenImpl<Value, Generator<Value, Source>> {
839 explicit Generator(Source source, size_t count)
840 : source_(std::move(source)) , count_(count) {}
843 void foreach(Body&& body) const {
845 source_.foreach(body);
849 source_.foreach([&](Value value) {
853 body(std::forward<Value>(value));
858 template<class Handler>
859 bool apply(Handler&& handler) const {
861 return source_.apply(std::forward<Handler>(handler));
864 return source_.apply([&](Value value) -> bool {
869 return handler(std::forward<Value>(value));
873 static constexpr bool infinite = Source::infinite;
876 template<class Source,
878 class Gen = Generator<Value, Source>>
879 Gen compose(GenImpl<Value, Source>&& source) const {
880 return Gen(std::move(source.self()), count_);
883 template<class Source,
885 class Gen = Generator<Value, Source>>
886 Gen compose(const GenImpl<Value, Source>& source) const {
887 return Gen(source.self(), count_);
892 * Order - For ordering a sequence of values from a source by key.
893 * The key is extracted by the given selector functor, and this key is then
894 * compared using the specified comparator.
896 * This type is usually used through the 'order' helper function, like:
898 * auto closest = from(places)
899 * | orderBy([](Place& p) {
900 * return -distance(p.location, here);
904 template<class Selector, class Comparer>
905 class Order : public Operator<Order<Selector, Comparer>> {
911 explicit Order(Selector selector)
912 : selector_(std::move(selector))
915 Order(Selector selector,
917 : selector_(std::move(selector))
918 , comparer_(std::move(comparer))
921 template<class Value,
923 class StorageType = typename std::decay<Value>::type,
924 class Result = typename std::result_of<Selector(Value)>::type>
926 public GenImpl<StorageType&&,
927 Generator<Value, Source, StorageType, Result>> {
928 static_assert(!Source::infinite, "Cannot sort infinite source!");
933 typedef std::vector<StorageType> VectorType;
935 VectorType asVector() const {
936 auto comparer = [&](const StorageType& a, const StorageType& b) {
937 return comparer_(selector_(a), selector_(b));
939 auto vals = source_ | as<VectorType>();
940 std::sort(vals.begin(), vals.end(), comparer);
941 return std::move(vals);
944 Generator(Source source,
947 : source_(std::move(source)),
948 selector_(std::move(selector)),
949 comparer_(std::move(comparer)) {}
951 VectorType operator|(const Collect<VectorType>&) const {
955 VectorType operator|(const CollectTemplate<std::vector>&) const {
960 void foreach(Body&& body) const {
961 for (auto& value : asVector()) {
962 body(std::move(value));
966 template<class Handler>
967 bool apply(Handler&& handler) const {
968 auto comparer = [&](const StorageType& a, const StorageType& b) {
969 // swapped for minHeap
970 return comparer_(selector_(b), selector_(a));
972 auto heap = source_ | as<VectorType>();
973 std::make_heap(heap.begin(), heap.end(), comparer);
974 while (!heap.empty()) {
975 std::pop_heap(heap.begin(), heap.end(), comparer);
976 if (!handler(std::move(heap.back()))) {
985 template<class Source,
987 class Gen = Generator<Value, Source>>
988 Gen compose(GenImpl<Value, Source>&& source) const {
989 return Gen(std::move(source.self()), selector_, comparer_);
992 template<class Source,
994 class Gen = Generator<Value, Source>>
995 Gen compose(const GenImpl<Value, Source>& source) const {
996 return Gen(source.self(), selector_, comparer_);
1001 * GroupBy - Group values by a given key selector, producing a sequence of
1004 * This type is usually used through the 'groupBy' helper function, like:
1008 * | groupBy([](const Place& p) {
1011 * | [](Group<std::string, Place>&& g) {
1012 * cout << g.key() << ": " << (g | first).description;
1015 template <class Selector>
1016 class GroupBy : public Operator<GroupBy<Selector>> {
1022 explicit GroupBy(Selector selector) : selector_(std::move(selector)) {}
1024 template <class Value,
1026 class ValueDecayed = typename std::decay<Value>::type,
1027 class Key = typename std::result_of<Selector(Value)>::type,
1028 class KeyDecayed = typename std::decay<Key>::type>
1031 Group<KeyDecayed, ValueDecayed>&&,
1032 Generator<Value, Source, ValueDecayed, Key, KeyDecayed>> {
1033 static_assert(!Source::infinite, "Cannot group infinite source!");
1038 Generator(Source source, Selector selector)
1039 : source_(std::move(source)), selector_(std::move(selector)) {}
1041 typedef Group<KeyDecayed, ValueDecayed> GroupType;
1043 template <class Handler>
1044 bool apply(Handler&& handler) const {
1045 std::unordered_map<KeyDecayed, typename GroupType::VectorType> groups;
1046 source_ | [&](Value value) {
1047 const Value& cv = value;
1048 auto& group = groups[selector_(cv)];
1049 group.push_back(std::forward<Value>(value));
1051 for (auto& kg : groups) {
1052 GroupType group(kg.first, std::move(kg.second));
1053 if (!handler(std::move(group))) {
1062 template <class Source, class Value, class Gen = Generator<Value, Source>>
1063 Gen compose(GenImpl<Value, Source>&& source) const {
1064 return Gen(std::move(source.self()), selector_);
1067 template <class Source, class Value, class Gen = Generator<Value, Source>>
1068 Gen compose(const GenImpl<Value, Source>& source) const {
1069 return Gen(source.self(), selector_);
1074 * TypeAssertion - For verifying the exact type of the value produced by a
1075 * generator. Useful for testing and debugging, and acts as a no-op at runtime.
1076 * Pass-through at runtime. Used through the 'assert_type<>()' factory method
1079 * auto c = from(vector) | assert_type<int&>() | sum;
1082 template<class Expected>
1083 class TypeAssertion : public Operator<TypeAssertion<Expected>> {
1085 template<class Source, class Value>
1086 const Source& compose(const GenImpl<Value, Source>& source) const {
1087 static_assert(std::is_same<Expected, Value>::value,
1088 "assert_type() check failed");
1089 return source.self();
1092 template<class Source, class Value>
1093 Source&& compose(GenImpl<Value, Source>&& source) const {
1094 static_assert(std::is_same<Expected, Value>::value,
1095 "assert_type() check failed");
1096 return std::move(source.self());
1101 * Distinct - For filtering duplicates out of a sequence. A selector may be
1102 * provided to generate a key to uniquify for each value.
1104 * This type is usually used through the 'distinct' helper function, like:
1106 * auto closest = from(results)
1107 * | distinctBy([](Item& i) {
1112 template<class Selector>
1113 class Distinct : public Operator<Distinct<Selector>> {
1116 Distinct() = default;
1118 explicit Distinct(Selector selector)
1119 : selector_(std::move(selector))
1122 template<class Value,
1124 class Generator : public GenImpl<Value, Generator<Value, Source>> {
1128 typedef typename std::decay<Value>::type StorageType;
1130 // selector_ cannot be passed an rvalue or it would end up passing the husk
1131 // of a value to the downstream operators.
1132 typedef const StorageType& ParamType;
1134 typedef typename std::result_of<Selector(ParamType)>::type KeyType;
1135 typedef typename std::decay<KeyType>::type KeyStorageType;
1138 Generator(Source source,
1140 : source_(std::move(source)),
1141 selector_(std::move(selector)) {}
1143 template<class Body>
1144 void foreach(Body&& body) const {
1145 std::unordered_set<KeyStorageType> keysSeen;
1146 source_.foreach([&](Value value) {
1147 if (keysSeen.insert(selector_(ParamType(value))).second) {
1148 body(std::forward<Value>(value));
1153 template<class Handler>
1154 bool apply(Handler&& handler) const {
1155 std::unordered_set<KeyStorageType> keysSeen;
1156 return source_.apply([&](Value value) -> bool {
1157 if (keysSeen.insert(selector_(ParamType(value))).second) {
1158 return handler(std::forward<Value>(value));
1165 template<class Source,
1167 class Gen = Generator<Value, Source>>
1168 Gen compose(GenImpl<Value, Source>&& source) const {
1169 return Gen(std::move(source.self()), selector_);
1172 template<class Source,
1174 class Gen = Generator<Value, Source>>
1175 Gen compose(const GenImpl<Value, Source>& source) const {
1176 return Gen(source.self(), selector_);
1181 * Composer - Helper class for adapting pipelines into functors. Primarily used
1184 template<class Operators>
1188 explicit Composer(Operators op)
1189 : op_(std::move(op)) {}
1191 template<class Source,
1192 class Ret = decltype(std::declval<Operators>()
1193 .compose(std::declval<Source>()))>
1194 Ret operator()(Source&& source) const {
1195 return op_.compose(std::forward<Source>(source));
1200 * Batch - For producing fixed-size batches of each value from a source.
1202 * This type is usually used through the 'batch' helper function:
1207 * | map([](const std::vector<int>& batch) {
1208 * return from(batch) | sum;
1212 class Batch : public Operator<Batch> {
1215 explicit Batch(size_t batchSize)
1216 : batchSize_(batchSize) {
1217 if (batchSize_ == 0) {
1218 throw std::invalid_argument("Batch size must be non-zero!");
1222 template<class Value,
1224 class StorageType = typename std::decay<Value>::type,
1225 class VectorType = std::vector<StorageType>>
1227 public GenImpl<VectorType&,
1228 Generator<Value, Source, StorageType, VectorType>> {
1232 explicit Generator(Source source, size_t batchSize)
1233 : source_(std::move(source))
1234 , batchSize_(batchSize) {}
1236 template<class Handler>
1237 bool apply(Handler&& handler) const {
1239 batch_.reserve(batchSize_);
1240 bool shouldContinue = source_.apply([&](Value value) -> bool {
1241 batch_.push_back(std::forward<Value>(value));
1242 if (batch_.size() == batchSize_) {
1243 bool needMore = handler(batch_);
1247 // Always need more if the handler is not called.
1250 // Flush everything, if and only if `handler` hasn't returned false.
1251 if (shouldContinue && !batch_.empty()) {
1252 shouldContinue = handler(batch_);
1255 return shouldContinue;
1258 static constexpr bool infinite = Source::infinite;
1261 template<class Source,
1263 class Gen = Generator<Value, Source>>
1264 Gen compose(GenImpl<Value, Source>&& source) const {
1265 return Gen(std::move(source.self()), batchSize_);
1268 template<class Source,
1270 class Gen = Generator<Value, Source>>
1271 Gen compose(const GenImpl<Value, Source>& source) const {
1272 return Gen(source.self(), batchSize_);
1280 * FoldLeft - Left-associative functional fold. For producing an aggregate value
1281 * from a seed and a folder function. Useful for custom aggregators on a
1284 * This type is primarily used through the 'foldl' helper method, like:
1286 * double movingAverage = from(values)
1287 * | foldl(0.0, [](double avg, double sample) {
1288 * return sample * 0.1 + avg * 0.9;
1291 template<class Seed,
1293 class FoldLeft : public Operator<FoldLeft<Seed, Fold>> {
1297 FoldLeft() = default;
1300 : seed_(std::move(seed))
1301 , fold_(std::move(fold))
1304 template<class Source,
1306 Seed compose(const GenImpl<Value, Source>& source) const {
1307 static_assert(!Source::infinite, "Cannot foldl infinite source");
1309 source | [&](Value v) {
1310 accum = fold_(std::move(accum), std::forward<Value>(v));
1317 * First - For finding the first value in a sequence.
1319 * This type is primarily used through the 'first' static value, like:
1321 * int firstThreeDigitPrime = seq(100) | filter(isPrime) | first;
1323 class First : public Operator<First> {
1327 template<class Source,
1329 class StorageType = typename std::decay<Value>::type>
1330 StorageType compose(const GenImpl<Value, Source>& source) const {
1331 Optional<StorageType> accum;
1332 source | [&](Value v) -> bool {
1333 accum = std::forward<Value>(v);
1336 if (!accum.hasValue()) {
1337 throw EmptySequence();
1339 return std::move(accum.value());
1344 * IsEmpty - a helper class for isEmpty and notEmpty
1346 * Essentially returns 'result' if the source is empty. Note that this cannot be
1347 * called on an infinite source, because then there is only one possible return
1350 template <bool emptyResult>
1351 class IsEmpty : public Operator<IsEmpty<emptyResult>> {
1353 IsEmpty() = default;
1355 template<class Source,
1357 bool compose(const GenImpl<Value, Source>& source) const {
1358 static_assert(!Source::infinite,
1359 "Cannot call 'all', 'any', 'isEmpty', or 'notEmpty' on "
1360 "infinite source. 'all' and 'isEmpty' will either return "
1361 "false or hang. 'any' or 'notEmpty' will either return true "
1363 bool ans = emptyResult;
1364 source | [&](Value v) -> bool {
1373 * Reduce - Functional reduce, for recursively combining values from a source
1374 * using a reducer function until there is only one item left. Useful for
1375 * combining values when an empty sequence doesn't make sense.
1377 * This type is primarily used through the 'reduce' helper method, like:
1379 * sring longest = from(names)
1380 * | reduce([](string&& best, string& current) {
1381 * return best.size() >= current.size() ? best : current;
1384 template<class Reducer>
1385 class Reduce : public Operator<Reduce<Reducer>> {
1389 explicit Reduce(Reducer reducer)
1390 : reducer_(std::move(reducer))
1393 template<class Source,
1395 class StorageType = typename std::decay<Value>::type>
1396 StorageType compose(const GenImpl<Value, Source>& source) const {
1397 Optional<StorageType> accum;
1398 source | [&](Value v) {
1399 if (accum.hasValue()) {
1400 accum = reducer_(std::move(accum.value()), std::forward<Value>(v));
1402 accum = std::forward<Value>(v);
1405 if (!accum.hasValue()) {
1406 throw EmptySequence();
1408 return accum.value();
1413 * Count - for simply counting the items in a collection.
1415 * This type is usually used through its singleton, 'count':
1417 * auto shortPrimes = seq(1, 100) | filter(isPrime) | count;
1419 class Count : public Operator<Count> {
1423 template<class Source,
1425 size_t compose(const GenImpl<Value, Source>& source) const {
1426 static_assert(!Source::infinite, "Cannot count infinite source");
1427 return foldl(size_t(0),
1428 [](size_t accum, Value v) {
1435 * Sum - For simply summing up all the values from a source.
1437 * This type is usually used through its singleton, 'sum':
1439 * auto gaussSum = seq(1, 100) | sum;
1441 class Sum : public Operator<Sum> {
1445 template<class Source,
1447 class StorageType = typename std::decay<Value>::type>
1448 StorageType compose(const GenImpl<Value, Source>& source) const {
1449 static_assert(!Source::infinite, "Cannot sum infinite source");
1450 return foldl(StorageType(0),
1451 [](StorageType&& accum, Value v) {
1452 return std::move(accum) + std::forward<Value>(v);
1458 * Contains - For testing whether a value matching the given value is contained
1461 * This type should be used through the 'contains' helper method, like:
1463 * bool contained = seq(1, 10) | map(square) | contains(49);
1465 template<class Needle>
1466 class Contains : public Operator<Contains<Needle>> {
1469 explicit Contains(Needle needle)
1470 : needle_(std::move(needle))
1473 template<class Source,
1475 class StorageType = typename std::decay<Value>::type>
1476 bool compose(const GenImpl<Value, Source>& source) const {
1477 static_assert(!Source::infinite,
1478 "Calling contains on an infinite source might cause "
1479 "an infinite loop.");
1480 return !(source | [this](Value value) {
1481 return !(needle_ == std::forward<Value>(value));
1487 * Min - For a value which minimizes a key, where the key is determined by a
1488 * given selector, and compared by given comparer.
1490 * This type is usually used through the singletone 'min' or through the helper
1491 * functions 'minBy' and 'maxBy'.
1493 * auto oldest = from(people)
1494 * | minBy([](Person& p) {
1495 * return p.dateOfBirth;
1498 template<class Selector,
1500 class Min : public Operator<Min<Selector, Comparer>> {
1506 explicit Min(Selector selector)
1507 : selector_(std::move(selector))
1510 Min(Selector selector,
1512 : selector_(std::move(selector))
1513 , comparer_(std::move(comparer))
1516 template<class Value,
1518 class StorageType = typename std::decay<Value>::type,
1519 class Key = typename std::decay<
1520 typename std::result_of<Selector(Value)>::type
1522 StorageType compose(const GenImpl<Value, Source>& source) const {
1523 Optional<StorageType> min;
1524 Optional<Key> minKey;
1525 source | [&](Value v) {
1526 Key key = selector_(std::forward<Value>(v));
1527 if (!minKey.hasValue() || comparer_(key, minKey.value())) {
1529 min = std::forward<Value>(v);
1532 if (!min.hasValue()) {
1533 throw EmptySequence();
1540 * Append - For collecting values from a source into a given output container
1543 * This type is usually used through the helper function 'appendTo', like:
1545 * vector<int64_t> ids;
1546 * from(results) | map([](Person& p) { return p.id })
1549 template<class Collection>
1550 class Append : public Operator<Append<Collection>> {
1551 Collection* collection_;
1553 explicit Append(Collection* collection)
1554 : collection_(collection)
1557 template<class Value,
1559 Collection& compose(const GenImpl<Value, Source>& source) const {
1560 source | [&](Value v) {
1561 collection_->insert(collection_->end(), std::forward<Value>(v));
1563 return *collection_;
1568 * Collect - For collecting values from a source in a collection of the desired
1571 * This type is usually used through the helper function 'as', like:
1573 * std::string upper = from(stringPiece)
1575 * | as<std::string>();
1577 template<class Collection>
1578 class Collect : public Operator<Collect<Collection>> {
1580 Collect() = default;
1582 template<class Value,
1584 class StorageType = typename std::decay<Value>::type>
1585 Collection compose(const GenImpl<Value, Source>& source) const {
1586 Collection collection;
1587 source | [&](Value v) {
1588 collection.insert(collection.end(), std::forward<Value>(v));
1596 * CollectTemplate - For collecting values from a source in a collection
1597 * constructed using the specified template type. Given the type of values
1598 * produced by the given generator, the collection type will be:
1599 * Container<Value, Allocator<Value>>
1601 * The allocator defaults to std::allocator, so this may be used for the STL
1602 * containers by simply using operators like 'as<set>', 'as<deque>',
1603 * 'as<vector>'. 'as', here is the helper method which is the usual means of
1604 * consturcting this operator.
1608 * set<string> uniqueNames = from(names) | as<set>();
1610 template<template<class, class> class Container,
1611 template<class> class Allocator>
1612 class CollectTemplate : public Operator<CollectTemplate<Container, Allocator>> {
1614 CollectTemplate() = default;
1616 template<class Value,
1618 class StorageType = typename std::decay<Value>::type,
1619 class Collection = Container<StorageType, Allocator<StorageType>>>
1620 Collection compose(const GenImpl<Value, Source>& source) const {
1621 Collection collection;
1622 source | [&](Value v) {
1623 collection.insert(collection.end(), std::forward<Value>(v));
1630 * Concat - For flattening generators of generators.
1632 * This type is usually used through the 'concat' static value, like:
1636 * | map([](Node& x) {
1637 * return from(x.neighbors)
1638 * | map([&](Node& y) {
1639 * return Edge(x, y);
1645 class Concat : public Operator<Concat> {
1649 template<class Inner,
1651 class InnerValue = typename std::decay<Inner>::type::ValueType>
1653 public GenImpl<InnerValue, Generator<Inner, Source, InnerValue>> {
1656 explicit Generator(Source source)
1657 : source_(std::move(source)) {}
1659 template<class Handler>
1660 bool apply(Handler&& handler) const {
1661 return source_.apply([&](Inner inner) -> bool {
1662 return inner.apply(std::forward<Handler>(handler));
1666 template<class Body>
1667 void foreach(Body&& body) const {
1668 source_.foreach([&](Inner inner) {
1669 inner.foreach(std::forward<Body>(body));
1673 static constexpr bool infinite = Source::infinite;
1676 template<class Value,
1678 class Gen = Generator<Value, Source>>
1679 Gen compose(GenImpl<Value, Source>&& source) const {
1680 return Gen(std::move(source.self()));
1683 template<class Value,
1685 class Gen = Generator<Value, Source>>
1686 Gen compose(const GenImpl<Value, Source>& source) const {
1687 return Gen(source.self());
1692 * RangeConcat - For flattening generators of iterables.
1694 * This type is usually used through the 'rconcat' static value, like:
1696 * map<int, vector<int>> adjacency;
1703 class RangeConcat : public Operator<RangeConcat> {
1705 RangeConcat() = default;
1707 template<class Range,
1709 class InnerValue = typename ValueTypeOfRange<Range>::RefType>
1711 : public GenImpl<InnerValue, Generator<Range, Source, InnerValue>> {
1714 explicit Generator(Source source)
1715 : source_(std::move(source)) {}
1717 template<class Body>
1718 void foreach(Body&& body) const {
1719 source_.foreach([&](Range range) {
1720 for (auto& value : range) {
1726 template<class Handler>
1727 bool apply(Handler&& handler) const {
1728 return source_.apply([&](Range range) -> bool {
1729 for (auto& value : range) {
1730 if (!handler(value)) {
1739 template<class Value,
1741 class Gen = Generator<Value, Source>>
1742 Gen compose(GenImpl<Value, Source>&& source) const {
1743 return Gen(std::move(source.self()));
1746 template<class Value,
1748 class Gen = Generator<Value, Source>>
1749 Gen compose(const GenImpl<Value, Source>& source) const {
1750 return Gen(source.self());
1756 * GuardImpl - For handling exceptions from downstream computation. Requires the
1757 * type of exception to catch, and handler function to invoke in the event of
1758 * the exception. Note that the handler may:
1759 * 1) return true to continue processing the sequence
1760 * 2) return false to end the sequence immediately
1761 * 3) throw, to pass the exception to the next catch
1762 * The handler must match the signature 'bool(Exception&, Value)'.
1764 * This type is used through the `guard` helper, like so:
1767 * = byLine(STDIN_FILENO)
1768 * | guard<std::runtime_error>([](std::runtime_error& e,
1770 * LOG(ERROR) << sp << ": " << e.str();
1771 * return true; // continue processing subsequent lines
1776 * TODO(tjackson): Rename this back to Guard.
1778 template<class Exception,
1780 class GuardImpl : public Operator<GuardImpl<Exception, ErrorHandler>> {
1781 ErrorHandler handler_;
1783 explicit GuardImpl(ErrorHandler handler) : handler_(std::move(handler)) {}
1785 template<class Value,
1787 class Generator : public GenImpl<Value, Generator<Value, Source>> {
1789 ErrorHandler handler_;
1791 explicit Generator(Source source,
1792 ErrorHandler handler)
1793 : source_(std::move(source)),
1794 handler_(std::move(handler)) {}
1796 template<class Handler>
1797 bool apply(Handler&& handler) const {
1798 return source_.apply([&](Value value) -> bool {
1800 handler(std::forward<Value>(value));
1802 } catch (Exception& e) {
1803 return handler_(e, std::forward<Value>(value));
1808 static constexpr bool infinite = Source::infinite;
1811 template<class Value,
1813 class Gen = Generator<Value, Source>>
1814 Gen compose(GenImpl<Value, Source>&& source) const {
1815 return Gen(std::move(source.self()), handler_);
1818 template<class Value,
1820 class Gen = Generator<Value, Source>>
1821 Gen compose(const GenImpl<Value, Source>& source) const {
1822 return Gen(source.self(), handler_);
1827 * Cycle - For repeating a sequence forever.
1829 * This type is usually used through the 'cycle' static value, like:
1836 * or in the finite case:
1838 * auto thrice = g | cycle(3);
1840 template <bool forever>
1841 class Cycle : public Operator<Cycle<forever>> {
1842 off_t limit_; // not used if forever == true
1846 explicit Cycle(off_t limit)
1850 "Cycle limit consturctor should not be used when forever == true.");
1853 template<class Value,
1855 class Generator : public GenImpl<Value, Generator<Value, Source>> {
1859 explicit Generator(Source source, off_t limit)
1860 : source_(std::move(source))
1863 template<class Handler>
1864 bool apply(Handler&& handler) const {
1866 auto handler2 = [&](Value value) {
1867 cont = handler(std::forward<Value>(value));
1870 // Becomes an infinte loop if forever == true
1871 for (off_t count = 0; (forever || count != limit_); ++count) {
1873 source_.apply(handler2);
1881 // not actually infinite, since an empty generator will end the cycles.
1882 static constexpr bool infinite = Source::infinite;
1885 template<class Source,
1887 class Gen = Generator<Value, Source>>
1888 Gen compose(GenImpl<Value, Source>&& source) const {
1889 return Gen(std::move(source.self()), limit_);
1892 template<class Source,
1894 class Gen = Generator<Value, Source>>
1895 Gen compose(const GenImpl<Value, Source>& source) const {
1896 return Gen(source.self(), limit_);
1900 * Convenience function for finite cycles used like:
1902 * auto tripled = gen | cycle(3);
1904 Cycle<false> operator()(off_t limit) const {
1905 return Cycle<false>(limit);
1910 * Dereference - For dereferencing a sequence of pointers while filtering out
1913 * This type is usually used through the 'dereference' static value, like:
1915 * auto refs = from(ptrs) | dereference;
1917 class Dereference : public Operator<Dereference> {
1919 Dereference() = default;
1921 template<class Value,
1923 class Result = decltype(*std::declval<Value>())>
1924 class Generator : public GenImpl<Result, Generator<Value, Source, Result>> {
1927 explicit Generator(Source source)
1928 : source_(std::move(source)) {}
1930 template<class Body>
1931 void foreach(Body&& body) const {
1932 source_.foreach([&](Value value) {
1934 return body(*value);
1939 template<class Handler>
1940 bool apply(Handler&& handler) const {
1941 return source_.apply([&](Value value) -> bool {
1943 return handler(*value);
1949 // not actually infinite, since an empty generator will end the cycles.
1950 static constexpr bool infinite = Source::infinite;
1953 template<class Source,
1955 class Gen = Generator<Value, Source>>
1956 Gen compose(GenImpl<Value, Source>&& source) const {
1957 return Gen(std::move(source.self()));
1960 template<class Source,
1962 class Gen = Generator<Value, Source>>
1963 Gen compose(const GenImpl<Value, Source>& source) const {
1964 return Gen(source.self());
1969 * Indirect - For producing a sequence of the addresses of the values in the
1972 * This type is usually used through the 'indirect' static value, like:
1974 * auto ptrs = from(refs) | indirect;
1976 class Indirect : public Operator<Indirect> {
1978 Indirect() = default;
1980 template <class Value,
1982 class Result = typename std::remove_reference<Value>::type*>
1983 class Generator : public GenImpl<Result, Generator<Value, Source, Result>> {
1985 static_assert(!std::is_rvalue_reference<Value>::value,
1986 "Cannot use indirect on an rvalue");
1989 explicit Generator(Source source) : source_(std::move(source)) {}
1991 template <class Body>
1992 void foreach(Body&& body) const {
1993 source_.foreach([&](Value value) {
1994 return body(&value);
1998 template <class Handler>
1999 bool apply(Handler&& handler) const {
2000 return source_.apply([&](Value value) -> bool {
2001 return handler(&value);
2005 // not actually infinite, since an empty generator will end the cycles.
2006 static constexpr bool infinite = Source::infinite;
2009 template <class Source, class Value, class Gen = Generator<Value, Source>>
2010 Gen compose(GenImpl<Value, Source>&& source) const {
2011 return Gen(std::move(source.self()));
2014 template <class Source, class Value, class Gen = Generator<Value, Source>>
2015 Gen compose(const GenImpl<Value, Source>& source) const {
2016 return Gen(source.self());
2023 * VirtualGen<T> - For wrapping template types in simple polymorphic wrapper.
2025 template<class Value>
2026 class VirtualGen : public GenImpl<Value, VirtualGen<Value>> {
2029 virtual ~WrapperBase() noexcept {}
2030 virtual bool apply(const std::function<bool(Value)>& handler) const = 0;
2031 virtual void foreach(const std::function<void(Value)>& body) const = 0;
2032 virtual std::unique_ptr<const WrapperBase> clone() const = 0;
2035 template<class Wrapped>
2036 class WrapperImpl : public WrapperBase {
2039 explicit WrapperImpl(Wrapped wrapped)
2040 : wrapped_(std::move(wrapped)) {
2043 virtual bool apply(const std::function<bool(Value)>& handler) const {
2044 return wrapped_.apply(handler);
2047 virtual void foreach(const std::function<void(Value)>& body) const {
2048 wrapped_.foreach(body);
2051 virtual std::unique_ptr<const WrapperBase> clone() const {
2052 return std::unique_ptr<const WrapperBase>(new WrapperImpl(wrapped_));
2056 std::unique_ptr<const WrapperBase> wrapper_;
2059 template <class Self>
2060 /* implicit */ VirtualGen(Self source)
2061 : wrapper_(new WrapperImpl<Self>(std::move(source))) {}
2063 VirtualGen(VirtualGen&& source) noexcept
2064 : wrapper_(std::move(source.wrapper_)) {}
2066 VirtualGen(const VirtualGen& source)
2067 : wrapper_(source.wrapper_->clone()) {}
2069 VirtualGen& operator=(const VirtualGen& source) {
2070 wrapper_.reset(source.wrapper_->clone());
2074 VirtualGen& operator=(VirtualGen&& source) noexcept {
2075 wrapper_= std::move(source.wrapper_);
2079 bool apply(const std::function<bool(Value)>& handler) const {
2080 return wrapper_->apply(handler);
2083 void foreach(const std::function<void(Value)>& body) const {
2084 wrapper_->foreach(body);
2089 * non-template operators, statically defined to avoid the need for anything but
2092 constexpr detail::Sum sum{};
2094 constexpr detail::Count count{};
2096 constexpr detail::First first{};
2099 * Use 'isEmpty' and 'notEmpty' for detecting if there are any values or not.
2101 * bool hasPrimes = g | filter(prime) | notEmpty;
2102 * bool lacksEvens = g | filter(even) | isEmpty;
2104 constexpr detail::IsEmpty<true> isEmpty{};
2106 constexpr detail::IsEmpty<false> notEmpty{};
2108 constexpr detail::Min<Identity, Less> min{};
2110 constexpr detail::Min<Identity, Greater> max{};
2112 constexpr detail::Order<Identity> order{};
2114 constexpr detail::Distinct<Identity> distinct{};
2116 constexpr detail::Map<Move> move{};
2118 constexpr detail::Concat concat{};
2120 constexpr detail::RangeConcat rconcat{};
2122 constexpr detail::Cycle<true> cycle{};
2124 constexpr detail::Dereference dereference{};
2126 constexpr detail::Indirect indirect{};
2128 inline detail::Take take(size_t count) {
2129 return detail::Take(count);
2132 inline detail::Stride stride(size_t s) {
2133 return detail::Stride(s);
2136 template<class Random = std::default_random_engine>
2137 inline detail::Sample<Random> sample(size_t count, Random rng = Random()) {
2138 return detail::Sample<Random>(count, std::move(rng));
2141 inline detail::Skip skip(size_t count) {
2142 return detail::Skip(count);
2145 inline detail::Batch batch(size_t batchSize) {
2146 return detail::Batch(batchSize);
2151 #pragma GCC diagnostic pop