Maybe.h 14 KB

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