Topic: allocator design questions


Author: tom_usenet@hotmail.com (tom_usenet)
Date: Fri, 4 Apr 2003 11:22:45 +0000 (UTC)
Raw View
On Sat, 29 Mar 2003 17:36:34 +0000 (UTC), conley.141@osu.edu (Mike
Conley) wrote:

>tom_usenet@hotmail.com (tom_usenet) wrote in
>news:3e81d76e.173816015@news.easynet.co.uk:
>
>> Actually, here's my (immature) shared memory allocator implementation.
>> As you can see, I have custom implementations of all these functions
>> that you say aren't required...
>
>Neat.  But I don't think you absolutely need those functions.  That is, you
>can define your classes such that they are nothing more than the obvious
>inline calls.

How about taking the address of a reference? And
constructing/destructing elements elements?

How do you get a pointer object out of an ordinary reference? You seem
to have dropped the address function.

>
>Here's a vague outline of how I'd do it (without regard for standards
>compliance :).  I've not given this too much thought, so it may be
>completely broken, but the general idea should be sound....
>
>
>template <key_t key>  // key_t is a typedefed integral value
>class shm_allocator {
>   // all allocators using this key have the same id value
>   static int identifier;
>
>   // address of segment - unique to this process
>   static void* segment;
>
>   // shm_ptr subtracts or adds the address of the segment (returned by
>   // shmat(id, ...)) as necessary, when dereferencing.  It provides a
>   // constructor taking an ordinary T*, an empty default constructor &
>   // a copy constructor, and is convertible to a T*.
>   template <class T, int id> shm_ptr;
>
>public:
>
>   // What we really want here is a template typedef:
>   // template <class T> shm_ptr<T, identifier> pointer;
>
>   template <class T> class pointer : public shm_ptr<T, identifier>;
>
>   shm_allocator() : identifier(shmget (key, ...)),
>                     segment(shmat(identifier, ...) {
>     if (! /* initialized */) {
>        // initialize memory management data structures (stored in shared
>        // memory).
>   }}
>
>   ~shm_allocator() {
>      shmdt (segment);
>      if (/* no other processes need the segment */)
>        shmctl (/* remove segment */);
>    }
>
>   template <class T>
>   pointer<T> allocate (size_t n) {
>      void* off = // get offset of a free chunk of size >= n
>      return pointer<T> (off);
>   }
>
>   // assume pointer<T> provides conversion to pointer<void>
>   void deallocate (const pointer<void>& v) {
>      // free block pointed to by v
>   }
>
>   friend bool operator== /*yadda yadda */ // returns true
>};
>
>
>I'm sure I've missed a few things...
>
>The advantage of defining an allocator this way (as opposed to using a
>class template) is that you can allocate various types of objects with the
>same allocator (without rebind).  This becomes a bigger advantage if you're
>using an allocator w/ per-object data (in which case using rebind creates a
>new allocator object that you probably don't need or want).  Not that your
>standard library necessarily supports such things anyway, but it would be
>nice if it did.

Can't you put all this per-object stuff in a different class that your
container compatible allocator holds a reference to? Or even put it in
a singleton?

>
>
>>        typedef shm::reference<value_type> reference;
>>        typedef shm::reference<const value_type> const_reference;
>
>
>If your pointers already adjust themselves (adding and subtracting the
>address of the shared segment), why do you need a reference type?

Well, it was so that you could use references as members of objects
you wanted to hold in shared memory, but I agree that this isn't
really necessary/

>
>I wrote (and was wrong):
>>>Since the standard allows implementations to assume that reference is
>>>typedefed as T&,
>
>
>I goofed.  The standard requires that reference is a typedef for T&.  Not
>that any implementation takes extraordinary measures to ensure that it
>really is (as opposed to a type that is convertible to T&).
>
>It is pointer that the implementation is allowed to assume is T* (but
>implementers are encouraged not to do so).

Right, I've confirmed this too now.

>> On the contrary, I have defined templated references and pointers for
>> shared memory that are independent of the address that the shared
>> memory is mapped to. They work fine with Dinkumware's containers.
>
>
>It will probably work everywhere,

Actually, almost all container implementations assume T* is the
pointer type. Certainly the code doesn't work with libcomo or
libstdc++3 (GCC 3+'s library), and probably not with STLport either.

 but it is nonstandard (so it isn't
>absolutely guaranteed).  I'm not sure why the requirements are the way they
>are (I can't think of a reason for it offhand, but that doesn't anything
>:).
>
>
>> Why would you subclass this pointer type? Are you intending that it
>> has virtual functions that you can override!?
>
>No.  I was thinking of something more along these lines:
>
>template <class T>
>class pointer {
>  T* t;
>public:
>  pointer() {}
>  pointer (T* tt) :t(tt) {}
>  pointer (const pointer& p) : t(p.t) {}
>  operator T*&() { return t; }
>  operator pointer<void>() const { return pointer<void>(t); }
>};

You need something in your interface for placement new (and obviously
operator->, etc, etc).

But the above is why they didn't go down the non-template allocator
approach, since the above is pretty hideous compared to:

typedef T* pointer;
//or typedef pointer<T> pointer

given that plain pointers are the norm.

Template typedefs would of course help your proposal greatly:

template <class T> T* pointer;

With them in place, I'd certainly agree that the non-template
allocator approach has some advantages.

>I'm not sure you'd want to use this for the allocator above (or for yours),
>so maybe I picked a bad example :)
>
>But I think I've rambled on for long enough.

Me too! Sorry about the delay in my response.

Tom

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: conley.141@osu.edu (Mike Conley)
Date: Tue, 8 Apr 2003 08:31:57 +0000 (UTC)
Raw View
tom_usenet@hotmail.com (tom_usenet) wrote in
news:3e8c68b3.289139062@news.easynet.co.uk:

> How about taking the address of a reference? And
> constructing/destructing elements elements?

A shm_allocator::pointer would provide a constructor taking an ordinary
T*.  It would subtract from this the address of the shared segment,
storing the offset.  operator-> would convert back by adding the address
of the shared segment to the stored offset.  In short, you should be able
to manipulate objects transparently thru shm_allocator::pointers.


> Actually, almost all container implementations assume T* is the
> pointer type. Certainly the code doesn't work with libcomo or
> libstdc++3 (GCC 3+'s library), and probably not with STLport either.


I'm surprised.  Hopefully the next version of the standard will address
this, because it's quite limiting and probably unnecessary.


> You need something in your interface for placement new (and obviously
> operator->, etc, etc).


Operations like these are the reason I provided a conversion to built in
pointers.


> Sorry about the delay in my response.

No problem :)  And thanks for the help.


--
Mike Conley

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: tom_usenet@hotmail.com (tom_usenet)
Date: Wed, 26 Mar 2003 19:28:03 +0000 (UTC)
Raw View
On Fri, 21 Mar 2003 21:12:23 +0000 (UTC), conley.141@osu.edu (Mike
Conley) wrote:

>tom_usenet@hotmail.com (tom_usenet) wrote in news:3e7afb46.3096031
>@news.easynet.co.uk:
>
>> Are you suggesting that you make
>> every single member function a template member?
>
>Yes.  I'm also suggesting ditching the member functions that don't
>logically belong in the interface, which is, I think, most of them.

Actually, here's my (immature) shared memory allocator implementation.
As you can see, I have custom implementations of all these functions
that you say aren't required...

template <class T>
    class allocator: public alloc
    {
    public:
        typedef T value_type;
        typedef shm::reference<value_type> reference;
        typedef shm::reference<const value_type> const_reference;
        typedef shm::pointer<value_type> pointer;
        typedef shm::pointer<const value_type> const_pointer;
        typedef size_t size_type;
        typedef ptrdiff_t difference_type;

        template <class Other>
        struct rebind
        {
            typedef allocator<Other> other;
        };

        allocator(void* mem, int size, int number)
            :alloc(mem, size, number)
        {
        }

        template<class Other>
        allocator(const allocator<Other>& right) throw()
            :alloc(right)
        {
        }

        template<class Other>
        allocator& operator=(const allocator<Other>& right)
        {
            static_cast<alloc&>(*this) = right;
            return *this;
        }


        pointer address(reference val) const
        {
            return val.p;
        }

        const_pointer address(const_reference val) const
        {
            return val.p;
        }

        pointer allocate(
            size_type count,
   const void* hint = 0)
        {
            return pointer((char*)malloc(count) - (char*)base(),
number());
        }

  void deallocate(pointer ptr, size_type count)
        {
            free((char*)base() + ptr.offset);
        }

        void construct(pointer ptr, const value_type& val)
        {
            new ((char*)base() + ptr.offset) value_type(val);
        }

        void destroy(pointer ptr)
        {
            value_type* p = (value_type*)((char*)base() + ptr.offset);
            p->~value_type();
        }

        size_type max_size() const throw()
        {
            return static_cast<alloc&>(*this).max_size() /
sizeof(value_type);
        }

  friend bool operator==(allocator const& lhs, allocator
const& rhs)
  {
   return lhs.number() == rhs.number();
  }

  friend bool operator!=(allocator const& lhs, allocator
const& rhs)
  {
   return !(lhs == rhs);
  }
    };

>
>
>>      typedef ?? reference;
>
>Since the standard allows implementations to assume that reference is
>typedefed as T&, and since it is impossible to define your own references

On the contrary, I have defined templated references and pointers for
shared memory that are independent of the address that the shared
memory is mapped to. They work fine with Dinkumware's containers.

>(so that the assumption is always correct), I think that typedef is of
>questionable value.
>
>
>> Why would you want to wrap a pointer rather than use it directly?
>
>Because it could be useful as a base class for other pointer types (eg, for
>a SysV shared memory allocator) and, more importantly, it gives you the
>pointer abstraction you get from the existing standard allocator's typedef.
>You loose nothing and you gain the abstraction, so why not?

Why would you subclass this pointer type? Are you intending that it
has virtual functions that you can override!? That would be a killer,
performance-wise.

Tom

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: conley.141@osu.edu (Mike Conley)
Date: Sat, 29 Mar 2003 17:36:34 +0000 (UTC)
Raw View
tom_usenet@hotmail.com (tom_usenet) wrote in
news:3e81d76e.173816015@news.easynet.co.uk:

> Actually, here's my (immature) shared memory allocator implementation.
> As you can see, I have custom implementations of all these functions
> that you say aren't required...

Neat.  But I don't think you absolutely need those functions.  That is, you
can define your classes such that they are nothing more than the obvious
inline calls.

Here's a vague outline of how I'd do it (without regard for standards
compliance :).  I've not given this too much thought, so it may be
completely broken, but the general idea should be sound....


template <key_t key>  // key_t is a typedefed integral value
class shm_allocator {
   // all allocators using this key have the same id value
   static int identifier;

   // address of segment - unique to this process
   static void* segment;

   // shm_ptr subtracts or adds the address of the segment (returned by
   // shmat(id, ...)) as necessary, when dereferencing.  It provides a
   // constructor taking an ordinary T*, an empty default constructor &
   // a copy constructor, and is convertible to a T*.
   template <class T, int id> shm_ptr;

public:

   // What we really want here is a template typedef:
   // template <class T> shm_ptr<T, identifier> pointer;

   template <class T> class pointer : public shm_ptr<T, identifier>;

   shm_allocator() : identifier(shmget (key, ...)),
                     segment(shmat(identifier, ...) {
     if (! /* initialized */) {
        // initialize memory management data structures (stored in shared
        // memory).
   }}

   ~shm_allocator() {
      shmdt (segment);
      if (/* no other processes need the segment */)
        shmctl (/* remove segment */);
    }

   template <class T>
   pointer<T> allocate (size_t n) {
      void* off = // get offset of a free chunk of size >= n
      return pointer<T> (off);
   }

   // assume pointer<T> provides conversion to pointer<void>
   void deallocate (const pointer<void>& v) {
      // free block pointed to by v
   }

   friend bool operator== /*yadda yadda */ // returns true
};


I'm sure I've missed a few things...

The advantage of defining an allocator this way (as opposed to using a
class template) is that you can allocate various types of objects with the
same allocator (without rebind).  This becomes a bigger advantage if you're
using an allocator w/ per-object data (in which case using rebind creates a
new allocator object that you probably don't need or want).  Not that your
standard library necessarily supports such things anyway, but it would be
nice if it did.


>        typedef shm::reference<value_type> reference;
>        typedef shm::reference<const value_type> const_reference;


If your pointers already adjust themselves (adding and subtracting the
address of the shared segment), why do you need a reference type?

I wrote (and was wrong):
>>Since the standard allows implementations to assume that reference is
>>typedefed as T&,


I goofed.  The standard requires that reference is a typedef for T&.  Not
that any implementation takes extraordinary measures to ensure that it
really is (as opposed to a type that is convertible to T&).

It is pointer that the implementation is allowed to assume is T* (but
implementers are encouraged not to do so).


(And I was again not quite correct):
>>and since it is impossible to define your own
>>references


OK.  You can define a class that is convertible to a reference, but that's
not quite the same thing.


> On the contrary, I have defined templated references and pointers for
> shared memory that are independent of the address that the shared
> memory is mapped to. They work fine with Dinkumware's containers.


It will probably work everywhere, but it is nonstandard (so it isn't
absolutely guaranteed).  I'm not sure why the requirements are the way they
are (I can't think of a reason for it offhand, but that doesn't anything
:).


> Why would you subclass this pointer type? Are you intending that it
> has virtual functions that you can override!?

No.  I was thinking of something more along these lines:

template <class T>
class pointer {
  T* t;
public:
  pointer() {}
  pointer (T* tt) :t(tt) {}
  pointer (const pointer& p) : t(p.t) {}
  operator T*&() { return t; }
  operator pointer<void>() const { return pointer<void>(t); }
};


I'm not sure you'd want to use this for the allocator above (or for yours),
so maybe I picked a bad example :)

But I think I've rambled on for long enough.

--
Mike Conley

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: tom_usenet@hotmail.com (tom_usenet)
Date: Fri, 21 Mar 2003 17:18:31 +0000 (UTC)
Raw View
On Fri, 21 Mar 2003 06:27:41 +0000 (UTC), conley.141@osu.edu (Mike
Conley) wrote:

>I'm curious about the design of the standard allocator.  In particular,
>why is it a template?  Template member functions could do just as well,
>and then we wouldn't need rebind.

What about the typedefs? Every member of allocator is reliant on the
type (except possibly constructors). Are you suggesting that you make
every single member function a template member?

I suspect that rebind will be deprecated if template typedefs are
added to the language.

>
>Something like:
>
>struct allocator {
>     template <class T>
>    T* allocate (size_t n) { return operator new(n*sizeof(T)); }

 typedef ?? reference;

>
>     //...
>};
>
>
>We could also define a member template class:
>
>template <class T>
>class pointer {
>     T* t;
>public: // define the normal pointer operations
>};
>
>and specialize it for void.

Why would you want to wrap a pointer rather than use it directly?
Under your scheme:

template <class T>
struct pointer
{
 typedef T* type;
};

as a member template would make more sense. But then you are in effect
adding a "rebind" style member for every single allocator typedef. The
solution is worse than the problem!

>
>
>On another note, why does an allocator have construct and destroy member
>functions?  Would any allocator have to do anything special to
>construct/destroy an object (aside from the fact that a GC allocator
>wouldn't have to do anything to destroy one)?

Personally, I can't think of a good reason for the construct and
destroy methods doing anything but the obvious, but I can think of
reasons for a container not to use them (optimization).

Tom

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: conley.141@osu.edu (Mike Conley)
Date: Fri, 21 Mar 2003 21:12:23 +0000 (UTC)
Raw View
tom_usenet@hotmail.com (tom_usenet) wrote in news:3e7afb46.3096031
@news.easynet.co.uk:

> Are you suggesting that you make
> every single member function a template member?

Yes.  I'm also suggesting ditching the member functions that don't
logically belong in the interface, which is, I think, most of them.


>      typedef ?? reference;

Since the standard allows implementations to assume that reference is
typedefed as T&, and since it is impossible to define your own references
(so that the assumption is always correct), I think that typedef is of
questionable value.


> Why would you want to wrap a pointer rather than use it directly?

Because it could be useful as a base class for other pointer types (eg, for
a SysV shared memory allocator) and, more importantly, it gives you the
pointer abstraction you get from the existing standard allocator's typedef.
You loose nothing and you gain the abstraction, so why not?


--
Mike Conley

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: conley.141@osu.edu (Mike Conley)
Date: Fri, 21 Mar 2003 06:27:41 +0000 (UTC)
Raw View
I'm curious about the design of the standard allocator.  In particular,
why is it a template?  Template member functions could do just as well,
and then we wouldn't need rebind.

Something like:

struct allocator {
     template <class T>
    T* allocate (size_t n) { return operator new(n*sizeof(T)); }

     //...
};


We could also define a member template class:

template <class T>
class pointer {
     T* t;
public: // define the normal pointer operations
};

and specialize it for void.


On another note, why does an allocator have construct and destroy member
functions?  Would any allocator have to do anything special to
construct/destroy an object (aside from the fact that a GC allocator
wouldn't have to do anything to destroy one)?


Just curious.

--
Mike Conley

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: James Kuyper <kuyper@wizard.net>
Date: 1999/04/08
Raw View
Scott Meyers wrote:
>
> I have two questions about the design of the interface for
> allocator<T>::allocate:
>
>   - Why is the return type T* instead of void*?  Like operator new,
>     allocate returns a pointer to raw storage, hence the claimed return
>     type of T* is misleading: there is in fact no T object at the
>     pointed-to location.

allocator<T>::allocate() is specifically designed for allocating T-sized
object, suitably aligned to hold a T. It is optimized, compared to
malloc() or new(), to make allocation of a single object of that size
and alignment an efficient operation. That was done so that people would
not be tempted by speed considerations to write their own procedures
that allocate many objects at a time, and then dole them out.
allocator<T> already does that, so that user doesn't have to.

If it returned a void*, that might encourage people to make the mistake
of assuming the memory was suitably aligned for building objects other
than T's, which is not guaranteed.

>   - Why is the meaning of the first argument to allocate different from the
>     meaning of the first argument to operator new?  If I need enough memory
>     to hold a T object, I pass sizeof(T) to operator new.  If I want the
>     same amount of memory from allocate, I pass 1.  What gives?

My guess is that someone prefered writing "n" over "n*sizeof(T)", which
strikes me as a quite reasonable preference. Furthermore, if it took a
size in bytes, rather than the number of elements, it would have to cope
with the possibility that someone would call it with a size that wasn't
a multiple of sizeof(T), which would be contrary to the whole purpose of
allocator<T>::allocate().
---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html              ]





Author: "Andrei Alexandrescu" <andrewalex@hotmail.com>
Date: 1999/04/08
Raw View
Scott Meyers <smeyers@aristeia.com> wrote in message
news:MPG.117340efc9cdf7ce98969a@news.teleport.com...
> I have two questions about the design of the interface for
> allocator<T>::allocate:
>
>   - Why is the return type T* instead of void*?  Like operator new,
>     allocate returns a pointer to raw storage, hence the claimed return
>     type of T* is misleading: there is in fact no T object at the
>     pointed-to location.
>
>   - Why is the meaning of the first argument to allocate different from
the
>     meaning of the first argument to operator new?  If I need enough
memory
>     to hold a T object, I pass sizeof(T) to operator new.  If I want the
>     same amount of memory from allocate, I pass 1.  What gives?

I don't know about the first question.
About the second one: while operator new is used to allocate objects for
class T and classes derived from it (which means the size can vary in
increments smaller than sizeof(T)), allocator<T> is used to allocate only
objects of type T. Due to this reason, the size that an allocator<T> will
have to allocate will always be a multiple of sizeof(T). This is reflected
in the interface.

Andrei
---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html              ]





Author: smeyers@aristeia.com (Scott Meyers)
Date: 1999/04/06
Raw View
I have two questions about the design of the interface for
allocator<T>::allocate:

  - Why is the return type T* instead of void*?  Like operator new,
    allocate returns a pointer to raw storage, hence the claimed return
    type of T* is misleading: there is in fact no T object at the
    pointed-to location.

  - Why is the meaning of the first argument to allocate different from the
    meaning of the first argument to operator new?  If I need enough memory
    to hold a T object, I pass sizeof(T) to operator new.  If I want the
    same amount of memory from allocate, I pass 1.  What gives?

Scott

--
Scott Meyers, Ph.D.                  smeyers@aristeia.com
Software Development Consultant      http://www.aristeia.com/
Visit http://meyerscd.awl.com/ to demo the Effective C++ CD
---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html              ]





Author: "Bill Wade" <bill.wade@stoner.com>
Date: 1999/04/06
Raw View

Scott Meyers wrote in message ...
>I have two questions about the design of the interface for
>allocator<T>::allocate:


I've never been a member of the committee, but I'm willing to speculate.

>  - Why is the return type T* instead of void*?  Like operator new,
>    allocate returns a pointer to raw storage, hence the claimed return
>    type of T* is misleading: there is in fact no T object at the
>    pointed-to location.


I agree that returning a T* is misleading for raw storage.  However
returning a T* (IMO) improves type safety for common uses.  If allocate()
returned void*, clients of allocate() would need to static_cast the result.
It is likely that I could write code like
   vector<double, Scotts_Allocator<float> > v;
and get no warnings or errors, but a run-time crash when
Scotts_Allocator<float> returned memory that wasn't suitably aligned (or big
enough) for doubles.

>  - Why is the meaning of the first argument to allocate different from the
>    meaning of the first argument to operator new?  If I need enough memory
>    to hold a T object, I pass sizeof(T) to operator new.  If I want the
>    same amount of memory from allocate, I pass 1.  What gives?


It makes it more obvious that allocator<T>::allocate() need only return
memory suitably aligned for objects of type T.  In particular
allocator<char>::allocate(sizeof(double)) doesn't necessarily provide memory
suitable for use as a double.  This allows some performance improvements
(time or space) on some platforms.

operator new promises the memory it returns is suitably aligned for any
object that fits.  allocator<T>::allocate makes a weaker promise, so it may
be possible to implement it more efficiently.




[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html              ]






Author: ncm@nospam.cantrip.org (Nathan Myers)
Date: 1999/04/06
Raw View
Scott Meyers<smeyers@aristeia.com> wrote:
>I have two questions about the design of the interface for
>allocator<T>::allocate:
>
>  - Why is the return type T* instead of void*?  Like operator new,
>    allocate returns a pointer to raw storage, hence the claimed return
>    type of T* is misleading: there is in fact no T object at the
>    pointed-to location.

This is a good argument for the allocate function to return void*.
However, arithmetic on the result would then be more dangerous.
The STL traffics in uninitialized-but-typed storage elsewhere,
probably for the same reason.

>  - Why is the meaning of the first argument to allocate different from the
>    meaning of the first argument to operator new?  If I need enough memory
>    to hold a T object, I pass sizeof(T) to operator new.  If I want the
>    same amount of memory from allocate, I pass 1.  What gives?

This is easier: why require each caller to use sizeof(T) when the
allocate function can do that itself, in one place?  The "new"
analogy to be drawn is really with new[].

--
Nathan Myers
ncm@nospam.cantrip.org  http://www.cantrip.org/
---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html              ]





