Sorry for this late reply.
I agree that we should avoid re-inventing the wheel, and the std::allocator is a good design and it supports scoped allocators in the c++ 11 standard. However its interface does not fits the usages of HeapBlock quite well, so I came up with the following design:
To goal of this design is to add flexible memory allocator support to any HeapBlock related class with:
- scoped allocator model similar to those in the c++11 standard http://www.open-std.org/Jtc1/sc22/wg21/docs/papers/2008/n2554.pdf
- maximized compiler compitablity (make use of c++ templates as simple as possible)
First we start with the HeapBlockAllocator class:
// This class is copyable and comparable (==, !=)
class HeapBlockAllocator
{
public:
typedef void* (*AllocateCallback) (void* /*context*/, size_t /*numOfBytes*/, bool /*initialiseToZero*/);
typedef void* (*ReallocateCallback) (void* /*context*/, void* /*ptr*/, size_t /*newNumOfBytes*/);
typedef void (*DeallocateCallback) (void* /*context*/, void* /*ptr*/);
private:
void* context;
AllocateCallback allocateCallback;
ReallocateCallback reallocateCallback;
DeallocateCallback deallocateCallback;
public:
// other helper functions used by HeapBlock and related-containers
};
The HeapBlockAllocator stores a void* context pointer and 3 callback function pointers which makes it copyable and comparable (operator == and != ). It is an important property in implementing the scoped allocator feature.
Then we modify the HeapBlock template to store an instance of HeapBlockAllocator and modify its constructors to accept any existing instances of HeapBlockAllocator.
template <class ElementType, bool throwOnFailure = false >
class HeapBlock
{
public:
HeapBlock(const HeapBlockAllocator& alloc = HeapBlockAllocator::crt()) noexcept : allocator (alloc), data (nullptr)
{
}
// const HeapBlockAllocator& alloc = HeapBlockAllocator::crt() are also added to other constructors of HeapBlock
private:
HeapBlockAllocator allocator;
ElementType* data;
};
Since std::is_constructible<> is not available in older compilers, instead, we use a type traits UseHeapBlockAllocator<> to distinguish whether a type is supporting scoped allocator or not:
template<typename T>
struct UseHeapBlockAllocator { static const bool value = false; };
For any containers or objects would like to support scoped allocator, they should specialise the type traits.
template<typename T, bool B>
struct UseHeapBlockAllocator< HeapBlock<T, B> > { static const bool value = true; };
template<typename T>
struct UseHeapBlockAllocator< Array<T> > { static const bool value = true; };
The implemetation details is avaible here https://github.com/ming4883/JUCE/compare/julianstorer:master...master
For testing and demonstrating purposes, I have modified the juce::Array<> to support HeapBlockAllocator; and added a simple unit test on scoped allocator.
Please comments .