• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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