Maybe.h 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551
  1. /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
  2. /* This Source Code Form is subject to the terms of the Mozilla Public
  3. * License, v. 2.0. If a copy of the MPL was not distributed with this
  4. * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
  5. /* A class for optional values and in-place lazy construction. */
  6. #ifndef mozilla_Maybe_h
  7. #define mozilla_Maybe_h
  8. #include "mozilla/Alignment.h"
  9. #include "mozilla/Assertions.h"
  10. #include "mozilla/Attributes.h"
  11. #include "mozilla/Move.h"
  12. #include "mozilla/TypeTraits.h"
  13. #include <new> // for placement new
  14. #include <type_traits>
  15. namespace mozilla {
  16. struct Nothing { };
  17. /*
  18. * Maybe is a container class which contains either zero or one elements. It
  19. * serves two roles. It can represent values which are *semantically* optional,
  20. * augmenting a type with an explicit 'Nothing' value. In this role, it provides
  21. * methods that make it easy to work with values that may be missing, along with
  22. * equality and comparison operators so that Maybe values can be stored in
  23. * containers. Maybe values can be constructed conveniently in expressions using
  24. * type inference, as follows:
  25. *
  26. * void doSomething(Maybe<Foo> aFoo) {
  27. * if (aFoo) // Make sure that aFoo contains a value...
  28. * aFoo->takeAction(); // and then use |aFoo->| to access it.
  29. * } // |*aFoo| also works!
  30. *
  31. * doSomething(Nothing()); // Passes a Maybe<Foo> containing no value.
  32. * doSomething(Some(Foo(100))); // Passes a Maybe<Foo> containing |Foo(100)|.
  33. *
  34. * You'll note that it's important to check whether a Maybe contains a value
  35. * before using it, using conversion to bool, |isSome()|, or |isNothing()|. You
  36. * can avoid these checks, and sometimes write more readable code, using
  37. * |valueOr()|, |ptrOr()|, and |refOr()|, which allow you to retrieve the value
  38. * in the Maybe and provide a default for the 'Nothing' case. You can also use
  39. * |apply()| to call a function only if the Maybe holds a value, and |map()| to
  40. * transform the value in the Maybe, returning another Maybe with a possibly
  41. * different type.
  42. *
  43. * Maybe's other role is to support lazily constructing objects without using
  44. * dynamic storage. A Maybe directly contains storage for a value, but it's
  45. * empty by default. |emplace()|, as mentioned above, can be used to construct a
  46. * value in Maybe's storage. The value a Maybe contains can be destroyed by
  47. * calling |reset()|; this will happen automatically if a Maybe is destroyed
  48. * while holding a value.
  49. *
  50. * It's a common idiom in C++ to use a pointer as a 'Maybe' type, with a null
  51. * value meaning 'Nothing' and any other value meaning 'Some'. You can convert
  52. * from such a pointer to a Maybe value using 'ToMaybe()'.
  53. *
  54. * Maybe is inspired by similar types in the standard library of many other
  55. * languages (e.g. Haskell's Maybe and Rust's Option). In the C++ world it's
  56. * very similar to std::optional, which was proposed for C++14 and originated in
  57. * Boost. The most important differences between Maybe and std::optional are:
  58. *
  59. * - std::optional<T> may be compared with T. We deliberately forbid that.
  60. * - std::optional allows in-place construction without a separate call to
  61. * |emplace()| by using a dummy |in_place_t| value to tag the appropriate
  62. * constructor.
  63. * - std::optional has |valueOr()|, equivalent to Maybe's |valueOr()|, but
  64. * lacks corresponding methods for |refOr()| and |ptrOr()|.
  65. * - std::optional lacks |map()| and |apply()|, making it less suitable for
  66. * functional-style code.
  67. * - std::optional lacks many convenience functions that Maybe has. Most
  68. * unfortunately, it lacks equivalents of the type-inferred constructor
  69. * functions |Some()| and |Nothing()|.
  70. *
  71. * N.B. GCC has missed optimizations with Maybe in the past and may generate
  72. * extra branches/loads/stores. Use with caution on hot paths; it's not known
  73. * whether or not this is still a problem.
  74. */
  75. template<class T>
  76. class Maybe
  77. {
  78. bool mIsSome;
  79. AlignedStorage2<T> mStorage;
  80. public:
  81. typedef T ValueType;
  82. Maybe() : mIsSome(false) { }
  83. ~Maybe() { reset(); }
  84. MOZ_IMPLICIT Maybe(Nothing) : mIsSome(false) { }
  85. Maybe(const Maybe& aOther)
  86. : mIsSome(false)
  87. {
  88. if (aOther.mIsSome) {
  89. emplace(*aOther);
  90. }
  91. }
  92. /**
  93. * Maybe<T*> can be copy-constructed from a Maybe<U*> if U* and T* are
  94. * compatible, or from Maybe<decltype(nullptr)>.
  95. */
  96. template<typename U,
  97. typename =
  98. typename std::enable_if<std::is_pointer<T>::value &&
  99. (std::is_same<U, decltype(nullptr)>::value ||
  100. (std::is_pointer<U>::value &&
  101. std::is_base_of<typename std::remove_pointer<T>::type,
  102. typename std::remove_pointer<U>::type>::value))>::type>
  103. MOZ_IMPLICIT
  104. Maybe(const Maybe<U>& aOther)
  105. : mIsSome(false)
  106. {
  107. if (aOther.isSome()) {
  108. emplace(*aOther);
  109. }
  110. }
  111. Maybe(Maybe&& aOther)
  112. : mIsSome(false)
  113. {
  114. if (aOther.mIsSome) {
  115. emplace(Move(*aOther));
  116. aOther.reset();
  117. }
  118. }
  119. /**
  120. * Maybe<T*> can be move-constructed from a Maybe<U*> if U* and T* are
  121. * compatible, or from Maybe<decltype(nullptr)>.
  122. */
  123. template<typename U,
  124. typename =
  125. typename std::enable_if<std::is_pointer<T>::value &&
  126. (std::is_same<U, decltype(nullptr)>::value ||
  127. (std::is_pointer<U>::value &&
  128. std::is_base_of<typename std::remove_pointer<T>::type,
  129. typename std::remove_pointer<U>::type>::value))>::type>
  130. MOZ_IMPLICIT
  131. Maybe(Maybe<U>&& aOther)
  132. : mIsSome(false)
  133. {
  134. if (aOther.isSome()) {
  135. emplace(Move(*aOther));
  136. aOther.reset();
  137. }
  138. }
  139. Maybe& operator=(const Maybe& aOther)
  140. {
  141. if (&aOther != this) {
  142. if (aOther.mIsSome) {
  143. if (mIsSome) {
  144. // XXX(seth): The correct code for this branch, below, can't be used
  145. // due to a bug in Visual Studio 2010. See bug 1052940.
  146. /*
  147. ref() = aOther.ref();
  148. */
  149. reset();
  150. emplace(*aOther);
  151. } else {
  152. emplace(*aOther);
  153. }
  154. } else {
  155. reset();
  156. }
  157. }
  158. return *this;
  159. }
  160. Maybe& operator=(Maybe&& aOther)
  161. {
  162. MOZ_ASSERT(this != &aOther, "Self-moves are prohibited");
  163. if (aOther.mIsSome) {
  164. if (mIsSome) {
  165. ref() = Move(aOther.ref());
  166. } else {
  167. emplace(Move(*aOther));
  168. }
  169. aOther.reset();
  170. } else {
  171. reset();
  172. }
  173. return *this;
  174. }
  175. /* Methods that check whether this Maybe contains a value */
  176. explicit operator bool() const { return isSome(); }
  177. bool isSome() const { return mIsSome; }
  178. bool isNothing() const { return !mIsSome; }
  179. /* Returns the contents of this Maybe<T> by value. Unsafe unless |isSome()|. */
  180. T value() const
  181. {
  182. MOZ_ASSERT(mIsSome);
  183. return ref();
  184. }
  185. /*
  186. * Returns the contents of this Maybe<T> by value. If |isNothing()|, returns
  187. * the default value provided.
  188. */
  189. template<typename V>
  190. T valueOr(V&& aDefault) const
  191. {
  192. if (isSome()) {
  193. return ref();
  194. }
  195. return Forward<V>(aDefault);
  196. }
  197. /*
  198. * Returns the contents of this Maybe<T> by value. If |isNothing()|, returns
  199. * the value returned from the function or functor provided.
  200. */
  201. template<typename F>
  202. T valueOrFrom(F&& aFunc) const
  203. {
  204. if (isSome()) {
  205. return ref();
  206. }
  207. return aFunc();
  208. }
  209. /* Returns the contents of this Maybe<T> by pointer. Unsafe unless |isSome()|. */
  210. T* ptr()
  211. {
  212. MOZ_ASSERT(mIsSome);
  213. return &ref();
  214. }
  215. const T* ptr() const
  216. {
  217. MOZ_ASSERT(mIsSome);
  218. return &ref();
  219. }
  220. /*
  221. * Returns the contents of this Maybe<T> by pointer. If |isNothing()|,
  222. * returns the default value provided.
  223. */
  224. T* ptrOr(T* aDefault)
  225. {
  226. if (isSome()) {
  227. return ptr();
  228. }
  229. return aDefault;
  230. }
  231. const T* ptrOr(const T* aDefault) const
  232. {
  233. if (isSome()) {
  234. return ptr();
  235. }
  236. return aDefault;
  237. }
  238. /*
  239. * Returns the contents of this Maybe<T> by pointer. If |isNothing()|,
  240. * returns the value returned from the function or functor provided.
  241. */
  242. template<typename F>
  243. T* ptrOrFrom(F&& aFunc)
  244. {
  245. if (isSome()) {
  246. return ptr();
  247. }
  248. return aFunc();
  249. }
  250. template<typename F>
  251. const T* ptrOrFrom(F&& aFunc) const
  252. {
  253. if (isSome()) {
  254. return ptr();
  255. }
  256. return aFunc();
  257. }
  258. T* operator->()
  259. {
  260. MOZ_ASSERT(mIsSome);
  261. return ptr();
  262. }
  263. const T* operator->() const
  264. {
  265. MOZ_ASSERT(mIsSome);
  266. return ptr();
  267. }
  268. /* Returns the contents of this Maybe<T> by ref. Unsafe unless |isSome()|. */
  269. T& ref()
  270. {
  271. MOZ_ASSERT(mIsSome);
  272. return *mStorage.addr();
  273. }
  274. const T& ref() const
  275. {
  276. MOZ_ASSERT(mIsSome);
  277. return *mStorage.addr();
  278. }
  279. /*
  280. * Returns the contents of this Maybe<T> by ref. If |isNothing()|, returns
  281. * the default value provided.
  282. */
  283. T& refOr(T& aDefault)
  284. {
  285. if (isSome()) {
  286. return ref();
  287. }
  288. return aDefault;
  289. }
  290. const T& refOr(const T& aDefault) const
  291. {
  292. if (isSome()) {
  293. return ref();
  294. }
  295. return aDefault;
  296. }
  297. /*
  298. * Returns the contents of this Maybe<T> by ref. If |isNothing()|, returns the
  299. * value returned from the function or functor provided.
  300. */
  301. template<typename F>
  302. T& refOrFrom(F&& aFunc)
  303. {
  304. if (isSome()) {
  305. return ref();
  306. }
  307. return aFunc();
  308. }
  309. template<typename F>
  310. const T& refOrFrom(F&& aFunc) const
  311. {
  312. if (isSome()) {
  313. return ref();
  314. }
  315. return aFunc();
  316. }
  317. T& operator*()
  318. {
  319. MOZ_ASSERT(mIsSome);
  320. return ref();
  321. }
  322. const T& operator*() const
  323. {
  324. MOZ_ASSERT(mIsSome);
  325. return ref();
  326. }
  327. /* If |isSome()|, runs the provided function or functor on the contents of
  328. * this Maybe. */
  329. template<typename Func>
  330. Maybe& apply(Func aFunc)
  331. {
  332. if (isSome()) {
  333. aFunc(ref());
  334. }
  335. return *this;
  336. }
  337. template<typename Func>
  338. const Maybe& apply(Func aFunc) const
  339. {
  340. if (isSome()) {
  341. aFunc(ref());
  342. }
  343. return *this;
  344. }
  345. /*
  346. * If |isSome()|, runs the provided function and returns the result wrapped
  347. * in a Maybe. If |isNothing()|, returns an empty Maybe value.
  348. */
  349. template<typename Func>
  350. auto map(Func aFunc) -> Maybe<decltype(aFunc(DeclVal<Maybe<T>>().ref()))>
  351. {
  352. using ReturnType = decltype(aFunc(ref()));
  353. if (isSome()) {
  354. Maybe<ReturnType> val;
  355. val.emplace(aFunc(ref()));
  356. return val;
  357. }
  358. return Maybe<ReturnType>();
  359. }
  360. template<typename Func>
  361. auto map(Func aFunc) const -> Maybe<decltype(aFunc(DeclVal<Maybe<T>>().ref()))>
  362. {
  363. using ReturnType = decltype(aFunc(ref()));
  364. if (isSome()) {
  365. Maybe<ReturnType> val;
  366. val.emplace(aFunc(ref()));
  367. return val;
  368. }
  369. return Maybe<ReturnType>();
  370. }
  371. /* If |isSome()|, empties this Maybe and destroys its contents. */
  372. void reset()
  373. {
  374. if (isSome()) {
  375. ref().T::~T();
  376. mIsSome = false;
  377. }
  378. }
  379. /*
  380. * Constructs a T value in-place in this empty Maybe<T>'s storage. The
  381. * arguments to |emplace()| are the parameters to T's constructor.
  382. */
  383. template<typename... Args>
  384. void emplace(Args&&... aArgs)
  385. {
  386. MOZ_ASSERT(!mIsSome);
  387. ::new (mStorage.addr()) T(Forward<Args>(aArgs)...);
  388. mIsSome = true;
  389. }
  390. };
  391. /*
  392. * Some() creates a Maybe<T> value containing the provided T value. If T has a
  393. * move constructor, it's used to make this as efficient as possible.
  394. *
  395. * Some() selects the type of Maybe it returns by removing any const, volatile,
  396. * or reference qualifiers from the type of the value you pass to it. This gives
  397. * it more intuitive behavior when used in expressions, but it also means that
  398. * if you need to construct a Maybe value that holds a const, volatile, or
  399. * reference value, you need to use emplace() instead.
  400. */
  401. template<typename T>
  402. Maybe<typename RemoveCV<typename RemoveReference<T>::Type>::Type>
  403. Some(T&& aValue)
  404. {
  405. typedef typename RemoveCV<typename RemoveReference<T>::Type>::Type U;
  406. Maybe<U> value;
  407. value.emplace(Forward<T>(aValue));
  408. return value;
  409. }
  410. template<typename T>
  411. Maybe<typename RemoveCV<typename RemoveReference<T>::Type>::Type>
  412. ToMaybe(T* aPtr)
  413. {
  414. if (aPtr) {
  415. return Some(*aPtr);
  416. }
  417. return Nothing();
  418. }
  419. /*
  420. * Two Maybe<T> values are equal if
  421. * - both are Nothing, or
  422. * - both are Some, and the values they contain are equal.
  423. */
  424. template<typename T> bool
  425. operator==(const Maybe<T>& aLHS, const Maybe<T>& aRHS)
  426. {
  427. if (aLHS.isNothing() != aRHS.isNothing()) {
  428. return false;
  429. }
  430. return aLHS.isNothing() || *aLHS == *aRHS;
  431. }
  432. template<typename T> bool
  433. operator!=(const Maybe<T>& aLHS, const Maybe<T>& aRHS)
  434. {
  435. return !(aLHS == aRHS);
  436. }
  437. /*
  438. * We support comparison to Nothing to allow reasonable expressions like:
  439. * if (maybeValue == Nothing()) { ... }
  440. */
  441. template<typename T> bool
  442. operator==(const Maybe<T>& aLHS, const Nothing& aRHS)
  443. {
  444. return aLHS.isNothing();
  445. }
  446. template<typename T> bool
  447. operator!=(const Maybe<T>& aLHS, const Nothing& aRHS)
  448. {
  449. return !(aLHS == aRHS);
  450. }
  451. template<typename T> bool
  452. operator==(const Nothing& aLHS, const Maybe<T>& aRHS)
  453. {
  454. return aRHS.isNothing();
  455. }
  456. template<typename T> bool
  457. operator!=(const Nothing& aLHS, const Maybe<T>& aRHS)
  458. {
  459. return !(aLHS == aRHS);
  460. }
  461. /*
  462. * Maybe<T> values are ordered in the same way T values are ordered, except that
  463. * Nothing comes before anything else.
  464. */
  465. template<typename T> bool
  466. operator<(const Maybe<T>& aLHS, const Maybe<T>& aRHS)
  467. {
  468. if (aLHS.isNothing()) {
  469. return aRHS.isSome();
  470. }
  471. if (aRHS.isNothing()) {
  472. return false;
  473. }
  474. return *aLHS < *aRHS;
  475. }
  476. template<typename T> bool
  477. operator>(const Maybe<T>& aLHS, const Maybe<T>& aRHS)
  478. {
  479. return !(aLHS < aRHS || aLHS == aRHS);
  480. }
  481. template<typename T> bool
  482. operator<=(const Maybe<T>& aLHS, const Maybe<T>& aRHS)
  483. {
  484. return aLHS < aRHS || aLHS == aRHS;
  485. }
  486. template<typename T> bool
  487. operator>=(const Maybe<T>& aLHS, const Maybe<T>& aRHS)
  488. {
  489. return !(aLHS < aRHS);
  490. }
  491. } // namespace mozilla
  492. #endif /* mozilla_Maybe_h */