Nullopt and InPlace should be structs, not enum values
[WebKit-https.git] / Source / WTF / wtf / Optional.h
1 /*
2  * Copyright (C) 2014 Apple Inc. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
14  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
15  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
17  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
19  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
20  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
21  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
22  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
23  * THE POSSIBILITY OF SUCH DAMAGE.
24  */
25
26 #ifndef Optional_h
27 #define Optional_h
28
29 #include <type_traits>
30 #include <wtf/Assertions.h>
31 #include <wtf/StdLibExtras.h>
32
33 // WTF::Optional is a class based on std::optional, described here:
34 // http://www.open-std.org/JTC1/SC22/WG21/docs/papers/2013/n3527.html
35 // If this ends up in a C++ standard, we should replace our implementation with it.
36
37 namespace WTF {
38
39 struct InPlaceTag { };
40 constexpr InPlaceTag InPlace { };
41
42 struct NulloptTag { explicit constexpr NulloptTag(int) { } };
43 constexpr NulloptTag Nullopt { 0 };
44
45 template<typename T>
46 class Optional {
47 public:
48     Optional()
49         : m_isEngaged(false)
50     {
51     }
52
53     Optional(NulloptTag)
54         : m_isEngaged(false)
55     {
56     }
57
58     Optional(const T& value)
59         : m_isEngaged(true)
60     {
61         new (NotNull, &m_value) T(value);
62     }
63
64     Optional(const Optional& other)
65         : m_isEngaged(other.m_isEngaged)
66     {
67         if (m_isEngaged)
68             new (NotNull, &m_value) T(*other.asPtr());
69     }
70
71     Optional(Optional&& other)
72         : m_isEngaged(other.m_isEngaged)
73     {
74         if (m_isEngaged)
75             new (NotNull, &m_value) T(WTFMove(*other.asPtr()));
76     }
77
78     Optional(T&& value)
79         : m_isEngaged(true)
80     {
81         new (NotNull, &m_value) T(WTFMove(value));
82     }
83
84     template<typename... Args>
85     Optional(InPlaceTag, Args&&... args)
86         : m_isEngaged(true)
87     {
88         new (NotNull, &m_value) T(std::forward<Args>(args)...);
89     }
90
91     ~Optional()
92     {
93         destroy();
94     }
95
96     Optional& operator=(NulloptTag)
97     {
98         destroy();
99         return *this;
100     }
101
102     Optional& operator=(const Optional& other)
103     {
104         if (this == &other)
105             return *this;
106
107         destroy();
108         if (other.m_isEngaged) {
109             new (NotNull, &m_value) T(*other.asPtr());
110             m_isEngaged = true;
111         }
112         return *this;
113     }
114
115     Optional& operator=(Optional&& other)
116     {
117         if (this == &other)
118             return *this;
119
120         destroy();
121         if (other.m_isEngaged) {
122             new (NotNull, &m_value) T(WTFMove(*other.asPtr()));
123             m_isEngaged = true;
124         }
125         return *this;
126     }
127
128     template<typename U, class = typename std::enable_if<std::is_same<typename std::remove_reference<U>::type, T>::value>::type>
129     Optional& operator=(U&& u)
130     {
131         destroy();
132         new (NotNull, &m_value) T(std::forward<U>(u));
133         m_isEngaged = true;
134         return *this;
135     }
136
137     explicit operator bool() const { return m_isEngaged; }
138
139     const T* operator->() const
140     {
141         ASSERT(m_isEngaged);
142         return asPtr();
143     }
144
145     T* operator->()
146     {
147         ASSERT(m_isEngaged);
148         return asPtr();
149     }
150
151     const T& operator*() const { return value(); }
152     T& operator*() { return value(); }
153
154     T& value()
155     {
156         ASSERT(m_isEngaged);
157         return *asPtr();
158     }
159
160     const T& value() const
161     {
162         ASSERT(m_isEngaged);
163         return *asPtr();
164     }
165
166     template<typename U>
167     T valueOr(U&& value) const
168     {
169         if (m_isEngaged)
170             return *asPtr();
171
172         return std::forward<U>(value);
173     }
174
175     template<typename U>
176     T valueOrCompute(U callback) const
177     {
178         if (m_isEngaged)
179             return *asPtr();
180
181         return callback();
182     }
183
184 private:
185     const T* asPtr() const { return reinterpret_cast<const T*>(&m_value); }
186     T* asPtr() { return reinterpret_cast<T*>(&m_value); }
187     void destroy()
188     {
189         if (m_isEngaged) {
190             asPtr()->~T();
191             m_isEngaged = false;
192         }
193     }
194
195     bool m_isEngaged;
196     typename std::aligned_storage<sizeof(T), std::alignment_of<T>::value>::type m_value;
197 };
198
199 template<typename T>
200 Optional<typename std::decay<T>::type>
201 makeOptional(T&& value)
202 {
203     return Optional<typename std::decay<T>::type>(std::forward<T>(value));
204 }
205
206 } // namespace WTF
207
208 using WTF::InPlace;
209 using WTF::Nullopt;
210 using WTF::Optional;
211 using WTF::makeOptional;
212
213 #endif // Optional_h