Listing 1: Complete implementation of the debugging allocator

template <class Allocator, class T = typename Allocator::value_type>
class debug_allocator {
public:                        // Typedefs from underlying allocator.
  typedef typename Allocator::size_type       size_type;
  typedef typename Allocator::difference_type difference_type;
  typedef typename Allocator::pointer         pointer;
  typedef typename Allocator::const_pointer   const_pointer;
  typedef typename Allocator::reference       reference;
  typedef typename Allocator::const_reference const_reference;
  typedef typename Allocator::value_type      value_type;

  template <class U> struct rebind {
   typedef typename Allocator::template rebind<U>::other A2;
   typedef debug_allocator<A2, typename A2::value_type> other;
  };

public:                        // Constructor, destructor, etc.
  // Default constructor.
  debug_allocator()
   : alloc(), hash_code(0)
   { compute_hash(); }

  // Constructor from an underlying allocator.
  template <class Allocator2>
  debug_allocator(const Allocator2& a)
   : alloc(a), hash_code(0)
   { compute_hash(); }

  // Copy constructor.
  debug_allocator(const debug_allocator& a)
   : alloc(a.alloc), hash_code(0)
   { compute_hash(); }

  // Generalized copy constructor.
  template <class A2, class T2>
  debug_allocator(const debug_allocator<A2, T2>& a)
   : alloc(a.alloc), hash_code(0)
   { compute_hash(); }

  // Destructor.
  ~debug_allocator() {}


public:                        // Member functions. 
                               // The only interesting ones 
                               // are allocate and deallocate.
  pointer allocate(size_type n, const void* = 0);
  void deallocate(pointer p, size_type n);

  pointer address(reference x) const { return a.address(x); }
  const_pointer address(const_reference x) const {
    return a.address(x);
  }

  void construct(pointer p, const value_type& x);
  void destroy(pointer p);

  size_type max_size() const { return a.max_size(); }

  friend bool operator==(const debug_allocator& x,
                         const debug_allocator& y)
   { return x.alloc == y.alloc; }

  friend bool operator!=(const debug_allocator& x, 
                         const debug_allocator& y)
   { return x.alloc != y.alloc; }

private:
  typedef typename Allocator::template rebind<char>::other
    char_alloc;
  typedef typename Allocator::template rebind<std::size_t>::other
    size_alloc;

  // Calculate the hash code, and store it in this->hash_code. 
  // Only used in the constructor.
  void compute_hash();

  const char* hash_code_as_bytes()
   { return reinterpret_cast<const char*>(&hash_code); }

  // Number of bytes required to store n objects of type value_type.
  // Does not include the overhead for debugging.
  size_type data_size(size_type n)
   { return n * sizeof(value_type); }

  // Number of bytes allocated in front of the user-visible memory
  // block. Must be large enough to store two objects of type
  // size_t, and to preserve alignment.
  size_type padding_before(size_type)
   { return 2 * sizeof(std::size_t); }

  // Number of bytes from the beginning of the block we allocate
  // until the beginning of the sentinel.
  size_type sentinel_offset(size_type n)
   { return data_size(n) + padding_before(n); }

  // Number of bytes in the sentinel.
  size_type sentinel_size()
   { return sizeof(std::size_t); }

  // Size of the area we allocate to store n objects,
  // including overhead.
  size_type total_bytes(size_type n)
  { return data_size(n) + padding_before(n) + sentinel_size(); }

  Allocator alloc;
  std::size_t hash_code;
};


// Specialization when the value type is void. We provide typedefs
// (and not even all of those), and we save the underlying allocator
// so we can convert back to some other type.

template <class Allocator>
class debug_allocator<Allocator, void> {
public:
  typedef typename Allocator::size_type       size_type;
  typedef typename Allocator::difference_type difference_type;
  typedef typename Allocator::pointer         pointer;
  typedef typename Allocator::const_pointer   const_pointer;
  typedef typename Allocator::value_type      value_type;

