A 2D tile-based sandbox game.
Você não pode selecionar mais de 25 tópicos Os tópicos devem começar com uma letra ou um número, podem incluir traços ('-') e podem ter até 35 caracteres.

SmallOptional.h 3.9KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183
  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. struct SmallNullOpt {};
  37. // This is probably UB but I don't care, it avoids wasting 8 bytes per entity
  38. template<typename T, typename Policy = SmallOptionalInitialBytesPolicy<T>>
  39. class SmallOptional {
  40. public:
  41. SmallOptional() {
  42. Policy::setEmpty(data_);
  43. }
  44. SmallOptional(SmallNullOpt): SmallOptional() {}
  45. SmallOptional(const T &other) {
  46. new (data_) T(other);
  47. }
  48. SmallOptional(T &&other) noexcept {
  49. new (data_) T(std::move(other));
  50. }
  51. SmallOptional(const SmallOptional<T, Policy> &other) {
  52. if (other.hasValue()) {
  53. new (data_) T(*other.get());
  54. } else {
  55. Policy::setEmpty(data_);
  56. }
  57. }
  58. SmallOptional(SmallOptional<T, Policy> &&other) noexcept {
  59. if (other.hasValue()) {
  60. new (data_) T(std::move(*other));
  61. } else {
  62. Policy::setEmpty(data_);
  63. }
  64. }
  65. ~SmallOptional() {
  66. if (hasValue()) {
  67. get()->~T();
  68. }
  69. }
  70. void reset() {
  71. if (hasValue()) {
  72. get()->~T();
  73. Policy::setEmpty(data_);
  74. }
  75. }
  76. template<typename... Args>
  77. T &emplace(Args&&... args) {
  78. if (hasValue()) {
  79. get()->~T();
  80. }
  81. new (data_) T(std::forward<Args>(args)...);
  82. return *get();
  83. }
  84. T *get() {
  85. return std::launder<T>((T *)data_);
  86. }
  87. const T *get() const {
  88. return std::launder<T>((T *)data_);
  89. }
  90. bool hasValue() const {
  91. return !Policy::isEmpty(data_);
  92. }
  93. SmallOptional<T, Policy> &operator=(SmallNullOpt) {
  94. reset();
  95. return *this;
  96. }
  97. SmallOptional<T, Policy> &operator=(const T &other) {
  98. if (hasValue()) {
  99. *get() = other;
  100. } else {
  101. new (data_) T(other);
  102. }
  103. return *this;
  104. }
  105. SmallOptional<T, Policy> &operator=(const T &&other) noexcept {
  106. if (hasValue()) {
  107. *get() = std::move(other);
  108. } else {
  109. new (data_) T(std::move(other));
  110. }
  111. return *this;
  112. }
  113. SmallOptional<T, Policy> &operator=(const SmallOptional<T, Policy> &other) {
  114. if (other.hasValue()) {
  115. if (hasValue()) {
  116. *get() = *other.get();
  117. } else {
  118. new (data_) T(*other.get());
  119. }
  120. } else {
  121. reset();
  122. }
  123. return *this;
  124. }
  125. SmallOptional &operator=(SmallOptional<T, Policy> &&other) noexcept {
  126. if (other.hasValue()) {
  127. if (hasValue()) {
  128. *get() = std::move(*other.get());
  129. } else {
  130. new (data_) T(std::move(*other.get()));
  131. }
  132. } else {
  133. reset();
  134. }
  135. return *this;
  136. }
  137. bool operator==(const SmallNullOpt &other) const {
  138. return !hasValue();
  139. }
  140. bool operator==(const SmallOptional<T, Policy> &other) const {
  141. bool a = hasValue(), b = other.hasValue();
  142. if (!a && !b) return true;
  143. if (!a || !b) return false;
  144. return *get() == *other.get();
  145. }
  146. operator bool() const noexcept {
  147. return !Policy::isEmpty(data_);
  148. }
  149. T *operator->() noexcept {
  150. return get();
  151. }
  152. const T *operator->() const noexcept {
  153. return get();
  154. }
  155. T &operator*() noexcept {
  156. return *get();
  157. }
  158. const T &operator*() const noexcept {
  159. return *get();
  160. }
  161. private:
  162. alignas(alignof(T)) unsigned char data_[sizeof(T)];
  163. };
  164. }