Topic: delete (nothrow)


Author: David R Tribble <david.tribble@noSPAM.central.beasys.com>
Date: 1998/06/25
Raw View
David Abrahams wrote:
> ...
> It's hard to see what the "right" solution is, since legacy systems
> which don't support throwing operator new are neccessarily more
> fragile and somewhat ad-hoc in their semantics in the case of memory
> exhaustion (you have to write special static construction functions,
> or you can end up with partially-constructed objects). Nonetheless, I
> think it would be more useful if nothrow applied to the constructor as
> well.

We use 'new(nothrow)' for the simple reason that exceptions are
not supported on all the platforms we port to.  (We code to a common
subset of C++; see the posts where I mention GCD-C++.)  This
forces us to be a little more careful about how we code constructors.
In particular, our constructors never allocate memory (i.e., they
don't call 'new').  In general, our constructors never reach a
condition that would throw (if 'throw' was supported).

Checking for NULL after calling 'new' is very easy to do.  It's
even easier to handle corrupted-heap errors by using
set_new_handler().  I take the position that you should use
set_new_handler() even if you have exceptions (for the simple
reason that if ::new() fails, it's not because you're out of memory
but because the free store is corrupt, and so you shouldn't risk
creating any new objects, including exception objects).

-- David R. Tribble, david.tribble@central.beasys.com --


[ 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: "Paul D. DeRocco" <pderocco@ix.netcom.com>
Date: 1998/06/23
Raw View
Christopher Eltschka wrote:
>
> I think what he wants is the equivalent of this:
>
> {
>   void* mem = operator new(sizeof(T), nothrow);
>   if(mem) try
>   {
>     new(mem) T;
>   }
>   catch(...)
>   {
>     mem=0;
>   }
>   t=(T*)mem;
> }
>
> That is, every exception the constructor throws is caught and sets the
> pointer to 0.
>
> However, since this throws away much information (namely the contents
> of the exception), I'm not at all shure this is a good idea. However,
> everyone is free to make his own "real nothrow" allocation functions:

Perhaps it would be better for the compiler only to catch bad_alloc, and
let other exceptions from the constructor pass. I don't know. The word
"nothrow" has certain obvious connotations. Perhaps "no_bad_alloc" would
be more to the point.

--

Ciao,
Paul
---
[ 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: "Paul D. DeRocco" <pderocco@ix.netcom.com>
Date: 1998/06/23
Raw View
Steve Clamage wrote:
>
> I'm still wondering what unsolved problem is worrying you.
>
> A new-expression expands into two separate operations:
> 1. allocate storage, and
> 2. construct object(s).
>
> The "nothrow" applies to operation 1. The operator new with
> a nothrow parameter must have an empty exception-specification,
> meaning the compiler can assume it will not exit via an exception.
> Operation 2 is unrelated to the allocation of storage for
> the object(s). A constructor is in general allowed to exit via
> an exception, so the compiler must genenerate code to deal
> with it. If you want to say the constructor will not exit via
> an exception, you already have a way to say that: put an
> empty exception-specification on the constructor.

You can't do this to the standard library objects, like string or
vector. I'm just saying that nothrow would be more useful if

 vector<int>* v = new(nothrow) vector<int>(100);

automatically suppressed the throwing of bad_alloc, both for the
allocation of the vector object and for the allocation of its subsidiary
array. After all, the programmer isn't supposed to care whether vector
is made out of one piece of memory, or two, or more. Conceivably, saying
"new(nothrow) foo;" where foo is any class type not invented by the user
can still throw bad_alloc. Thus, the construct is almost useless, except
when the user knows that the object is made of one piece of memory,
which the user is not supposed to have to know.

--

Ciao,
Paul
---
[ 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: abrahams@motu.com (David Abrahams)
Date: 1998/06/23
Raw View
On 23 Jun 98 12:02:31 GMT, "Paul D. DeRocco" <pderocco@ix.netcom.com>
wrote:

>Perhaps it would be better for the compiler only to catch bad_alloc, and
>let other exceptions from the constructor pass. I don't know. The word
>"nothrow" has certain obvious connotations. Perhaps "no_bad_alloc" would
>be more to the point.

First of all, I want to thank you for understanding the motivation for
my original question better than I did myself. The example you posted
regarding vector is exactly to-the-point, and better than any example
I could think of.

As far as I can tell, new(nothrow) is only useful for an *extremely*
narrow class of legacy code: one has to be able to modify the classes
being constructed so that any allocation they perform (even
indirectly) in their constructor ALSO calls new(nothrow), but at the
same time one must be very careful never to modernize them further to
call any functions which might throw an exception.

However, I don't think that making new(nothrow) mean new(nobad_alloc)
would be useful at all. As far as most exception-handling code is
concerned, one exception is as good as another. It generally won't
make any difference whether the exception is an out-of-memory error or
some other resource acquisition problem.

It's hard to see what the "right" solution is, since legacy systems
which don't support throwing operator new are neccessarily more
fragile and somewhat ad-hoc in their semantics in the case of memory
exhaustion (you have to write special static construction functions,
or you can end up with partially-constructed objects). Nonetheless, I
think it would be more useful if nothrow applied to the constructor as
well.

-Dave
---
[ 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: stephen.clamage@sun.com (Steve Clamage)
Date: 1998/06/21
Raw View
"Paul D. DeRocco" <pderocco@ix.netcom.com> writes:

>Sometimes one wants to handle an error right where it occurs, rather
>than n levels back up the call chain, and nothrow looks like a useful
>way of avoiding the ugliness of writing a try/catch. I suggest this as
>an analog to exceptions(), which was added to ios_base so the user could
>specify whether errors would be detected with explicit tests or with
>try/catch.

>Changing the semantics of nothrow, however, would of course require that
>nothrow (or nothrow_t) be recognized as special by the compiler, so that
>it could automatically generate a hidden try block around the
>constructor call. I can understand why this sort of thing wasn't done,
>since it can't be accomplished merely by writing some library code or
>header macros.

I'm still wondering what unsolved problem is worrying you.

A new-expression expands into two separate operations:
1. allocate storage, and
2. construct object(s).

The "nothrow" applies to operation 1. The operator new with
a nothrow parameter must have an empty exception-specification,
meaning the compiler can assume it will not exit via an exception.

Operation 2 is unrelated to the allocation of storage for
the object(s). A constructor is in general allowed to exit via
an exception, so the compiler must genenerate code to deal
with it. If you want to say the constructor will not exit via
an exception, you already have a way to say that: put an
empty exception-specification on the constructor.

Assuming existing language semantics, if we write
 class T { ... T() throw(); ... };
 ...
 T* t = new (nothrow) T;
the compiler can notice that no exceptions will escape the
entire new-expression, and can avoid generating any extra code.

--
Steve Clamage, stephen.clamage@sun.com


[ 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: Christopher Eltschka <celtschk@physik.tu-muenchen.de>
Date: 1998/06/22
Raw View
Steve Clamage wrote:
>
> "Paul D. DeRocco" <pderocco@ix.netcom.com> writes:
>
> >Sometimes one wants to handle an error right where it occurs, rather
> >than n levels back up the call chain, and nothrow looks like a useful
> >way of avoiding the ugliness of writing a try/catch. I suggest this as
> >an analog to exceptions(), which was added to ios_base so the user could
> >specify whether errors would be detected with explicit tests or with
> >try/catch.
>
> >Changing the semantics of nothrow, however, would of course require that
> >nothrow (or nothrow_t) be recognized as special by the compiler, so that
> >it could automatically generate a hidden try block around the
> >constructor call. I can understand why this sort of thing wasn't done,
> >since it can't be accomplished merely by writing some library code or
> >header macros.
>
> I'm still wondering what unsolved problem is worrying you.
>
> A new-expression expands into two separate operations:
> 1. allocate storage, and
> 2. construct object(s).
>
> The "nothrow" applies to operation 1. The operator new with
> a nothrow parameter must have an empty exception-specification,
> meaning the compiler can assume it will not exit via an exception.
>
> Operation 2 is unrelated to the allocation of storage for
> the object(s). A constructor is in general allowed to exit via
> an exception, so the compiler must genenerate code to deal
> with it. If you want to say the constructor will not exit via
> an exception, you already have a way to say that: put an
> empty exception-specification on the constructor.
>
> Assuming existing language semantics, if we write
>         class T { ... T() throw(); ... };
>         ...
>         T* t = new (nothrow) T;
> the compiler can notice that no exceptions will escape the
> entire new-expression, and can avoid generating any extra code.

I think what he wants is the equivalent of this:

{
  void* mem = operator new(sizeof(T), nothrow);
  if(mem) try
  {
    new(mem) T;
  }
  catch(...)
  {
    mem=0;
  }
  t=(T*)mem;
}

That is, every exception the constructor throws is caught and sets the
pointer to 0.

However, since this throws away much information (namely the contents
of the exception), I'm not at all shure this is a good idea. However,
everyone is free to make his own "real nothrow" allocation functions:

// untested, but should work

template<class T>
 inline T* create() throw()
{
  try
  {
    return new T;
  }
  catch(...)
  {
    return 0;
  }
}

template<class T, class P1>
 inline T* create(P1& p1) throw()
{
  try
  {
    return new T(p1);
  }
  catch(...)
  {
    return 0;
  }
}

template<class T, class P1, class P2>
 inline T* create(P1& p1, P2& p2) throw()
{
  try
  {
    return new T(p1, p2);
  }
  catch(...)
  {
    return 0;
  }
}

// Sometimes needed for rvalues to work:
template<class T>
 inline T const& Const(T const& t) { return t; }

// example usages:

double* d=create<double>(Const(sqrt(2.0)));
    // Const to make rvalue trigger const version

vector<bool>* vi=create<vector<bool> >();

vector<double>* vd=create<vector<double> >(10, *d);
---
[ 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: AlanStokes@my-dejanews.com
Date: 1998/06/22
Raw View
In article <6mjelk$6lo@engnews1.Eng.Sun.COM>,
  stephen.clamage@sun.com (Steve Clamage) wrote:
> I'm still wondering what unsolved problem is worrying you.
[Not actually addressed to me, but if I may interject:]

The problem is we have two available options on a new: a) Allocate &
construct an object, throwing an exception if anything goes wrong. b)
Allocate & construct an object, returning null if the initial allocation
fails and throwing an exception if anything else (including further
allocation) fails.

What we don't have is c) Allocate and construct an object, returning null if
anything goes wrong.

This third option would quite often be helpful. I think it's much more useful
than option b) - std::nothrow is useable only in a large code base that has no
use of exceptions at all, IMHO.

What's even more problematic is that it is very easy to fall into the trap of
assuming that nothrow provides c) not b) - since c) is useful while b) isn't,
and "nothrow" does sound pretty much like it's going to make the new
expression not throw.

