]>
Commit | Line | Data |
---|---|---|
7c673cae FG |
1 | /* Copyright (c) 2010-2015 Stanford University |
2 | * | |
3 | * Permission to use, copy, modify, and distribute this software for any | |
4 | * purpose with or without fee is hereby granted, provided that the above | |
5 | * copyright notice and this permission notice appear in all copies. | |
6 | * | |
7 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR(S) DISCLAIM ALL WARRANTIES | |
8 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | |
9 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL AUTHORS BE LIABLE FOR | |
10 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | |
11 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | |
12 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | |
13 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |
14 | */ | |
15 | ||
16 | #ifndef CEPH_COMMON_TUB_H | |
17 | #define CEPH_COMMON_TUB_H | |
18 | ||
19 | /** | |
20 | * A Tub holds an object that may be uninitialized; it allows the allocation of | |
21 | * memory for objects to be separated from its construction and destruction. | |
22 | * When you initially create a Tub its object is uninitialized (and should not | |
23 | * be used). You can call #construct and #destroy to invoke the constructor and | |
24 | * destructor of the embedded object, and #get or #operator-> will return the | |
25 | * embedded object. The embedded object is automatically destroyed when the Tub | |
26 | * is destroyed (if it was ever constructed in the first place). | |
27 | * | |
28 | * Tubs are useful in situations like the following: | |
29 | * - You want to create an array of objects, but the objects need complex | |
30 | * constructors with multiple arguments. | |
31 | * - You want to create a collection of objects, only some of which will be | |
32 | * used, and you don't want to pay the cost of constructing objects that will | |
33 | * never be used. | |
34 | * - You want automatic destruction of an object but don't want to | |
35 | * heap-allocate the object (as with std::unique_ptr). | |
36 | * - You want a way to return failure from a method without using pointers, | |
37 | * exceptions, or special values (e.g. -1). The Tub gives you a 'maybe' | |
38 | * object; it may be empty if a failure occurred. | |
39 | * - You want a singleton, but don't want to deal with heap-allocating an | |
40 | * object on first use and freeing it later. Instead, just declare your object | |
41 | * in a tub and do: | |
42 | * if (!tub) tub.construct(); | |
43 | * - You want optional arguments to a function, but don't want to use pointers | |
44 | * (i.e. use the Tub's boolean to determine that an argument was passed, | |
45 | * rather than checking arg != NULL). | |
46 | * | |
47 | * Tub is CopyConstructible if and only if ElementType is CopyConstructible, | |
48 | * and Tub is Assignable if and only if ElementType is Assignable. | |
49 | * | |
50 | * \tparam ElementType | |
51 | * The type of the object to be stored within the Tub. | |
52 | */ | |
53 | template<typename ElementType> | |
54 | class Tub { | |
55 | public: | |
56 | /// The type of the object to be stored within the Tub. | |
57 | typedef ElementType element_type; | |
58 | ||
59 | /** | |
60 | * Default constructor: the object starts off uninitialized. | |
61 | */ | |
62 | Tub(): occupied(false) {} | |
63 | ||
64 | /** | |
65 | * Construct an occupied Tub, whose contained object is initialized | |
66 | * with a copy of the given object. | |
67 | * \pre | |
68 | * ElementType is CopyConstructible. | |
69 | * \param other | |
70 | * Source of the copy. | |
71 | */ | |
72 | Tub(const ElementType& other) // NOLINT | |
73 | : occupied(false) { | |
74 | construct(other); | |
75 | } | |
76 | ||
77 | /** | |
78 | * Construct an occupied Tub, whose contained object is initialized | |
79 | * with a move of the given object. | |
80 | * \pre | |
81 | * ElementType is MoveConstructible. | |
82 | * \param other | |
83 | * Source of the move. | |
84 | */ | |
85 | Tub(ElementType&& other) // NOLINT | |
86 | : occupied(false) { | |
87 | construct(std::move(other)); | |
88 | } | |
89 | ||
90 | /** | |
91 | * Copy constructor. | |
92 | * The object will be initialized if and only if the source of the copy is | |
93 | * initialized. | |
94 | * \pre | |
95 | * ElementType is CopyConstructible. | |
96 | * \param other | |
97 | * Source of the copy. | |
98 | */ | |
99 | Tub(const Tub<ElementType>& other) // NOLINT | |
100 | : occupied(false) { | |
101 | if (other.occupied) { | |
102 | construct(*other.object); // use ElementType's copy constructor | |
103 | } | |
104 | } | |
105 | ||
106 | /** | |
107 | * Move constructor. | |
108 | * The object will be initialized if and only if the source of the move is | |
109 | * initialized. | |
110 | * \pre | |
111 | * ElementType is MoveConstructible. | |
112 | * \param other | |
113 | * Source of the move. | |
114 | */ | |
115 | Tub(Tub<ElementType>&& other) // NOLINT | |
116 | : occupied(false) { | |
117 | if (other.occupied) | |
118 | construct(std::move(*other.object)); // use ElementType's copy constructor | |
119 | } | |
120 | ||
121 | /** | |
122 | * Destructor: destroy the object if it was initialized. | |
123 | */ | |
124 | ~Tub() { | |
125 | destroy(); | |
126 | } | |
127 | ||
128 | /** | |
129 | * Assignment: destroy current object if initialized, replace with | |
130 | * source. Result will be uninitialized if source is uninitialized. | |
131 | * \pre | |
132 | * ElementType is Assignable. | |
133 | */ | |
134 | Tub<ElementType>& operator=(const Tub<ElementType>& other) { | |
135 | if (this != &other) { | |
136 | if (other.occupied) { | |
137 | if (occupied) { | |
138 | #if __GNUC__ && __GNUC__ >= 4 && __GNUC_MINOR__ >= 7 | |
139 | #pragma GCC diagnostic push | |
140 | #pragma GCC diagnostic ignored "-Warray-bounds" | |
141 | #endif | |
142 | *object = *other.object; // use ElementType's assignment | |
143 | #if __GNUC__ && __GNUC__ >= 4 && __GNUC_MINOR__ >= 7 | |
144 | #pragma GCC diagnostic pop | |
145 | #endif | |
146 | } else { | |
147 | construct(*other.object); | |
148 | } | |
149 | } else { | |
150 | destroy(); | |
151 | } | |
152 | } | |
153 | return *this; | |
154 | } | |
155 | ||
156 | /** | |
157 | * Assignment: destroy current object if initialized, replace with | |
158 | * source. Result will be uninitialized if source is uninitialized. | |
159 | * \pre | |
160 | * ElementType is Assignable. | |
161 | */ | |
162 | Tub<ElementType>& operator=(Tub<ElementType> &&other) { | |
163 | if (this != &other) { | |
164 | if (other.occupied) { | |
165 | if (occupied) | |
166 | *object = std::move(*other.object); | |
167 | else | |
168 | construct(std::move(*other.object)); | |
169 | other.destroy(); | |
170 | } else { | |
171 | destroy(); | |
172 | } | |
173 | } | |
174 | return *this; | |
175 | } | |
176 | ||
177 | /** | |
178 | * Assignment: destroy current object if initialized, replace with | |
179 | * source. Result will be uninitialized if source is uninitialized. | |
180 | * \pre | |
181 | * ElementType is Assignable. | |
182 | */ | |
183 | Tub<ElementType>& operator=(ElementType &&elt) { | |
184 | if (occupied) { | |
185 | *object = std::move(elt); | |
186 | } else { | |
187 | construct(std::forward<ElementType>(elt)); | |
188 | } | |
189 | return *this; | |
190 | } | |
191 | ||
192 | /** | |
193 | * Initialize the object. | |
194 | * If the object was already initialized, it will be destroyed first. | |
195 | * \param args | |
196 | * Arguments to ElementType's constructor. | |
197 | * \return | |
198 | * A pointer to the newly initialized object. | |
199 | * \post | |
200 | * The object is initialized. | |
201 | */ | |
202 | template<typename... Args> | |
203 | ElementType* construct(Args&&... args) { | |
204 | destroy(); | |
205 | new(object) ElementType(std::forward<Args>(args)...); | |
206 | occupied = true; | |
207 | return object; | |
208 | } | |
209 | ||
210 | /** | |
211 | * Destroy the object, leaving the Tub in the same state | |
212 | * as after the no-argument constructor. | |
213 | * If the object was not initialized, this will have no effect. | |
214 | * \post | |
215 | * The object is uninitialized. | |
216 | */ | |
217 | void destroy() { | |
218 | if (occupied) { | |
219 | object->~ElementType(); | |
220 | occupied = false; | |
221 | } | |
222 | } | |
223 | ||
224 | /// See #get(). | |
225 | const ElementType& operator*() const { | |
226 | return *get(); | |
227 | } | |
228 | ||
229 | /// See #get(). | |
230 | ElementType& operator*() { | |
231 | return *get(); | |
232 | } | |
233 | ||
234 | /// See #get(). | |
235 | const ElementType* operator->() const { | |
236 | return get(); | |
237 | } | |
238 | ||
239 | /// See #get(). | |
240 | ElementType* operator->() { | |
241 | return get(); | |
242 | } | |
243 | ||
244 | /** | |
245 | * Return a pointer to the object. | |
246 | * \pre | |
247 | * The object is initialized. | |
248 | */ | |
249 | ElementType* get() { | |
250 | if (!occupied) | |
251 | return NULL; | |
252 | return object; | |
253 | } | |
254 | ||
255 | /// See #get(). | |
256 | const ElementType* get() const { | |
257 | if (!occupied) | |
258 | return NULL; | |
259 | return object; | |
260 | } | |
261 | ||
262 | /** | |
263 | * Return whether the object is initialized. | |
264 | */ | |
265 | operator bool() const { | |
266 | return occupied; | |
267 | } | |
268 | ||
269 | private: | |
270 | /** | |
271 | * A pointer to where the object is, if it is initialized. | |
272 | * This must directly precede #raw in the struct. | |
273 | */ | |
274 | ElementType object[0]; | |
275 | ||
276 | /** | |
277 | * A storage area to back the object while it is initialized. | |
278 | */ | |
279 | char raw[sizeof(ElementType)]; | |
280 | ||
281 | /** | |
282 | * Whether the object is initialized. | |
283 | */ | |
284 | bool occupied; | |
285 | }; | |
286 | ||
287 | #endif // CEPH_COMMON_TUB_H |