rcp/include/rcp.h

146 lines
3.0 KiB
C++

/*
* Copyright (C) 2026 Josh Holtrop
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the “Software”), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*/
#pragma once
#include <atomic>
namespace rcp_internal
{
class root
{
private:
mutable std::atomic<int> ref_count{0};
rcp<root> root_rcp;
protected:
root()
{
root_rcp = rcp<root>(this);
}
virtual ~root() = default;
public:
void rcp_inc() const
{
ref_count.fetch_add(1, std::memory_order_relaxed);
}
void rcp_dec() const
{
if (ref_count.fetch_sub(1, std::memory_order_acq_rel) == 0)
{
delete this;
}
}
rcp<root> & get_root_rcp()
{
return root_rcp;
}
}
template <typename T>
class managed
{
public:
static_assert(std::is_base_of<root, T>::value,
"T must inherit from rcp::root to use rcp::managed<T>");
rcp<T> get_rcp()
{
root * r = (root *)this;
return rcp<T>(r->get_root_ref());
}
}
}
/**
* Reference-counting pointer.
*/
template <typename T>
class rcp
{
private:
T * ptr = nullptr;
explicit rcp(T * p) : ptr(p)
{
if (p)
{
ptr->rcp_inc();
}
}
friend T;
public:
rcp() = default;
rcp(const rcp & other) : ptr(other.ptr)
{
if (ptr)
{
ptr->rcp_inc();
}
}
~rcp()
{
if (ptr)
{
ptr->rcp_dec();
}
}
/* Casting constructor: allows rcp<Base> -> rcp<Derived> */
template <typename U>
rcp(const rcp<U> & other) : ptr(static_cast<T *>(other.get_raw()))
{
if (ptr)
{
ptr->rcp_inc();
}
}
T * get_raw() const
{
return ptr;
}
T * operator->() const
{
return ptr;
}
bool operator!() const
{
return !ptr;
}
using root = rcp_internal::root;
using managed = rcp_internal::managed;
}