зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1873769 - Add TempPtrToSetter(UniquePtr<T>*) -> T**-ish for safe out-ptr RAII. r=mccr8
Differential Revision: https://phabricator.services.mozilla.com/D198087
This commit is contained in:
Родитель
ee970b1fe9
Коммит
2bc302ee20
|
@ -9,6 +9,7 @@
|
|||
#ifndef mozilla_UniquePtr_h
|
||||
#define mozilla_UniquePtr_h
|
||||
|
||||
#include <memory>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
|
@ -645,4 +646,92 @@ void swap(mozilla::UniquePtr<T, D>& aX, mozilla::UniquePtr<T, D>& aY) {
|
|||
|
||||
} // namespace std
|
||||
|
||||
/**
|
||||
TempPtrToSetter(UniquePtr<T>*) -> T**-ish
|
||||
TempPtrToSetter(std::unique_ptr<T>*) -> T**-ish
|
||||
|
||||
Make a temporary class to support assigning to UniquePtr/unique_ptr via passing
|
||||
a pointer to the callee.
|
||||
|
||||
Often, APIs will be shaped like this trivial example:
|
||||
```
|
||||
nsresult Foo::NewChildBar(Bar** out) {
|
||||
if (!IsOk()) return NS_ERROR_FAILURE;
|
||||
*out = new Bar(this);
|
||||
return NS_OK;
|
||||
}
|
||||
```
|
||||
|
||||
In order to make this work with unique ptrs, it's often either risky or
|
||||
overwrought:
|
||||
```
|
||||
Bar* bar = nullptr;
|
||||
const auto cleanup = MakeScopeExit([&]() {
|
||||
if (bar) {
|
||||
delete bar;
|
||||
}
|
||||
});
|
||||
if (FAILED(foo->NewChildBar(&bar)) {
|
||||
// handle it
|
||||
}
|
||||
```
|
||||
|
||||
```
|
||||
UniquePtr<Bar> bar;
|
||||
{
|
||||
Bar* raw = nullptr;
|
||||
const auto res = foo->NewChildBar(&bar);
|
||||
bar.reset(raw);
|
||||
if (FAILED(res) {
|
||||
// handle it
|
||||
}
|
||||
}
|
||||
```
|
||||
TempPtrToSettable is a shorthand for the latter approach, allowing something
|
||||
cleaner but also safe:
|
||||
|
||||
```
|
||||
UniquePtr<Bar> bar;
|
||||
if (FAILED(foo->NewChildBar(TempPtrToSetter(&bar))) {
|
||||
// handle it
|
||||
}
|
||||
```
|
||||
*/
|
||||
|
||||
namespace mozilla {
|
||||
namespace detail {
|
||||
|
||||
template <class T, class UniquePtrT>
|
||||
class MOZ_TEMPORARY_CLASS TempPtrToSetterT final {
|
||||
private:
|
||||
UniquePtrT* const mDest;
|
||||
T* mNewVal;
|
||||
|
||||
public:
|
||||
explicit TempPtrToSetterT(UniquePtrT* dest)
|
||||
: mDest(dest), mNewVal(mDest->get()) {}
|
||||
|
||||
operator T**() { return &mNewVal; }
|
||||
|
||||
~TempPtrToSetterT() {
|
||||
if (mDest->get() != mNewVal) {
|
||||
mDest->reset(mNewVal);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace detail
|
||||
|
||||
template <class T, class Deleter>
|
||||
auto TempPtrToSetter(UniquePtr<T, Deleter>* const p) {
|
||||
return detail::TempPtrToSetterT<T, UniquePtr<T, Deleter>>{p};
|
||||
}
|
||||
|
||||
template <class T, class Deleter>
|
||||
auto TempPtrToSetter(std::unique_ptr<T, Deleter>* const p) {
|
||||
return detail::TempPtrToSetterT<T, std::unique_ptr<T, Deleter>>{p};
|
||||
}
|
||||
|
||||
} // namespace mozilla
|
||||
|
||||
#endif /* mozilla_UniquePtr_h */
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
|
||||
#include <stddef.h>
|
||||
|
||||
#include <memory> // For unique_ptr
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
|
@ -544,6 +545,36 @@ static bool TestVoid() {
|
|||
return true;
|
||||
}
|
||||
|
||||
static bool TestTempPtrToSetter() {
|
||||
static int sFooRefcount = 0;
|
||||
struct Foo {
|
||||
Foo() { sFooRefcount += 1; }
|
||||
|
||||
~Foo() { sFooRefcount -= 1; }
|
||||
};
|
||||
|
||||
const auto AllocByOutvar = [](Foo** out) -> bool {
|
||||
*out = new Foo;
|
||||
return true;
|
||||
};
|
||||
|
||||
{
|
||||
UniquePtr<Foo> f;
|
||||
(void)AllocByOutvar(mozilla::TempPtrToSetter(&f));
|
||||
CHECK(sFooRefcount == 1);
|
||||
}
|
||||
CHECK(sFooRefcount == 0);
|
||||
|
||||
{
|
||||
std::unique_ptr<Foo> f;
|
||||
(void)AllocByOutvar(mozilla::TempPtrToSetter(&f));
|
||||
CHECK(sFooRefcount == 1);
|
||||
}
|
||||
CHECK(sFooRefcount == 0);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
int main() {
|
||||
TestDeleterType();
|
||||
|
||||
|
@ -571,5 +602,8 @@ int main() {
|
|||
if (!TestVoid()) {
|
||||
return 1;
|
||||
}
|
||||
if (!TestTempPtrToSetter()) {
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
|
Загрузка…
Ссылка в новой задаче