xref: /aosp_15_r20/frameworks/native/include/ftl/non_null.h (revision 38e8c45f13ce32b0dcecb25141ffecaf386fa17f)
1 /*
2  * Copyright 2022 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #pragma once
18 
19 #include <cstdlib>
20 #include <type_traits>
21 #include <utility>
22 
23 namespace android::ftl {
24 
25 // Enforces and documents non-null pre/post-condition for (raw or smart) pointers.
26 //
27 //   void get_length(const ftl::NonNull<std::shared_ptr<std::string>>& string_ptr,
28 //                   ftl::NonNull<std::size_t*> length_ptr) {
29 //     // No need for `nullptr` checks.
30 //     *length_ptr = string_ptr->length();
31 //   }
32 //
33 //   const auto string_ptr = ftl::as_non_null(std::make_shared<std::string>("android"));
34 //   std::size_t size;
35 //   get_length(string_ptr, ftl::as_non_null(&size));
36 //   assert(size == 7u);
37 //
38 // For compatibility with std::unique_ptr<T> and performance with std::shared_ptr<T>, move
39 // operations are allowed despite breaking the invariant:
40 //
41 //   using Pair = std::pair<ftl::NonNull<std::shared_ptr<int>>, std::shared_ptr<int>>;
42 //
43 //   Pair dupe_if(ftl::NonNull<std::unique_ptr<int>> non_null_ptr, bool condition) {
44 //     // Move the underlying pointer out, so `non_null_ptr` must not be accessed after this point.
45 //     auto unique_ptr = std::move(non_null_ptr).take();
46 //
47 //     auto non_null_shared_ptr = ftl::as_non_null(std::shared_ptr<int>(std::move(unique_ptr)));
48 //     auto nullable_shared_ptr = condition ? non_null_shared_ptr.get() : nullptr;
49 //
50 //     return {std::move(non_null_shared_ptr), std::move(nullable_shared_ptr)};
51 //   }
52 //
53 //   auto ptr = ftl::as_non_null(std::make_unique<int>(42));
54 //   const auto [ptr1, ptr2] = dupe_if(std::move(ptr), true);
55 //   assert(ptr1.get() == ptr2);
56 //
57 template <typename Pointer>
58 class NonNull final {
59   struct Passkey {};
60 
61  public:
62   // Disallow `nullptr` explicitly for clear compilation errors.
63   NonNull() = delete;
64   NonNull(std::nullptr_t) = delete;
65 
66   // Copy operations.
67 
68   constexpr NonNull(const NonNull&) = default;
69   constexpr NonNull& operator=(const NonNull&) = default;
70 
71   template <typename U, typename = std::enable_if_t<std::is_convertible_v<U, Pointer>>>
NonNull(const NonNull<U> & other)72   constexpr NonNull(const NonNull<U>& other) : pointer_(other.get()) {}
73 
74   template <typename U, typename = std::enable_if_t<std::is_convertible_v<U, Pointer>>>
75   constexpr NonNull& operator=(const NonNull<U>& other) {
76     pointer_ = other.get();
77     return *this;
78   }
79 
get()80   [[nodiscard]] constexpr const Pointer& get() const { return pointer_; }
81   [[nodiscard]] constexpr explicit operator const Pointer&() const { return get(); }
82 
83   // Move operations. These break the invariant, so care must be taken to avoid subsequent access.
84 
85   constexpr NonNull(NonNull&&) = default;
86   constexpr NonNull& operator=(NonNull&&) = default;
87 
take()88   [[nodiscard]] constexpr Pointer take() && { return std::move(pointer_); }
Pointer()89   [[nodiscard]] constexpr explicit operator Pointer() && { return take(); }
90 
91   // Dereferencing.
decltype(auto)92   [[nodiscard]] constexpr decltype(auto) operator*() const { return *get(); }
93   [[nodiscard]] constexpr decltype(auto) operator->() const { return get(); }
94 
95   [[nodiscard]] constexpr explicit operator bool() const { return !(pointer_ == nullptr); }
96 
97   // Private constructor for ftl::as_non_null. Excluded from candidate constructors for conversions
98   // through the passkey idiom, for clear compilation errors.
99   template <typename P>
NonNull(Passkey,P && pointer)100   constexpr NonNull(Passkey, P&& pointer) : pointer_(std::forward<P>(pointer)) {
101     if (pointer_ == nullptr) std::abort();
102   }
103 
104  private:
105   template <typename P>
106   friend constexpr auto as_non_null(P&&) -> NonNull<std::decay_t<P>>;
107 
108   Pointer pointer_;
109 };
110 
111 template <typename P>
112 [[nodiscard]] constexpr auto as_non_null(P&& pointer) -> NonNull<std::decay_t<P>> {
113   using Passkey = typename NonNull<std::decay_t<P>>::Passkey;
114   return {Passkey{}, std::forward<P>(pointer)};
115 }
116 
117 // NonNull<P> <=> NonNull<Q>
118 
119 template <typename P, typename Q>
120 constexpr bool operator==(const NonNull<P>& lhs, const NonNull<Q>& rhs) {
121   return lhs.get() == rhs.get();
122 }
123 
124 template <typename P, typename Q>
125 constexpr bool operator!=(const NonNull<P>& lhs, const NonNull<Q>& rhs) {
126   return !operator==(lhs, rhs);
127 }
128 
129 template <typename P, typename Q>
130 constexpr bool operator<(const NonNull<P>& lhs, const NonNull<Q>& rhs) {
131   return lhs.get() < rhs.get();
132 }
133 
134 template <typename P, typename Q>
135 constexpr bool operator<=(const NonNull<P>& lhs, const NonNull<Q>& rhs) {
136   return lhs.get() <= rhs.get();
137 }
138 
139 template <typename P, typename Q>
140 constexpr bool operator>=(const NonNull<P>& lhs, const NonNull<Q>& rhs) {
141   return lhs.get() >= rhs.get();
142 }
143 
144 template <typename P, typename Q>
145 constexpr bool operator>(const NonNull<P>& lhs, const NonNull<Q>& rhs) {
146   return lhs.get() > rhs.get();
147 }
148 
149 // NonNull<P> <=> Q
150 
151 template <typename P, typename Q>
152 constexpr bool operator==(const NonNull<P>& lhs, const Q& rhs) {
153   return lhs.get() == rhs;
154 }
155 
156 template <typename P, typename Q>
157 constexpr bool operator!=(const NonNull<P>& lhs, const Q& rhs) {
158   return lhs.get() != rhs;
159 }
160 
161 template <typename P, typename Q>
162 constexpr bool operator<(const NonNull<P>& lhs, const Q& rhs) {
163   return lhs.get() < rhs;
164 }
165 
166 template <typename P, typename Q>
167 constexpr bool operator<=(const NonNull<P>& lhs, const Q& rhs) {
168   return lhs.get() <= rhs;
169 }
170 
171 template <typename P, typename Q>
172 constexpr bool operator>=(const NonNull<P>& lhs, const Q& rhs) {
173   return lhs.get() >= rhs;
174 }
175 
176 template <typename P, typename Q>
177 constexpr bool operator>(const NonNull<P>& lhs, const Q& rhs) {
178   return lhs.get() > rhs;
179 }
180 
181 // P <=> NonNull<Q>
182 
183 template <typename P, typename Q>
184 constexpr bool operator==(const P& lhs, const NonNull<Q>& rhs) {
185   return lhs == rhs.get();
186 }
187 
188 template <typename P, typename Q>
189 constexpr bool operator!=(const P& lhs, const NonNull<Q>& rhs) {
190   return lhs != rhs.get();
191 }
192 
193 template <typename P, typename Q>
194 constexpr bool operator<(const P& lhs, const NonNull<Q>& rhs) {
195   return lhs < rhs.get();
196 }
197 
198 template <typename P, typename Q>
199 constexpr bool operator<=(const P& lhs, const NonNull<Q>& rhs) {
200   return lhs <= rhs.get();
201 }
202 
203 template <typename P, typename Q>
204 constexpr bool operator>=(const P& lhs, const NonNull<Q>& rhs) {
205   return lhs >= rhs.get();
206 }
207 
208 template <typename P, typename Q>
209 constexpr bool operator>(const P& lhs, const NonNull<Q>& rhs) {
210   return lhs > rhs.get();
211 }
212 
213 }  // namespace android::ftl
214 
215 // Specialize std::hash for ftl::NonNull<T>
216 template <typename P>
217 struct std::hash<android::ftl::NonNull<P>> {
218   std::size_t operator()(const android::ftl::NonNull<P>& ptr) const {
219     return std::hash<P>()(ptr.get());
220   }
221 };
222