As part of a unit test suite for custom std-compatible containers, I want to do some memory tracking. The easiest thing is to define a new allocator and track allocations and deallocations with it, and call malloc and free. However, there are cases when I want to use a different allocator as a sub allocator and route all allocations through it. This is kind of working except for the rebind required by containers. It is also limited to the std::allocator, which is not great for unit testing with other allocators. I am using C++/17 to do this.
So far I have this working example allocator, but the type of the base allocator is fixed - it only works with std::allocators.
template <typename T, typename BaseAllocator = std::allocator<T> >
struct TrackingAllocator
{
typedef T value_type;
TrackingAllocator() = default;
TrackingAllocator( const TrackingAllocator& aOther ) noexcept = default;
template <class U>
constexpr TrackingAllocator( const TrackingAllocator<U, std::allocator<U>>& ) noexcept {}
// recognize this is old style; would like to move to new style C++17 rebind
template <class U>
struct rebind
{
using other = TrackingAllocator<U, std::allocator<U>>;
};
BaseAllocator m_allocator;
T* allocate( std::size_t n )
{
// track allocation
return m_allocator.allocate( n );
}
void deallocate( T* p, std::size_t n ) noexcept
{
// track deallocation
m_allocator.deallocate( p, n );
}
// allocation logging
};
However, I need it to work with any kind of allocator:
std::vector<int, TrackingAllocator< FixedAllocator<int> > > vector_of_ints;
My goal is to be able to write tests such as:
TrackingAllocator<int> trackingAllocator;
{
CustomContainer<TrackingAllocator<int>> container ( trackingAllocator );
// a bunch of allocation-related operations
EXPECT_EQ( trackingAllocator.allocationCount, the_expected_number );
container.shrink_to_fit();
EXPECT_EQ( trackingAllocator.allocationCount, the_expected_number );
}
// container out of scope, free memory in container
EXPECT_EQ( trackingAllocator.allocationCount, 0 );
But I am struggling with two issues:
- How to get modern rebind working as rebind has been deprecated in C++17
- Rebind the sub allocator to the new type U as well.
The issue I am getting is that I cannot return the correct T* from the rebound allocator in this call:
T* allocate( std::size_t n )
{
// m_allocator's T and the rebound Tracking
// allocator's T are not related.
return m_allocator.allocate( n );
}
Any way I can solve this?