optional.h 3.03 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138
#ifndef AIRMAP_OPTIONAL_H_
#define AIRMAP_OPTIONAL_H_

#include <iostream>
#include <type_traits>

namespace airmap {

/// Optional manages an optional contained value of type T.
template <typename T>
class Optional {
 public:
  /// Optional initializes a new instance with no contained value.
  Optional() : has_value{false} {
  }

  /// Optional initializes a new instance with 'other'.
  Optional(const Optional& other) : has_value{other.has_value} {
    if (has_value)
      new (&storage.value) T(other.storage.value);
  }

  /// Optional initializes a new instance with 'other'.
  Optional(Optional<T>&& other) : has_value{other.has_value} {
    if (has_value)
      new (&storage.value) T(other.storage.value);
  }

  /// Optional initializes a new instance with 'value'.
  Optional(const T& value) : has_value{true} {
    new (&storage.value) T(value);
  }

  /// Optional initializes a new instance with 'value'.
  Optional(T&& value) : has_value{true} {
    new (&storage.value) T(value);
  }

  /// ~Optional cleans up the instance and calls the destructor
  /// of the contained value if one is set.
  ~Optional() {
    reset();
  }

  /// @cond
  Optional& operator=(const Optional& rhs) {
    if (rhs.has_value)
      set(rhs.storage.value);
    else
      reset();

    return *this;
  }

  Optional& operator=(const T& rhs) {
    set(rhs);
    return *this;
  }

  Optional& operator=(Optional&& rhs) {
    if (rhs.has_value)
      set(rhs.storage.value);
    else
      reset();

    return *this;
  }

  bool operator==(const Optional& rhs) const {
    if (has_value != rhs.has_value)
      return false;

    return has_value && (storage.value == rhs.storage.value);
  }
  /// @endcond

  /// operator bool returns true if this instance contains a value.
  explicit operator bool() const {
    return has_value;
  }

  /// get returns an immutable reference to the contained value.
  /// If no value is contained in this instance, the result of the call is undefined.
  const T& get() const {
    return storage.value;
  }

  /// get returns an immutable reference to the contained value.
  /// If no value is contained in this instance, the result of the call is undefined.
  T& get() {
    return storage.value;
  }

  /// set adjusts the contained value to 'value'.
  void set(const T& value) {
    reset();

    has_value = true;
    new (&storage.value) T(value);
  }

  /// reset frees up any contained value if one is set.
  /// After this call has completed, no value is contained in this Optional instance.
  void reset() {
    if (has_value)
      (&storage.value)->~T();
    has_value = false;
  }

 private:
  bool has_value;
  union Storage {
    Storage() {
    }
    ~Storage() {
    }
    T value;
  } storage;
};

/// operator<< inserts value into out.
template <typename T>
inline std::ostream& operator<<(std::ostream& out, const Optional<T>& value) {
  if (value)
    out << value.get();
  else
    out << "not set";
  return out;
}

/// @cond
template <typename T>
using Required = Optional<T>;
/// @endcond

}  // namespace airmap

#endif  // AIRMAP_OPTIONAL_H_