/* * Copyright (c) Facebook, Inc. and its affiliates. * * 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 /** * C++ Core Guideline's not_null PtrT>. * * not_null holds a pointer-like type PtrT which is not nullptr. * * not_null is a drop-in replacement for T* (as long as it's never null). * Specializations not_null_unique_ptr and not_null_shared_ptr are * drop-in replacements for unique_ptr and shared_ptr, respecitively. * * Example: * void foo(not_null nnpi) { * *nnpi = 7; // Safe, since `nnpi` is not null. * } * * void bar(not_null_shared_ptr nnspi) { * foo(nnsp.get()); * } * * Notes: * - Constructing a not_null from a nullptr-equivalent argument throws * a std::invalid_argument exception. * - Cannot be used after move. * - In debug mode, not_null checks that it is not null on all accesses, * since use-after-move can cause the underlying PtrT to be null. */ #include #include #include #include #include namespace folly { namespace detail { template struct is_not_null; template struct is_not_null_convertible; template struct is_not_null_nothrow_constructible; template struct is_not_null_castable; template struct is_not_null_move_castable; } // namespace detail class guaranteed_not_null_provider { protected: struct guaranteed_not_null {}; }; /** * not_null_base, the common interface for all not_null subclasses. * - Implicitly constructs and casts just like a PtrT. * - Has unwrap() function to access the underlying PtrT. */ template class not_null_base : protected guaranteed_not_null_provider { template struct implicit_tag {}; public: using pointer = PtrT; using element_type = typename std::pointer_traits::element_type; /** * Construction: * - Throws std::invalid_argument if null. * - Cannot default construct. * - Cannot construct from nullptr. * - Allows implicit construction iff PtrT allows implicit construction. * - Construction from another not_null skips null check (in opt builds). */ not_null_base() = delete; /* implicit */ not_null_base(std::nullptr_t) = delete; not_null_base(const not_null_base& nn) = default; not_null_base(not_null_base& nn) = default; not_null_base(not_null_base&& nn) = default; template < typename U, typename = std::enable_if_t::value>> /* implicit */ not_null_base(U&& u, implicit_tag = {}) noexcept( detail::is_not_null_nothrow_constructible::value); template < typename U, typename = std::enable_if_t::value>> explicit not_null_base(U&& u, implicit_tag = {}) noexcept( detail::is_not_null_nothrow_constructible::value); // Allow construction without a null check for trusted callsites. explicit not_null_base( PtrT&& ptr, guaranteed_not_null_provider::guaranteed_not_null) noexcept; /** * Assignment: * - Due to implicit construction, just need to assign from self. * - Cannot assign from nullptr. */ not_null_base& operator=(std::nullptr_t) = delete; not_null_base& operator=(const not_null_base& nn) = default; not_null_base& operator=(not_null_base&& nn) = default; /** * Dereferencing: * - Does not return mutable references, since that would allow the * underlying pointer to be assigned to nullptr. */ element_type& operator*() const noexcept; const PtrT& operator->() const noexcept; /** * Casting: * - Implicit casting to PtrT allowed, so that not_null can be used * wherever a PtrT is expected. * - Does not return mutable references, since that would allow the * underlying pointer to be assigned to nullptr. * - Boolean cast is always true. */ operator const PtrT&() const& noexcept; operator PtrT&&() && noexcept; template < typename U, typename = std::enable_if_t::value>> operator U() const& noexcept(std::is_nothrow_constructible_v); template < typename U, typename = std::enable_if_t::value>> operator U() && noexcept(std::is_nothrow_constructible_v); explicit inline operator bool() const noexcept { return true; } /** * Swap */ void swap(not_null_base& other) noexcept; /** * Accessor: * - Can explicitly access the underlying type via `unwrap`. * - Does not return mutable references, since that would allow the * underlying pointer to be assigned to nullptr. */ const PtrT& unwrap() const& noexcept; PtrT&& unwrap() && noexcept; protected: void throw_if_null() const; template static void throw_if_null(const T& ptr); template static void terminate_if_null(const T& ptr); template static Deleter&& forward_or_throw_if_null(Deleter&& deleter); // Non-const accessor. PtrT& mutable_unwrap() noexcept; private: struct private_tag {}; template not_null_base(U&& u, private_tag); PtrT ptr_; }; /** * not_null specializable class. * * Default implementation is not_null_base. */ template class not_null : public not_null_base { public: using pointer = typename not_null_base::pointer; using element_type = typename not_null_base::element_type; using not_null_base::not_null_base; }; /** * not_null> specialization. * * alias: not_null_unique_ptr * * Provides API compatibility with unique_ptr, except: * - Pointer arguments must be non-null. * - Cannot reset(). * - Functions are not noexcept, since debug-mode checks can throw exceptions. * - Promotes returned pointers to be not_null pointers. Implicit casting * allows these to be used in place of regular pointers. * * Notes: * - Has make_not_null_unique, equivalent to std::make_unique */ template class not_null> : public not_null_base> { public: using pointer = not_null::pointer>; using element_type = typename std::unique_ptr::element_type; using deleter_type = typename std::unique_ptr::deleter_type; /** * Constructors. Most are inherited from not_null_base. */ using not_null_base>::not_null_base; not_null(pointer p, const Deleter& d); not_null(pointer p, Deleter&& d); /** * not_null_unique_ptr cannot be released - that would cause it to be null. */ pointer release() = delete; /** * not_null_unique_ptr can only be reset to a non-null pointer. */ void reset(std::nullptr_t) = delete; void reset(pointer ptr) noexcept; /** * get() returns a not_null (pointer type is not_null). * * Due to implicit casting, can still capture the result of get() as a regular * pointer type: * * int* ptr = not_null_unique_ptr(...).get(); // valid */ pointer get() const noexcept; /** * get_deleter(): same as for unique_ptr. */ Deleter& get_deleter() noexcept; const Deleter& get_deleter() const noexcept; }; template > using not_null_unique_ptr = not_null>; template not_null_unique_ptr make_not_null_unique(Args&&... args); /** * not_null> specialization. * * alias: not_null_shared_ptr * * Provides API compatibility with shared_ptr, except: * - Pointer arguments must be non-null. * - Cannot reset(). * - Functions are not noexcept, since debug-mode checks can throw exceptions. * - Promotes returned pointers to be not_null pointers. Implicit casting * allows these to be used in place of regular pointers. * * Notes: * - Has make_not_null_shared, equivalent to std::make_shared. */ template class not_null> : public not_null_base> { public: using element_type = typename std::shared_ptr::element_type; using pointer = not_null; using weak_type = typename std::shared_ptr::weak_type; /** * Constructors. Most are inherited from not_null_base. */ using not_null_base>::not_null_base; template not_null(U* ptr, Deleter d); template not_null(not_null ptr, Deleter d); /** * Aliasing constructors. * * Note: * - The aliased shared_ptr argument, @r, is allowed to be null. The * constructed object is not null iff @ptr is. */ template not_null(const std::shared_ptr& r, not_null ptr) noexcept; template not_null( const not_null>& r, not_null ptr) noexcept; template not_null(std::shared_ptr&& r, not_null ptr) noexcept; template not_null( not_null>&& r, not_null ptr) noexcept; /** * not_null_shared_ptr can only be reset to a non-null pointer. */ void reset() = delete; template void reset(U* ptr); template void reset(not_null ptr) noexcept; template void reset(U* ptr, Deleter d); template void reset(not_null ptr, Deleter d); /** * get() returns a not_null. * * Due to implicit casting, can still capture the result of get() as a regular * pointer type: * * int* ptr = not_null_shared_ptr(...).get(); // valid */ pointer get() const noexcept; /** * use_count() * owner_before() * * Same as shared_ptr. * * Notes: * - unique() is deprecated in c++17, so is not implemented here. Can call * not_null_shared_ptr.unwrap().unique() as a workaround, until unique() * is removed in C++20. */ long use_count() const noexcept; template bool owner_before(const std::shared_ptr& other) const noexcept; template bool owner_before(const not_null>& other) const noexcept; }; template using not_null_shared_ptr = not_null>; template not_null_shared_ptr make_not_null_shared(Args&&... args); template not_null_shared_ptr allocate_not_null_shared( const Alloc& alloc, Args&&... args); /** * Comparison: * - Forwards to underlying PtrT. * - Works when one of the operands is not not_null. * - Works when one of the operands is nullptr. */ #define FB_NOT_NULL_MK_OP(op) \ template \ bool operator op(const not_null& lhs, const T& rhs); \ template < \ typename PtrT, \ typename T, \ typename = std::enable_if_t::value>> \ bool operator op(const T& lhs, const not_null& rhs); FB_NOT_NULL_MK_OP(==) FB_NOT_NULL_MK_OP(!=) FB_NOT_NULL_MK_OP(<) FB_NOT_NULL_MK_OP(<=) FB_NOT_NULL_MK_OP(>) FB_NOT_NULL_MK_OP(>=) #undef FB_NOT_NULL_MK_OP /** * Output: * - Forwards to underlying PtrT. */ template std::basic_ostream& operator<<( std::basic_ostream& os, const not_null& ptr); /** * Swap */ template void swap(not_null& lhs, not_null& rhs) noexcept; /** * Getters */ template Deleter* get_deleter(const not_null_shared_ptr& ptr); /** * Casting */ template not_null_shared_ptr static_pointer_cast(const not_null_shared_ptr& r); template not_null_shared_ptr static_pointer_cast(not_null_shared_ptr&& r); template std::shared_ptr dynamic_pointer_cast(const not_null_shared_ptr& r); template std::shared_ptr dynamic_pointer_cast(not_null_shared_ptr&& r); template not_null_shared_ptr const_pointer_cast(const not_null_shared_ptr& r); template not_null_shared_ptr const_pointer_cast(not_null_shared_ptr&& r); template not_null_shared_ptr reinterpret_pointer_cast( const not_null_shared_ptr& r); template not_null_shared_ptr reinterpret_pointer_cast(not_null_shared_ptr&& r); } // namespace folly namespace std { /** * Hashing: * - Forwards to underlying PtrT. */ template struct hash<::folly::not_null> : hash {}; } // namespace std #include