Author: sbnaran@bardeen.ceg.uiuc.edu (Siemel Naran)
Date: 1999/04/06
Raw View
On 06 Apr 99 07:03:26 GMT, Scott Meyers <smeyers@aristeia.com> wrote:

>  - Why is the return type T* instead of void*?  Like operator new,
>    allocate returns a pointer to raw storage, hence the claimed return
>    type of T* is misleading: there is in fact no T object at the
>    pointed-to location.

If the return type were "void *", then the client would have to
explicitly cast this to "T *".
   d_data=static_cast<T*>(alloc.allocate(1));
I guess they want to avoid as many casts as possible in client code.


>  - Why is the meaning of the first argument to allocate different from the
>    meaning of the first argument to operator new?  If I need enough memory
>    to hold a T object, I pass sizeof(T) to operator new.  If I want the
>    same amount of memory from allocate, I pass 1.  What gives?

Suppose sizeof(T)==4.
Suppose that the first argument to allocate(...) is the number of bytes.
Then what is the meaning of
   alloc.allocate(6); // space for 1.5 objects!

--
----------------------------------
Siemel B. Naran (sbnaran@uiuc.edu)
----------------------------------
---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html              ]





Author: "Jim Barry" <jim.barry@bigfoot.com>
Date: 1999/04/07
Raw View
Scott Meyers wrote in message ...
>I have two questions about the design of the interface for
>allocator<T>::allocate:
>
>  - Why is the return type T* instead of void*?  Like operator new,
>    allocate returns a pointer to raw storage, hence the claimed
return
>    type of T* is misleading: there is in fact no T object at the
>    pointed-to location.
>
>  - Why is the meaning of the first argument to allocate different
from the
>    meaning of the first argument to operator new?  If I need enough
memory
>    to hold a T object, I pass sizeof(T) to operator new.  If I want
the
>    same amount of memory from allocate, I pass 1.  What gives?

The class allocator<T> manages allocation for arrays of objects of
type T. A particular instantiation of allocator is not supposed to be
used as a general-purpose allocator, and this is reflected in the
design. The allocator knows (via its template parameter) how much
memory to allocate for each object, so it doesn't need to be told
explicity as well. The result of allocator<T>::allocate is a T* rather
than a void* because it points to storage that is intended to be used
only for a T, and it can be passed to (e.g.) allocator<T>::construct
without furthur ado.

Regards,

- Jim

--
Jim Barry, Thermoteknix Systems Ltd., Cambridge, UK.
http://www.thermoteknix.co.uk - Queen's Award for Export 1998
http://www.geocities.com/SiliconValley/2060
---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html              ]