azure-core
Loading...
Searching...
No Matches
nullable.hpp
Go to the documentation of this file.
1// Copyright (c) Microsoft Corporation.
2// Licensed under the MIT License.
3
9#pragma once
10
12
13#include <new> // for placement new
14#include <type_traits>
15#include <utility> // for swap and move
16
17namespace Azure {
18namespace _detail {
19 struct NontrivialEmptyType final
20 {
21 constexpr NontrivialEmptyType() noexcept {}
22 };
23} // namespace _detail
24
30template <class T> class Nullable final {
31 union
32 {
33 _detail::NontrivialEmptyType m_disengaged; // due to constexpr rules for the default constructor
34 T m_value;
35 };
36
37 bool m_hasValue;
38
39public:
44 constexpr Nullable() : m_disengaged{}, m_hasValue(false) {}
45
51 constexpr Nullable(T initialValue) noexcept(std::is_nothrow_move_constructible<T>::value)
52 : m_value(std::move(initialValue)), m_hasValue(true)
53 {
54 }
55
61 Nullable(const Nullable& other) noexcept(std::is_nothrow_copy_constructible<T>::value)
62 : m_disengaged{}, m_hasValue(other.m_hasValue)
63 {
64 if (m_hasValue)
65 {
66 ::new (static_cast<void*>(&m_value)) T(other.m_value);
67 }
68 }
69
75 Nullable(Nullable&& other) noexcept(std::is_nothrow_move_constructible<T>::value)
76 : m_disengaged{}, m_hasValue(other.m_hasValue)
77 {
78 if (m_hasValue)
79 {
80 ::new (static_cast<void*>(&m_value)) T(std::move(other.m_value));
81 }
82 }
83
90 {
91 if (m_hasValue)
92 {
93 m_value.~T();
94 }
95 }
96
101 void Reset() noexcept(std::is_nothrow_destructible<T>::value) /* enforces termination */
102 {
103 if (m_hasValue)
104 {
105 m_hasValue = false;
106 m_value.~T();
107 }
108 }
109
115 // this assumes that swap can't throw if T is nothrow move constructible because
116 // is_nothrow_swappable is added in C++17
117 void Swap(Nullable& other) noexcept(std::is_nothrow_move_constructible<T>::value)
118 {
119 if (m_hasValue)
120 {
121 if (other.m_hasValue)
122 {
123 using std::swap;
124 swap(m_value, other.m_value);
125 }
126 else
127 {
128 ::new (static_cast<void*>(&other.m_value)) T(std::move(m_value)); // throws
129 other.m_hasValue = true;
130 Reset();
131 }
132 }
133 else if (other.m_hasValue)
134 {
135 ::new (static_cast<void*>(&m_value)) T(std::move(other.m_value)); // throws
136 m_hasValue = true;
137 other.Reset();
138 }
139 }
140
147 friend void swap(Nullable& lhs, Nullable& rhs) noexcept(
148 std::is_nothrow_move_constructible<T>::value)
149 {
150 lhs.Swap(rhs);
151 }
152
155 {
156 // this copy and swap may be inefficient for some Ts but
157 // it's a lot less code than the standard implementation :)
158 Nullable{other}.Swap(*this);
159 return *this;
160 }
161
163 Nullable& operator=(Nullable&& other) noexcept(std::is_nothrow_move_constructible<T>::value)
164 {
165 // this move and swap may be inefficient for some Ts but
166 // it's a lot less code than the standard implementation :)
167 Nullable{std::move(other)}.Swap(*this);
168 return *this;
169 }
170
178 template <
179 class U = T,
180 typename std::enable_if<
181 !std::is_same<
182 Nullable,
183 typename std::remove_cv<typename std::remove_reference<U>::type>::type>::
184 value // Avoid repeated assignment
185 && !(
186 std::is_scalar<U>::value
187 && std::is_same<T, typename std::decay<U>::type>::value) // Avoid repeated
188 // assignment of
189 // equivalent scalar
190 // types
191 && std::is_constructible<T, U>::value // Ensure the type is constructible
192 && std::is_assignable<T&, U>::value, // Ensure the type is assignable
193 int>::type
194 = 0>
195 Nullable& operator=(U&& other) noexcept(
196 std::is_nothrow_constructible<T, U>::value&& std::is_nothrow_assignable<T&, U>::value)
197 {
198 if (m_hasValue)
199 {
200 m_value = std::forward<U>(other);
201 }
202 else
203 {
204 ::new (static_cast<void*>(&m_value)) T(std::forward<U>(other));
205 m_hasValue = true;
206 }
207 return *this;
208 }
209
216 template <class... U>
217 T& Emplace(U&&... Args) noexcept(std::is_nothrow_constructible<T, U...>::value)
218 {
219 Reset();
220 ::new (static_cast<void*>(&m_value)) T(std::forward<U>(Args)...);
221 m_hasValue = true;
222 return m_value;
223 }
224
230 bool HasValue() const noexcept { return m_hasValue; }
231
236 const T& Value() const& noexcept
237 {
238 AZURE_ASSERT_MSG(m_hasValue, "Empty Nullable, check HasValue() first.");
239
240 return m_value;
241 }
242
247 T& Value() & noexcept
248 {
249 AZURE_ASSERT_MSG(m_hasValue, "Empty Nullable, check HasValue() first.");
250
251 return m_value;
252 }
253
258 T&& Value() && noexcept
259 {
260 AZURE_ASSERT_MSG(m_hasValue, "Empty Nullable, check HasValue() first.");
261
262 return std::move(m_value);
263 }
264
265 // observers
266
271 constexpr explicit operator bool() const noexcept { return HasValue(); }
272
281 constexpr const T* operator->() const { return std::addressof(m_value); }
282
291 constexpr T* operator->() { return std::addressof(m_value); }
292
301 constexpr const T& operator*() const& { return m_value; }
302
311 constexpr T& operator*() & { return m_value; }
312
321 constexpr T&& operator*() && { return std::move(m_value); }
322
331 constexpr const T&& operator*() const&& { return std::move(m_value); }
332
338 template <
339 class U = T,
340 typename std::enable_if<
341 std::is_convertible<const T&, typename std::remove_cv<T>::type>::value
342 && std::is_convertible<U, T>::value,
343 int>::type
344 = 0>
345 constexpr typename std::remove_cv<T>::type ValueOr(U&& other) const&
346 {
347 if (m_hasValue)
348 {
349 return m_value;
350 }
351
352 return static_cast<typename std::remove_cv<T>::type>(std::forward<U>(other));
353 }
354
360 template <
361 class U = T,
362 typename std::enable_if<
363 std::is_convertible<T, typename std::remove_cv<T>::type>::value
364 && std::is_convertible<U, T>::value,
365 int>::type
366 = 0>
367 constexpr typename std::remove_cv<T>::type ValueOr(U&& other) &&
368 {
369 if (m_hasValue)
370 {
371 return std::move(m_value);
372 }
373
374 return static_cast<typename std::remove_cv<T>::type>(std::forward<U>(other));
375 }
376};
377} // namespace Azure
Provide assert macros to use with pre-conditions.
#define AZURE_ASSERT_MSG(exp, msg)
Azure specific assert macro with message.
Definition azure_assert.hpp:53
Manages an optional contained value, i.e. a value that may or may not be present.
Definition nullable.hpp:30
constexpr T * operator->()
Accesses the contained value.
Definition nullable.hpp:291
Nullable & operator=(Nullable &&other) noexcept(std::is_nothrow_move_constructible< T >::value)
Assignment operator with move semantics.
Definition nullable.hpp:163
Nullable(const Nullable &other) noexcept(std::is_nothrow_copy_constructible< T >::value)
Constructs a Nullable by copying another Nullable.
Definition nullable.hpp:61
friend void swap(Nullable &lhs, Nullable &rhs) noexcept(std::is_nothrow_move_constructible< T >::value)
Invokes Azure::Nullable::Swap while having a lowercase name that satisfies swappable requirements (se...
Definition nullable.hpp:147
constexpr std::remove_cv< T >::type ValueOr(U &&other) const &
Get the contained value, returns other if value is absent.
Definition nullable.hpp:345
constexpr T && operator*() &&
Accesses the contained value.
Definition nullable.hpp:321
Nullable & operator=(const Nullable &other)
Assignment operator.
Definition nullable.hpp:154
constexpr const T * operator->() const
Accesses the contained value.
Definition nullable.hpp:281
const T & Value() const &noexcept
Get the contained value.
Definition nullable.hpp:236
T & Value() &noexcept
Get the contained value reference.
Definition nullable.hpp:247
bool HasValue() const noexcept
Check whether a value is contained.
Definition nullable.hpp:230
Nullable(Nullable &&other) noexcept(std::is_nothrow_move_constructible< T >::value)
Constructs a Nullable by moving in another Nullable.
Definition nullable.hpp:75
constexpr Nullable(T initialValue) noexcept(std::is_nothrow_move_constructible< T >::value)
Constructs a Nullable having an initialValue.
Definition nullable.hpp:51
constexpr Nullable()
Constructs a Nullable that represents the absence of value.
Definition nullable.hpp:44
void Swap(Nullable &other) noexcept(std::is_nothrow_move_constructible< T >::value)
Exchanges the contents.
Definition nullable.hpp:117
T & Emplace(U &&... Args) noexcept(std::is_nothrow_constructible< T, U... >::value)
Construct the contained value in-place.
Definition nullable.hpp:217
constexpr const T && operator*() const &&
Accesses the contained value.
Definition nullable.hpp:331
constexpr T & operator*() &
Accesses the contained value.
Definition nullable.hpp:311
~Nullable()
Destructs the Nullable, calling the destructor for the contained value if there is one.
Definition nullable.hpp:89
constexpr std::remove_cv< T >::type ValueOr(U &&other) &&
Get the contained value, returns other if value is absent.
Definition nullable.hpp:367
constexpr const T & operator*() const &
Accesses the contained value.
Definition nullable.hpp:301
void Reset() noexcept(std::is_nothrow_destructible< T >::value)
Destructs the contained value, if there is one.
Definition nullable.hpp:101
T && Value() &&noexcept
Get the contained value (as rvalue reference).
Definition nullable.hpp:258
Compute the hash value for the input binary data, using SHA256, SHA384 and SHA512.
Definition azure_assert.hpp:57