  template <class U> struct rebind {
   typedef typename Allocator::template rebind<U>::other A2;
   typedef debug_allocator<A2, typename A2::value_type> other;
  };

  debug_allocator() : alloc() { }

  template <class A2>
  debug_allocator(const A2& a) : alloc(a) { }

  debug_allocator(const debug_allocator& a) : alloc(a.alloc) { }

  template <class A2, class T2>
  debug_allocator(const debug_allocator<A2, T2>& a) :
    alloc(a.alloc) { }

  ~debug_allocator() {}

private:
  Allocator alloc;
};


// Noninline member functions for debug_allocator. They are not defined
// for the void specialization.

template <class Allocator, class T>
typename debug_allocator<Allocator, T>::pointer
debug_allocator<Allocator, T>::allocate
  (size_type n, const void* = 0) {
  assert(n != 0);

  // Allocate enough space for n objects of type T, plus the debug 
  // info at the beginning, plus a one-byte sentinel at the end.
  typedef typename char_alloc::pointer char_pointer;
  typedef typename size_alloc::pointer size_pointer;
  char_pointer result = char_alloc(alloc).allocate(total_bytes(n));

  // Store the size.
  size_pointer debug_area = size_pointer(result);
  size_alloc(alloc).construct(debug_area + 0, n);

  // Store a hash code based on the type name.
  size_alloc(alloc).construct(debug_area + 1, hash_code);

  // Store the sentinel, which is just the hash code again. 
  // For reasons of alignment, we have to copy it byte by byte.
  typename char_alloc::pointer sentinel_area =
    result + sentinel_offset(n);
  const char* sentinel = hash_code_as_bytes();
  {
   char_alloc ca(alloc);
   int i = 0;
   try {
     for ( ; i < sentinel_size(); ++i)
       ca.construct(sentinel_area + i, sentinel[i]);
   }
   catch(...) {
     for (int j = 0; j < i; ++j)
       ca.destroy(&*(sentinel_area + j));
     throw;
   }
  }

  // Return a pointer to the user-visible portion of the memory.
  pointer data_area = pointer(result + padding_before(n));
  return data_area;
}

template <class Allocator, class T>
void debug_allocator<Allocator, T>::deallocate
  (pointer p, size_type n) {
  assert(n != 0);

  // Get a pointer to the space where we put the debugging information.
  typedef typename char_alloc::pointer char_pointer;
  typedef typename size_alloc::pointer size_pointer;
  char_pointer cp = char_pointer(p);
  size_pointer debug_area = size_pointer(cp - padding_before(n));

  // Get the size request and the hash code, and check for consistency.
  size_t stored_n    = debug_area[0];
  size_t stored_hash = debug_area[1];
  assert(n == stored_n);
  assert(hash_code == stored_hash);

  // Get the sentinel, and check for consistency.
  char_pointer sentinel_area =
    char_pointer(debug_area) + sentinel_offset(n);
  const char* sentinel = hash_code_as_bytes();
  assert(std::equal(sentinel, sentinel + sentinel_size(),
    sentinel_area));

  // Destroy our debugging information.
  size_alloc(alloc).destroy(debug_area + 0);
  size_alloc(alloc).destroy(debug_area + 1);
  for (size_type i = 0; i < sentinel_size(); ++i)
    char_alloc(alloc).destroy(sentinel_area + i);

  // Release the storage.
  char_alloc(alloc).deallocate(cp - padding_before(n), total_bytes(n));
}

template <class Allocator, class T>
void debug_allocator<Allocator, T>::construct(pointer p, const 
value_type& x)
{
  assert(p);
  a.construct(p, x);
}

template <class Allocator, class T>
void debug_allocator<Allocator, T>::destroy(pointer p)
{
  assert(p);
  a.destroy(p);
}

template <class Allocator, class T>
void debug_allocator<Allocator, T>::compute_hash() {
  const char* name = typeid(value_type).name();
  hash_code = 0;
  for ( ; *name != '\0'; ++name)
   hash_code = hash_code * (size_t) 37 + (size_t) *name;
}