MaybeOneOf.h 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143
  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_MaybeOneOf_h
  7. #define mozilla_MaybeOneOf_h
  8. #include "mozilla/Alignment.h"
  9. #include "mozilla/Assertions.h"
  10. #include "mozilla/Move.h"
  11. #include "mozilla/TemplateLib.h"
  12. #include <new> // For placement new
  13. namespace mozilla {
  14. /*
  15. * MaybeOneOf<T1, T2> is like Maybe, but it supports constructing either T1
  16. * or T2. When a MaybeOneOf<T1, T2> is constructed, it is |empty()|, i.e.,
  17. * no value has been constructed and no destructor will be called when the
  18. * MaybeOneOf<T1, T2> is destroyed. Upon calling |construct<T1>()| or
  19. * |construct<T2>()|, a T1 or T2 object will be constructed with the given
  20. * arguments and that object will be destroyed when the owning MaybeOneOf is
  21. * destroyed.
  22. */
  23. template<class T1, class T2>
  24. class MaybeOneOf
  25. {
  26. AlignedStorage<tl::Max<sizeof(T1), sizeof(T2)>::value> storage;
  27. enum State { None, SomeT1, SomeT2 } state;
  28. template <class T, class Ignored = void> struct Type2State {};
  29. template <class T>
  30. T& as()
  31. {
  32. MOZ_ASSERT(state == Type2State<T>::result);
  33. return *(T*)storage.addr();
  34. }
  35. template <class T>
  36. const T& as() const
  37. {
  38. MOZ_ASSERT(state == Type2State<T>::result);
  39. return *(T*)storage.addr();
  40. }
  41. public:
  42. MaybeOneOf() : state(None) {}
  43. ~MaybeOneOf() { destroyIfConstructed(); }
  44. MaybeOneOf(MaybeOneOf&& rhs)
  45. : state(None)
  46. {
  47. if (!rhs.empty()) {
  48. if (rhs.constructed<T1>()) {
  49. construct<T1>(Move(rhs.as<T1>()));
  50. rhs.as<T1>().~T1();
  51. } else {
  52. construct<T2>(Move(rhs.as<T2>()));
  53. rhs.as<T2>().~T2();
  54. }
  55. rhs.state = None;
  56. }
  57. }
  58. MaybeOneOf &operator=(MaybeOneOf&& rhs)
  59. {
  60. MOZ_ASSERT(this != &rhs, "Self-move is prohibited");
  61. this->~MaybeOneOf();
  62. new(this) MaybeOneOf(Move(rhs));
  63. return *this;
  64. }
  65. bool empty() const { return state == None; }
  66. template <class T>
  67. bool constructed() const { return state == Type2State<T>::result; }
  68. template <class T, class... Args>
  69. void construct(Args&&... aArgs)
  70. {
  71. MOZ_ASSERT(state == None);
  72. state = Type2State<T>::result;
  73. ::new (storage.addr()) T(Forward<Args>(aArgs)...);
  74. }
  75. template <class T>
  76. T& ref()
  77. {
  78. return as<T>();
  79. }
  80. template <class T>
  81. const T& ref() const
  82. {
  83. return as<T>();
  84. }
  85. void destroy()
  86. {
  87. MOZ_ASSERT(state == SomeT1 || state == SomeT2);
  88. if (state == SomeT1) {
  89. as<T1>().~T1();
  90. } else if (state == SomeT2) {
  91. as<T2>().~T2();
  92. }
  93. state = None;
  94. }
  95. void destroyIfConstructed()
  96. {
  97. if (!empty()) {
  98. destroy();
  99. }
  100. }
  101. private:
  102. MaybeOneOf(const MaybeOneOf& aOther) = delete;
  103. const MaybeOneOf& operator=(const MaybeOneOf& aOther) = delete;
  104. };
  105. template <class T1, class T2>
  106. template <class Ignored>
  107. struct MaybeOneOf<T1, T2>::Type2State<T1, Ignored>
  108. {
  109. typedef MaybeOneOf<T1, T2> Enclosing;
  110. static const typename Enclosing::State result = Enclosing::SomeT1;
  111. };
  112. template <class T1, class T2>
  113. template <class Ignored>
  114. struct MaybeOneOf<T1, T2>::Type2State<T2, Ignored>
  115. {
  116. typedef MaybeOneOf<T1, T2> Enclosing;
  117. static const typename Enclosing::State result = Enclosing::SomeT2;
  118. };
  119. } // namespace mozilla
  120. #endif /* mozilla_MaybeOneOf_h */