/* * 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 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_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> essentially behaves like auto_ptr. 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 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 ::type> MoveWrapper makeMoveWrapper(T&& t) { return MoveWrapper(std::forward(t)); } } // namespace facebook::react