1 /* An optional object. 2 3 Copyright (C) 2017-2020 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 volatile char dont_use; /* Silences -Wmaybe-uninitialized warning, see 212 PR gcc/80635. */ 213 }; 214 215 /* True if the object was ever emplaced. */ 216 bool m_instantiated = false; 217 }; 218 219 } 220 221 #endif /* COMMON_GDB_OPTIONAL_H */ 222