Shouldn't stringstream ONLY use the allocator object?

Carlo Wood carlo@alinoe.com
Tue May 8 08:57:00 GMT 2001


Hi all,

I did run into a very nasty bug of my own library when trying to
port it to libstdc++.  A problem that is caused by the fact that
std::basic_stringstream<char, std::char_traits<char>, my_allocator>
is calling operator new and delete directly, without using
'my_allocator'.

My question is: shouldn't classes that take allocators *only* use
those allocators to allocate memory?  Or is the allocator object
solely intended to allocate space for the buffer that stores the
data being written to it?  This is not clear to me when reading
the standard.

Secondly, shouldn't it use allocator::contruct() and allocator::destroy()
instead of new with placement etc for every object that is actually
allocated by calling allocator::allocate()?  Because it doesn't.
For example in g++-v3/bits/basic_string.tcc we find in
  template<typename _CharT, typename _Traits, typename _Alloc>
      basic_string<_CharT, _Traits, _Alloc>::_Rep*
      basic_string<_CharT, _Traits, _Alloc>::_Rep::
      _S_create(size_t __capacity, const _Alloc& __alloc) :

      void* __place = _Raw_bytes_alloc(__alloc).allocate(__size);
      _Rep *__p = new (__place) _Rep;

I am pretty sure it should use _Raw_bytes_alloc(__alloc).construct()
there.


Here is a test program to back up my first problem:

---------------------------------------------------------------
#include <new>
#include <cstdlib>
#define _GLIBCPP_FULLY_COMPLIANT_HEADERS
#include <sstream>
#include <iostream>

#include <unistd.h>	// Needed for 'write'
static bool write_enabled = false;
#define print(x) if (write_enabled) write(1, x, sizeof(x));

void* operator new(size_t size)
{
  print("Calling new\n");
  return malloc(size);
}

void* operator new[](size_t size)
{
  print("Calling new[]\n");
  return malloc(size);
}

void operator delete(void* ptr)
{
  print("Calling delete\n");
  free(ptr);
}

void operator delete[](void* ptr)
{
  print("Calling delete[]\n");
  free(ptr);
}

class my_allocator : public std::allocator<char> {
public:
  template <class _Tp1> struct rebind {
    typedef my_allocator other;
  };
  char* allocate(size_type __n, const void* = 0)
  {
    print("Calling allocate()\n");
    write_enabled = false;
    char* p = new char [__n];
    write_enabled = true;
    return p;
  }
  void deallocate(pointer __p, size_type __n)
  {
    print("Calling deallocate()\n");
    write_enabled = false;
    delete [] __p;
    write_enabled = true;
  }
};

typedef std::basic_stringstream<char, std::char_traits<char>, my_allocator> my_stringstream;

size_t size;

void test(void)
{
  print("Creating my_stringstream object:\n");
  my_stringstream ss;
  print("Writing 12 characters to stringstream:\n");
  ss << "Test " << 123456 << '\n';
  print("Writing 2000 characters to stringstream:\n");
  for(int i = 0; i < 20; ++i)
    ss << "0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789";
  print("Done writing to stringstream.\n");
  size = ss.rdbuf()->pubseekoff(0, std::ios_base::cur, std::ios_base::out) -
         ss.rdbuf()->pubseekoff(0, std::ios_base::cur, std::ios_base::in);
  print("Destructing my_stringstream object:\n");
}

int main(void)
{
  write_enabled = true;
  print("Start test\n");
  test();
  print("End test\n");
  write_enabled = false;
  std::cout << "Size of stringstream buffer was: " << size << '\n';
  return 0;
}
---------------------------------------------------------------

The output of this program is:

Start test
Creating my_stringstream object:
Calling new					<--- 1
Writing 12 characters to stringstream:
Calling allocate()
Calling new					<--- 2
Calling new					<--- 3
Writing 2000 characters to stringstream:
Calling allocate()
Calling deallocate()
Calling allocate()
Calling deallocate()
Done writing to stringstream.
Destructing my_stringstream object:
Calling deallocate()
Calling delete					<--- 3
Calling delete					<--- 2
Calling delete					<--- 1
End test
Size of stringstream buffer was: 2012

As you see, it calls three times 'new' directly; without
using the allocator object.

-- 
Carlo Wood <carlo@alinoe.com>



More information about the Libstdc++ mailing list