Commit 45f4b89d authored by Marshall Cline's avatar Marshall Cline Committed by Facebook Github Bot

pipe fittings between a container/range-v3 and a folly::gen

Summary:
Create pipe-adapters ("pipe fittings") that allow a `|`-based pipeline mixing {std-containers and/or range-v3} with folly::gen, e.g.,

```
auto result = myVec              // anything consistent with range-v3 or these adapters
    | ranges::view::filter(...)  // zero-or-more range-v3 pipe-elems
    | <adapter-goes-here>        // <==**one of the pipe-adapters provided by this task**
    | folly::gen::blah();        // trailing pipe-elems are folly::gen
```

This diff supplies only adapters that transition from {std-containers and/or range-v3} to folly::gen, but not the other way around.

Q&A:

* Why distinguish containers from range-v3's? E.g., container_to_gen vs. rangev3_to_gen?
    * Containers and range-v3's have different copy-costs: range-v3's have O(1) copy
    * Using two different names lets us separate the implementations so we can know which can be copied without significant overhead.
* Why `#include` range-v3 from inside folly::gen? Why the dependency?
    * That `#include` / dependency adds value to the process, and it is included only for the client-files that need it.
    * It adds value to the process since the subset of client-files to be migrated are precisely the same subset of client-files that _need_ to include range-v3.
    * The alternative would be to add the `#include` out in all the various client-files during the migration process, and we will do that as the last step in the migration process.
    * The migration process is sped up by including range-v3 for that specific subset of client-files _and_ it hurts no one since only those who need that `#include` actually get it.
    * Note: we limit the `#include` to the subset of files to be migrated via the `FOLLY_USE_RANGEV3` define; see folly-config.h.

Reviewed By: yfeldblum

Differential Revision: D15035294

fbshipit-source-id: 694979850d1f35dd382e3afde792ea51a2397af0
parent c3ac4113
......@@ -23,6 +23,11 @@
#include <folly/container/F14Set.h>
#include <folly/functional/Invoke.h>
#if FOLLY_USE_RANGEV3
#include <range/v3/view/filter.hpp>
#include <range/v3/view/transform.hpp>
#endif
// Ignore shadowing warnings within this file, so includers can use -Wshadow.
FOLLY_PUSH_WARNING
FOLLY_GNU_DISABLE_WARNING("-Wshadow")
......@@ -2480,8 +2485,118 @@ const T& operator|(const Optional<T>& opt, const Unwrap&) {
return opt.value();
}
#if FOLLY_USE_RANGEV3
template <class RangeV3, class Value>
class RangeV3Source
: public gen::GenImpl<Value, RangeV3Source<RangeV3, Value>> {
mutable RangeV3 r_; // mutable since some ranges are not const-iteratable
public:
explicit RangeV3Source(RangeV3 const& r) : r_(r) {}
template <class Body>
void foreach(Body&& body) const {
for (auto const& value : r_) {
body(value);
}
}
template <class Handler>
bool apply(Handler&& handler) const {
for (auto const& value : r_) {
if (!handler(value)) {
return false;
}
}
return true;
}
static constexpr bool infinite = false;
};
template <class RangeV3, class Value>
class RangeV3CopySource
: public gen::GenImpl<Value, RangeV3CopySource<RangeV3, Value>> {
mutable RangeV3 r_; // mutable since some ranges are not const-iteratable
public:
explicit RangeV3CopySource(RangeV3&& r) : r_(std::move(r)) {}
template <class Body>
void foreach(Body&& body) const {
for (auto const& value : r_) {
body(value);
}
}
template <class Handler>
bool apply(Handler&& handler) const {
for (auto const& value : r_) {
if (!handler(value)) {
return false;
}
}
return true;
}
static constexpr bool infinite = false;
};
struct container_to_gen_fn {};
struct rangev3_to_gen_fn {};
struct rangev3_to_gen_copy_fn {};
struct rangev3_will_be_consumed_fn {};
#endif // FOLLY_USE_RANGEV3
} // namespace detail
#if FOLLY_USE_RANGEV3
/*
******************************************************************************
* Pipe fittings between a container/range-v3 and a folly::gen.
* Example: vec | container_to_gen | folly::gen::filter(...);
* Example: vec | ranges::view::filter(...) | rangev3_to_gen | folly::gen::xxx;
******************************************************************************
*/
constexpr detail::container_to_gen_fn from_container;
constexpr detail::rangev3_to_gen_fn from_rangev3;
constexpr detail::rangev3_to_gen_copy_fn from_rangev3_copy;
template <typename Container>
auto operator|(Container&& c, detail::container_to_gen_fn) {
return gen::from(std::forward<Container>(c));
}
template <typename Range>
auto operator|(Range&& r, detail::rangev3_to_gen_fn) {
using DecayedRange = std::decay_t<Range>;
using DecayedValue = std::decay_t<decltype(*r.begin())>;
return detail::RangeV3Source<DecayedRange, DecayedValue>(r);
}
template <typename Range>
auto operator|(Range&& r, detail::rangev3_to_gen_copy_fn) {
using RangeDecay = std::decay_t<Range>;
using Value = std::decay_t<decltype(*r.begin())>;
return detail::RangeV3CopySource<RangeDecay, Value>(std::move(r));
}
template <typename Range>
auto rangev3_to_gen_call(Range&& r) {
using Value = std::decay_t<decltype(*r.begin())>;
return detail::RangeV3Source<Range, Value>(r);
}
// it is safe to pipe an rvalue into a range-v3 view if the rest of the pipeline
// will finish its traversal within the current full-expr, a condition provided
// by folly::gen.
template <typename Range>
auto rangev3_will_be_consumed(Range&& r) {
// intentionally use `r` instead of `std::forward<Range>(r)`; see above.
// range-v3 ranges copy in O(1) so it is appropriate.
return ranges::view::all(r);
}
#endif // FOLLY_USE_RANGEV3
/**
* VirtualGen<T> - For wrapping template types in simple polymorphic wrapper.
**/
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment