mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2024-09-11 13:36:13 +03:00
`OwnPtrWithCustomDeleter` was a decorator which provided the ability to add a custom deleter to `OwnPtr` by wrapping and taking the deleter as a run-time argument to the constructor. This solution means that no additional space is needed for the `OwnPtr` because it doesn't need to store a pointer to the deleter, but comes at the cost of having an extra type that stores a pointer for every instance. This logic is moved directly into `OwnPtr` by adding a template argument that is defaulted to the default deleter for the type. This means that the type itself stores the pointer to the deleter instead of every instance and adds some type safety by encoding the deleter in the type itself instead of taking a run-time argument.
281 lines
6.3 KiB
C++
281 lines
6.3 KiB
C++
/*
|
|
* Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
|
|
*
|
|
* SPDX-License-Identifier: BSD-2-Clause
|
|
*/
|
|
|
|
#pragma once
|
|
|
|
#define NONNULLREFPTR_SCRUB_BYTE 0xe1
|
|
|
|
#include <AK/Assertions.h>
|
|
#include <AK/Atomic.h>
|
|
#include <AK/Format.h>
|
|
#include <AK/Traits.h>
|
|
#include <AK/Types.h>
|
|
|
|
namespace AK {
|
|
|
|
template<typename T>
|
|
class RefPtr;
|
|
|
|
template<typename T>
|
|
ALWAYS_INLINE void ref_if_not_null(T* ptr)
|
|
{
|
|
if (ptr)
|
|
ptr->ref();
|
|
}
|
|
|
|
template<typename T>
|
|
ALWAYS_INLINE void unref_if_not_null(T* ptr)
|
|
{
|
|
if (ptr)
|
|
ptr->unref();
|
|
}
|
|
|
|
template<typename T>
|
|
class [[nodiscard]] NonnullRefPtr {
|
|
template<typename U>
|
|
friend class RefPtr;
|
|
template<typename U>
|
|
friend class NonnullRefPtr;
|
|
template<typename U>
|
|
friend class WeakPtr;
|
|
|
|
public:
|
|
using ElementType = T;
|
|
|
|
enum AdoptTag { Adopt };
|
|
|
|
ALWAYS_INLINE NonnullRefPtr(T const& object)
|
|
: m_ptr(const_cast<T*>(&object))
|
|
{
|
|
m_ptr->ref();
|
|
}
|
|
|
|
template<typename U>
|
|
ALWAYS_INLINE NonnullRefPtr(U const& object)
|
|
requires(IsConvertible<U*, T*>)
|
|
: m_ptr(const_cast<T*>(static_cast<T const*>(&object)))
|
|
{
|
|
m_ptr->ref();
|
|
}
|
|
|
|
ALWAYS_INLINE NonnullRefPtr(AdoptTag, T& object)
|
|
: m_ptr(&object)
|
|
{
|
|
}
|
|
|
|
ALWAYS_INLINE NonnullRefPtr(NonnullRefPtr&& other)
|
|
: m_ptr(&other.leak_ref())
|
|
{
|
|
}
|
|
|
|
template<typename U>
|
|
ALWAYS_INLINE NonnullRefPtr(NonnullRefPtr<U>&& other)
|
|
requires(IsConvertible<U*, T*>)
|
|
: m_ptr(static_cast<T*>(&other.leak_ref()))
|
|
{
|
|
}
|
|
|
|
ALWAYS_INLINE NonnullRefPtr(NonnullRefPtr const& other)
|
|
: m_ptr(const_cast<T*>(other.ptr()))
|
|
{
|
|
m_ptr->ref();
|
|
}
|
|
|
|
template<typename U>
|
|
ALWAYS_INLINE NonnullRefPtr(NonnullRefPtr<U> const& other)
|
|
requires(IsConvertible<U*, T*>)
|
|
: m_ptr(const_cast<T*>(static_cast<T const*>(other.ptr())))
|
|
{
|
|
m_ptr->ref();
|
|
}
|
|
|
|
ALWAYS_INLINE ~NonnullRefPtr()
|
|
{
|
|
unref_if_not_null(m_ptr);
|
|
m_ptr = nullptr;
|
|
#ifdef SANITIZE_PTRS
|
|
m_ptr = reinterpret_cast<T*>(explode_byte(NONNULLREFPTR_SCRUB_BYTE));
|
|
#endif
|
|
}
|
|
|
|
template<typename U>
|
|
NonnullRefPtr(OwnPtr<U> const&) = delete;
|
|
template<typename U>
|
|
NonnullRefPtr& operator=(OwnPtr<U> const&) = delete;
|
|
|
|
template<typename U>
|
|
NonnullRefPtr(RefPtr<U> const&) = delete;
|
|
template<typename U>
|
|
NonnullRefPtr& operator=(RefPtr<U> const&) = delete;
|
|
NonnullRefPtr(RefPtr<T> const&) = delete;
|
|
NonnullRefPtr& operator=(RefPtr<T> const&) = delete;
|
|
|
|
NonnullRefPtr& operator=(NonnullRefPtr const& other)
|
|
{
|
|
NonnullRefPtr tmp { other };
|
|
swap(tmp);
|
|
return *this;
|
|
}
|
|
|
|
template<typename U>
|
|
NonnullRefPtr& operator=(NonnullRefPtr<U> const& other)
|
|
requires(IsConvertible<U*, T*>)
|
|
{
|
|
NonnullRefPtr tmp { other };
|
|
swap(tmp);
|
|
return *this;
|
|
}
|
|
|
|
ALWAYS_INLINE NonnullRefPtr& operator=(NonnullRefPtr&& other)
|
|
{
|
|
NonnullRefPtr tmp { move(other) };
|
|
swap(tmp);
|
|
return *this;
|
|
}
|
|
|
|
template<typename U>
|
|
NonnullRefPtr& operator=(NonnullRefPtr<U>&& other)
|
|
requires(IsConvertible<U*, T*>)
|
|
{
|
|
NonnullRefPtr tmp { move(other) };
|
|
swap(tmp);
|
|
return *this;
|
|
}
|
|
|
|
NonnullRefPtr& operator=(T const& object)
|
|
{
|
|
NonnullRefPtr tmp { object };
|
|
swap(tmp);
|
|
return *this;
|
|
}
|
|
|
|
[[nodiscard]] ALWAYS_INLINE T& leak_ref()
|
|
{
|
|
T* ptr = exchange(m_ptr, nullptr);
|
|
VERIFY(ptr);
|
|
return *ptr;
|
|
}
|
|
|
|
ALWAYS_INLINE RETURNS_NONNULL T* ptr() const
|
|
{
|
|
return as_nonnull_ptr();
|
|
}
|
|
|
|
ALWAYS_INLINE RETURNS_NONNULL T* operator->() const
|
|
{
|
|
return as_nonnull_ptr();
|
|
}
|
|
|
|
ALWAYS_INLINE T& operator*() const
|
|
{
|
|
return *as_nonnull_ptr();
|
|
}
|
|
|
|
ALWAYS_INLINE RETURNS_NONNULL operator T*() const
|
|
{
|
|
return as_nonnull_ptr();
|
|
}
|
|
|
|
ALWAYS_INLINE operator T&() const
|
|
{
|
|
return *as_nonnull_ptr();
|
|
}
|
|
|
|
operator bool() const = delete;
|
|
bool operator!() const = delete;
|
|
|
|
void swap(NonnullRefPtr& other)
|
|
{
|
|
AK::swap(m_ptr, other.m_ptr);
|
|
}
|
|
|
|
template<typename U>
|
|
void swap(NonnullRefPtr<U>& other)
|
|
requires(IsConvertible<U*, T*>)
|
|
{
|
|
AK::swap(m_ptr, other.m_ptr);
|
|
}
|
|
|
|
bool operator==(NonnullRefPtr const& other) const { return m_ptr == other.m_ptr; }
|
|
|
|
template<typename RawPtr>
|
|
bool operator==(RawPtr other) const
|
|
requires(IsPointer<RawPtr>)
|
|
{
|
|
return m_ptr == other;
|
|
}
|
|
|
|
private:
|
|
NonnullRefPtr() = delete;
|
|
|
|
ALWAYS_INLINE RETURNS_NONNULL T* as_nonnull_ptr() const
|
|
{
|
|
VERIFY(m_ptr);
|
|
return m_ptr;
|
|
}
|
|
|
|
T* m_ptr { nullptr };
|
|
};
|
|
|
|
template<typename T>
|
|
inline NonnullRefPtr<T> adopt_ref(T& object)
|
|
{
|
|
return NonnullRefPtr<T>(NonnullRefPtr<T>::Adopt, object);
|
|
}
|
|
|
|
template<Formattable T>
|
|
struct Formatter<NonnullRefPtr<T>> : Formatter<T> {
|
|
ErrorOr<void> format(FormatBuilder& builder, NonnullRefPtr<T> const& value)
|
|
{
|
|
return Formatter<T>::format(builder, *value);
|
|
}
|
|
};
|
|
|
|
template<typename T>
|
|
requires(!HasFormatter<T>)
|
|
struct Formatter<NonnullRefPtr<T>> : Formatter<T const*> {
|
|
ErrorOr<void> format(FormatBuilder& builder, NonnullRefPtr<T> const& value)
|
|
{
|
|
return Formatter<T const*>::format(builder, value.ptr());
|
|
}
|
|
};
|
|
|
|
template<typename T, typename U>
|
|
inline void swap(NonnullRefPtr<T>& a, NonnullRefPtr<U>& b)
|
|
requires(IsConvertible<U*, T*>)
|
|
{
|
|
a.swap(b);
|
|
}
|
|
|
|
template<typename T, class... Args>
|
|
requires(IsConstructible<T, Args...>) inline NonnullRefPtr<T> make_ref_counted(Args&&... args)
|
|
{
|
|
return NonnullRefPtr<T>(NonnullRefPtr<T>::Adopt, *new T(forward<Args>(args)...));
|
|
}
|
|
|
|
// FIXME: Remove once P0960R3 is available in Clang.
|
|
template<typename T, class... Args>
|
|
inline NonnullRefPtr<T> make_ref_counted(Args&&... args)
|
|
{
|
|
return NonnullRefPtr<T>(NonnullRefPtr<T>::Adopt, *new T { forward<Args>(args)... });
|
|
}
|
|
|
|
template<typename T>
|
|
struct Traits<NonnullRefPtr<T>> : public GenericTraits<NonnullRefPtr<T>> {
|
|
using PeekType = T*;
|
|
using ConstPeekType = T const*;
|
|
static unsigned hash(NonnullRefPtr<T> const& p) { return ptr_hash(p.ptr()); }
|
|
static bool equals(NonnullRefPtr<T> const& a, NonnullRefPtr<T> const& b) { return a.ptr() == b.ptr(); }
|
|
};
|
|
|
|
}
|
|
|
|
#if USING_AK_GLOBALLY
|
|
using AK::adopt_ref;
|
|
using AK::make_ref_counted;
|
|
using AK::NonnullRefPtr;
|
|
#endif
|