gdb_optional.h 5.3 KB

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