Topic: Why no allocator-specific delete?


Author: mtimmerm@microstar.com (Matt Timmermans)
Date: 1996/02/01
Raw View
(kanze@lts.sel.alcatel.de (James Kanze US/ESC 60/3/141 #40763))

|   In article <4e85k6$b04@noc.tor.hookup.net> mtimmerm@microstar.com
|   (Matt Timmermans) writes:
|
|   |   ...  I can't, in fact, think of a single way that
|   |   placement-new could be used properly.
|
|   [two decent uses described]
|
|   |   The lack of a placement-delete is on of the many factors in C++ conspiring
|   |   to make it absolutely impossible to make generic collections that hold
|   |   actual objects (with constructors) instead of just pointers.

|   I don't understand the problem.  My containers all contain actual
|   objects, and not just pointers.

I spoke somewhat too generally.  Let's say I want to make a generic list:

template <class T> class ListOf;

which stores acutal objects of type T (not just pointers) and contains
methods to add and delete items from the list.  Lets say I also have a
class:

class A
    {
    ...
    public:
    A(int alpha,int beta);
    ~A();
    };

Note that class A has no valid copy semantics.  How can I implement ListOf
such that ListOf<int> and ListOf<A> both work?  There are lots of slimy
hacks, but no elegant solutions.


</Matt>

--------------------------------------------------------------
Matt Timmermans               | Phone:  +1 613 596-2233
Microstar Software Ltd.       | Fax:    +1 613 596-5934
3775 Richmond Rd.             | E-mail: mtimmerm@microstar.com
Nepean Ontario CANADA K2H 5B7 | http://www.microstar.com
---
[ comp.std.c++ is moderated.  Submission address: std-c++@ncar.ucar.edu.
  Contact address: std-c++-request@ncar.ucar.edu.  The moderation policy
  is summarized in http://dogbert.lbl.gov/~matt/std-c++/policy.html. ]





Author: James Kanze US/ESC 60/3/141 #40763 <kanze@lts.sel.alcatel.de>
Date: 1996/02/03
Raw View
In article <4eqn5b$les@noc.tor.hookup.net> mtimmerm@microstar.com
(Matt Timmermans) writes:

|> (kanze@lts.sel.alcatel.de (James Kanze US/ESC 60/3/141 #40763))

|> |   In article <4e85k6$b04@noc.tor.hookup.net> mtimmerm@microstar.com
|> |   (Matt Timmermans) writes:
|> |
|> |   |   ...  I can't, in fact, think of a single way that
|> |   |   placement-new could be used properly.
|> |
|> |   [two decent uses described]
|> |
|> |   |   The lack of a placement-delete is on of the many factors in C++
|> |   |   conspiring to make it absolutely impossible to make generic
|> |   |   collections that hold actual objects (with constructors) instead
|> |   |   of just pointers.

|> |   I don't understand the problem.  My containers all contain actual
|> |   objects, and not just pointers.

|> I spoke somewhat too generally.  Let's say I want to make a generic list:

|> template <class T> class ListOf;

|> which stores acutal objects of type T (not just pointers) and contains
|> methods to add and delete items from the list.  Lets say I also have a
|> class:

|> class A
|>     {
|>     ...
|>     public:
|>     A(int alpha,int beta);
|>     ~A();
|>     };

|> Note that class A has no valid copy semantics.  How can I implement ListOf
|> such that ListOf<int> and ListOf<A> both work?  There are lots of slimy
|> hacks, but no elegant solutions.

You must provide ListOf with some way of knowing how to construct an
object in the list.  STL takes the simple approach: a copy constructor
will be used.  My own container classes will use either a copy
constructor *or* the default constructor, according to the function
invoked.  (In fact, not all of my container classes do this.)  In both
cases, however, ListOf implicitly knows how to construct an object; if
what ListOf implicitly knows isn't true, you're out of luck.

The alternative would be to provide a constructor functional object as
parameter for all functions which might need to construct an object.
This is totally general, but may be a pain for users in the vaste
majority of cases where copying is the best solution.

Thus, in the list class itself:

 template< class T >
 class ListOf
 {
 public :
     class ElemCtor
     {
     public :
         virtual             ~ElemCtor() {}
         virtual void        construct( T* rawMemory ) const = 0 ;
     } ;

     void                insertAtHead( ElemCtor const& ctor ) ;
     //  ...
 } ;

As a user, I might write:

 void
 insertIntoList( ListOf< A >& list , int alpha , int beta )
 {
     class C : public ListOf< A >::ElemCtor
     {
     public :
                             C( int a , int b )
                                 :   alpha( a )
                                 ,   beta( b )
                             {}
         virtual void        construct( T* rawMemory ) const
         {
             new ( rawMemory ) A( alpha , beta ) ;
         }
     private :
         int                 alpha ;
         int                 beta ;
     } ;
     list.insertAtHead( C( alpha , beta ) ) ;
 }

With member templates, this could be even easier, since you no longer
need the base class ListOf< T >::ElemCtor, and you could pass global
functions or functional objects indifferently.  Even using the above
scheme, most of the derived classes can be simpler.  Typically, you
might skip the constructor, make the data members public, declare a
named local instance of the class, and share the data members, thus:

 void
 insertIntoList( ListOf< A >& list , int alpha , int beta )
 {
     class C : public ListOf< A >::ElemCtor
     {
     public :
         virtual void        construct( T* rawMemory ) const
         {
             new ( rawMemory ) A( alpha , beta ) ;
         }

         int                 alpha ;
         int                 beta ;
     }                   c ;
     c.alpha = alpha ;
     c.beta = beta ;
     list.insertAtHead( c ) ;
 }

If the constructor arguments are not just the arguments of the
function, this is the classical way of simulating nested functions and
closure.  A bit verbose, but still quite usable.

--
James Kanze         Tel.: (+33) 88 14 49 00        email: kanze@gabi-soft.fr
GABI Software, Sarl., 8 rue des Francs-Bourgeois, F-67000 Strasbourg, France
Conseils, itudes et rialisations en logiciel orienti objet --
                -- A la recherche d'une activiti dans une region francophone
---
[ comp.std.c++ is moderated.  Submission address: std-c++@ncar.ucar.edu.
  Contact address: std-c++-request@ncar.ucar.edu.  The moderation policy
  is summarized in http://dogbert.lbl.gov/~matt/std-c++/policy.html. ]





Author: John Max Skaller <maxtal@suphys.physics.su.oz.au>
Date: 1996/01/26
Raw View
Matt Timmermans wrote:
> Yes, and this is very annoying.  The lack of a placement-delete makes
> placement-new nearly useless.  I can't, in fact, think of a single way that
> placement-new could be used properly.
>
> The lack of a placement-delete is on of the many factors in C++ conspiring
> to make it absolutely impossible to make generic collections that hold
> actual objects (with constructors) instead of just pointers.

With all due repsect please examine STL carefully.

Who needs "delete"? With STL, deallocation of storage
and destruction of an object are separable.
If you don't like "delete", just don't use it.

The same applies to "new" -- the ONLY form of
new that is necessary is the standard placement form,
because that is the way to create an object in a
specified storage location. So that, too, allows
separation of construction and allocation.

With your own deallocation functions, you can
do whatever overloading you want. So what is the problem?

--
John Max Skaller               voice: 61-2-566-2189
81 Glebe Point Rd              fax:   61-2-660-0850
GLEBE NSW 2037                 web: http://www.maxtal.com.au/~skaller/
AUSTRALIA                      email: skaller@maxtal.com.au
---
[ comp.std.c++ is moderated.  Submission address: std-c++@ncar.ucar.edu.
  Contact address: std-c++-request@ncar.ucar.edu.  The moderation policy
  is summarized in http://dogbert.lbl.gov/~matt/std-c++/policy.html. ]





Author: kanze@lts.sel.alcatel.de (James Kanze US/ESC 60/3/141 #40763)
Date: 1996/01/29
Raw View
In article <4e85k6$b04@noc.tor.hookup.net> mtimmerm@microstar.com
(Matt Timmermans) writes:

|> (clamage@Eng.Sun.COM (Steve Clamage))

|> |   In article 460@news.bridge.net, David Byrden
|> |   <100101.2547@compuserve.com> writes:
|> |   >Why, in the Septenber draft standard, is an allocator-specific verrion
|> |   >of 'new' provided;
|> |   >
|> |   >20.1.4.4
|> |   >
|> |   >           new(x) T
|> |   >
|> |   >    returns an X::types<T>::pointer, where x is of an
|> |   >    allocator type X

|> |   You are actually referring to the "placement new" syntax not having a
|> |   corresponding "placement delete" syntax.

|> Yes, and this is very annoying.  The lack of a placement-delete makes
|> placement-new nearly useless.  I can't, in fact, think of a single way that
|> placement-new could be used properly.

The most common usage of placement new is simply to invoke the
constructor on an arbitrary block of memory.  Since the destructor can
be invoked directly, there is no need for a corresponding placement
delete.

Another potential use of placement new is that described in the
standard: adding a parameter to modify error handling.

Finally, if you really are using the placement to pass information
concerning which arena to use, presumably each arena has a distinct
address range, so you can overload operator delete to test the address
range and choose the correct arena.  In this case, it is a bit of a
bother that you can no longer get at the original operator delete, so
you have to overload the normal operator new too.  (Luckily malloc is
still available.)

|> The lack of a placement-delete is on of the many factors in C++ conspiring
|> to make it absolutely impossible to make generic collections that hold
|> actual objects (with constructors) instead of just pointers.

I don't understand the problem.  My containers all contain actual
objects, and not just pointers.
--
James Kanze         Tel.: (+33) 88 14 49 00        email: kanze@gabi-soft.fr
GABI Software, Sarl., 8 rue des Francs-Bourgeois, F-67000 Strasbourg, France
Conseils, itudes et rialisations en logiciel orienti objet --
                -- A la recherche d'une activiti dans une region francophone
---
[ comp.std.c++ is moderated.  Submission address: std-c++@ncar.ucar.edu.
  Contact address: std-c++-request@ncar.ucar.edu.  The moderation policy
  is summarized in http://dogbert.lbl.gov/~matt/std-c++/policy.html. ]





Author: David Byrden <100101.2547@compuserve.com>
Date: 1996/01/22
Raw View
Why, in the Septenber draft standard, is an allocator-specific verrion of
'new' provided;

20.1.4.4

           new(x) T

    returns an X::types<T>::pointer, where x is of an
    allocator type X


but there is no matching version of 'delete'? Are we supposed to use both
destroy() and deallocate() to get rid of that object?


           David
---
[ comp.std.c++ is moderated.  Submission address: std-c++@ncar.ucar.edu.
  Contact address: std-c++-request@ncar.ucar.edu.  The moderation policy
  is summarized in http://dogbert.lbl.gov/~matt/std-c++/policy.html. ]





Author: clamage@Eng.Sun.COM (Steve Clamage)
Date: 1996/01/22
Raw View
In article 460@news.bridge.net, David Byrden <100101.2547@compuserve.com>
writes:
>Why, in the Septenber draft standard, is an allocator-specific verrion of
>'new' provided;
>
>20.1.4.4
>
>           new(x) T
>
>    returns an X::types<T>::pointer, where x is of an
>    allocator type X

You are actually referring to the "placement new" syntax not having a
corresponding "placement delete" syntax.

No good syntax was found for such. The obvious syntax
 delete (x) p;
leads to too many ambiguities. More importantly, it means that the
point of deletion would somehow have to know what placement version
of "new" was used and what the parameters were. Needing that knowledge
would make "placement delete" unsafe and hard to use. (Consider the
general case where the "new" and "delete" expressions are in different
compilation units, and the parameters to "new" affect how "delete"
should be called.)

You can define a version of "operator delete()" corresponding to a
placement new. If the constructor for the object exits via an exception,
the compiler will call the corresponding placement delete automatically
as a special case.
 T* p = new (x) T;
The reasons for this special case are
1. You do not know what needs to be deleted in general, but the compiler does.
2. The compiler has all the information it needs to invoke the special
operator delete() correctly. It is invoked at the point of the new-expression.

>but there is no matching version of 'delete'? Are we supposed to use both
>destroy() and deallocate() to get rid of that object?

Yes. In the above example:
 p->~T();
 operator delete(p, x);

---
Steve Clamage, stephen.clamage@eng.sun.com
---
[ comp.std.c++ is moderated.  Submission address: std-c++@ncar.ucar.edu.
  Contact address: std-c++-request@ncar.ucar.edu.  The moderation policy
  is summarized in http://dogbert.lbl.gov/~matt/std-c++/policy.html. ]





Author: fjh@munta.cs.mu.OZ.AU (Fergus Henderson)
Date: 1996/01/23
Raw View
b91926@fsgm01.fnal.gov (David Sachs) writes:

>One form for placement operator delete, that might avoid ambiguity is:
>
>delete (placement parameters) (pointer)
>
>The extra parentheses around the pointer would make this not a
>syntactically correct non-placement delete.

Nope, that won't help.  Consider the following code:

 operator delete (void *, void (*)(int *));
 void f(int *);
 int *p;
 // ...
 delete (f) (p);

Should this be a call to placement delete, or should
it be equivalent to

 delete f(p);

?

The ambiguity problems could have been resolved by using a
syntax such as `delete {placement parameters} pointer'.

--
Fergus Henderson              WWW: http://www.cs.mu.oz.au/~fjh
fjh@cs.mu.oz.au               PGP: finger fjh@128.250.37.3
---
[ comp.std.c++ is moderated.  Submission address: std-c++@ncar.ucar.edu.
  Contact address: std-c++-request@ncar.ucar.edu.  The moderation policy
  is summarized in http://dogbert.lbl.gov/~matt/std-c++/policy.html. ]





Author: b91926@fsgm01.fnal.gov (David Sachs)
Date: 1996/01/23
Raw View
clamage@Eng.Sun.COM (Steve Clamage) writes:

>In article 460@news.bridge.net, David Byrden <100101.2547@compuserve.com>
>writes:
>>Why, in the Septenber draft standard, is an allocator-specific verrion of
>>'new' provided;
>>
>>20.1.4.4
>>
>>           new(x) T
>>
>>    returns an X::types<T>::pointer, where x is of an
>>    allocator type X

>You are actually referring to the "placement new" syntax not having a
>corresponding "placement delete" syntax.

>No good syntax was found for such. The obvious syntax
> delete (x) p;
>leads to too many ambiguities. More importantly, it means that the
>point of deletion would somehow have to know what placement version
>of "new" was used and what the parameters were. Needing that knowledge
>would make "placement delete" unsafe and hard to use. (Consider the
>general case where the "new" and "delete" expressions are in different
>compilation units, and the parameters to "new" affect how "delete"
>should be called.)

>You can define a version of "operator delete()" corresponding to a
>placement new. If the constructor for the object exits via an exception,
>the compiler will call the corresponding placement delete automatically
>as a special case.
> T* p = new (x) T;
>The reasons for this special case are
>1. You do not know what needs to be deleted in general, but the compiler
>does.
>2. The compiler has all the information it needs to invoke the special
>operator delete() correctly. It is invoked at the point of the
>new-expression.

>>but there is no matching version of 'delete'? Are we supposed to use both
>>destroy() and deallocate() to get rid of that object?

>Yes. In the above example:
> p->~T();
> operator delete(p, x);

How can you guarantee that the first argument to operator delete
will be correct? It might be slightly safer to write:

 void* q = p;  // evaluate before destruction
 p->~T();
 operator delete(q,x);

and I am not sure if this is guaranteed to be safe.

One form for placement operator delete, that might avoid ambiguity is:

delete (placement parameters) (pointer)

The extra parentheses around the pointer would make this not a
syntactically correct non-placement delete.
--
* IRS, IRS, lord of the federal tax, checking all income and every deduction,*
* mailing out form 10 40, penalizing noncompliers, regulating, and auditing. *
David Sachs - Fermilab, HPPC MS369 - P. O. Box 500 - Batavia, IL 60510
Voice: 1 708 840 3942      Deparment Fax: 1 708 840 3785
---
[ comp.std.c++ is moderated.  Submission address: std-c++@ncar.ucar.edu.
  Contact address: std-c++-request@ncar.ucar.edu.  The moderation policy
  is summarized in http://dogbert.lbl.gov/~matt/std-c++/policy.html. ]





Author: James Kanze US/ESC 60/3/141 #40763 <kanze@lts.sel.alcatel.de>
Date: 1996/01/24
Raw View
In article <9601231054.7277@mulga.cs.mu.OZ.AU> fjh@munta.cs.mu.OZ.AU
(Fergus Henderson) writes:

    [Concerning placement delete...]
|> The ambiguity problems could have been resolved by using a
|> syntax such as `delete {placement parameters} pointer'.

Which, of course, would make it horribly unorthagonal with placement
new.  Unless we also change placement new to use {...} (as was the
case with early versions of g++).  Which, of course, would break so
many existing programs as to be unthinkable.

Would it be worth it to change the syntax of placement new, making the
old syntax a depricated feature (like strstream), so that we can use
the same syntax for placement delete?  (I don't particularly think it
worth the bother, but others might.)

--
James Kanze         Tel.: (+33) 88 14 49 00        email: kanze@gabi-soft.fr
GABI Software, Sarl., 8 rue des Francs-Bourgeois, F-67000 Strasbourg, France
Conseils, itudes et rialisations en logiciel orienti objet --
                -- A la recherche d'une activiti dans une region francophone
---
[ comp.std.c++ is moderated.  Submission address: std-c++@ncar.ucar.edu.
  Contact address: std-c++-request@ncar.ucar.edu.  The moderation policy
  is summarized in http://dogbert.lbl.gov/~matt/std-c++/policy.html. ]





Author: mtimmerm@microstar.com (Matt Timmermans)
Date: 1996/01/25
Raw View
(clamage@Eng.Sun.COM (Steve Clamage))

|   In article 460@news.bridge.net, David Byrden <100101.2547@compuserve.com>
|   writes:
|   >Why, in the Septenber draft standard, is an allocator-specific verrion
|   >of 'new' provided;
|   >
|   >20.1.4.4
|   >
|   >           new(x) T
|   >
|   >    returns an X::types<T>::pointer, where x is of an
|   >    allocator type X

|   You are actually referring to the "placement new" syntax not having a
|   corresponding "placement delete" syntax.

Yes, and this is very annoying.  The lack of a placement-delete makes
placement-new nearly useless.  I can't, in fact, think of a single way that
placement-new could be used properly.


|   No good syntax was found for such. The obvious syntax
|    delete (x) p;
|   leads to too many ambiguities. More importantly, it means that the
|   point of deletion would somehow have to know what placement version
|   of "new" was used and what the parameters were. Needing that knowledge
|   would make "placement delete" unsafe and hard to use. (Consider the
|   general case where the "new" and "delete" expressions are in different
|   compilation units, and the parameters to "new" affect how "delete"
|   should be called.)


The caller of 'delete' already has to know the allocation policy of the
object -- otherwise it would not know that it was safe to delete.  Further,
we already do this sort of thing all the time -- but we have to code it
explicitly, i.e., remove object from collection, delete object.

The lack of a placement-delete is on of the many factors in C++ conspiring
to make it absolutely impossible to make generic collections that hold
actual objects (with constructors) instead of just pointers.


</Matt>

--------------------------------------------------------------
Matt Timmermans               | Phone:  +1 613 596-2233
Microstar Software Ltd.       | Fax:    +1 613 596-5934
3775 Richmond Rd.             | E-mail: mtimmerm@microstar.com
Nepean Ontario CANADA K2H 5B7 | http://www.microstar.com
---
[ comp.std.c++ is moderated.  Submission address: std-c++@ncar.ucar.edu.
  Contact address: std-c++-request@ncar.ucar.edu.  The moderation policy
  is summarized in http://dogbert.lbl.gov/~matt/std-c++/policy.html. ]





Author: fjh@munta.cs.mu.OZ.AU (Fergus Henderson)
Date: 1996/01/25
Raw View
clamage@Eng.Sun.COM (Steve Clamage) writes:

>David Byrden <100101.2547@compuserve.com>
>writes:
>>Why, in the Septenber draft standard, is an allocator-specific verrion of
>>'new' provided;
>>
>>20.1.4.4
>>
>>           new(x) T
>>
>>    returns an X::types<T>::pointer, where x is of an
>>    allocator type X
>
>You are actually referring to the "placement new" syntax not having a
>corresponding "placement delete" syntax.

No; that's part of the issue, but there is also a question of why
allocators are not required to define an operator delete() function.
If I write

 new (x) T

where x is an allocator, and the constructor for T throws an
exception, does x.deallocate() get called?  According to the
September draft, I don't think it does -- and that is a real
problem.

There also appears to be another problem with

 new (x) T[n];

The supposed semantics of this are

 new((void*)x.template allocate<T>(n)) T[n]

However, that may have undefined behaviour!
The reason is that it may not allocate enough memory.
It is only guaranteed to allocate enough memory to hold
`n' objects of type `T'.  However, the compiler
need additional space for ``array allocation overhead''
(see [expr.new]).

--
Fergus Henderson              WWW: http://www.cs.mu.oz.au/~fjh
fjh@cs.mu.oz.au               PGP: finger fjh@128.250.37.3
---
[ comp.std.c++ is moderated.  Submission address: std-c++@ncar.ucar.edu.
  Contact address: std-c++-request@ncar.ucar.edu.  The moderation policy
  is summarized in http://dogbert.lbl.gov/~matt/std-c++/policy.html. ]