This is the mail archive of the gcc-bugs@gcc.gnu.org mailing list for the GCC project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]

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 ===


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]