/** This file is a part of rexy's general purpose library Copyright (C) 2022 rexy712 This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifndef REXY_COMPAT_CPP20_DEFAULT_ALLOCATOR_HPP #define REXY_COMPAT_CPP20_DEFAULT_ALLOCATOR_HPP #include //ptrdiff_t, size_t #include //true_type, false_type #include #include //numeric_limits #include //allocator #ifdef __cpp_concepts #include //convertible_to template concept Allocator = requires(T a, typename T::size_type sz, typename T::pointer pa){ typename T::value_type; typename T::size_type; typename T::pointer; typename T::const_pointer; {a.allocate(sz)} -> std::convertible_to; {a.deallocate(pa, sz)}; }; #define REXY_ALLOCATOR_CONCEPT Allocator #else //__cpp_concepts #define REXY_ALLOCATOR_CONCEPT class #endif // __cpp_concepts #include "../../rexy.hpp" #include "../if_consteval.hpp" namespace rexy{ template struct allocator { using pointer = T*; using const_pointer = const T*; using void_pointer = void*; using const_void_pointer = const void*; using value_type = T; using size_type = std::size_t; using difference_type = std::ptrdiff_t; using is_always_equal = std::true_type; using propagate_on_container_copy_assignment = std::false_type; using propagate_on_container_move_assignment = std::false_type; using propagate_on_container_swap = std::false_type; template struct rebind{ using other = allocator; }; private: constexpr bool has_overflow(size_type n)const{ return n > max_size(); } public: constexpr allocator(void) = default; constexpr allocator(const allocator&) = default; constexpr allocator(allocator&&) = default; template constexpr allocator(const allocator&)noexcept{} constexpr ~allocator(void) = default; //'::operator new' is never a constexpr call as of C++23. However, 'std::allocator::allocate' *is* transiently constexpr as of C++20, //even though it directly calls '::operator new' as is stated in the standard. Therefore, when evaluating this call with support for //'if consteval', we can use the 'std::allocator::allocate' constexpr-ness when this is in a constant evaluation context. constexpr pointer allocate(size_type n){ size_type bytes = has_overflow(n) ? std::numeric_limits::max() : n*sizeof(T); REXY_if_consteval{ std::allocator a; const size_type num_items = n / sizeof(value_type); return a.allocate(num_items); }else{ if constexpr(alignof(T) <= __STDCPP_DEFAULT_NEW_ALIGNMENT__){ return reinterpret_cast(::operator new(bytes)); }else{ return reinterpret_cast(::operator new(bytes, static_cast(alignof(T)))); } } //if consteval } constexpr void deallocate(pointer p, size_type n){ REXY_if_consteval{ std::allocator a; const size_type num_items = n / sizeof(value_type); return a.deallocate(p, num_items); }else{ #if !defined(__cpp_sized_deallocation) //clang does not enable ::operator delete(void* ptr, std::size_t sz) by default for some reason if constexpr(alignof(T) <= __STDCPP_DEFAULT_NEW_ALIGNMENT__){ ::operator delete(p); }else{ ::operator delete(p, static_cast(alignof(T))); } #else //__cpp_sized_deallocation size_type bytes = has_overflow(n) ? std::numeric_limits::max() : n*sizeof(T); if constexpr(alignof(T) <= __STDCPP_DEFAULT_NEW_ALIGNMENT__){ ::operator delete(p, bytes); }else{ ::operator delete(p, bytes, static_cast(alignof(T))); } #endif //__cpp_sized_deallocation } //if consteval } constexpr size_type max_size(void)const{ return std::numeric_limits::max()/sizeof(T); } }; } #endif