iceoryx2
C++ Language Bindings
Loading...
Searching...
No Matches
optional.hpp
Go to the documentation of this file.
1// Copyright (c) 2025 Contributors to the Eclipse Foundation
2//
3// See the NOTICE file(s) distributed with this work for additional
4// information regarding copyright ownership.
5//
6// This program and the accompanying materials are made available under the
7// terms of the Apache Software License 2.0 which is available at
8// https://www.apache.org/licenses/LICENSE-2.0, or the MIT license
9// which is available at https://opensource.org/licenses/MIT.
10//
11// SPDX-License-Identifier: Apache-2.0 OR MIT
12
13#ifndef IOX2_INCLUDE_GUARD_BB_STL_OPTIONAL_HPP
14#define IOX2_INCLUDE_GUARD_BB_STL_OPTIONAL_HPP
15
19
20#include <memory>
21#include <type_traits>
22#include <utility>
23
24namespace iox2 {
25namespace bb {
26namespace stl {
27
29template <class T>
30class Optional;
31
32namespace detail {
33// An empty literal type used as a tag to NulloptT's constructor.
35 explicit NulloptTConstructorTag() noexcept = default;
36};
37} // namespace detail
38
40struct NulloptT {
41 constexpr explicit NulloptT(detail::NulloptTConstructorTag /* unused */) noexcept {
42 }
43};
45
46namespace detail {
49template <typename T>
51 private:
52 union {
54 std::remove_cv_t<T> m_u_value;
55 };
56 bool m_is_empty = true;
57
58 public:
59 constexpr OptionalValueHolder() noexcept
60 : m_u_null() {
61 }
62 constexpr explicit OptionalValueHolder(T const& value)
63 : m_u_value(value)
64 , m_is_empty(false) {
65 }
66 constexpr explicit OptionalValueHolder(T&& value)
67 : m_u_value(std::move(value))
68 , m_is_empty(false) {
69 }
72 if (!rhs.m_is_empty) {
73 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-union-access), access guarded by if
74 set(rhs.m_u_value);
75 }
76 }
77 constexpr OptionalValueHolder(OptionalValueHolder&& rhs) noexcept(std::is_nothrow_move_constructible<T>::value)
79 if (!rhs.m_is_empty) {
80 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-union-access), access guarded by if
81 set(std::move(rhs.m_u_value));
82 }
83 }
84
88
89 constexpr auto operator=(OptionalValueHolder const& rhs) -> OptionalValueHolder& {
90 if (this != &rhs) {
91 if (rhs.m_is_empty) {
92 reset();
93 } else {
94 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-union-access), access guarded by if
95 set(rhs.m_u_value);
96 }
97 }
98 return *this;
99 }
100
101 constexpr auto operator=(OptionalValueHolder&& rhs) noexcept(std::is_nothrow_move_assignable<T>::value)
103 if (this != &rhs) {
104 if (rhs.m_is_empty) {
105 reset();
106 } else {
107 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-union-access), access guarded by if
108 set(std::move(rhs.m_u_value));
109 }
110 }
111 return *this;
112 }
113
114 constexpr auto is_empty() const -> bool {
115 return m_is_empty;
116 }
117
118 constexpr auto set(T const& value) -> void {
119 if (m_is_empty) {
120 m_is_empty = false;
121 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-union-access), access guarded by if
122 new (&m_u_value) T { value };
123 } else {
124 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-union-access), access guarded by if
125 m_u_value = value;
126 }
127 }
128
129 constexpr auto set(T&& value) -> void {
130 if (m_is_empty) {
131 m_is_empty = false;
132 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-union-access), access guarded by if
133 new (&m_u_value) T { std::move(value) };
134 } else {
135 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-union-access), access guarded by if
136 m_u_value = std::move(value);
137 }
138 }
139
140 constexpr auto unchecked_get() & -> T& {
141 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-union-access), unchecked access guarded by caller
142 return m_u_value;
143 }
144
145 constexpr auto unchecked_get() const& -> T const& {
146 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-union-access), unchecked access guarded by caller
147 return m_u_value;
148 }
149
150 constexpr auto unchecked_get() && -> T&& {
151 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-union-access), unchecked access guarded by caller
152 return std::move(m_u_value);
153 }
154
155 constexpr auto unchecked_get() const&& -> T const&& {
156 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-union-access), unchecked access guarded by caller
157 return std::move(m_u_value);
158 }
159
160 constexpr auto reset() -> void {
161 if (!m_is_empty) {
162 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-union-access), access guarded by if
163 m_u_value.~T();
164 m_is_empty = true;
165 }
166 }
167};
168} // namespace detail
169
171template <typename T>
172class Optional {
173 private:
175
176 public:
177 // NOLINTNEXTLINE(readability-identifier-naming), as specified in ISO14882:2017 [optional]
178 using value_type = T;
179
180 // constructors
181 constexpr Optional() noexcept
183 }
184
185 constexpr Optional(const Optional& rhs) = default;
186 constexpr Optional(Optional&& rhs) = default;
187
188 // NOLINTNEXTLINE(hicpp-explicit-conversions), as specified in ISO14882:2017 [optional]
189 constexpr Optional(const NulloptT& /* unused */) noexcept {
190 }
191
192 template <typename U = std::remove_cv_t<T>,
193 std::enable_if_t<std::is_constructible<T, U>::value && !std::is_same<std::decay_t<U>, Optional<T>>::value
194 && !std::is_same<std::decay_t<U>, NulloptT>::value,
195 bool> = true>
196 // NOLINTNEXTLINE(hicpp-explicit-conversions), as specified in ISO14882:2017 [optional]
197 constexpr Optional(U&& value)
198 : m_value(std::forward<U>(value)) {
199 }
200
201 template <typename... Args>
202 constexpr auto emplace(Args&&... args) noexcept -> T& {
203 if (!m_value.is_empty()) {
204 m_value.reset();
205 }
206
207 m_value.set(std::forward<Args>(args)...);
208
209 return value();
210 }
211
212 // destructor
214
215 // assignment
216 constexpr auto operator=(NulloptT& /* unused */) noexcept -> Optional& {
217 reset();
218 return *this;
219 }
220
221 constexpr auto operator=(const Optional& rhs) -> Optional& = default;
222 constexpr auto operator=(Optional&& rhs) -> Optional& = default;
223
224 // observers
225 constexpr auto operator->() const noexcept -> const T* {
226 return m_value.is_empty() ? nullptr : std::addressof(m_value.unchecked_get());
227 }
228
229 constexpr auto operator->() noexcept -> T* {
230 return m_value.is_empty() ? nullptr : std::addressof(m_value.unchecked_get());
231 }
232
233 constexpr auto operator*() const& noexcept -> const T& {
234 IOX2_ENFORCE_INTERNAL(bb::detail::SourceLocation::current(),
235 has_value(),
236 "Optional::has_value()",
237 "Trying to access the value on an empty Optional!");
238 return m_value.unchecked_get();
239 }
240
241 constexpr auto operator*() & noexcept -> T& {
242 IOX2_ENFORCE_INTERNAL(bb::detail::SourceLocation::current(),
243 has_value(),
244 "Optional::has_value()",
245 "Trying to access the value on an empty Optional!");
246 return m_value.unchecked_get();
247 }
248
249 constexpr auto operator*() && noexcept -> T&& {
250 IOX2_ENFORCE_INTERNAL(bb::detail::SourceLocation::current(),
251 has_value(),
252 "Optional::has_value()",
253 "Trying to access the value on an empty Optional!");
254 return std::move(m_value).unchecked_get();
255 }
256
257 constexpr auto operator*() const&& noexcept -> const T&& {
258 IOX2_ENFORCE_INTERNAL(bb::detail::SourceLocation::current(),
259 has_value(),
260 "Optional::has_value()",
261 "Trying to access the value on an empty Optional!");
262 return std::move(m_value).unchecked_get();
263 }
264
265 constexpr explicit operator bool() const noexcept {
266 return !m_value.is_empty();
267 }
268
269 constexpr auto has_value() const noexcept -> bool {
270 return !m_value.is_empty();
271 }
272
273 constexpr auto
274 value(const bb::detail::SourceLocation location = bb::detail::SourceLocation::current()) const& -> const T& {
276 location, has_value(), "Optional::has_value()", "Trying to access the value on an empty Optional!");
277 return m_value.unchecked_get();
278 }
279
280 constexpr auto value(const bb::detail::SourceLocation location = bb::detail::SourceLocation::current()) & -> T& {
282 location, has_value(), "Optional::has_value()", "Trying to access the value on an empty Optional!");
283 return m_value.unchecked_get();
284 }
285
286 constexpr auto value(const bb::detail::SourceLocation location = bb::detail::SourceLocation::current()) && -> T&& {
288 location, has_value(), "Optional::has_value()", "Trying to access the value on an empty Optional!");
289 return std::move(m_value).unchecked_get();
290 }
291
292 constexpr auto
293 value(const bb::detail::SourceLocation location = bb::detail::SourceLocation::current()) const&& -> const T&& {
295 location, has_value(), "Optional::has_value()", "Trying to access the value on an empty Optional!");
296 return std::move(m_value).unchecked_get();
297 }
298
299 template <class U = std::remove_cv_t<T>>
300 constexpr auto value_or(U&& fallback) const& -> T {
301 if (m_value.is_empty()) {
302 return std::forward<U>(fallback);
303 } else {
304 return m_value.unchecked_get();
305 }
306 }
307
308 template <class U = std::remove_cv_t<T>>
309 constexpr auto value_or(U&& fallback) && -> T {
310 if (m_value.is_empty()) {
311 return std::forward<U>(fallback);
312 } else {
313 return std::move(m_value).unchecked_get();
314 }
315 }
316
317 // modifiers
318 constexpr auto reset() noexcept -> void {
319 m_value.reset();
320 }
321
322 private:
323 friend auto operator==(const Optional<T>& lhs, NulloptT /* unused */) noexcept -> bool {
324 return !lhs.has_value();
325 }
326 friend auto operator==(NulloptT /* unused */, const Optional<T>& rhs) noexcept -> bool {
327 return !rhs.has_value();
328 }
329 friend auto operator!=(const Optional<T>& lhs, NulloptT /* unused */) noexcept -> bool {
330 return lhs.has_value();
331 }
332 friend auto operator!=(NulloptT /* unused */, const Optional<T>& rhs) noexcept -> bool {
333 return rhs.has_value();
334 }
335};
336
337template <typename T>
338auto operator==(const Optional<T>& lhs, const Optional<T>& rhs) noexcept -> bool {
339 if (lhs.has_value() != rhs.has_value()) {
340 return false;
341 } else if (lhs.has_value()) { // NOTE due to the previous check, lhs and rhs 'has_value' is equal
342 return lhs.value() == rhs.value();
343 } else {
344 return true;
345 }
346}
347
348template <typename T>
349auto operator!=(const Optional<T>& lhs, const Optional<T>& rhs) noexcept -> bool {
350 if (lhs.has_value() != rhs.has_value()) {
351 return true;
352 } else if (lhs.has_value()) { // NOTE due to the previous check, lhs and rhs 'has_value' is equal
353 return lhs.value() != rhs.value();
354 } else {
355 return false;
356 }
357}
358
359#if __cplusplus >= 201703L
360template <class T>
362#endif
363
364} // namespace stl
365} // namespace bb
366} // namespace iox2
367
368#endif // IOX2_INCLUDE_GUARD_BB_STL_OPTIONAL_HPP
#define IOX2_ENFORCE_INTERNAL(location, condition, stringified_condition, message)
Only for internal usage.
#define IOX2_CONSTEXPR_DTOR
A drop-in replacement for C++17 std::optional.
Definition optional.hpp:172
constexpr auto has_value() const noexcept -> bool
Definition optional.hpp:269
constexpr Optional(const NulloptT &) noexcept
Definition optional.hpp:189
constexpr auto value_or(U &&fallback) const &-> T
Definition optional.hpp:300
constexpr auto operator=(NulloptT &) noexcept -> Optional &
Definition optional.hpp:216
constexpr auto value(const bb::detail::SourceLocation location=bb::detail::SourceLocation::current()) &-> T &
Definition optional.hpp:280
friend auto operator!=(NulloptT, const Optional< T > &rhs) noexcept -> bool
Definition optional.hpp:332
constexpr auto value(const bb::detail::SourceLocation location=bb::detail::SourceLocation::current()) const &-> const T &
Definition optional.hpp:274
constexpr auto operator*() &noexcept -> T &
Definition optional.hpp:241
constexpr auto operator=(Optional &&rhs) -> Optional &=default
constexpr auto operator->() noexcept -> T *
Definition optional.hpp:229
constexpr Optional(const Optional &rhs)=default
constexpr auto reset() noexcept -> void
Definition optional.hpp:318
IOX2_CONSTEXPR_DTOR ~Optional()=default
constexpr auto operator*() const &noexcept -> const T &
Definition optional.hpp:233
friend auto operator==(const Optional< T > &lhs, NulloptT) noexcept -> bool
Definition optional.hpp:323
constexpr Optional() noexcept
Definition optional.hpp:181
constexpr auto emplace(Args &&... args) noexcept -> T &
Definition optional.hpp:202
friend auto operator!=(const Optional< T > &lhs, NulloptT) noexcept -> bool
Definition optional.hpp:329
constexpr auto value(const bb::detail::SourceLocation location=bb::detail::SourceLocation::current()) &&-> T &&
Definition optional.hpp:286
constexpr Optional(Optional &&rhs)=default
constexpr auto value_or(U &&fallback) &&-> T
Definition optional.hpp:309
constexpr auto operator->() const noexcept -> const T *
Definition optional.hpp:225
friend auto operator==(NulloptT, const Optional< T > &rhs) noexcept -> bool
Definition optional.hpp:326
constexpr auto operator*() &&noexcept -> T &&
Definition optional.hpp:249
constexpr auto operator=(const Optional &rhs) -> Optional &=default
constexpr Optional(U &&value)
Definition optional.hpp:197
constexpr auto operator*() const &&noexcept -> const T &&
Definition optional.hpp:257
constexpr auto value(const bb::detail::SourceLocation location=bb::detail::SourceLocation::current()) const &&-> const T &&
Definition optional.hpp:293
IOX2_CONSTEXPR_DTOR ~OptionalValueHolder()
Definition optional.hpp:85
constexpr auto unchecked_get() const &-> T const &
Definition optional.hpp:145
constexpr auto unchecked_get() &&-> T &&
Definition optional.hpp:150
constexpr OptionalValueHolder(OptionalValueHolder &&rhs) noexcept(std::is_nothrow_move_constructible< T >::value)
Definition optional.hpp:77
constexpr OptionalValueHolder(OptionalValueHolder const &rhs)
Definition optional.hpp:70
constexpr OptionalValueHolder(T const &value)
Definition optional.hpp:62
constexpr auto set(T const &value) -> void
Definition optional.hpp:118
constexpr auto set(T &&value) -> void
Definition optional.hpp:129
constexpr auto is_empty() const -> bool
Definition optional.hpp:114
constexpr auto unchecked_get() const &&-> T const &&
Definition optional.hpp:155
constexpr auto operator=(OptionalValueHolder const &rhs) -> OptionalValueHolder &
Definition optional.hpp:89
constexpr auto unchecked_get() &-> T &
Definition optional.hpp:140
constexpr auto operator=(OptionalValueHolder &&rhs) noexcept(std::is_nothrow_move_assignable< T >::value) -> OptionalValueHolder &
Definition optional.hpp:101
auto operator!=(const Optional< T > &lhs, const Optional< T > &rhs) noexcept -> bool
Definition optional.hpp:349
constexpr NulloptT NULLOPT
Definition optional.hpp:44
auto operator==(const Optional< T > &lhs, const Optional< T > &rhs) noexcept -> bool
Definition optional.hpp:338
iox2::bb::variation::Optional< T > Optional
Definition optional.hpp:25
A drop-in replacement for C++17 std::nullopt_t for use with Optional.
Definition optional.hpp:40
constexpr NulloptT(detail::NulloptTConstructorTag) noexcept
Definition optional.hpp:41