NotNull.h 6.6 KB

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