diff --git a/src/util/DisposablePointer.hxx b/src/util/DisposablePointer.hxx new file mode 100644 index 000000000..2ee30e410 --- /dev/null +++ b/src/util/DisposablePointer.hxx @@ -0,0 +1,127 @@ +// SPDX-License-Identifier: BSD-2-Clause +// Copyright CM4all GmbH +// author: Max Kellermann + +#pragma once + +#include + +/** + * 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 +class TypedDisposablePointer : public DisposablePointer { +public: + template + TypedDisposablePointer(Args&&... args) noexcept + :DisposablePointer(std::forward(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(ptr), [](void *) noexcept {}}; +} + +template +TypedDisposablePointer +ToDeletePointer(T *ptr) noexcept +{ + return {ptr, [](void *p) noexcept { + T *t = (T *)p; + delete t; + }}; +} + +template +TypedDisposablePointer +ToDeleteArray(T *ptr) noexcept +{ + return {ptr, [](void *p) noexcept { + T *t = (T *)p; + delete[] t; + }}; +} + +template +TypedDisposablePointer +ToDestructPointer(T *ptr) noexcept +{ + return {ptr, [](void *p) noexcept { + T *t = (T *)p; + t->~T(); + }}; +}