/*
- * Copyright 2014 Facebook, Inc.
+ * Copyright 2017 Facebook, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* limitations under the License.
*/
-#ifndef FOLLY_GEN_BASE_H
+#ifndef FOLLY_GEN_BASE_H_
#error This file may only be included from folly/gen/Base.h
#endif
+#include <folly/Portability.h>
+
// Ignore shadowing warnings within this file, so includers can use -Wshadow.
-#pragma GCC diagnostic push
-#pragma GCC diagnostic ignored "-Wshadow"
+FOLLY_PUSH_WARNING
+FOLLY_GCC_DISABLE_WARNING("-Wshadow")
-namespace folly { namespace gen {
+namespace folly {
+namespace gen {
/**
* ArgumentReference - For determining ideal argument type to receive a value.
T&& // int -> int&&
>::type> {};
+/**
+ * Group - The output objects from the GroupBy operator
+ */
+template <class Key, class Value>
+class Group : public GenImpl<Value&&, Group<Key, Value>> {
+ public:
+ static_assert(!std::is_reference<Key>::value &&
+ !std::is_reference<Value>::value,
+ "Key and Value must be decayed types");
+
+ typedef std::vector<Value> VectorType;
+ typedef Key KeyType;
+ typedef Value ValueType;
+
+ Group(Key key, VectorType values)
+ : key_(std::move(key)), values_(std::move(values)) {}
+
+ const Key& key() const { return key_; }
+
+ size_t size() const { return values_.size(); }
+ const VectorType& values() const { return values_; }
+ VectorType& values() { return values_; }
+
+ VectorType operator|(const detail::Collect<VectorType>&) const {
+ return values();
+ }
+
+ VectorType operator|(const detail::CollectTemplate<std::vector>&) const {
+ return values();
+ }
+
+ template <class Body>
+ void foreach(Body&& body) const {
+ for (auto& value : values_) {
+ body(std::move(value));
+ }
+ }
+
+ template <class Handler>
+ bool apply(Handler&& handler) const {
+ for (auto& value : values_) {
+ if (!handler(std::move(value))) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ // GroupBy only takes in finite generators, so we only have finite groups
+ static constexpr bool infinite = false;
+
+ private:
+ Key key_;
+ mutable VectorType values_;
+};
+
namespace detail {
+// Classes used for the implementation of Sources, Operators, and Sinks
+
+/*
+ ******************************* Sources ***************************************
+ */
+
/*
* ReferencedSource - Generate values from an STL-like container using
* iterators from .begin() until .end(). Value type defaults to the type of
* string& longestName = from(names)
* | maxBy([](string& s) { return s.size() });
*/
-template<class Container,
- class Value>
-class ReferencedSource :
- public GenImpl<Value, ReferencedSource<Container, Value>> {
+template <class Container, class Value>
+class ReferencedSource
+ : public GenImpl<Value, ReferencedSource<Container, Value>> {
Container* container_;
-public:
- explicit ReferencedSource(Container* container)
- : container_(container) {}
- template<class Body>
+ public:
+ explicit ReferencedSource(Container* container) : container_(container) {}
+
+ template <class Body>
void foreach(Body&& body) const {
for (auto& value : *container_) {
body(std::forward<Value>(value));
}
}
- template<class Handler>
+ template <class Handler>
bool apply(Handler&& handler) const {
for (auto& value : *container_) {
if (!handler(std::forward<Value>(value))) {
}
return true;
}
+
+ // from takes in a normal stl structure, which are all finite
+ static constexpr bool infinite = false;
};
/**
*
* Though it is also used for the initializer_list specialization of from().
*/
-template<class StorageType,
- class Container>
-class CopiedSource :
- public GenImpl<const StorageType&,
- CopiedSource<StorageType, Container>> {
- static_assert(
- !std::is_reference<StorageType>::value, "StorageType must be decayed");
+template <class StorageType, class Container>
+class CopiedSource
+ : public GenImpl<const StorageType&, CopiedSource<StorageType, Container>> {
+ static_assert(!std::is_reference<StorageType>::value,
+ "StorageType must be decayed");
+
public:
// Generator objects are often copied during normal construction as they are
// encapsulated by downstream generators. It would be bad if this caused
// a copy of the entire container each time, and since we're only exposing a
// const reference to the value, it's safe to share it between multiple
// generators.
- static_assert(
- !std::is_reference<Container>::value,
- "Can't copy into a reference");
+ static_assert(!std::is_reference<Container>::value,
+ "Can't copy into a reference");
std::shared_ptr<const Container> copy_;
-public:
+
+ public:
typedef Container ContainerType;
- template<class SourceContainer>
+ template <class SourceContainer>
explicit CopiedSource(const SourceContainer& container)
- : copy_(new Container(begin(container), end(container))) {}
+ : copy_(new Container(begin(container), end(container))) {}
- explicit CopiedSource(Container&& container) :
- copy_(new Container(std::move(container))) {}
+ explicit CopiedSource(Container&& container)
+ : copy_(new Container(std::move(container))) {}
// To enable re-use of cached results.
CopiedSource(const CopiedSource<StorageType, Container>& source)
- : copy_(source.copy_) {}
+ : copy_(source.copy_) {}
- template<class Body>
+ template <class Body>
void foreach(Body&& body) const {
for (const auto& value : *copy_) {
body(value);
}
}
- template<class Handler>
+ template <class Handler>
bool apply(Handler&& handler) const {
// The collection may be reused by others, we can't allow it to be changed.
for (const auto& value : *copy_) {
}
return true;
}
+
+ // from takes in a normal stl structure, which are all finite
+ static constexpr bool infinite = false;
};
/**
*
* Reminder: Be careful not to invalidate iterators when using ranges like this.
*/
-template<class Iterator>
+template <class Iterator>
class RangeSource : public GenImpl<typename Range<Iterator>::reference,
RangeSource<Iterator>> {
Range<Iterator> range_;
+
public:
- RangeSource() {}
- explicit RangeSource(Range<Iterator> range)
- : range_(std::move(range))
- {}
+ RangeSource() = default;
+ explicit RangeSource(Range<Iterator> range) : range_(std::move(range)) {}
- template<class Handler>
+ template <class Handler>
bool apply(Handler&& handler) const {
for (auto& value : range_) {
if (!handler(value)) {
return true;
}
- template<class Body>
+ template <class Body>
void foreach(Body&& body) const {
for (auto& value : range_) {
body(value);
}
}
+
+ // folly::Range only supports finite ranges
+ static constexpr bool infinite = false;
};
/**
* Sequence - For generating values from beginning value, incremented along the
- * way with the ++ and += operators. Iteration may continue indefinitely by
- * setting the 'endless' template parameter to true. If set to false, iteration
- * will stop when value reaches 'end', either inclusively or exclusively,
- * depending on the template parameter 'endInclusive'. Value type specified
- * explicitly.
+ * way with the ++ and += operators. Iteration may continue indefinitely.
+ * Value type specified explicitly.
*
* This type is primarily used through the 'seq' and 'range' function, like:
*
* int total = seq(1, 10) | sum;
* auto indexes = range(0, 10);
+ * auto endless = seq(0); // 0, 1, 2, 3, ...
*/
-template<class Value,
- bool endless,
- bool endInclusive>
-class Sequence : public GenImpl<const Value&,
- Sequence<Value, endless, endInclusive>> {
+template <class Value, class SequenceImpl>
+class Sequence : public GenImpl<const Value&, Sequence<Value, SequenceImpl>> {
static_assert(!std::is_reference<Value>::value &&
- !std::is_const<Value>::value, "Value mustn't be const or ref.");
- Value bounds_[endless ? 1 : 2];
-public:
- explicit Sequence(Value begin)
- : bounds_{std::move(begin)} {
- static_assert(endless, "Must supply 'end'");
- }
+ !std::is_const<Value>::value,
+ "Value mustn't be const or ref.");
+ Value start_;
+ SequenceImpl impl_;
- Sequence(Value begin,
- Value end)
- : bounds_{std::move(begin), std::move(end)} {}
+ public:
+ explicit Sequence(Value start, SequenceImpl impl)
+ : start_(std::move(start)), impl_(std::move(impl)) {}
- template<class Handler>
+ template <class Handler>
bool apply(Handler&& handler) const {
- Value value = bounds_[0];
- for (;endless || value < bounds_[1]; ++value) {
- const Value& arg = value;
- if (!handler(arg)) {
- return false;
- }
- }
- if (endInclusive && value == bounds_[1]) {
- const Value& arg = value;
- if (!handler(arg)) {
+ for (Value current = start_; impl_.test(current); impl_.step(current)) {
+ if (!handler(current)) {
return false;
}
}
return true;
}
- template<class Body>
+ template <class Body>
void foreach(Body&& body) const {
- Value value = bounds_[0];
- for (;endless || value < bounds_[1]; ++value) {
- const Value& arg = value;
- body(arg);
- }
- if (endInclusive && value == bounds_[1]) {
- const Value& arg = value;
- body(arg);
+ for (Value current = start_; impl_.test(current); impl_.step(current)) {
+ body(current);
}
}
- static constexpr bool infinite = endless;
+ // Let the implementation say if we are infinite or not
+ static constexpr bool infinite = SequenceImpl::infinite;
+};
+
+/**
+ * Sequence implementations (range, sequence, infinite, with/without step)
+ **/
+template <class Value>
+class RangeImpl {
+ Value end_;
+
+ public:
+ explicit RangeImpl(Value end) : end_(std::move(end)) {}
+ bool test(const Value& current) const { return current < end_; }
+ void step(Value& current) const { ++current; }
+ static constexpr bool infinite = false;
+};
+
+template <class Value, class Distance>
+class RangeWithStepImpl {
+ Value end_;
+ Distance step_;
+
+ public:
+ explicit RangeWithStepImpl(Value end, Distance step)
+ : end_(std::move(end)), step_(std::move(step)) {}
+ bool test(const Value& current) const { return current < end_; }
+ void step(Value& current) const { current += step_; }
+ static constexpr bool infinite = false;
+};
+
+template <class Value>
+class SeqImpl {
+ Value end_;
+
+ public:
+ explicit SeqImpl(Value end) : end_(std::move(end)) {}
+ bool test(const Value& current) const { return current <= end_; }
+ void step(Value& current) const { ++current; }
+ static constexpr bool infinite = false;
+};
+
+template <class Value, class Distance>
+class SeqWithStepImpl {
+ Value end_;
+ Distance step_;
+
+ public:
+ explicit SeqWithStepImpl(Value end, Distance step)
+ : end_(std::move(end)), step_(std::move(step)) {}
+ bool test(const Value& current) const { return current <= end_; }
+ void step(Value& current) const { current += step_; }
+ static constexpr bool infinite = false;
+};
+
+template <class Value>
+class InfiniteImpl {
+ public:
+ bool test(const Value& /* current */) const { return true; }
+ void step(Value& current) const { ++current; }
+ static constexpr bool infinite = true;
};
/**
* GenratorBuilder - Helper for GENERTATOR macro.
**/
-template<class Value>
+template <class Value>
struct GeneratorBuilder {
- template<class Source,
- class Yield = detail::Yield<Value, Source>>
+ template <class Source, class Yield = detail::Yield<Value, Source>>
Yield operator+(Source&& source) {
return Yield(std::forward<Source>(source));
}
* Yield - For producing values from a user-defined generator by way of a
* 'yield' function.
**/
-template<class Value, class Source>
+template <class Value, class Source>
class Yield : public GenImpl<Value, Yield<Value, Source>> {
Source source_;
+
public:
- explicit Yield(Source source)
- : source_(std::move(source)) {
- }
+ explicit Yield(Source source) : source_(std::move(source)) {}
- template<class Handler>
+ template <class Handler>
bool apply(Handler&& handler) const {
struct Break {};
auto body = [&](Value value) {
}
}
- template<class Body>
+ template <class Body>
void foreach(Body&& body) const {
source_(std::forward<Body>(body));
}
};
-template<class Value>
+template <class Value>
class Empty : public GenImpl<Value, Empty<Value>> {
public:
template <class Handler>
template <class Body>
void foreach(Body&&) const {}
+
+ // No values, so finite
+ static constexpr bool infinite = false;
+};
+
+template <class Value>
+class SingleReference : public GenImpl<Value&, SingleReference<Value>> {
+ static_assert(!std::is_reference<Value>::value,
+ "SingleReference requires non-ref types");
+ Value* ptr_;
+
+ public:
+ explicit SingleReference(Value& ref) : ptr_(&ref) {}
+
+ template <class Handler>
+ bool apply(Handler&& handler) const {
+ return handler(*ptr_);
+ }
+
+ template <class Body>
+ void foreach(Body&& body) const {
+ body(*ptr_);
+ }
+
+ // One value, so finite
+ static constexpr bool infinite = false;
};
-template<class Value>
-class Just : public GenImpl<const Value&, Just<Value>> {
+template <class Value>
+class SingleCopy : public GenImpl<const Value&, SingleCopy<Value>> {
static_assert(!std::is_reference<Value>::value,
- "Just requires non-ref types");
- const Value value_;
+ "SingleCopy requires non-ref types");
+ Value value_;
+
public:
- Just(Value value) : value_(std::forward<Value>(value)) {}
+ explicit SingleCopy(Value value) : value_(std::forward<Value>(value)) {}
template <class Handler>
bool apply(Handler&& handler) const {
void foreach(Body&& body) const {
body(value_);
}
+
+ // One value, so finite
+ static constexpr bool infinite = false;
};
/*
- * Operators
+ ***************************** Operators ***************************************
*/
/**
*
* This type is usually used through the 'map' or 'mapped' helper function:
*
- * auto squares = seq(1, 10) | map(square) | asVector;
+ * auto squares = seq(1, 10) | map(square) | as<std::vector>();
*/
-template<class Predicate>
+template <class Predicate>
class Map : public Operator<Map<Predicate>> {
Predicate pred_;
+
public:
- Map() {}
-
- explicit Map(Predicate pred)
- : pred_(std::move(pred))
- { }
-
- template<class Value,
- class Source,
- class Result = typename ArgumentReference<
- typename std::result_of<Predicate(Value)>::type
- >::type>
- class Generator :
- public GenImpl<Result, Generator<Value, Source, Result>> {
+ Map() = default;
+
+ explicit Map(Predicate pred) : pred_(std::move(pred)) {}
+
+ template <
+ class Value,
+ class Source,
+ class Result = typename ArgumentReference<
+ typename std::result_of<Predicate(Value)>::type>::type>
+ class Generator : public GenImpl<Result, Generator<Value, Source, Result>> {
Source source_;
Predicate pred_;
- public:
+
+ public:
explicit Generator(Source source, const Predicate& pred)
- : source_(std::move(source)), pred_(pred) {}
+ : source_(std::move(source)), pred_(pred) {}
- template<class Body>
+ template <class Body>
void foreach(Body&& body) const {
- source_.foreach([&](Value value) {
- body(pred_(std::forward<Value>(value)));
- });
+ source_.foreach(
+ [&](Value value) { body(pred_(std::forward<Value>(value))); });
}
- template<class Handler>
+ template <class Handler>
bool apply(Handler&& handler) const {
return source_.apply([&](Value value) {
return handler(pred_(std::forward<Value>(value)));
static constexpr bool infinite = Source::infinite;
};
- template<class Source,
- class Value,
- class Gen = Generator<Value, Source>>
+ template <class Source, class Value, class Gen = Generator<Value, Source>>
Gen compose(GenImpl<Value, Source>&& source) const {
return Gen(std::move(source.self()), pred_);
}
- template<class Source,
- class Value,
- class Gen = Generator<Value, Source>>
+ template <class Source, class Value, class Gen = Generator<Value, Source>>
Gen compose(const GenImpl<Value, Source>& source) const {
return Gen(source.self(), pred_);
}
};
-
/**
* Filter - For filtering values from a source sequence by a predicate.
*
* | filter([](const string& str) -> bool {
* return !str.empty();
* });
+ *
+ * Note that if no predicate is provided, the values are casted to bool and
+ * filtered based on that. So if pointers is a vector of pointers,
+ *
+ * auto nonNull = from(pointers) | filter();
+ *
+ * will give a vector of all the pointers != nullptr.
*/
-template<class Predicate>
+template <class Predicate>
class Filter : public Operator<Filter<Predicate>> {
Predicate pred_;
+
public:
- Filter() {}
- explicit Filter(Predicate pred)
- : pred_(std::move(pred))
- { }
+ Filter() = default;
+ explicit Filter(Predicate pred) : pred_(std::move(pred)) {}
- template<class Value,
- class Source>
+ template <class Value, class Source>
class Generator : public GenImpl<Value, Generator<Value, Source>> {
Source source_;
Predicate pred_;
- public:
+
+ public:
explicit Generator(Source source, const Predicate& pred)
- : source_(std::move(source)), pred_(pred) {}
+ : source_(std::move(source)), pred_(pred) {}
- template<class Body>
+ template <class Body>
void foreach(Body&& body) const {
source_.foreach([&](Value value) {
- if (pred_(std::forward<Value>(value))) {
+ // NB: Argument not forwarded to avoid accidental move-construction
+ if (pred_(value)) {
body(std::forward<Value>(value));
}
});
}
- template<class Handler>
+ template <class Handler>
bool apply(Handler&& handler) const {
return source_.apply([&](Value value) -> bool {
- if (pred_(std::forward<Value>(value))) {
+ // NB: Argument not forwarded to avoid accidental move-construction
+ if (pred_(value)) {
return handler(std::forward<Value>(value));
}
return true;
static constexpr bool infinite = Source::infinite;
};
- template<class Source,
- class Value,
- class Gen = Generator<Value, Source>>
+ template <class Source, class Value, class Gen = Generator<Value, Source>>
Gen compose(GenImpl<Value, Source>&& source) const {
return Gen(std::move(source.self()), pred_);
}
- template<class Source,
- class Value,
- class Gen = Generator<Value, Source>>
+ template <class Source, class Value, class Gen = Generator<Value, Source>>
Gen compose(const GenImpl<Value, Source>& source) const {
return Gen(source.self(), pred_);
}
*
* auto best = from(sortedItems)
* | until([](Item& item) { return item.score > 100; })
- * | asVector;
+ * | as<std::vector>();
*/
-template<class Predicate>
+template <class Predicate>
class Until : public Operator<Until<Predicate>> {
Predicate pred_;
+
public:
- Until() {}
- explicit Until(Predicate pred)
- : pred_(std::move(pred))
- {}
+ Until() = default;
+ explicit Until(Predicate pred) : pred_(std::move(pred)) {}
- template<class Value,
- class Source>
+ template <class Value, class Source>
class Generator : public GenImpl<Value, Generator<Value, Source>> {
Source source_;
Predicate pred_;
+
public:
explicit Generator(Source source, const Predicate& pred)
- : source_(std::move(source)), pred_(pred) {}
+ : source_(std::move(source)), pred_(pred) {}
- template<class Handler>
+ template <class Handler>
bool apply(Handler&& handler) const {
bool cancelled = false;
source_.apply([&](Value value) -> bool {
});
return !cancelled;
}
+
+ // Theoretically an 'until' might stop an infinite
+ static constexpr bool infinite = false;
};
- template<class Source,
- class Value,
- class Gen = Generator<Value, Source>>
+ template <class Source, class Value, class Gen = Generator<Value, Source>>
Gen compose(GenImpl<Value, Source>&& source) const {
return Gen(std::move(source.self()), pred_);
}
- template<class Source,
- class Value,
- class Gen = Generator<Value, Source>>
+ template <class Source, class Value, class Gen = Generator<Value, Source>>
Gen compose(const GenImpl<Value, Source>& source) const {
return Gen(source.self(), pred_);
}
-
- // Theoretically an 'until' might stop an infinite
- static constexpr bool infinite = false;
};
/**
*/
class Take : public Operator<Take> {
size_t count_;
+
public:
- explicit Take(size_t count)
- : count_(count) {}
+ explicit Take(size_t count) : count_(count) {}
- template<class Value,
- class Source>
- class Generator :
- public GenImpl<Value, Generator<Value, Source>> {
+ template <class Value, class Source>
+ class Generator : public GenImpl<Value, Generator<Value, Source>> {
Source source_;
size_t count_;
- public:
+
+ public:
explicit Generator(Source source, size_t count)
- : source_(std::move(source)) , count_(count) {}
+ : source_(std::move(source)), count_(count) {}
- template<class Handler>
+ template <class Handler>
bool apply(Handler&& handler) const {
- if (count_ == 0) { return false; }
+ if (count_ == 0) {
+ return false;
+ }
size_t n = count_;
bool cancelled = false;
source_.apply([&](Value value) -> bool {
});
return !cancelled;
}
+
+ // take will stop an infinite generator
+ static constexpr bool infinite = false;
};
- template<class Source,
- class Value,
- class Gen = Generator<Value, Source>>
+ template <class Source, class Value, class Gen = Generator<Value, Source>>
Gen compose(GenImpl<Value, Source>&& source) const {
return Gen(std::move(source.self()), count_);
}
- template<class Source,
- class Value,
- class Gen = Generator<Value, Source>>
+ template <class Source, class Value, class Gen = Generator<Value, Source>>
Gen compose(const GenImpl<Value, Source>& source) const {
return Gen(source.self(), count_);
}
};
+/**
+ * Visit - For calling a function on each item before passing it down the
+ * pipeline.
+ *
+ * This type is usually used through the 'visit' helper function:
+ *
+ * auto printedValues = seq(1) | visit(debugPrint);
+ * // nothing printed yet
+ * auto results = take(10) | as<std::vector>();
+ * // results now populated, 10 values printed
+ */
+template <class Visitor>
+class Visit : public Operator<Visit<Visitor>> {
+ Visitor visitor_;
+
+ public:
+ Visit() = default;
+
+ explicit Visit(Visitor visitor) : visitor_(std::move(visitor)) {}
+
+ template <class Value, class Source>
+ class Generator : public GenImpl<Value, Generator<Value, Source>> {
+ Source source_;
+ Visitor visitor_;
+
+ public:
+ explicit Generator(Source source, const Visitor& visitor)
+ : source_(std::move(source)), visitor_(visitor) {}
+
+ template <class Body>
+ void foreach(Body&& body) const {
+ source_.foreach([&](Value value) {
+ visitor_(value); // not forwarding to avoid accidental moves
+ body(std::forward<Value>(value));
+ });
+ }
+
+ template <class Handler>
+ bool apply(Handler&& handler) const {
+ return source_.apply([&](Value value) {
+ visitor_(value); // not forwarding to avoid accidental moves
+ return handler(std::forward<Value>(value));
+ });
+ }
+
+ static constexpr bool infinite = Source::infinite;
+ };
+
+ template <class Source, class Value, class Gen = Generator<Value, Source>>
+ Gen compose(GenImpl<Value, Source>&& source) const {
+ return Gen(std::move(source.self()), visitor_);
+ }
+
+ template <class Source, class Value, class Gen = Generator<Value, Source>>
+ Gen compose(const GenImpl<Value, Source>& source) const {
+ return Gen(source.self(), visitor_);
+ }
+};
+
+/**
+ * Stride - For producing every Nth value from a source.
+ *
+ * This type is usually used through the 'stride' helper function, like:
+ *
+ * auto half = from(samples)
+ * | stride(2);
+ */
+class Stride : public Operator<Stride> {
+ size_t stride_;
+
+ public:
+ explicit Stride(size_t stride) : stride_(stride) {
+ if (stride == 0) {
+ throw std::invalid_argument("stride must not be 0");
+ }
+ }
+
+ template <class Value, class Source>
+ class Generator : public GenImpl<Value, Generator<Value, Source>> {
+ Source source_;
+ size_t stride_;
+
+ public:
+ explicit Generator(Source source, size_t stride)
+ : source_(std::move(source)), stride_(stride) {}
+
+ template <class Handler>
+ bool apply(Handler&& handler) const {
+ size_t distance = stride_;
+ return source_.apply([&](Value value) -> bool {
+ if (++distance >= stride_) {
+ if (!handler(std::forward<Value>(value))) {
+ return false;
+ }
+ distance = 0;
+ }
+ return true;
+ });
+ }
+
+ template <class Body>
+ void foreach(Body&& body) const {
+ size_t distance = stride_;
+ source_.foreach([&](Value value) {
+ if (++distance >= stride_) {
+ body(std::forward<Value>(value));
+ distance = 0;
+ }
+ });
+ }
+
+ // Taking every Nth of an infinite list is still infinte
+ static constexpr bool infinite = Source::infinite;
+ };
+
+ template <class Source, class Value, class Gen = Generator<Value, Source>>
+ Gen compose(GenImpl<Value, Source>&& source) const {
+ return Gen(std::move(source.self()), stride_);
+ }
+
+ template <class Source, class Value, class Gen = Generator<Value, Source>>
+ Gen compose(const GenImpl<Value, Source>& source) const {
+ return Gen(source.self(), stride_);
+ }
+};
+
/**
* Sample - For taking a random sample of N elements from a sequence
* (without replacement).
*/
-template<class Random>
+template <class Random>
class Sample : public Operator<Sample<Random>> {
size_t count_;
Random rng_;
+
public:
explicit Sample(size_t count, Random rng)
- : count_(count), rng_(std::move(rng)) {}
-
- template<class Value,
- class Source,
- class Rand,
- class StorageType = typename std::decay<Value>::type>
- class Generator :
- public GenImpl<StorageType&&,
- Generator<Value, Source, Rand, StorageType>> {
+ : count_(count), rng_(std::move(rng)) {}
+
+ template <
+ class Value,
+ class Source,
+ class Rand,
+ class StorageType = typename std::decay<Value>::type>
+ class Generator
+ : public GenImpl<StorageType&&,
+ Generator<Value, Source, Rand, StorageType>> {
static_assert(!Source::infinite, "Cannot sample infinite source!");
// It's too easy to bite ourselves if random generator is only 16-bit
static_assert(Random::max() >= std::numeric_limits<int32_t>::max() - 1,
Source source_;
size_t count_;
mutable Rand rng_;
- public:
+
+ public:
explicit Generator(Source source, size_t count, Random rng)
- : source_(std::move(source)) , count_(count), rng_(std::move(rng)) {}
+ : source_(std::move(source)), count_(count), rng_(std::move(rng)) {}
- template<class Handler>
+ template <class Handler>
bool apply(Handler&& handler) const {
- if (count_ == 0) { return false; }
+ if (count_ == 0) {
+ return false;
+ }
std::vector<StorageType> v;
v.reserve(count_);
// use reservoir sampling to give each source value an equal chance
// of appearing in our output.
size_t n = 1;
source_.foreach([&](Value value) -> void {
- if (v.size() < count_) {
- v.push_back(std::forward<Value>(value));
- } else {
- // alternatively, we could create a std::uniform_int_distribution
- // instead of using modulus, but benchmarks show this has
- // substantial overhead.
- size_t index = rng_() % n;
- if (index < v.size()) {
- v[index] = std::forward<Value>(value);
- }
+ if (v.size() < count_) {
+ v.push_back(std::forward<Value>(value));
+ } else {
+ // alternatively, we could create a std::uniform_int_distribution
+ // instead of using modulus, but benchmarks show this has
+ // substantial overhead.
+ size_t index = rng_() % n;
+ if (index < v.size()) {
+ v[index] = std::forward<Value>(value);
}
- ++n;
- });
+ }
+ ++n;
+ });
// output is unsorted!
- for (auto& val: v) {
+ for (auto& val : v) {
if (!handler(std::move(val))) {
return false;
}
}
return true;
}
+
+ // Only takes N elements, so finite
+ static constexpr bool infinite = false;
};
- template<class Source,
- class Value,
- class Gen = Generator<Value, Source, Random>>
+ template <
+ class Source,
+ class Value,
+ class Gen = Generator<Value, Source, Random>>
Gen compose(GenImpl<Value, Source>&& source) const {
return Gen(std::move(source.self()), count_, rng_);
}
- template<class Source,
- class Value,
- class Gen = Generator<Value, Source, Random>>
+ template <
+ class Source,
+ class Value,
+ class Gen = Generator<Value, Source, Random>>
Gen compose(const GenImpl<Value, Source>& source) const {
return Gen(source.self(), count_, rng_);
}
*/
class Skip : public Operator<Skip> {
size_t count_;
+
public:
- explicit Skip(size_t count)
- : count_(count) {}
+ explicit Skip(size_t count) : count_(count) {}
- template<class Value,
- class Source>
- class Generator :
- public GenImpl<Value, Generator<Value, Source>> {
+ template <class Value, class Source>
+ class Generator : public GenImpl<Value, Generator<Value, Source>> {
Source source_;
size_t count_;
+
public:
explicit Generator(Source source, size_t count)
- : source_(std::move(source)) , count_(count) {}
+ : source_(std::move(source)), count_(count) {}
- template<class Body>
+ template <class Body>
void foreach(Body&& body) const {
if (count_ == 0) {
source_.foreach(body);
}
size_t n = 0;
source_.foreach([&](Value value) {
- if (n < count_) {
- ++n;
- } else {
- body(std::forward<Value>(value));
- }
- });
+ if (n < count_) {
+ ++n;
+ } else {
+ body(std::forward<Value>(value));
+ }
+ });
}
- template<class Handler>
+ template <class Handler>
bool apply(Handler&& handler) const {
if (count_ == 0) {
return source_.apply(std::forward<Handler>(handler));
}
size_t n = 0;
return source_.apply([&](Value value) -> bool {
- if (n < count_) {
- ++n;
- return true;
- }
- return handler(std::forward<Value>(value));
- });
+ if (n < count_) {
+ ++n;
+ return true;
+ }
+ return handler(std::forward<Value>(value));
+ });
}
+ // Skipping N items of an infinite source is still infinite
static constexpr bool infinite = Source::infinite;
};
- template<class Source,
- class Value,
- class Gen = Generator<Value, Source>>
+ template <class Source, class Value, class Gen = Generator<Value, Source>>
Gen compose(GenImpl<Value, Source>&& source) const {
return Gen(std::move(source.self()), count_);
}
- template<class Source,
- class Value,
- class Gen = Generator<Value, Source>>
+ template <class Source, class Value, class Gen = Generator<Value, Source>>
Gen compose(const GenImpl<Value, Source>& source) const {
return Gen(source.self(), count_);
}
* })
* | take(10);
*/
-template<class Selector, class Comparer>
+template <class Selector, class Comparer>
class Order : public Operator<Order<Selector, Comparer>> {
Selector selector_;
Comparer comparer_;
+
public:
- Order() {}
-
- explicit Order(Selector selector)
- : selector_(std::move(selector))
- {}
-
- Order(Selector selector,
- Comparer comparer)
- : selector_(std::move(selector))
- , comparer_(std::move(comparer))
- {}
-
- template<class Value,
- class Source,
- class StorageType = typename std::decay<Value>::type,
- class Result = typename std::result_of<Selector(Value)>::type>
- class Generator :
- public GenImpl<StorageType&&,
- Generator<Value, Source, StorageType, Result>> {
+ Order() = default;
+
+ explicit Order(Selector selector) : selector_(std::move(selector)) {}
+
+ Order(Selector selector, Comparer comparer)
+ : selector_(std::move(selector)), comparer_(std::move(comparer)) {}
+
+ template <
+ class Value,
+ class Source,
+ class StorageType = typename std::decay<Value>::type,
+ class Result = typename std::result_of<Selector(Value)>::type>
+ class Generator
+ : public GenImpl<StorageType&&,
+ Generator<Value, Source, StorageType, Result>> {
static_assert(!Source::infinite, "Cannot sort infinite source!");
Source source_;
Selector selector_;
std::sort(vals.begin(), vals.end(), comparer);
return std::move(vals);
}
+
public:
- Generator(Source source,
- Selector selector,
- Comparer comparer)
- : source_(std::move(source)),
- selector_(std::move(selector)),
- comparer_(std::move(comparer)) {}
+ Generator(Source source, Selector selector, Comparer comparer)
+ : source_(std::move(source)),
+ selector_(std::move(selector)),
+ comparer_(std::move(comparer)) {}
VectorType operator|(const Collect<VectorType>&) const {
return asVector();
return asVector();
}
- template<class Body>
+ template <class Body>
void foreach(Body&& body) const {
for (auto& value : asVector()) {
body(std::move(value));
}
}
- template<class Handler>
+ template <class Handler>
bool apply(Handler&& handler) const {
auto comparer = [&](const StorageType& a, const StorageType& b) {
// swapped for minHeap
}
return true;
}
+
+ // Can only be run on and produce finite generators
+ static constexpr bool infinite = false;
};
- template<class Source,
- class Value,
- class Gen = Generator<Value, Source>>
+ template <class Source, class Value, class Gen = Generator<Value, Source>>
Gen compose(GenImpl<Value, Source>&& source) const {
return Gen(std::move(source.self()), selector_, comparer_);
}
- template<class Source,
- class Value,
- class Gen = Generator<Value, Source>>
+ template <class Source, class Value, class Gen = Generator<Value, Source>>
Gen compose(const GenImpl<Value, Source>& source) const {
return Gen(source.self(), selector_, comparer_);
}
};
-/*
- * TypeAssertion - For verifying the exact type of the value produced by a
- * generator. Useful for testing and debugging, and acts as a no-op at runtime.
- * Pass-through at runtime. Used through the 'assert_type<>()' factory method
- * like so:
+/**
+ * GroupBy - Group values by a given key selector, producing a sequence of
+ * groups.
*
- * auto c = from(vector) | assert_type<int&>() | sum;
+ * This type is usually used through the 'groupBy' helper function, like:
*
+ * auto bests
+ * = from(places)
+ * | groupBy([](const Place& p) {
+ * return p.city;
+ * })
+ * | [](Group<std::string, Place>&& g) {
+ * cout << g.key() << ": " << (g | first).description;
+ * };
*/
-template<class Expected>
-class TypeAssertion : public Operator<TypeAssertion<Expected>> {
+template <class Selector>
+class GroupBy : public Operator<GroupBy<Selector>> {
+ Selector selector_;
+
public:
- template<class Source, class Value>
- const Source& compose(const GenImpl<Value, Source>& source) const {
- static_assert(std::is_same<Expected, Value>::value,
- "assert_type() check failed");
+ GroupBy() {}
+
+ explicit GroupBy(Selector selector) : selector_(std::move(selector)) {}
+
+ template <
+ class Value,
+ class Source,
+ class ValueDecayed = typename std::decay<Value>::type,
+ class Key = typename std::result_of<Selector(Value)>::type,
+ class KeyDecayed = typename std::decay<Key>::type>
+ class Generator
+ : public GenImpl<
+ Group<KeyDecayed, ValueDecayed>&&,
+ Generator<Value, Source, ValueDecayed, Key, KeyDecayed>> {
+ static_assert(!Source::infinite, "Cannot group infinite source!");
+ Source source_;
+ Selector selector_;
+
+ public:
+ Generator(Source source, Selector selector)
+ : source_(std::move(source)), selector_(std::move(selector)) {}
+
+ typedef Group<KeyDecayed, ValueDecayed> GroupType;
+
+ template <class Handler>
+ bool apply(Handler&& handler) const {
+ std::unordered_map<KeyDecayed, typename GroupType::VectorType> groups;
+ source_ | [&](Value value) {
+ const Value& cv = value;
+ auto& group = groups[selector_(cv)];
+ group.push_back(std::forward<Value>(value));
+ };
+ for (auto& kg : groups) {
+ GroupType group(kg.first, std::move(kg.second));
+ if (!handler(std::move(group))) {
+ return false;
+ }
+ kg.second.clear();
+ }
+ return true;
+ }
+
+ // Can only be run on and produce finite generators
+ static constexpr bool infinite = false;
+ };
+
+ template <class Source, class Value, class Gen = Generator<Value, Source>>
+ Gen compose(GenImpl<Value, Source>&& source) const {
+ return Gen(std::move(source.self()), selector_);
+ }
+
+ template <class Source, class Value, class Gen = Generator<Value, Source>>
+ Gen compose(const GenImpl<Value, Source>& source) const {
+ return Gen(source.self(), selector_);
+ }
+};
+
+/*
+ * TypeAssertion - For verifying the exact type of the value produced by a
+ * generator. Useful for testing and debugging, and acts as a no-op at runtime.
+ * Pass-through at runtime. Used through the 'assert_type<>()' factory method
+ * like so:
+ *
+ * auto c = from(vector) | assert_type<int&>() | sum;
+ *
+ */
+template <class Expected>
+class TypeAssertion : public Operator<TypeAssertion<Expected>> {
+ public:
+ template <class Source, class Value>
+ const Source& compose(const GenImpl<Value, Source>& source) const {
+ static_assert(std::is_same<Expected, Value>::value,
+ "assert_type() check failed");
return source.self();
}
- template<class Source, class Value>
+ template <class Source, class Value>
Source&& compose(GenImpl<Value, Source>&& source) const {
static_assert(std::is_same<Expected, Value>::value,
"assert_type() check failed");
* })
* | take(10);
*/
-template<class Selector>
+template <class Selector>
class Distinct : public Operator<Distinct<Selector>> {
Selector selector_;
+
public:
- Distinct() {}
+ Distinct() = default;
- explicit Distinct(Selector selector)
- : selector_(std::move(selector))
- {}
+ explicit Distinct(Selector selector) : selector_(std::move(selector)) {}
- template<class Value,
- class Source>
+ template <class Value, class Source>
class Generator : public GenImpl<Value, Generator<Value, Source>> {
Source source_;
Selector selector_;
typedef typename std::decay<KeyType>::type KeyStorageType;
public:
- Generator(Source source,
- Selector selector)
- : source_(std::move(source)),
- selector_(std::move(selector)) {}
+ Generator(Source source, Selector selector)
+ : source_(std::move(source)), selector_(std::move(selector)) {}
- template<class Body>
+ template <class Body>
void foreach(Body&& body) const {
std::unordered_set<KeyStorageType> keysSeen;
source_.foreach([&](Value value) {
});
}
- template<class Handler>
+ template <class Handler>
bool apply(Handler&& handler) const {
std::unordered_set<KeyStorageType> keysSeen;
return source_.apply([&](Value value) -> bool {
return true;
});
}
+
+ // While running distinct on an infinite sequence might produce a
+ // conceptually finite sequence, it will take infinite time
+ static constexpr bool infinite = Source::infinite;
};
- template<class Source,
- class Value,
- class Gen = Generator<Value, Source>>
+ template <class Source, class Value, class Gen = Generator<Value, Source>>
Gen compose(GenImpl<Value, Source>&& source) const {
return Gen(std::move(source.self()), selector_);
}
- template<class Source,
- class Value,
- class Gen = Generator<Value, Source>>
+ template <class Source, class Value, class Gen = Generator<Value, Source>>
Gen compose(const GenImpl<Value, Source>& source) const {
return Gen(source.self(), selector_);
}
* Composer - Helper class for adapting pipelines into functors. Primarily used
* for 'mapOp'.
*/
-template<class Operators>
+template <class Operators>
class Composer {
Operators op_;
+
public:
- explicit Composer(Operators op)
- : op_(std::move(op)) {}
+ explicit Composer(Operators op) : op_(std::move(op)) {}
- template<class Source,
- class Ret = decltype(std::declval<Operators>()
- .compose(std::declval<Source>()))>
+ template <
+ class Source,
+ class Ret =
+ decltype(std::declval<Operators>().compose(std::declval<Source>()))>
Ret operator()(Source&& source) const {
return op_.compose(std::forward<Source>(source));
}
*/
class Batch : public Operator<Batch> {
size_t batchSize_;
+
public:
- explicit Batch(size_t batchSize)
- : batchSize_(batchSize) {
+ explicit Batch(size_t batchSize) : batchSize_(batchSize) {
if (batchSize_ == 0) {
throw std::invalid_argument("Batch size must be non-zero!");
}
}
- template<class Value,
- class Source,
- class StorageType = typename std::decay<Value>::type,
- class VectorType = std::vector<StorageType>>
- class Generator :
- public GenImpl<VectorType&,
- Generator<Value, Source, StorageType, VectorType>> {
+ template <
+ class Value,
+ class Source,
+ class StorageType = typename std::decay<Value>::type,
+ class VectorType = std::vector<StorageType>>
+ class Generator
+ : public GenImpl<VectorType&,
+ Generator<Value, Source, StorageType, VectorType>> {
Source source_;
size_t batchSize_;
- public:
+
+ public:
explicit Generator(Source source, size_t batchSize)
- : source_(std::move(source))
- , batchSize_(batchSize) {}
+ : source_(std::move(source)), batchSize_(batchSize) {}
- template<class Handler>
+ template <class Handler>
bool apply(Handler&& handler) const {
VectorType batch_;
batch_.reserve(batchSize_);
bool shouldContinue = source_.apply([&](Value value) -> bool {
- batch_.push_back(std::forward<Value>(value));
- if (batch_.size() == batchSize_) {
- bool needMore = handler(batch_);
- batch_.clear();
- return needMore;
- }
- // Always need more if the handler is not called.
- return true;
- });
+ batch_.push_back(std::forward<Value>(value));
+ if (batch_.size() == batchSize_) {
+ bool needMore = handler(batch_);
+ batch_.clear();
+ return needMore;
+ }
+ // Always need more if the handler is not called.
+ return true;
+ });
// Flush everything, if and only if `handler` hasn't returned false.
if (shouldContinue && !batch_.empty()) {
shouldContinue = handler(batch_);
return shouldContinue;
}
+ // Taking n-tuples of an infinite source is still infinite
static constexpr bool infinite = Source::infinite;
};
- template<class Source,
- class Value,
- class Gen = Generator<Value, Source>>
+ template <class Source, class Value, class Gen = Generator<Value, Source>>
Gen compose(GenImpl<Value, Source>&& source) const {
return Gen(std::move(source.self()), batchSize_);
}
- template<class Source,
- class Value,
- class Gen = Generator<Value, Source>>
+ template <class Source, class Value, class Gen = Generator<Value, Source>>
Gen compose(const GenImpl<Value, Source>& source) const {
return Gen(source.self(), batchSize_);
}
};
+
+/**
+ * Window - For overlapping the lifetimes of pipeline values, especially with
+ * Futures.
+ *
+ * This type is usually used through the 'window' helper function:
+ *
+ * auto responses
+ * = byLine(STDIN)
+ * | map(makeRequestFuture)
+ * | window(1000)
+ * | map(waitFuture)
+ * | as<vector>();
+ */
+class Window : public Operator<Window> {
+ size_t windowSize_;
+
+ public:
+ explicit Window(size_t windowSize) : windowSize_(windowSize) {
+ if (windowSize_ == 0) {
+ throw std::invalid_argument("Window size must be non-zero!");
+ }
+ }
+
+ template <
+ class Value,
+ class Source,
+ class StorageType = typename std::decay<Value>::type>
+ class Generator
+ : public GenImpl<StorageType&&, Generator<Value, Source, StorageType>> {
+ Source source_;
+ size_t windowSize_;
+
+ public:
+ explicit Generator(Source source, size_t windowSize)
+ : source_(std::move(source)), windowSize_(windowSize) {}
+
+ template <class Handler>
+ bool apply(Handler&& handler) const {
+ std::vector<StorageType> buffer;
+ buffer.reserve(windowSize_);
+ size_t readIndex = 0;
+ bool shouldContinue = source_.apply([&](Value value) -> bool {
+ if (buffer.size() < windowSize_) {
+ buffer.push_back(std::forward<Value>(value));
+ } else {
+ StorageType& entry = buffer[readIndex++];
+ if (readIndex == windowSize_) {
+ readIndex = 0;
+ }
+ if (!handler(std::move(entry))) {
+ return false;
+ }
+ entry = std::forward<Value>(value);
+ }
+ return true;
+ });
+ if (!shouldContinue) {
+ return false;
+ }
+ if (buffer.size() < windowSize_) {
+ for (StorageType& entry : buffer) {
+ if (!handler(std::move(entry))) {
+ return false;
+ }
+ }
+ } else {
+ for (size_t i = readIndex;;) {
+ StorageType& entry = buffer[i++];
+ if (!handler(std::move(entry))) {
+ return false;
+ }
+ if (i == windowSize_) {
+ i = 0;
+ }
+ if (i == readIndex) {
+ break;
+ }
+ }
+ }
+ return true;
+ }
+
+ // Taking n-tuples of an infinite source is still infinite
+ static constexpr bool infinite = Source::infinite;
+ };
+
+ template <class Source, class Value, class Gen = Generator<Value, Source>>
+ Gen compose(GenImpl<Value, Source>&& source) const {
+ return Gen(std::move(source.self()), windowSize_);
+ }
+
+ template <class Source, class Value, class Gen = Generator<Value, Source>>
+ Gen compose(const GenImpl<Value, Source>& source) const {
+ return Gen(source.self(), windowSize_);
+ }
+};
+
+/**
+ * Concat - For flattening generators of generators.
+ *
+ * This type is usually used through the 'concat' static value, like:
+ *
+ * auto edges =
+ * from(nodes)
+ * | map([](Node& x) {
+ * return from(x.neighbors)
+ * | map([&](Node& y) {
+ * return Edge(x, y);
+ * });
+ * })
+ * | concat
+ * | as<std::set>();
+ */
+class Concat : public Operator<Concat> {
+ public:
+ Concat() = default;
+
+ template <
+ class Inner,
+ class Source,
+ class InnerValue = typename std::decay<Inner>::type::ValueType>
+ class Generator
+ : public GenImpl<InnerValue, Generator<Inner, Source, InnerValue>> {
+ Source source_;
+
+ public:
+ explicit Generator(Source source) : source_(std::move(source)) {}
+
+ template <class Handler>
+ bool apply(Handler&& handler) const {
+ return source_.apply([&](Inner inner) -> bool {
+ return inner.apply(std::forward<Handler>(handler));
+ });
+ }
+
+ template <class Body>
+ void foreach(Body&& body) const {
+ source_.foreach([&](Inner inner) {
+ inner.foreach(std::forward<Body>(body));
+ });
+ }
+
+ // Resulting concatination is only finite if both Source and Inner are also
+ // finite. In one sence, if dosn't make sence to call concat when the Inner
+ // generator is infinite (you could just call first), so we could also just
+ // static_assert if the inner is infinite. Taking the less restrictive
+ // approch for now.
+ static constexpr bool infinite =
+ Source::infinite || std::decay<Inner>::type::infinite;
+ };
+
+ template <class Value, class Source, class Gen = Generator<Value, Source>>
+ Gen compose(GenImpl<Value, Source>&& source) const {
+ return Gen(std::move(source.self()));
+ }
+
+ template <class Value, class Source, class Gen = Generator<Value, Source>>
+ Gen compose(const GenImpl<Value, Source>& source) const {
+ return Gen(source.self());
+ }
+};
+
+/**
+ * RangeConcat - For flattening generators of iterables.
+ *
+ * This type is usually used through the 'rconcat' static value, like:
+ *
+ * map<int, vector<int>> adjacency;
+ * auto sinks =
+ * from(adjacency)
+ * | get<1>()
+ * | rconcat()
+ * | as<std::set>();
+ */
+class RangeConcat : public Operator<RangeConcat> {
+ public:
+ RangeConcat() = default;
+
+ template <
+ class Range,
+ class Source,
+ class InnerValue = typename ValueTypeOfRange<Range>::RefType>
+ class Generator
+ : public GenImpl<InnerValue, Generator<Range, Source, InnerValue>> {
+ Source source_;
+
+ public:
+ explicit Generator(Source source) : source_(std::move(source)) {}
+
+ template <class Body>
+ void foreach(Body&& body) const {
+ source_.foreach([&](Range range) {
+ for (auto& value : range) {
+ body(value);
+ }
+ });
+ }
+
+ template <class Handler>
+ bool apply(Handler&& handler) const {
+ return source_.apply([&](Range range) -> bool {
+ for (auto& value : range) {
+ if (!handler(value)) {
+ return false;
+ }
+ }
+ return true;
+ });
+ }
+
+ // This is similar to concat, except that the inner iterables all are finite
+ // so the only thing that matters is that the source is infinite.
+ static constexpr bool infinite = Source::infinite;
+ };
+
+ template <class Value, class Source, class Gen = Generator<Value, Source>>
+ Gen compose(GenImpl<Value, Source>&& source) const {
+ return Gen(std::move(source.self()));
+ }
+
+ template <class Value, class Source, class Gen = Generator<Value, Source>>
+ Gen compose(const GenImpl<Value, Source>& source) const {
+ return Gen(source.self());
+ }
+};
+
+/**
+ * GuardImpl - For handling exceptions from downstream computation. Requires the
+ * type of exception to catch, and handler function to invoke in the event of
+ * the exception. Note that the handler may:
+ * 1) return true to continue processing the sequence
+ * 2) return false to end the sequence immediately
+ * 3) throw, to pass the exception to the next catch
+ * The handler must match the signature 'bool(Exception&, Value)'.
+ *
+ * This type is used through the `guard` helper, like so:
+ *
+ * auto indexes
+ * = byLine(STDIN_FILENO)
+ * | guard<std::runtime_error>([](std::runtime_error& e,
+ * StringPiece sp) {
+ * LOG(ERROR) << sp << ": " << e.str();
+ * return true; // continue processing subsequent lines
+ * })
+ * | eachTo<int>()
+ * | as<vector>();
+ *
+ * TODO(tjackson): Rename this back to Guard.
+ **/
+template <class Exception, class ErrorHandler>
+class GuardImpl : public Operator<GuardImpl<Exception, ErrorHandler>> {
+ ErrorHandler handler_;
+
+ public:
+ explicit GuardImpl(ErrorHandler handler) : handler_(std::move(handler)) {}
+
+ template <class Value, class Source>
+ class Generator : public GenImpl<Value, Generator<Value, Source>> {
+ Source source_;
+ ErrorHandler handler_;
+
+ public:
+ explicit Generator(Source source, ErrorHandler handler)
+ : source_(std::move(source)), handler_(std::move(handler)) {}
+
+ template <class Handler>
+ bool apply(Handler&& handler) const {
+ return source_.apply([&](Value value) -> bool {
+ try {
+ handler(std::forward<Value>(value));
+ return true;
+ } catch (Exception& e) {
+ return handler_(e, std::forward<Value>(value));
+ }
+ });
+ }
+
+ // Just passes value though, length unaffected
+ static constexpr bool infinite = Source::infinite;
+ };
+
+ template <class Value, class Source, class Gen = Generator<Value, Source>>
+ Gen compose(GenImpl<Value, Source>&& source) const {
+ return Gen(std::move(source.self()), handler_);
+ }
+
+ template <class Value, class Source, class Gen = Generator<Value, Source>>
+ Gen compose(const GenImpl<Value, Source>& source) const {
+ return Gen(source.self(), handler_);
+ }
+};
+
+/**
+ * Dereference - For dereferencing a sequence of pointers while filtering out
+ * null pointers.
+ *
+ * This type is usually used through the 'dereference' static value, like:
+ *
+ * auto refs = from(ptrs) | dereference;
+ */
+class Dereference : public Operator<Dereference> {
+ public:
+ Dereference() = default;
+
+ template <
+ class Value,
+ class Source,
+ class Result = decltype(*std::declval<Value>())>
+ class Generator : public GenImpl<Result, Generator<Value, Source, Result>> {
+ Source source_;
+
+ public:
+ explicit Generator(Source source) : source_(std::move(source)) {}
+
+ template <class Body>
+ void foreach(Body&& body) const {
+ source_.foreach([&](Value value) {
+ if (value) {
+ return body(*std::forward<Value>(value));
+ }
+ });
+ }
+
+ template <class Handler>
+ bool apply(Handler&& handler) const {
+ return source_.apply([&](Value value) -> bool {
+ if (value) {
+ return handler(*std::forward<Value>(value));
+ }
+ return true;
+ });
+ }
+
+ // Just passes value though, length unaffected
+ static constexpr bool infinite = Source::infinite;
+ };
+
+ template <class Source, class Value, class Gen = Generator<Value, Source>>
+ Gen compose(GenImpl<Value, Source>&& source) const {
+ return Gen(std::move(source.self()));
+ }
+
+ template <class Source, class Value, class Gen = Generator<Value, Source>>
+ Gen compose(const GenImpl<Value, Source>& source) const {
+ return Gen(source.self());
+ }
+};
+
+/**
+ * Indirect - For producing a sequence of the addresses of the values in the
+ * input.
+ *
+ * This type is usually used through the 'indirect' static value, like:
+ *
+ * auto ptrs = from(refs) | indirect;
+ */
+class Indirect : public Operator<Indirect> {
+ public:
+ Indirect() = default;
+
+ template <
+ class Value,
+ class Source,
+ class Result = typename std::remove_reference<Value>::type*>
+ class Generator : public GenImpl<Result, Generator<Value, Source, Result>> {
+ Source source_;
+ static_assert(!std::is_rvalue_reference<Value>::value,
+ "Cannot use indirect on an rvalue");
+
+ public:
+ explicit Generator(Source source) : source_(std::move(source)) {}
+
+ template <class Body>
+ void foreach(Body&& body) const {
+ source_.foreach([&](Value value) {
+ return body(&std::forward<Value>(value));
+ });
+ }
+
+ template <class Handler>
+ bool apply(Handler&& handler) const {
+ return source_.apply([&](Value value) -> bool {
+ return handler(&std::forward<Value>(value));
+ });
+ }
+
+ // Just passes value though, length unaffected
+ static constexpr bool infinite = Source::infinite;
+ };
+
+ template <class Source, class Value, class Gen = Generator<Value, Source>>
+ Gen compose(GenImpl<Value, Source>&& source) const {
+ return Gen(std::move(source.self()));
+ }
+
+ template <class Source, class Value, class Gen = Generator<Value, Source>>
+ Gen compose(const GenImpl<Value, Source>& source) const {
+ return Gen(source.self());
+ }
+};
+
+/**
+ * Cycle - For repeating a sequence forever.
+ *
+ * This type is usually used through the 'cycle' static value, like:
+ *
+ * auto tests
+ * = from(samples)
+ * | cycle
+ * | take(100);
+ *
+ * or in the finite case:
+ *
+ * auto thrice = g | cycle(3);
+ */
+template <bool forever>
+class Cycle : public Operator<Cycle<forever>> {
+ off_t limit_; // not used if forever == true
+ public:
+ Cycle() = default;
+
+ explicit Cycle(off_t limit) : limit_(limit) {
+ static_assert(
+ !forever,
+ "Cycle limit constructor should not be used when forever == true.");
+ }
+
+ template <class Value, class Source>
+ class Generator : public GenImpl<Value, Generator<Value, Source>> {
+ Source source_;
+ off_t limit_;
+
+ public:
+ explicit Generator(Source source, off_t limit)
+ : source_(std::move(source)), limit_(limit) {}
+
+ template <class Handler>
+ bool apply(Handler&& handler) const {
+ bool cont;
+ auto handler2 = [&](Value value) {
+ cont = handler(std::forward<Value>(value));
+ return cont;
+ };
+ // Becomes an infinte loop if forever == true
+ for (off_t count = 0; (forever || count != limit_); ++count) {
+ cont = false;
+ source_.apply(handler2);
+ if (!cont) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ // This is the hardest one to infer. If we are simply doing a finite cycle,
+ // then (gen | cycle(n)) is infinite if and only if gen is infinite.
+ // However, if we are doing an infinite cycle, (gen | cycle) is infinite
+ // unless gen is empty. However, we will always mark (gen | cycle) as
+ // infinite, because patterns such as (gen | cycle | count) can either take
+ // on exactly one value, or infinite loop.
+ static constexpr bool infinite = forever || Source::infinite;
+ };
+
+ template <class Source, class Value, class Gen = Generator<Value, Source>>
+ Gen compose(GenImpl<Value, Source>&& source) const {
+ return Gen(std::move(source.self()), limit_);
+ }
+
+ template <class Source, class Value, class Gen = Generator<Value, Source>>
+ Gen compose(const GenImpl<Value, Source>& source) const {
+ return Gen(source.self(), limit_);
+ }
+
+ /**
+ * Convenience function for finite cycles used like:
+ *
+ * auto tripled = gen | cycle(3);
+ */
+ Cycle<false> operator()(off_t limit) const { return Cycle<false>(limit); }
+};
+
/*
- * Sinks
+ ******************************* Sinks *****************************************
*/
/**
* return sample * 0.1 + avg * 0.9;
* });
*/
-template<class Seed,
- class Fold>
+template <class Seed, class Fold>
class FoldLeft : public Operator<FoldLeft<Seed, Fold>> {
Seed seed_;
Fold fold_;
+
public:
- FoldLeft() {}
- FoldLeft(Seed seed,
- Fold fold)
- : seed_(std::move(seed))
- , fold_(std::move(fold))
- {}
-
- template<class Source,
- class Value>
+ FoldLeft() = default;
+ FoldLeft(Seed seed, Fold fold)
+ : seed_(std::move(seed)), fold_(std::move(fold)) {}
+
+ template <class Source, class Value>
Seed compose(const GenImpl<Value, Source>& source) const {
static_assert(!Source::infinite, "Cannot foldl infinite source");
Seed accum = seed_;
*/
class First : public Operator<First> {
public:
- First() { }
+ First() = default;
- template<class Source,
- class Value,
- class StorageType = typename std::decay<Value>::type>
- StorageType compose(const GenImpl<Value, Source>& source) const {
+ template <
+ class Source,
+ class Value,
+ class StorageType = typename std::decay<Value>::type>
+ Optional<StorageType> compose(const GenImpl<Value, Source>& source) const {
Optional<StorageType> accum;
source | [&](Value v) -> bool {
accum = std::forward<Value>(v);
return false;
};
- if (!accum.hasValue()) {
- throw EmptySequence();
- }
- return std::move(accum.value());
+ return accum;
}
};
-
/**
- * Any - For determining whether any values in a sequence satisfy a predicate.
+ * IsEmpty - a helper class for isEmpty and notEmpty
*
- * This type is primarily used through the 'any' static value, like:
+ * Essentially returns 'result' if the source is empty. Note that this cannot be
+ * called on an infinite source, because then there is only one possible return
+ * value.
*
- * bool any20xPrimes = seq(200, 210) | filter(isPrime) | any;
*
- * Note that it may also be used like so:
+ * Used primarily through 'isEmpty' and 'notEmpty' static values
*
- * bool any20xPrimes = seq(200, 210) | any(isPrime);
+ * bool hasPrimes = g | filter(prime) | notEmpty;
+ * bool lacksEvens = g | filter(even) | isEmpty;
*
+ * Also used in the implementation of 'any' and 'all'
*/
-class Any : public Operator<Any> {
+template <bool emptyResult>
+class IsEmpty : public Operator<IsEmpty<emptyResult>> {
public:
- Any() { }
+ IsEmpty() = default;
- template<class Source,
- class Value>
+ template <class Source, class Value>
bool compose(const GenImpl<Value, Source>& source) const {
- bool any = false;
- source | [&](Value v) -> bool {
- any = true;
- return false;
- };
- return any;
- }
-
- /**
- * Convenience function for use like:
- *
- * bool found = gen | any([](int i) { return i * i > 100; });
- */
- template<class Predicate,
- class Filter = Filter<Predicate>,
- class Composed = Composed<Filter, Any>>
- Composed operator()(Predicate pred) const {
- return Composed(Filter(std::move(pred)), Any());
- }
-};
-
-/**
- * All - For determining whether all values in a sequence satisfy a predicate.
- *
- * This type is primarily used through the 'any' static value, like:
- *
- * bool valid = from(input) | all(validate);
- *
- * Note: Passing an empty sequence through 'all()' will always return true.
- */
-template<class Predicate>
-class All : public Operator<All<Predicate>> {
- Predicate pred_;
- public:
- All() {}
- explicit All(Predicate pred)
- : pred_(std::move(pred))
- { }
-
- template<class Source,
- class Value>
- bool compose(const GenImpl<Value, Source>& source) const {
- static_assert(!Source::infinite, "Cannot call 'all' on infinite source");
- bool all = true;
- source | [&](Value v) -> bool {
- if (!pred_(std::forward<Value>(v))) {
- all = false;
- return false;
- }
- return true;
- };
- return all;
+ static_assert(!Source::infinite,
+ "Cannot call 'all', 'any', 'isEmpty', or 'notEmpty' on "
+ "infinite source. 'all' and 'isEmpty' will either return "
+ "false or hang. 'any' or 'notEmpty' will either return true "
+ "or hang.");
+ bool ans = emptyResult;
+ source |
+ [&](Value /* v */) -> bool {
+ ans = !emptyResult;
+ return false;
+ };
+ return ans;
}
};
* return best.size() >= current.size() ? best : current;
* });
*/
-template<class Reducer>
+template <class Reducer>
class Reduce : public Operator<Reduce<Reducer>> {
Reducer reducer_;
+
public:
- Reduce() {}
- explicit Reduce(Reducer reducer)
- : reducer_(std::move(reducer))
- {}
-
- template<class Source,
- class Value,
- class StorageType = typename std::decay<Value>::type>
- StorageType compose(const GenImpl<Value, Source>& source) const {
+ Reduce() = default;
+ explicit Reduce(Reducer reducer) : reducer_(std::move(reducer)) {}
+
+ template <
+ class Source,
+ class Value,
+ class StorageType = typename std::decay<Value>::type>
+ Optional<StorageType> compose(const GenImpl<Value, Source>& source) const {
+ static_assert(!Source::infinite, "Cannot reduce infinite source");
Optional<StorageType> accum;
source | [&](Value v) {
- if (accum.hasValue()) {
- accum = reducer_(std::move(accum.value()), std::forward<Value>(v));
+ if (auto target = accum.get_pointer()) {
+ *target = reducer_(std::move(*target), std::forward<Value>(v));
} else {
accum = std::forward<Value>(v);
}
};
- if (!accum.hasValue()) {
- throw EmptySequence();
- }
- return accum.value();
+ return accum;
}
};
*/
class Count : public Operator<Count> {
public:
- Count() { }
+ Count() = default;
- template<class Source,
- class Value>
+ template <class Source, class Value>
size_t compose(const GenImpl<Value, Source>& source) const {
static_assert(!Source::infinite, "Cannot count infinite source");
return foldl(size_t(0),
- [](size_t accum, Value v) {
- return accum + 1;
- }).compose(source);
+ [](size_t accum, Value /* v */) { return accum + 1; })
+ .compose(source);
}
};
*/
class Sum : public Operator<Sum> {
public:
- Sum() : Operator<Sum>() {}
+ Sum() = default;
- template<class Source,
- class Value,
- class StorageType = typename std::decay<Value>::type>
+ template <
+ class Source,
+ class Value,
+ class StorageType = typename std::decay<Value>::type>
StorageType compose(const GenImpl<Value, Source>& source) const {
static_assert(!Source::infinite, "Cannot sum infinite source");
return foldl(StorageType(0),
*
* bool contained = seq(1, 10) | map(square) | contains(49);
*/
-template<class Needle>
+template <class Needle>
class Contains : public Operator<Contains<Needle>> {
Needle needle_;
+
public:
- explicit Contains(Needle needle)
- : needle_(std::move(needle))
- {}
+ explicit Contains(Needle needle) : needle_(std::move(needle)) {}
- template<class Source,
- class Value,
- class StorageType = typename std::decay<Value>::type>
+ template <
+ class Source,
+ class Value,
+ class StorageType = typename std::decay<Value>::type>
bool compose(const GenImpl<Value, Source>& source) const {
static_assert(!Source::infinite,
"Calling contains on an infinite source might cause "
"an infinite loop.");
return !(source | [this](Value value) {
- return !(needle_ == std::forward<Value>(value));
- });
+ return !(needle_ == std::forward<Value>(value));
+ });
}
};
* return p.dateOfBirth;
* });
*/
-template<class Selector,
- class Comparer>
+template <class Selector, class Comparer>
class Min : public Operator<Min<Selector, Comparer>> {
Selector selector_;
Comparer comparer_;
+
+ template <typename T>
+ const T& asConst(const T& t) const {
+ return t;
+ }
+
public:
- Min() {}
-
- explicit Min(Selector selector)
- : selector_(std::move(selector))
- {}
-
- Min(Selector selector,
- Comparer comparer)
- : selector_(std::move(selector))
- , comparer_(std::move(comparer))
- {}
-
- template<class Value,
- class Source,
- class StorageType = typename std::decay<Value>::type,
- class Key = typename std::decay<
- typename std::result_of<Selector(Value)>::type
- >::type>
- StorageType compose(const GenImpl<Value, Source>& source) const {
+ Min() = default;
+
+ explicit Min(Selector selector) : selector_(std::move(selector)) {}
+
+ Min(Selector selector, Comparer comparer)
+ : selector_(std::move(selector)), comparer_(std::move(comparer)) {}
+
+ template <
+ class Value,
+ class Source,
+ class StorageType = typename std::decay<Value>::type,
+ class Key = typename std::decay<
+ typename std::result_of<Selector(Value)>::type>::type>
+ Optional<StorageType> compose(const GenImpl<Value, Source>& source) const {
+ static_assert(!Source::infinite,
+ "Calling min or max on an infinite source will cause "
+ "an infinite loop.");
Optional<StorageType> min;
Optional<Key> minKey;
source | [&](Value v) {
- Key key = selector_(std::forward<Value>(v));
- if (!minKey.hasValue() || comparer_(key, minKey.value())) {
- minKey = key;
- min = std::forward<Value>(v);
+ Key key = selector_(asConst(v)); // so that selector_ cannot mutate v
+ if (auto lastKey = minKey.get_pointer()) {
+ if (!comparer_(key, *lastKey)) {
+ return;
+ }
}
+ minKey = std::move(key);
+ min = std::forward<Value>(v);
};
- if (!min.hasValue()) {
- throw EmptySequence();
- }
- return min.value();
+ return min;
}
};
* from(results) | map([](Person& p) { return p.id })
* | appendTo(ids);
*/
-template<class Collection>
+template <class Collection>
class Append : public Operator<Append<Collection>> {
Collection* collection_;
+
public:
- explicit Append(Collection* collection)
- : collection_(collection)
- {}
+ explicit Append(Collection* collection) : collection_(collection) {}
- template<class Value,
- class Source>
+ template <class Value, class Source>
Collection& compose(const GenImpl<Value, Source>& source) const {
+ static_assert(!Source::infinite, "Cannot appendTo with infinite source");
source | [&](Value v) {
collection_->insert(collection_->end(), std::forward<Value>(v));
};
* | map(&toupper)
* | as<std::string>();
*/
-template<class Collection>
+template <class Collection>
class Collect : public Operator<Collect<Collection>> {
public:
- Collect() { }
+ Collect() = default;
- template<class Value,
- class Source,
- class StorageType = typename std::decay<Value>::type>
+ template <
+ class Value,
+ class Source,
+ class StorageType = typename std::decay<Value>::type>
Collection compose(const GenImpl<Value, Source>& source) const {
+ static_assert(!Source::infinite,
+ "Cannot convert infinite source to object with as.");
Collection collection;
source | [&](Value v) {
collection.insert(collection.end(), std::forward<Value>(v));
}
};
-
/**
* CollectTemplate - For collecting values from a source in a collection
* constructed using the specified template type. Given the type of values
* The allocator defaults to std::allocator, so this may be used for the STL
* containers by simply using operators like 'as<set>', 'as<deque>',
* 'as<vector>'. 'as', here is the helper method which is the usual means of
- * consturcting this operator.
+ * constructing this operator.
*
* Example:
*
* set<string> uniqueNames = from(names) | as<set>();
*/
-template<template<class, class> class Container,
- template<class> class Allocator>
+template <
+ template <class, class> class Container,
+ template <class> class Allocator>
class CollectTemplate : public Operator<CollectTemplate<Container, Allocator>> {
public:
- CollectTemplate() { }
+ CollectTemplate() = default;
- template<class Value,
- class Source,
- class StorageType = typename std::decay<Value>::type,
- class Collection = Container<StorageType, Allocator<StorageType>>>
+ template <
+ class Value,
+ class Source,
+ class StorageType = typename std::decay<Value>::type,
+ class Collection = Container<StorageType, Allocator<StorageType>>>
Collection compose(const GenImpl<Value, Source>& source) const {
+ static_assert(!Source::infinite,
+ "Cannot convert infinite source to object with as.");
Collection collection;
source | [&](Value v) {
collection.insert(collection.end(), std::forward<Value>(v));
};
/**
- * Concat - For flattening generators of generators.
+ * UnwrapOr - For unwrapping folly::Optional values, or providing the given
+ * fallback value. Usually used through the 'unwrapOr' helper like so:
*
- * This type is usually used through the 'concat' static value, like:
+ * auto best = from(scores) | max | unwrapOr(-1);
*
- * auto edges =
- * from(nodes)
- * | map([](Node& x) {
- * return from(x.neighbors)
- * | map([&](Node& y) {
- * return Edge(x, y);
- * });
- * })
- * | concat
- * | as<std::set>();
+ * Note that the fallback value needn't match the value in the Optional it is
+ * unwrapping. If mis-matched types are supported, the common type of the two is
+ * returned by value. If the types match, a reference (T&& > T& > const T&) is
+ * returned.
*/
-class Concat : public Operator<Concat> {
+template <class T>
+class UnwrapOr {
public:
- Concat() { }
-
- template<class Inner,
- class Source,
- class InnerValue = typename std::decay<Inner>::type::ValueType>
- class Generator :
- public GenImpl<InnerValue, Generator<Inner, Source, InnerValue>> {
- Source source_;
- public:
- explicit Generator(Source source)
- : source_(std::move(source)) {}
-
- template<class Handler>
- bool apply(Handler&& handler) const {
- return source_.apply([&](Inner inner) -> bool {
- return inner.apply(std::forward<Handler>(handler));
- });
- }
-
- template<class Body>
- void foreach(Body&& body) const {
- source_.foreach([&](Inner inner) {
- inner.foreach(std::forward<Body>(body));
- });
- }
-
- static constexpr bool infinite = Source::infinite;
- };
+ explicit UnwrapOr(T&& value) : value_(std::move(value)) {}
+ explicit UnwrapOr(const T& value) : value_(value) {}
- template<class Value,
- class Source,
- class Gen = Generator<Value, Source>>
- Gen compose(GenImpl<Value, Source>&& source) const {
- return Gen(std::move(source.self()));
- }
+ T& value() { return value_; }
+ const T& value() const { return value_; }
- template<class Value,
- class Source,
- class Gen = Generator<Value, Source>>
- Gen compose(const GenImpl<Value, Source>& source) const {
- return Gen(source.self());
- }
+ private:
+ T value_;
};
-/**
- * RangeConcat - For flattening generators of iterables.
- *
- * This type is usually used through the 'rconcat' static value, like:
- *
- * map<int, vector<int>> adjacency;
- * auto sinks =
- * from(adjacency)
- * | get<1>()
- * | rconcat()
- * | as<std::set>();
- */
-class RangeConcat : public Operator<RangeConcat> {
- public:
- RangeConcat() { }
-
- template<class Range,
- class Source,
- class InnerValue = typename ValueTypeOfRange<Range>::RefType>
- class Generator
- : public GenImpl<InnerValue, Generator<Range, Source, InnerValue>> {
- Source source_;
- public:
- explicit Generator(Source source)
- : source_(std::move(source)) {}
-
- template<class Body>
- void foreach(Body&& body) const {
- source_.foreach([&](Range range) {
- for (auto& value : range) {
- body(value);
- }
- });
- }
-
- template<class Handler>
- bool apply(Handler&& handler) const {
- return source_.apply([&](Range range) -> bool {
- for (auto& value : range) {
- if (!handler(value)) {
- return false;
- }
- }
- return true;
- });
- }
- };
-
- template<class Value,
- class Source,
- class Gen = Generator<Value, Source>>
- Gen compose(GenImpl<Value, Source>&& source) const {
- return Gen(std::move(source.self()));
- }
-
- template<class Value,
- class Source,
- class Gen = Generator<Value, Source>>
- Gen compose(const GenImpl<Value, Source>& source) const {
- return Gen(source.self());
+template <class T>
+T&& operator|(Optional<T>&& opt, UnwrapOr<T>&& fallback) {
+ if (T* p = opt.get_pointer()) {
+ return std::move(*p);
}
-};
-
-
-/**
- * GuardImpl - For handling exceptions from downstream computation. Requires the
- * type of exception to catch, and handler function to invoke in the event of
- * the exception. Note that the handler may:
- * 1) return true to continue processing the sequence
- * 2) return false to end the sequence immediately
- * 3) throw, to pass the exception to the next catch
- * The handler must match the signature 'bool(Exception&, Value)'.
- *
- * This type is used through the `guard` helper, like so:
- *
- * auto indexes
- * = byLine(STDIN_FILENO)
- * | guard<std::runtime_error>([](std::runtime_error& e,
- * StringPiece sp) {
- * LOG(ERROR) << sp << ": " << e.str();
- * return true; // continue processing subsequent lines
- * })
- * | eachTo<int>()
- * | as<vector>();
- *
- * TODO(tjackson): Rename this back to Guard.
- **/
-template<class Exception,
- class ErrorHandler>
-class GuardImpl : public Operator<GuardImpl<Exception, ErrorHandler>> {
- ErrorHandler handler_;
- public:
- GuardImpl(ErrorHandler handler)
- : handler_(std::move(handler)) {}
-
- template<class Value,
- class Source>
- class Generator : public GenImpl<Value, Generator<Value, Source>> {
- Source source_;
- ErrorHandler handler_;
- public:
- explicit Generator(Source source,
- ErrorHandler handler)
- : source_(std::move(source)),
- handler_(std::move(handler)) {}
-
- template<class Handler>
- bool apply(Handler&& handler) const {
- return source_.apply([&](Value value) -> bool {
- try {
- handler(std::forward<Value>(value));
- return true;
- } catch (Exception& e) {
- return handler_(e, std::forward<Value>(value));
- }
- });
- }
-
- static constexpr bool infinite = Source::infinite;
- };
+ return std::move(fallback.value());
+}
- template<class Value,
- class Source,
- class Gen = Generator<Value, Source>>
- Gen compose(GenImpl<Value, Source>&& source) const {
- return Gen(std::move(source.self()), handler_);
+template <class T>
+T& operator|(Optional<T>& opt, UnwrapOr<T>& fallback) {
+ if (T* p = opt.get_pointer()) {
+ return *p;
}
+ return fallback.value();
+}
- template<class Value,
- class Source,
- class Gen = Generator<Value, Source>>
- Gen compose(const GenImpl<Value, Source>& source) const {
- return Gen(source.self(), handler_);
+template <class T>
+const T& operator|(const Optional<T>& opt, const UnwrapOr<T>& fallback) {
+ if (const T* p = opt.get_pointer()) {
+ return *p;
}
-};
-
-/**
- * Cycle - For repeating a sequence forever.
- *
- * This type is usually used through the 'cycle' static value, like:
- *
- * auto tests
- * = from(samples)
- * | cycle
- * | take(100);
- */
-class Cycle : public Operator<Cycle> {
- off_t limit_; // -1 for infinite
- public:
- Cycle()
- : limit_(-1) { }
-
- explicit Cycle(off_t limit)
- : limit_(limit) { }
-
- template<class Value,
- class Source>
- class Generator : public GenImpl<Value, Generator<Value, Source>> {
- Source source_;
- off_t limit_; // -1 for infinite
- public:
- explicit Generator(Source source, off_t limit)
- : source_(std::move(source))
- , limit_(limit) {}
-
- template<class Handler>
- bool apply(Handler&& handler) const {
- bool cont;
- auto handler2 = [&](Value value) {
- cont = handler(std::forward<Value>(value));
- return cont;
- };
- for (off_t count = 0; count != limit_; ++count) {
- cont = false;
- source_.apply(handler2);
- if (!cont) {
- return false;
- }
- }
- return true;
- }
+ return fallback.value();
+}
- // not actually infinite, since an empty generator will end the cycles.
- static constexpr bool infinite = Source::infinite;
- };
+// Mixed type unwrapping always returns values, moving where possible
+template <
+ class T,
+ class U,
+ class R = typename std::enable_if<
+ !std::is_same<T, U>::value,
+ typename std::common_type<T, U>::type>::type>
+R operator|(Optional<T>&& opt, UnwrapOr<U>&& fallback) {
+ if (T* p = opt.get_pointer()) {
+ return std::move(*p);
+ }
+ return std::move(fallback.value());
+}
- template<class Source,
- class Value,
- class Gen = Generator<Value, Source>>
- Gen compose(GenImpl<Value, Source>&& source) const {
- return Gen(std::move(source.self()), limit_);
- }
+template <
+ class T,
+ class U,
+ class R = typename std::enable_if<
+ !std::is_same<T, U>::value,
+ typename std::common_type<T, U>::type>::type>
+R operator|(const Optional<T>& opt, UnwrapOr<U>&& fallback) {
+ if (const T* p = opt.get_pointer()) {
+ return *p;
+ }
+ return std::move(fallback.value());
+}
- template<class Source,
- class Value,
- class Gen = Generator<Value, Source>>
- Gen compose(const GenImpl<Value, Source>& source) const {
- return Gen(source.self(), limit_);
- }
+template <
+ class T,
+ class U,
+ class R = typename std::enable_if<
+ !std::is_same<T, U>::value,
+ typename std::common_type<T, U>::type>::type>
+R operator|(Optional<T>&& opt, const UnwrapOr<U>& fallback) {
+ if (T* p = opt.get_pointer()) {
+ return std::move(*p);
+ }
+ return fallback.value();
+}
- /**
- * Convenience function for use like:
- *
- * auto tripled = gen | cycle(3);
- */
- Cycle operator()(off_t limit) const {
- return Cycle(limit);
- }
-};
+template <
+ class T,
+ class U,
+ class R = typename std::enable_if<
+ !std::is_same<T, U>::value,
+ typename std::common_type<T, U>::type>::type>
+R operator|(const Optional<T>& opt, const UnwrapOr<U>& fallback) {
+ if (const T* p = opt.get_pointer()) {
+ return *p;
+ }
+ return fallback.value();
+}
/**
- * Dereference - For dereferencing a sequence of pointers while filtering out
- * null pointers.
- *
- * This type is usually used through the 'dereference' static value, like:
+ * Unwrap - For unwrapping folly::Optional values in a folly::gen style. Usually
+ * used through the 'unwrap' instace like so:
*
- * auto refs = from(ptrs) | dereference;
+ * auto best = from(scores) | max | unwrap; // may throw
*/
-class Dereference : public Operator<Dereference> {
- public:
- Dereference() {}
-
- template<class Value,
- class Source,
- class Result = decltype(*std::declval<Value>())>
- class Generator : public GenImpl<Result, Generator<Value, Source, Result>> {
- Source source_;
- public:
- explicit Generator(Source source)
- : source_(std::move(source)) {}
-
- template<class Body>
- void foreach(Body&& body) const {
- source_.foreach([&](Value value) {
- if (value) {
- return body(*value);
- }
- });
- }
-
- template<class Handler>
- bool apply(Handler&& handler) const {
- return source_.apply([&](Value value) -> bool {
- if (value) {
- return handler(*value);
- }
- return true;
- });
- }
+class Unwrap {};
- // not actually infinite, since an empty generator will end the cycles.
- static constexpr bool infinite = Source::infinite;
- };
+template <class T>
+T&& operator|(Optional<T>&& opt, const Unwrap&) {
+ return std::move(opt.value());
+}
- template<class Source,
- class Value,
- class Gen = Generator<Value, Source>>
- Gen compose(GenImpl<Value, Source>&& source) const {
- return Gen(std::move(source.self()));
- }
+template <class T>
+T& operator|(Optional<T>& opt, const Unwrap&) {
+ return opt.value();
+}
- template<class Source,
- class Value,
- class Gen = Generator<Value, Source>>
- Gen compose(const GenImpl<Value, Source>& source) const {
- return Gen(source.self());
- }
-};
+template <class T>
+const T& operator|(const Optional<T>& opt, const Unwrap&) {
+ return opt.value();
+}
-} //::detail
+} // namespace detail
/**
* VirtualGen<T> - For wrapping template types in simple polymorphic wrapper.
**/
-template<class Value>
+template <class Value>
class VirtualGen : public GenImpl<Value, VirtualGen<Value>> {
class WrapperBase {
public:
- virtual ~WrapperBase() {}
+ virtual ~WrapperBase() noexcept {}
virtual bool apply(const std::function<bool(Value)>& handler) const = 0;
virtual void foreach(const std::function<void(Value)>& body) const = 0;
virtual std::unique_ptr<const WrapperBase> clone() const = 0;
};
- template<class Wrapped>
+ template <class Wrapped>
class WrapperImpl : public WrapperBase {
Wrapped wrapped_;
+
public:
- explicit WrapperImpl(Wrapped wrapped)
- : wrapped_(std::move(wrapped)) {
- }
+ explicit WrapperImpl(Wrapped wrapped) : wrapped_(std::move(wrapped)) {}
- virtual bool apply(const std::function<bool(Value)>& handler) const {
+ bool apply(const std::function<bool(Value)>& handler) const override {
return wrapped_.apply(handler);
}
- virtual void foreach(const std::function<void(Value)>& body) const {
+ void foreach(const std::function<void(Value)>& body) const override {
wrapped_.foreach(body);
}
- virtual std::unique_ptr<const WrapperBase> clone() const {
+ std::unique_ptr<const WrapperBase> clone() const override {
return std::unique_ptr<const WrapperBase>(new WrapperImpl(wrapped_));
}
};
std::unique_ptr<const WrapperBase> wrapper_;
public:
- template<class Self>
+ template <class Self>
/* implicit */ VirtualGen(Self source)
- : wrapper_(new WrapperImpl<Self>(std::move(source)))
- { }
+ : wrapper_(new WrapperImpl<Self>(std::move(source))) {}
- VirtualGen(VirtualGen&& source)
- : wrapper_(std::move(source.wrapper_))
- { }
+ VirtualGen(VirtualGen&& source) noexcept
+ : wrapper_(std::move(source.wrapper_)) {}
- VirtualGen(const VirtualGen& source)
- : wrapper_(source.wrapper_->clone())
- { }
+ VirtualGen(const VirtualGen& source) : wrapper_(source.wrapper_->clone()) {}
VirtualGen& operator=(const VirtualGen& source) {
wrapper_.reset(source.wrapper_->clone());
return *this;
}
- VirtualGen& operator=(VirtualGen&& source) {
- wrapper_= std::move(source.wrapper_);
+ VirtualGen& operator=(VirtualGen&& source) noexcept {
+ wrapper_ = std::move(source.wrapper_);
return *this;
}
* non-template operators, statically defined to avoid the need for anything but
* the header.
*/
-static const detail::Sum sum;
+constexpr detail::Sum sum{};
-static const detail::Count count;
+constexpr detail::Count count{};
-static const detail::First first;
+constexpr detail::First first{};
-/**
- * Use directly for detecting any values, or as a function to detect values
- * which pass a predicate:
- *
- * auto nonempty = g | any;
- * auto evens = g | any(even);
- */
-static const detail::Any any;
+constexpr detail::IsEmpty<true> isEmpty{};
-static const detail::Min<Identity, Less> min;
+constexpr detail::IsEmpty<false> notEmpty{};
-static const detail::Min<Identity, Greater> max;
+constexpr detail::Min<Identity, Less> min{};
-static const detail::Order<Identity> order;
+constexpr detail::Min<Identity, Greater> max{};
-static const detail::Distinct<Identity> distinct;
+constexpr detail::Order<Identity> order{};
-static const detail::Map<Move> move;
+constexpr detail::Distinct<Identity> distinct{};
-static const detail::Concat concat;
+constexpr detail::Map<Move> move{};
-static const detail::RangeConcat rconcat;
+constexpr detail::Concat concat{};
-/**
- * Use directly for infinite sequences, or as a function to limit cycle count.
- *
- * auto forever = g | cycle;
- * auto thrice = g | cycle(3);
- */
-static const detail::Cycle cycle;
+constexpr detail::RangeConcat rconcat{};
-static const detail::Dereference dereference;
+constexpr detail::Cycle<true> cycle{};
-inline detail::Take take(size_t count) {
- return detail::Take(count);
+constexpr detail::Dereference dereference{};
+
+constexpr detail::Indirect indirect{};
+
+constexpr detail::Unwrap unwrap{};
+
+template <class Number>
+inline detail::Take take(Number count) {
+ if (count < 0) {
+ throw std::invalid_argument("Negative value passed to take()");
+ }
+ return detail::Take(static_cast<size_t>(count));
}
-template<class Random = std::default_random_engine>
+inline detail::Stride stride(size_t s) { return detail::Stride(s); }
+
+template <class Random = std::default_random_engine>
inline detail::Sample<Random> sample(size_t count, Random rng = Random()) {
return detail::Sample<Random>(count, std::move(rng));
}
-inline detail::Skip skip(size_t count) {
- return detail::Skip(count);
-}
+inline detail::Skip skip(size_t count) { return detail::Skip(count); }
inline detail::Batch batch(size_t batchSize) {
return detail::Batch(batchSize);
}
-}} //folly::gen
+inline detail::Window window(size_t windowSize) {
+ return detail::Window(windowSize);
+}
+
+} // namespace gen
+} // namespace folly
-#pragma GCC diagnostic pop
+FOLLY_POP_WARNING