From 2dedc14bc6f68c740555aca71fe747f512e9138e Mon Sep 17 00:00:00 2001 From: Yedidya Feldblum Date: Mon, 29 Aug 2016 14:57:05 -0700 Subject: [PATCH] gen::dereference should perfectly-forward unwrapped values Summary: [Folly] `gen::dereference` should perfectly-forward unwrapped values. The problem comes in when the wrapped value is not actually a pointer, but is actually an rvalue-ref to some other kind of wrapper type with `Inner&& operator*() &&`. In such cases, the compiler emits a type mismatch error that it cannot cast `Inner` to `Inner&&`, with the errors originating in `Dereference::foreach` and `Dereference::apply`. Fixes a couple other missing-forwarding and extra-forwarding bugs. Reviewed By: ddrcoder Differential Revision: D3776617 fbshipit-source-id: 6926fc18244a572846b22d428bd407d37fb20aa1 --- folly/gen/Base-inl.h | 17 +++++++++------ folly/gen/test/BaseTest.cpp | 42 ++++++++++++++++++++++++++++++++++++- 2 files changed, 52 insertions(+), 7 deletions(-) diff --git a/folly/gen/Base-inl.h b/folly/gen/Base-inl.h index 11308b3e..8be8e01d 100644 --- a/folly/gen/Base-inl.h +++ b/folly/gen/Base-inl.h @@ -1567,7 +1567,7 @@ class Dereference : public Operator { void foreach(Body&& body) const { source_.foreach([&](Value value) { if (value) { - return body(*value); + return body(*std::forward(value)); } }); } @@ -1576,7 +1576,7 @@ class Dereference : public Operator { bool apply(Handler&& handler) const { return source_.apply([&](Value value) -> bool { if (value) { - return handler(*value); + return handler(*std::forward(value)); } return true; }); @@ -1627,14 +1627,14 @@ class Indirect : public Operator { template void foreach(Body&& body) const { source_.foreach([&](Value value) { - return body(&value); + return body(&std::forward(value)); }); } template bool apply(Handler&& handler) const { return source_.apply([&](Value value) -> bool { - return handler(&value); + return handler(&std::forward(value)); }); } @@ -1964,6 +1964,11 @@ class Min : public Operator> { Selector selector_; Comparer comparer_; + template + const T& asConst(const T& t) const { + return t; + } + public: Min() = default; @@ -1984,9 +1989,9 @@ class Min : public Operator> { Optional min; Optional minKey; source | [&](Value v) { - Key key = selector_(std::forward(v)); + Key key = selector_(asConst(v)); // so that selector_ cannot mutate v if (!minKey.hasValue() || comparer_(key, minKey.value())) { - minKey = key; + minKey = std::move(key); min = std::forward(v); } }; diff --git a/folly/gen/test/BaseTest.cpp b/folly/gen/test/BaseTest.cpp index fa5bcecb..66b987e4 100644 --- a/folly/gen/test/BaseTest.cpp +++ b/folly/gen/test/BaseTest.cpp @@ -24,9 +24,10 @@ #include #include #include +#include #include -#include #include +#include using namespace folly::gen; using namespace folly; @@ -1113,6 +1114,45 @@ TEST(Gen, Dereference) { } } +namespace { +struct DereferenceWrapper { + string data; + string& operator*() & { + return data; + } + string&& operator*() && { + return std::move(data); + } + explicit operator bool() { + return true; + } +}; +bool operator==(const DereferenceWrapper& a, const DereferenceWrapper& b) { + return a.data == b.data; +} +void PrintTo(const DereferenceWrapper& a, std::ostream* o) { + *o << "Wrapper{\"" << cEscape(a.data) << "\"}"; +} +} + +TEST(Gen, DereferenceWithLValueRef) { + auto original = vector{{"foo"}, {"bar"}}; + auto copy = original; + auto expected = vector{"foo", "bar"}; + auto actual = from(original) | dereference | as(); + EXPECT_EQ(expected, actual); + EXPECT_EQ(copy, original); +} + +TEST(Gen, DereferenceWithRValueRef) { + auto original = vector{{"foo"}, {"bar"}}; + auto empty = vector{{}, {}}; + auto expected = vector{"foo", "bar"}; + auto actual = from(original) | move | dereference | as(); + EXPECT_EQ(expected, actual); + EXPECT_EQ(empty, original); +} + TEST(Gen, Indirect) { vector vs{1}; EXPECT_EQ(&vs[0], from(vs) | indirect | first | unwrap); -- 2.34.1