throwing_allocator.hpp 2.7 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192
  1. #ifndef ENTT_COMMON_THROWING_ALLOCATOR_HPP
  2. #define ENTT_COMMON_THROWING_ALLOCATOR_HPP
  3. #include <cstddef>
  4. #include <limits>
  5. #include <memory>
  6. #include <type_traits>
  7. #include <entt/container/dense_map.hpp>
  8. #include <entt/core/fwd.hpp>
  9. #include <entt/core/type_info.hpp>
  10. namespace test {
  11. struct throwing_allocator_exception {};
  12. template<typename Type>
  13. class throwing_allocator {
  14. template<typename Other>
  15. friend class throwing_allocator;
  16. template<typename Other, typename = std::enable_if_t<!std::is_void_v<Type> || std::is_constructible_v<std::allocator<Type>, std::allocator<Other>>>>
  17. throwing_allocator(int, const throwing_allocator<Other> &other)
  18. : allocator{other.allocator},
  19. config{other.config} {}
  20. template<typename Other, typename = std::enable_if_t<std::is_void_v<Type> && !std::is_constructible_v<std::allocator<Type>, std::allocator<Other>>>>
  21. throwing_allocator(char, const throwing_allocator<Other> &other)
  22. : allocator{},
  23. config{other.config} {}
  24. public:
  25. using value_type = Type;
  26. using pointer = value_type *;
  27. using const_pointer = const value_type *;
  28. using void_pointer = void *;
  29. using const_void_pointer = const void *;
  30. using propagate_on_container_move_assignment = std::true_type;
  31. using propagate_on_container_swap = std::true_type;
  32. using container_type = entt::dense_map<entt::id_type, std::size_t>;
  33. template<typename Other>
  34. struct rebind {
  35. using other = throwing_allocator<Other>;
  36. };
  37. throwing_allocator()
  38. : allocator{},
  39. config{std::allocate_shared<container_type>(allocator)} {}
  40. template<typename Other>
  41. throwing_allocator(const throwing_allocator<Other> &other)
  42. // std::allocator<void> has no cross constructors (waiting for C++20)
  43. : throwing_allocator{0, other} {}
  44. pointer allocate(std::size_t length) {
  45. if(const auto hash = entt::type_id<Type>().hash(); config->contains(hash)) {
  46. if(auto &elem = (*config)[hash]; elem == 0u) {
  47. config->erase(hash);
  48. throw throwing_allocator_exception{};
  49. } else {
  50. --elem;
  51. }
  52. }
  53. return allocator.allocate(length);
  54. }
  55. void deallocate(pointer mem, std::size_t length) {
  56. allocator.deallocate(mem, length);
  57. }
  58. template<typename Other>
  59. void throw_counter(const std::size_t len) {
  60. (*config)[entt::type_id<Other>().hash()] = len;
  61. }
  62. bool operator==(const throwing_allocator<Type> &) const {
  63. return true;
  64. }
  65. bool operator!=(const throwing_allocator<Type> &other) const {
  66. return !(*this == other);
  67. }
  68. private:
  69. std::allocator<Type> allocator;
  70. std::shared_ptr<container_type> config;
  71. };
  72. } // namespace test
  73. #endif