* 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 the handler returns false. Returns false if and only if the
- * handler 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.
+ * 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 Value>
+class Empty : public GenImpl<Value, Empty<Value>> {
+ public:
+ template<class Handler>
+ bool apply(Handler&&) const { return true; }
+};
/*
* Operators
{}
template<class Value,
- class Source,
- class Result = typename std::result_of<Predicate(Value)>::type>
- class Generator :
- public GenImpl<Result, Generator<Value, Source, Result>> {
+ class Source>
+ class Generator : public GenImpl<Value, Generator<Value, Source>> {
Source source_;
Predicate pred_;
public:
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;
}
};
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;
}
};
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 {
}
};
+/*
+ * 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.
}
};
+/**
+ * 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.
public:
RangeConcat() { }
- template<class Source,
- class Range,
+ template<class Range,
+ class Source,
class InnerValue = typename ValueTypeOfRange<Range>::RefType>
class Generator
- : public GenImpl<InnerValue, Generator<Source, Range, InnerValue>> {
+ : public GenImpl<InnerValue, Generator<Range, Source, InnerValue>> {
Source source_;
public:
explicit Generator(Source source)
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());
+ }
+};
+
+
+/**
+ * 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;
+ };
+
+ 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());
}
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::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);
}
return detail::Skip(count);
}
-}} //folly::gen::detail
+inline detail::Batch batch(size_t batchSize) {
+ return detail::Batch(batchSize);
+}
+
+}} //folly::gen
+
+#pragma GCC diagnostic pop