|
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ |
|
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */ |
|
3 /* This Source Code Form is subject to the terms of the Mozilla Public |
|
4 * License, v. 2.0. If a copy of the MPL was not distributed with this |
|
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
|
6 |
|
7 /* A number of structures to simplify scope-based RAII management. */ |
|
8 |
|
9 #ifndef mozilla_Scoped_h |
|
10 #define mozilla_Scoped_h |
|
11 |
|
12 /* |
|
13 * Resource Acquisition Is Initialization is a programming idiom used |
|
14 * to write robust code that is able to deallocate resources properly, |
|
15 * even in presence of execution errors or exceptions that need to be |
|
16 * propagated. The Scoped* classes defined in this header perform the |
|
17 * deallocation of the resource they hold once program execution |
|
18 * reaches the end of the scope for which they have been defined. |
|
19 * |
|
20 * This header provides the following RAII classes: |
|
21 * |
|
22 * - |ScopedFreePtr| - a container for a pointer, that automatically calls |
|
23 * |free()| at the end of the scope; |
|
24 * - |ScopedDeletePtr| - a container for a pointer, that automatically calls |
|
25 * |delete| at the end of the scope; |
|
26 * - |ScopedDeleteArray| - a container for a pointer to an array, that |
|
27 * automatically calls |delete[]| at the end of the scope. |
|
28 * |
|
29 * The general scenario for each of the RAII classes is the following: |
|
30 * |
|
31 * ScopedClass foo(create_value()); |
|
32 * // ... In this scope, |foo| is defined. Use |foo.get()| or |foo.rwget()| |
|
33 * to access the value. |
|
34 * // ... In case of |return| or |throw|, |foo| is deallocated automatically. |
|
35 * // ... If |foo| needs to be returned or stored, use |foo.forget()| |
|
36 * |
|
37 * Note that the RAII classes defined in this header do _not_ perform any form |
|
38 * of reference-counting or garbage-collection. These classes have exactly two |
|
39 * behaviors: |
|
40 * |
|
41 * - if |forget()| has not been called, the resource is always deallocated at |
|
42 * the end of the scope; |
|
43 * - if |forget()| has been called, any control on the resource is unbound |
|
44 * and the resource is not deallocated by the class. |
|
45 * |
|
46 * Extension: |
|
47 * |
|
48 * In addition, this header provides class |Scoped| and macros |SCOPED_TEMPLATE| |
|
49 * and |MOZ_TYPE_SPECIFIC_SCOPED_POINTER_TEMPLATE| to simplify the definition |
|
50 * of RAII classes for other scenarios. These macros have been used to |
|
51 * automatically close file descriptors/file handles when reaching the end of |
|
52 * the scope, graphics contexts, etc. |
|
53 */ |
|
54 |
|
55 #include "mozilla/Assertions.h" |
|
56 #include "mozilla/Attributes.h" |
|
57 #include "mozilla/GuardObjects.h" |
|
58 #include "mozilla/Move.h" |
|
59 #include "mozilla/NullPtr.h" |
|
60 |
|
61 namespace mozilla { |
|
62 |
|
63 /* |
|
64 * Scoped is a helper to create RAII wrappers |
|
65 * Type argument |Traits| is expected to have the following structure: |
|
66 * |
|
67 * struct Traits { |
|
68 * // Define the type of the value stored in the wrapper |
|
69 * typedef value_type type; |
|
70 * // Returns the value corresponding to the uninitialized or freed state |
|
71 * const static type empty(); |
|
72 * // Release resources corresponding to the wrapped value |
|
73 * // This function is responsible for not releasing an |empty| value |
|
74 * const static void release(type); |
|
75 * } |
|
76 */ |
|
77 template<typename Traits> |
|
78 class Scoped |
|
79 { |
|
80 public: |
|
81 typedef typename Traits::type Resource; |
|
82 |
|
83 explicit Scoped(MOZ_GUARD_OBJECT_NOTIFIER_ONLY_PARAM) |
|
84 : value(Traits::empty()) |
|
85 { |
|
86 MOZ_GUARD_OBJECT_NOTIFIER_INIT; |
|
87 } |
|
88 |
|
89 explicit Scoped(const Resource& v |
|
90 MOZ_GUARD_OBJECT_NOTIFIER_PARAM) |
|
91 : value(v) |
|
92 { |
|
93 MOZ_GUARD_OBJECT_NOTIFIER_INIT; |
|
94 } |
|
95 |
|
96 /* Move constructor. */ |
|
97 explicit Scoped(Scoped&& v |
|
98 MOZ_GUARD_OBJECT_NOTIFIER_PARAM) |
|
99 : value(Move(v.value)) |
|
100 { |
|
101 MOZ_GUARD_OBJECT_NOTIFIER_INIT; |
|
102 v.value = Traits::empty(); |
|
103 } |
|
104 |
|
105 ~Scoped() { |
|
106 Traits::release(value); |
|
107 } |
|
108 |
|
109 // Constant getter |
|
110 operator const Resource&() const { return value; } |
|
111 const Resource& operator->() const { return value; } |
|
112 const Resource& get() const { return value; } |
|
113 // Non-constant getter. |
|
114 Resource& rwget() { return value; } |
|
115 |
|
116 /* |
|
117 * Forget the resource. |
|
118 * |
|
119 * Once |forget| has been called, the |Scoped| is neutralized, i.e. it will |
|
120 * have no effect at destruction (unless it is reset to another resource by |
|
121 * |operator=|). |
|
122 * |
|
123 * @return The original resource. |
|
124 */ |
|
125 Resource forget() { |
|
126 Resource tmp = value; |
|
127 value = Traits::empty(); |
|
128 return tmp; |
|
129 } |
|
130 |
|
131 /* |
|
132 * Perform immediate clean-up of this |Scoped|. |
|
133 * |
|
134 * If this |Scoped| is currently empty, this method has no effect. |
|
135 */ |
|
136 void dispose() { |
|
137 Traits::release(value); |
|
138 value = Traits::empty(); |
|
139 } |
|
140 |
|
141 bool operator==(const Resource& other) const { |
|
142 return value == other; |
|
143 } |
|
144 |
|
145 /* |
|
146 * Replace the resource with another resource. |
|
147 * |
|
148 * Calling |operator=| has the side-effect of triggering clean-up. If you do |
|
149 * not want to trigger clean-up, you should first invoke |forget|. |
|
150 * |
|
151 * @return this |
|
152 */ |
|
153 Scoped& operator=(const Resource& other) { |
|
154 return reset(other); |
|
155 } |
|
156 Scoped& reset(const Resource& other) { |
|
157 Traits::release(value); |
|
158 value = other; |
|
159 return *this; |
|
160 } |
|
161 |
|
162 /* Move assignment operator. */ |
|
163 Scoped& operator=(Scoped&& rhs) { |
|
164 MOZ_ASSERT(&rhs != this, "self-move-assignment not allowed"); |
|
165 this->~Scoped(); |
|
166 new(this) Scoped(Move(rhs)); |
|
167 return *this; |
|
168 } |
|
169 |
|
170 private: |
|
171 explicit Scoped(const Scoped& value) MOZ_DELETE; |
|
172 Scoped& operator=(const Scoped& value) MOZ_DELETE; |
|
173 |
|
174 private: |
|
175 Resource value; |
|
176 MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER |
|
177 }; |
|
178 |
|
179 /* |
|
180 * SCOPED_TEMPLATE defines a templated class derived from Scoped |
|
181 * This allows to implement templates such as ScopedFreePtr. |
|
182 * |
|
183 * @param name The name of the class to define. |
|
184 * @param Traits A struct implementing clean-up. See the implementations |
|
185 * for more details. |
|
186 */ |
|
187 #define SCOPED_TEMPLATE(name, Traits) \ |
|
188 template<typename Type> \ |
|
189 struct name : public mozilla::Scoped<Traits<Type> > \ |
|
190 { \ |
|
191 typedef mozilla::Scoped<Traits<Type> > Super; \ |
|
192 typedef typename Super::Resource Resource; \ |
|
193 name& operator=(Resource rhs) { \ |
|
194 Super::operator=(rhs); \ |
|
195 return *this; \ |
|
196 } \ |
|
197 name& operator=(name&& rhs) { \ |
|
198 Super::operator=(Move(rhs)); \ |
|
199 return *this; \ |
|
200 } \ |
|
201 explicit name(MOZ_GUARD_OBJECT_NOTIFIER_ONLY_PARAM) \ |
|
202 : Super(MOZ_GUARD_OBJECT_NOTIFIER_ONLY_PARAM_TO_PARENT) \ |
|
203 {} \ |
|
204 explicit name(Resource rhs \ |
|
205 MOZ_GUARD_OBJECT_NOTIFIER_PARAM) \ |
|
206 : Super(rhs \ |
|
207 MOZ_GUARD_OBJECT_NOTIFIER_PARAM_TO_PARENT) \ |
|
208 {} \ |
|
209 explicit name(name&& rhs \ |
|
210 MOZ_GUARD_OBJECT_NOTIFIER_PARAM) \ |
|
211 : Super(Move(rhs) \ |
|
212 MOZ_GUARD_OBJECT_NOTIFIER_PARAM_TO_PARENT) \ |
|
213 {} \ |
|
214 private: \ |
|
215 explicit name(name&) MOZ_DELETE; \ |
|
216 name& operator=(name&) MOZ_DELETE; \ |
|
217 }; |
|
218 |
|
219 /* |
|
220 * ScopedFreePtr is a RAII wrapper for pointers that need to be free()d. |
|
221 * |
|
222 * struct S { ... }; |
|
223 * ScopedFreePtr<S> foo = malloc(sizeof(S)); |
|
224 * ScopedFreePtr<char> bar = strdup(str); |
|
225 */ |
|
226 template<typename T> |
|
227 struct ScopedFreePtrTraits |
|
228 { |
|
229 typedef T* type; |
|
230 static T* empty() { return nullptr; } |
|
231 static void release(T* ptr) { free(ptr); } |
|
232 }; |
|
233 SCOPED_TEMPLATE(ScopedFreePtr, ScopedFreePtrTraits) |
|
234 |
|
235 /* |
|
236 * ScopedDeletePtr is a RAII wrapper for pointers that need to be deleted. |
|
237 * |
|
238 * struct S { ... }; |
|
239 * ScopedDeletePtr<S> foo = new S(); |
|
240 */ |
|
241 template<typename T> |
|
242 struct ScopedDeletePtrTraits : public ScopedFreePtrTraits<T> |
|
243 { |
|
244 static void release(T* ptr) { delete ptr; } |
|
245 }; |
|
246 SCOPED_TEMPLATE(ScopedDeletePtr, ScopedDeletePtrTraits) |
|
247 |
|
248 /* |
|
249 * ScopedDeleteArray is a RAII wrapper for pointers that need to be delete[]ed. |
|
250 * |
|
251 * struct S { ... }; |
|
252 * ScopedDeleteArray<S> foo = new S[42]; |
|
253 */ |
|
254 template<typename T> |
|
255 struct ScopedDeleteArrayTraits : public ScopedFreePtrTraits<T> |
|
256 { |
|
257 static void release(T* ptr) { delete [] ptr; } |
|
258 }; |
|
259 SCOPED_TEMPLATE(ScopedDeleteArray, ScopedDeleteArrayTraits) |
|
260 |
|
261 /* |
|
262 * MOZ_TYPE_SPECIFIC_SCOPED_POINTER_TEMPLATE makes it easy to create scoped |
|
263 * pointers for types with custom deleters; just overload |
|
264 * TypeSpecificDelete(T*) in the same namespace as T to call the deleter for |
|
265 * type T. |
|
266 * |
|
267 * @param name The name of the class to define. |
|
268 * @param Type A struct implementing clean-up. See the implementations |
|
269 * for more details. |
|
270 * *param Deleter The function that is used to delete/destroy/free a |
|
271 * non-null value of Type*. |
|
272 * |
|
273 * Example: |
|
274 * |
|
275 * MOZ_TYPE_SPECIFIC_SCOPED_POINTER_TEMPLATE(ScopedPRFileDesc, PRFileDesc, \ |
|
276 * PR_Close) |
|
277 * ... |
|
278 * { |
|
279 * ScopedPRFileDesc file(PR_OpenFile(...)); |
|
280 * ... |
|
281 * } // file is closed with PR_Close here |
|
282 */ |
|
283 #define MOZ_TYPE_SPECIFIC_SCOPED_POINTER_TEMPLATE(name, Type, Deleter) \ |
|
284 template <> inline void TypeSpecificDelete(Type * value) { Deleter(value); } \ |
|
285 typedef ::mozilla::TypeSpecificScopedPointer<Type> name; |
|
286 |
|
287 template <typename T> void TypeSpecificDelete(T * value); |
|
288 |
|
289 template <typename T> |
|
290 struct TypeSpecificScopedPointerTraits |
|
291 { |
|
292 typedef T* type; |
|
293 const static type empty() { return nullptr; } |
|
294 const static void release(type value) |
|
295 { |
|
296 if (value) |
|
297 TypeSpecificDelete(value); |
|
298 } |
|
299 }; |
|
300 |
|
301 SCOPED_TEMPLATE(TypeSpecificScopedPointer, TypeSpecificScopedPointerTraits) |
|
302 |
|
303 } /* namespace mozilla */ |
|
304 |
|
305 #endif /* mozilla_Scoped_h */ |