LCOV - code coverage report
Current view: top level - src/gsl - pointers.h (source / functions) Hit Total Coverage
Test: test_dash_coverage.info Lines: 18 18 100.0 %
Date: 2026-06-25 07:23:51 Functions: 38 60 63.3 %

          Line data    Source code
       1             : ///////////////////////////////////////////////////////////////////////////////
       2             : //
       3             : // Copyright (c) 2015 Microsoft Corporation. All rights reserved.
       4             : //
       5             : // This code is licensed under the MIT License (MIT).
       6             : //
       7             : // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
       8             : // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
       9             : // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
      10             : // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
      11             : // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
      12             : // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
      13             : // THE SOFTWARE.
      14             : //
      15             : ///////////////////////////////////////////////////////////////////////////////
      16             : 
      17             : #ifndef GSL_POINTERS_H
      18             : #define GSL_POINTERS_H
      19             : 
      20             : #include <gsl/assert.h> // for Ensures, Expects
      21             : #include <source_location.h>
      22             : 
      23             : #include <cstddef>      // for ptrdiff_t, nullptr_t, size_t
      24             : #include <memory>       // for shared_ptr, unique_ptr, hash
      25             : #include <type_traits>  // for enable_if_t, is_convertible, is_assignable
      26             : #include <utility>      // for declval, forward
      27             : 
      28             : #if !defined(GSL_NO_IOSTREAMS)
      29             : #include <iosfwd> // for ostream
      30             : #endif            // !defined(GSL_NO_IOSTREAMS)
      31             : 
      32             : namespace gsl
      33             : {
      34             : 
      35             :     namespace details
      36             :     {
      37             :         template <typename T, typename = void>
      38             :         struct is_comparable_to_nullptr : std::false_type
      39             :         {
      40             :         };
      41             : 
      42             :         template <typename T>
      43             :         struct is_comparable_to_nullptr<
      44             :                 T,
      45             :                 std::enable_if_t<std::is_convertible<decltype(std::declval<T>() != nullptr), bool>::value>>
      46             :                 : std::true_type
      47             :         {
      48             :         };
      49             : 
      50             :         // Resolves to the more efficient of `const T` or `const T&`, in the context of returning a const-qualified value
      51             :         // of type T.
      52             :         //
      53             :         // Copied from cppfront's implementation of the CppCoreGuidelines F.16 (https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#Rf-in)
      54             :         template<typename T>
      55             :         using value_or_reference_return_t = std::conditional_t<
      56             :                 sizeof(T) < 2*sizeof(void*) && std::is_trivially_copy_constructible<T>::value,
      57             :                 const T,
      58             :                 const T&>;
      59             : 
      60             :     } // namespace details
      61             : 
      62             : //
      63             : // GSL.owner: ownership pointers
      64             : //
      65             :     using std::shared_ptr;
      66             :     using std::unique_ptr;
      67             : 
      68             : //
      69             : // owner
      70             : //
      71             : // `gsl::owner<T>` is designed as a safety mechanism for code that must deal directly with raw pointers that own memory.
      72             : // Ideally such code should be restricted to the implementation of low-level abstractions. `gsl::owner` can also be used
      73             : // as a stepping point in converting legacy code to use more modern RAII constructs, such as smart pointers.
      74             : //
      75             : // T must be a pointer type
      76             : // - disallow construction from any type other than pointer type
      77             : //
      78             :     template <class T, class = std::enable_if_t<std::is_pointer<T>::value>>
      79             :     using owner = T;
      80             : 
      81             : //
      82             : // not_null
      83             : //
      84             : // Restricts a pointer or smart pointer to only hold non-null values.
      85             : //
      86             : // Has zero size overhead over T.
      87             : //
      88             : // If T is a pointer (i.e. T == U*) then
      89             : // - allow construction from U*
      90             : // - disallow construction from nullptr_t
      91             : // - disallow default construction
      92             : // - ensure construction from null U* fails
      93             : // - allow implicit conversion to U*
      94             : //
      95             :     template <class T>
      96             :     class not_null
      97             :     {
      98             :     public:
      99             :         static_assert(details::is_comparable_to_nullptr<T>::value, "T cannot be compared to nullptr.");
     100             : 
     101             :         template <typename U, typename = std::enable_if_t<std::is_convertible<U, T>::value>>
     102     1927852 :         constexpr not_null(U&& u, nostd::source_location loc = nostd::source_location::current()) : ptr_(std::forward<U>(u))
     103      963926 :         {
     104      963926 :             Expects(ptr_ != nullptr, loc);
     105     1927852 :         }
     106             : 
     107             :         template <typename = std::enable_if_t<!std::is_same<std::nullptr_t, T>::value>>
     108      848148 :         constexpr not_null(T u, nostd::source_location loc = nostd::source_location::current()) : ptr_(std::move(u))
     109      424074 :         {
     110      424074 :             Expects(ptr_ != nullptr, loc);
     111      848148 :         }
     112             : 
     113             :         template <typename U, typename = std::enable_if_t<std::is_convertible<U, T>::value>>
     114             :         constexpr not_null(const not_null<U>& other) : not_null(other.get())
     115             :         {}
     116             : 
     117             :         not_null(const not_null& other) = default;
     118          58 :         not_null& operator=(const not_null& other) = default;
     119     2926819 :         constexpr details::value_or_reference_return_t<T> get() const
     120             :         noexcept(noexcept(details::value_or_reference_return_t<T>{std::declval<T&>()}))
     121             :         {
     122     2926819 :             return ptr_;
     123             :         }
     124             : 
     125     1313892 :         constexpr operator T() const { return get(); }
     126     1263692 :         constexpr decltype(auto) operator->() const { return get(); }
     127      276259 :         constexpr decltype(auto) operator*() const { return *get(); }
     128             : 
     129             :         // prevents compilation when someone attempts to assign a null pointer constant
     130             :         not_null(std::nullptr_t) = delete;
     131             :         not_null& operator=(std::nullptr_t) = delete;
     132             : 
     133             :         // unwanted operators...pointers only point to single objects!
     134             :         not_null& operator++() = delete;
     135             :         not_null& operator--() = delete;
     136             :         not_null operator++(int) = delete;
     137             :         not_null operator--(int) = delete;
     138             :         not_null& operator+=(std::ptrdiff_t) = delete;
     139             :         not_null& operator-=(std::ptrdiff_t) = delete;
     140             :         void operator[](std::ptrdiff_t) const = delete;
     141             : 
     142             :     private:
     143             :         T ptr_;
     144             :     };
     145             : 
     146             :     template <class T>
     147             :     auto make_not_null(T&& t) noexcept
     148             :     {
     149             :         return not_null<std::remove_cv_t<std::remove_reference_t<T>>>{std::forward<T>(t)};
     150             :     }
     151             : 
     152             : #if !defined(GSL_NO_IOSTREAMS)
     153             :     template <class T>
     154             :     std::ostream& operator<<(std::ostream& os, const not_null<T>& val)
     155             :     {
     156             :         os << val.get();
     157             :         return os;
     158             :     }
     159             : #endif // !defined(GSL_NO_IOSTREAMS)
     160             : 
     161             :     template <class T, class U>
     162       36480 :     auto operator==(const not_null<T>& lhs,
     163             :                     const not_null<U>& rhs) noexcept(noexcept(lhs.get() == rhs.get()))
     164             :     -> decltype(lhs.get() == rhs.get())
     165             :     {
     166       36480 :         return lhs.get() == rhs.get();
     167             :     }
     168             : 
     169             :     template <class T, class U>
     170           8 :     auto operator!=(const not_null<T>& lhs,
     171             :                     const not_null<U>& rhs) noexcept(noexcept(lhs.get() != rhs.get()))
     172             :     -> decltype(lhs.get() != rhs.get())
     173             :     {
     174           8 :         return lhs.get() != rhs.get();
     175             :     }
     176             : 
     177             :     template <class T, class U>
     178             :     auto operator<(const not_null<T>& lhs,
     179             :                    const not_null<U>& rhs) noexcept(noexcept(std::less<>{}(lhs.get(), rhs.get())))
     180             :     -> decltype(std::less<>{}(lhs.get(), rhs.get()))
     181             :     {
     182             :         return std::less<>{}(lhs.get(), rhs.get());
     183             :     }
     184             : 
     185             :     template <class T, class U>
     186             :     auto operator<=(const not_null<T>& lhs,
     187             :                     const not_null<U>& rhs) noexcept(noexcept(std::less_equal<>{}(lhs.get(), rhs.get())))
     188             :     -> decltype(std::less_equal<>{}(lhs.get(), rhs.get()))
     189             :     {
     190             :         return std::less_equal<>{}(lhs.get(), rhs.get());
     191             :     }
     192             : 
     193             :     template <class T, class U>
     194             :     auto operator>(const not_null<T>& lhs,
     195             :                    const not_null<U>& rhs) noexcept(noexcept(std::greater<>{}(lhs.get(), rhs.get())))
     196             :     -> decltype(std::greater<>{}(lhs.get(), rhs.get()))
     197             :     {
     198             :         return std::greater<>{}(lhs.get(), rhs.get());
     199             :     }
     200             : 
     201             :     template <class T, class U>
     202             :     auto operator>=(const not_null<T>& lhs,
     203             :                     const not_null<U>& rhs) noexcept(noexcept(std::greater_equal<>{}(lhs.get(), rhs.get())))
     204             :     -> decltype(std::greater_equal<>{}(lhs.get(), rhs.get()))
     205             :     {
     206             :         return std::greater_equal<>{}(lhs.get(), rhs.get());
     207             :     }
     208             : 
     209             : // more unwanted operators
     210             :     template <class T, class U>
     211             :     std::ptrdiff_t operator-(const not_null<T>&, const not_null<U>&) = delete;
     212             :     template <class T>
     213             :     not_null<T> operator-(const not_null<T>&, std::ptrdiff_t) = delete;
     214             :     template <class T>
     215             :     not_null<T> operator+(const not_null<T>&, std::ptrdiff_t) = delete;
     216             :     template <class T>
     217             :     not_null<T> operator+(std::ptrdiff_t, const not_null<T>&) = delete;
     218             : 
     219             : 
     220             :     template <class T, class U = decltype(std::declval<const T&>().get()), bool = std::is_default_constructible<std::hash<U>>::value>
     221             :     struct not_null_hash
     222             :     {
     223             :         std::size_t operator()(const T& value) const { return std::hash<U>{}(value.get()); }
     224             :     };
     225             : 
     226             :     template <class T, class U>
     227             :     struct not_null_hash<T, U, false>
     228             :     {
     229             :         not_null_hash() = delete;
     230             :         not_null_hash(const not_null_hash&) = delete;
     231             :         not_null_hash& operator=(const not_null_hash&) = delete;
     232             :     };
     233             : 
     234             : } // namespace gsl
     235             : 
     236             : namespace std
     237             : {
     238             :     template <class T>
     239             :     struct hash<gsl::not_null<T>> : gsl::not_null_hash<gsl::not_null<T>>
     240             : {
     241             : };
     242             : 
     243             : } // namespace std
     244             : 
     245             : namespace gsl
     246             : {
     247             : 
     248             : //
     249             : // strict_not_null
     250             : //
     251             : // Restricts a pointer or smart pointer to only hold non-null values,
     252             : //
     253             : // - provides a strict (i.e. explicit constructor from T) wrapper of not_null
     254             : // - to be used for new code that wishes the design to be cleaner and make not_null
     255             : //   checks intentional, or in old code that would like to make the transition.
     256             : //
     257             : //   To make the transition from not_null, incrementally replace not_null
     258             : //   by strict_not_null and fix compilation errors
     259             : //
     260             : //   Expect to
     261             : //   - remove all unneeded conversions from raw pointer to not_null and back
     262             : //   - make API clear by specifying not_null in parameters where needed
     263             : //   - remove unnecessary asserts
     264             : //
     265             :     template <class T>
     266             :     class strict_not_null : public not_null<T>
     267             :     {
     268             :     public:
     269             :         template <typename U, typename = std::enable_if_t<std::is_convertible<U, T>::value>>
     270             :         constexpr explicit strict_not_null(U&& u) : not_null<T>(std::forward<U>(u))
     271             :         {}
     272             : 
     273             :         template <typename = std::enable_if_t<!std::is_same<std::nullptr_t, T>::value>>
     274             :         constexpr explicit strict_not_null(T u) : not_null<T>(u)
     275             :         {}
     276             : 
     277             :         template <typename U, typename = std::enable_if_t<std::is_convertible<U, T>::value>>
     278             :         constexpr strict_not_null(const not_null<U>& other) : not_null<T>(other)
     279             :         {}
     280             : 
     281             :         template <typename U, typename = std::enable_if_t<std::is_convertible<U, T>::value>>
     282             :         constexpr strict_not_null(const strict_not_null<U>& other) : not_null<T>(other)
     283             :         {}
     284             : 
     285             :         // To avoid invalidating the "not null" invariant, the contained pointer is actually copied
     286             :         // instead of moved. If it is a custom pointer, its constructor could in theory throw exceptions.
     287             :         strict_not_null(strict_not_null&& other) noexcept(std::is_nothrow_copy_constructible<T>::value) = default;
     288             :         strict_not_null(const strict_not_null& other) = default;
     289             :         strict_not_null& operator=(const strict_not_null& other) = default;
     290             :         strict_not_null& operator=(const not_null<T>& other)
     291             :         {
     292             :             not_null<T>::operator=(other);
     293             :             return *this;
     294             :         }
     295             : 
     296             :         // prevents compilation when someone attempts to assign a null pointer constant
     297             :         strict_not_null(std::nullptr_t) = delete;
     298             :         strict_not_null& operator=(std::nullptr_t) = delete;
     299             : 
     300             :         // unwanted operators...pointers only point to single objects!
     301             :         strict_not_null& operator++() = delete;
     302             :         strict_not_null& operator--() = delete;
     303             :         strict_not_null operator++(int) = delete;
     304             :         strict_not_null operator--(int) = delete;
     305             :         strict_not_null& operator+=(std::ptrdiff_t) = delete;
     306             :         strict_not_null& operator-=(std::ptrdiff_t) = delete;
     307             :         void operator[](std::ptrdiff_t) const = delete;
     308             :     };
     309             : 
     310             : // more unwanted operators
     311             :     template <class T, class U>
     312             :     std::ptrdiff_t operator-(const strict_not_null<T>&, const strict_not_null<U>&) = delete;
     313             :     template <class T>
     314             :     strict_not_null<T> operator-(const strict_not_null<T>&, std::ptrdiff_t) = delete;
     315             :     template <class T>
     316             :     strict_not_null<T> operator+(const strict_not_null<T>&, std::ptrdiff_t) = delete;
     317             :     template <class T>
     318             :     strict_not_null<T> operator+(std::ptrdiff_t, const strict_not_null<T>&) = delete;
     319             : 
     320             :     template <class T>
     321             :     auto make_strict_not_null(T&& t) noexcept
     322             :     {
     323             :         return strict_not_null<std::remove_cv_t<std::remove_reference_t<T>>>{std::forward<T>(t)};
     324             :     }
     325             : 
     326             : #if (defined(__cpp_deduction_guides) && (__cpp_deduction_guides >= 201611L))
     327             : 
     328             : // deduction guides to prevent the ctad-maybe-unsupported warning
     329             :     template <class T>
     330             :     not_null(T) -> not_null<T>;
     331             :     template <class T>
     332             :     strict_not_null(T) -> strict_not_null<T>;
     333             : 
     334             : #endif // ( defined(__cpp_deduction_guides) && (__cpp_deduction_guides >= 201611L) )
     335             : } // namespace gsl
     336             : 
     337             : namespace std
     338             : {
     339             :     template <class T>
     340             :     struct hash<gsl::strict_not_null<T>> : gsl::not_null_hash<gsl::strict_not_null<T>>
     341             : {
     342             : };
     343             : 
     344             : } // namespace std
     345             : 
     346             : #endif // GSL_POINTERS_H

Generated by: LCOV version 1.16