1 #ifndef optional_hh_INCLUDED 2 #define optional_hh_INCLUDED 3 4 #include "assert.hh" 5 6 #include <utility> 7 8 namespace Kakoune 9 { 10 11 template<typename T> 12 struct Optional 13 { 14 public: OptionalKakoune::Optional15 constexpr Optional() : m_valid{false} {} OptionalKakoune::Optional16 Optional(const T& other) : m_valid{true} { new (&m_value) T(other); } OptionalKakoune::Optional17 Optional(T&& other) : m_valid{true} { new (&m_value) T(std::move(other)); } 18 OptionalKakoune::Optional19 Optional(const Optional& other) 20 : m_valid{other.m_valid} 21 { 22 if (m_valid) 23 new (&m_value) T(other.m_value); 24 } 25 OptionalKakoune::Optional26 Optional(Optional&& other) 27 noexcept(noexcept(new (nullptr) T(std::move(other.m_value)))) 28 : m_valid{other.m_valid} 29 { 30 if (m_valid) 31 new (&m_value) T(std::move(other.m_value)); 32 } 33 operator =Kakoune::Optional34 Optional& operator=(const Optional& other) 35 { 36 destruct_ifn(); 37 if ((m_valid = other.m_valid)) 38 new (&m_value) T(other.m_value); 39 return *this; 40 } 41 operator =Kakoune::Optional42 Optional& operator=(Optional&& other) 43 { 44 destruct_ifn(); 45 if ((m_valid = other.m_valid)) 46 new (&m_value) T(std::move(other.m_value)); 47 return *this; 48 } 49 ~OptionalKakoune::Optional50 ~Optional() { destruct_ifn(); } 51 operator boolKakoune::Optional52 constexpr explicit operator bool() const noexcept { return m_valid; } 53 operator ==Kakoune::Optional54 bool operator==(const Optional& other) const 55 { 56 return m_valid == other.m_valid and 57 (not m_valid or m_value == other.m_value); 58 } 59 operator !=Kakoune::Optional60 bool operator!=(const Optional& other) const { return !(*this == other); } 61 62 template<typename... Args> emplaceKakoune::Optional63 T& emplace(Args&&... args) 64 { 65 destruct_ifn(); 66 new (&m_value) T{std::forward<Args>(args)...}; 67 m_valid = true; 68 return m_value; 69 } 70 operator *Kakoune::Optional71 T& operator*() & 72 { 73 kak_assert(m_valid); 74 return m_value; 75 } 76 operator *Kakoune::Optional77 T&& operator*() && 78 { 79 kak_assert(m_valid); 80 return std::move(m_value); 81 } 82 operator *Kakoune::Optional83 const T& operator*() const & { return *const_cast<Optional&>(*this); } operator *Kakoune::Optional84 const T& operator*() const && { return *const_cast<Optional&>(*this); } 85 operator ->Kakoune::Optional86 T* operator->() 87 { 88 kak_assert(m_valid); 89 return &m_value; 90 } operator ->Kakoune::Optional91 const T* operator->() const { return const_cast<Optional&>(*this).operator->(); } 92 93 template<typename U> struct DecayOptionalImpl { using Type = U; }; 94 template<typename U> struct DecayOptionalImpl<Optional<U>> { using Type = typename DecayOptionalImpl<U>::Type; }; 95 template<typename U> using DecayOptional = typename DecayOptionalImpl<U>::Type; 96 97 template<typename F> mapKakoune::Optional98 auto map(F f) -> Optional<DecayOptional<decltype(f(std::declval<T&&>()))>> 99 { 100 if (not m_valid) 101 return {}; 102 return {f(m_value)}; 103 } 104 105 template<typename U> castKakoune::Optional106 auto cast() const -> Optional<U> 107 { 108 if (not m_valid) 109 return {}; 110 return {(U)m_value}; 111 } 112 113 template<typename U> value_orKakoune::Optional114 T value_or(U&& fallback) const { return m_valid ? m_value : T{std::forward<U>(fallback)}; } 115 116 template<typename U> value_or_computeKakoune::Optional117 T value_or_compute(U&& compute_func) const { return m_valid ? m_value : compute_func(); } 118 resetKakoune::Optional119 void reset() { destruct_ifn(); m_valid = false; } 120 121 private: destruct_ifnKakoune::Optional122 void destruct_ifn() { if (m_valid) m_value.~T(); } 123 124 struct Empty {}; 125 union 126 { 127 Empty m_empty; // disable default construction of value 128 T m_value; 129 }; 130 bool m_valid; 131 }; 132 133 } 134 135 #endif // optional_hh_INCLUDED 136