/*
* Copyright (c) 2024 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef META_BASE_SHARED_PTR_H
#define META_BASE_SHARED_PTR_H
#include
#include "shared_ptr_internals.h"
BASE_BEGIN_NAMESPACE()
/**
* @brief C++ standard like weak_ptr.
*/
template
class weak_ptr final : public Internals::PtrCountedBase {
public:
using element_type = BASE_NS::remove_extent_t;
weak_ptr() = default;
~weak_ptr()
{
if (this->control_) {
this->control_->ReleaseWeak();
}
};
weak_ptr(nullptr_t) {}
weak_ptr(const shared_ptr& p) : Internals::PtrCountedBase(p)
{
if (this->control_) {
this->control_->AddWeak();
}
}
weak_ptr(const weak_ptr& p) noexcept : Internals::PtrCountedBase(p)
{
if (this->control_) {
this->control_->AddWeak();
}
}
weak_ptr(weak_ptr&& p) noexcept : Internals::PtrCountedBase(p)
{
p.InternalReset();
}
// NOLINTNEXTLINE(readability-identifier-naming) to keep std like syntax
shared_ptr lock() const
{
return shared_ptr(*this);
}
weak_ptr& operator=(weak_ptr&& p) noexcept
{
if (this != &p) {
reset();
this->control_ = p.control_;
this->pointer_ = p.pointer_;
p.InternalReset();
}
return *this;
}
weak_ptr& operator=(const weak_ptr& p) noexcept
{
if (this != &p) {
reset();
this->control_ = p.control_;
this->pointer_ = p.pointer_;
if (this->control_) {
this->control_->AddWeak();
}
}
return *this;
}
weak_ptr& operator=(const shared_ptr& p)
{
reset();
this->control_ = p.control_;
this->pointer_ = p.pointer_;
if (this->control_) {
this->control_->AddWeak();
}
return *this;
}
weak_ptr& operator=(nullptr_t) noexcept
{
reset();
return *this;
}
// NOLINTNEXTLINE(readability-identifier-naming) to keep std like syntax
void reset()
{
if (this->control_) {
this->control_->ReleaseWeak();
this->InternalReset();
}
}
/*"implicit" casting constructors */
template>
weak_ptr(const shared_ptr& p)
// handle casting by using functionality in shared_ptr. (creates an aliased shared_ptr to original.)
: weak_ptr(shared_ptr(p))
{}
template>
weak_ptr(const weak_ptr& p) : weak_ptr(shared_ptr(p.lock()))
{}
/* "implicit" casting move */
template>
weak_ptr(weak_ptr&& p) noexcept : weak_ptr(shared_ptr(p.lock()))
{
p.reset();
}
/* "implicit" casting operators */
template>
weak_ptr& operator=(const shared_ptr& p)
{
// handle casting by using functionality in shared_ptr. (creates an aliased shared_ptr to original.)
*this = shared_ptr(p);
return *this;
}
template>
weak_ptr& operator=(const weak_ptr& p)
{
// first lock the given weak ptr. (to see if it has expired, and to get a pointer that can be cast)
*this = shared_ptr(p.lock());
return *this;
}
// NOLINTNEXTLINE(readability-identifier-naming) to keep std like syntax
bool expired() const noexcept
{
return !this->control_ || this->control_->GetStrongCount() == 0;
}
private:
friend class shared_ptr;
template
friend class weak_ptr;
};
/**
* @brief C++ standard like shared_ptr with IInterface support for reference counting.
*/
template
class shared_ptr final : public Internals::PtrCountedBase {
public:
using element_type = BASE_NS::remove_extent_t;
using weak_type = weak_ptr;
constexpr shared_ptr() noexcept = default;
constexpr shared_ptr(nullptr_t) noexcept {}
shared_ptr(const shared_ptr& p) noexcept : Internals::PtrCountedBase(p)
{
if (this->control_) {
this->control_->AddStrongCopy();
}
}
shared_ptr(shared_ptr&& p) noexcept : Internals::PtrCountedBase(p)
{
p.InternalReset();
}
explicit shared_ptr(T* ptr)
{
if (ptr) {
ConstructBlock(ptr);
}
}
template
shared_ptr(T* ptr, Deleter deleter)
{
if (ptr) {
ConstructBlock(ptr, BASE_NS::move(deleter));
}
}
explicit shared_ptr(const weak_type& p) noexcept : Internals::PtrCountedBase(p)
{
if (this->control_) {
if (!this->control_->AddStrongLock()) {
this->InternalReset();
}
}
}
template
shared_ptr(const shared_ptr& r, T* ptr) noexcept : Internals::PtrCountedBase(r.control_)
{
if (this->control_ && ptr) {
this->control_->AddStrongCopy();
this->pointer_ = const_cast(ptr);
} else {
this->InternalReset();
}
}
template>
shared_ptr(shared_ptr&& p) noexcept : Internals::PtrCountedBase(p.control_)
{
if (this->control_) {
void* ptr = nullptr;
if constexpr (BASE_NS::is_same_v> ||
!META_NS::HasGetInterfaceMethod_v) {
ptr = p.get();
} else {
// make a proper interface cast here.
if constexpr (BASE_NS::is_const_v) {
ptr = const_cast(static_cast(p->GetInterface(T::UID)));
} else {
ptr = static_cast(p->GetInterface(T::UID));
}
}
if (ptr) {
this->pointer_ = static_cast(ptr);
p.InternalReset();
} else {
this->InternalReset();
p.reset();
}
}
}
template>
shared_ptr(const shared_ptr& p) noexcept : shared_ptr(shared_ptr(p)) // use the above move constructor
{}
template>
shared_ptr(unique_ptr&& p) noexcept
{
if (p) {
ConstructBlock(p.release(), BASE_NS::move(p.get_deleter()));
}
}
~shared_ptr()
{
if (this->control_) {
this->control_->Release();
}
}
T* operator->() const noexcept
{
return get();
}
T& operator*() const noexcept
{
return *get();
}
explicit operator bool() const
{
return get();
}
bool operator==(const shared_ptr& other) const noexcept
{
return get() == other.get();
}
bool operator!=(const shared_ptr& other) const noexcept
{
return !(*this == other);
}
// NOLINTNEXTLINE(readability-identifier-naming) to keep std like syntax
void reset()
{
if (this->control_) {
this->control_->Release();
this->InternalReset();
}
}
// NOLINTNEXTLINE(readability-identifier-naming) to keep std like syntax
void reset(T* ptr)
{
if (ptr != this->pointer_) {
reset();
if (ptr) {
ConstructBlock(ptr);
}
}
}
template
// NOLINTNEXTLINE(readability-identifier-naming) to keep std like syntax
void reset(T* ptr, Deleter deleter)
{
if (ptr != this->pointer_) {
reset();
if (ptr) {
ConstructBlock(ptr, BASE_NS::move(deleter));
}
}
}
shared_ptr& operator=(nullptr_t) noexcept
{
reset();
return *this;
}
shared_ptr& operator=(const shared_ptr& o)
{
if (this != &o) {
reset();
this->control_ = o.control_;
this->pointer_ = o.pointer_;
if (this->control_) {
this->control_->AddStrongCopy();
}
}
return *this;
}
shared_ptr& operator=(shared_ptr&& o) noexcept
{
if (this != &o) {
reset();
this->control_ = o.control_;
this->pointer_ = o.pointer_;
o.InternalReset();
}
return *this;
}
// NOLINTNEXTLINE(readability-identifier-naming) to keep std like syntax
void swap(shared_ptr& p)
{
auto tp = p.pointer_;
auto tc = p.control_;
p.pointer_ = this->pointer_;
p.control_ = this->control_;
this->pointer_ = tp;
this->control_ = tc;
}
// NOLINTNEXTLINE(readability-identifier-naming) to keep std like syntax
element_type* get() const noexcept
{
return this->pointer_;
}
private:
using deletableType = BASE_NS::remove_const_t;
void ConstructBlock(T* ptr)
{
static_assert(sizeof(T), "type has to be complete when constructing control block");
if constexpr (BASE_NS::is_convertible_v) {
this->control_ = new Internals::RefCountedObjectStorageBlock(ptr);
} else {
this->control_ = new Internals::StorageBlock(ptr);
}
this->pointer_ = ptr;
}
template
void ConstructBlock(T* ptr, Deleter deleter)
{
this->control_ = new Internals::StorageBlockWithDeleter(ptr, BASE_NS::move(deleter));
this->pointer_ = ptr;
}
template
friend class weak_ptr;
template
friend class shared_ptr;
};
BASE_END_NAMESPACE()
// NOLINTBEGIN(readability-identifier-naming) to keep std like syntax
template
BASE_NS::shared_ptr static_pointer_cast(const BASE_NS::shared_ptr& ptr)
{
if (ptr) {
return BASE_NS::shared_ptr(ptr, static_cast(ptr.get()));
}
return {};
}
template
BASE_NS::shared_ptr interface_pointer_cast(const BASE_NS::shared_ptr& ptr)
{
static_assert(META_NS::HasGetInterfaceMethod_v, "T::GetInterface not defined");
static_assert(META_NS::HasGetInterfaceMethod_v, "U::GetInterface not defined");
if (ptr) {
if constexpr (BASE_NS::is_same_v) {
// same type.
return ptr;
} else {
return BASE_NS::shared_ptr(ptr, static_cast(static_cast(ptr->GetInterface(U::UID))));
}
}
return {};
}
template
BASE_NS::shared_ptr interface_pointer_cast(const BASE_NS::shared_ptr& ptr)
{
static_assert(META_NS::HasGetInterfaceMethod_v, "T::GetInterface not defined");
static_assert(META_NS::HasGetInterfaceMethod_v, "U::GetInterface not defined");
if (ptr) {
if constexpr (BASE_NS::is_same_v) {
// same type.
return ptr;
} else {
return BASE_NS::shared_ptr(
ptr, static_cast(static_cast(ptr->GetInterface(U::UID))));
}
}
return {};
}
template
BASE_NS::shared_ptr interface_pointer_cast(const BASE_NS::weak_ptr& weak)
{
return interface_pointer_cast(weak.lock());
}
template
BASE_NS::shared_ptr interface_pointer_cast(const BASE_NS::weak_ptr& weak)
{
return interface_pointer_cast(weak.lock());
}
template
U* interface_cast(const BASE_NS::shared_ptr& ptr)
{
static_assert(META_NS::HasGetInterfaceMethod_v, "T::GetInterface not defined");
static_assert(META_NS::HasGetInterfaceMethod_v, "U::GetInterface not defined");
if (ptr) {
if constexpr (BASE_NS::is_same_v) {
// same type.
return ptr.get();
} else {
return static_cast(static_cast(ptr->GetInterface(U::UID)));
}
}
return {};
}
template
const U* interface_cast(const BASE_NS::shared_ptr& ptr)
{
static_assert(META_NS::HasGetInterfaceMethod_v, "T::GetInterface not defined");
static_assert(META_NS::HasGetInterfaceMethod_v, "U::GetInterface not defined");
if (ptr) {
if constexpr (BASE_NS::is_same_v) {
// same type.
return ptr.get();
} else {
return static_cast(static_cast(ptr->GetInterface(U::UID)));
}
}
return {};
}
template
U* interface_cast(CORE_NS::IInterface* ptr)
{
static_assert(META_NS::HasGetInterfaceMethod_v, "U::GetInterface not defined");
if (ptr) {
return static_cast(static_cast(ptr->GetInterface(U::UID)));
}
return {};
}
template
const U* interface_cast(const CORE_NS::IInterface* ptr)
{
static_assert(META_NS::HasGetInterfaceMethod_v, "U::GetInterface not defined");
if (ptr) {
return static_cast(static_cast(ptr->GetInterface(U::UID)));
}
return {};
}
// NOLINTEND(readability-identifier-naming) to keep std like syntax
template
BASE_NS::shared_ptr CreateShared(Args&&... args)
{
return BASE_NS::shared_ptr(new T(BASE_NS::forward(args)...));
}
META_TYPE(BASE_NS::shared_ptr)
META_TYPE(BASE_NS::shared_ptr)
META_TYPE(BASE_NS::weak_ptr)
META_TYPE(BASE_NS::weak_ptr)
#endif