carb/IObject.h

File members: carb/IObject.h

// Copyright (c) 2020-2022, NVIDIA CORPORATION. All rights reserved.
//
// NVIDIA CORPORATION and its licensors retain all intellectual property
// and proprietary rights in and to this software, related documentation
// and any modifications thereto. Any use, reproduction, disclosure or
// distribution of this software and related documentation without an express
// license agreement from NVIDIA CORPORATION is strictly prohibited.
//

#pragma once

#include "Interface.h"

#include <cstdint>

namespace carb
{

class IObject
{
public:
    CARB_PLUGIN_INTERFACE("carb::IObject", 1, 0)

    virtual ~IObject() = default;

    virtual size_t addRef() = 0;

    virtual size_t release() = 0;
};

template <class T>
class ObjectPtr
{
public:

    enum class InitPolicy
    {
        eBorrow,
        eSteal
    };

    ObjectPtr() : m_object(nullptr)
    {
    }

    ObjectPtr(std::nullptr_t) : m_object(nullptr)
    {
    }

    explicit ObjectPtr(T* object) : m_object(object)
    {
        if (m_object)
        {
            m_object->addRef();
        }
    }

    ObjectPtr(T* object, InitPolicy policy) : m_object(object)
    {
        if (policy == InitPolicy::eBorrow && m_object != nullptr)
        {
            m_object->addRef();
        }
    }

    ObjectPtr(const ObjectPtr<T>& other) : ObjectPtr(other.m_object, InitPolicy::eBorrow)
    {
    }

    template <class U>
    ObjectPtr(const ObjectPtr<U>& other) : ObjectPtr(other.m_object, InitPolicy::eBorrow)
    {
    }

    ObjectPtr(ObjectPtr<T>&& other) : m_object(other.m_object)
    {
        other.m_object = nullptr;
    }

    template <class U>
    ObjectPtr(ObjectPtr<U>&& other) : m_object(other.m_object)
    {
        other.m_object = nullptr;
    }

    ~ObjectPtr()
    {
        _release();
    }

    T* get() const
    {
        return m_object;
    }

    T* operator->() const
    {
        CARB_ASSERT(m_object);
        return m_object;
    }

    T& operator*() const
    {
        CARB_ASSERT(m_object);
        return *m_object;
    }

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

    T* const* getAddressOf() const
    {
        return &m_object;
    }

    T** getAddressOf()
    {
        return &m_object;
    }

    T** releaseAndGetAddressOf()
    {
        _release();
        return &m_object;
    }

    T* detach()
    {
        T* temp = m_object;
        m_object = nullptr;
        return temp;
    }

    void attach(T* other)
    {
        _release();
        m_object = other;
    }

    ObjectPtr& operator=(decltype(nullptr))
    {
        _release();
        return *this;
    }

    ObjectPtr& operator=(T* other)
    {
        ObjectPtr(other).swap(*this);
        return *this;
    }

    template <typename U>
    ObjectPtr& operator=(U* other)
    {
        ObjectPtr(other).swap(*this);
        return *this;
    }

    ObjectPtr& operator=(const ObjectPtr& other)
    {
        ObjectPtr(other).swap(*this);
        return *this;
    }

    template <class U>
    ObjectPtr& operator=(const ObjectPtr<U>& other)
    {
        ObjectPtr(other).swap(*this);
        return *this;
    }

    ObjectPtr& operator=(ObjectPtr&& other)
    {
        other.swap(*this);
        return *this;
    }

    template <class U>
    ObjectPtr& operator=(ObjectPtr<U>&& other)
    {
        ObjectPtr(std::move(other)).swap(*this);
        return *this;
    }

    template <class U>
    bool operator==(const ObjectPtr<U>& other) const
    {
        return get() == other.get();
    }

    template <class U>
    bool operator!=(const ObjectPtr<U>& other) const
    {
        return get() != other.get();
    }

    void swap(ObjectPtr& other)
    {
        std::swap(m_object, other.m_object);
    }

private:
    void _release()
    {
        if (T* old = std::exchange(m_object, nullptr))
        {
            old->release();
        }
    }

    T* m_object;
};

template <class T>
inline ObjectPtr<T> stealObject(T* other)
{
    return ObjectPtr<T>(other, ObjectPtr<T>::InitPolicy::eSteal);
}

template <class T>
inline ObjectPtr<T> borrowObject(T* other)
{
    return ObjectPtr<T>(other, ObjectPtr<T>::InitPolicy::eBorrow);
}

} // namespace carb