'Getting weird compiler errors while compiling my own "allocator<>" with MSVC 2022 in Debug-mode
I need an std::allocator<> compliant allocator with page-aligned allocation and preferrably no memory-pooling for very large allocations. Another requirement is that the allocator should have a non-standard reset()-method to reset a row of pages that the pages become discardable by the kernel if you have a row of trivial data types.
I wrote such an allocator which I post here below completely since I think others could use the allocator also if they also have this certain allocation constraints.
#pragma once
#if defined(_WIN32)
#include <Windows.h>
#elif defined(__unix__)
#include <unistd.h>
#include <sys/mman.h>
#else
#error platform not supported
#endif
#include <cstddef>
#include <type_traits>
#include <memory>
#include <utility>
#include <atomic>
#include <cassert>
#include <new>
#if !defined(NDEBUG)
#include <bit>
#endif
#include <utility>
template <typename T>
struct virtual_allocator
{
using allocator_type = virtual_allocator;
using value_type = T;
using size_type = std::size_t;
using difference_type = std::ptrdiff_t;
using propagate_on_container_move_assignment = std::false_type;
constexpr virtual_allocator() noexcept {}
constexpr virtual_allocator( virtual_allocator const & ) noexcept {}
static value_type *allocate( std::size_t n );
static void deallocate( value_type *p, std::size_t n ) noexcept;
static void reset( void *p, std::size_t n )
requires (sizeof(T) == std::bit_floor( sizeof(T) )) && std::is_trivial_v<T>;
static std::size_t get_page_size();
#if defined(__cpp_lib_allocate_at_least)
static std::allocation_result<value_type *> allocate_at_least( std::size_t n );
#endif
private:
using alloc_ret_t = std::pair<value_type *, std::size_t>;
static alloc_ret_t allocateAtLeast( std::size_t n );
static std::size_t getPageSize();
};
template <typename T>
typename virtual_allocator<T>::value_type *virtual_allocator<T>::allocate( std::size_t n )
{
return allocateAtLeast( n ).first;
}
template <typename T>
void virtual_allocator<T>::deallocate( value_type *p, std::size_t n ) noexcept
{
std::size_t pageSize = getPageSize();
assert(n * pageSize / pageSize == n);
n = n * pageSize + pageSize - 1 & -(ptrdiff_t)pageSize;
#if defined(_WIN32)
bool succ = (bool)VirtualFree( p, 0, MEM_RELEASE );
#elif defined(__unix__)
bool succ = !munmap( p, n );
#endif
assert(succ);
}
#if defined(__cpp_lib_allocate_at_least)
template <typename T>
std::allocation_result<typename virtual_allocator<T>::value_type *> virtual_allocator<T>::allocate_at_least( std::size_t n )
{
auto ret = allocateAtLeast( n );
return std::allocation_result<value_type *>( ret.first, ret.second );
}
#endif
template <typename T>
typename virtual_allocator<T>::alloc_ret_t virtual_allocator<T>::allocateAtLeast( std::size_t n )
{
using namespace std;
if( n * sizeof(value_type) / sizeof(value_type) != n )
throw bad_alloc();
size_t pageSize = getPageSize();
n = n * sizeof(value_type) + pageSize - 1 & -(ptrdiff_t)pageSize;
#if defined(_WIN32)
value_type *p = (value_type *)VirtualAlloc( nullptr, n, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE );
#elif defined(__unix__)
value_type *p = (value_type *)mmap( nullptr, n, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0 );
#endif
if( !p ) [[unlikely]]
throw bad_alloc();
return alloc_ret_t( p, n / sizeof(value_type) );
}
template <typename T>
void virtual_allocator<T>::reset( void *p, std::size_t n )
requires (sizeof(T) == std::bit_floor( sizeof(T) )) && std::is_trivial_v<T>
{
using namespace std;
if( ((size_t)p | n) & getPageSize() - 1 || n * sizeof(value_type) / sizeof(value_type) != n ) [[unlikely]]
throw bad_alloc();
#if defined(_WIN32)
bool succ = (bool)VirtualAlloc( p, n, MEM_RESET, PAGE_READWRITE );
#elif defined(__unix__)
bool succ = !madvise( p, n, MADV_DONTNEED );
#endif
assert(succ);
}
template <typename T>
inline
std::size_t virtual_allocator<T>::get_page_size()
{
return getPageSize();
}
template <typename T>
inline
std::size_t virtual_allocator<T>::getPageSize()
{
using namespace std;
static atomic<size_t> aPageSize( 0 );
size_t pageSize = aPageSize.load( memory_order_relaxed );
if( !pageSize ) [[unlikely]]
{
#if defined(_WIN32)
SYSTEM_INFO si;
GetSystemInfo( &si );
pageSize = si.dwPageSize;
#elif defined(__unix__)
pageSize = sysconf( _SC_PAGESIZE );
#endif
assert(pageSize && pageSize == bit_floor( pageSize ));
aPageSize.store( pageSize, memory_order_relaxed );
}
return pageSize;
}
The testing code compiles fine with g++ 11.1.0 and clang++ 13 under Linux. Under Windows it compiles if you have a "Release" compile but not in "Debug" mode. I get some weird errors for which I don't have any clue what they're abut.
Severity Code Description Project File Line Suppression State
Error C2440 'static_cast': cannot convert from 'virtual_allocator<array_t>' to 'virtual_allocator<_Newfirst>' virtual_allocator C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.31.31103\include\vector 595
Error C2530 '_Alproxy': references must be initialized virtual_allocator C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.31.31103\include\vector 595
Error C3536 '_Alproxy': cannot be used before it is initialized virtual_allocator C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.31.31103\include\vector 596
Error C2672 '_Delete_plain_internal': no matching overloaded function found virtual_allocator C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.31.31103\include\vector 596
Error C2893 Failed to specialize function template 'void std::_Delete_plain_internal(_Alloc &,_Alloc::value_type *const ) noexcept' virtual_allocator C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.31.31103\include\vector 596
Although I think the code would be helpful for other C++-developers I don't post this with any C++-tags. That's while if others search such an allcator they'll find it by googling anyway and my question is only related to Visual C++.
Solution 1:[1]
Yeah, Igor got it ! It was the missing templated constructor.
template<typename T2>
constexpr virtual_allocator( virtual_allocator<T2> const & ) noexcept {}
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
Solution | Source |
---|---|
Solution 1 |