Create std-compliant allocator that wraps arbitrary allocator for diagnostics

61 views Asked by At

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:

  1. How to get modern rebind working as rebind has been deprecated in C++17
  2. 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?

0

There are 0 answers