My lallocator<T>

Discussion in 'C++' started by Oliver S., Oct 27, 2005.

  1. Oliver S.

    Oliver S. Guest

    I was often annoyed about the performance of std::basic-string because
    of complex memory-allocators working in the background to found the work
    of std::allocator<T>. Basically, the performance doesn't reeach the per-
    formance of a stack-based C-string in most cases. But nevertheless, the
    comfort and the implicit protection against buffer-overflows makes std::
    basic_string woth to be used. So I searched for a way to have my own
    stack-based memory-allocation. I came up with the following idea which
    resulted in a class I called the lallocator (derived from local alloca-
    tor; this sounds funny in german because to "lall" means to babble in
    german *g*).
    First, we have the lallocator-class which is an interface to the locally
    allocated storage for the stl-object (my lallocator can't be used for
    strings only). This class is the same for all sizes of local stack-based
    pools to prevent that we would compile derived stl-classes for any size
    we use. Second, there's a class called lallocator_buffer_if which is the
    interface to the local storage we allocated for the lallocator. I defined
    this interface for all sizes of locally allocated pools to prevent any
    secial-buffer-size-compiling I mentioned before. Third there's a dervied
    class of lallocator_buffer_if<T>, called lallocator_buffer<T, buffers,
    size>; T is the usual data-type in the buffers, buffers is the number of
    buffers which can be allocated by the lallocator and size is the size of
    each buffer. Whenever someone tries to do an allocate() on the lallocator
    and there's a free buffer and the buffer is large enough to satisfy the
    allocation-request, we'll allocate the buffer from the pool; otherwise
    we fall back to std::allocator.

    Here's an example of how this is used:

    typedef std::basic_string<char, char_traits<char>, lallocator<char> >
    lallostring;

    lallocator_buffer<char, 5, 128> lbc;
    lallocator<char> lallo( &lbc );
    lallostring lsTest( lallo );

    lsTest = "sdsdass";

    Unfortunately my compiler isn't able to eat the following code
    to prevent explicit instanciation of a lallocator<T>-object:

    lallocator_buffer<char, 5, 128> lbc;
    lallostring lsTest( lallocator<char>( &lbc ) );

    Does anyone know if there's a conformance-problem here or is this
    just a bug of my compiler?


    So here's my lalloator (it isn't fully stl-conformant and I used a single
    trick to prevent having an additional pointer in lallocator_buffer_if which
    causes my code not to work on theoretical C++-implementations; sorry to all
    religious developers):



    template<typename T>
    class lallocator_buffer_if
    {
    protected:
    template<typename T, std::size_t buffers, std::size_t buffer_size>
    friend class lallocator_buffer;

    template<typename T>
    friend class lallocator;

    private:
    lallocator_buffer_if() {}
    T *pop_buffer();
    void push_buffer( T *pt );
    bool is_yours( T *pt );

    protected:
    union buffer_header
    {
    buffer_header *pbhNextFree;
    T at[1];
    };

    protected:
    std::size_t m_buffer_size;
    buffer_header *m_pbhFirstFree;
    buffer_header *m_pbhBufferEnd;
    };

    template<typename T>
    inline
    T *lallocator_buffer_if<T>::pop_buffer()
    {
    buffer_header *pbh;

    if( (pbh = m_pbhFirstFree) == NULL )
    return NULL;

    return m_pbhFirstFree = pbh->pbhNextFree,
    &pbh->at[0];
    }


    template<typename T>
    inline
    void lallocator_buffer_if<T>::push_buffer( T *pt )
    {
    buffer_header *pbh;

    pbh = (buffer_header *)pt;
    pbh->pbhNextFree = m_pbhFirstFree;
    m_pbhFirstFree = pbh;
    }

    template<typename T>
    inline
    bool lallocator_buffer_if<T>::is_yours( T *pt )
    {
    return (void*)pt >= (void *)this &&
    pt < &m_pbhBufferEnd->at[0];
    }



    template<typename T, std::size_t buffers, std::size_t buffer_size>
    class lallocator_buffer : public lallocator_buffer_if<T>
    {
    public:
    lallocator_buffer();

    private:
    union buffer
    {
    buffer_header bh;
    T atUnReferenced[buffer_size];
    };

    private:
    buffer aBuffers[buffers];
    };

    template<typename T, std::size_t buffers, std::size_t buffer_size>
    inline
    lallocator_buffer<T, buffers, buffer_size>::lallocator_buffer()
    {
    buffer *pbuf,
    *pbufNext;

    for( (pbuf = &aBuffers[buffers - 1],
    pbufNext = NULL);
    pbuf >= aBuffers;
    (pbufNext = pbuf,
    pbuf -= 1) )
    pbuf->bh.pbhNextFree = &pbufNext->bh;

    m_buffer_size = buffer_size;
    m_pbhBufferEnd = &aBuffers[buffers].bh;
    m_pbhFirstFree = &aBuffers[0].bh;
    }



    template<typename T>
    class lallocator : public std::allocator<T>
    {
    public:
    lallocator( lallocator_buffer_if<T> *plbi );
    lallocator( lallocator const &lc );
    ~lallocator() {};
    pointer allocate( size_type count, void *hint = NULL );
    void deallocate( pointer ptr, size_type count );
    lallocator &operator =( lallocator const &lc );

    public:
    template<class Other>
    struct rebind
    {
    typedef lallocator<Other> other;
    };

    private:
    lallocator_buffer_if<T> *m_plbi;
    };

    template<typename T>
    inline
    lallocator<T>::lallocator( lallocator_buffer_if<T> *plbi ) :
    std::allocator<T>()
    {
    m_plbi = plbi;
    }

    template<typename T>
    inline
    lallocator<T>::lallocator( lallocator const &lc ) :
    std::allocator<T>( lc )
    {
    m_plbi = lc.m_plbi;
    }

    template<typename T>
    inline
    typename lallocator<T>::pointer lallocator<T>::allocate( size_type count,
    void *hint )
    {
    T *pt;

    if( count > m_plbi->m_buffer_size ||
    (pt = m_plbi->pop_buffer()) == NULL )
    return std::allocator<T>::allocate( count );

    return pt;
    }

    template<typename T>
    inline
    void lallocator<T>::deallocate( pointer ptr, size_type count )
    {
    if( !m_plbi->is_yours( ptr ) )
    return (void)std::allocator<T>::deallocate( ptr, count );

    m_plbi->push_buffer( ptr );
    }

    template<typename T>
    inline
    typename lallocator<T> &lallocator<T>::eek:perator =( lallocator const &lc )
    {
    return m_plbi = lc.m_plbi,
    *this;
    }




    [ See http://www.gotw.ca/resources/clcm.htm for info about ]
    [ comp.lang.c++.moderated. First time posters: Do this! ]

    [ comp.std.c++ is moderated. To submit articles, try just posting with ]
    [ your news-reader. If that fails, use mailto: ]
    [ --- Please see the FAQ before posting. --- ]
    [ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html ]
    Oliver S., Oct 27, 2005
    #1
    1. Advertising

  2. Oliver S.

    Greg Guest

    Oliver S. wrote:
    > I was often annoyed about the performance of std::basic-string because
    > of complex memory-allocators working in the background to found the work
    > of std::allocator<T>. Basically, the performance doesn't reeach the per-
    > formance of a stack-based C-string in most cases. But nevertheless, the
    > comfort and the implicit protection against buffer-overflows makes std::
    > basic_string woth to be used. So I searched for a way to have my own
    > stack-based memory-allocation. I came up with the following idea which
    > resulted in a class I called the lallocator (derived from local alloca-
    > tor; this sounds funny in german because to "lall" means to babble in
    > german *g*).
    > First, we have the lallocator-class which is an interface to the locally
    > allocated storage for the stl-object (my lallocator can't be used for
    > strings only). This class is the same for all sizes of local stack-based
    > pools to prevent that we would compile derived stl-classes for any size
    > we use. Second, there's a class called lallocator_buffer_if which is the
    > interface to the local storage we allocated for the lallocator. I defined
    > this interface for all sizes of locally allocated pools to prevent any
    > secial-buffer-size-compiling I mentioned before. Third there's a dervied
    > class of lallocator_buffer_if<T>, called lallocator_buffer<T, buffers,
    > size>; T is the usual data-type in the buffers, buffers is the number of
    > buffers which can be allocated by the lallocator and size is the size of
    > each buffer. Whenever someone tries to do an allocate() on the lallocator
    > and there's a free buffer and the buffer is large enough to satisfy the
    > allocation-request, we'll allocate the buffer from the pool; otherwise
    > we fall back to std::allocator.
    >
    > Here's an example of how this is used:
    >
    > typedef std::basic_string<char, char_traits<char>, lallocator<char> >
    > lallostring;
    >
    > lallocator_buffer<char, 5, 128> lbc;
    > lallocator<char> lallo( &lbc );
    > lallostring lsTest( lallo );
    >
    > lsTest = "sdsdass";
    >
    > Unfortunately my compiler isn't able to eat the following code
    > to prevent explicit instanciation of a lallocator<T>-object:
    >
    > lallocator_buffer<char, 5, 128> lbc;
    > lallostring lsTest( lallocator<char>( &lbc ) );
    >
    > Does anyone know if there's a conformance-problem here or is this
    > just a bug of my compiler?
    >
    >
    > So here's my lalloator (it isn't fully stl-conformant and I used a single
    > trick to prevent having an additional pointer in lallocator_buffer_if which
    > causes my code not to work on theoretical C++-implementations; sorry to all
    > religious developers):
    >...


    Actually, there is nothing wrong with the union of the buffer and the
    pointer to the next free buffer. It follows the standard design for a
    memory pool - which is what you have implemented. See boost's "pool"
    library for another example of a memory pool.

    Memory pools work best for constant sized memory allocations - while
    std::string allocations will vary in size. But since std::string often
    makes many small allocations, there can still be some benefit from
    using a sufficiently large size for the memory pool blocks. An even
    more effective optimization would to add a fixed-sized character buffer
    data member to the string class itself. By so doing, the performance
    for std::strings would equal that of stack-based strings for strings
    shorter than a certain length.

    Greg


    [ See http://www.gotw.ca/resources/clcm.htm for info about ]
    [ comp.lang.c++.moderated. First time posters: Do this! ]

    [ comp.std.c++ is moderated. To submit articles, try just posting with ]
    [ your news-reader. If that fails, use mailto: ]
    [ --- Please see the FAQ before posting. --- ]
    [ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html ]
    Greg, Oct 28, 2005
    #2
    1. Advertising

  3. Oliver S. wrote:
    > I was often annoyed about the performance of std::basic-string because
    > of complex memory-allocators working in the background to found the work
    > of std::allocator<T>. Basically, the performance doesn't reeach the per-
    > formance of a stack-based C-string in most cases. But nevertheless, the
    > comfort and the implicit protection against buffer-overflows makes std::
    > basic_string woth to be used. So I searched for a way to have my own
    > stack-based memory-allocation. I came up with the following idea which
    > resulted in a class I called the lallocator (derived from local alloca-
    > tor; this sounds funny in german because to "lall" means to babble in
    > german *g*).



    >
    > Here's an example of how this is used:
    >
    > typedef std::basic_string<char, char_traits<char>, lallocator<char> >
    > lallostring;
    >
    > lallocator_buffer<char, 5, 128> lbc;
    > lallocator<char> lallo( &lbc );
    > lallostring lsTest( lallo );
    >
    > lsTest = "sdsdass";
    >
    > Unfortunately my compiler isn't able to eat the following code
    > to prevent explicit instanciation of a lallocator<T>-object:
    >
    > lallocator_buffer<char, 5, 128> lbc;
    > lallostring lsTest( lallocator<char>( &lbc ) );


    Oh, this is just common error you have declared function.


    >
    > template<typename T>
    > class lallocator : public std::allocator<T>
    > {
    > public:
    > lallocator( lallocator_buffer_if<T> *plbi );
    > lallocator( lallocator const &lc );
    > ~lallocator() {};
    > pointer allocate( size_type count, void *hint = NULL );
    > void deallocate( pointer ptr, size_type count );
    > lallocator &operator =( lallocator const &lc );
    >
    > public:
    > template<class Other>
    > struct rebind
    > {
    > typedef lallocator<Other> other;
    > };
    >
    > private:
    > lallocator_buffer_if<T> *m_plbi;
    > };
    >


    This class needs default constructor for rebind purposes.
    Ok, after correcting compiler errors here is your code:
    you can make static pointer used for default constructor,
    into a static thread local storage pointer if needed.
    There is alwayspossibility that memory allocated with
    one allocator could be freed with other, so you have
    to be carefull not to set different buffers in a scope.

    #include <string>
    #include <cassert>
    //using namespace std;


    #ifndef __GNUG__
    #define __thread /* this_would_be_a_great_thing_to_have */
    #endif


    template<typename T>
    class lallocator_buffer_if
    {
    protected:
    template<typename TT, std::size_t buffers, std::size_t buffer_size>
    friend class lallocator_buffer;

    template<typename TT>
    friend class lallocator;

    private:
    lallocator_buffer_if() {}
    T *pop_buffer();
    void push_buffer( T *pt );
    bool is_yours( T *pt );

    protected:
    union buffer_header
    {
    buffer_header *pbhNextFree;
    T at[1];
    };

    protected:
    std::size_t m_buffer_size;
    buffer_header *m_pbhFirstFree;
    buffer_header *m_pbhBufferEnd;

    };

    template<typename T>
    inline
    T *lallocator_buffer_if<T>::pop_buffer()
    {
    buffer_header *pbh;

    if( (pbh = m_pbhFirstFree) == NULL )
    return NULL;

    return m_pbhFirstFree = pbh->pbhNextFree,
    &pbh->at[0];

    }

    template<typename T>
    inline
    void lallocator_buffer_if<T>::push_buffer( T *pt )
    {
    buffer_header *pbh;

    pbh = (buffer_header *)pt;
    pbh->pbhNextFree = m_pbhFirstFree;
    m_pbhFirstFree = pbh;

    }

    template<typename T>
    inline
    bool lallocator_buffer_if<T>::is_yours( T *pt )
    {
    return (void*)pt >= (void *)this &&
    pt < &m_pbhBufferEnd->at[0];

    }

    template<typename T, std::size_t buffers, std::size_t buffer_size>
    class lallocator_buffer : public lallocator_buffer_if<T>
    {
    public:
    lallocator_buffer();

    private:
    union buffer
    {
    typename lallocator_buffer_if<T>::buffer_header bh;
    T atUnReferenced[buffer_size];
    };

    private:
    buffer aBuffers[buffers];

    };

    template<typename T, std::size_t buffers, std::size_t buffer_size>
    inline
    lallocator_buffer<T, buffers, buffer_size>::lallocator_buffer()
    {
    buffer *pbuf,
    *pbufNext;

    for( (pbuf = &aBuffers[buffers - 1],
    pbufNext = NULL);
    pbuf >= aBuffers;
    (pbufNext = pbuf,
    pbuf -= 1) )
    pbuf->bh.pbhNextFree = &pbufNext->bh;

    this->m_buffer_size = buffer_size;
    this->m_pbhBufferEnd = &aBuffers[buffers].bh;
    this->m_pbhFirstFree = &aBuffers[0].bh;

    }

    template<typename T>
    class lallocator : public std::allocator<T>
    {
    public:
    typedef typename std::allocator<T>::pointer pointer;
    typedef typename std::allocator<T>::size_type size_type;
    lallocator():m_plbi(m_buffer) { assert(m_plbi); }
    lallocator( lallocator_buffer_if<T> *plbi );
    lallocator( lallocator const &lc );
    ~lallocator() {};
    pointer allocate(size_type count, void *hint = NULL );
    void deallocate( pointer ptr, size_type count );
    lallocator &operator =( lallocator const &lc );

    static void set_buffer(lallocator_buffer_if<T>* b)
    {
    m_buffer=b;
    }

    public:
    template<class Other>
    struct rebind
    {
    typedef lallocator<Other> other;
    };

    private:
    lallocator_buffer_if<T> *m_plbi;
    static __thread lallocator_buffer_if<T>* m_buffer;
    };

    template <typename T>
    __thread lallocator_buffer_if<T>* lallocator<T>::m_buffer=0;

    template<typename T>
    inline
    lallocator<T>::lallocator( lallocator_buffer_if<T> *plbi ):
    std::allocator<T>()
    {
    m_plbi = plbi;

    }

    template<typename T>
    inline
    lallocator<T>::lallocator( lallocator const &lc ) :
    std::allocator<T>( lc )
    {
    m_plbi = lc.m_plbi;

    }

    template<typename T>
    inline
    typename lallocator<T>::pointer lallocator<T>::allocate( size_type
    count, void *hint )
    {
    T *pt;

    if( count > m_plbi->m_buffer_size ||
    (pt = m_plbi->pop_buffer()) == NULL )
    return std::allocator<T>::allocate( count );

    return pt;

    }

    template<typename T>
    inline
    void lallocator<T>::deallocate( pointer ptr, size_type count )
    {
    if( !m_plbi->is_yours( ptr ) )
    return (void)std::allocator<T>::deallocate( ptr, count );

    m_plbi->push_buffer( ptr );

    }

    template<typename T>
    inline
    lallocator<T>& lallocator<T>::eek:perator =( lallocator<T> const &lc )
    {
    return m_plbi = lc.m_plbi,
    *this;

    }

    int main()
    {
    typedef std::basic_string<char, std::char_traits<char>,
    lallocator<char> >
    lallostring;

    lallocator_buffer<char, 5, 128> lbc;
    lallocator<char> lallo( &lbc );
    lallocator<char>::set_buffer(&lbc);
    lallostring lsTest( lallo );

    lsTest = "sdsdass";
    {
    lallocator_buffer<char, 5, 128> lbc;
    lallocator<char>::set_buffer(&lbc);
    lallostring lsTest ((lallocator<char>( &lbc
    )));
    lsTest+="abc"+lallostring("def");
    }
    }

    Greetings, Bane.


    [ See http://www.gotw.ca/resources/clcm.htm for info about ]
    [ comp.lang.c++.moderated. First time posters: Do this! ]

    [ comp.std.c++ is moderated. To submit articles, try just posting with ]
    [ your news-reader. If that fails, use mailto: ]
    [ --- Please see the FAQ before posting. --- ]
    [ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html ]
    Branimir Maksimovic, Oct 28, 2005
    #3
  4. Oliver S.

    Guest

    Oliver S. wrote:
    ...
    > Unfortunately my compiler isn't able to eat the following code


    That isn't very specific. It would help a great deal if you indicated
    what, precisely, your compiler said was indigestible about this code.

    ...
    > So here's my lalloator (it isn't fully stl-conformant and I used a single
    > trick to prevent having an additional pointer in lallocator_buffer_if which
    > causes my code not to work on theoretical C++-implementations; sorry to all
    > religious developers):


    Your use of the words "theoretical" and "religious" implies that you
    think the non-conformance is unimportant. It might be; but you should
    seriously consider the possibility that the problems you're having may
    in fact be due to that non-conformance. In particular, implementations
    are allowed to assume that all instances of a given allocator type are
    equivalent (20.1.5p4), which isn't the case for your allocators. While
    the standard encourages implementators to create implementations that
    don't rely on that assumption, many of them do in fact take advantage
    of that option, for instance by not actually copying allocators when
    they have the same type.

    std::list<T,Allocator>::splice() is particularly difficult to implement
    efficiently unless you build in an assumption that all instances of
    Allocator are equivalent.


    [ See http://www.gotw.ca/resources/clcm.htm for info about ]
    [ comp.lang.c++.moderated. First time posters: Do this! ]

    [ comp.std.c++ is moderated. To submit articles, try just posting with ]
    [ your news-reader. If that fails, use mailto: ]
    [ --- Please see the FAQ before posting. --- ]
    [ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html ]
    , Oct 28, 2005
    #4
  5. Oliver S. wrote:

    []


    >> Unfortunately my compiler isn't able to eat the following code
    >> to prevent explicit instanciation of a lallocator<T>-object:
    >>
    >> lallocator_buffer<char, 5, 128> lbc;
    >> lallostring lsTest( lallocator<char>( &lbc ) );
    >>
    >> Does anyone know if there's a conformance-problem here or is this
    >> just a bug of my compiler?



    You could try putting additional parenthesis like this:

    lallostring lsTest( ( lallocator<char>( &lbc ) ) );


    [ See http://www.gotw.ca/resources/clcm.htm for info about ]
    [ comp.lang.c++.moderated. First time posters: Do this! ]

    [ comp.std.c++ is moderated. To submit articles, try just posting with ]
    [ your news-reader. If that fails, use mailto: ]
    [ --- Please see the FAQ before posting. --- ]
    [ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html ]
    Maxim Yegorushkin, Oct 28, 2005
    #5
  6. Oliver S.

    Lance Diduck Guest

    Oliver S. wrote:
    >> Does anyone know if there's a conformance-problem here or is this

    > just a bug of my compiler?


    In this paper on Using Stateful Allocator with the STL there is some
    code that I've used on a number of platforms, with all the major STL
    versions http://www.lancediduck.com/papers/Cpp/StatefulSTL.pdf


    [ See http://www.gotw.ca/resources/clcm.htm for info about ]
    [ comp.lang.c++.moderated. First time posters: Do this! ]

    [ comp.std.c++ is moderated. To submit articles, try just posting with ]
    [ your news-reader. If that fails, use mailto: ]
    [ --- Please see the FAQ before posting. --- ]
    [ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html ]
    Lance Diduck, Oct 30, 2005
    #6
  7. Oliver S.

    Oliver S. Guest


    > Actually, there is nothing wrong with the union of the buffer and the
    > pointer to the next free buffer. ...


    That's not the in-conformance I thought about. Look at is_yours in the
    base-class and you can find this in-conformance which might offense some
    religious C++ers.

    > Memory pools work best for constant sized memory allocations ...


    Mine does work equally good for everything up to the limit for a buffer.

    > An even more effective optimization would to add a fixed-sized character
    > buffer data member to the string class itself.


    Maybe, but this wouldn't be a genral-purpose-string anymore because
    every string would carry around a buffer that might not be used.

    [ See http://www.gotw.ca/resources/clcm.htm for info about ]
    [ comp.lang.c++.moderated. First time posters: Do this! ]

    [ comp.std.c++ is moderated. To submit articles, try just posting with ]
    [ your news-reader. If that fails, use mailto: ]
    [ --- Please see the FAQ before posting. --- ]
    [ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html ]
    Oliver S., Oct 30, 2005
    #7
  8. Oliver S.

    Oliver S. Guest


    > In this paper on Using Stateful Allocator with the STL there is some
    > code that I've used on a number of platforms, with all the major STL
    > versions ...


    Nearly the same idea, but very low-levelish in your flavour.
    I think mine is a bit more comfortable.

    [ See http://www.gotw.ca/resources/clcm.htm for info about ]
    [ comp.lang.c++.moderated. First time posters: Do this! ]

    [ comp.std.c++ is moderated. To submit articles, try just posting with ]
    [ your news-reader. If that fails, use mailto: ]
    [ --- Please see the FAQ before posting. --- ]
    [ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html ]
    Oliver S., Oct 31, 2005
    #8
  9. Oliver S.

    Oliver S. Guest


    > While the standard encourages implementators to create implementations
    > that don't rely on that assumption, many of them do in fact take advantage
    > of that option, for instance by not actually copying allocators when
    > they have the same type.


    I intentionally missed a default-constructor to prevent that and thereby
    enforce a compilation-error.

    [ See http://www.gotw.ca/resources/clcm.htm for info about ]
    [ comp.lang.c++.moderated. First time posters: Do this! ]

    [ comp.std.c++ is moderated. To submit articles, try just posting with ]
    [ your news-reader. If that fails, use mailto: ]
    [ --- Please see the FAQ before posting. --- ]
    [ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html ]
    Oliver S., Oct 31, 2005
    #9
  10. Oliver S.

    Oliver S. Guest


    > You could try putting additional parenthesis like this:
    >
    > lallostring lsTest( ( lallocator<char>( &lbc ) ) );


    Thanks, but that doesn't help!

    [ See http://www.gotw.ca/resources/clcm.htm for info about ]
    [ comp.lang.c++.moderated. First time posters: Do this! ]

    [ comp.std.c++ is moderated. To submit articles, try just posting with ]
    [ your news-reader. If that fails, use mailto: ]
    [ --- Please see the FAQ before posting. --- ]
    [ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html ]
    Oliver S., Oct 31, 2005
    #10
  11. Oliver S.

    Guest

    Oliver S. wrote:
    > > Actually, there is nothing wrong with the union of the buffer and the
    > > pointer to the next free buffer. ...

    >
    > That's not the in-conformance I thought about. Look at is_yours in the
    > base-class and you can find this in-conformance which might offense some
    > religious C++ers.


    I'm not sure I follow that; in what sense is it non-conformant? I'm
    sure it will be obvious two minutes after I post this message, but
    right now it isn't.


    [ See http://www.gotw.ca/resources/clcm.htm for info about ]
    [ comp.lang.c++.moderated. First time posters: Do this! ]

    [ comp.std.c++ is moderated. To submit articles, try just posting with ]
    [ your news-reader. If that fails, use mailto: ]
    [ --- Please see the FAQ before posting. --- ]
    [ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html ]
    , Nov 1, 2005
    #11
  12. Oliver S.

    Greg Herlihy Guest

    Oliver S. wrote:
    > > Actually, there is nothing wrong with the union of the buffer and the
    > > pointer to the next free buffer. ...

    >
    > That's not the in-conformance I thought about. Look at is_yours in the
    > base-class and you can find this in-conformance which might offense some
    > religious C++ers.


    I'm not sure why is_yours() compares the pointer against the pointer to
    the buffer. Comparing it against the first item in the buffer's array
    would certainly be a valid test:

    template<typename T>
    inline
    bool lallocator_buffer_if<T>::is_yours( T *pt )
    {
    return pt >= &at[0] and pt < &m_pbhBufferEnd->at[0];
    }

    > > Memory pools work best for constant sized memory allocations ...

    >
    > Mine does work equally good for everything up to the limit for a buffer.


    Using fixed sized memory blocks to speed up variably-sized allocations
    has two principal shortcomings: oversized allocations see no benefit,
    while undersized allocations waste memory.

    > > An even more effective optimization would to add a fixed-sized character
    > > buffer data member to the string class itself.

    >
    > Maybe, but this wouldn't be a genral-purpose-string anymore because
    > every string would carry around a buffer that might not be used.


    The string with an internal character buffer would still be general
    purpose - oversized strings would continue to allocate their character
    buffer dynamically. An unused internal character buffer wastes no more
    memory than the memory wasted by deallocated a fixed-sized buffer.
    After all, deallocated buffers are never really "freed" (that is,
    returned to the general memory pool where it can be used to fulfill any
    subsequent memory allocation request). Rather freed buffers are held in
    reserve just in case a future string allocation could use it. A string
    with an internal buffer on the other hand, returns the buffer to the
    general purpose memory pool (or the stack) upon its destruction.

    A further advantage that an internal character buffer has over external
    storage is that it adds no meaningful delay when constructing or
    copying a string object. The entire internal character buffer does not
    need to be initialized or copied with the string, just the portion that
    contains the string's characters.

    Greg


    [ See http://www.gotw.ca/resources/clcm.htm for info about ]
    [ comp.lang.c++.moderated. First time posters: Do this! ]

    [ comp.std.c++ is moderated. To submit articles, try just posting with ]
    [ your news-reader. If that fails, use mailto: ]
    [ --- Please see the FAQ before posting. --- ]
    [ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html ]
    Greg Herlihy, Nov 1, 2005
    #12
  13. Oliver S.

    Oliver S. Guest

    > I'm not sure I follow that; in what sense is it non-conformant? I'm
    > sure it will be obvious two minutes after I post this message, but
    > right now it isn't.


    I assume the data for the buffers to be placed after the data of the
    base-class; this might not be true for theoretical C++-implementations.

    ---
    [ comp.std.c++ is moderated. To submit articles, try just posting with ]
    [ your news-reader. If that fails, use mailto: ]
    [ --- Please see the FAQ before posting. --- ]
    [ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html ]
    Oliver S., Nov 2, 2005
    #13
  14. Oliver S.

    Oliver S. Guest

    >> lallocator_buffer<char, 5, 128> lbc;
    >> lallostring lsTest( lallocator<char>( &lbc ) );


    > Oh, this is just common error you have declared function.


    Ok, these seem to have priority over variable-definitions.

    > This class needs default constructor for rebind purposes.


    I won't supply a default-constructor because I want the STL-classes used with
    my lallocator<T> to work with the pools attached to the allocator-objects. The
    allocator-objects usually stored internally in the STL-container-classes are
    typically rebind'ed versions of the supplied allocator-type to allow for the
    allocators supplied to the constructor of the stl-container to be of a dif-
    ferent type. These allocator-objects are created through a copy-constructor
    I supply. As the major STL-implementations are prepared for stateful alloca-
    tors as far as possible, this works.

    ---
    [ comp.std.c++ is moderated. To submit articles, try just posting with ]
    [ your news-reader. If that fails, use mailto: ]
    [ --- Please see the FAQ before posting. --- ]
    [ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html ]
    Oliver S., Nov 3, 2005
    #14
  15. Oliver S.

    Guest

    "Oliver S." wrote:
    > > I'm not sure I follow that; in what sense is it non-conformant? I'm
    > > sure it will be obvious two minutes after I post this message, but
    > > right now it isn't.

    >
    > I assume the data for the buffers to be placed after the data of the
    > base-class; this might not be true for theoretical C++-implementations.


    OK - I can see the issue now. I hadn't noticed that assumption. You're
    right - Section 10 paragraph 3 indicates that you can't rely on such an
    assumption. You could remove that assumption by using a pointer to the
    entire derived object, converted to void*, rather than a pointer to the
    base class sub-object.

    ---
    [ comp.std.c++ is moderated. To submit articles, try just posting with ]
    [ your news-reader. If that fails, use mailto: ]
    [ --- Please see the FAQ before posting. --- ]
    [ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html ]
    , Nov 3, 2005
    #15
  16. Oliver S. wrote:
    > > This class needs default constructor for rebind purposes.

    >
    > I won't supply a default-constructor because I want the STL-classes used with
    > my lallocator<T> to work with the pools attached to the allocator-objects. The
    > allocator-objects usually stored internally in the STL-container-classes are
    > typically rebind'ed versions of the supplied allocator-type to allow for the
    > allocators supplied to the constructor of the stl-container to be of a dif-
    > ferent type. These allocator-objects are created through a copy-constructor
    > I supply.


    How? On gcc 3.4.3 and 3.4.2 string requires default constructor in
    allocator because it probably have some internal data structure?
    Since rebinded allocator have different type then one passed in
    constructor string doesn't even try to use copy constructor?
    That creates big problem because string can do something like this:
    //......
    ~string()
    {
    buf->alloc.deallocate(ptr->buf);
    typename Allocator<T>::rebind<MyStruct>::eek:ther alloc;
    alloc.deallocate(ptr); // my example will crash in this case
    }
    it would be great if string have to use:
    typename Allocator<T>::rebind<MyStruct>::eek:ther alloc(buff->alloc);
    but it does not, I guess, at least gcc string.

    Greetings, Bane.

    ---
    [ comp.std.c++ is moderated. To submit articles, try just posting with ]
    [ your news-reader. If that fails, use mailto: ]
    [ --- Please see the FAQ before posting. --- ]
    [ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html ]
    Branimir Maksimovic, Nov 3, 2005
    #16
  17. Oliver S.

    Guest

    Branimir Maksimovic wrote:
    > Oliver S. wrote:
    > > > This class needs default constructor for rebind purposes.

    > >
    > > I won't supply a default-constructor because I want the STL-classes used with
    > > my lallocator<T> to work with the pools attached to the allocator-objects. The
    > > allocator-objects usually stored internally in the STL-container-classes are
    > > typically rebind'ed versions of the supplied allocator-type to allow for the
    > > allocators supplied to the constructor of the stl-container to be of a dif-
    > > ferent type. These allocator-objects are created through a copy-constructor
    > > I supply.

    >
    > How? On gcc 3.4.3 and 3.4.2 string requires default constructor in
    > allocator because it probably have some internal data structure?
    > Since rebinded allocator have different type then one passed in
    > constructor string doesn't even try to use copy constructor?


    Section 21.3.1 says that "In all basic_string constructors, a copy of
    the Allocator argument is used for any memory allocation performed by
    the constructor or member functions during the lifetime of the object."
    Section 21.3 says the same thing about standard containers.

    Granted, many of the requirements imposed on strings and containers can
    be satisfied by non-member operator overloads, and those are permitted
    to make use of the fact that the allocator type is required to have a
    default constructor.

    An implementation is allowed to assume that all instances of the
    allocator type are equivalent, which means that the default constructed
    allocator can be assumed to be equivalent to a copy constructed one.
    However, the standard encourages implementors to avoid relying upon
    that assumption.

    ---
    [ comp.std.c++ is moderated. To submit articles, try just posting with ]
    [ your news-reader. If that fails, use mailto: ]
    [ --- Please see the FAQ before posting. --- ]
    [ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html ]
    , Nov 4, 2005
    #17
  18. Oliver S.

    Oliver S. Guest

    > Granted, many of the requirements imposed on strings and containers can
    > be satisfied by non-member operator overloads, and those are permitted
    > to make use of the fact that the allocator type is required to have a
    > default constructor.


    Ok, but this isn't really a cosntraint for me because of the following
    reason: "Global" string operators are usually slow because they generate
    unnecessary temporary objects. And why should someone use a performance
    -enhancing allocator primarily designed for container objects placed on
    the stack *and* use global operators on strings using these allocators?

    ---
    [ comp.std.c++ is moderated. To submit articles, try just posting with ]
    [ your news-reader. If that fails, use mailto: ]
    [ --- Please see the FAQ before posting. --- ]
    [ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html ]
    Oliver S., Nov 7, 2005
    #18
  19. Oliver S.

    Guest

    "Oliver S." wrote:
    > > Granted, many of the requirements imposed on strings and containers can
    > > be satisfied by non-member operator overloads, and those are permitted
    > > to make use of the fact that the allocator type is required to have a
    > > default constructor.

    >
    > Ok, but this isn't really a cosntraint for me because of the following
    > reason: "Global" string operators are usually slow because they generate
    > unnecessary temporary objects. And why should someone use a performance
    > -enhancing allocator primarily designed for container objects placed on
    > the stack *and* use global operators on strings using these allocators?


    The most likely reason for doing something like that is because the
    string/container class that they are instantiating with your
    performance-enhancing allocator was written by someone with other
    objectives.

    ---
    [ comp.std.c++ is moderated. To submit articles, try just posting with ]
    [ your news-reader. If that fails, use mailto: ]
    [ --- Please see the FAQ before posting. --- ]
    [ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html ]
    , Nov 7, 2005
    #19
  20. Oliver S.

    Oliver S. Guest

    > The most likely reason for doing something like that is because
    > the string/container class that they are instantiating with your
    > performance-enhancing allocator was written by someone with other
    > objectives.



    Why should someone not see this objective when the only
    purpose of my allocator is to enhance the performance.

    ---
    [ comp.std.c++ is moderated. To submit articles, try just posting with ]
    [ your news-reader. If that fails, use mailto: ]
    [ --- Please see the FAQ before posting. --- ]
    [ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html ]
    Oliver S., Nov 8, 2005
    #20
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.

Share This Page