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
|