Commit | Line | Data |
---|---|---|
d4b0bb18 TT |
1 | /* An optional object. |
2 | ||
3 | Copyright (C) 2017 Free Software Foundation, Inc. | |
4 | ||
5 | This file is part of GDB. | |
6 | ||
7 | This program is free software; you can redistribute it and/or modify | |
8 | it under the terms of the GNU General Public License as published by | |
9 | the Free Software Foundation; either version 3 of the License, or | |
10 | (at your option) any later version. | |
11 | ||
12 | This program is distributed in the hope that it will be useful, | |
13 | but WITHOUT ANY WARRANTY; without even the implied warranty of | |
14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
15 | GNU General Public License for more details. | |
16 | ||
17 | You should have received a copy of the GNU General Public License | |
18 | along with this program. If not, see <http://www.gnu.org/licenses/>. */ | |
19 | ||
20 | #ifndef GDB_OPTIONAL_H | |
21 | #define GDB_OPTIONAL_H | |
22 | ||
22796e97 PA |
23 | #include "common/traits.h" |
24 | ||
d4b0bb18 TT |
25 | namespace gdb |
26 | { | |
27 | ||
22796e97 PA |
28 | struct in_place_t |
29 | { | |
30 | explicit in_place_t () = default; | |
31 | }; | |
32 | ||
33 | constexpr gdb::in_place_t in_place {}; | |
34 | ||
d4b0bb18 TT |
35 | /* This class attempts to be a compatible subset of std::optional, |
36 | which is slated to be available in C++17. This class optionally | |
37 | holds an object of some type -- by default it is constructed not | |
38 | holding an object, but later the object can be "emplaced". This is | |
39 | similar to using std::unique_ptr, but in-object allocation is | |
22796e97 PA |
40 | guaranteed. |
41 | ||
42 | Unlike std::optional, we currently only support copy/move | |
43 | construction/assignment of an optional<T> from either exactly | |
44 | optional<T> or T. I.e., we don't support copy/move | |
45 | construction/assignment from optional<U> or U, when U is a type | |
46 | convertible to T. Making that work depending on the definitions of | |
47 | T and U is somewhat complicated, and currently the users of this | |
48 | class don't need it. */ | |
49 | ||
d4b0bb18 TT |
50 | template<typename T> |
51 | class optional | |
52 | { | |
53 | public: | |
54 | ||
c053b654 | 55 | constexpr optional () |
22796e97 PA |
56 | : m_dummy () |
57 | {} | |
58 | ||
59 | template<typename... Args> | |
60 | constexpr optional (in_place_t, Args &&... args) | |
61 | : m_item (std::forward<Args> (args)...), | |
62 | m_instantiated (true) | |
63 | {} | |
64 | ||
65 | ~optional () | |
66 | { this->reset (); } | |
67 | ||
68 | /* Copy and move constructors. */ | |
69 | ||
70 | optional (const optional &other) | |
d4b0bb18 | 71 | { |
22796e97 PA |
72 | if (other.m_instantiated) |
73 | this->emplace (other.get ()); | |
d4b0bb18 TT |
74 | } |
75 | ||
22796e97 PA |
76 | optional (optional &&other) |
77 | noexcept(std::is_nothrow_move_constructible<T> ()) | |
78 | { | |
79 | if (other.m_instantiated) | |
80 | this->emplace (std::move (other.get ())); | |
81 | } | |
82 | ||
83 | constexpr optional (const T &other) | |
84 | : m_item (other), | |
85 | m_instantiated (true) | |
86 | {} | |
87 | ||
88 | constexpr optional (T &&other) | |
89 | noexcept (std::is_nothrow_move_constructible<T> ()) | |
90 | : m_item (std::move (other)), | |
91 | m_instantiated (true) | |
92 | {} | |
93 | ||
94 | /* Assignment operators. */ | |
95 | ||
96 | optional & | |
97 | operator= (const optional &other) | |
98 | { | |
99 | if (m_instantiated && other.m_instantiated) | |
100 | this->get () = other.get (); | |
101 | else | |
102 | { | |
103 | if (other.m_instantiated) | |
104 | this->emplace (other.get ()); | |
105 | else | |
106 | this->reset (); | |
107 | } | |
108 | ||
109 | return *this; | |
110 | } | |
111 | ||
112 | optional & | |
113 | operator= (optional &&other) | |
114 | noexcept (And<std::is_nothrow_move_constructible<T>, | |
115 | std::is_nothrow_move_assignable<T>> ()) | |
116 | { | |
117 | if (m_instantiated && other.m_instantiated) | |
118 | this->get () = std::move (other.get ()); | |
119 | else | |
120 | { | |
121 | if (other.m_instantiated) | |
122 | this->emplace (std::move (other.get ())); | |
123 | else | |
124 | this->reset (); | |
125 | } | |
126 | return *this; | |
127 | } | |
128 | ||
129 | optional & | |
130 | operator= (const T &other) | |
d4b0bb18 TT |
131 | { |
132 | if (m_instantiated) | |
22796e97 PA |
133 | this->get () = other; |
134 | else | |
135 | this->emplace (other); | |
136 | return *this; | |
d4b0bb18 TT |
137 | } |
138 | ||
22796e97 PA |
139 | optional & |
140 | operator= (T &&other) | |
141 | noexcept (And<std::is_nothrow_move_constructible<T>, | |
142 | std::is_nothrow_move_assignable<T>> ()) | |
143 | { | |
144 | if (m_instantiated) | |
145 | this->get () = std::move (other); | |
146 | else | |
147 | this->emplace (std::move (other)); | |
148 | return *this; | |
149 | } | |
d4b0bb18 TT |
150 | |
151 | template<typename... Args> | |
22796e97 | 152 | T &emplace (Args &&... args) |
d4b0bb18 | 153 | { |
22796e97 | 154 | this->reset (); |
d4b0bb18 TT |
155 | new (&m_item) T (std::forward<Args>(args)...); |
156 | m_instantiated = true; | |
22796e97 | 157 | return this->get (); |
d4b0bb18 TT |
158 | } |
159 | ||
d194f1fe PA |
160 | /* Observers. */ |
161 | constexpr const T *operator-> () const | |
162 | { return std::addressof (this->get ()); } | |
163 | ||
164 | T *operator-> () | |
165 | { return std::addressof (this->get ()); } | |
166 | ||
167 | constexpr const T &operator* () const & | |
168 | { return this->get (); } | |
169 | ||
170 | T &operator* () & | |
171 | { return this->get (); } | |
172 | ||
173 | T &&operator* () && | |
174 | { return std::move (this->get ()); } | |
175 | ||
176 | constexpr const T &&operator* () const && | |
177 | { return std::move (this->get ()); } | |
178 | ||
179 | constexpr explicit operator bool () const noexcept | |
180 | { return m_instantiated; } | |
181 | ||
182 | constexpr bool has_value () const noexcept | |
183 | { return m_instantiated; } | |
184 | ||
22796e97 PA |
185 | /* 'reset' is a 'safe' operation with no precondition. */ |
186 | void reset () noexcept | |
187 | { | |
188 | if (m_instantiated) | |
189 | this->destroy (); | |
190 | } | |
191 | ||
d4b0bb18 TT |
192 | private: |
193 | ||
194 | /* Destroy the object. */ | |
195 | void destroy () | |
196 | { | |
197 | gdb_assert (m_instantiated); | |
198 | m_instantiated = false; | |
199 | m_item.~T (); | |
200 | } | |
201 | ||
d194f1fe PA |
202 | /* The get operations have m_instantiated as a precondition. */ |
203 | T &get () noexcept { return m_item; } | |
204 | constexpr const T &get () const noexcept { return m_item; } | |
205 | ||
d4b0bb18 TT |
206 | /* The object. */ |
207 | union | |
208 | { | |
209 | struct { } m_dummy; | |
210 | T m_item; | |
211 | }; | |
212 | ||
213 | /* True if the object was ever emplaced. */ | |
22796e97 | 214 | bool m_instantiated = false; |
d4b0bb18 TT |
215 | }; |
216 | ||
217 | } | |
218 | ||
219 | #endif /* GDB_OPTIONAL_H */ |