util/DisposablePointer: new class

This commit is contained in:
Max Kellermann 2024-05-15 05:57:55 +02:00
parent 48d3bd1cca
commit f5092cb73d

View File

@ -0,0 +1,127 @@
// SPDX-License-Identifier: BSD-2-Clause
// Copyright CM4all GmbH
// author: Max Kellermann <mk@cm4all.com>
#pragma once
#include <utility>
/**
* A generic object which is owned by somebody who doesn't know how to
* dispose of it; to do this, a function pointer for disposing it is
* provided. Some implementations may do "delete this", but others
* may be allocated from a custom allocator and may need different
* ways to dispose of it.
*
* Unlike std::any, this class does not require the contained object
* to be copyable; quite contrary, it is designed to adopt ownership
* of the contained value.
*/
class DisposablePointer {
public:
using DisposeFunction = void(*)(void *ptr) noexcept;
private:
void *ptr = nullptr;
DisposeFunction dispose;
public:
DisposablePointer() = default;
DisposablePointer(std::nullptr_t) noexcept {}
DisposablePointer(void *_ptr, DisposeFunction _dispose) noexcept
:ptr(_ptr), dispose(_dispose) {}
DisposablePointer(DisposablePointer &&src) noexcept
:ptr(std::exchange(src.ptr, nullptr)), dispose(src.dispose) {}
~DisposablePointer() noexcept {
if (ptr != nullptr)
dispose(ptr);
}
DisposablePointer &operator=(DisposablePointer &&other) noexcept {
using std::swap;
swap(ptr, other.ptr);
swap(dispose, other.dispose);
return *this;
}
operator bool() const noexcept {
return ptr != nullptr;
}
void *get() const noexcept {
return ptr;
}
void reset() noexcept {
if (ptr != nullptr)
dispose(std::exchange(ptr, nullptr));
}
};
template<typename T>
class TypedDisposablePointer : public DisposablePointer {
public:
template<typename... Args>
TypedDisposablePointer(Args&&... args) noexcept
:DisposablePointer(std::forward<Args>(args)...) {}
TypedDisposablePointer(void *_ptr, DisposeFunction _dispose) noexcept;
TypedDisposablePointer(T *_ptr, DisposeFunction _dispose) noexcept
:DisposablePointer(_ptr, _dispose) {}
T *get() const noexcept {
return (T *)DisposablePointer::get();
}
T *operator->() const noexcept {
return get();
}
T &operator*() const noexcept {
return *get();
}
};
inline DisposablePointer
ToNopPointer(const void *ptr) noexcept
{
/* since the disposer is a no-op, we allow passing a const
pointer here; the const_cast is necessary because
DisposablePointer wants a non-const pointer */
return {const_cast<void *>(ptr), [](void *) noexcept {}};
}
template<typename T>
TypedDisposablePointer<T>
ToDeletePointer(T *ptr) noexcept
{
return {ptr, [](void *p) noexcept {
T *t = (T *)p;
delete t;
}};
}
template<typename T>
TypedDisposablePointer<T>
ToDeleteArray(T *ptr) noexcept
{
return {ptr, [](void *p) noexcept {
T *t = (T *)p;
delete[] t;
}};
}
template<typename T>
TypedDisposablePointer<T>
ToDestructPointer(T *ptr) noexcept
{
return {ptr, [](void *p) noexcept {
T *t = (T *)p;
t->~T();
}};
}