userver: userver/utils/not_null.hpp Source File
Loading...
Searching...
No Matches
not_null.hpp
Go to the documentation of this file.
1#pragma once
2
3/// @file userver/utils/not_null.hpp
4/// @brief @copybrief utils::NotNull
5
6#include <functional>
7#include <memory>
8#include <type_traits>
9#include <utility>
10
11#include <userver/compiler/impl/lifetime.hpp>
12#include <userver/utils/assert.hpp>
13
14USERVER_NAMESPACE_BEGIN
15
16namespace utils {
17
18namespace impl {
19
20template <typename T>
21struct IsStdSharedPtr : std::false_type {};
22
23template <typename T>
24struct IsStdSharedPtr<std::shared_ptr<T>> : std::true_type {};
25
26} // namespace impl
27
28/// @ingroup userver_universal userver_containers
29///
30/// @brief Restricts a pointer or smart pointer to only hold non-null values.
31template <typename T>
32class NotNull {
33 static_assert(!std::is_reference_v<T>, "NotNull does not work with references");
34 static_assert(!std::is_const_v<T>, "NotNull does not work with const T");
35
36public:
37 constexpr explicit NotNull() = delete;
38
39 constexpr explicit NotNull(const T& u)
40 : ptr_(u)
41 {
42 UASSERT_MSG(ptr_, "Trying to construct NotNull from null");
43 }
44
45 constexpr explicit NotNull(T&& u)
46 : ptr_(std::move(u))
47 {
48 UASSERT_MSG(ptr_, "Trying to construct NotNull from null");
49 }
50
51 template <typename U, typename = std::enable_if_t<std::is_convertible_v<U, T>>>
52 constexpr explicit NotNull(U&& u)
53 : ptr_(std::forward<U>(u))
54 {
55 UASSERT_MSG(ptr_, "Trying to construct NotNull from null");
56 }
57
58 template <typename U, typename = std::enable_if_t<std::is_convertible_v<U*, T>>>
59 constexpr /*implicit*/ NotNull(U& u)
60 : ptr_(std::addressof(u))
61 {}
62
63 template <typename U, typename = std::enable_if_t<std::is_convertible_v<U, T>>>
64 constexpr NotNull(const NotNull<U>& other)
65 : ptr_(other.GetBase())
66 {
67 UASSERT_MSG(ptr_, "Trying to construct NotNull from null (moved-from) NotNull");
68 }
69
70 template <typename U, typename = std::enable_if_t<std::is_convertible_v<U, T>>>
71 constexpr NotNull(NotNull<U>&& other)
72 : ptr_(std::move(other).GetBase())
73 {
74 UASSERT_MSG(ptr_, "Trying to construct NotNull from null (moved-from) NotNull");
75 }
76
77 constexpr NotNull(std::nullptr_t) = delete;
78
79 NotNull(const NotNull& other) noexcept = default;
80 NotNull(NotNull&& other) noexcept = default;
81
82 NotNull& operator=(const NotNull& other) noexcept = default;
83 NotNull& operator=(NotNull&& other) noexcept = default;
84
85 constexpr NotNull& operator=(std::nullptr_t) = delete;
86
87 constexpr const T& GetBase() const& {
88 UASSERT_MSG(ptr_, "Trying to access a null (moved-from) NotNull");
89 return ptr_;
90 }
91
92 constexpr T&& GetBase() && {
93 UASSERT_MSG(ptr_, "Trying to access a null (moved-from) NotNull");
94 return std::move(ptr_);
95 }
96
97 constexpr /*implicit*/ operator const T&() const& { return GetBase(); }
98
99 constexpr /*implicit*/ operator bool() = delete;
100
101 constexpr decltype(auto) operator->() const& { return GetBase(); }
102
103 constexpr decltype(auto) operator*() const& { return *GetBase(); }
104
105 constexpr decltype(auto) operator->() const& USERVER_IMPL_LIFETIME_BOUND
106 requires(!std::is_trivially_copyable_v<T>) && (!impl::IsStdSharedPtr<T>::value)
107 {
108 return GetBase();
109 }
110
111 constexpr decltype(auto) operator*() const& USERVER_IMPL_LIFETIME_BOUND
112 requires(!std::is_trivially_copyable_v<T>) && (!impl::IsStdSharedPtr<T>::value)
113 {
114 return *GetBase();
115 }
116
117 template <typename U>
118 constexpr bool operator==(const NotNull<U>& other) const& {
119 return GetBase() == other.GetBase();
120 }
121
122 template <typename U>
123 constexpr bool operator!=(const NotNull<U>& other) const& {
124 return GetBase() != other.GetBase();
125 }
126
127private:
128 T ptr_;
129};
130
131/// @ingroup userver_universal
132///
133/// @brief A `std::shared_ptr` that is guaranteed to be not-null.
134/// @see MakeSharedRef
135template <typename U>
136using SharedRef = NotNull<std::shared_ptr<U>>;
137
138/// @ingroup userver_universal
139///
140/// @brief A `std::unique_ptr` that is guaranteed to be not-null.
141/// @see MakeUniqueRef
142template <typename U>
143using UniqueRef = NotNull<std::unique_ptr<U>>;
144
145/// @brief An equivalent of `std::make_shared` for SharedRef.
146template <typename U, typename... Args, typename = std::enable_if_t<std::is_constructible_v<U, Args...>>>
147SharedRef<U> MakeSharedRef(Args&&... args) {
148 return SharedRef<U>{std::make_shared<U>(std::forward<Args>(args)...)};
149}
150
151/// @brief An equivalent of `std::make_unique` for UniqueRef.
152template <typename U, typename... Args, typename = std::enable_if_t<std::is_constructible_v<U, Args...>>>
153UniqueRef<U> MakeUniqueRef(Args&&... args) {
154 return UniqueRef<U>{std::make_unique<U>(std::forward<Args>(args)...)};
155}
156
157} // namespace utils
158
159USERVER_NAMESPACE_END
160
161template <typename T>
162// NOLINTNEXTLINE(cert-dcl58-cpp)
163struct std::hash<USERVER_NAMESPACE::utils::NotNull<T>> : public std::hash<T> {
164 using std::hash<T>::hash;
165
166 auto operator()(const USERVER_NAMESPACE::utils::NotNull<T>& value
167 ) const noexcept(std::is_nothrow_invocable_v<const std::hash<T>&, const T&>) {
168 return this->std::hash<T>::operator()(value.GetBase());
169 }
170};