/* * Copyright 2022 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #pragma once #include #include #include namespace android::ftl { // Enforces and documents non-null pre/post-condition for (raw or smart) pointers. // // void get_length(const ftl::NonNull>& string_ptr, // ftl::NonNull length_ptr) { // // No need for `nullptr` checks. // *length_ptr = string_ptr->length(); // } // // const auto string_ptr = ftl::as_non_null(std::make_shared("android")); // std::size_t size; // get_length(string_ptr, ftl::as_non_null(&size)); // assert(size == 7u); // // For compatibility with std::unique_ptr and performance with std::shared_ptr, move // operations are allowed despite breaking the invariant: // // using Pair = std::pair>, std::shared_ptr>; // // Pair dupe_if(ftl::NonNull> non_null_ptr, bool condition) { // // Move the underlying pointer out, so `non_null_ptr` must not be accessed after this point. // auto unique_ptr = std::move(non_null_ptr).take(); // // auto non_null_shared_ptr = ftl::as_non_null(std::shared_ptr(std::move(unique_ptr))); // auto nullable_shared_ptr = condition ? non_null_shared_ptr.get() : nullptr; // // return {std::move(non_null_shared_ptr), std::move(nullable_shared_ptr)}; // } // // auto ptr = ftl::as_non_null(std::make_unique(42)); // const auto [ptr1, ptr2] = dupe_if(std::move(ptr), true); // assert(ptr1.get() == ptr2); // template class NonNull final { struct Passkey {}; public: // Disallow `nullptr` explicitly for clear compilation errors. NonNull() = delete; NonNull(std::nullptr_t) = delete; // Copy operations. constexpr NonNull(const NonNull&) = default; constexpr NonNull& operator=(const NonNull&) = default; template >> constexpr NonNull(const NonNull& other) : pointer_(other.get()) {} template >> constexpr NonNull& operator=(const NonNull& other) { pointer_ = other.get(); return *this; } [[nodiscard]] constexpr const Pointer& get() const { return pointer_; } [[nodiscard]] constexpr explicit operator const Pointer&() const { return get(); } // Move operations. These break the invariant, so care must be taken to avoid subsequent access. constexpr NonNull(NonNull&&) = default; constexpr NonNull& operator=(NonNull&&) = default; [[nodiscard]] constexpr Pointer take() && { return std::move(pointer_); } [[nodiscard]] constexpr explicit operator Pointer() && { return take(); } // Dereferencing. [[nodiscard]] constexpr decltype(auto) operator*() const { return *get(); } [[nodiscard]] constexpr decltype(auto) operator->() const { return get(); } [[nodiscard]] constexpr explicit operator bool() const { return !(pointer_ == nullptr); } // Private constructor for ftl::as_non_null. Excluded from candidate constructors for conversions // through the passkey idiom, for clear compilation errors. template constexpr NonNull(Passkey, P&& pointer) : pointer_(std::forward

(pointer)) { if (pointer_ == nullptr) std::abort(); } private: template friend constexpr auto as_non_null(P&&) -> NonNull>; Pointer pointer_; }; template [[nodiscard]] constexpr auto as_non_null(P&& pointer) -> NonNull> { using Passkey = typename NonNull>::Passkey; return {Passkey{}, std::forward

(pointer)}; } // NonNull

<=> NonNull template constexpr bool operator==(const NonNull

& lhs, const NonNull& rhs) { return lhs.get() == rhs.get(); } template constexpr bool operator!=(const NonNull

& lhs, const NonNull& rhs) { return !operator==(lhs, rhs); } template constexpr bool operator<(const NonNull

& lhs, const NonNull& rhs) { return lhs.get() < rhs.get(); } template constexpr bool operator<=(const NonNull

& lhs, const NonNull& rhs) { return lhs.get() <= rhs.get(); } template constexpr bool operator>=(const NonNull

& lhs, const NonNull& rhs) { return lhs.get() >= rhs.get(); } template constexpr bool operator>(const NonNull

& lhs, const NonNull& rhs) { return lhs.get() > rhs.get(); } // NonNull

<=> Q template constexpr bool operator==(const NonNull

& lhs, const Q& rhs) { return lhs.get() == rhs; } template constexpr bool operator!=(const NonNull

& lhs, const Q& rhs) { return lhs.get() != rhs; } template constexpr bool operator<(const NonNull

& lhs, const Q& rhs) { return lhs.get() < rhs; } template constexpr bool operator<=(const NonNull

& lhs, const Q& rhs) { return lhs.get() <= rhs; } template constexpr bool operator>=(const NonNull

& lhs, const Q& rhs) { return lhs.get() >= rhs; } template constexpr bool operator>(const NonNull

& lhs, const Q& rhs) { return lhs.get() > rhs; } // P <=> NonNull template constexpr bool operator==(const P& lhs, const NonNull& rhs) { return lhs == rhs.get(); } template constexpr bool operator!=(const P& lhs, const NonNull& rhs) { return lhs != rhs.get(); } template constexpr bool operator<(const P& lhs, const NonNull& rhs) { return lhs < rhs.get(); } template constexpr bool operator<=(const P& lhs, const NonNull& rhs) { return lhs <= rhs.get(); } template constexpr bool operator>=(const P& lhs, const NonNull& rhs) { return lhs >= rhs.get(); } template constexpr bool operator>(const P& lhs, const NonNull& rhs) { return lhs > rhs.get(); } } // namespace android::ftl // Specialize std::hash for ftl::NonNull template struct std::hash> { std::size_t operator()(const android::ftl::NonNull

& ptr) const { return std::hash

()(ptr.get()); } };