/*
- * Copyright 2012 Facebook, Inc.
+ * Copyright 2013 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.
*/
+// Ignore shadowing warnings within this file, so includers can use -Wshadow.
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wshadow"
+
namespace folly { namespace gen {
/**
typedef typename std::decay<Value>::type StorageType;
/**
- * apply() - Send all values produced by this generator to given
- * handler until it returns false. Returns true if the false iff the handler
- * returned false.
+ * apply() - Send all values produced by this generator to given handler until
+ * the handler returns false. Returns false if and only if the handler passed
+ * in returns false. Note: It should return true even if it completes (without
+ * the handler returning false), as 'Chain' uses the return value of apply to
+ * determine if it should process the second object in its chain.
*/
template<class Handler>
bool apply(Handler&& handler) const;
*/
template<class Body>
void foreach(Body&& body) const {
- this->self().apply([&](Value value) {
+ this->self().apply([&](Value value) -> bool {
+ static_assert(!infinite, "Cannot call foreach on infinite GenImpl");
body(std::forward<Value>(value));
return true;
});
}
+
+ // Child classes should override if the sequence generated is *definitely*
+ // infinite. 'infinite' may be false_type for some infinite sequences
+ // (due the the Halting Problem).
+ static constexpr bool infinite = false;
};
template<class LeftValue,
typename std::enable_if<
IsCompatibleSignature<Handler, void(Value)>::value>::type
operator|(const GenImpl<Value, Gen>& gen, Handler&& handler) {
+ static_assert(!Gen::infinite,
+ "Cannot pull all values from an infinite sequence.");
gen.self().foreach(std::forward<Handler>(handler));
}
class Gen,
class Handler>
typename std::enable_if<
- IsCompatibleSignature<Handler, bool(Value)>::value>::type
+ IsCompatibleSignature<Handler, bool(Value)>::value, bool>::type
operator|(const GenImpl<Value, Gen>& gen, Handler&& handler) {
- gen.self().apply(std::forward<Handler>(handler));
+ return gen.self().apply(std::forward<Handler>(handler));
}
/**
class Gen,
class Op>
auto operator|(const GenImpl<Value, Gen>& gen, const Operator<Op>& op) ->
-decltype(op.self().compose(gen)) {
+decltype(op.self().compose(gen.self())) {
return op.self().compose(gen.self());
}
class Value>
class ReferencedSource :
public GenImpl<Value, ReferencedSource<Container, Value>> {
- Container* const container_;
+ Container* container_;
public:
explicit ReferencedSource(Container* container)
: container_(container) {}
static_assert(
!std::is_reference<Container>::value,
"Can't copy into a reference");
- const std::shared_ptr<const Container> copy_;
+ std::shared_ptr<const Container> copy_;
public:
typedef Container ContainerType;
!std::is_const<Value>::value, "Value mustn't be const or ref.");
Value bounds_[endless ? 1 : 2];
public:
- explicit Sequence(const Value& begin)
- : bounds_{begin} {
+ explicit Sequence(Value begin)
+ : bounds_{std::move(begin)} {
static_assert(endless, "Must supply 'end'");
}
- explicit Sequence(const Value& begin, const Value& end)
- : bounds_{begin, end} {}
+ Sequence(Value begin,
+ Value end)
+ : bounds_{std::move(begin), std::move(end)} {}
template<class Handler>
bool apply(Handler&& handler) const {
body(arg);
}
}
+
+ static constexpr bool infinite = endless;
};
/**
template<class Value, class First, class Second>
class Chain : public GenImpl<Value,
Chain<Value, First, Second>> {
- const First first_;
- const Second second_;
+ First first_;
+ Second second_;
public:
explicit Chain(First first, Second second)
: first_(std::move(first))
first_.foreach(std::forward<Body>(body));
second_.foreach(std::forward<Body>(body));
}
+
+ static constexpr bool infinite = First::infinite || Second::infinite;
+};
+
+/**
+ * GenratorBuilder - Helper for GENERTATOR macro.
+ **/
+template<class Value>
+struct GeneratorBuilder {
+ template<class Source,
+ class Yield = detail::Yield<Value, Source>>
+ Yield operator+(Source&& source) {
+ return Yield(std::forward<Source>(source));
+ }
};
/**
**/
template<class Value, class Source>
class Yield : public GenImpl<Value, Yield<Value, Source>> {
- const Source source_;
+ Source source_;
public:
explicit Yield(Source source)
: source_(std::move(source)) {
}
};
+template<class Value>
+class Empty : public GenImpl<Value, Empty<Value>> {
+ public:
+ template<class Handler>
+ bool apply(Handler&&) const { return true; }
+};
/*
* Operators
*/
template<class Predicate>
class Map : public Operator<Map<Predicate>> {
- const Predicate predicate_;
+ Predicate pred_;
public:
- explicit Map(const Predicate& predicate = Predicate())
- : predicate_(predicate)
+ Map() {}
+
+ explicit Map(Predicate pred)
+ : pred_(std::move(pred))
{ }
template<class Value,
>::type>
class Generator :
public GenImpl<Result, Generator<Value, Source, Result>> {
- const Source source_;
- const Predicate pred_;
+ Source source_;
+ Predicate pred_;
public:
explicit Generator(Source source, const Predicate& pred)
: source_(std::move(source)), pred_(pred) {}
return handler(pred_(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()), predicate_);
+ return Gen(std::move(source.self()), pred_);
}
template<class Source,
class Value,
class Gen = Generator<Value, Source>>
Gen compose(const GenImpl<Value, Source>& source) const {
- return Gen(source.self(), predicate_);
+ return Gen(source.self(), pred_);
}
};
*/
template<class Predicate>
class Filter : public Operator<Filter<Predicate>> {
- const Predicate predicate_;
+ Predicate pred_;
public:
- explicit Filter(const Predicate& predicate)
- : predicate_(predicate)
+ Filter() {}
+ explicit Filter(Predicate pred)
+ : pred_(std::move(pred))
{ }
template<class Value,
class Source>
class Generator : public GenImpl<Value, Generator<Value, Source>> {
- const Source source_;
- const Predicate pred_;
+ Source source_;
+ Predicate pred_;
public:
explicit Generator(Source source, const Predicate& pred)
: source_(std::move(source)), pred_(pred) {}
return true;
});
}
+
+ 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()), predicate_);
+ return Gen(std::move(source.self()), pred_);
}
template<class Source,
class Value,
class Gen = Generator<Value, Source>>
Gen compose(const GenImpl<Value, Source>& source) const {
- return Gen(source.self(), predicate_);
+ return Gen(source.self(), pred_);
}
};
*/
template<class Predicate>
class Until : public Operator<Until<Predicate>> {
- const Predicate predicate_;
+ Predicate pred_;
public:
- explicit Until(const Predicate& predicate)
- : predicate_(predicate)
- { }
+ Until() {}
+ explicit Until(Predicate pred)
+ : pred_(std::move(pred))
+ {}
template<class Value,
- class Source,
- class Result = typename std::result_of<Predicate(Value)>::type>
- class Generator :
- public GenImpl<Result, Generator<Value, Source, Result>> {
- const Source source_;
- const Predicate pred_;
- public:
+ 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) {}
template<class Handler>
bool apply(Handler&& handler) const {
- return source_.apply([&](Value value) -> bool {
- return !pred_(std::forward<Value>(value))
- && handler(std::forward<Value>(value));
+ bool cancelled = false;
+ source_.apply([&](Value value) -> bool {
+ if (pred_(value)) { // un-forwarded to disable move
+ return false;
+ }
+ if (!handler(std::forward<Value>(value))) {
+ cancelled = true;
+ return false;
+ }
+ return true;
});
+ return !cancelled;
}
};
class Value,
class Gen = Generator<Value, Source>>
Gen compose(GenImpl<Value, Source>&& source) const {
- return Gen(std::move(source.self()), predicate_);
+ return Gen(std::move(source.self()), pred_);
}
template<class Source,
class Value,
class Gen = Generator<Value, Source>>
Gen compose(const GenImpl<Value, Source>& source) const {
- return Gen(source.self(), predicate_);
+ return Gen(source.self(), pred_);
}
+
+ // Theoretically an 'until' might stop an infinite
+ static constexpr bool infinite = false;
};
/**
* | take(10);
*/
class Take : public Operator<Take> {
- const size_t count_;
-public:
+ size_t count_;
+ public:
explicit Take(size_t count)
: count_(count) {}
class Source>
class Generator :
public GenImpl<Value, Generator<Value, Source>> {
- const Source source_;
- const size_t count_;
+ Source source_;
+ size_t count_;
public:
explicit Generator(Source source, size_t count)
: source_(std::move(source)) , count_(count) {}
bool apply(Handler&& handler) const {
if (count_ == 0) { return false; }
size_t n = count_;
- return source_.apply([&](Value value) -> bool {
- if (!handler(std::forward<Value>(value))) {
- return false;
- }
- return --n;
- });
+ bool cancelled = false;
+ source_.apply([&](Value value) -> bool {
+ if (!handler(std::forward<Value>(value))) {
+ cancelled = true;
+ return false;
+ }
+ return --n;
+ });
+ return !cancelled;
}
};
}
};
+/**
+ * Sample - For taking a random sample of N elements from a sequence
+ * (without replacement).
+ */
+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>> {
+ 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,
+ "Random number generator must support big values");
+ Source source_;
+ size_t count_;
+ mutable Rand rng_;
+ public:
+ explicit Generator(Source source, size_t count, Random rng)
+ : source_(std::move(source)) , count_(count), rng_(std::move(rng)) {}
+
+ template<class Handler>
+ bool apply(Handler&& handler) const {
+ 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);
+ }
+ }
+ ++n;
+ });
+
+ // output is unsorted!
+ for (auto& val: v) {
+ if (!handler(std::move(val))) {
+ return false;
+ }
+ }
+ return true;
+ }
+ };
+
+ 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>>
+ Gen compose(const GenImpl<Value, Source>& source) const {
+ return Gen(source.self(), count_, rng_);
+ }
+};
+
/**
* Skip - For skipping N items from the beginning of a source generator.
*
* | take(10);
*/
class Skip : public Operator<Skip> {
- const size_t count_;
-public:
+ size_t count_;
+ public:
explicit Skip(size_t count)
: count_(count) {}
class Source>
class Generator :
public GenImpl<Value, Generator<Value, Source>> {
- const Source source_;
- const size_t count_;
- public:
+ Source source_;
+ size_t count_;
+ public:
explicit Generator(Source source, size_t count)
: source_(std::move(source)) , count_(count) {}
template<class Handler>
bool apply(Handler&& handler) const {
if (count_ == 0) {
- return source_.apply(handler);
+ return source_.apply(std::forward<Handler>(handler));
}
size_t n = 0;
return source_.apply([&](Value value) -> bool {
return handler(std::forward<Value>(value));
});
}
+
+ static constexpr bool infinite = Source::infinite;
};
template<class Source,
*/
template<class Selector, class Comparer>
class Order : public Operator<Order<Selector, Comparer>> {
- const Selector selector_;
- const Comparer comparer_;
+ Selector selector_;
+ Comparer comparer_;
public:
- Order(const Selector& selector = Selector(),
- const Comparer& comparer = Comparer())
- : selector_(selector) , comparer_(comparer) {}
+ 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 Generator :
public GenImpl<StorageType&&,
Generator<Value, Source, StorageType, Result>> {
- const Source source_;
- const Selector selector_;
- const Comparer comparer_;
+ static_assert(!Source::infinite, "Cannot sort infinite source!");
+ Source source_;
+ Selector selector_;
+ Comparer comparer_;
typedef std::vector<StorageType> VectorType;
}
public:
Generator(Source source,
- const Selector& selector,
- const Comparer& comparer)
+ Selector selector,
+ Comparer comparer)
: source_(std::move(source)),
- selector_(selector),
- comparer_(comparer) {}
+ selector_(std::move(selector)),
+ comparer_(std::move(comparer)) {}
VectorType operator|(const Collect<VectorType>&) const {
return asVector();
}
};
+/*
+ * 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>
+ Source&& compose(GenImpl<Value, Source>&& source) const {
+ static_assert(std::is_same<Expected, Value>::value,
+ "assert_type() check failed");
+ return std::move(source.self());
+ }
+};
+
+/**
+ * Distinct - For filtering duplicates out of a sequence. A selector may be
+ * provided to generate a key to uniquify for each value.
+ *
+ * This type is usually used through the 'distinct' helper function, like:
+ *
+ * auto closest = from(results)
+ * | distinctBy([](Item& i) {
+ * return i.target;
+ * })
+ * | take(10);
+ */
+template<class Selector>
+class Distinct : public Operator<Distinct<Selector>> {
+ Selector selector_;
+ public:
+ Distinct() {}
+
+ explicit Distinct(Selector selector)
+ : selector_(std::move(selector))
+ {}
+
+ template<class Value,
+ class Source>
+ class Generator : public GenImpl<Value, Generator<Value, Source>> {
+ Source source_;
+ Selector selector_;
+
+ typedef typename std::decay<Value>::type StorageType;
+
+ // selector_ cannot be passed an rvalue or it would end up passing the husk
+ // of a value to the downstream operators.
+ typedef const StorageType& ParamType;
+
+ typedef typename std::result_of<Selector(ParamType)>::type KeyType;
+ typedef typename std::decay<KeyType>::type KeyStorageType;
+
+ public:
+ Generator(Source source,
+ Selector selector)
+ : source_(std::move(source)),
+ selector_(std::move(selector)) {}
+
+ template<class Body>
+ void foreach(Body&& body) const {
+ std::unordered_set<KeyStorageType> keysSeen;
+ source_.foreach([&](Value value) {
+ if (keysSeen.insert(selector_(ParamType(value))).second) {
+ body(std::forward<Value>(value));
+ }
+ });
+ }
+
+ template<class Handler>
+ bool apply(Handler&& handler) const {
+ std::unordered_set<KeyStorageType> keysSeen;
+ return source_.apply([&](Value value) -> bool {
+ if (keysSeen.insert(selector_(ParamType(value))).second) {
+ return handler(std::forward<Value>(value));
+ }
+ return true;
+ });
+ }
+ };
+
+ 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_);
+ }
+};
+
+/**
+ * Batch - For producing fixed-size batches of each value from a source.
+ *
+ * This type is usually used through the 'batch' helper function:
+ *
+ * auto batchSums
+ * = seq(1, 10)
+ * | batch(3)
+ * | map([](const std::vector<int>& batch) {
+ * return from(batch) | sum;
+ * })
+ * | as<vector>();
+ */
+class Batch : public Operator<Batch> {
+ size_t batchSize_;
+ public:
+ 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>> {
+ Source source_;
+ size_t batchSize_;
+ public:
+ explicit Generator(Source source, size_t batchSize)
+ : source_(std::move(source))
+ , batchSize_(batchSize) {}
+
+ 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;
+ });
+ // Flush everything, if and only if `handler` hasn't returned false.
+ if (shouldContinue && !batch_.empty()) {
+ shouldContinue = handler(batch_);
+ batch_.clear();
+ }
+ return shouldContinue;
+ }
+
+ 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()), batchSize_);
+ }
+
+ template<class Source,
+ class Value,
+ class Gen = Generator<Value, Source>>
+ Gen compose(const GenImpl<Value, Source>& source) const {
+ return Gen(source.self(), batchSize_);
+ }
+};
+
/**
* Composed - For building up a pipeline of operations to perform, absent any
* particular source generator. Useful for building up custom pipelines.
template<class First,
class Second>
class Composed : public Operator<Composed<First, Second>> {
- const First first_;
- const Second second_;
- public:
- Composed() {}
- Composed(First first, Second second)
- : first_(std::move(first))
- , second_(std::move(second)) {}
+ First first_;
+ Second second_;
+ public:
+ Composed() {}
+
+ Composed(First first, Second second)
+ : first_(std::move(first))
+ , second_(std::move(second)) {}
template<class Source,
class Value,
template<class Seed,
class Fold>
class FoldLeft : public Operator<FoldLeft<Seed, Fold>> {
- const Seed seed_;
- const Fold fold_;
+ Seed seed_;
+ Fold fold_;
public:
- FoldLeft(const Seed& seed, const Fold& fold)
- : seed_(seed)
- , fold_(fold)
+ FoldLeft() {}
+ 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_;
source | [&](Value v) {
accum = fold_(std::move(accum), std::forward<Value>(v));
*/
class First : public Operator<First> {
public:
+ First() { }
+
template<class Source,
class Value,
class StorageType = typename std::decay<Value>::type>
StorageType compose(const GenImpl<Value, Source>& source) const {
- static_assert(std::is_same<StorageType, int>::value, "wtf");
Optional<StorageType> accum;
source | [&](Value v) -> bool {
accum = std::forward<Value>(v);
/**
- * Any - For determining whether any values are contained in a sequence.
+ * Any - For determining whether any values in a sequence satisfy a predicate.
*
* This type is primarily used through the 'any' static value, like:
*
* bool any20xPrimes = seq(200, 210) | filter(isPrime) | any;
+ *
+ * Note that it may also be used like so:
+ *
+ * bool any20xPrimes = seq(200, 210) | any(isPrime);
+ *
*/
class Any : public Operator<Any> {
public:
+ Any() { }
+
template<class Source,
class Value>
bool compose(const GenImpl<Value, Source>& source) const {
};
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;
+ }
};
/**
*/
template<class Reducer>
class Reduce : public Operator<Reduce<Reducer>> {
- const Reducer reducer_;
+ Reducer reducer_;
public:
- Reduce(const Reducer& reducer)
- : reducer_(reducer)
+ Reduce() {}
+ explicit Reduce(Reducer reducer)
+ : reducer_(std::move(reducer))
{}
template<class Source,
*/
class Count : public Operator<Count> {
public:
+ Count() { }
+
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;
*/
class Sum : public Operator<Sum> {
public:
+ Sum() : Operator<Sum>() {}
+
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),
[](StorageType&& accum, Value v) {
return std::move(accum) + std::forward<Value>(v);
}
};
+/**
+ * Contains - For testing whether a value matching the given value is contained
+ * in a sequence.
+ *
+ * This type should be used through the 'contains' helper method, like:
+ *
+ * bool contained = seq(1, 10) | map(square) | contains(49);
+ */
+template<class Needle>
+class Contains : public Operator<Contains<Needle>> {
+ Needle needle_;
+ public:
+ explicit Contains(Needle needle)
+ : needle_(std::move(needle))
+ {}
+
+ 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));
+ });
+ }
+};
+
/**
* Min - For a value which minimizes a key, where the key is determined by a
* given selector, and compared by given comparer.
Selector selector_;
Comparer comparer_;
public:
- Min(const Selector& selector = Selector(),
- const Comparer& comparer = Comparer())
- : selector_(selector)
- , comparer_(comparer)
+ 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,
*/
template<class Collection>
class Append : public Operator<Append<Collection>> {
- Collection* const collection_;
+ Collection* collection_;
public:
explicit Append(Collection* collection)
: collection_(collection)
template<class Collection>
class Collect : public Operator<Collect<Collection>> {
public:
+ Collect() { }
+
template<class Value,
class Source,
class StorageType = typename std::decay<Value>::type>
template<class> class Allocator>
class CollectTemplate : public Operator<CollectTemplate<Container, Allocator>> {
public:
+ CollectTemplate() { }
+
template<class Value,
class Source,
class StorageType = typename std::decay<Value>::type,
return collection;
}
};
+
/**
* Concat - For flattening generators of generators.
*
* | as<std::set>();
*/
class Concat : public Operator<Concat> {
-public:
+ public:
+ Concat() { }
+
template<class Inner,
class Source,
class InnerValue = typename std::decay<Inner>::type::ValueType>
class Generator :
public GenImpl<InnerValue, Generator<Inner, Source, InnerValue>> {
- const Source source_;
- public:
+ Source source_;
+ public:
explicit Generator(Source source)
: source_(std::move(source)) {}
inner.foreach(std::forward<Body>(body));
});
}
+
+ static constexpr bool infinite = Source::infinite;
};
template<class Value,
};
/**
- * RangeConcat - For flattening generators of generators.
+ * RangeConcat - For flattening generators of iterables.
*
* This type is usually used through the 'rconcat' static value, like:
*
* | as<std::set>();
*/
class RangeConcat : public Operator<RangeConcat> {
-public:
- template<class Source,
- class Range,
+ public:
+ RangeConcat() { }
+
+ template<class Range,
+ class Source,
class InnerValue = typename ValueTypeOfRange<Range>::RefType>
class Generator
- : public GenImpl<InnerValue, Generator<Source, Range, InnerValue>> {
- const Source source_;
+ : public GenImpl<InnerValue, Generator<Range, Source, InnerValue>> {
+ Source source_;
public:
explicit Generator(Source source)
: source_(std::move(source)) {}
template<class Handler>
bool apply(Handler&& handler) const {
- return source_.apply([&](Range range) {
+ return source_.apply([&](Range range) -> bool {
for (auto& value : range) {
if (!handler(value)) {
return false;
template<class Value,
class Source,
- class Gen = Generator<Source, Value>>
+ 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<Source, Value>>
+ class Gen = Generator<Value, Source>>
Gen compose(const GenImpl<Value, Source>& source) const {
return Gen(source.self());
}
};
-} //::detail
/**
- * Gen<T> - For wrapping template types in simple polymorphic wrapper.
+ * 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 usually used through the 'rconcat' static value, like:
+ * This type is used through the `guard` helper, like so:
*
- * map<int, vector<int>> adjacency;
- * auto sinks =
- * from(adjacency)
- * | get<1>()
- * | rconcat()
- * | as<std::set>();
+ * 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;
+ };
+
+ 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_);
+ }
+};
+
+/**
+ * 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;
+ }
+
+ // not actually infinite, since an empty generator will end the cycles.
+ 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()), 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 use like:
+ *
+ * auto tripled = gen | cycle(3);
+ */
+ Cycle operator()(off_t limit) const {
+ return Cycle(limit);
+ }
+};
+
+/**
+ * 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() {}
+
+ 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;
+ });
+ }
+
+ // not actually infinite, since an empty generator will end the cycles.
+ 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());
+ }
+};
+
+} //::detail
+
+/**
+ * VirtualGen<T> - For wrapping template types in simple polymorphic wrapper.
**/
template<class Value>
class VirtualGen : public GenImpl<Value, VirtualGen<Value>> {
template<class Wrapped>
class WrapperImpl : public WrapperBase {
- const Wrapped wrapped_;
+ Wrapped wrapped_;
public:
explicit WrapperImpl(Wrapped wrapped)
: wrapped_(std::move(wrapped)) {
static const 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;
static const detail::Min<Identity, Less> min;
static const detail::Order<Identity> order;
+static const detail::Distinct<Identity> distinct;
+
static const detail::Map<Move> move;
static const detail::Concat concat;
static const detail::RangeConcat rconcat;
+/**
+ * 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;
+
+static const detail::Dereference dereference;
+
inline detail::Take take(size_t count) {
return detail::Take(count);
}
+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);
}
-}} //folly::gen::detail
+inline detail::Batch batch(size_t batchSize) {
+ return detail::Batch(batchSize);
+}
+
+}} //folly::gen
+
+#pragma GCC diagnostic pop