blob: 0eb654324c6ae735f9db636c85a185718f4abe21 [file]
/* Reference-counted smart pointer class
Copyright (C) 2016-2026 Free Software Foundation, Inc.
This file is part of GDB.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>. */
#ifndef GDBSUPPORT_GDB_REF_PTR_H
#define GDBSUPPORT_GDB_REF_PTR_H
#include <cstddef>
namespace gdb
{
/* An instance of this class either holds a reference to a
reference-counted object or is "NULL". Reference counting is
handled externally by a policy class. If the object holds a
reference, then when the object is destroyed, the reference is
decref'd.
Normally an instance is constructed using a pointer. This sort of
initialization lets this class manage the lifetime of that
reference.
Assignment and copy construction will make a new reference as
appropriate. Assignment from a plain pointer is disallowed to
avoid confusion about whether this acquires a new reference;
instead use the "reset" method -- which, like the pointer
constructor, transfers ownership.
The policy class must provide two static methods:
void incref (T *);
void decref (T *);
*/
template<typename T, typename Policy>
class ref_ptr
{
public:
/* Befriend all instantiations of this template, so that the
templated copy constructors and assignment operators can access
the data. */
template<typename X, typename Y> friend class ref_ptr;
/* Create a new NULL instance. */
ref_ptr () noexcept
: m_obj (NULL)
{
}
/* Create a new NULL instance. Note that this is not explicit. */
ref_ptr (const std::nullptr_t) noexcept
: m_obj (NULL)
{
}
/* Create a new instance. OBJ is a reference, management of which
is now transferred to this class. */
explicit ref_ptr (T *obj) noexcept
: m_obj (obj)
{
}
/* Copy another instance. */
template<typename U,
typename = std::is_convertible<U *, T*>>
ref_ptr (const ref_ptr<U, Policy> &other)
: m_obj (other.m_obj)
{
if (m_obj != NULL)
Policy::incref (m_obj);
}
ref_ptr (const ref_ptr &other)
: m_obj (other.m_obj)
{
if (m_obj != NULL)
Policy::incref (m_obj);
}
/* Transfer ownership from OTHER. */
template<typename U,
typename = std::is_convertible<U *, T*>>
ref_ptr (ref_ptr<U, Policy> &&other) noexcept
: m_obj (other.m_obj)
{
other.m_obj = NULL;
}
ref_ptr (ref_ptr &&other) noexcept
: m_obj (other.m_obj)
{
other.m_obj = NULL;
}
/* Destroy this instance. */
~ref_ptr ()
{
if (m_obj != NULL)
Policy::decref (m_obj);
}
/* Copy another instance. */
template<typename U,
typename = std::is_convertible<U *, T*>>
ref_ptr &operator= (const ref_ptr<U, Policy> &other)
{
/* Note that self-assignment is not checked here, as it isn't
possible: self-assignments will choose the non-template
function. */
reset (other.m_obj);
if (m_obj != NULL)
Policy::incref (m_obj);
return *this;
}
ref_ptr &operator= (const ref_ptr &other)
{
/* Do nothing on self-assignment. */
if (this != &other)
{
reset (other.m_obj);
if (m_obj != NULL)
Policy::incref (m_obj);
}
return *this;
}
/* Transfer ownership from OTHER. */
template<typename U,
typename = std::is_convertible<U *, T*>>
ref_ptr &operator= (ref_ptr<U, Policy> &&other)
{
/* Note that self-assignment is not checked here, as it isn't
possible: self-assignments will choose the non-template
function. */
reset (other.m_obj);
other.m_obj = NULL;
return *this;
}
ref_ptr &operator= (ref_ptr &&other)
{
/* Do nothing on self-assignment. */
if (this != &other)
{
reset (other.m_obj);
other.m_obj = NULL;
}
return *this;
}
/* Change this instance's referent. OBJ is a reference, management
of which is now transferred to this class. */
void reset (T *obj)
{
if (m_obj != NULL)
Policy::decref (m_obj);
m_obj = obj;
}
/* Return this instance's referent without changing the state of
this class. */
T *get () const noexcept
{
return m_obj;
}
/* Return this instance's referent, and stop managing this
reference. The caller is now responsible for the ownership of
the reference. */
ATTRIBUTE_UNUSED_RESULT T *release () noexcept
{
T *result = m_obj;
m_obj = NULL;
return result;
}
/* Let users refer to members of the underlying pointer. */
T *operator-> () const noexcept
{
return m_obj;
}
/* Acquire a new reference and return a ref_ptr that owns it. */
template <class TObj>
static ref_ptr<T, Policy> new_reference (TObj *obj)
{
Policy::incref (obj);
if constexpr (std::is_base_of<T, TObj>::value)
return ref_ptr<T, Policy> (static_cast<T *> (obj));
return ref_ptr<T, Policy> (obj);
}
private:
T *m_obj;
};
template<typename T, typename U, typename Policy>
inline bool operator== (const ref_ptr<T, Policy> &lhs,
const ref_ptr<U, Policy> &rhs)
{
return lhs.get () == rhs.get ();
}
template<typename T, typename U, typename Policy>
inline bool operator== (const ref_ptr<T, Policy> &lhs, const U *rhs)
{
return lhs.get () == rhs;
}
template<typename T, typename Policy>
inline bool operator== (const ref_ptr<T, Policy> &lhs, const std::nullptr_t)
{
return lhs.get () == nullptr;
}
template<typename T, typename U, typename Policy>
inline bool operator== (const T *lhs, const ref_ptr<U, Policy> &rhs)
{
return lhs == rhs.get ();
}
template<typename T, typename Policy>
inline bool operator== (const std::nullptr_t, const ref_ptr<T, Policy> &rhs)
{
return nullptr == rhs.get ();
}
template<typename T, typename U, typename Policy>
inline bool operator!= (const ref_ptr<T, Policy> &lhs,
const ref_ptr<U, Policy> &rhs)
{
return lhs.get () != rhs.get ();
}
template<typename T, typename U, typename Policy>
inline bool operator!= (const ref_ptr<T, Policy> &lhs, const U *rhs)
{
return lhs.get () != rhs;
}
template<typename T, typename Policy>
inline bool operator!= (const ref_ptr<T, Policy> &lhs, const std::nullptr_t)
{
return lhs.get () != nullptr;
}
template<typename T, typename U, typename Policy>
inline bool operator!= (const T *lhs, const ref_ptr<U, Policy> &rhs)
{
return lhs != rhs.get ();
}
template<typename T, typename Policy>
inline bool operator!= (const std::nullptr_t, const ref_ptr<T, Policy> &rhs)
{
return nullptr != rhs.get ();
}
}
#endif /* GDBSUPPORT_GDB_REF_PTR_H */