Remove some checks of .empty()
[external/binutils.git] / gdb / gdbsupport / gdb_optional.h
1 /* An optional object.
2
3    Copyright (C) 2017-2019 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 COMMON_GDB_OPTIONAL_H
21 #define COMMON_GDB_OPTIONAL_H
22
23 #include "gdbsupport/traits.h"
24
25 namespace gdb
26 {
27
28 struct in_place_t
29 {
30   explicit in_place_t () = default;
31 };
32
33 constexpr gdb::in_place_t in_place {};
34
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
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
50 template<typename T>
51 class optional
52 {
53 public:
54
55   constexpr optional ()
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)
71   {
72     if (other.m_instantiated)
73       this->emplace (other.get ());
74   }
75
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)
131   {
132     if (m_instantiated)
133       this->get () = other;
134     else
135       this->emplace (other);
136     return *this;
137   }
138
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   }
150
151   template<typename... Args>
152   T &emplace (Args &&... args)
153   {
154     this->reset ();
155     new (&m_item) T (std::forward<Args>(args)...);
156     m_instantiated = true;
157     return this->get ();
158   }
159
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
185   /* 'reset' is a 'safe' operation with no precondition.  */
186   void reset () noexcept
187   {
188     if (m_instantiated)
189       this->destroy ();
190   }
191
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
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
206   /* The object.  */
207   union
208   {
209     struct { } m_dummy;
210     T m_item;
211   };
212
213   /* True if the object was ever emplaced.  */
214   bool m_instantiated = false;
215 };
216
217 }
218
219 #endif /* COMMON_GDB_OPTIONAL_H */