MaybeOneOf.h 3.4 KB

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