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:
Kelsey Gilbert 2024-01-26 21:05:30 +00:00
Родитель ee970b1fe9
Коммит 2bc302ee20
2 изменённых файлов: 123 добавлений и 0 удалений

Просмотреть файл

@ -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;
}