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