rcp/include/rcp.h

137 lines
2.8 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>
/**
* 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;
}
};
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;
}
};
#define rcp_managed(classname) \
rcp<classname> get_rcp() \
{ \
root * r = (root *)this; \
return rcp<classname>(r->get_root_rcp()); \
} \
template <typename... Args> \
static rcp<classname> create(Args&&... args) \
{ \
return (new classname(std::forward<Args>(args)...))->get_rcp(); \
}