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/utils/assert.hpp>
12
13USERVER_NAMESPACE_BEGIN
14
15namespace utils {
16
17/// @ingroup userver_universal userver_containers
18///
19/// @brief Restricts a pointer or smart pointer to only hold non-null values.
20template <typename T>
21class NotNull {
22 static_assert(!std::is_reference_v<T>, "NotNull does not work with references");
23 static_assert(!std::is_const_v<T>);
24
25public:
26 constexpr explicit NotNull() = delete;
27
28 constexpr explicit NotNull(const T& u)
29 : ptr_(u)
30 {
31 UASSERT_MSG(ptr_, "Trying to construct NotNull from null");
32 }
33
34 constexpr explicit NotNull(T&& u)
35 : ptr_(std::move(u))
36 {
37 UASSERT_MSG(ptr_, "Trying to construct NotNull from null");
38 }
39
40 template <typename U, typename = std::enable_if_t<std::is_convertible_v<U, T>>>
41 constexpr explicit NotNull(U&& u)
42 : ptr_(std::forward<U>(u))
43 {
44 UASSERT_MSG(ptr_, "Trying to construct NotNull from null");
45 }
46
47 template <typename U, typename = std::enable_if_t<std::is_convertible_v<U*, T>>>
48 constexpr /*implicit*/ NotNull(U& u)
49 : ptr_(std::addressof(u))
50 {}
51
52 template <typename U, typename = std::enable_if_t<std::is_convertible_v<U, T>>>
53 constexpr NotNull(const NotNull<U>& other)
54 : ptr_(other.GetBase())
55 {
56 UASSERT_MSG(ptr_, "Trying to construct NotNull from null (moved-from) NotNull");
57 }
58
59 template <typename U, typename = std::enable_if_t<std::is_convertible_v<U, T>>>
60 constexpr NotNull(NotNull<U>&& other)
61 : ptr_(std::move(other).GetBase())
62 {
63 UASSERT_MSG(ptr_, "Trying to construct NotNull from null (moved-from) NotNull");
64 }
65
66 constexpr NotNull(std::nullptr_t) = delete;
67
68 NotNull(const NotNull& other) noexcept = default;
69 NotNull(NotNull&& other) noexcept = default;
70
71 NotNull& operator=(const NotNull& other) noexcept = default;
72 NotNull& operator=(NotNull&& other) noexcept = default;
73
74 constexpr NotNull& operator=(std::nullptr_t) = delete;
75
76 constexpr const T& GetBase() const& {
77 UASSERT_MSG(ptr_, "Trying to access a null (moved-from) NotNull");
78 return ptr_;
79 }
80
81 constexpr T&& GetBase() && {
82 UASSERT_MSG(ptr_, "Trying to access a null (moved-from) NotNull");
83 return std::move(ptr_);
84 }
85
86 constexpr /*implicit*/ operator const T&() const& { return GetBase(); }
87
88 constexpr /*implicit*/ operator bool() = delete;
89
90 constexpr decltype(auto) operator->() const& { return GetBase(); }
91
92 constexpr decltype(auto) operator*() const& { return *GetBase(); }
93
94 template <typename U>
95 constexpr bool operator==(const NotNull<U>& other) const& {
96 return GetBase() == other.GetBase();
97 }
98
99 template <typename U>
100 constexpr bool operator!=(const NotNull<U>& other) const& {
101 return GetBase() != other.GetBase();
102 }
103
104private:
105 T ptr_;
106};
107
108/// @ingroup userver_universal
109///
110/// @brief A `std::shared_ptr` that is guaranteed to be not-null.
111/// @see MakeSharedRef
112template <typename U>
113using SharedRef = NotNull<std::shared_ptr<U>>;
114
115/// @ingroup userver_universal
116///
117/// @brief A `std::unique_ptr` that is guaranteed to be not-null.
118/// @see MakeUniqueRef
119template <typename U>
120using UniqueRef = NotNull<std::unique_ptr<U>>;
121
122/// @brief An equivalent of `std::make_shared` for SharedRef.
123template <typename U, typename... Args, typename = std::enable_if_t<std::is_constructible_v<U, Args...>>>
124SharedRef<U> MakeSharedRef(Args&&... args) {
125 return SharedRef<U>{std::make_shared<U>(std::forward<Args>(args)...)};
126}
127
128/// @brief An equivalent of `std::make_unique` for UniqueRef.
129template <typename U, typename... Args, typename = std::enable_if_t<std::is_constructible_v<U, Args...>>>
130UniqueRef<U> MakeUniqueRef(Args&&... args) {
131 return UniqueRef<U>{std::make_unique<U>(std::forward<Args>(args)...)};
132}
133
134} // namespace utils
135
136USERVER_NAMESPACE_END
137
138template <typename T>
139// NOLINTNEXTLINE(cert-dcl58-cpp)
140struct std::hash<USERVER_NAMESPACE::utils::NotNull<T>> : public std::hash<T> {
141 using std::hash<T>::hash;
142
143 auto operator()(const USERVER_NAMESPACE::utils::NotNull<T>& value
144 ) const noexcept(std::is_nothrow_invocable_v<const std::hash<T>&, const T&>) {
145 return this->std::hash<T>::operator()(value.GetBase());
146 }
147};