A 2D tile-based sandbox game.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

SmallOptional.h 3.7KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173
  1. #pragma once
  2. #include <new>
  3. #include <stdlib.h>
  4. #include <utility>
  5. #include <iostream>
  6. namespace Swan {
  7. // The initial bytes policy assumes that no T object will ever start with
  8. // 'size' 'empty_byte' bytes. This works for types where, for example,
  9. // the first few bytes are a pointer.
  10. template<typename T, size_t size = sizeof(T), unsigned char empty_byte = 0xFF>
  11. struct SmallOptionalInitialBytesPolicy {
  12. static_assert(sizeof(T) >= size);
  13. static void setEmpty(unsigned char *ptr) {
  14. for (size_t i = 0; i < size; ++i)
  15. ptr[i] = empty_byte;
  16. }
  17. static bool isEmpty(const unsigned char *ptr) {
  18. for (size_t i = 0; i < size; ++i)
  19. if (ptr[i] != empty_byte)
  20. return false;
  21. return true;
  22. }
  23. };
  24. // The value policy lets you define a particular T value which can be a sentinel
  25. // and is considered different from valid T values according to T::operator==.
  26. // For example, if T is int, -1 could be a sentinel if only positive values are expected.
  27. template<typename T, T empty_value>
  28. struct SmallOptionalValuePolicy {
  29. static void setEmpty(unsigned char *ptr) {
  30. *(T *)ptr = empty_value;
  31. }
  32. static bool isEmpty(const unsigned char *ptr) {
  33. return *(T *)ptr == empty_value;
  34. }
  35. };
  36. // This is probably UB but I don't care, it avoids wasting 8 bytes per entity
  37. template<typename T, typename Policy = SmallOptionalInitialBytesPolicy<T>>
  38. class SmallOptional {
  39. public:
  40. SmallOptional() {
  41. Policy::setEmpty(data_);
  42. }
  43. SmallOptional(const T &other) {
  44. new (data_) T(other);
  45. }
  46. SmallOptional(T &&other) noexcept {
  47. new (data_) T(std::move(other));
  48. }
  49. SmallOptional(const SmallOptional<T, Policy> &other) {
  50. if (other.hasValue()) {
  51. new (data_) T(*other.get());
  52. } else {
  53. Policy::setEmpty(data_);
  54. }
  55. }
  56. SmallOptional(SmallOptional<T, Policy> &&other) noexcept {
  57. if (other.hasValue()) {
  58. new (data_) T(std::move(*other));
  59. } else {
  60. Policy::setEmpty(data_);
  61. }
  62. }
  63. ~SmallOptional() {
  64. if (hasValue()) {
  65. get()->~T();
  66. }
  67. }
  68. void reset() {
  69. if (hasValue()) {
  70. get()->~T();
  71. Policy::setEmpty(data_);
  72. }
  73. }
  74. template<typename... Args>
  75. T &emplace(Args&&... args) {
  76. if (hasValue()) {
  77. get()->~T();
  78. }
  79. new (data_) T(std::forward<Args>(args)...);
  80. return *get();
  81. }
  82. T *get() {
  83. return std::launder<T>((T *)data_);
  84. }
  85. const T *get() const {
  86. return std::launder<T>((T *)data_);
  87. }
  88. bool hasValue() const {
  89. return !Policy::isEmpty(data_);
  90. }
  91. SmallOptional<T, Policy> &operator=(const T &other) {
  92. if (hasValue()) {
  93. *get() = other;
  94. } else {
  95. new (data_) T(other);
  96. }
  97. return *this;
  98. }
  99. SmallOptional<T, Policy> &operator=(const T &&other) noexcept {
  100. if (hasValue()) {
  101. *get() = std::move(other);
  102. } else {
  103. new (data_) T(std::move(other));
  104. }
  105. return *this;
  106. }
  107. SmallOptional<T, Policy> &operator=(const SmallOptional<T, Policy> &other) {
  108. if (other.hasValue()) {
  109. if (hasValue()) {
  110. *get() = *other.get();
  111. } else {
  112. new (data_) T(*other.get());
  113. }
  114. } else {
  115. reset();
  116. }
  117. return *this;
  118. }
  119. SmallOptional &operator=(SmallOptional<T, Policy> &&other) noexcept {
  120. if (other.hasValue()) {
  121. if (hasValue()) {
  122. *get() = std::move(*other.get());
  123. } else {
  124. new (data_) T(std::move(*other.get()));
  125. }
  126. } else {
  127. reset();
  128. }
  129. return *this;
  130. }
  131. bool operator==(const SmallOptional<T, Policy> &other) const {
  132. bool a = hasValue(), b = other.hasValue();
  133. if (!a && !b) return true;
  134. if (!a || !b) return false;
  135. return *get() == *other.get();
  136. }
  137. operator bool() const noexcept {
  138. return !Policy::isEmpty(data_);
  139. }
  140. T *operator->() noexcept {
  141. return get();
  142. }
  143. const T *operator->() const noexcept {
  144. return get();
  145. }
  146. T &operator*() noexcept {
  147. return *get();
  148. }
  149. const T &operator*() const noexcept {
  150. return *get();
  151. }
  152. private:
  153. alignas(alignof(T)) unsigned char data_[sizeof(T)];
  154. };
  155. }