Commit 2dedc14b authored by Yedidya Feldblum's avatar Yedidya Feldblum Committed by Facebook Github Bot 2

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
parent 214d5f7c
...@@ -1567,7 +1567,7 @@ class Dereference : public Operator<Dereference> { ...@@ -1567,7 +1567,7 @@ class Dereference : public Operator<Dereference> {
void foreach(Body&& body) const { void foreach(Body&& body) const {
source_.foreach([&](Value value) { source_.foreach([&](Value value) {
if (value) { if (value) {
return body(*value); return body(*std::forward<Value>(value));
} }
}); });
} }
...@@ -1576,7 +1576,7 @@ class Dereference : public Operator<Dereference> { ...@@ -1576,7 +1576,7 @@ class Dereference : public Operator<Dereference> {
bool apply(Handler&& handler) const { bool apply(Handler&& handler) const {
return source_.apply([&](Value value) -> bool { return source_.apply([&](Value value) -> bool {
if (value) { if (value) {
return handler(*value); return handler(*std::forward<Value>(value));
} }
return true; return true;
}); });
...@@ -1627,14 +1627,14 @@ class Indirect : public Operator<Indirect> { ...@@ -1627,14 +1627,14 @@ class Indirect : public Operator<Indirect> {
template <class Body> template <class Body>
void foreach(Body&& body) const { void foreach(Body&& body) const {
source_.foreach([&](Value value) { source_.foreach([&](Value value) {
return body(&value); return body(&std::forward<Value>(value));
}); });
} }
template <class Handler> template <class Handler>
bool apply(Handler&& handler) const { bool apply(Handler&& handler) const {
return source_.apply([&](Value value) -> bool { return source_.apply([&](Value value) -> bool {
return handler(&value); return handler(&std::forward<Value>(value));
}); });
} }
...@@ -1964,6 +1964,11 @@ class Min : public Operator<Min<Selector, Comparer>> { ...@@ -1964,6 +1964,11 @@ class Min : public Operator<Min<Selector, Comparer>> {
Selector selector_; Selector selector_;
Comparer comparer_; Comparer comparer_;
template <typename T>
const T& asConst(const T& t) const {
return t;
}
public: public:
Min() = default; Min() = default;
...@@ -1984,9 +1989,9 @@ class Min : public Operator<Min<Selector, Comparer>> { ...@@ -1984,9 +1989,9 @@ class Min : public Operator<Min<Selector, Comparer>> {
Optional<StorageType> min; Optional<StorageType> min;
Optional<Key> minKey; Optional<Key> minKey;
source | [&](Value v) { source | [&](Value v) {
Key key = selector_(std::forward<Value>(v)); Key key = selector_(asConst(v)); // so that selector_ cannot mutate v
if (!minKey.hasValue() || comparer_(key, minKey.value())) { if (!minKey.hasValue() || comparer_(key, minKey.value())) {
minKey = key; minKey = std::move(key);
min = std::forward<Value>(v); min = std::forward<Value>(v);
} }
}; };
......
...@@ -24,9 +24,10 @@ ...@@ -24,9 +24,10 @@
#include <folly/FBVector.h> #include <folly/FBVector.h>
#include <folly/MapUtil.h> #include <folly/MapUtil.h>
#include <folly/Memory.h> #include <folly/Memory.h>
#include <folly/String.h>
#include <folly/dynamic.h> #include <folly/dynamic.h>
#include <folly/gen/Base.h>
#include <folly/experimental/TestUtil.h> #include <folly/experimental/TestUtil.h>
#include <folly/gen/Base.h>
using namespace folly::gen; using namespace folly::gen;
using namespace folly; using namespace folly;
...@@ -1113,6 +1114,45 @@ TEST(Gen, Dereference) { ...@@ -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<string>(a.data) << "\"}";
}
}
TEST(Gen, DereferenceWithLValueRef) {
auto original = vector<DereferenceWrapper>{{"foo"}, {"bar"}};
auto copy = original;
auto expected = vector<string>{"foo", "bar"};
auto actual = from(original) | dereference | as<vector>();
EXPECT_EQ(expected, actual);
EXPECT_EQ(copy, original);
}
TEST(Gen, DereferenceWithRValueRef) {
auto original = vector<DereferenceWrapper>{{"foo"}, {"bar"}};
auto empty = vector<DereferenceWrapper>{{}, {}};
auto expected = vector<string>{"foo", "bar"};
auto actual = from(original) | move | dereference | as<vector>();
EXPECT_EQ(expected, actual);
EXPECT_EQ(empty, original);
}
TEST(Gen, Indirect) { TEST(Gen, Indirect) {
vector<int> vs{1}; vector<int> vs{1};
EXPECT_EQ(&vs[0], from(vs) | indirect | first | unwrap); EXPECT_EQ(&vs[0], from(vs) | indirect | first | unwrap);
......
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