gcroot.h

// gcroot.h
//
// Author David Barrett-Lennard
// (C)opyright Cedanet Pty Ltd 2021

@import "IObject.h"
@import "WCSpace.h"
#include <atomic>
#include <type_traits>

namespace ceda
{

// See https://en.cppreference.com/w/cpp/types/is_convertible
//     https://en.cppreference.com/w/cpp/types/enable_if

template<typename Tp>
class gcroot
{
public:
    using pointer_type = Tp;

    constexpr gcroot() noexcept {}          // construct empty gcroot
    constexpr gcroot(nullptr_t) noexcept {} // construct empty gcroot

    template <class R, std::enable_if_t<std::is_convertible<R,pointer_type>::value, int> = 0>
    explicit gcroot(R p) noexcept
    { 
        if (p)
        {
            ptr_ = p;
            ref_ = &p->GetIObjectSysState();
            ref_->IncRef();
        }
    }

    template <class R>
    gcroot(R r, pointer_type p) noexcept
    { 
        ptr_ = p;
        if (r)
        {
            ref_ = &r->GetIObjectSysState();
            ref_->IncRef();
        }
    }

    ~gcroot() noexcept 
    { 
        if (ref_) 
        {
            ref_->DecRef();
        }
    }

    template <class R>
    gcroot(const gcroot<R>& right, pointer_type p) noexcept 
    {
        // construct gcroot object that aliases right
        this->_Alias_construct_from(right, p);
    }

    template <class R>
    gcroot(gcroot<R>&& right, pointer_type p) noexcept 
    {
        // move construct gcroot object that aliases right
        this->_Alias_move_construct_from(std::move(right), p);
    }

    gcroot(const gcroot& right) noexcept 
    { 
        // construct gcroot object that owns same resource as right
        this->_Copy_construct_from(right);
    }


    template <class R, std::enable_if_t<std::is_convertible<R,pointer_type>::value, int> = 0>
    gcroot(const gcroot<R>& right) noexcept 
    {
        // construct gcroot object that owns same resource as right
        this->_Copy_construct_from(right);
    }

    gcroot(gcroot&& right) noexcept 
    { 
        // construct gcroot object that takes resource from right
        this->_Move_construct_from(std::move(right));
    }

    template <class R, std::enable_if_t<std::is_convertible<R,pointer_type>::value, int> = 0>
    gcroot(gcroot<R>&& right) noexcept 
    { 
        // construct gcroot object that takes resource from right
        this->_Move_construct_from(std::move(right));
    }

    void swap(gcroot& right) noexcept 
    { 
        // swap pointers
        using std::swap;
        swap(ptr_, right.ptr_);
        swap(ref_, right.ref_);
    }
                                                                                                                                                                                 
    void reset() noexcept 
    {
        // release resource and convert to empty gcroot object
        gcroot().swap(*this);
    }

    template <class R>
    void reset(R p) 
    { 
        // release, take ownership of p
        gcroot(p).swap(*this);
    }

    gcroot& operator=(const gcroot& right) noexcept 
    {
        gcroot(right).swap(*this);
        return *this;
    }

    template <class R>
    gcroot& operator=(const gcroot<R>& right) noexcept 
    {
        gcroot(right).swap(*this);
        return *this;
    }

    gcroot& operator=(gcroot&& right) noexcept 
    { 
        // take resource from right
        gcroot(std::move(right)).swap(*this);
        return *this;
    }

    template <class R>
    gcroot& operator=(gcroot<R>&& right) noexcept 
    { 
        // take resource from right
        gcroot(std::move(right)).swap(*this);
        return *this;
    }

    [[nodiscard]] pointer_type get() const noexcept 
    {
        return ptr_;
    }

    [[nodiscard]] auto& operator*() const noexcept 
    {
        return *get();
    }

    [[nodiscard]] pointer_type operator->() const noexcept 
    {
        return get();
    }

    explicit operator bool() const noexcept 
    {
        return get() != nullptr;
    }

private:
    template <class S, class... Args>
    friend gcroot<S*> make_gcroot(Args&&... args);

    template <class R>
    friend class gcroot;

    template <class R>
    void _Move_construct_from(gcroot<R>&& right) noexcept 
    {
        ptr_ = right.ptr_;
        ref_ = right.ref_;
        right.ptr_ = nullptr;
        right.ref_ = nullptr;
    }

    template <class R>
    void _Copy_construct_from(const gcroot<R>& right) noexcept 
    {
        if (right.ref_) 
        {
            right.ref_->IncRef();
        }
        ptr_ = right.ptr_;
        ref_ = right.ref_;
    }

    template <class R>
    void _Alias_construct_from(const gcroot<R>& right, pointer_type p) noexcept 
    {
        if (right.ref_) 
        {
            right.ref_->IncRef();
        }
        ptr_ = p;
        ref_ = right.ref_;
    }

    template <class R>
    void _Alias_move_construct_from(gcroot<R>&& right, pointer_type p) noexcept 
    {
        ptr_ = p;
        ref_ = right.ref_;
        right.ptr_ = nullptr;
        right.ref_ = nullptr;
    }

