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 constexpr const Pointer& get() const { return pointer_; } 72 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 constexpr Pointer take() && { return std::move(pointer_); } Pointer()80 constexpr explicit operator Pointer() && { return take(); } 81 82 // Dereferencing. decltype(auto)83 constexpr decltype(auto) operator*() const { return *get(); } 84 constexpr decltype(auto) operator->() const { return get(); } 85 86 // Private constructor for ftl::as_non_null. Excluded from candidate constructors for conversions 87 // through the passkey idiom, for clear compilation errors. 88 template <typename P> NonNull(Passkey,P && pointer)89 constexpr NonNull(Passkey, P&& pointer) : pointer_(std::forward<P>(pointer)) { 90 if (!pointer_) std::abort(); 91 } 92 93 private: 94 template <typename P> 95 friend constexpr auto as_non_null(P&&) -> NonNull<std::decay_t<P>>; 96 97 Pointer pointer_; 98 }; 99 100 template <typename P> 101 constexpr auto as_non_null(P&& pointer) -> NonNull<std::decay_t<P>> { 102 using Passkey = typename NonNull<std::decay_t<P>>::Passkey; 103 return {Passkey{}, std::forward<P>(pointer)}; 104 } 105 106 template <typename P, typename Q> 107 constexpr bool operator==(const NonNull<P>& lhs, const NonNull<Q>& rhs) { 108 return lhs.get() == rhs.get(); 109 } 110 111 template <typename P, typename Q> 112 constexpr bool operator!=(const NonNull<P>& lhs, const NonNull<Q>& rhs) { 113 return !operator==(lhs, rhs); 114 } 115 116 } // namespace android::ftl 117