There is a custom allocator class:
template<typename T>
class pool_allocator {
public:
using value_type = T;
using pointer = value_type *;
/* Default constructor */
constexpr pool_allocator( void ) noexcept = default;
/* Converting constructor used for rebinding */
template<typename U>
constexpr pool_allocator( const pool_allocator<U> & ) noexcept {}
[[nodiscard]] pointer allocate( size_t n, [[maybe_unused]] const pointer hint = nullptr ) const noexcept {
return get_pool().allocate( n );
}
void deallocate( pointer ptr, size_t n ) noexcept {
get_pool().deallocate( ptr, n );
}
private:
template<size_t CAPACITY>
memory_pool<value_type, CAPACITY> & get_pool( void ) noexcept;
};
The pool_allocator
serves as the allocator to a memory_pool
implementation:
template<typename T, size_t CAPACITY>
class memory_pool {
public:
using element_type = T;
using pointer = element_type *;
static constexpr size_t capacity { CAPACITY };
[[nodiscard]] inline pointer allocate( [[maybe_unused]] size_t n = 1 ) noexcept { ... }
inline void deallocate( pointer ptr, [[maybe_unused]] size_t n = 1 ) noexcept { ... }
};
The problem is there are many instantiations of memory pool for particular type of various sizes. For instance:
struct sample { /* ... */ };
memory_pool<sample, 10> m_samples; /* Serves for 10 instances of 'sample' type */
Once tried to use the pool_allocator
to allocate the instance from the m_samples
pool:
allocator_traits<pool_allocator<sample>>::allocate( pool_allocator<sample> {}, 1 );
It is required to "link" concrete memory_pool
instance with the allocator. Let's consider, the type T
matches every time, but the allocator shall not be aware of the memory_pool
capacity. So my thoughts were to declare a getter for particular memory_pool
type get_pool()
- to declare it within the allocator but to have it defined in a .cpp file to implement the link, something like:
.cpp:
template<> template<>
memory_pool<sample, 10> & pool_allocator<sample>::get_pool( void ) noexcept {
return m_samples; /* The instance being created before */
}
So my question is: How to make this implemented? Simply to have the option to use type specific pool_allocator<sample>
to operate with concrete memory_pool
instance for the same type but not to specify the capacity as explicit pool_allocator
template parameter... to make it deduced? To use delegate/function pointer instead? Any other way of doing so?
The solution is quite simple (only important parts are listed):
template<typename T>
class pool_allocator {
public:
[[nodiscard]] pointer allocate( size_t n, [[maybe_unused]] const pointer hint = nullptr ) const noexcept {
return get_pool().allocate( n );
}
void deallocate( pointer ptr, size_t n ) noexcept {
get_pool().deallocate( ptr, n );
}
private:
static auto & get_pool( void ) noexcept; /* <-- here it comes */
};
and then somewhere in a .cpp file, specialize the get_pool()
static member for particular type:
template<>
auto & pool_allocator<sample>::get_pool( void ) noexcept {
return m_samples;
}
So simple auto declaration does the trick. Maybe the next step to improve would be to constrain the get_pool()
return value by a concept, but it is another story...