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