Dr. Dobb's is part of the Informa Tech Division of Informa PLC

This site is operated by a business or businesses owned by Informa PLC and all copyright resides with them. Informa PLC's registered office is 5 Howick Place, London SW1P 1WG. Registered in England and Wales. Number 8860726.


Channels ▼
RSS

The Standard Librarian: A Debugging Allocator


December 2001 C++ Experts Forum/The Standard Librarian/Listing 1

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;
}

Related Reading


More Insights






Currently we allow the following HTML tags in comments:

Single tags

These tags can be used alone and don't need an ending tag.

<br> Defines a single line break

<hr> Defines a horizontal line

Matching tags

These require an ending tag - e.g. <i>italic text</i>

<a> Defines an anchor

<b> Defines bold text

<big> Defines big text

<blockquote> Defines a long quotation

<caption> Defines a table caption

<cite> Defines a citation

<code> Defines computer code text

<em> Defines emphasized text

<fieldset> Defines a border around elements in a form

<h1> This is heading 1

<h2> This is heading 2

<h3> This is heading 3

<h4> This is heading 4

<h5> This is heading 5

<h6> This is heading 6

<i> Defines italic text

<p> Defines a paragraph

<pre> Defines preformatted text

<q> Defines a short quotation

<samp> Defines sample computer code text

<small> Defines small text

<span> Defines a section in a document

<s> Defines strikethrough text

<strike> Defines strikethrough text

<strong> Defines strong text

<sub> Defines subscripted text

<sup> Defines superscripted text

<u> Defines underlined text

Dr. Dobb's encourages readers to engage in spirited, healthy debate, including taking us to task. However, Dr. Dobb's moderates all comments posted to our site, and reserves the right to modify or remove any content that it determines to be derogatory, offensive, inflammatory, vulgar, irrelevant/off-topic, racist or obvious marketing or spam. Dr. Dobb's further reserves the right to disable the profile of any commenter participating in said activities.

 
Disqus Tips To upload an avatar photo, first complete your Disqus profile. | View the list of supported HTML tags you can use to style comments. | Please read our commenting policy.