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
|