aloptional.h 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161
  1. #ifndef AL_OPTIONAL_H
  2. #define AL_OPTIONAL_H
  3. #include <initializer_list>
  4. #include <type_traits>
  5. #include <utility>
  6. #include "almalloc.h"
  7. namespace al {
  8. struct nullopt_t { };
  9. struct in_place_t { };
  10. constexpr nullopt_t nullopt{};
  11. constexpr in_place_t in_place{};
  12. template<typename T, bool = std::is_trivially_destructible<T>::value>
  13. struct optional_storage;
  14. template<typename T>
  15. struct optional_storage<T, true> {
  16. bool mHasValue{false};
  17. union {
  18. char mDummy;
  19. T mValue;
  20. };
  21. optional_storage() { }
  22. template<typename ...Args>
  23. explicit optional_storage(in_place_t, Args&& ...args)
  24. : mHasValue{true}, mValue{std::forward<Args>(args)...}
  25. { }
  26. ~optional_storage() = default;
  27. };
  28. template<typename T>
  29. struct optional_storage<T, false> {
  30. bool mHasValue{false};
  31. union {
  32. char mDummy;
  33. T mValue;
  34. };
  35. optional_storage() { }
  36. template<typename ...Args>
  37. explicit optional_storage(in_place_t, Args&& ...args)
  38. : mHasValue{true}, mValue{std::forward<Args>(args)...}
  39. { }
  40. ~optional_storage() { if(mHasValue) al::destroy_at(std::addressof(mValue)); }
  41. };
  42. template<typename T>
  43. class optional {
  44. using storage_t = optional_storage<T>;
  45. storage_t mStore;
  46. template<typename... Args>
  47. void doConstruct(Args&& ...args)
  48. {
  49. ::new(std::addressof(mStore.mValue)) T{std::forward<Args>(args)...};
  50. mStore.mHasValue = true;
  51. }
  52. public:
  53. using value_type = T;
  54. optional() = default;
  55. optional(nullopt_t) noexcept { }
  56. optional(const optional &rhs) { if(rhs) doConstruct(*rhs); }
  57. optional(optional&& rhs) { if(rhs) doConstruct(std::move(*rhs)); }
  58. template<typename ...Args>
  59. explicit optional(in_place_t, Args&& ...args)
  60. : mStore{al::in_place, std::forward<Args>(args)...}
  61. { }
  62. ~optional() = default;
  63. optional& operator=(nullopt_t) noexcept { reset(); return *this; }
  64. std::enable_if_t<std::is_copy_constructible<T>::value && std::is_copy_assignable<T>::value,
  65. optional&> operator=(const optional &rhs)
  66. {
  67. if(!rhs)
  68. reset();
  69. else if(*this)
  70. mStore.mValue = *rhs;
  71. else
  72. doConstruct(*rhs);
  73. return *this;
  74. }
  75. std::enable_if_t<std::is_move_constructible<T>::value && std::is_move_assignable<T>::value,
  76. optional&> operator=(optional&& rhs)
  77. {
  78. if(!rhs)
  79. reset();
  80. else if(*this)
  81. mStore.mValue = std::move(*rhs);
  82. else
  83. doConstruct(std::move(*rhs));
  84. return *this;
  85. }
  86. template<typename U=T>
  87. std::enable_if_t<std::is_constructible<T, U>::value
  88. && std::is_assignable<T&, U>::value
  89. && !std::is_same<std::decay_t<U>, optional<T>>::value
  90. && (!std::is_same<std::decay_t<U>, T>::value || !std::is_scalar<U>::value),
  91. optional&> operator=(U&& rhs)
  92. {
  93. if(*this)
  94. mStore.mValue = std::forward<U>(rhs);
  95. else
  96. doConstruct(std::forward<U>(rhs));
  97. return *this;
  98. }
  99. const T* operator->() const { return std::addressof(mStore.mValue); }
  100. T* operator->() { return std::addressof(mStore.mValue); }
  101. const T& operator*() const& { return this->mValue; }
  102. T& operator*() & { return mStore.mValue; }
  103. const T&& operator*() const&& { return std::move(mStore.mValue); }
  104. T&& operator*() && { return std::move(mStore.mValue); }
  105. operator bool() const noexcept { return mStore.mHasValue; }
  106. bool has_value() const noexcept { return mStore.mHasValue; }
  107. T& value() & { return mStore.mValue; }
  108. const T& value() const& { return mStore.mValue; }
  109. T&& value() && { return std::move(mStore.mValue); }
  110. const T&& value() const&& { return std::move(mStore.mValue); }
  111. template<typename U>
  112. T value_or(U&& defval) const&
  113. { return bool{*this} ? **this : static_cast<T>(std::forward<U>(defval)); }
  114. template<typename U>
  115. T value_or(U&& defval) &&
  116. { return bool{*this} ? std::move(**this) : static_cast<T>(std::forward<U>(defval)); }
  117. void reset() noexcept
  118. {
  119. if(mStore.mHasValue)
  120. al::destroy_at(std::addressof(mStore.mValue));
  121. mStore.mHasValue = false;
  122. }
  123. };
  124. template<typename T>
  125. inline optional<std::decay_t<T>> make_optional(T&& arg)
  126. { return optional<std::decay_t<T>>{in_place, std::forward<T>(arg)}; }
  127. template<typename T, typename... Args>
  128. inline optional<T> make_optional(Args&& ...args)
  129. { return optional<T>{in_place, std::forward<Args>(args)...}; }
  130. template<typename T, typename U, typename... Args>
  131. inline optional<T> make_optional(std::initializer_list<U> il, Args&& ...args)
  132. { return optional<T>{in_place, il, std::forward<Args>(args)...}; }
  133. } // namespace al
  134. #endif /* AL_OPTIONAL_H */