summaryrefslogtreecommitdiffstats
path: root/src/common/Tub.h
diff options
context:
space:
mode:
Diffstat (limited to 'src/common/Tub.h')
-rw-r--r--src/common/Tub.h287
1 files changed, 287 insertions, 0 deletions
diff --git a/src/common/Tub.h b/src/common/Tub.h
new file mode 100644
index 00000000..73cb1235
--- /dev/null
+++ b/src/common/Tub.h
@@ -0,0 +1,287 @@
+/* Copyright (c) 2010-2015 Stanford University
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR(S) DISCLAIM ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL AUTHORS BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef CEPH_COMMON_TUB_H
+#define CEPH_COMMON_TUB_H
+
+/**
+ * A Tub holds an object that may be uninitialized; it allows the allocation of
+ * memory for objects to be separated from its construction and destruction.
+ * When you initially create a Tub its object is uninitialized (and should not
+ * be used). You can call #construct and #destroy to invoke the constructor and
+ * destructor of the embedded object, and #get or #operator-> will return the
+ * embedded object. The embedded object is automatically destroyed when the Tub
+ * is destroyed (if it was ever constructed in the first place).
+ *
+ * Tubs are useful in situations like the following:
+ * - You want to create an array of objects, but the objects need complex
+ * constructors with multiple arguments.
+ * - You want to create a collection of objects, only some of which will be
+ * used, and you don't want to pay the cost of constructing objects that will
+ * never be used.
+ * - You want automatic destruction of an object but don't want to
+ * heap-allocate the object (as with std::unique_ptr).
+ * - You want a way to return failure from a method without using pointers,
+ * exceptions, or special values (e.g. -1). The Tub gives you a 'maybe'
+ * object; it may be empty if a failure occurred.
+ * - You want a singleton, but don't want to deal with heap-allocating an
+ * object on first use and freeing it later. Instead, just declare your object
+ * in a tub and do:
+ * if (!tub) tub.construct();
+ * - You want optional arguments to a function, but don't want to use pointers
+ * (i.e. use the Tub's boolean to determine that an argument was passed,
+ * rather than checking arg != NULL).
+ *
+ * Tub is CopyConstructible if and only if ElementType is CopyConstructible,
+ * and Tub is Assignable if and only if ElementType is Assignable.
+ *
+ * \tparam ElementType
+ * The type of the object to be stored within the Tub.
+ */
+template<typename ElementType>
+class Tub {
+ public:
+ /// The type of the object to be stored within the Tub.
+ typedef ElementType element_type;
+
+ /**
+ * Default constructor: the object starts off uninitialized.
+ */
+ Tub(): occupied(false) {}
+
+ /**
+ * Construct an occupied Tub, whose contained object is initialized
+ * with a copy of the given object.
+ * \pre
+ * ElementType is CopyConstructible.
+ * \param other
+ * Source of the copy.
+ */
+ Tub(const ElementType& other) // NOLINT
+ : occupied(false) {
+ construct(other);
+ }
+
+ /**
+ * Construct an occupied Tub, whose contained object is initialized
+ * with a move of the given object.
+ * \pre
+ * ElementType is MoveConstructible.
+ * \param other
+ * Source of the move.
+ */
+ Tub(ElementType&& other) // NOLINT
+ : occupied(false) {
+ construct(std::move(other));
+ }
+
+ /**
+ * Copy constructor.
+ * The object will be initialized if and only if the source of the copy is
+ * initialized.
+ * \pre
+ * ElementType is CopyConstructible.
+ * \param other
+ * Source of the copy.
+ */
+ Tub(const Tub<ElementType>& other) // NOLINT
+ : occupied(false) {
+ if (other.occupied) {
+ construct(*other.object); // use ElementType's copy constructor
+ }
+ }
+
+ /**
+ * Move constructor.
+ * The object will be initialized if and only if the source of the move is
+ * initialized.
+ * \pre
+ * ElementType is MoveConstructible.
+ * \param other
+ * Source of the move.
+ */
+ Tub(Tub<ElementType>&& other) // NOLINT
+ : occupied(false) {
+ if (other.occupied)
+ construct(std::move(*other.object)); // use ElementType's copy constructor
+ }
+
+ /**
+ * Destructor: destroy the object if it was initialized.
+ */
+ ~Tub() {
+ destroy();
+ }
+
+ /**
+ * Assignment: destroy current object if initialized, replace with
+ * source. Result will be uninitialized if source is uninitialized.
+ * \pre
+ * ElementType is Assignable.
+ */
+ Tub<ElementType>& operator=(const Tub<ElementType>& other) {
+ if (this != &other) {
+ if (other.occupied) {
+ if (occupied) {
+#if __GNUC__ && __GNUC__ >= 4 && __GNUC_MINOR__ >= 7
+ #pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Warray-bounds"
+#endif
+ *object = *other.object; // use ElementType's assignment
+#if __GNUC__ && __GNUC__ >= 4 && __GNUC_MINOR__ >= 7
+#pragma GCC diagnostic pop
+#endif
+ } else {
+ construct(*other.object);
+ }
+ } else {
+ destroy();
+ }
+ }
+ return *this;
+ }
+
+ /**
+ * Assignment: destroy current object if initialized, replace with
+ * source. Result will be uninitialized if source is uninitialized.
+ * \pre
+ * ElementType is Assignable.
+ */
+ Tub<ElementType>& operator=(Tub<ElementType> &&other) {
+ if (this != &other) {
+ if (other.occupied) {
+ if (occupied)
+ *object = std::move(*other.object);
+ else
+ construct(std::move(*other.object));
+ other.destroy();
+ } else {
+ destroy();
+ }
+ }
+ return *this;
+ }
+
+ /**
+ * Assignment: destroy current object if initialized, replace with
+ * source. Result will be uninitialized if source is uninitialized.
+ * \pre
+ * ElementType is Assignable.
+ */
+ Tub<ElementType>& operator=(ElementType &&elt) {
+ if (occupied) {
+ *object = std::move(elt);
+ } else {
+ construct(std::forward<ElementType>(elt));
+ }
+ return *this;
+ }
+
+ /**
+ * Initialize the object.
+ * If the object was already initialized, it will be destroyed first.
+ * \param args
+ * Arguments to ElementType's constructor.
+ * \return
+ * A pointer to the newly initialized object.
+ * \post
+ * The object is initialized.
+ */
+ template<typename... Args>
+ ElementType* construct(Args&&... args) {
+ destroy();
+ new(object) ElementType(std::forward<Args>(args)...);
+ occupied = true;
+ return object;
+ }
+
+ /**
+ * Destroy the object, leaving the Tub in the same state
+ * as after the no-argument constructor.
+ * If the object was not initialized, this will have no effect.
+ * \post
+ * The object is uninitialized.
+ */
+ void destroy() {
+ if (occupied) {
+ object->~ElementType();
+ occupied = false;
+ }
+ }
+
+ /// See #get().
+ const ElementType& operator*() const {
+ return *get();
+ }
+
+ /// See #get().
+ ElementType& operator*() {
+ return *get();
+ }
+
+ /// See #get().
+ const ElementType* operator->() const {
+ return get();
+ }
+
+ /// See #get().
+ ElementType* operator->() {
+ return get();
+ }
+
+ /**
+ * Return a pointer to the object.
+ * \pre
+ * The object is initialized.
+ */
+ ElementType* get() {
+ if (!occupied)
+ return NULL;
+ return object;
+ }
+
+ /// See #get().
+ const ElementType* get() const {
+ if (!occupied)
+ return NULL;
+ return object;
+ }
+
+ /**
+ * Return whether the object is initialized.
+ */
+ operator bool() const {
+ return occupied;
+ }
+
+ private:
+ /**
+ * A pointer to where the object is, if it is initialized.
+ * This must directly precede #raw in the struct.
+ */
+ ElementType object[0];
+
+ /**
+ * A storage area to back the object while it is initialized.
+ */
+ char raw[sizeof(ElementType)];
+
+ /**
+ * Whether the object is initialized.
+ */
+ bool occupied;
+};
+
+#endif // CEPH_COMMON_TUB_H