#ifndef ENTT_COMMON_THROWING_ALLOCATOR_HPP #define ENTT_COMMON_THROWING_ALLOCATOR_HPP #include #include #include #include #include #include #include #include namespace test { struct throwing_allocator_exception {}; template class throwing_allocator { template friend class throwing_allocator; template requires (!std::same_as || std::constructible_from, std::allocator>) throwing_allocator(int, const throwing_allocator &other) : allocator{other.allocator}, config{other.config} {} template requires std::same_as || std::constructible_from, std::allocator> throwing_allocator(char, const throwing_allocator &other) : allocator{}, config{other.config} {} public: using value_type = Type; using pointer = value_type *; using const_pointer = const value_type *; using void_pointer = void *; using const_void_pointer = const void *; using propagate_on_container_move_assignment = std::true_type; using propagate_on_container_swap = std::true_type; using container_type = entt::dense_map; template struct rebind { using other = throwing_allocator; }; throwing_allocator() : allocator{}, config{std::allocate_shared(allocator)} {} template throwing_allocator(const throwing_allocator &other) // std::allocator has no cross constructors (waiting for C++20) : throwing_allocator{0, other} {} pointer allocate(std::size_t length) { if(const auto hash = entt::type_id().hash(); config->contains(hash)) { if(auto &elem = (*config)[hash]; elem == 0u) { config->erase(hash); throw throwing_allocator_exception{}; } else { --elem; } } return allocator.allocate(length); } void deallocate(pointer mem, std::size_t length) { allocator.deallocate(mem, length); } template void throw_counter(const std::size_t len) { (*config)[entt::type_id().hash()] = len; } [[nodiscard]] bool operator==(const throwing_allocator &) const noexcept { return true; } private: std::allocator allocator; std::shared_ptr config; }; } // namespace test #endif