aboutsummaryrefslogtreecommitdiff
path: root/option.h
diff options
context:
space:
mode:
Diffstat (limited to 'option.h')
-rw-r--r--option.h130
1 files changed, 130 insertions, 0 deletions
diff --git a/option.h b/option.h
new file mode 100644
index 0000000..b9ebe2f
--- /dev/null
+++ b/option.h
@@ -0,0 +1,130 @@
+#include <cassert>
+#include <new> // placement new
+#include <type_traits>
+#include <utility> // move
+
+namespace option {
+namespace impl {
+/// Definition of std::is_trivially_destructible_v for older c++ std versions.
+template <typename T>
+constexpr bool is_trivially_destructible_v =
+ std::is_trivially_destructible<T>::value;
+
+/// Definition of std::enable_if_t for older c++ std versions.
+template <bool Cond, typename T = bool>
+using enable_if_t = typename std::enable_if<Cond, T>::type;
+} // namespace impl
+
+/// The NONE type.
+struct none {};
+
+// The OPTION type.
+template <typename T>
+struct option {
+ static_assert(!std::is_reference<T>::value, "T must not be a reference type");
+
+ // -- CONSTRUCTOR - NONE -----------------------------------------------------
+
+ constexpr option() = default;
+ constexpr option(none) : option() {}
+
+ // -- CONSTRUCTOR - VALUE ----------------------------------------------------
+
+ constexpr option(const option& op) : m_has_value{op.has_value()} {
+ if (op.has_value()) {
+ // Copy construct from inner VALUE of OP.
+ new (m_value) T(op.value());
+ }
+ }
+
+ constexpr option(option&& op) : m_has_value{op.has_value()} {
+ if (op.m_has_value) {
+ // Move construct from inner VALUE of OP.
+ new (m_value) T(std::move(op.take()));
+ }
+ }
+
+ template <typename U = T>
+ constexpr option(T&& val) : m_has_value{true} {
+ new (m_value) T(std::move(val));
+ }
+
+ // -- DESTRUCTOR -------------------------------------------------------------
+
+ ~option() {
+ reset();
+ }
+
+ // -- MODIFIER ---------------------------------------------------------------
+
+ template <typename... Params>
+ constexpr T& emplace(Params&&... params) {
+ reset();
+ new (m_value) T(std::forward<Params>(params)...);
+ m_has_value = true;
+ return value();
+ }
+
+ // -- OPERATOR ---------------------------------------------------------------
+
+ /// Conversion to BOOL, true iff option holds a VALUE.
+ ///
+ /// Marked as explicit to disable implicit conversion of option objects to
+ /// bool in the following case:
+ ///
+ /// if (opt1 == opt2) {
+ /// ...
+ /// }
+ constexpr explicit operator bool() const {
+ return has_value();
+ }
+
+ // -- ACCESSOR ---------------------------------------------------------------
+
+ constexpr bool has_value() const {
+ return m_has_value;
+ }
+
+ constexpr T& value() & {
+ assert(m_has_value);
+ return *reinterpret_cast<T*>(m_value);
+ }
+
+ constexpr const T& value() const& {
+ assert(m_has_value);
+ return *reinterpret_cast<const T*>(m_value);
+ }
+
+ constexpr T value() && {
+ return take();
+ }
+
+ constexpr T take() {
+ assert(m_has_value);
+ T val = std::move(value());
+ reset();
+ return val;
+ }
+
+ // -- INTERNAL ---------------------------------------------------------------
+
+ private:
+ template <typename U = T,
+ impl::enable_if_t<!impl::is_trivially_destructible_v<U>> = true>
+ constexpr void reset() {
+ if (m_has_value) {
+ value().~T();
+ m_has_value = false;
+ }
+ }
+
+ template <typename U = T,
+ impl::enable_if_t<impl::is_trivially_destructible_v<U>> = true>
+ constexpr void reset() {
+ m_has_value = false;
+ }
+
+ alignas(T) char m_value[sizeof(T)];
+ bool m_has_value{false};
+};
+} // namespace option