Google Cloud C++ Client  0.4.0
C++ Client Library for Google Cloud Platform
optional.h
Go to the documentation of this file.
1 // Copyright 2018 Google LLC
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 #ifndef GOOGLE_CLOUD_CPP_GOOGLE_CLOUD_OPTIONAL_H_
16 #define GOOGLE_CLOUD_CPP_GOOGLE_CLOUD_OPTIONAL_H_
17 
18 #include "google/cloud/internal/throw_delegate.h"
19 #include <type_traits>
20 #include <utility>
21 
22 namespace google {
23 namespace cloud {
24 inline namespace GOOGLE_CLOUD_CPP_NS {
25 /**
26  * A poor's man version of std::optional<T>.
27  *
28  * This project needs to support C++11 and C++14, so std::optional<> is not
29  * available. We cannot use Abseil either, see #232 for the reasons.
30  * So we implement a very minimal "optional" class that documents the intent
31  * and we will remove it when possible.
32  *
33  * @tparam T the type of the optional value.
34  *
35  * @warning this is not a drop-in replacement for std::optional<>, it has at
36  * least the following defects:
37  * - it raises std::logic_error instead of std::bad_optional_access.
38  * - emplace() is a very simple version.
39  * - It does not have the full complement of assignment operators and
40  * constructors required by the standard.
41  * - It lacks comparison operators.
42  * - No nullopt_t.
43  * - No std::swap(), std::make_optional(), or std::hash().
44  *
45  * @warning Some of the member functions have a (commented-out) `constexpr`
46  * qualifier. The spec requires them to be `constexpr` functions, but the spec
47  * assumes C++14 (or newer) semantics for non-const constexpr member functions,
48  * and we often compile with C++11 semantics.
49  *
50  * TODO(#687) - replace with absl::optional<> or std::optional<> when possible.
51  */
52 template <typename T>
53 class optional {
54  public:
55  optional() : buffer_{}, has_value_(false) {}
56  explicit optional(T const& x) : has_value_(true) {
57  new (reinterpret_cast<T*>(&buffer_)) T(x);
58  }
59  explicit optional(T&& x) noexcept : has_value_(true) {
60  new (reinterpret_cast<T*>(&buffer_)) T(std::move(x));
61  }
62  optional(optional<T>&& rhs) noexcept : has_value_(rhs.has_value_) {
63  if (has_value_) {
64  new (reinterpret_cast<T*>(&buffer_)) T(std::move(*rhs));
65  }
66  }
67  optional(optional<T> const& rhs) : has_value_(rhs.has_value_) {
68  if (has_value_) {
69  new (reinterpret_cast<T*>(&buffer_)) T(*rhs);
70  }
71  }
72  ~optional() { reset(); }
73 
74  optional& operator=(optional<T> const& rhs) {
75  // There may be shorter ways to express this, but this is fairly readable,
76  // and should be reasonably efficient. Note that we must avoid destructing
77  // the destination and/or default initializing it unless really needed.
78  if (!has_value_) {
79  if (!rhs.has_value_) {
80  return *this;
81  }
82  new (reinterpret_cast<T*>(&buffer_)) T(*rhs);
83  has_value_ = true;
84  return *this;
85  }
86  if (!rhs.has_value_) {
87  reset();
88  return *this;
89  }
90  **this = *rhs;
91  has_value_ = true;
92  return *this;
93  }
94 
95  optional& operator=(optional<T>&& rhs) noexcept {
96  // There may be shorter ways to express this, but this is fairly readable,
97  // and should be reasonably efficient. Note that we must avoid destructing
98  // the destination and/or default initializing it unless really needed.
99  if (!has_value_) {
100  if (!rhs.has_value_) {
101  return *this;
102  }
103  new (reinterpret_cast<T*>(&buffer_)) T(std::move(*rhs));
104  has_value_ = true;
105  return *this;
106  }
107  if (!rhs.has_value_) {
108  reset();
109  return *this;
110  }
111  **this = std::move(*rhs);
112  has_value_ = true;
113  return *this;
114  }
115 
116  // Disable this assignment if U==optional<T>. Well, really if U is a
117  // cv-qualified version of optional<T>, so we need to apply std::decay<> to
118  // it first.
119  template <typename U = T>
120  typename std::enable_if<
121  not std::is_same<optional, typename std::decay<U>::type>::value,
122  optional>::type&
123  operator=(U&& rhs) {
124  // There may be shorter ways to express this, but this is fairly readable,
125  // and should be reasonably efficient. Note that we must avoid destructing
126  // the destination and/or default initializing it unless really needed.
127  if (!has_value_) {
128  new (reinterpret_cast<T*>(&buffer_)) T(std::forward<U>(rhs));
129  has_value_ = true;
130  return *this;
131  }
132  **this = std::forward<U>(rhs);
133  has_value_ = true;
134  return *this;
135  }
136 
137  constexpr T const* operator->() const {
138  return reinterpret_cast<T const*>(&buffer_);
139  }
140  /*constexpr*/ T* operator->() { return reinterpret_cast<T*>(&buffer_); }
141  constexpr T const& operator*() const& {
142  return *reinterpret_cast<T const*>(&buffer_);
143  }
144  /*constexpr*/ T& operator*() & { return *reinterpret_cast<T*>(&buffer_); }
145  // Kind of useless, but the spec requires it.
146 #if GOOGLE_CLOUD_CPP_HAVE_CONST_REF_REF
147  /*constexpr*/ T const&& operator*() const&& {
148  return std::move(*reinterpret_cast<T const*>(&buffer_));
149  }
150 #endif // GOOGLE_CLOUD_CPP_HAVE_CONST_REF_REF
151  /*constexpr*/ T&& operator*() && {
152  return std::move(*reinterpret_cast<T*>(&buffer_));
153  }
154 
155  /*constexpr*/ T& value() & {
156  check_access();
157  return **this;
158  }
159  /*constexpr*/ T const& value() const& {
160  check_access();
161  return **this;
162  }
163  /*constexpr*/ T&& value() && {
164  check_access();
165  return std::move(**this);
166  }
167  // Kind of useless, but the spec requires it.
168  /*constexpr*/ T const&& value() const&& {
169  check_access();
170  return **this;
171  }
172 
173  template <typename U>
174  constexpr T value_or(U&& default_value) const& {
175  return bool(*this) ? **this
176  : static_cast<T>(std::forward<U>(default_value));
177  }
178 
179  template <typename U>
180  /*constexpr*/ T value_or(U&& default_value) && {
181  return bool(*this) ? std::move(**this)
182  : static_cast<T>(std::forward<U>(default_value));
183  }
184 
185  operator bool() const { return has_value_; }
186  bool has_value() const { return has_value_; }
187 
188  void reset() {
189  if (has_value_) {
190  reinterpret_cast<T*>(&buffer_)->~T();
191  has_value_ = false;
192  }
193  }
194  T& emplace(T&& value) {
195  reset();
196  new (reinterpret_cast<T*>(&buffer_)) T(std::move(value));
197  has_value_ = true;
198  return **this;
199  }
200 
201  bool operator==(optional const& rhs) const {
202  if (has_value() != rhs.has_value()) {
203  return false;
204  }
205  if (!has_value()) {
206  return true;
207  }
208  return **this == *rhs;
209  }
210 
211  bool operator!=(optional const& rhs) const {
212  return std::rel_ops::operator!=(*this, rhs);
213  }
214 
215  bool operator<(optional const& rhs) const {
216  if (has_value()) {
217  if (!rhs.has_value()) {
218  return false;
219  }
220  // Both have a value, compare them
221  return **this < *rhs;
222  }
223  // If both do not have a value, then they are equal, so this returns false.
224  // If rhs has a value then it compares larger than *this because *this does
225  // not have a value.
226  return rhs.has_value();
227  }
228 
229  bool operator>(optional const& rhs) const {
230  return std::rel_ops::operator>(*this, rhs);
231  }
232 
233  bool operator>=(optional const& rhs) const {
234  return std::rel_ops::operator>=(*this, rhs);
235  }
236 
237  bool operator<=(optional const& rhs) const {
238  return std::rel_ops::operator<=(*this, rhs);
239  }
240 
241  private:
242  void check_access() const {
243  if (has_value_) {
244  return;
245  }
246  google::cloud::internal::ThrowLogicError("access unset optional");
247  }
248 
249  // We use std::aligned_storage<T> because T may not have a default
250  // constructor, if we used 'T' here we could not default initialize this class
251  // either.
252  using aligned_storage_t = std::aligned_storage<sizeof(T), alignof(T)>;
253  typename aligned_storage_t::type buffer_;
254  bool has_value_;
255 };
256 
257 template <typename T>
259  return optional<T>(std::forward<T>(t));
260 }
261 
262 } // namespace GOOGLE_CLOUD_CPP_NS
263 } // namespace cloud
264 } // namespace google
265 
266 #endif // GOOGLE_CLOUD_CPP_GOOGLE_CLOUD_OPTIONAL_H_
constexpr T const & operator*() const &
Definition: optional.h:141
#define GOOGLE_CLOUD_CPP_NS
Definition: version.h:24
bool operator==(optional const &rhs) const
Definition: optional.h:201
Contains all the Google Cloud C++ Library APIs.
Definition: iam_bindings.cc:21
bool operator>(optional const &rhs) const
Definition: optional.h:229
bool operator<=(optional const &rhs) const
Definition: optional.h:237
T & emplace(T &&value)
Definition: optional.h:194
std::enable_if< not std::is_same< optional, typename std::decay< U >::type >::value, optional >::type & operator=(U &&rhs)
Definition: optional.h:123
A poor&#39;s man version of std::optional<T>.
Definition: optional.h:53
optional(T &&x) noexcept
Definition: optional.h:59
T const && value() const &&
Definition: optional.h:168
optional & operator=(optional< T > &&rhs) noexcept
Definition: optional.h:95
bool operator>=(optional const &rhs) const
Definition: optional.h:233
T value_or(U &&default_value) &&
Definition: optional.h:180
optional< T > make_optional(T &&t)
Definition: optional.h:258
bool operator!=(optional const &rhs) const
Definition: optional.h:211
optional & operator=(optional< T > const &rhs)
Definition: optional.h:74
optional(optional< T > const &rhs)
Definition: optional.h:67
T const & value() const &
Definition: optional.h:159
constexpr T const * operator->() const
Definition: optional.h:137
constexpr T value_or(U &&default_value) const &
Definition: optional.h:174
bool operator<(optional const &rhs) const
Definition: optional.h:215
optional(optional< T > &&rhs) noexcept
Definition: optional.h:62