    ObjSysState* ref_{nullptr};
    pointer_type ptr_{nullptr};
};

template <class L, class R>
[[nodiscard]] bool operator==(const gcroot<L>& left, const gcroot<R>& right) noexcept 
{
    return left.get() == right.get();
}

template <class L, class R>
[[nodiscard]] bool operator!=(const gcroot<L>& left, const gcroot<R>& right) noexcept 
{
    return left.get() != right.get();
}

template <class L, class R>
[[nodiscard]] bool operator<(const gcroot<L>& left, const gcroot<R>& right) noexcept 
{
    return left.get() < right.get();
}

template <class L, class R>
[[nodiscard]] bool operator>=(const gcroot<L>& left, const gcroot<R>& right) noexcept 
{
    return left.get() >= right.get();
}

template <class L, class R>
[[nodiscard]] bool operator>(const gcroot<L>& left, const gcroot<R>& right) noexcept 
{
    return left.get() > right.get();
}

template <class L, class R>
[[nodiscard]] bool operator<=(const gcroot<L>& left, const gcroot<R>& right) noexcept 
{
    return left.get() <= right.get();
}

template <class T>
[[nodiscard]] bool operator==(const gcroot<T>& left, nullptr_t) noexcept 
{
    return left.get() == nullptr;
}

template <class T>
[[nodiscard]] bool operator==(nullptr_t, const gcroot<T>& right) noexcept 
{
    return nullptr == right.get();
}

template <class T>
[[nodiscard]] bool operator!=(const gcroot<T>& left, nullptr_t) noexcept 
{
    return left.get() != nullptr;
}

template <class T>
[[nodiscard]] bool operator!=(nullptr_t, const gcroot<T>& right) noexcept 
{
    return nullptr != right.get();
}

template <class T>
[[nodiscard]] bool operator<(const gcroot<T>& left, nullptr_t) noexcept 
{
    return left.get() < static_cast<T>(nullptr);
}

template <class T>
[[nodiscard]] bool operator<(nullptr_t, const gcroot<T>& right) noexcept 
{
    return static_cast<T>(nullptr) < right.get();
}

template <class T>
[[nodiscard]] bool operator>=(const gcroot<T>& left, nullptr_t) noexcept 
{
    return left.get() >= static_cast<T>(nullptr);
}

template <class T>
[[nodiscard]] bool operator>=(nullptr_t, const gcroot<T>& right) noexcept 
{
    return static_cast<T>(nullptr) >= right.get();
}

template <class T>
[[nodiscard]] bool operator>(const gcroot<T>& left, nullptr_t) noexcept 
{
    return left.get() > static_cast<T>(nullptr);
}

template <class T>
[[nodiscard]] bool operator>(nullptr_t, const gcroot<T>& right) noexcept 
{
    return static_cast<T>(nullptr) > right.get();
}

template <class T>
[[nodiscard]] bool operator<=(const gcroot<T>& left, nullptr_t) noexcept 
{
    return left.get() <= static_cast<T>(nullptr);
}

template <class T>
[[nodiscard]] bool operator<=(nullptr_t, const gcroot<T>& right) noexcept 
{
    return static_cast<T>(nullptr) <= right.get();
}

template <class _Elem, class _Traits, class T>
std::basic_ostream<_Elem, _Traits>& operator<<(std::basic_ostream<_Elem, _Traits>& os, const gcroot<T>& p) 
{
    // write contained pointer to stream
    return os << p.get();
}

template <class T>
void swap(gcroot<T>& left, gcroot<T>& right) noexcept
{
    left.swap(right);
}

template <class L, class R>
[[nodiscard]] gcroot<L> static_pointer_cast(const gcroot<R>& right) noexcept 
{
    const auto p = static_cast<L>(right.get());
    return gcroot<L>(right, p);
}

template <class L, class R>
[[nodiscard]] gcroot<L> static_pointer_cast(gcroot<R>&& right) noexcept 
{
    const auto p = static_cast<L>(right.get());
    return gcroot<L>(std::move(right), p);
}

template <class L, class R>
[[nodiscard]] gcroot<L> const_pointer_cast(const gcroot<R>& right) noexcept 
{
    const auto p = const_cast<L>(right.get());
    return gcroot<L>(right, p);
}

template <class L, class R>
[[nodiscard]] gcroot<L> const_pointer_cast(gcroot<R>&& right) noexcept 
{
    const auto p = const_cast<L>(right.get());
    return gcroot<L>(std::move(right), p);
}

template <class L, class R>
[[nodiscard]] gcroot<L> reinterpret_pointer_cast(const gcroot<R>& right) noexcept 
{
    const auto p = reinterpret_cast<L>(right.get());
    return gcroot<L>(right, p);
}

template <class L, class R>
[[nodiscard]] gcroot<L> reinterpret_pointer_cast(gcroot<R>&& right) noexcept 
{
    const auto p = reinterpret_cast<L>(right.get());
    return gcroot<L>(std::move(right), p);
}

template <class L, class R>
[[nodiscard]] gcroot<L> dynamic_pointer_cast(const gcroot<R>& right) noexcept 
{
    if (const auto p = dynamic_cast<L>(right.get())) 
    {
        return gcroot<L>(right, p);
    }
    return gcroot<L>();
}

template <class L, class R>
[[nodiscard]] gcroot<L> dynamic_pointer_cast(gcroot<R>&& right) noexcept 
{
    if (const auto p = dynamic_cast<L>(right.get())) 
    {
        return gcroot<L>(std::move(right), p);
    }
    return gcroot<L>();
}

// make a gcroot
template <class T, class... Args>
[[nodiscard]] gcroot<T*> make_gcroot(Args&&... args) 
{ 
    auto p = new T(std::forward<Args>(args)...);
    RegisterGcObject(p);
    return gcroot<T*>(p);
}

} // ceda