// demoalloc.C /* 11 August 2000 This program is intended to demonstrate a problem in the GNU Standard C++ Library which accompanies gcc 2.96 and 2.95.2. I have reproduced the same results on the Red Hat 7.0 (Pinstripe) beta, and on Linux Mandrake 7.0 (Air). The problem I have encountered is a discrepancy between allocator types expected by standard library classes. The STL container classes requires an allocator that follows the C++ standard, while the basic_string class requires an allocator that deviates from the standard. Specifically, the latter expects an allocator class to contain static allocate() and deallocate() member functions, whose arguments represent a number of bytes (rather than a number of objects). The result is that I cannot use strings and STL containers together in the same code, when I require them to use a non-default allocator. Examples of such cases include "plug-in" code designed to extend PostgreSQL, PHP, and Apache. These programs (and many others) have their own memory management routines, which means that plug-ins using C++ strings and STL containers must use allocators that call said routines. In other words, the default allocators are not an option in these cases. With libstdc++ in its current state, a coder in this situation would have to create two different versions of each allocator, to even hope for a working system. reported by: Forest Wilkinson fspam */ // Uncomment this line to define an allocator that works with the // basic_string class in libstdc++: //#define USE_NONSTANDARD_ALLOCATOR 1 #include <algorithm> #include <cstddef> #include <cstdlib> #include <climits> #include <string> #include <vector> // === C++ allocator class === // (uses malloc/free behind the scenes, and does not throw exceptions) // The allocator clas itself: template< class T> class my_allocator { public: // types: typedef T value_type; typedef std::size_t size_type; typedef std::ptrdiff_t difference_type; typedef T* pointer; typedef const T* const_pointer; typedef T& reference; typedef const T& const_reference; template< class U> struct rebind { typedef my_allocator<U> other; }; // in effect: typedef my_allocator<U> other // 'structors: my_allocator() throw() {} template< class U> my_allocator( const my_allocator<U> &foreign_al) throw() {} ~my_allocator() throw() {} // operations: pointer address( reference r) const { return &r; } const_pointer address( const_reference r) const { return &r; } #ifdef USE_NONSTANDARD_ALLOCATOR // allocate space for n bytes: static void *allocate( size_t nbytes) { return std::malloc( nbytes); } // deallocate n bytes: static void deallocate( void *p, size_t nbytes ) { std::free( p); } #else // allocate space for n objects of type T: pointer allocate( size_type n, const void* hint = 0) { return static_cast<pointer>( std::malloc( n * sizeof(T))); } // deallocate n objects of type T: void deallocate( pointer p, size_type /* n */ ) { std::free( p); } #endif // initialize *p with val: void construct( pointer p, const T& val) { new(static_cast<const void *>(p)) T(val); } // destroy *p (don't deallocate it): void destroy( pointer p) { p->~T(); } size_type max_size() const throw() { return std::max( size_type(1), size_type( UINT_MAX/sizeof(T)));} friend bool operator==<>( const my_allocator<T>&, const my_allocator<T>&); friend bool operator!=<>( const my_allocator<T>&, const my_allocator<T>&); private: // helper function, which allocates chars or throws an exception: static void *alloc_or_throw( size_t nchars); }; /* According to Stroustrup 3 (sec. 19.4.2 and 19.4.3), and the C++ Standard (ISO 14882 sec. 20.1.5): Standard container implementations may treat all allocators of the same type as equivalent. This makes operators == and != simpler, as they always return true and false, respectively. */ /* Two my_allocator objects should compare equal if one was constructed from the other. */ template< class T> inline bool operator==( const my_allocator<T>& a, const my_allocator<T>& b) throw() { return true; } /* Two my_allocator objects should compare equal if one was constructed from the other. */ template< class T> inline bool operator!=( const my_allocator<T>& a, const my_allocator<T>& b) throw() { return false; } // === main part of our demonstration === using namespace std; typedef basic_string< char, string_char_traits<char>, my_allocator<char> > my_char_string; typedef vector< char, my_allocator<char> > my_char_vector; int main( int argc, char *argv[]) { // This section compiles when my_allocator follows the C++ standard, // and defines per-opbject allocate/deallocate functions: my_char_vector iv; iv.push_back( 'a'); iv.push_back( 'b'); iv.erase( iv.begin()); // This section compiles only when my_allocator deviates from the C++ // standard, by defining static per-byte allocate/deallocate functions: my_char_string str; str = my_char_string( "hello, world."); return 0; } // === eof ===
Created attachment 2414 [details] Demonstration of libstdc++ basic_string / STL container allocator conflict.
This is indeed a bug in the libstdc++ implementation pre-3.4. For the 3.4 libstdc++ release, we've fixed this. This fix will not be ported to previous versions due to ABI stability requirements. -benjamin