libcopp 2.3.1
All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
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
21LIBCOPP_COPP_NAMESPACE_BEGIN
22
23namespace gsl {
24
25namespace details {
26template <typename T, typename = void>
27struct is_comparable_to_nullptr : std::false_type {};
28
29template <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//
48template <class T, class = typename std::enable_if<std::is_pointer<T>::value>::type>
49using 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//
65template <class T>
66class 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:
108};
109
110template <class T>
111auto make_not_null(T&& t) noexcept {
112 return not_null<typename std::decay<T>::type>{std::forward<T>(t)};
113}
114
115template <class T, class U>
116auto 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
121template <class T, class U>
122auto 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
127template <class T, class U>
128auto 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
133template <class T, class U>
134auto 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
139template <class T, class U>
140auto 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
145template <class T, class U>
146auto 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
152template <class T, class U>
153std::ptrdiff_t operator-(const not_null<T>&, const not_null<U>&) = delete;
154template <class T>
155not_null<T> operator-(const not_null<T>&, std::ptrdiff_t) = delete;
156template <class T>
157not_null<T> operator+(const not_null<T>&, std::ptrdiff_t) = delete;
158template <class T>
159not_null<T> operator+(std::ptrdiff_t, const not_null<T>&) = delete;
160
161} // namespace gsl
162
163namespace 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//
182template <class T>
183class 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
198 strict_not_null(const strict_not_null& other) = default;
199 strict_not_null& operator=(const strict_not_null& other) = default;
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
220template <class T, class U>
221std::ptrdiff_t operator-(const strict_not_null<T>&, const strict_not_null<U>&) = delete;
222template <class T>
223strict_not_null<T> operator-(const strict_not_null<T>&, std::ptrdiff_t) = delete;
224template <class T>
225strict_not_null<T> operator+(const strict_not_null<T>&, std::ptrdiff_t) = delete;
226template <class T>
227strict_not_null<T> operator+(std::ptrdiff_t, const strict_not_null<T>&) = delete;
228
229template <class T>
230auto 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
237template <class T>
238not_null(T) -> not_null<T>;
239template <class T>
240strict_not_null(T) -> strict_not_null<T>;
241
242#endif // ( defined(__cpp_deduction_guides) && (__cpp_deduction_guides >= 201611L) )
243
244} // namespace gsl
245
246LIBCOPP_COPP_NAMESPACE_END
247
248namespace std {
249
250template <class T>
251struct 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
257template <class T>
258ostream& operator<<(ostream& os, const LIBCOPP_COPP_NAMESPACE_ID::gsl::not_null<T>& val) {
259 os << val.get();
260 return os;
261}
262
263template <class T>
264struct 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
not_null & operator=(std::nullptr_t)=delete
void operator[](std::ptrdiff_t) const =delete
constexpr value_type get() const
Definition not_null.h:84
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=(const not_null &other)=default
not_null & operator+=(std::ptrdiff_t)=delete
constexpr not_null(T u)
Definition not_null.h:77
constexpr std::remove_pointer< value_type >::type operator*() const
Definition not_null.h:91
not_null & operator-=(std::ptrdiff_t)=delete
constexpr not_null(const not_null< U > &other)
Definition not_null.h:80
not_null(const not_null &other)=default
not_null operator--(int)=delete
not_null & operator++()=delete
not_null operator++(int)=delete
constexpr value_type operator->() const
Definition not_null.h:90
not_null & operator--()=delete
strict_not_null operator--(int)=delete
strict_not_null(const strict_not_null &other)=default
strict_not_null & operator-=(std::ptrdiff_t)=delete
strict_not_null & operator=(std::nullptr_t)=delete
strict_not_null operator++(int)=delete
strict_not_null(std::nullptr_t)=delete
constexpr strict_not_null(T u)
Definition not_null.h:189
void operator[](std::ptrdiff_t) const =delete
constexpr strict_not_null(U &&u)
Definition not_null.h:186
strict_not_null & operator=(const not_null< T > &other)
Definition not_null.h:200
constexpr strict_not_null(const not_null< U > &other)
Definition not_null.h:192
strict_not_null & operator--()=delete
strict_not_null & operator+=(std::ptrdiff_t)=delete
constexpr strict_not_null(const strict_not_null< U > &other)
Definition not_null.h:195
strict_not_null(strict_not_null &&other)=default
strict_not_null & operator++()=delete
strict_not_null & operator=(const 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
not_null< T > operator+(const not_null< T > &, std::ptrdiff_t)=delete
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
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
STL namespace.
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