libcopp  2.2.0
not_null.h
Go to the documentation of this file.
1 // Copyright 2023 owent
2 
3 #pragma once
4 
5 #include <libcopp/utils/config/libcopp_build_features.h>
6 
7 // clang-format off
8 #include <libcopp/utils/config/stl_include_prefix.h> // NOLINT(build/include_order)
9 // clang-format on
10 #include <assert.h> // for forward
11 #include <algorithm> // for forward
12 #include <cstddef> // for ptrdiff_t, nullptr_t, size_t
13 #include <iosfwd> // for ostream
14 #include <system_error> // for hash
15 #include <type_traits> // for enable_if_t, is_convertible, is_assignable
16 
17 // clang-format off
18 #include <libcopp/utils/config/stl_include_suffix.h> // NOLINT(build/include_order)
19 // clang-format on
20 
21 LIBCOPP_COPP_NAMESPACE_BEGIN
22 
23 namespace gsl {
24 
25 namespace details {
26 template <typename T, typename = void>
27 struct is_comparable_to_nullptr : std::false_type {};
28 
29 template <typename T>
31  T, typename std::enable_if<std::is_convertible<decltype(std::declval<T>() != nullptr), bool>::value>::type>
32  : std::true_type {};
33 } // namespace details
34 
35 //
36 // GSL.owner: ownership pointers
37 //
38 
39 //
40 // owner
41 //
42 // owner<T> is designed as a bridge for code that must deal directly with owning pointers for some
43 // reason
44 //
45 // T must be a pointer type
46 // - disallow construction from any type other than pointer type
47 //
48 template <class T, class = typename std::enable_if<std::is_pointer<T>::value>::type>
49 using owner = T;
50 
51 //
52 // not_null
53 //
54 // Restricts a pointer or smart pointer to only hold non-null values.
55 //
56 // Has zero size overhead over T.
57 //
58 // If T is a pointer (i.e. T == U*) then
59 // - allow construction from U*
60 // - disallow construction from nullptr_t
61 // - disallow default construction
62 // - ensure construction from null U* fails
63 // - allow implicit conversion to U*
64 //
65 template <class T>
66 class not_null {
67  public:
68  using value_type = typename std::conditional<std::is_copy_constructible<T>::value, T, const T&>::type;
69 
70  public:
71  static_assert(details::is_comparable_to_nullptr<T>::value, "T cannot be compared to nullptr.");
72 
73  template <class U, class = typename std::enable_if<std::is_convertible<U, T>::value>::type>
74  constexpr not_null(U&& u) : ptr_(std::forward<U>(u)) {}
75 
76  template <class = typename std::enable_if<!std::is_same<std::nullptr_t, T>::value>::type>
77  constexpr not_null(T u) : ptr_(std::move(u)) {}
78 
79  template <class U, class = typename std::enable_if<std::is_convertible<U, T>::value>::type>
80  constexpr not_null(const not_null<U>& other) : not_null(other.get()) {}
81 
82  not_null(const not_null& other) = default;
83  not_null& operator=(const not_null& other) = default;
84  constexpr value_type get() const {
85  assert(ptr_ != nullptr);
86  return ptr_;
87  }
88 
89  constexpr operator T() const { return get(); }
90  constexpr value_type operator->() const { return get(); }
91  constexpr typename std::remove_pointer<value_type>::type operator*() const { return *get(); }
92 
93  // prevents compilation when someone attempts to assign a null pointer constant
94  not_null(std::nullptr_t) = delete;
95  not_null& operator=(std::nullptr_t) = delete;
96 
97  // unwanted operators...pointers only point to single objects!
98  not_null& operator++() = delete;
99  not_null& operator--() = delete;
100  not_null operator++(int) = delete;
101  not_null operator--(int) = delete;
102  not_null& operator+=(std::ptrdiff_t) = delete;
103  not_null& operator-=(std::ptrdiff_t) = delete;
104  void operator[](std::ptrdiff_t) const = delete;
105 
106  private:
107  T ptr_;
108 };
109 
110 template <class T>
111 auto make_not_null(T&& t) noexcept {
112  return not_null<typename std::decay<T>::type>{std::forward<T>(t)};
113 }
114 
115 template <class T, class U>
116 auto operator==(const not_null<T>& lhs, const not_null<U>& rhs) noexcept(noexcept(lhs.get() == rhs.get()))
117  -> decltype(lhs.get() == rhs.get()) {
118  return lhs.get() == rhs.get();
119 }
120 
121 template <class T, class U>
122 auto operator!=(const not_null<T>& lhs, const not_null<U>& rhs) noexcept(noexcept(lhs.get() != rhs.get()))
123  -> decltype(lhs.get() != rhs.get()) {
124  return lhs.get() != rhs.get();
125 }
126 
127 template <class T, class U>
128 auto operator<(const not_null<T>& lhs, const not_null<U>& rhs) noexcept(noexcept(lhs.get() < rhs.get()))
129  -> decltype(lhs.get() < rhs.get()) {
130  return lhs.get() < rhs.get();
131 }
132 
133 template <class T, class U>
134 auto operator<=(const not_null<T>& lhs, const not_null<U>& rhs) noexcept(noexcept(lhs.get() <= rhs.get()))
135  -> decltype(lhs.get() <= rhs.get()) {
136  return lhs.get() <= rhs.get();
137 }
138 
139 template <class T, class U>
140 auto operator>(const not_null<T>& lhs, const not_null<U>& rhs) noexcept(noexcept(lhs.get() > rhs.get()))
141  -> decltype(lhs.get() > rhs.get()) {
142  return lhs.get() > rhs.get();
143 }
144 
145 template <class T, class U>
146 auto operator>=(const not_null<T>& lhs, const not_null<U>& rhs) noexcept(noexcept(lhs.get() >= rhs.get()))
147  -> decltype(lhs.get() >= rhs.get()) {
148  return lhs.get() >= rhs.get();
149 }
150 
151 // more unwanted operators
152 template <class T, class U>
153 std::ptrdiff_t operator-(const not_null<T>&, const not_null<U>&) = delete;
154 template <class T>
155 not_null<T> operator-(const not_null<T>&, std::ptrdiff_t) = delete;
156 template <class T>
157 not_null<T> operator+(const not_null<T>&, std::ptrdiff_t) = delete;
158 template <class T>
159 not_null<T> operator+(std::ptrdiff_t, const not_null<T>&) = delete;
160 
161 } // namespace gsl
162 
163 namespace gsl {
164 
165 //
166 // strict_not_null
167 //
168 // Restricts a pointer or smart pointer to only hold non-null values,
169 //
170 // - provides a strict (i.e. explicit constructor from T) wrapper of not_null
171 // - to be used for new code that wishes the design to be cleaner and make not_null
172 // checks intentional, or in old code that would like to make the transition.
173 //
174 // To make the transition from not_null, incrementally replace not_null
175 // by strict_not_null and fix compilation errors
176 //
177 // Expect to
178 // - remove all unneeded conversions from raw pointer to not_null and back
179 // - make API clear by specifying not_null in parameters where needed
180 // - remove unnecessary asserts
181 //
182 template <class T>
183 class strict_not_null : public not_null<T> {
184  public:
185  template <class U, class = typename std::enable_if<std::is_convertible<U, T>::value>::type>
186  constexpr explicit strict_not_null(U&& u) : not_null<T>(std::forward<U>(u)) {}
187 
188  template <class = typename std::enable_if<!std::is_same<std::nullptr_t, T>::value>::type>
189  constexpr explicit strict_not_null(T u) : not_null<T>(u) {}
190 
191  template <class U, class = typename std::enable_if<std::is_convertible<U, T>::value>::type>
192  constexpr strict_not_null(const not_null<U>& other) : not_null<T>(other) {}
193 
194  template <class U, class = typename std::enable_if<std::is_convertible<U, T>::value>::type>
195  constexpr strict_not_null(const strict_not_null<U>& other) : not_null<T>(other) {}
196 
197  strict_not_null(strict_not_null&& other) = default;
198  strict_not_null(const strict_not_null& other) = default;
199  strict_not_null& operator=(const strict_not_null& other) = default;
201  not_null<T>::operator=(other);
202  return *this;
203  }
204 
205  // prevents compilation when someone attempts to assign a null pointer constant
206  strict_not_null(std::nullptr_t) = delete;
207  strict_not_null& operator=(std::nullptr_t) = delete;
208 
209  // unwanted operators...pointers only point to single objects!
214  strict_not_null& operator+=(std::ptrdiff_t) = delete;
215  strict_not_null& operator-=(std::ptrdiff_t) = delete;
216  void operator[](std::ptrdiff_t) const = delete;
217 };
218 
219 // more unwanted operators
220 template <class T, class U>
221 std::ptrdiff_t operator-(const strict_not_null<T>&, const strict_not_null<U>&) = delete;
222 template <class T>
223 strict_not_null<T> operator-(const strict_not_null<T>&, std::ptrdiff_t) = delete;
224 template <class T>
225 strict_not_null<T> operator+(const strict_not_null<T>&, std::ptrdiff_t) = delete;
226 template <class T>
227 strict_not_null<T> operator+(std::ptrdiff_t, const strict_not_null<T>&) = delete;
228 
229 template <class T>
230 auto make_strict_not_null(T&& t) noexcept {
232 }
233 
234 #if (defined(__cpp_deduction_guides) && (__cpp_deduction_guides >= 201611L))
235 
236 // deduction guides to prevent the ctad-maybe-unsupported warning
237 template <class T>
238 not_null(T) -> not_null<T>;
239 template <class T>
240 strict_not_null(T) -> strict_not_null<T>;
241 
242 #endif // ( defined(__cpp_deduction_guides) && (__cpp_deduction_guides >= 201611L) )
243 
244 } // namespace gsl
245 
246 LIBCOPP_COPP_NAMESPACE_END
247 
248 namespace std {
249 
250 template <class T>
251 struct hash<LIBCOPP_COPP_NAMESPACE_ID::gsl::not_null<T>> {
252  std::size_t operator()(const LIBCOPP_COPP_NAMESPACE_ID::gsl::not_null<T>& value) const {
253  return hash<T>{}(value.get());
254  }
255 };
256 
257 template <class T>
258 ostream& operator<<(ostream& os, const LIBCOPP_COPP_NAMESPACE_ID::gsl::not_null<T>& val) {
259  os << val.get();
260  return os;
261 }
262 
263 template <class T>
264 struct hash<LIBCOPP_COPP_NAMESPACE_ID::gsl::strict_not_null<T>> {
265  std::size_t operator()(const LIBCOPP_COPP_NAMESPACE_ID::gsl::strict_not_null<T>& value) const {
266  return hash<T>{}(value.get());
267  }
268 };
269 
270 } // namespace std
void operator[](std::ptrdiff_t) const =delete
constexpr value_type get() const
Definition: not_null.h:84
not_null & operator++()=delete
not_null(std::nullptr_t)=delete
constexpr not_null(U &&u)
Definition: not_null.h:74
typename std::conditional< std::is_copy_constructible< T >::value, T, const T & >::type value_type
Definition: not_null.h:68
not_null & operator--()=delete
constexpr not_null(T u)
Definition: not_null.h:77
not_null & operator=(const not_null &other)=default
constexpr not_null(const not_null< U > &other)
Definition: not_null.h:80
not_null & operator+=(std::ptrdiff_t)=delete
not_null(const not_null &other)=default
not_null & operator=(std::nullptr_t)=delete
not_null operator--(int)=delete
not_null operator++(int)=delete
not_null & operator-=(std::ptrdiff_t)=delete
constexpr std::remove_pointer< value_type >::type operator*() const
Definition: not_null.h:91
constexpr value_type operator->() const
Definition: not_null.h:90
strict_not_null & operator=(const not_null< T > &other)
Definition: not_null.h:200
strict_not_null & operator--()=delete
strict_not_null operator--(int)=delete
strict_not_null & operator+=(std::ptrdiff_t)=delete
strict_not_null(const strict_not_null &other)=default
strict_not_null operator++(int)=delete
strict_not_null(std::nullptr_t)=delete
constexpr strict_not_null(T u)
Definition: not_null.h:189
strict_not_null & operator=(std::nullptr_t)=delete
strict_not_null & operator-=(std::ptrdiff_t)=delete
strict_not_null & operator++()=delete
void operator[](std::ptrdiff_t) const =delete
constexpr strict_not_null(U &&u)
Definition: not_null.h:186
strict_not_null & operator=(const strict_not_null &other)=default
constexpr strict_not_null(const not_null< U > &other)
Definition: not_null.h:192
constexpr strict_not_null(const strict_not_null< U > &other)
Definition: not_null.h:195
strict_not_null(strict_not_null &&other)=default
Definition: not_null.h:23
T owner
Definition: not_null.h:49
auto operator<=(const not_null< T > &lhs, const not_null< U > &rhs) noexcept(noexcept(lhs.get()<=rhs.get())) -> decltype(lhs.get()<=rhs.get())
Definition: not_null.h:134
auto make_strict_not_null(T &&t) noexcept
Definition: not_null.h:230
auto make_not_null(T &&t) noexcept
Definition: not_null.h:111
auto operator==(const not_null< T > &lhs, const not_null< U > &rhs) noexcept(noexcept(lhs.get()==rhs.get())) -> decltype(lhs.get()==rhs.get())
Definition: not_null.h:116
auto operator<(const not_null< T > &lhs, const not_null< U > &rhs) noexcept(noexcept(lhs.get()< rhs.get())) -> decltype(lhs.get()< rhs.get())
Definition: not_null.h:128
auto operator!=(const not_null< T > &lhs, const not_null< U > &rhs) noexcept(noexcept(lhs.get() !=rhs.get())) -> decltype(lhs.get() !=rhs.get())
Definition: not_null.h:122
auto operator>=(const not_null< T > &lhs, const not_null< U > &rhs) noexcept(noexcept(lhs.get() >=rhs.get())) -> decltype(lhs.get() >=rhs.get())
Definition: not_null.h:146
std::ptrdiff_t operator-(const not_null< T > &, const not_null< U > &)=delete
not_null< T > operator+(const not_null< T > &, std::ptrdiff_t)=delete
auto operator>(const not_null< T > &lhs, const not_null< U > &rhs) noexcept(noexcept(lhs.get() > rhs.get())) -> decltype(lhs.get() > rhs.get())
Definition: not_null.h:140
ostream & operator<<(ostream &os, const LIBCOPP_COPP_NAMESPACE_ID::gsl::not_null< T > &val)
Definition: not_null.h:258
std::size_t operator()(const LIBCOPP_COPP_NAMESPACE_ID::gsl::not_null< T > &value) const
Definition: not_null.h:252
std::size_t operator()(const LIBCOPP_COPP_NAMESPACE_ID::gsl::strict_not_null< T > &value) const
Definition: not_null.h:265