1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
|
#pragma once
#include <optional>
namespace NYT {
////////////////////////////////////////////////////////////////////////////////
//! Convenience sentinel traits for structural types (integers, enums, pointers).
/*!
* Usage: |TSentinelOptional<int, TValueSentinel<-1>>|
*/
template <auto V>
struct TValueSentinel
{
static constexpr auto Sentinel = V;
};
////////////////////////////////////////////////////////////////////////////////
//! A compact alternative to |std::optional<T>| that uses a sentinel value
//! to represent the null state rather than an additional boolean field.
/*!
* The null state is represented by |TSentinel::Sentinel| of type |T|.
* The stored representation is exactly one |T|, so:
* sizeof(TSentinelOptional<T, TSentinel>) == sizeof(T)
* alignof(TSentinelOptional<T, TSentinel>) == alignof(T)
*
* Whenever |T| is trivially copyable the class is also trivially copyable,
* which guarantees that |std::atomic<TSentinelOptional<T, TSentinel>>| is lock-free
* if and only if |std::atomic<T>| is lock-free.
*
* The interface is a drop-in replacement for |std::optional<T>|.
*
* |TSentinel| must provide a |static constexpr T Sentinel| member.
* For structural types, use |TValueSentinel<V>| as a convenience.
*/
template <class T, class TSentinel>
class TSentinelOptional
{
public:
using value_type = T;
//! Constructs a null optional (stores the sentinel value).
constexpr TSentinelOptional() = default;
//! Constructs a null optional.
constexpr TSentinelOptional(std::nullopt_t) noexcept;
//! Constructs an optional holding |value|.
/*!
* \note The behavior is undefined if |value == TSentinel::Sentinel|.
*/
constexpr TSentinelOptional(T value) noexcept;
//! Converts from |std::optional<T>|; nullopt maps to the null state.
constexpr TSentinelOptional(std::optional<T> opt) noexcept;
constexpr TSentinelOptional(const TSentinelOptional&) = default;
constexpr TSentinelOptional(TSentinelOptional&&) = default;
constexpr TSentinelOptional& operator=(const TSentinelOptional&) = default;
constexpr TSentinelOptional& operator=(TSentinelOptional&&) = default;
//! Resets to null.
constexpr TSentinelOptional& operator=(std::nullopt_t) noexcept;
//! Assigns |value|.
/*!
* \note The behavior is undefined if |value == TSentinel::Sentinel|.
*/
constexpr TSentinelOptional& operator=(T value) noexcept;
//! Returns |true| iff the optional holds a value (i.e., the stored value differs from the sentinel).
[[nodiscard]] constexpr bool has_value() const noexcept;
//! Returns |true| iff the optional holds a value.
constexpr explicit operator bool() const noexcept;
//! Returns a reference to the held value; behavior is undefined if null.
constexpr T& operator*() noexcept;
constexpr const T& operator*() const noexcept;
//! Returns a pointer to the held value; behavior is undefined if null.
constexpr T* operator->() noexcept;
constexpr const T* operator->() const noexcept;
//! Returns a reference to the held value; aborts if null.
T& value() noexcept;
const T& value() const noexcept;
//! Returns the held value if present, otherwise |default_value|.
constexpr T value_or(T default_value) const noexcept;
//! Resets to null (equivalent to assigning |std::nullopt|).
constexpr void reset() noexcept;
//! Converts to |std::optional<T>|; the null state maps to nullopt.
constexpr operator std::optional<T>() const noexcept;
constexpr bool operator==(const TSentinelOptional& other) const = default;
[[nodiscard]] constexpr bool operator==(std::nullopt_t) const noexcept;
private:
T Value_ = TSentinel::Sentinel;
};
////////////////////////////////////////////////////////////////////////////////
} // namespace NYT
#define SENTINEL_OPTIONAL_INL_H_
#include "sentinel_optional-inl.h"
#undef SENTINEL_OPTIONAL_INL_H_
|