• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2020 The Dawn Authors
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 //     http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 #ifndef COMMON_REFBASE_H_
16 #define COMMON_REFBASE_H_
17 
18 #include "common/Assert.h"
19 #include "common/Compiler.h"
20 
21 #include <type_traits>
22 #include <utility>
23 
24 // A common class for various smart-pointers acting on referenceable/releasable pointer-like
25 // objects. Logic for each specialization can be customized using a Traits type that looks
26 // like the following:
27 //
28 //   struct {
29 //      static constexpr T kNullValue = ...;
30 //      static void Reference(T value) { ... }
31 //      static void Release(T value) { ... }
32 //   };
33 //
34 // RefBase supports
35 template <typename T, typename Traits>
36 class RefBase {
37   public:
38     // Default constructor and destructor.
RefBase()39     RefBase() : mValue(Traits::kNullValue) {
40     }
41 
~RefBase()42     ~RefBase() {
43         Release(mValue);
44     }
45 
46     // Constructors from nullptr.
RefBase(std::nullptr_t)47     constexpr RefBase(std::nullptr_t) : RefBase() {
48     }
49 
50     RefBase<T, Traits>& operator=(std::nullptr_t) {
51         Set(Traits::kNullValue);
52         return *this;
53     }
54 
55     // Constructors from a value T.
RefBase(T value)56     RefBase(T value) : mValue(value) {
57         Reference(value);
58     }
59 
60     RefBase<T, Traits>& operator=(const T& value) {
61         Set(value);
62         return *this;
63     }
64 
65     // Constructors from a RefBase<T>
RefBase(const RefBase<T,Traits> & other)66     RefBase(const RefBase<T, Traits>& other) : mValue(other.mValue) {
67         Reference(other.mValue);
68     }
69 
70     RefBase<T, Traits>& operator=(const RefBase<T, Traits>& other) {
71         Set(other.mValue);
72         return *this;
73     }
74 
RefBase(RefBase<T,Traits> && other)75     RefBase(RefBase<T, Traits>&& other) {
76         mValue = other.Detach();
77     }
78 
79     RefBase<T, Traits>& operator=(RefBase<T, Traits>&& other) {
80         if (&other != this) {
81             Release(mValue);
82             mValue = other.Detach();
83         }
84         return *this;
85     }
86 
87     // Constructors from a RefBase<U>. Note that in the *-assignment operators this cannot be the
88     // same as `other` because overload resolution rules would have chosen the *-assignement
89     // operators defined with `other` == RefBase<T, Traits>.
90     template <typename U, typename UTraits, typename = typename std::is_convertible<U, T>::type>
RefBase(const RefBase<U,UTraits> & other)91     RefBase(const RefBase<U, UTraits>& other) : mValue(other.mValue) {
92         Reference(other.mValue);
93     }
94 
95     template <typename U, typename UTraits, typename = typename std::is_convertible<U, T>::type>
96     RefBase<T, Traits>& operator=(const RefBase<U, UTraits>& other) {
97         Set(other.mValue);
98         return *this;
99     }
100 
101     template <typename U, typename UTraits, typename = typename std::is_convertible<U, T>::type>
RefBase(RefBase<U,UTraits> && other)102     RefBase(RefBase<U, UTraits>&& other) {
103         mValue = other.Detach();
104     }
105 
106     template <typename U, typename UTraits, typename = typename std::is_convertible<U, T>::type>
107     RefBase<T, Traits>& operator=(RefBase<U, UTraits>&& other) {
108         Release(mValue);
109         mValue = other.Detach();
110         return *this;
111     }
112 
113     // Comparison operators.
114     bool operator==(const T& other) const {
115         return mValue == other;
116     }
117 
118     bool operator!=(const T& other) const {
119         return mValue != other;
120     }
121 
122     const T operator->() const {
123         return mValue;
124     }
125     T operator->() {
126         return mValue;
127     }
128 
129     // Smart pointer methods.
Get()130     const T& Get() const {
131         return mValue;
132     }
Get()133     T& Get() {
134         return mValue;
135     }
136 
Detach()137     T Detach() DAWN_NO_DISCARD {
138         T value{std::move(mValue)};
139         mValue = Traits::kNullValue;
140         return value;
141     }
142 
Acquire(T value)143     void Acquire(T value) {
144         Release(mValue);
145         mValue = value;
146     }
147 
InitializeInto()148     T* InitializeInto() DAWN_NO_DISCARD {
149         ASSERT(mValue == Traits::kNullValue);
150         return &mValue;
151     }
152 
153   private:
154     // Friend is needed so that instances of RefBase<U> can call Reference and Release on
155     // RefBase<T>.
156     template <typename U, typename UTraits>
157     friend class RefBase;
158 
Reference(T value)159     static void Reference(T value) {
160         if (value != Traits::kNullValue) {
161             Traits::Reference(value);
162         }
163     }
Release(T value)164     static void Release(T value) {
165         if (value != Traits::kNullValue) {
166             Traits::Release(value);
167         }
168     }
169 
Set(T value)170     void Set(T value) {
171         if (mValue != value) {
172             // Ensure that the new value is referenced before the old is released to prevent any
173             // transitive frees that may affect the new value.
174             Reference(value);
175             Release(mValue);
176             mValue = value;
177         }
178     }
179 
180     T mValue;
181 };
182 
183 #endif  // COMMON_REFBASE_H_
184