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]

bug: libstdc++ container allocator discrepancy


I'm posting this to gcc-bugs@gcc.gnu.org, because I haven't found a better
place for C++ standard library bug reports.  I'm not sure whether this
applies to the v3 library, but I'm sure it applies to the pre-v3 library,
because the problem occurs with the gcc 2.95.2 distribution (as well as
2.96).

See the comments in the following sample code for details.  It was tested
with two Linux distributions.

Forest Wilkinson

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


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