Line data Source code
1 : /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 : /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 : /* This Source Code Form is subject to the terms of the Mozilla Public
4 : * License, v. 2.0. If a copy of the MPL was not distributed with this
5 : * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 :
7 : #ifndef mozilla_NotNull_h
8 : #define mozilla_NotNull_h
9 :
10 : // It's often unclear if a particular pointer, be it raw (T*) or smart
11 : // (RefPtr<T>, nsCOMPtr<T>, etc.) can be null. This leads to missing null
12 : // checks (which can cause crashes) and unnecessary null checks (which clutter
13 : // the code).
14 : //
15 : // C++ has a built-in alternative that avoids these problems: references. This
16 : // module defines another alternative, NotNull, which can be used in cases
17 : // where references are not suitable.
18 : //
19 : // In the comments below we use the word "handle" to cover all varieties of
20 : // pointers and references.
21 : //
22 : // References
23 : // ----------
24 : // References are always non-null. (You can do |T& r = *p;| where |p| is null,
25 : // but that's undefined behaviour. C++ doesn't provide any built-in, ironclad
26 : // guarantee of non-nullness.)
27 : //
28 : // A reference works well when you need a temporary handle to an existing
29 : // single object, e.g. for passing a handle to a function, or as a local handle
30 : // within another object. (In Rust parlance, this is a "borrow".)
31 : //
32 : // A reference is less appropriate in the following cases.
33 : //
34 : // - As a primary handle to an object. E.g. code such as this is possible but
35 : // strange: |T& t = *new T(); ...; delete &t;|
36 : //
37 : // - As a handle to an array. It's common for |T*| to refer to either a single
38 : // |T| or an array of |T|, but |T&| cannot refer to an array of |T| because
39 : // you can't index off a reference (at least, not without first converting it
40 : // to a pointer).
41 : //
42 : // - When the handle identity is meaningful, e.g. if you have a hashtable of
43 : // handles, because you have to use |&| on the reference to convert it to a
44 : // pointer.
45 : //
46 : // - Some people don't like using non-const references as function parameters,
47 : // because it is not clear at the call site that the argument might be
48 : // modified.
49 : //
50 : // - When you need "smart" behaviour. E.g. we lack reference equivalents to
51 : // RefPtr and nsCOMPtr.
52 : //
53 : // - When interfacing with code that uses pointers a lot, sometimes using a
54 : // reference just feels like an odd fit.
55 : //
56 : // Furthermore, a reference is impossible in the following cases.
57 : //
58 : // - When the handle is rebound to another object. References don't allow this.
59 : //
60 : // - When the handle has type |void|. |void&| is not allowed.
61 : //
62 : // NotNull is an alternative that can be used in any of the above cases except
63 : // for the last one, where the handle type is |void|. See below.
64 :
65 : #include "mozilla/Assertions.h"
66 : #include <stddef.h>
67 :
68 : namespace mozilla {
69 :
70 : // NotNull can be used to wrap a "base" pointer (raw or smart) to indicate it
71 : // is not null. Some examples:
72 : //
73 : // - NotNull<char*>
74 : // - NotNull<RefPtr<Event>>
75 : // - NotNull<nsCOMPtr<Event>>
76 : //
77 : // NotNull has the following notable properties.
78 : //
79 : // - It has zero space overhead.
80 : //
81 : // - It must be initialized explicitly. There is no default initialization.
82 : //
83 : // - It auto-converts to the base pointer type.
84 : //
85 : // - It does not auto-convert from a base pointer. Implicit conversion from a
86 : // less-constrained type (e.g. T*) to a more-constrained type (e.g.
87 : // NotNull<T*>) is dangerous. Creation and assignment from a base pointer can
88 : // only be done with WrapNotNull(), which makes them impossible to overlook,
89 : // both when writing and reading code.
90 : //
91 : // - When initialized (or assigned) it is checked, and if it is null we abort.
92 : // This guarantees that it cannot be null.
93 : //
94 : // - |operator bool()| is deleted. This means you cannot check a NotNull in a
95 : // boolean context, which eliminates the possibility of unnecessary null
96 : // checks.
97 : //
98 : // NotNull currently doesn't work with UniquePtr. See
99 : // https://github.com/Microsoft/GSL/issues/89 for some discussion.
100 : //
101 : template <typename T>
102 860 : class NotNull
103 : {
104 : template <typename U> friend NotNull<U> WrapNotNull(U aBasePtr);
105 :
106 : T mBasePtr;
107 :
108 : // This constructor is only used by WrapNotNull().
109 : template <typename U>
110 7997 : explicit NotNull(U aBasePtr) : mBasePtr(aBasePtr) {}
111 :
112 : public:
113 : // Disallow default construction.
114 : NotNull() = delete;
115 :
116 : // Construct/assign from another NotNull with a compatible base pointer type.
117 : template <typename U>
118 995 : MOZ_IMPLICIT NotNull(const NotNull<U>& aOther) : mBasePtr(aOther.get()) {
119 : static_assert(sizeof(T) == sizeof(NotNull<T>),
120 : "NotNull must have zero space overhead.");
121 : static_assert(offsetof(NotNull<T>, mBasePtr) == 0,
122 : "mBasePtr must have zero offset.");
123 995 : }
124 :
125 : // Default copy/move construction and assignment.
126 85 : NotNull(const NotNull<T>&) = default;
127 : NotNull<T>& operator=(const NotNull<T>&) = default;
128 85 : NotNull(NotNull<T>&&) = default;
129 : NotNull<T>& operator=(NotNull<T>&&) = default;
130 :
131 : // Disallow null checks, which are unnecessary for this type.
132 : explicit operator bool() const = delete;
133 :
134 : // Explicit conversion to a base pointer. Use only to resolve ambiguity or to
135 : // get a castable pointer.
136 3836112 : const T& get() const { return mBasePtr; }
137 :
138 : // Implicit conversion to a base pointer. Preferable to get().
139 349 : operator const T&() const { return get(); }
140 :
141 : // Dereference operators.
142 3819375 : const T& operator->() const { return get(); }
143 : decltype(*mBasePtr) operator*() const { return *mBasePtr; }
144 : };
145 :
146 : template <typename T>
147 : NotNull<T>
148 7997 : WrapNotNull(const T aBasePtr)
149 : {
150 7997 : NotNull<T> notNull(aBasePtr);
151 7997 : MOZ_RELEASE_ASSERT(aBasePtr);
152 7997 : return notNull;
153 : }
154 :
155 : // Compare two NotNulls.
156 : template <typename T, typename U>
157 : inline bool
158 67 : operator==(const NotNull<T>& aLhs, const NotNull<U>& aRhs)
159 : {
160 67 : return aLhs.get() == aRhs.get();
161 : }
162 : template <typename T, typename U>
163 : inline bool
164 78 : operator!=(const NotNull<T>& aLhs, const NotNull<U>& aRhs)
165 : {
166 78 : return aLhs.get() != aRhs.get();
167 : }
168 :
169 : // Compare a NotNull to a base pointer.
170 : template <typename T, typename U>
171 : inline bool
172 14 : operator==(const NotNull<T>& aLhs, const U& aRhs)
173 : {
174 14 : return aLhs.get() == aRhs;
175 : }
176 : template <typename T, typename U>
177 : inline bool
178 13 : operator!=(const NotNull<T>& aLhs, const U& aRhs)
179 : {
180 13 : return aLhs.get() != aRhs;
181 : }
182 :
183 : // Compare a base pointer to a NotNull.
184 : template <typename T, typename U>
185 : inline bool
186 9636 : operator==(const T& aLhs, const NotNull<U>& aRhs)
187 : {
188 9636 : return aLhs == aRhs.get();
189 : }
190 : template <typename T, typename U>
191 : inline bool
192 0 : operator!=(const T& aLhs, const NotNull<U>& aRhs)
193 : {
194 0 : return aLhs != aRhs.get();
195 : }
196 :
197 : // Disallow comparing a NotNull to a nullptr.
198 : template <typename T>
199 : bool
200 : operator==(const NotNull<T>&, decltype(nullptr)) = delete;
201 : template <typename T>
202 : bool
203 : operator!=(const NotNull<T>&, decltype(nullptr)) = delete;
204 :
205 : // Disallow comparing a nullptr to a NotNull.
206 : template <typename T>
207 : bool
208 : operator==(decltype(nullptr), const NotNull<T>&) = delete;
209 : template <typename T>
210 : bool
211 : operator!=(decltype(nullptr), const NotNull<T>&) = delete;
212 :
213 : } // namespace mozilla
214 :
215 : #endif /* mozilla_NotNull_h */
|