// Copyright 2020 The Dawn Authors // // 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 COMMON_REFBASE_H_ #define COMMON_REFBASE_H_ #include "common/Assert.h" #include "common/Compiler.h" #include #include // A common class for various smart-pointers acting on referenceable/releasable pointer-like // objects. Logic for each specialization can be customized using a Traits type that looks // like the following: // // struct { // static constexpr T kNullValue = ...; // static void Reference(T value) { ... } // static void Release(T value) { ... } // }; // // RefBase supports template class RefBase { public: // Default constructor and destructor. RefBase() : mValue(Traits::kNullValue) { } ~RefBase() { Release(mValue); } // Constructors from nullptr. constexpr RefBase(std::nullptr_t) : RefBase() { } RefBase& operator=(std::nullptr_t) { Set(Traits::kNullValue); return *this; } // Constructors from a value T. RefBase(T value) : mValue(value) { Reference(value); } RefBase& operator=(const T& value) { Set(value); return *this; } // Constructors from a RefBase RefBase(const RefBase& other) : mValue(other.mValue) { Reference(other.mValue); } RefBase& operator=(const RefBase& other) { Set(other.mValue); return *this; } RefBase(RefBase&& other) { mValue = other.Detach(); } RefBase& operator=(RefBase&& other) { if (&other != this) { Release(mValue); mValue = other.Detach(); } return *this; } // Constructors from a RefBase. Note that in the *-assignment operators this cannot be the // same as `other` because overload resolution rules would have chosen the *-assignement // operators defined with `other` == RefBase. template ::type> RefBase(const RefBase& other) : mValue(other.mValue) { Reference(other.mValue); } template ::type> RefBase& operator=(const RefBase& other) { Set(other.mValue); return *this; } template ::type> RefBase(RefBase&& other) { mValue = other.Detach(); } template ::type> RefBase& operator=(RefBase&& other) { Release(mValue); mValue = other.Detach(); return *this; } // Comparison operators. bool operator==(const T& other) const { return mValue == other; } bool operator!=(const T& other) const { return mValue != other; } const T operator->() const { return mValue; } T operator->() { return mValue; } // Smart pointer methods. const T& Get() const { return mValue; } T& Get() { return mValue; } T Detach() DAWN_NO_DISCARD { T value{std::move(mValue)}; mValue = Traits::kNullValue; return value; } void Acquire(T value) { Release(mValue); mValue = value; } T* InitializeInto() DAWN_NO_DISCARD { ASSERT(mValue == Traits::kNullValue); return &mValue; } private: // Friend is needed so that instances of RefBase can call Reference and Release on // RefBase. template friend class RefBase; static void Reference(T value) { if (value != Traits::kNullValue) { Traits::Reference(value); } } static void Release(T value) { if (value != Traits::kNullValue) { Traits::Release(value); } } void Set(T value) { if (mValue != value) { // Ensure that the new value is referenced before the old is released to prevent any // transitive frees that may affect the new value. Reference(value); Release(mValue); mValue = value; } } T mValue; }; #endif // COMMON_REFBASE_H_