WinLocalAllocResource.h
Code: Select all
#pragma once
#include <cassert>
#include <cstdint>
#include <memory_resource>
#include <Windows.h>
class WinLocalAllocResource : public std::pmr::memory_resource
{
void* do_allocate( std::size_t size_, std::size_t alignment_ )override
{
// Check if alignment_ is power of 2
assert( ( ( alignment_ & ( alignment_ - 1 ) ) == 0 ) && "Alignment must be a power of 2" );
// Verify size_ is a multiple of alignment_
assert( ( size_ % alignment_ ) == 0 );
if( auto memory = LocalAlloc( LMEM_FIXED, size_ ); memory != nullptr )
{
// Verify that the address returned is a multiple of alignment_
assert( ( reinterpret_cast< std::uintptr_t >( memory ) % alignment_ ) == 0 );
return memory;
}
throw std::bad_alloc();
}
void do_deallocate( void * memory_, size_t alloc_size_, size_t alignment_ )override
{
// A possible reason that this function throws if you pass in a pointer
// that wasn't allocated using LocalAlloc, the MSDN doesn't specify
// the reason so we can use std::system_error and std::error_code
// to have Windows tell us what we did wrong...most of the tiem.
if( LocalFree( memory_ ) != nullptr )
throw std::system_error(
std::error_code( GetLastError(), std::system_category() ),
"Deallocation failed"
);
}
bool do_is_equal( const memory_resource& other_ ) const noexcept override
{
// If RTTI is disabled or other_ isn't a WinLocalAllocResource,
// an exception is hopefully thrown and we can catch and return false
try
{
const auto& other_resource =
dynamic_cast< WinLocalAllocResource const& >( other_ );
return true;
}
catch( const std::exception& )
{
// We'll return false even for RTTI being disabled since we
// have no way of knowing if other_ is or isn't a WinLocalAllocResource
return false;
}
}
};
// This has no data to maintain so a global instance should be fine.
inline auto LocalAllocResource = WinLocalAllocResource{};
Code: Select all
template<typename T>
class WinLocalAlloc
{
public:
using value_type = T;
// Set true to let containers know to copy the allocator when container is copied
using propagate_on_container_copy_assignment = std::true_type;
// Set true to let containers know to move the allocator when container is moved
using propagate_on_container_move_assignment = std::true_type;
// We can compare always equal since WinLocalAlloc doesn't accept
// ouside memory resource classes
using is_always_equal = std::true_type;
public:
WinLocalAlloc() = default;
template<typename U> WinLocalAlloc( WinLocalAlloc<U> const& other_ )noexcept
:
m_resource( other_.resource() )
{}
// WinLocalAllocResource handles throwing if allocation not possible
T* allocate( std::size_t count_ ) {
auto* memory = m_resource->allocate( sizeof( T ) * count_, alignof( T ) );
return static_cast< T* >( memory );
}
void deallocate( T* memory_, std::size_t count_ ) {
m_resource->deallocate( memory_, sizeof( T ) * count_, alignof( T ) );
}
// Construct a T in-place at address pointed to by memory_ using args...
template<typename...Args>
void construct( T* memory_, Args...args_ )noexcept( noexcept( std::is_nothrow_constructible_v<T> ) )
{
new ( memory_ ) T{ std::forward<Args>( args_ )... };
}
// Objects constructed using placement new must have their destructors
// called explicitly
void destroy( T* memory_ )noexcept {
memory_->~T();
}
// Returns a copy of this allocator, when you copy one container
// to another, this function is called.
WinLocalAlloc select_on_container_copy_construction( WinLocalAlloc const& ) {
return *this;
}
// Used in conversion constructor since it's templated on a different type,
// we can't reach the private data of the other class
std::pmr::memory_resource* resource()const noexcept {
return m_resource;
}
private:
std::pmr::memory_resource* m_resource = std::addressof( LocalAllocResource );
};
template<typename T, typename U>
bool operator==( WinLocalAlloc<T> const& left_, WinLocalAlloc<U> const& right_ )noexcept {
return ( *left_.resource() ) == ( *right_.resource() );
}
template<typename T, typename U>
bool operator!=( WinLocalAlloc<T> const& left_, WinLocalAlloc<U> const& right_ )noexcept {
return !( left_ == right_ );
}
Code: Select all
#define WIN32_LEAN_AND_MEAN
#define NOMINMAX
#include "WinLocalAllocResource.h"
#include <vector>
#include <iostream>
class Object
{
public:
Object()
{
std::cout << "Default constructor called\n";
}
Object( Object const& other_ )
{
std::cout << "Copy constructor called\n";
}
Object( Object&& other_ )noexcept
{
std::cout << "Move constructor called\n";
}
Object& operator=( Object const& other_ )
{
if( this != std::addressof( other_ ) ) {}
std::cout << "Copy assignment operator called\n";
return *this;
}
Object& operator=( Object&& other_ )noexcept
{
if( this != std::addressof( other_ ) ) {}
std::cout << "Move assignment operator called\n";
return *this;
}
~Object()noexcept
{
std::cout << "Destructor called\n";
}
private:
char v4[ 99 ];
char v1;
bool v3;
double v5;
float v0;
int v2;
};
constexpr auto szObject = sizeof( Object );
constexpr auto alObject = alignof( Object );
int main( int argc, char* argv[] )
{
try
{
auto vec = std::vector<Object, WinLocalAlloc<Object>>( WinLocalAlloc<Object>{} );
for( int i = 0; i < 10; ++i )
{
vec.push_back( Object{} );
}
auto sp_complex =
std::allocate_shared<Object>( WinLocalAlloc<Object>() );
std::cout << sp_complex.use_count() << '\n';
{
auto sp2 = sp_complex;
std::cout << sp2.use_count() << '\n';
}
std::cout << sp_complex.use_count() << '\n';
}
catch( std::exception const& error )
{
std::cout << error.what() << '\n';
}
return 0;
}