I know I thought this for a while, and I'm sure I'm not the only one.

("nothrow" should probably have been called
"nothrowonallocationfailurebutmaystillthrow" - that way not only would the
semantics have been clearer, it would have been so unwieldy that no-one would
have used it, which would be A Good Thing <g>.)

- Alan

-----== Posted via Deja News, The Leader in Internet Discussion ==-----
http://www.dejanews.com/   Now offering spam-free web-based newsreading
---
[ 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: "Paul D. DeRocco" <pderocco@ix.netcom.com>
Date: 1998/06/20
Raw View
Greg Colvin wrote:
>
> The problem I designed operator new(nothrow) to solve was this:
> In ARM C++ operator new reported out-of-memory by returning
> zero, but in Standard C++ operator new reports out-of-memory
> by throwing bad_alloc.  This change can silently break code
> which was not prepared to handle such exceptions, but you can
> fix such code by simply replacing "new" with "new(nothrow)".
> Of course in ARM C++ it was already possible for constructors
> to throw, and new(nothrow) doesn't change that.
>
> There should seldom be a need for new(nothrow) in new code,
> unless by design you are not using exceptions.  In that case
> you will need to provide vector with a custom allocator that
> doesn't throw.

Fair enough. But things evolve beyond the intentions of their inventors.
Sometimes one wants to handle an error right where it occurs, rather
than n levels back up the call chain, and nothrow looks like a useful
way of avoiding the ugliness of writing a try/catch. I suggest this as
an analog to exceptions(), which was added to ios_base so the user could
specify whether errors would be detected with explicit tests or with
try/catch.

Changing the semantics of nothrow, however, would of course require that
nothrow (or nothrow_t) be recognized as special by the compiler, so that
it could automatically generate a hidden try block around the
constructor call. I can understand why this sort of thing wasn't done,
since it can't be accomplished merely by writing some library code or
header macros.

--

Ciao,
Paul


[ 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: "Paul D. DeRocco" <pderocco@ix.netcom.com>
Date: 1998/06/19
Raw View
Steve Clamage wrote:
>
> "Paul D. DeRocco" <pderocco@ix.netcom.com> writes:
>
> >Any comments on whether or not it would have been a good idea to make
> >the compiler aware of the semantics of nothrow, and have it invoke
> >the constructor inside a try-block, so that it could delete the
> >memory and return NULL if the constructor failed? That would seem to
> >me to make nothrow even more useful.
>
> If a constructor exits via an exception in a new-expression,
> the compiler generates code that in effect catches the
> exception, calls the appropriate operator delete, then
> rethrows the exception. Whether a throwing or no-throw
> operator new is involved is irrelevent. You can do the
> construction in a try-block if you want to catch the
> constructor exception.
>
> So what problem are you trying to solve?

The fact that nothrow doesn't really mean nothrow. It just means
nothrow-if-out-of-memory. In fact, it actually means
nothrow-if-out-of-memory-when-allocating-the-root-object. It's quite
common for an object's constructor to allocate additional memory, and
report an error there by throwing bad_alloc. Currently,

 vector<int>* v = new(nothrow) vector<int>(100);

can throw bad_alloc, which is strange because the caller doesn't really
care whether the system ran out of memory when allocating the vector
object or its subsidiary array.

--

Ciao,
Paul
---
[ 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: "Greg Colvin" <spam@me.not>
Date: 1998/06/19
Raw View
Paul D. DeRocco <pderocco@ix.netcom.com> wrote in article
>
> > So what problem are you trying to solve?
>
> The fact that nothrow doesn't really mean nothrow. It just means
> nothrow-if-out-of-memory. In fact, it actually means
> nothrow-if-out-of-memory-when-allocating-the-root-object. It's quite
> common for an object's constructor to allocate additional memory, and
> report an error there by throwing bad_alloc. Currently,
>
>  vector<int>* v = new(nothrow) vector<int>(100);
>
> can throw bad_alloc, which is strange because the caller doesn't really
> care whether the system ran out of memory when allocating the vector
> object or its subsidiary array.

The problem I designed operator new(nothrow) to solve was this:
In ARM C++ operator new reported out-of-memory by returning
zero, but in Standard C++ operator new reports out-of-memory
by throwing bad_alloc.  This change can silently break code
which was not prepared to handle such exceptions, but you can
fix such code by simply replacing "new" with "new(nothrow)".
Of course in ARM C++ it was already possible for constructors
to throw, and new(nothrow) doesn't change that.

There should seldom be a need for new(nothrow) in new code,
unless by design you are not using exceptions.  In that case
you will need to provide vector with a custom allocator that
doesn't throw.

Greg Colvin


[ 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: abrahams@motu.com (David Abrahams)
Date: 1998/06/15
Raw View
I wrote:

>Does anyone out there know what the "nothrow" forms of operator
>delete are there for? I can't find anything in FDIS to
>distinguish them from the standard forms.

Okay, I think I answered my own question, and it's a slightly strange
answer: delete(nothrow) is there in case an exception is thrown from
the constructor of the object being allocated.

I think it would make sense that a new(std::nothrow) expression always
returned 0 instead of throwing an exception, but I don't see that from
reading FDIS. I'd like someone who knows to confirm for me that in the
following code a call to foo() always throws 1, never calls
unexpected(), and never asserts.

struct X { X() {throw 1;} };

void foo()
{
  X* x = new(std::nothrow) X;
  assert(x == 0);
}



[ 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: clamage@Eng (Steve Clamage)
Date: 1998/06/15
Raw View
In article 17098169@news.motu.com, abrahams@motu.com (David Abrahams) writes:
>
>I think it would make sense that a new(std::nothrow) expression always
>returned 0 instead of throwing an exception, but I don't see that from
>reading FDIS.

Well, section 18.4.1.1 "Single   object forms", paragraph 7 says:

"void* operator new(std::size_t size, const std::nothrow_t&) throw();
 ...
 Required behavior: Return a non   null pointer to suitably aligned
 storage (3.7.3), or else return a null pointer. ..."

> I'd like someone who knows to confirm for me that in the
>following code a call to foo() always throws 1, never calls
>unexpected(), and never asserts.
>
>struct X { X() {throw 1;} };
>
>void foo()
>{
>  X* x = new(std::nothrow) X;
>  assert(x == 0);
>}

Section 5.3.4 "New" says that a new-expression first invokes operator
new, and if it returns a non-null pointer invokes the constructor.
If the operator new returns a null pointer, the constructor is not
invoked. See especially paragraph 13.

So if allocation fails, the assertion will be reached, and will pass
(the value of x must compare equal to 0).

But if allocation succeeds, the constructor is called, which exits via
an exception. Operator delete is then called to return the storage,
after which function foo exits via the same exception, and the assert
is never tested.

---
Steve Clamage, stephen.clamage@sun.com



[ 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: abrahams@motu.com (David Abrahams)
Date: 1998/06/15
Raw View
Does anyone out there know what the "nothrow" forms of operator delete
are there for? I can't find anything in FDIS to distinguish them from
the standard forms. In particular, the standard form of operator
delete is required to deal with memory allocated with the nothrow form
of operator new, so why bother with the nothrow form of delete?

*** WARNING ***

If you have been overriding operators new/delete but not the nothrow
forms, you'd better start. Libraries you're using are free to use the
nothrow form of new but delete with the standard form, wreaking havoc
with your heap!

-Dave


[ 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: stephen.clamage@sun.com (Steve Clamage)
Date: 1998/06/15
Raw View
abrahams@motu.com (David Abrahams) writes:

>Does anyone out there know what the "nothrow" forms of operator delete
>are there for? I can't find anything in FDIS to distinguish them from
>the standard forms. In particular, the standard form of operator
>delete is required to deal with memory allocated with the nothrow form
>of operator new, so why bother with the nothrow form of delete?
>
>*** WARNING ***
>
>If you have been overriding operators new/delete but not the nothrow
>forms, you'd better start. Libraries you're using are free to use the
>nothrow form of new but delete with the standard form, wreaking havoc
>with your heap!

None of the versions of operator delete are allowed to throw
an exception.  Thus, for each throw and no-throw pair of
operator new, there is a single operator delete that they share.

The draft standard accordingly states that the no-throw version
of operator new must allocate memory "as if" by calling the
throwing version.

So, yes, if you override one form of operator new, you must
override both. The compiler will probably help you out here
if you are converting from ARM-style code to standard forms,
because the ARM signatures are no longer valid.
OLD:
    #include <new.h>
    void* operator new(size_t) { ... }
    void operator delete(void*) { ... }
NEW:
    #include <new>  // not <new.h>
    void* operator new(std::size_t) throw(std::bad_alloc) { ... }
    void* operator new(std::size_t, const std::nothrow_t&) throw() { ... }
    void operator delete(void*) throw() { ... }
    ... // plus two array forms of new, one array form of delete

--
Steve Clamage, stephen.clamage@sun.com


[ 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              ]