/* An optional object. Copyright (C) 2017-2021 Free Software Foundation, Inc. This file is part of GDB. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifndef COMMON_GDB_OPTIONAL_H #define COMMON_GDB_OPTIONAL_H #include "gdbsupport/traits.h" namespace gdb { struct in_place_t { explicit in_place_t () = default; }; constexpr gdb::in_place_t in_place {}; /* This class attempts to be a compatible subset of std::optional, which is slated to be available in C++17. This class optionally holds an object of some type -- by default it is constructed not holding an object, but later the object can be "emplaced". This is similar to using std::unique_ptr, but in-object allocation is guaranteed. Unlike std::optional, we currently only support copy/move construction/assignment of an optional from either exactly optional or T. I.e., we don't support copy/move construction/assignment from optional or U, when U is a type convertible to T. Making that work depending on the definitions of T and U is somewhat complicated, and currently the users of this class don't need it. */ template class optional { public: constexpr optional () : m_dummy () {} template constexpr optional (in_place_t, Args &&... args) : m_item (std::forward (args)...), m_instantiated (true) {} ~optional () { this->reset (); } /* Copy and move constructors. */ optional (const optional &other) { if (other.m_instantiated) this->emplace (other.get ()); } optional (optional &&other) noexcept(std::is_nothrow_move_constructible ()) { if (other.m_instantiated) this->emplace (std::move (other.get ())); } constexpr optional (const T &other) : m_item (other), m_instantiated (true) {} constexpr optional (T &&other) noexcept (std::is_nothrow_move_constructible ()) : m_item (std::move (other)), m_instantiated (true) {} /* Assignment operators. */ optional & operator= (const optional &other) { if (m_instantiated && other.m_instantiated) this->get () = other.get (); else { if (other.m_instantiated) this->emplace (other.get ()); else this->reset (); } return *this; } optional & operator= (optional &&other) noexcept (And, std::is_nothrow_move_assignable> ()) { if (m_instantiated && other.m_instantiated) this->get () = std::move (other.get ()); else { if (other.m_instantiated) this->emplace (std::move (other.get ())); else this->reset (); } return *this; } optional & operator= (const T &other) { if (m_instantiated) this->get () = other; else this->emplace (other); return *this; } optional & operator= (T &&other) noexcept (And, std::is_nothrow_move_assignable> ()) { if (m_instantiated) this->get () = std::move (other); else this->emplace (std::move (other)); return *this; } template T &emplace (Args &&... args) { this->reset (); new (&m_item) T (std::forward(args)...); m_instantiated = true; return this->get (); } /* Observers. */ constexpr const T *operator-> () const { return std::addressof (this->get ()); } T *operator-> () { return std::addressof (this->get ()); } constexpr const T &operator* () const & { return this->get (); } T &operator* () & { return this->get (); } T &&operator* () && { return std::move (this->get ()); } constexpr const T &&operator* () const && { return std::move (this->get ()); } constexpr explicit operator bool () const noexcept { return m_instantiated; } constexpr bool has_value () const noexcept { return m_instantiated; } /* 'reset' is a 'safe' operation with no precondition. */ void reset () noexcept { if (m_instantiated) this->destroy (); } private: /* Destroy the object. */ void destroy () { gdb_assert (m_instantiated); m_instantiated = false; m_item.~T (); } /* The get operations have m_instantiated as a precondition. */ T &get () noexcept { #if defined(_GLIBCXX_DEBUG) gdb_assert (this->has_value ()); #endif return m_item; } constexpr const T &get () const noexcept { #if defined(_GLIBCXX_DEBUG) gdb_assert (this->has_value ()); #endif return m_item; } /* The object. */ union { struct { } m_dummy; T m_item; volatile char dont_use; /* Silences -Wmaybe-uninitialized warning, see PR gcc/80635. */ }; /* True if the object was ever emplaced. */ bool m_instantiated = false; }; } #endif /* COMMON_GDB_OPTIONAL_H */