]> git.proxmox.com Git - ceph.git/blob - ceph/src/common/Tub.h
buildsys: switch source download to quincy
[ceph.git] / ceph / src / common / Tub.h
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