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 get()71 [[nodiscard]] constexpr const Pointer& get() const { return pointer_; } 72 [[nodiscard]] constexpr explicit operator const Pointer&() const { return get(); } 73 74 // Move operations. These break the invariant, so care must be taken to avoid subsequent access. 75 76 constexpr NonNull(NonNull&&) = default; 77 constexpr NonNull& operator=(NonNull&&) = default; 78 take()79 [[nodiscard]] constexpr Pointer take() && { return std::move(pointer_); } Pointer()80 [[nodiscard]] constexpr explicit operator Pointer() && { return take(); } 81 82 // Dereferencing. decltype(auto)83 [[nodiscard]] constexpr decltype(auto) operator*() const { return *get(); } 84 [[nodiscard]] constexpr decltype(auto) operator->() const { return get(); } 85 86 [[nodiscard]] constexpr explicit operator bool() const { return !(pointer_ == nullptr); } 87 88 // Private constructor for ftl::as_non_null. Excluded from candidate constructors for conversions 89 // through the passkey idiom, for clear compilation errors. 90 template <typename P> NonNull(Passkey,P && pointer)91 constexpr NonNull(Passkey, P&& pointer) : pointer_(std::forward<P>(pointer)) { 92 if (pointer_ == nullptr) std::abort(); 93 } 94 95 private: 96 template <typename P> 97 friend constexpr auto as_non_null(P&&) -> NonNull<std::decay_t<P>>; 98 99 Pointer pointer_; 100 }; 101 102 template <typename P> 103 [[nodiscard]] constexpr auto as_non_null(P&& pointer) -> NonNull<std::decay_t<P>> { 104 using Passkey = typename NonNull<std::decay_t<P>>::Passkey; 105 return {Passkey{}, std::forward<P>(pointer)}; 106 } 107 108 // NonNull<P> <=> NonNull<Q> 109 110 template <typename P, typename Q> 111 constexpr bool operator==(const NonNull<P>& lhs, const NonNull<Q>& rhs) { 112 return lhs.get() == rhs.get(); 113 } 114 115 template <typename P, typename Q> 116 constexpr bool operator!=(const NonNull<P>& lhs, const NonNull<Q>& rhs) { 117 return !operator==(lhs, rhs); 118 } 119 120 template <typename P, typename Q> 121 constexpr bool operator<(const NonNull<P>& lhs, const NonNull<Q>& rhs) { 122 return lhs.get() < rhs.get(); 123 } 124 125 template <typename P, typename Q> 126 constexpr bool operator<=(const NonNull<P>& lhs, const NonNull<Q>& rhs) { 127 return lhs.get() <= rhs.get(); 128 } 129 130 template <typename P, typename Q> 131 constexpr bool operator>=(const NonNull<P>& lhs, const NonNull<Q>& rhs) { 132 return lhs.get() >= rhs.get(); 133 } 134 135 template <typename P, typename Q> 136 constexpr bool operator>(const NonNull<P>& lhs, const NonNull<Q>& rhs) { 137 return lhs.get() > rhs.get(); 138 } 139 140 // NonNull<P> <=> Q 141 142 template <typename P, typename Q> 143 constexpr bool operator==(const NonNull<P>& lhs, const Q& rhs) { 144 return lhs.get() == rhs; 145 } 146 147 template <typename P, typename Q> 148 constexpr bool operator!=(const NonNull<P>& lhs, const Q& rhs) { 149 return lhs.get() != rhs; 150 } 151 152 template <typename P, typename Q> 153 constexpr bool operator<(const NonNull<P>& lhs, const Q& rhs) { 154 return lhs.get() < rhs; 155 } 156 157 template <typename P, typename Q> 158 constexpr bool operator<=(const NonNull<P>& lhs, const Q& rhs) { 159 return lhs.get() <= rhs; 160 } 161 162 template <typename P, typename Q> 163 constexpr bool operator>=(const NonNull<P>& lhs, const Q& rhs) { 164 return lhs.get() >= rhs; 165 } 166 167 template <typename P, typename Q> 168 constexpr bool operator>(const NonNull<P>& lhs, const Q& rhs) { 169 return lhs.get() > rhs; 170 } 171 172 // P <=> NonNull<Q> 173 174 template <typename P, typename Q> 175 constexpr bool operator==(const P& lhs, const NonNull<Q>& rhs) { 176 return lhs == rhs.get(); 177 } 178 179 template <typename P, typename Q> 180 constexpr bool operator!=(const P& lhs, const NonNull<Q>& rhs) { 181 return lhs != rhs.get(); 182 } 183 184 template <typename P, typename Q> 185 constexpr bool operator<(const P& lhs, const NonNull<Q>& rhs) { 186 return lhs < rhs.get(); 187 } 188 189 template <typename P, typename Q> 190 constexpr bool operator<=(const P& lhs, const NonNull<Q>& rhs) { 191 return lhs <= rhs.get(); 192 } 193 194 template <typename P, typename Q> 195 constexpr bool operator>=(const P& lhs, const NonNull<Q>& rhs) { 196 return lhs >= rhs.get(); 197 } 198 199 template <typename P, typename Q> 200 constexpr bool operator>(const P& lhs, const NonNull<Q>& rhs) { 201 return lhs > rhs.get(); 202 } 203 204 } // namespace android::ftl 205 206 // Specialize std::hash for ftl::NonNull<T> 207 template <typename P> 208 struct std::hash<android::ftl::NonNull<P>> { 209 std::size_t operator()(const android::ftl::NonNull<P>& ptr) const { 210 return std::hash<P>()(ptr.get()); 211 } 212 }; 213