This is the mail archive of the
gcc-bugs@gcc.gnu.org
mailing list for the GCC project.
Re: bug: libstdc++ container allocator discrepancy
On 15 Aug 2000 at 01:45 (-0700), Forest Wilkinson wrote:
| Uh, sure, by changing my bug demonstration code, one can probably get it
| to compile. That's fine for a temporary workaround. (Care to share the
| method you used?)
certainly.
| The fact remains that the "standard" basic_string class
| is broken, because it requires its own nonstandard allocator class, which
| is different from the STL containers' requirements.
|
| Also, I suspect that the namespace trickery you used was arrived at
| through a good understanding of the basic_string implementation. Such
| knowledge can't reasonably be assumed in your average users, and I for one
| would probably fall asleep reading through the source code to gain said
| knowledge. Furthermore, depending on this nonstandard implementation
| means forcing us into writing nonportable code. (yuck.)
|
| In short, while I'd like to see your workaround, I hope it doesn't mean
| the problem won't be fixed.
The namespace problem with v3 currently means that standard C
types/functions ard not within 'namepace std', so I had to remove
std:: from size_t,prtdiff_t,malloc,free.... this is a known issue,
which is being worked on, however this 'workaround' does not produce
non-portable code, AFAIK.
Changed string_char_traits to char_traits, as ther was no occurrence
of the char sequence 'string_char_traits' defined in my version of
14882.
| Can someone tell me whether the v3 library's basic_string uses standard
| allocators?
It appears to (but I claim _no_ level of guru-ness to be able to
definitively say yes/no without studying chapter 20).
hth.
brent
// (working,modified program follows)
// 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 (at) home (dot) com
*/
// 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 size_t size_type;
typedef 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 malloc( nbytes); }
// deallocate n bytes:
static void deallocate( void *p, size_t nbytes )
{ free( p); }
#else
// allocate space for n objects of type T:
pointer allocate( size_type n, const void* hint = 0)
{ return static_cast<pointer>( malloc( n * sizeof(T))); }
// deallocate n objects of type T:
void deallocate( pointer p, size_type /* n */ )
{ 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, 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 ===