Commit 41b7e6ba authored by Tom Jackson's avatar Tom Jackson Committed by Facebook GitHub Bot

More type erasure support

Summary:
* `VirtualGen` gains a move-only counterpart, `VirtualGenMoveOnly`
* `virtualize` added as an operator to type-erase generator names, particularly for making errors and stack traces more legible.
* `FunctionRef` used to avoid heap allocations where short-lived references are sufficient.

Differential Revision: D28167691

fbshipit-source-id: 5beaab3f613666ffafc7ac1951be1933c2863d3f
parent 2b4649b4
...@@ -18,6 +18,7 @@ ...@@ -18,6 +18,7 @@
#error This file may only be included from folly/gen/Base.h #error This file may only be included from folly/gen/Base.h
#endif #endif
#include <folly/Function.h>
#include <folly/Portability.h> #include <folly/Portability.h>
#include <folly/container/F14Map.h> #include <folly/container/F14Map.h>
#include <folly/container/F14Set.h> #include <folly/container/F14Set.h>
...@@ -521,15 +522,13 @@ class Map : public Operator<Map<Predicate>> { ...@@ -521,15 +522,13 @@ class Map : public Operator<Map<Predicate>> {
static constexpr bool infinite = Source::infinite; static constexpr bool infinite = Source::infinite;
}; };
template <class Source, class Value, class Gen = Generator<Value, Source>> template <
Gen compose(GenImpl<Value, Source>&& source) const { class Source,
class Gen =
Generator<typename Source::ValueType, typename Source::SelfType>>
Gen compose(Source source) const {
return Gen(std::move(source.self()), pred_); 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(), pred_);
}
}; };
/** /**
...@@ -2451,6 +2450,16 @@ const T& operator|(const Optional<T>& opt, const Unwrap&) { ...@@ -2451,6 +2450,16 @@ const T& operator|(const Optional<T>& opt, const Unwrap&) {
return opt.value(); return opt.value();
} }
class ToVirtualGen : public Operator<ToVirtualGen> {
public:
template <
class Source,
class Generator = VirtualGenMoveOnly<typename Source::ValueType>>
Generator compose(Source source) const {
return Generator(std::move(source.self()));
}
};
#if FOLLY_USE_RANGEV3 #if FOLLY_USE_RANGEV3
template <class RangeV3, class Value> template <class RangeV3, class Value>
class RangeV3Source class RangeV3Source
...@@ -2567,64 +2576,104 @@ auto rangev3_will_be_consumed(Range&& r) { ...@@ -2567,64 +2576,104 @@ auto rangev3_will_be_consumed(Range&& r) {
/** /**
* VirtualGen<T> - For wrapping template types in simple polymorphic wrapper. * VirtualGen<T> - For wrapping template types in simple polymorphic wrapper.
**/ **/
template <class Value> template <class Value, class Self>
class VirtualGen : public GenImpl<Value, VirtualGen<Value>> { class VirtualGenBase : public GenImpl<Value, Self> {
protected:
class WrapperBase { class WrapperBase {
public: public:
virtual ~WrapperBase() noexcept {} virtual ~WrapperBase() noexcept {}
virtual bool apply(const std::function<bool(Value)>& handler) const = 0; virtual bool apply(const FunctionRef<bool(Value)>& handler) const = 0;
virtual void foreach(const std::function<void(Value)>& body) const = 0; virtual void foreach(const FunctionRef<void(Value)>& body) const = 0;
virtual std::unique_ptr<const WrapperBase> clone() const = 0; virtual std::unique_ptr<const WrapperBase> clone() const {
// clone() is only ever called from VirtualGen<>, where it
// is overridden with a real implementation.
return nullptr;
}
}; };
template <class Wrapped> template <class Wrapped>
class WrapperImpl : public WrapperBase { class WrapperImpl : public WrapperBase {
protected:
Wrapped wrapped_; Wrapped wrapped_;
public: public:
explicit WrapperImpl(Wrapped wrapped) : wrapped_(std::move(wrapped)) {} explicit WrapperImpl(Wrapped wrapped) : wrapped_(std::move(wrapped)) {}
bool apply(const std::function<bool(Value)>& handler) const override { bool apply(const FunctionRef<bool(Value)>& handler) const final {
return wrapped_.apply(handler); return wrapped_.apply(handler);
} }
void foreach(const std::function<void(Value)>& body) const override { void foreach(const FunctionRef<void(Value)>& body) const final {
wrapped_.foreach(body); wrapped_.foreach(body);
} }
std::unique_ptr<const WrapperBase> clone() const override {
return std::unique_ptr<const WrapperBase>(new WrapperImpl(wrapped_));
}
}; };
std::unique_ptr<const WrapperBase> wrapper_; std::unique_ptr<const WrapperBase> wrapper_;
VirtualGenBase() = default;
VirtualGenBase(VirtualGenBase&&) = default;
VirtualGenBase& operator=(VirtualGenBase&&) = default;
public:
bool apply(const FunctionRef<bool(Value)>& handler) const {
return wrapper_->apply(handler);
}
void foreach(const FunctionRef<void(Value)>& body) const {
wrapper_->foreach(body);
}
};
template <class Value>
class VirtualGen : public VirtualGenBase<Value, VirtualGen<Value>> {
using Base = VirtualGenBase<Value, VirtualGen>;
using WrapperBase = typename Base::WrapperBase;
template <class Wrapped>
using WrapperImpl = typename Base::template WrapperImpl<Wrapped>;
template <class Wrapped>
class CloneableWrapperImpl : public WrapperImpl<Wrapped> {
public: public:
template <class Self> using WrapperImpl<Wrapped>::WrapperImpl;
/* implicit */ VirtualGen(Self source)
: wrapper_(new WrapperImpl<Self>(std::move(source))) {}
VirtualGen(VirtualGen&& source) noexcept std::unique_ptr<const WrapperBase> clone() const final {
: wrapper_(std::move(source.wrapper_)) {} return std::make_unique<CloneableWrapperImpl<Wrapped>>(this->wrapped_);
}
};
VirtualGen(const VirtualGen& source) : wrapper_(source.wrapper_->clone()) {} public:
VirtualGen() = default;
VirtualGen(VirtualGen&& source) = default;
VirtualGen& operator=(VirtualGen&& source) = default;
VirtualGen& operator=(const VirtualGen& source) { template <class Source>
wrapper_.reset(source.wrapper_->clone()); /* implicit */ VirtualGen(Source source) {
return *this; this->wrapper_ =
std::make_unique<CloneableWrapperImpl<Source>>(std::move(source));
} }
VirtualGen& operator=(VirtualGen&& source) noexcept { VirtualGen(const VirtualGen& source) {
wrapper_ = std::move(source.wrapper_); this->wrapper_ = source.wrapper_->clone();
return *this;
} }
bool apply(const std::function<bool(Value)>& handler) const { VirtualGen& operator=(const VirtualGen& source) {
return wrapper_->apply(handler); return *this = VirtualGen(source);
} }
};
void foreach(const std::function<void(Value)>& body) const { template <class Value>
wrapper_->foreach(body); class VirtualGenMoveOnly
: public VirtualGenBase<Value, VirtualGenMoveOnly<Value>> {
public:
VirtualGenMoveOnly() = default;
VirtualGenMoveOnly(VirtualGenMoveOnly&& other) = default;
VirtualGenMoveOnly& operator=(VirtualGenMoveOnly&&) = default;
template <class Source>
/* implicit */ VirtualGenMoveOnly(Source source) {
this->wrapper_ = std::make_unique<
typename VirtualGenBase<Value, VirtualGenMoveOnly<Value>>::
template WrapperImpl<Source>>(std::move(source));
} }
}; };
...@@ -2664,6 +2713,8 @@ constexpr detail::Indirect indirect{}; ...@@ -2664,6 +2713,8 @@ constexpr detail::Indirect indirect{};
constexpr detail::Unwrap unwrap{}; constexpr detail::Unwrap unwrap{};
constexpr detail::ToVirtualGen virtualize{};
template <class Number> template <class Number>
inline detail::Take take(Number count) { inline detail::Take take(Number count) {
if (count < 0) { if (count < 0) {
......
...@@ -401,6 +401,9 @@ class Unwrap; ...@@ -401,6 +401,9 @@ class Unwrap;
template <class Value> template <class Value>
class VirtualGen; class VirtualGen;
template <class Value>
class VirtualGenMoveOnly;
/* /*
* Source Factories * Source Factories
*/ */
......
...@@ -68,6 +68,7 @@ class IsCompatibleSignature<Candidate, ExpectedReturn(ArgTypes...)> { ...@@ -68,6 +68,7 @@ class IsCompatibleSignature<Candidate, ExpectedReturn(ArgTypes...)> {
*/ */
template <class Self> template <class Self>
struct FBounded { struct FBounded {
using SelfType = Self;
const Self& self() const { return *static_cast<const Self*>(this); } const Self& self() const { return *static_cast<const Self*>(this); }
Self& self() { return *static_cast<Self*>(this); } Self& self() { return *static_cast<Self*>(this); }
......
...@@ -880,6 +880,14 @@ TEST(Gen, VirtualGen) { ...@@ -880,6 +880,14 @@ TEST(Gen, VirtualGen) {
EXPECT_EQ(30, v | take(4) | sum); EXPECT_EQ(30, v | take(4) | sum);
} }
TEST(Gen, VirtualGenMoveOnly) {
VirtualGenMoveOnly<int> v(seq(1, 10));
EXPECT_EQ(55, std::move(v) | sum);
v = seq(1, 10) | virtualize;
v = std::move(v) | map(square);
EXPECT_EQ(385, std::move(v) | sum);
}
TEST(Gen, CustomType) { TEST(Gen, CustomType) {
struct Foo { struct Foo {
int y; int y;
......
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