126 lines
4.3 KiB
C++
126 lines
4.3 KiB
C++
/*
|
||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||
*
|
||
* This source code is licensed under the MIT license found in the
|
||
* LICENSE file in the root directory of this source tree.
|
||
*/
|
||
|
||
#pragma once
|
||
|
||
#include <memory>
|
||
|
||
namespace facebook::react {
|
||
|
||
/*
|
||
NOTE: we keep this internal copy of folly/MoveWrapper.h to unblock
|
||
the the workstream of dropping the dependency on folly in RN!
|
||
|
||
For a technical explanation on why we still need this we defer
|
||
to the doc in folly/Function.h:
|
||
|
||
"There are some limitations in std::function that folly::Function tries to
|
||
avoid. std::function is copy-constructible and requires that the callable that
|
||
it wraps is copy-constructible as well, which is a constraint that is often
|
||
inconvenient. In most cases when using a std::function you don't make use of
|
||
its copy-constructibility, so you might sometimes feel like you get back very
|
||
little in return for a noticeable restriction. This restriction becomes
|
||
apparent when trying to use a lambda capturing a unique_ptr (or any
|
||
non-copyable type) as a callback for a folly::Future.
|
||
|
||
std::unique_ptr<Foo> foo_ptr = new Foo;
|
||
|
||
some_future.then(
|
||
[foo_ptr = std::move(foo_ptr)] mutable
|
||
(int x)
|
||
{ foo_ptr->setX(x); }
|
||
);
|
||
|
||
This piece of code did not compile before folly::Future started using
|
||
folly::Function instead of std::function to store the callback. Because the
|
||
lambda captures something non-copyable (the unique_ptr), it is not copyable
|
||
itself. And std::function can only store copyable callables.
|
||
|
||
The implementation of folly::Future did not make use of the
|
||
copy-constructibility of std::function at any point. There was no benefit from
|
||
the fact that the std::function is copy-constructible, but the fact that it can
|
||
only wrap copy-constructible callables posed a restriction.
|
||
|
||
A workaround was available: folly::MoveWrapper, which wraps an object that may
|
||
be non-copyable and implements copy operations by moving the embedded object.
|
||
Using a folly::MoveWrapper, you can capture non-copyable objects in a lambda,
|
||
and the lambda itself is still copyable and may be wrapped in a std::function.
|
||
It is a pragmatic solution for the above problem, but you have to be a little
|
||
careful. The problem is that you can’t use a MoveWrapper anywhere where copy
|
||
operations are assumed to behave like actual copy operations. Also, a
|
||
folly::MoveWrapper<std::unique_ptr<T>> essentially behaves like auto_ptr<T>. Ask
|
||
yourself whether you’d want to use lots of auto_ptrs in your codebase. And the
|
||
original question still persists: we very often don’t benefit from
|
||
copy-constructibility of std::function, so why do we have to live with this
|
||
restriction? I.e. why do we have to use MoveWrapper?"
|
||
*/
|
||
|
||
/** C++11 closures don't support move-in capture. Nor does std::bind.
|
||
facepalm.
|
||
|
||
http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2013/n3610.html
|
||
|
||
"[...] a work-around that should make people's stomach crawl:
|
||
write a wrapper that performs move-on-copy, much like the deprecated
|
||
auto_ptr"
|
||
|
||
Unlike auto_ptr, this doesn't require a heap allocation.
|
||
*/
|
||
template <class T>
|
||
class MoveWrapper {
|
||
public:
|
||
/** If value can be default-constructed, why not?
|
||
Then we don't have to move it in */
|
||
MoveWrapper() = default;
|
||
|
||
/// Move a value in.
|
||
explicit MoveWrapper(T&& t) : value(std::move(t)) {}
|
||
|
||
/// copy is move
|
||
MoveWrapper(const MoveWrapper& other) : value(std::move(other.value)) {}
|
||
|
||
/// move is also move
|
||
MoveWrapper(MoveWrapper&& other) noexcept : value(std::move(other.value)) {}
|
||
|
||
const T& operator*() const {
|
||
return value;
|
||
}
|
||
T& operator*() {
|
||
return value;
|
||
}
|
||
|
||
const T* operator->() const {
|
||
return &value;
|
||
}
|
||
T* operator->() {
|
||
return &value;
|
||
}
|
||
|
||
/// move the value out (sugar for std::move(*moveWrapper))
|
||
T&& move() {
|
||
return std::move(value);
|
||
}
|
||
|
||
// If you want these you're probably doing it wrong, though they'd be
|
||
// easy enough to implement
|
||
MoveWrapper& operator=(const MoveWrapper&) = delete;
|
||
MoveWrapper& operator=(MoveWrapper&&) = delete;
|
||
|
||
private:
|
||
mutable T value;
|
||
};
|
||
|
||
/// Make a MoveWrapper from the argument. Because the name "makeMoveWrapper"
|
||
/// is already quite transparent in its intent, this will work for lvalues as
|
||
/// if you had wrapped them in std::move.
|
||
template <class T, class T0 = typename std::remove_reference<T>::type>
|
||
MoveWrapper<T0> makeMoveWrapper(T&& t) {
|
||
return MoveWrapper<T0>(std::forward<T0>(t));
|
||
}
|
||
|
||
} // namespace facebook::react
|