--- /dev/null
+/*
+ * Copyright 2012 Facebook, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+namespace folly { namespace gen {
+
+/**
+ * IsCompatibleSignature - Trait type for testing whether a given Functor
+ * matches an expected signature.
+ *
+ * Usage:
+ * IsCompatibleSignature<FunctorType, bool(int, float)>::value
+ */
+template<class Candidate, class Expected>
+class IsCompatibleSignature {
+ static constexpr bool value = false;
+};
+
+template<class Candidate,
+ class ExpectedReturn,
+ class... ArgTypes>
+class IsCompatibleSignature<Candidate, ExpectedReturn(ArgTypes...)> {
+ template<class F,
+ class ActualReturn =
+ decltype(std::declval<F>()(std::declval<ArgTypes>()...)),
+ bool good = std::is_same<ExpectedReturn, ActualReturn>::value>
+ static constexpr bool testArgs(int* p) {
+ return good;
+ }
+
+ template<class F>
+ static constexpr bool testArgs(...) {
+ return false;
+ }
+public:
+ static constexpr bool value = testArgs<Candidate>(nullptr);
+};
+
+/**
+ * ArgumentReference - For determining ideal argument type to receive a value.
+ */
+template<class T>
+struct ArgumentReference :
+ public std::conditional<std::is_reference<T>::value,
+ T, // T& -> T&, T&& -> T&&, const T& -> const T&
+ typename std::conditional<
+ std::is_const<T>::value,
+ T&, // const int -> const int&
+ T&& // int -> int&&
+ >::type> {};
+
+/**
+ * FBounded - Helper type for the curiously recurring template pattern, used
+ * heavily here to enable inlining and obviate virtual functions
+ */
+template<class Self>
+struct FBounded {
+ const Self& self() const {
+ return *static_cast<const Self*>(this);
+ }
+
+ Self& self() {
+ return *static_cast<Self*>(this);
+ }
+};
+
+/**
+ * Operator - Core abstraction of an operation which may be applied to a
+ * generator. All operators implement a method compose(), which takes a
+ * generator and produces an output generator.
+ */
+template<class Self>
+class Operator : public FBounded<Self> {
+ public:
+ /**
+ * compose() - Must be implemented by child class to compose a new Generator
+ * out of a given generator. This function left intentionally unimplemented.
+ */
+ template<class Source,
+ class Value,
+ class ResultGen = void>
+ ResultGen compose(const GenImpl<Value, Source>& source) const;
+ protected:
+ Operator() = default;
+ Operator(const Operator&) = default;
+ Operator(Operator&&) = default;
+};
+
+/**
+ * GenImpl - Core abstraction of a generator, an object which produces values by
+ * passing them to a given handler lambda. All generator implementations must
+ * implement apply(). foreach() may also be implemented to special case the
+ * condition where the entire sequence is consumed.
+ */
+template<class Value,
+ class Self>
+class GenImpl : public FBounded<Self> {
+ protected:
+ // To prevent slicing
+ GenImpl() = default;
+ GenImpl(const GenImpl&) = default;
+ GenImpl(GenImpl&&) = default;
+
+ public:
+ typedef Value ValueType;
+ 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.
+ */
+ template<class Handler>
+ bool apply(Handler&& handler) const;
+
+ /**
+ * foreach() - Send all values produced by this generator to given lambda.
+ */
+ template<class Body>
+ void foreach(Body&& body) const {
+ this->self().apply([&](Value value) {
+ body(std::forward<Value>(value));
+ return true;
+ });
+ }
+
+ template<class Next,
+ class Chain = detail::Chain<Value, Self, Next>,
+ class ValueNext>
+ Chain operator+(const GenImpl<ValueNext, Next>& next) const {
+ static_assert(
+ std::is_same<Value, ValueNext>::value,
+ "Generators may ony be combined if Values are the exact same type.");
+ return Chain(*this, next);
+ }
+};
+
+/**
+ * operator|() which enables foreach-like usage:
+ * gen | [](Value v) -> void {...};
+ */
+template<class Value,
+ class Gen,
+ class Handler>
+typename std::enable_if<
+ IsCompatibleSignature<Handler, void(Value)>::value>::type
+operator|(const GenImpl<Value, Gen>& gen, Handler&& handler) {
+ gen.self().foreach(std::forward<Handler>(handler));
+}
+
+/**
+ * operator|() which enables foreach-like usage with 'break' support:
+ * gen | [](Value v) -> bool { return shouldContinue(); };
+ */
+template<class Value,
+ class Gen,
+ class Handler>
+typename std::enable_if<
+ IsCompatibleSignature<Handler, bool(Value)>::value>::type
+operator|(const GenImpl<Value, Gen>& gen, Handler&& handler) {
+ gen.self().apply(std::forward<Handler>(handler));
+}
+
+/**
+ * operator|() for composing generators with operators, similar to boosts' range
+ * adaptors:
+ * gen | map(square) | sum
+ */
+template<class Value,
+ class Gen,
+ class Op>
+auto operator|(const GenImpl<Value, Gen>& gen, const Operator<Op>& op) ->
+decltype(op.self().compose(gen)) {
+ return op.self().compose(gen);
+}
+
+namespace detail {
+
+/*
+ * ReferencedSource - Generate values from an STL-like container using
+ * iterators from .begin() until .end(). Value type defaults to the type of
+ * *container->begin(). For std::vector<int>, this would be int&. Note that the
+ * value here is a reference, so the values in the vector will be passed by
+ * reference to downstream operators.
+ *
+ * This type is primarily used through the 'from' helper method, like:
+ *
+ * string& longestName = from(names)
+ * | maxBy([](string& s) { return s.size() });
+ */
+template<class Container,
+ class Value>
+class ReferencedSource :
+ public GenImpl<Value, ReferencedSource<Container, Value>> {
+ Container* const container_;
+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>
+ bool apply(Handler&& handler) const {
+ for (auto& value : *container_) {
+ if (!handler(std::forward<Value>(value))) {
+ return false;
+ }
+ }
+ return true;
+ }
+};
+
+/**
+ * CopiedSource - For producing values from eagerly from a sequence of values
+ * whose storage is owned by this class. Useful for preparing a generator for
+ * use after a source collection will no longer be available, or for when the
+ * values are specified literally with an initializer list.
+ *
+ * This type is primarily used through the 'fromCopy' function, like:
+ *
+ * auto sourceCopy = fromCopy(makeAVector());
+ * auto sum = sourceCopy | sum;
+ * auto max = sourceCopy | max;
+ *
+ * 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");
+ 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");
+ const std::shared_ptr<const Container> copy_;
+public:
+ typedef Container ContainerType;
+
+ template<class SourceContainer>
+ explicit CopiedSource(const SourceContainer& container)
+ : copy_(new Container(begin(container), end(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_) {}
+
+ template<class Body>
+ void foreach(Body&& body) const {
+ for (const auto& value : *copy_) {
+ body(value);
+ }
+ }
+
+ 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_) {
+ if (!handler(value)) {
+ return false;
+ }
+ }
+ return true;
+ }
+};
+
+/**
+ * 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.
+ *
+ * This type is primarily used through the 'seq' and 'range' function, like:
+ *
+ * int total = seq(1, 10) | sum;
+ * auto indexes = range(0, 10);
+ */
+template<class Value,
+ bool endless,
+ bool endInclusive>
+class Sequence : public GenImpl<const Value&,
+ Sequence<Value, endless, endInclusive>> {
+ 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(const Value& begin)
+ : bounds_{begin} {
+ static_assert(endless, "Must supply 'end'");
+ }
+
+ explicit Sequence(const Value& begin, const Value& end)
+ : bounds_{begin, end} {}
+
+ 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)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ 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);
+ }
+ }
+};
+
+/**
+ * Chain - For concatenating the values produced by two Generators.
+ *
+ * This type is primarily used through using '+' to combine generators, like:
+ *
+ * auto nums = seq(1, 10) + seq(20, 30);
+ * int total = nums | sum;
+ */
+template<class Value, class First, class Second>
+class Chain : public GenImpl<Value,
+ Chain<Value, First, Second>> {
+ const First first_;
+ const Second second_;
+public:
+ explicit Chain(const GenImpl<Value, First>& first,
+ const GenImpl<Value, Second>& second)
+ : first_(first.self())
+ , second_(second.self()) {}
+
+ template<class Handler>
+ bool apply(Handler&& handler) const {
+ return first_.apply(std::forward<Handler>(handler))
+ && second_.apply(std::forward<Handler>(handler));
+ }
+
+ template<class Body>
+ void foreach(Body&& body) const {
+ first_.foreach(std::forward<Body>(body));
+ second_.foreach(std::forward<Body>(body));
+ }
+};
+
+/**
+ * Yield - For producing values from a user-defined generator by way of a
+ * 'yield' function.
+ **/
+template<class Value, class Source>
+class Yield : public GenImpl<Value, Yield<Value, Source>> {
+ const Source source_;
+ public:
+ explicit Yield(const Source& source)
+ : source_(source) {
+ }
+
+ template<class Handler>
+ bool apply(Handler&& handler) const {
+ struct Break {};
+ auto body = [&](Value value) {
+ if (!handler(std::forward<Value>(value))) {
+ throw Break();
+ }
+ };
+ try {
+ source_(body);
+ return true;
+ } catch (Break&) {
+ return false;
+ }
+ }
+
+ template<class Body>
+ void foreach(Body&& body) const {
+ source_(std::forward<Body>(body));
+ }
+};
+
+
+/*
+ * Operators
+ */
+
+/**
+ * Map - For producing a sequence of values by passing each value from a source
+ * collection through a predicate.
+ *
+ * This type is usually used through the 'map' or 'mapped' helper function:
+ *
+ * auto squares = seq(1, 10) | map(square) | asVector;
+ */
+template<class Predicate>
+class Map : public Operator<Map<Predicate>> {
+ const Predicate predicate_;
+ public:
+ explicit Map(const Predicate& predicate = Predicate())
+ : predicate_(predicate)
+ { }
+
+ 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>> {
+ const Source source_;
+ const Predicate pred_;
+ public:
+ explicit Generator(const Source& source, const Predicate& pred)
+ : source_(source), pred_(pred) {}
+
+ template<class Body>
+ void foreach(Body&& body) const {
+ source_.foreach([&](Value value) {
+ body(pred_(std::forward<Value>(value)));
+ });
+ }
+
+ template<class Handler>
+ bool apply(Handler&& handler) const {
+ return source_.apply([&](Value value) {
+ return handler(pred_(std::forward<Value>(value)));
+ });
+ }
+ };
+
+ template<class Source,
+ class Value,
+ class Gen = Generator<Value, Source>>
+ Gen compose(const GenImpl<Value, Source>& source) const {
+ return Gen(source.self(), predicate_);
+ }
+};
+
+
+/**
+ * Filter - For filtering values from a source sequence by a predicate.
+ *
+ * This type is usually used through the 'filter' helper function, like:
+ *
+ * auto nonEmpty = from(strings)
+ * | filter([](const string& str) -> bool {
+ * return !str.empty();
+ * });
+ */
+template<class Predicate>
+class Filter : public Operator<Filter<Predicate>> {
+ const Predicate predicate_;
+ public:
+ explicit Filter(const Predicate& predicate)
+ : predicate_(predicate)
+ { }
+
+ template<class Value,
+ class Source>
+ class Generator : public GenImpl<Value, Generator<Value, Source>> {
+ const Source source_;
+ const Predicate pred_;
+ public:
+ explicit Generator(const Source& source, const Predicate& pred)
+ : source_(source), pred_(pred) {}
+
+ template<class Body>
+ void foreach(Body&& body) const {
+ source_.foreach([&](Value value) {
+ if (pred_(std::forward<Value>(value))) {
+ body(std::forward<Value>(value));
+ }
+ });
+ }
+
+ template<class Handler>
+ bool apply(Handler&& handler) const {
+ return source_.apply([&](Value value) -> bool {
+ if (pred_(std::forward<Value>(value))) {
+ return handler(std::forward<Value>(value));
+ }
+ return true;
+ });
+ }
+ };
+
+ template<class Source,
+ class Value,
+ class Gen = Generator<Value, Source>>
+ Gen compose(const GenImpl<Value, Source>& source) const {
+ return Gen(source.self(), predicate_);
+ }
+};
+
+/**
+ * Until - For producing values from a source until a predicate is satisfied.
+ *
+ * This type is usually used through the 'until' helper function, like:
+ *
+ * auto best = from(sortedItems)
+ * | until([](Item& item) { return item.score > 100; })
+ * | asVector;
+ */
+template<class Predicate>
+class Until : public Operator<Until<Predicate>> {
+ const Predicate predicate_;
+ public:
+ explicit Until(const Predicate& predicate)
+ : predicate_(predicate)
+ { }
+
+ 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:
+ explicit Generator(const Source& source, const Predicate& pred)
+ : source_(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));
+ });
+ }
+ };
+
+ template<class Source,
+ class Value,
+ class Gen = Generator<Value, Source>>
+ Gen compose(const GenImpl<Value, Source>& source) const {
+ return Gen(source.self(), predicate_);
+ }
+};
+
+/**
+ * Take - For producing up to N values from a source.
+ *
+ * This type is usually used through the 'take' helper function, like:
+ *
+ * auto best = from(docs)
+ * | orderByDescending(scoreDoc)
+ * | take(10);
+ */
+class Take : public Operator<Take> {
+ const size_t count_;
+public:
+ explicit Take(size_t count)
+ : count_(count) {}
+
+ template<class Value,
+ class Source>
+ class Generator :
+ public GenImpl<Value, Generator<Value, Source>> {
+ const Source source_;
+ const size_t count_;
+ public:
+ explicit Generator(const Source& source, size_t count)
+ : source_(source) , count_(count) {}
+
+ template<class Handler>
+ 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;
+ });
+ }
+ };
+
+ template<class Source,
+ class Value,
+ class Gen = Generator<Value, Source>>
+ Gen compose(const GenImpl<Value, Source>& source) const {
+ return Gen(source.self(), count_);
+ }
+};
+
+/**
+ * Skip - For skipping N items from the beginning of a source generator.
+ *
+ * This type is usually used through the 'skip' helper function, like:
+ *
+ * auto page = from(results)
+ * | skip(pageSize * startPage)
+ * | take(10);
+ */
+class Skip : public Operator<Skip> {
+ const size_t count_;
+public:
+ explicit Skip(size_t count)
+ : count_(count) {}
+
+ template<class Value,
+ class Source>
+ class Generator :
+ public GenImpl<Value, Generator<Value, Source>> {
+ const Source source_;
+ const size_t count_;
+ public:
+ explicit Generator(const Source& source, size_t count)
+ : source_(source) , count_(count) {}
+
+ template<class Body>
+ void foreach(Body&& body) const {
+ if (count_ == 0) {
+ source_.foreach(body);
+ return;
+ }
+ size_t n = 0;
+ source_.foreach([&](Value value) {
+ if (n < count_) {
+ ++n;
+ } else {
+ body(std::forward<Value>(value));
+ }
+ });
+ }
+
+ template<class Handler>
+ bool apply(Handler&& handler) const {
+ if (count_ == 0) {
+ return source_.apply(handler);
+ }
+ size_t n = 0;
+ return source_.apply([&](Value value) -> bool {
+ if (n < count_) {
+ ++n;
+ return true;
+ }
+ return handler(std::forward<Value>(value));
+ });
+ }
+ };
+
+ template<class Source,
+ class Value,
+ class Gen = Generator<Value, Source>>
+ Gen compose(const GenImpl<Value, Source>& source) const {
+ return Gen(source.self(), count_);
+ }
+};
+
+/**
+ * Order - For ordering a sequence of values from a source by key.
+ * The key is extracted by the given selector functor, and this key is then
+ * compared using the specified comparator.
+ *
+ * This type is usually used through the 'order' helper function, like:
+ *
+ * auto closest = from(places)
+ * | orderBy([](Place& p) {
+ * return -distance(p.location, here);
+ * })
+ * | take(10);
+ */
+template<class Selector, class Comparer>
+class Order : public Operator<Order<Selector, Comparer>> {
+ const Selector selector_;
+ const Comparer comparer_;
+ public:
+ Order(const Selector& selector = Selector(),
+ const Comparer& comparer = Comparer())
+ : selector_(selector) , comparer_(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>> {
+ const Source source_;
+ const Selector selector_;
+ const Comparer comparer_;
+
+ typedef std::vector<StorageType> VectorType;
+
+ VectorType asVector() const {
+ auto comparer = [&](const StorageType& a, const StorageType& b) {
+ return comparer_(selector_(a), selector_(b));
+ };
+ auto vals = source_ | as<VectorType>();
+ std::sort(vals.begin(), vals.end(), comparer);
+ return std::move(vals);
+ }
+ public:
+ Generator(const Source& source,
+ const Selector& selector,
+ const Comparer& comparer)
+ : source_(source) , selector_(selector) , comparer_(comparer) {}
+
+ VectorType operator|(const Collect<VectorType>&) const {
+ return asVector();
+ }
+
+ VectorType operator|(const CollectTemplate<std::vector>&) const {
+ return asVector();
+ }
+
+ template<class Body>
+ void foreach(Body&& body) const {
+ for (auto& value : asVector()) {
+ body(std::move(value));
+ }
+ }
+
+ template<class Handler>
+ bool apply(Handler&& handler) const {
+ auto comparer = [&](const StorageType& a, const StorageType& b) {
+ // swapped for minHeap
+ return comparer_(selector_(b), selector_(a));
+ };
+ auto heap = source_ | as<VectorType>();
+ std::make_heap(heap.begin(), heap.end(), comparer);
+ while (!heap.empty()) {
+ std::pop_heap(heap.begin(), heap.end(), comparer);
+ if (!handler(std::move(heap.back()))) {
+ return false;
+ }
+ heap.pop_back();
+ }
+ return true;
+ }
+ };
+
+ template<class Source,
+ class Value,
+ class Gen = Generator<Value, Source>>
+ Gen compose(const GenImpl<Value, Source>& source) const {
+ return Gen(source.self(), selector_, comparer_);
+ }
+};
+
+/*
+ * Sinks
+ */
+
+/**
+ * FoldLeft - Left-associative functional fold. For producing an aggregate value
+ * from a seed and a folder function. Useful for custom aggregators on a
+ * sequence.
+ *
+ * This type is primarily used through the 'foldl' helper method, like:
+ *
+ * double movingAverage = from(values)
+ * | foldl(0.0, [](double avg, double sample) {
+ * return sample * 0.1 + avg * 0.9;
+ * });
+ */
+template<class Seed,
+ class Fold>
+class FoldLeft : public Operator<FoldLeft<Seed, Fold>> {
+ const Seed seed_;
+ const Fold fold_;
+ public:
+ FoldLeft(const Seed& seed, const Fold& fold)
+ : seed_(seed)
+ , fold_(fold)
+ {}
+
+ template<class Source,
+ class Value>
+ Seed compose(const GenImpl<Value, Source>& source) const {
+ Seed accum = seed_;
+ source | [&](Value v) {
+ accum = fold_(std::move(accum), std::forward<Value>(v));
+ };
+ return accum;
+ }
+};
+
+/**
+ * First - For finding the first value in a sequence.
+ *
+ * This type is primarily used through the 'first' static value, like:
+ *
+ * int firstThreeDigitPrime = seq(100) | filter(isPrime) | first;
+ */
+class First : public Operator<First> {
+ public:
+ 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);
+ return false;
+ };
+ if (!accum.hasValue()) {
+ throw EmptySequence();
+ }
+ return std::move(accum.value());
+ }
+};
+
+
+/**
+ * Any - For determining whether any values are contained in a sequence.
+ *
+ * This type is primarily used through the 'any' static value, like:
+ *
+ * bool any20xPrimes = seq(200, 210) | filter(isPrime) | any;
+ */
+class Any : public Operator<Any> {
+ public:
+ 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;
+ }
+};
+
+/**
+ * Reduce - Functional reduce, for recursively combining values from a source
+ * using a reducer function until there is only one item left. Useful for
+ * combining values when an empty sequence doesn't make sense.
+ *
+ * This type is primarily used through the 'reduce' helper method, like:
+ *
+ * sring longest = from(names)
+ * | reduce([](string&& best, string& current) {
+ * return best.size() >= current.size() ? best : current;
+ * });
+ */
+template<class Reducer>
+class Reduce : public Operator<Reduce<Reducer>> {
+ const Reducer reducer_;
+ public:
+ Reduce(const Reducer& reducer)
+ : reducer_(reducer)
+ {}
+
+ template<class Source,
+ class Value,
+ class StorageType = typename std::decay<Value>::type>
+ StorageType compose(const GenImpl<Value, Source>& source) const {
+ Optional<StorageType> accum;
+ source | [&](Value v) {
+ if (accum.hasValue()) {
+ accum = reducer_(std::move(accum.value()), std::forward<Value>(v));
+ } else {
+ accum = std::forward<Value>(v);
+ }
+ };
+ if (!accum.hasValue()) {
+ throw EmptySequence();
+ }
+ return accum.value();
+ }
+};
+
+/**
+ * Count - for simply counting the items in a collection.
+ *
+ * This type is usually used through its singleton, 'count':
+ *
+ * auto shortPrimes = seq(1, 100) | filter(isPrime) | count;
+ */
+class Count : public Operator<Count> {
+ public:
+ template<class Source,
+ class Value>
+ size_t compose(const GenImpl<Value, Source>& source) const {
+ return foldl(size_t(0),
+ [](size_t accum, Value v) {
+ return accum + 1;
+ }).compose(source);
+ }
+};
+
+/**
+ * Sum - For simply summing up all the values from a source.
+ *
+ * This type is usually used through its singleton, 'sum':
+ *
+ * auto gaussSum = seq(1, 100) | sum;
+ */
+class Sum : public Operator<Sum> {
+ public:
+ template<class Source,
+ class Value,
+ class StorageType = typename std::decay<Value>::type>
+ StorageType compose(const GenImpl<Value, Source>& source) const {
+ return foldl(StorageType(0),
+ [](StorageType&& accum, Value v) {
+ return std::move(accum) + std::forward<Value>(v);
+ }).compose(source);
+ }
+};
+
+/**
+ * Min - For a value which minimizes a key, where the key is determined by a
+ * given selector, and compared by given comparer.
+ *
+ * This type is usually used through the singletone 'min' or through the helper
+ * functions 'minBy' and 'maxBy'.
+ *
+ * auto oldest = from(people)
+ * | minBy([](Person& p) {
+ * return p.dateOfBirth;
+ * });
+ */
+template<class Selector,
+ class Comparer>
+class Min : public Operator<Min<Selector, Comparer>> {
+ Selector selector_;
+ Comparer comparer_;
+ public:
+ Min(const Selector& selector = Selector(),
+ const Comparer& comparer = Comparer())
+ : selector_(selector)
+ , comparer_(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 {
+ 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);
+ }
+ };
+ if (!min.hasValue()) {
+ throw EmptySequence();
+ }
+ return min.value();
+ }
+};
+
+/**
+ * Append - For collecting values from a source into a given output container
+ * by appending.
+ *
+ * This type is usually used through the helper function 'appendTo', like:
+ *
+ * vector<int64_t> ids;
+ * from(results) | map([](Person& p) { return p.id })
+ * | appendTo(ids);
+ */
+template<class Collection>
+class Append : public Operator<Append<Collection>> {
+ Collection* const collection_;
+ public:
+ explicit Append(Collection* collection)
+ : collection_(collection)
+ {}
+
+ template<class Value,
+ class Source>
+ Collection& compose(const GenImpl<Value, Source>& source) const {
+ source | [&](Value v) {
+ collection_->insert(collection_->end(), std::forward<Value>(v));
+ };
+ return *collection_;
+ }
+};
+
+/**
+ * Collect - For collecting values from a source in a collection of the desired
+ * type.
+ *
+ * This type is usually used through the helper function 'as', like:
+ *
+ * std::string upper = from(stringPiece)
+ * | map(&toupper)
+ * | as<std::string>();
+ */
+template<class Collection>
+class Collect : public Operator<Collect<Collection>> {
+ public:
+ template<class Value,
+ class Source,
+ class StorageType = typename std::decay<Value>::type>
+ Collection compose(const GenImpl<Value, Source>& source) const {
+ Collection collection;
+ source | [&](Value v) {
+ collection.insert(collection.end(), std::forward<Value>(v));
+ };
+ return collection;
+ }
+};
+
+
+/**
+ * CollectTemplate - For collecting values from a source in a collection
+ * constructed using the specified template type. Given the type of values
+ * produced by the given generator, the collection type will be:
+ * Container<Value, Allocator<Value>>
+ *
+ * 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.
+ *
+ * Example:
+ *
+ * set<string> uniqueNames = from(names) | as<set>();
+ */
+template<template<class, class> class Container,
+ template<class> class Allocator>
+class CollectTemplate : public Operator<CollectTemplate<Container, Allocator>> {
+ public:
+ 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 {
+ Collection collection;
+ source | [&](Value v) {
+ collection.insert(collection.end(), std::forward<Value>(v));
+ };
+ return collection;
+ }
+};
+/**
+ * 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:
+ 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:
+ explicit Generator(const Source& source)
+ : source_(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));
+ });
+ }
+ };
+
+ 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 generators.
+ *
+ * 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:
+ template<class Source,
+ class Range,
+ class InnerValue = typename ValueTypeOfRange<Range>::RefType>
+ class Generator
+ : public GenImpl<InnerValue, Generator<Source, Range, InnerValue>> {
+ const Source source_;
+ public:
+ Generator(const Source& source)
+ : source_(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) {
+ for (auto& value : range) {
+ if (!handler(value)) {
+ return false;
+ }
+ }
+ return true;
+ });
+ }
+ };
+
+ template<class Value,
+ class Source,
+ class Gen = Generator<Source, Value>>
+ Gen compose(const GenImpl<Value, Source>& source) const {
+ return Gen(source.self());
+ }
+};
+
+} //::detail
+
+/**
+ * Gen<T> - For wrapping template types in simple polymorphic wrapper.
+ *
+ * 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>();
+ **/
+template<class Value>
+class VirtualGen : public GenImpl<Value, VirtualGen<Value>> {
+ class WrapperBase {
+ public:
+ virtual ~WrapperBase() {}
+ 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>
+ class WrapperImpl : public WrapperBase {
+ const Wrapped wrapped_;
+ public:
+ WrapperImpl(const Wrapped& wrapped)
+ : wrapped_(wrapped) {
+ }
+
+ virtual bool apply(const std::function<bool(Value)>& handler) const {
+ return wrapped_.apply(handler);
+ }
+
+ virtual void foreach(const std::function<void(Value)>& body) const {
+ wrapped_.foreach(body);
+ }
+
+ virtual std::unique_ptr<const WrapperBase> clone() const {
+ return std::unique_ptr<const WrapperBase>(new WrapperImpl(wrapped_));
+ }
+ };
+
+ std::unique_ptr<const WrapperBase> wrapper_;
+
+ public:
+ template<class SourceValue,
+ class Self>
+ /* implicit */ VirtualGen(const GenImpl<SourceValue, Self>& source)
+ : wrapper_(new WrapperImpl<Self>(source.self()))
+ { }
+
+ VirtualGen(VirtualGen&& source)
+ : wrapper_(std::move(source.wrapper_))
+ { }
+
+ 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_);
+ return *this;
+ }
+
+ bool apply(const std::function<bool(Value)>& handler) const {
+ return wrapper_->apply(handler);
+ }
+
+ void foreach(const std::function<void(Value)>& body) const {
+ wrapper_->foreach(body);
+ }
+};
+
+/**
+ * non-template operators, statically defined to avoid the need for anything but
+ * the header.
+ */
+static const detail::Sum sum;
+
+static const detail::Count count;
+
+static const detail::First first;
+
+static const detail::Any any;
+
+static const detail::Min<Identity, Less> min;
+
+static const detail::Min<Identity, Greater> max;
+
+static const detail::Order<Identity> order;
+
+static const detail::Map<Move> move;
+
+static const detail::Concat concat;
+
+static const detail::RangeConcat rconcat;
+
+inline detail::Take take(size_t count) {
+ return detail::Take(count);
+}
+
+inline detail::Skip skip(size_t count) {
+ return detail::Skip(count);
+}
+
+}} //folly::gen::detail
--- /dev/null
+/*
+ * Copyright 2012 Facebook, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <functional>
+#include <memory>
+#include <type_traits>
+#include <utility>
+#include <algorithm>
+
+#include "folly/Range.h"
+#include "folly/Optional.h"
+
+/**
+ * Generator-based Sequence Comprehensions in C++, akin to C#'s LINQ
+ * @author Tom Jackson <tjackson@fb.com>
+ *
+ * This library makes it possible to write declarative comprehensions for
+ * processing sequences of values efficiently in C++. The operators should be
+ * familiar to those with experience in functional programming, and the
+ * performance will be virtually identical to the equivalent, boilerplate C++
+ * implementations.
+ *
+ * Generator objects may be created from either an stl-like container (anything
+ * supporting begin() and end()), from sequences of values, or from another
+ * generator (see below). To create a generator that pulls values from a vector,
+ * for example, one could write:
+ *
+ * vector<string> names { "Jack", "Jill", "Sara", "Tom" };
+ * auto gen = from(names);
+ *
+ * Generators are composed by building new generators out of old ones through
+ * the use of operators. These are reminicent of shell pipelines, and afford
+ * similar composition. Lambda functions are used liberally to describe how to
+ * handle individual values:
+ *
+ * auto lengths = gen
+ * | mapped([](const fbstring& name) { return name.size(); });
+ *
+ * Generators are lazy; they don't actually perform any work until they need to.
+ * As an example, the 'lengths' generator (above) won't actually invoke the
+ * provided lambda until values are needed:
+ *
+ * auto lengthVector = lengths | asVector();
+ * auto totalLength = lengths | sum;
+ *
+ * 'auto' is useful in here because the actual types of the generators objects
+ * are usually complicated and implementation-sensitive.
+ *
+ * If a simpler type is desired (for returning, as an example), VirtualGen<T>
+ * may be used to wrap the generator in a polymorphic wrapper:
+ *
+ * VirtualGen<float> powersOfE() {
+ * return seq(1) | mapped(&expf);
+ * }
+ *
+ * To learn more about this library, including the use of infinite generators,
+ * see the examples in the comments, or the docs (coming soon).
+*/
+
+namespace folly { namespace gen {
+
+template<class Value, class Self>
+class GenImpl;
+
+template<class Self>
+class Operator;
+
+class EmptySequence : std::exception {
+public:
+ virtual const char* what() const noexcept {
+ return "This operation cannot be called on an empty sequence";
+ }
+};
+
+class Less {
+public:
+ template<class First,
+ class Second>
+ auto operator()(const First& first, const Second& second) const ->
+ decltype(first < second) {
+ return first < second;
+ }
+};
+
+class Greater {
+public:
+ template<class First,
+ class Second>
+ auto operator()(const First& first, const Second& second) const ->
+ decltype(first > second) {
+ return first > second;
+ }
+};
+
+template<int n>
+class Get {
+public:
+ template<class Value>
+ auto operator()(Value&& value) const ->
+ decltype(std::get<n>(std::forward<Value>(value))) {
+ return std::get<n>(std::forward<Value>(value));
+ }
+};
+
+class Move {
+public:
+ template<class Value>
+ auto operator()(Value&& value) const ->
+ decltype(std::move(std::forward<Value>(value))) {
+ return std::move(std::forward<Value>(value));
+ }
+};
+
+class Identity {
+public:
+ template<class Value>
+ auto operator()(Value&& value) const ->
+ decltype(std::forward<Value>(value)) {
+ return std::forward<Value>(value);
+ }
+};
+
+namespace detail {
+
+template<class Self>
+struct FBounded;
+
+/*
+ * Type Traits
+ */
+template<class Container>
+struct ValueTypeOfRange {
+ private:
+ static Container container_;
+ public:
+ typedef decltype(*std::begin(container_))
+ RefType;
+ typedef typename std::decay<decltype(*std::begin(container_))>::type
+ StorageType;
+};
+
+
+/*
+ * Sources
+ */
+template<class Container,
+ class Value = typename ValueTypeOfRange<Container>::RefType>
+class ReferencedSource;
+
+template<class Value,
+ class Container = std::vector<typename std::decay<Value>::type>>
+class CopiedSource;
+
+template<class Value, bool endless = false, bool endInclusive = false>
+class Sequence;
+
+template<class Value, class First, class Second>
+class Chain;
+
+template<class Value, class Source>
+class Yield;
+
+/*
+ * Operators
+ */
+template<class Predicate>
+class Map;
+
+template<class Predicate>
+class Filter;
+
+template<class Predicate>
+class Until;
+
+class Take;
+
+class Skip;
+
+template<class Selector, class Comparer = Less>
+class Order;
+
+
+/*
+ * Sinks
+ */
+template<class Seed,
+ class Fold>
+class FoldLeft;
+
+template<class Reducer>
+class Reduce;
+
+class Sum;
+
+template<class Selector,
+ class Comparer>
+class Min;
+
+template<class Container>
+class Collect;
+
+template<template<class, class> class Collection = std::vector,
+ template<class> class Allocator = std::allocator>
+class CollectTemplate;
+
+template<class Collection>
+class Append;
+
+}
+
+/**
+ * Polymorphic wrapper
+ **/
+template<class Value>
+class VirtualGen;
+
+/*
+ * Source Factories
+ */
+template<class Container,
+ class From = detail::ReferencedSource<const Container>>
+From fromConst(const Container& source) {
+ return From(&source);
+}
+
+template<class Container,
+ class From = detail::ReferencedSource<Container>>
+From from(Container& source) {
+ return From(&source);
+}
+
+template<class Container,
+ class Value =
+ typename detail::ValueTypeOfRange<Container>::StorageType,
+ class CopyOf = detail::CopiedSource<Value>>
+CopyOf fromCopy(Container&& source) {
+ return CopyOf(std::forward<Container>(source));
+}
+
+template<class Value,
+ class From = detail::CopiedSource<Value>>
+From from(std::initializer_list<Value> source) {
+ return From(source);
+}
+
+template<class Container,
+ class From = detail::CopiedSource<typename Container::value_type,
+ Container>>
+From from(Container&& source) {
+ return From(std::move(source));
+}
+
+template<class Value, class Gen = detail::Sequence<Value, false, false>>
+Gen range(Value begin, Value end) {
+ return Gen(begin, end);
+}
+
+template<class Value,
+ class Gen = detail::Sequence<Value, false, true>>
+Gen seq(Value first, Value last) {
+ return Gen(first, last);
+}
+
+template<class Value,
+ class Gen = detail::Sequence<Value, true>>
+Gen seq(Value begin) {
+ return Gen(begin);
+}
+
+template<class Value,
+ class Source,
+ class Yield = detail::Yield<Value, Source>>
+Yield generator(Source&& source) {
+ return Yield(std::forward<Source>(source));
+}
+
+#define GENERATOR(type, body) \
+ ::folly::gen::generator<type>( \
+ [=](const std::function<void(type)>& yield) \
+ { body })
+
+/*
+ * Operator Factories
+ */
+template<class Predicate,
+ class Map = detail::Map<Predicate>>
+Map mapped(const Predicate& pred = Predicate()) {
+ return Map(pred);
+}
+
+template<class Predicate,
+ class Map = detail::Map<Predicate>>
+Map map(const Predicate& pred = Predicate()) {
+ return Map(pred);
+}
+
+template<class Predicate,
+ class Filter = detail::Filter<Predicate>>
+Filter filter(const Predicate& pred = Predicate()) {
+ return Filter(pred);
+}
+
+template<class Predicate,
+ class Until = detail::Until<Predicate>>
+Until until(const Predicate& pred = Predicate()) {
+ return Until(pred);
+}
+
+template<class Selector,
+ class Comparer = Less,
+ class Order = detail::Order<Selector, Comparer>>
+Order orderBy(const Selector& selector,
+ const Comparer& comparer = Comparer()) {
+ return Order(selector, comparer);
+}
+
+template<class Selector,
+ class Order = detail::Order<Selector, Greater>>
+Order orderByDescending(const Selector& selector) {
+ return Order(selector);
+}
+
+template<int n,
+ class Get = detail::Map<Get<n>>>
+Get get() {
+ return Get();
+}
+
+/*
+ * Sink Factories
+ */
+template<class Seed,
+ class Fold,
+ class FoldLeft = detail::FoldLeft<Seed, Fold>>
+FoldLeft foldl(const Seed& seed, const Fold& fold) {
+ return FoldLeft(seed, fold);
+}
+
+template<class Reducer,
+ class Reduce = detail::Reduce<Reducer>>
+Reduce reduce(const Reducer& reducer) {
+ return Reduce(reducer);
+}
+
+template<class Selector,
+ class Min = detail::Min<Selector, Less>>
+Min minBy(const Selector& selector = Selector()) {
+ return Min(selector);
+}
+
+template<class Selector,
+ class MaxBy = detail::Min<Selector, Greater>>
+MaxBy maxBy(const Selector& selector = Selector()) {
+ return MaxBy(selector);
+}
+
+template<class Collection,
+ class Collect = detail::Collect<Collection>>
+Collect as() {
+ return Collect();
+}
+
+template<template<class, class> class Container = std::vector,
+ template<class> class Allocator = std::allocator,
+ class Collect = detail::CollectTemplate<Container, Allocator>>
+Collect as() {
+ return Collect();
+}
+
+template<class Collection,
+ class Append = detail::Append<Collection>>
+Append appendTo(Collection& collection) {
+ return Append(&collection);
+}
+
+}} // folly::gen
+
+#include "folly/experimental/Gen-inl.h"
--- /dev/null
+/*
+ * Copyright 2012 Facebook, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <glog/logging.h>
+#include <gtest/gtest.h>
+#include <iostream>
+#include <set>
+#include <vector>
+#include "folly/experimental/Gen.h"
+#include "folly/FBVector.h"
+#include "folly/dynamic.h"
+
+using namespace folly::gen;
+using namespace folly;
+using std::ostream;
+using std::pair;
+using std::set;
+using std::unique_ptr;
+using std::vector;
+using std::tuple;
+using std::make_tuple;
+//using std::unordered_map;
+
+#define EXPECT_SAME(A, B) \
+ static_assert(std::is_same<A, B>::value, "Mismatched: " #A ", " #B)
+EXPECT_SAME(int&&, typename ArgumentReference<int>::type);
+EXPECT_SAME(int&, typename ArgumentReference<int&>::type);
+EXPECT_SAME(const int&, typename ArgumentReference<const int&>::type);
+EXPECT_SAME(const int&, typename ArgumentReference<const int>::type);
+
+template<typename T>
+ostream& operator<<(ostream& os, const set<T>& values) {
+ return os << from(values);
+}
+
+template<typename T>
+ostream& operator<<(ostream& os, const vector<T>& values) {
+ os << "[";
+ for (auto& value : values) {
+ if (&value != &values.front()) {
+ os << " ";
+ }
+ os << value;
+ }
+ return os << "]";
+}
+
+auto square = [](int x) { return x * x; };
+auto add = [](int a, int b) { return a + b; };
+auto multiply = [](int a, int b) { return a * b; };
+
+auto product = foldl(1, multiply);
+
+template<typename A, typename B>
+ostream& operator<<(ostream& os, const pair<A, B>& pair) {
+ return os << "(" << pair.first << ", " << pair.second << ")";
+}
+
+TEST(Gen, Count) {
+ auto gen = seq(1, 10);
+ EXPECT_EQ(10, gen | count);
+ EXPECT_EQ(5, gen | take(5) | count);
+}
+
+TEST(Gen, Sum) {
+ auto gen = seq(1, 10);
+ EXPECT_EQ((1 + 10) * 10 / 2, gen | sum);
+ EXPECT_EQ((1 + 5) * 5 / 2, gen | take(5) | sum);
+}
+
+TEST(Gen, Foreach) {
+ auto gen = seq(1, 4);
+ int accum = 0;
+ gen | [&](int x) { accum += x; };
+ EXPECT_EQ(10, accum);
+ int accum2 = 0;
+ gen | take(3) | [&](int x) { accum2 += x; };
+ EXPECT_EQ(6, accum2);
+}
+
+TEST(Gen, Map) {
+ auto expected = vector<int>{4, 9, 16};
+ auto gen = from({2, 3, 4}) | map(square);
+ EXPECT_EQ((vector<int>{4, 9, 16}), gen | as<vector>());
+ EXPECT_EQ((vector<int>{4, 9}), gen | take(2) | as<vector>());
+}
+
+TEST(Gen, Seq) {
+ // cover the fenceposts of the loop unrolling
+ for (int n = 1; n < 100; ++n) {
+ EXPECT_EQ(n, seq(1, n) | count);
+ EXPECT_EQ(n + 1, seq(1) | take(n + 1) | count);
+ }
+}
+
+TEST(Gen, Range) {
+ // cover the fenceposts of the loop unrolling
+ for (int n = 1; n < 100; ++n) {
+ EXPECT_EQ(range(0, n) | count, n);
+ }
+}
+
+TEST(Gen, FromIterators) {
+ vector<int> source {2, 3, 5, 7, 11};
+ auto gen = from(makeRange(source.begin() + 1, source.end() - 1));
+ EXPECT_EQ(3 * 5 * 7, gen | product);
+}
+
+TEST(Gen, FromMap) {
+ auto source = seq(0, 10)
+ | map([](int i) { return std::make_pair(i, i * i); })
+ | as<std::map<int, int>>();
+ auto gen = fromConst(source)
+ | map([&](const std::pair<const int, int>& p) {
+ return p.second - p.first;
+ });
+ EXPECT_EQ(330, gen | sum);
+}
+
+TEST(Gen, Filter) {
+ const auto expected = vector<int>{1, 2, 4, 5, 7, 8};
+ auto actual =
+ seq(1, 9)
+ | filter([](int x) { return x % 3; })
+ | as<vector<int>>();
+ EXPECT_EQ(expected, actual);
+}
+
+TEST(Gen, Take) {
+ auto expected = vector<int>{1, 4, 9, 16};
+ auto actual =
+ seq(1, 1000)
+ | mapped([](int x) { return x * x; })
+ | take(4)
+ | as<vector<int>>();
+ EXPECT_EQ(expected, actual);
+}
+
+TEST(Gen, Skip) {
+ auto gen =
+ seq(1, 1000)
+ | mapped([](int x) { return x * x; })
+ | skip(4)
+ | take(4);
+ EXPECT_EQ((vector<int>{25, 36, 49, 64}), gen | as<vector>());
+}
+
+TEST(Gen, Until) {
+ auto gen =
+ seq(1) //infinite
+ | mapped([](int x) { return x * x; })
+ | until([](int x) { return x >= 1000; });
+ EXPECT_EQ(31, gen | count);
+}
+
+
+TEST(Gen, Chain) {
+ std::vector<int> nums {2, 3, 5, 7};
+ std::map<int, int> mappings { { 3, 9}, {5, 25} };
+ auto gen = from(nums) + (from(mappings) | get<1>());
+ EXPECT_EQ(51, gen | sum);
+ EXPECT_EQ(5, gen | take(2) | sum);
+ EXPECT_EQ(26, gen | take(5) | sum);
+}
+
+TEST(Gen, Concat) {
+ std::vector<std::vector<int>> nums {{2, 3}, {5, 7}};
+ auto gen = from(nums) | rconcat;
+ EXPECT_EQ(17, gen | sum);
+ EXPECT_EQ(10, gen | take(3) | sum);
+}
+
+TEST(Gen, ConcatGen) {
+ auto gen = seq(1, 10)
+ | map([](int i) { return seq(1, i); })
+ | concat;
+ EXPECT_EQ(220, gen | sum);
+ EXPECT_EQ(10, gen | take(6) | sum);
+}
+
+TEST(Gen, ConcatAlt) {
+ std::vector<std::vector<int>> nums {{2, 3}, {5, 7}};
+ auto actual = from(nums)
+ | map([](std::vector<int>& v) { return from(v); })
+ | concat
+ | sum;
+ auto expected = 17;
+ EXPECT_EQ(expected, actual);
+}
+
+TEST(Gen, Order) {
+ auto expected = vector<int>{0, 3, 5, 6, 7, 8, 9};
+ auto actual =
+ from({8, 6, 7, 5, 3, 0, 9})
+ | order
+ | as<vector>();
+ EXPECT_EQ(expected, actual);
+}
+
+TEST(Gen, OrderMoved) {
+ auto expected = vector<int>{0, 9, 25, 36, 49, 64, 81};
+ auto actual =
+ from({8, 6, 7, 5, 3, 0, 9})
+ | move
+ | order
+ | map(square)
+ | as<vector>();
+ EXPECT_EQ(expected, actual);
+}
+
+TEST(Gen, OrderTake) {
+ auto expected = vector<int>{9, 8, 7};
+ auto actual =
+ from({8, 6, 7, 5, 3, 0, 9})
+ | orderByDescending(square)
+ | take(3)
+ | as<vector>();
+ EXPECT_EQ(expected, actual);
+}
+
+TEST(Gen, MinBy) {
+ EXPECT_EQ(7, seq(1, 10)
+ | minBy([](int i) {
+ auto d = i - 6.8;
+ return d * d;
+ }));
+}
+
+TEST(Gen, MaxBy) {
+ auto gen = from({"three", "eleven", "four"});
+
+ EXPECT_EQ("eleven", gen | maxBy(&strlen));
+}
+
+TEST(Gen, Append) {
+ fbstring expected = "facebook";
+ fbstring actual = "face";
+ from(StringPiece("book")) | appendTo(actual);
+ EXPECT_EQ(expected, actual);
+}
+
+TEST(Gen, FromRValue) {
+ {
+ // AFAICT The C++ Standard does not specify what happens to the rvalue
+ // reference of a std::vector when it is used as the 'other' for an rvalue
+ // constructor. Use fbvector because we're sure its size will be zero in
+ // this case.
+ folly::fbvector<int> v({1,2,3,4});
+ auto q1 = from(v);
+ EXPECT_EQ(v.size(), 4); // ensure that the lvalue version was called!
+ auto expected = 1 * 2 * 3 * 4;
+ EXPECT_EQ(expected, q1 | product);
+
+ auto q2 = from(std::move(v));
+ EXPECT_EQ(v.size(), 0); // ensure that rvalue version was called
+ EXPECT_EQ(expected, q2 | product);
+ }
+ {
+ auto expected = 7;
+ auto q = from([] {return vector<int>({3,7,5}); }());
+ EXPECT_EQ(expected, q | max);
+ }
+ {
+ for (auto size: {5, 1024, 16384, 1<<20}) {
+ auto q1 = from(vector<int>(size, 2));
+ auto q2 = from(vector<int>(size, 3));
+ // If the rvalue specialization is broken/gone, then the compiler will
+ // (disgustingly!) just store a *reference* to the temporary object,
+ // which is bad. Try to catch this by allocating two temporary vectors
+ // of the same size, so that they'll probably use the same underlying
+ // buffer if q1's vector is destructed before q2's vector is constructed.
+ EXPECT_EQ(size * 2 + size * 3, (q1 | sum) + (q2 | sum));
+ }
+ }
+ {
+ auto q = from(set<int>{1,2,3,2,1});
+ EXPECT_EQ(q | sum, 6);
+ }
+}
+
+TEST(Gen, OrderBy) {
+ auto expected = vector<int>{5, 6, 4, 7, 3, 8, 2, 9, 1, 10};
+ auto actual =
+ seq(1, 10)
+ | orderBy([](int x) { return (5.1 - x) * (5.1 - x); })
+ | as<vector>();
+ EXPECT_EQ(expected, actual);
+}
+
+TEST(Gen, Foldl) {
+ int expected = 2 * 3 * 4 * 5;
+ auto actual =
+ seq(2, 5)
+ | foldl(1, multiply);
+ EXPECT_EQ(expected, actual);
+}
+
+TEST(Gen, Reduce) {
+ int expected = 2 + 3 + 4 + 5;
+ auto actual = seq(2, 5) | reduce(add);
+ EXPECT_EQ(expected, actual);
+}
+
+TEST(Gen, ReduceBad) {
+ auto gen = seq(1) | take(0);
+ try {
+ EXPECT_TRUE(true);
+ gen | reduce(add);
+ EXPECT_TRUE(false);
+ } catch (...) {
+ }
+}
+
+TEST(Gen, Moves) {
+ std::vector<unique_ptr<int>> ptrs;
+ ptrs.emplace_back(new int(1));
+ EXPECT_NE(ptrs.front().get(), nullptr);
+ auto ptrs2 = from(ptrs) | move | as<vector>();
+ EXPECT_EQ(ptrs.front().get(), nullptr);
+ EXPECT_EQ(**ptrs2.data(), 1);
+}
+
+TEST(Gen, First) {
+ auto gen =
+ seq(0)
+ | filter([](int x) { return x > 3; });
+ EXPECT_EQ(4, gen | first);
+}
+
+TEST(Gen, FromCopy) {
+ vector<int> v {3, 5};
+ auto src = from(v);
+ auto copy = fromCopy(v);
+ EXPECT_EQ(8, src | sum);
+ EXPECT_EQ(8, copy | sum);
+ v[1] = 7;
+ EXPECT_EQ(10, src | sum);
+ EXPECT_EQ(8, copy | sum);
+}
+
+TEST(Gen, Get) {
+ std::map<int, int> pairs {
+ {1, 1},
+ {2, 4},
+ {3, 9},
+ {4, 16},
+ };
+ auto pairSrc = from(pairs);
+ auto keys = pairSrc | get<0>();
+ auto values = pairSrc | get<1>();
+ EXPECT_EQ(10, keys | sum);
+ EXPECT_EQ(30, values | sum);
+ EXPECT_EQ(30, keys | map(square) | sum);
+ pairs[5] = 25;
+ EXPECT_EQ(15, keys | sum);
+ EXPECT_EQ(55, values | sum);
+
+ vector<tuple<int, int, int>> tuples {
+ make_tuple(1, 1, 1),
+ make_tuple(2, 4, 8),
+ make_tuple(3, 9, 27),
+ };
+ EXPECT_EQ(36, from(tuples) | get<2>() | sum);
+}
+
+TEST(Gen, Any) {
+ EXPECT_TRUE(seq(0) | any);
+ EXPECT_TRUE(seq(0, 1) | any);
+ EXPECT_TRUE(from({1}) | any);
+ EXPECT_FALSE(range(0, 0) | any);
+ EXPECT_FALSE(from({1}) | take(0) | any);
+}
+
+TEST(Gen, Yielders) {
+ auto gen = GENERATOR(int, {
+ for (int i = 1; i <= 5; ++i) {
+ yield(i);
+ }
+ yield(7);
+ for (int i = 3; ; ++i) {
+ yield(i * i);
+ }
+ });
+ vector<int> expected {
+ 1, 2, 3, 4, 5, 7, 9, 16, 25
+ };
+ EXPECT_EQ(expected, gen | take(9) | as<vector>());
+}
+
+TEST(Gen, NestedYield) {
+ auto nums = GENERATOR(int, {
+ for (int i = 1; ; ++i) {
+ yield(i);
+ }
+ });
+ auto gen = GENERATOR(int, {
+ nums | take(10) | yield;
+ seq(1, 5) | [&](int i) {
+ yield(i);
+ };
+ });
+ EXPECT_EQ(70, gen | sum);
+}
+
+TEST(Gen, MapYielders) {
+ auto gen = seq(1, 5)
+ | map([](int n) {
+ return GENERATOR(int, {
+ int i;
+ for (i = 1; i < n; ++i)
+ yield(i);
+ for (; i >= 1; --i)
+ yield(i);
+ });
+ })
+ | concat;
+ vector<int> expected {
+ 1,
+ 1, 2, 1,
+ 1, 2, 3, 2, 1,
+ 1, 2, 3, 4, 3, 2, 1,
+ 1, 2, 3, 4, 5, 4, 3, 2, 1,
+ };
+ EXPECT_EQ(expected, gen | as<vector>());
+}
+
+TEST(Gen, VirtualGen) {
+ VirtualGen<int> v(seq(1, 10));
+ EXPECT_EQ(55, v | sum);
+ v = v | map(square);
+ EXPECT_EQ(385, v | sum);
+ v = v | take(5);
+ EXPECT_EQ(55, v | sum);
+ EXPECT_EQ(30, v | take(4) | sum);
+}
+
+
+TEST(Gen, CustomType) {
+ struct Foo{
+ int y;
+ };
+ auto gen = from({Foo{2}, Foo{3}})
+ | map([](const Foo& f) { return f.y; });
+ EXPECT_EQ(5, gen | sum);
+}
+
+TEST(Gen, NoNeedlessCopies) {
+ auto gen = seq(1, 5)
+ | map([](int x) { return unique_ptr<int>(new int(x)); })
+ | map([](unique_ptr<int> p) { return p; })
+ | map([](unique_ptr<int>&& p) { return std::move(p); })
+ | map([](const unique_ptr<int>& p) { return *p; });
+ EXPECT_EQ(15, gen | sum);
+ EXPECT_EQ(6, gen | take(3) | sum);
+}
+
+TEST(Gen, FromArray) {
+ int source[] = {2, 3, 5, 7};
+ auto gen = from(source);
+ EXPECT_EQ(2 * 3 * 5 * 7, gen | product);
+}
+
+TEST(Gen, FromStdArray) {
+ std::array<int,4> source {{2, 3, 5, 7}};
+ auto gen = from(source);
+ EXPECT_EQ(2 * 3 * 5 * 7, gen | product);
+}
+
+TEST(Gen, StringConcat) {
+ auto gen = seq(1, 10)
+ | map([](int n) { return folly::to<fbstring>(n); })
+ | rconcat;
+ EXPECT_EQ("12345678910", gen | as<fbstring>());
+}
+
+struct CopyCounter {
+ static int alive;
+ int copies;
+ int moves;
+
+ CopyCounter() : copies(0), moves(0) {
+ ++alive;
+ }
+
+ CopyCounter(CopyCounter&& source) {
+ *this = std::move(source);
+ ++alive;
+ }
+
+ CopyCounter(const CopyCounter& source) {
+ *this = source;
+ ++alive;
+ }
+
+ ~CopyCounter() {
+ --alive;
+ }
+
+ CopyCounter& operator=(const CopyCounter& source) {
+ this->copies = source.copies + 1;
+ this->moves = source.moves;
+ return *this;
+ }
+
+ CopyCounter& operator=(CopyCounter&& source) {
+ this->copies = source.copies;
+ this->moves = source.moves + 1;
+ return *this;
+ }
+};
+
+int CopyCounter::alive = 0;
+
+TEST(Gen, CopyCount) {
+ vector<CopyCounter> originals;
+ originals.emplace_back();
+ EXPECT_EQ(1, originals.size());
+ EXPECT_EQ(0, originals.back().copies);
+
+ vector<CopyCounter> copies = from(originals) | as<vector>();
+ EXPECT_EQ(1, copies.back().copies);
+ EXPECT_EQ(0, copies.back().moves);
+
+ vector<CopyCounter> moves = from(originals) | move | as<vector>();
+ EXPECT_EQ(0, moves.back().copies);
+ EXPECT_EQ(1, moves.back().moves);
+}
+
+// test dynamics with various layers of nested arrays.
+TEST(Gen, Dynamic) {
+ dynamic array1 = {1, 2};
+ EXPECT_EQ(dynamic(3), from(array1) | sum);
+ dynamic array2 = {{1}, {1, 2}};
+ EXPECT_EQ(dynamic(4), from(array2) | rconcat | sum);
+ dynamic array3 = {{{1}}, {{1}, {1, 2}}};
+ EXPECT_EQ(dynamic(5), from(array3) | rconcat | rconcat | sum);
+}
+
+int main(int argc, char *argv[]) {
+ testing::InitGoogleTest(&argc, argv);
+ google::ParseCommandLineFlags(&argc, &argv, true);
+ return RUN_ALL_TESTS();
+}