Topic: N2855 - Rvalue References and Exception Safety


Author: german diago <germandiago@gmail.com>
Date: Fri, 19 Jun 2009 23:49:43 CST
Raw View
I am of the same opinion. Exceptions should be fixed with the
mechanism that we have today. I mean:

throw() -> in c++0x statically checked
trhow(std::bad_alloc) -> same for this

throw(...) -> new proposal to throw anything

Functions that don't have any exception specification should throw
either anything or nothing
I don't know which should be the default, but for compatibility I'm
almost sure that the former is
safer for backwards compatibility.

And this would work really well. Of course it would break code, but
just
INCORRECT code. Code that made some assertions that don't hold. That's
the reason why I think there shouldn't be an additional mechanism.

> The difference is that my proposal is to reuse existing
> exception specification instead of adding a new keyword.

--
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@netlab.cs.rpi.edu]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html                      ]





Author: Richard Smith <richard@ex-parrot.com>
Date: Mon, 11 May 2009 13:39:58 CST
Raw View
On May 1, 8:08 pm, goo...@dalvander.com wrote:
> After reading N2855 - Rvalue References and Exception Safety (http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2009/n2855.html) I'm still
> not confident that a new keyword `noexcept` would be optimal.

I agree.  I would prefer to see this integrating better with existing
exception specification mechanism, and the latter made usable.

> Especially when the proposal combines this new keyword with a somewhat
> enhanced version of the already existing exception specification `throw
> (...)` to explicitly indicate that a move constructor or a destructor
> may throw exceptions.

The current language does not have a way to /explicitly/ indicate that
a function may throw but without saying what it might throw.  The
syntax "throw(...)" is /not/ valid currently and N2855 does not change
this.  Yes, I can say "throw (std::bad_alloc)" or "throw
(my_exception)", but the way of signifying that an arbitrary exception
may be thrown is to omit the entirely ES.

> And at the same time propose to deprecate
> exception specifications alltogether.

I have to say, I have misgivings about this too.  As they stand, ESs
have two major problems: (i) they cannot be statically checked; and
(ii) they don't work well in templates where you don't know what might
be thrown.

But there is also a lot of misinformation about them.  Eighteen months
ago I did some extensive research into adding no-throw ESs in the
lowest-level parts of a library I was writing.  I only considered
compilation with whatever the most recent version of GCC was at the
time.  My finding was that when applied judiciously, they could
noticeably reduce code size (by eliminating unused exception handling
paths) and lead to marginally better machine code being generated (by
freeing up a register and occasionally eliminating a test and jump).

However, a much bigger gain that you can get from today's ESs is code-
correctness.  One example that I frequently found was when writing a
swap function (or assignment operator if that isn't written in terms
of swap).  This problem is very similar to the problem exhibited by
the move constructor example in N2855.

   template <class Value, class Allocator>
   struct foo {
     Value val;
     Allocator al;
   };

   template <class Value, class Allocator>
   void swap( foo<Value, Allocator> & x,
              foo<Value, Allocator> & y ) {
     std::swap( x.val, y.val );
     std::swap( x.al,  y.al  );
   }

What happens if swapping the allocators throws?   In fact, I probably
assuming the Allocator concept has a no-throw swap.  C++03 doesn't
allow me to enforce that at compile time.  However I can do the next
best thing and trap it at run time.  And to this aim I found two
little helper templates useful:

   template <class T>
   void do_swap( T& x, T& y ) {
     using std::swap; swap( x, y );
   }

   template <class T>
   void do_swap( T& x, T& y, std::nothrow_t ) throw() {
     using std::swap; swap( x, y );
   }

(These also handle ADL of swap correctly.)  Using these, the body of
the original swap function can be rewritten:

     do_swap( x.val, y.val );
     do_swap( x.al,  y.al,  std::nothrow );

And we now have a swap that is guaranteed to leave the class
consistent -- it'll either work; or, if the Allocator violates its
exception contract, it'll abort.  Far better, especially if the
destructor requires the allocator and value to be in sync.

So, yes, I think that a lot could be done to improve ESs, but I
certainly don't consider them to be useless.  However, I'm certainly
not defending the Java-esque application of long ESs to higher-level
functions.  I don't think ESs have a place in C++ at that level.

> I think that it would be better to use a simplified exception
> specification:
>
> - `throw()` to indicate that a function does not throw any exception,
> which is statically checked by the compiler. This would behave as
> `noexcept` in the N2855 proposal.

This is a breaking change.

What if someone has put a throw() specification on an existing
function that might throw?  Assuming the author knew what they were
doing, they presumably knew there were circumstances in which the
function could throw but had determined that there was no way for the
caller to safely handle the condition, and so would rather terminate
then and there.

Changing throw() to be statically checked would prevent their code
from compiling.

I'm all for statically-checked no-throw ESs, but they need a new
syntax.  Personally I would rather see:

   void foo() static throw();

rather than

   noexcept void foo();


> - `throw(...)` to indicate that a function may throw any exception.
> Main use for throwing move constructors and throwing destructors.

Why do you need a syntax for this?  If a function does not say that it
does not throw then it may throw.  This is true in C++03; why should
it be different in C++0x?

> - Any other exception specification would be ill-formed and thus would
> require a compiler diagnostic.

I would prefer to see the status quo remain for more complicated ESs.
I don't believe they have a use in the current language; however I can
imagine that if the no-throw ES starts to be applied more widely,
there /might/ be a place for the occasional other ES in low-level
code.

So lets leave the ability to write throw(my_exception) until we know
whether it will be of any use.  If not, a future version of the
language can deprecate and/or remove it.

> Unfortunately I don't have a really good idea to implement something
> similar to the `noexcept` block as described in N2855. Perhaps a `try`
> block without a `catch` handler, perhaps someone else has a better
> idea?

I'm not convinced that it is necessary at all.  If you want this
behaviour you can do it by re-factoring the enclosed code into a
separate function.  So, in the example in N2855, instead of:

   noexcept void f(double &x) {
     if (x > 0) {
       noexcept { x = sqrt(x); }
       // okay: if sqrt(x) throws, invokes undefined behavior
     }
   }

... I would sooner see:

   void f(double &x) static throw() {
     if (x > 0)
       call_sqrt(x);
       // okay: call_sqrt cannot throw
   }

   void call_sqrt(double &x) throw() {
     x = sqrt(x);
     // okay: if sqrt(x) throws, invokes std::terminate
   }

Note how we're using run-time ESs and statically-checked ESs to
complement each other.  And, furthermore, we now have no undefined
behaviour which has to be a good thing.

I'm also concerned that this gives the noexcept keyword two
diametrically opposite meanings.  As a function specifier it means
statically check this block of code; as a block specifier it means do
not statically check this block of code.  That sounds like a recipe
for confusion.

However, /if/ some sort of noexcept block is deemed necessary, I quite
like your idea of reusing the try keyword.  But looking further at the
grammar, I don't think a keyword is needed at all -- why not /just/
write an ES (whether static or run-time) followed by a block?  E.g.,
the direct equivalent of the noexcept block proposed by N2855 would
be:

   throw() {
     x = sqrt(x);
   }

But we now have more options.  I can certainly imagine wanting to
statically check a fragment of code in an otherwise unchecked
function:

   static throw() {
     bar( std::move(x) );
   }

Richard Smith


--
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@netlab.cs.rpi.edu]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html                      ]





Author: google@dalvander.com
Date: Wed, 13 May 2009 21:19:40 CST
Raw View
Hi Richard, and thanks for your comments.

On May 11, 9:39 pm, Richard Smith <rich...@ex-parrot.com> wrote:
> On May 1, 8:08 pm, goo...@dalvander.com wrote:
>
> > Especially when the proposal combines this new keyword with a somewhat
> > enhanced version of the already existing exception specification `throw
> > (...)` to explicitly indicate that a move constructor or a destructor
> > may throw exceptions.
>
> The current language does not have a way to /explicitly/ indicate that
> a function may throw but without saying what it might throw.  The
> syntax "throw(...)" is /not/ valid currently and N2855 does not change
> this.

In the "Destructors are Non-throwing" section, N2855 propose that all
destructors be implicitly be declared `noexcept`. In order to allow
throwing destructors, N2855 propose to use the syntax `throw(...)` to
mean "this function can throw any exception".

> But there is also a lot of misinformation about them.  Eighteen months
> ago I did some extensive research into adding no-throw ESs in the
> lowest-level parts of a library I was writing.  I only considered
> compilation with whatever the most recent version of GCC was at the
> time.  My finding was that when applied judiciously, they could
> noticeably reduce code size (by eliminating unused exception handling
> paths) and lead to marginally better machine code being generated (by
> freeing up a register and occasionally eliminating a test and jump).

Any C++ compiler which always use `throw()` as an optimization
opportunity are probaly flawed. Visual C++ is an example of one of
these flawed compilers.

See http://msdn.microsoft.com/en-us/library/wfa0edys.aspx
> > > throw() - The function does not throw an exception. However, if
> > > an exception is thrown out of a function marked throw(), the
> > > Visual C++ compiler will not call unexpected (see unexpected
> > > (CRT) and unexpected (<exception>) for more information).
> > > If a function is marked with throw(), the Visual C++ compiler
> > > will assume that the function does not throw C++ exceptions
> > > and generated code accordingly. Due to code optimizations that
> > > maybe performed by the C++ compiler (based on the assumption
> > > that the function does not throw any C++ exceptions) if
> > > function does throw an exception, the program may not execute
> > > correctly.

According to the existing C++ standard the following functions foo1
and foo2 should behave equivalent.

// may throw
void bar();

// using empty exception specification
void foo1() throw()
{
  bar();
}

// using function try-block
void foo2()
try
{
  bar();
}
catch (...)
{
  std::unexpected();
}

That is, if `bar` throws an exception `std::unexpected` shall be
called.
On the other hand, if `bar` would have the following signature then a
function try-block isn't needed to be generated in `foo1`, as no
exception can escape from `bar`:

// will not throw
void bar() throw();

> > - `throw()` to indicate that a function does not throw any exception,
> > which is statically checked by the compiler. This would behave as
> > `noexcept` in the N2855 proposal.
>
> This is a breaking change.
>
> What if someone has put a throw() specification on an existing
> function that might throw?  Assuming the author knew what they were
> doing, they presumably knew there were circumstances in which the
> function could throw but had determined that there was no way for the
> caller to safely handle the condition, and so would rather terminate
> then and there.
>
> Changing throw() to be statically checked would prevent their code
> from compiling.

Yes, that is true. A breaking change in order to ensure that the
program isn't already made flawed by a flawed compiler.

> > - `throw(...)` to indicate that a function may throw any exception.
> > Main use for throwing move constructors and throwing destructors.
>
> Why do you need a syntax for this?  If a function does not say that it
> does not throw then it may throw.  This is true in C++03; why should
> it be different in C++0x?

See above.

> > Unfortunately I don't have a really good idea to implement something
> > similar to the `noexcept` block as described in N2855. Perhaps a `try`
> > block without a `catch` handler, perhaps someone else has a better
> > idea?
>
> I'm not convinced that it is necessary at all.  If you want this
> behaviour you can do it by re-factoring the enclosed code into a
> separate function.  So, in the example in N2855, instead of:
>
>    noexcept void f(double &x) {
>      if (x > 0) {
>        noexcept { x = sqrt(x); }
>        // okay: if sqrt(x) throws, invokes undefined behavior
>      }
>    }
>
> ... I would sooner see:
>
>    void f(double &x) static throw() {
>      if (x > 0)
>        call_sqrt(x);
>        // okay: call_sqrt cannot throw
>    }
>
>    void call_sqrt(double &x) throw() {
>      x = sqrt(x);
>      // okay: if sqrt(x) throws, invokes std::terminate
>    }

I beleive the purpose of the `noexcept` block as described in N2855 is
so that developers does not need to write all these wrapper functions
in order to support legacy functions, which does not have a exception
specification, such as the C++ std lib and the C std lib.

> And, furthermore, we now have no undefined behaviour which has to be
> a good thing.

Undefined behavior isn't a bad thing as such. Many constructs in C++,
such as cast operators `const_cast`, `static_cast` and
`reinterpret_cast`, are powerful tools but may lead to undefined
behavior if used incorrectly.

> However, /if/ some sort of noexcept block is deemed necessary, I quite
> like your idea of reusing the try keyword.  But looking further at the
> grammar, I don't think a keyword is needed at all -- why not /just/
> write an ES (whether static or run-time) followed by a block?  E.g.,
> the direct equivalent of the noexcept block proposed by N2855 would
> be:
>
>    throw() {
>      x = sqrt(x);
>    }

That is using a keyword, just a different one. You propose `throw`, I
propose `try`.

> But we now have more options.  I can certainly imagine wanting to
> statically check a fragment of code in an otherwise unchecked
> function:
>
>    static throw() {
>      bar( std::move(x) );
>    }

That would add value. But on the other hand, a `const` block would
also add value in non-const member functions.

> Richard Smith

--
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@netlab.cs.rpi.edu]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html                      ]





Author: Giovanni Deretta <gpderetta@gmail.com>
Date: Thu, 14 May 2009 17:21:40 CST
Raw View
On May 14, 5:19 am, goo...@dalvander.com wrote:
> Hi Richard, and thanks for your comments.
>
> On May 11, 9:39 pm, Richard Smith <rich...@ex-parrot.com> wrote:
>
> > But there is also a lot of misinformation about them.  Eighteen months
> > ago I did some extensive research into adding no-throw ESs in the
> > lowest-level parts of a library I was writing.  I only considered
> > compilation with whatever the most recent version of GCC was at the
> > time.  My finding was that when applied judiciously, they could
> > noticeably reduce code size (by eliminating unused exception handling
> > paths) and lead to marginally better machine code being generated (by
> > freeing up a register and occasionally eliminating a test and jump).
>
> Any C++ compiler which always use `throw()` as an optimization
> opportunity are probaly flawed. Visual C++ is an example of one of
> these flawed compilers.
>

Why would optimizing using 'throw()' information is wrong in general?
Broken compilers notwithstanding.

> > > - `throw()` to indicate that a function does not throw any exception,
> > > which is statically checked by the compiler. This would behave as
> > > `noexcept` in the N2855 proposal.
>
> > This is a breaking change.
>
> > What if someone has put a throw() specification on an existing
> > function that might throw?  Assuming the author knew what they were
> > doing, they presumably knew there were circumstances in which the
> > function could throw but had determined that there was no way for the
> > caller to safely handle the condition, and so would rather terminate
> > then and there.
>
> > Changing throw() to be statically checked would prevent their code
> > from compiling.
>
> Yes, that is true. A breaking change in order to ensure that the
> program isn't already made flawed by a flawed compiler.
>

How breaking correct code is better than fixing broken compilers?
FWIW, I use throw() quite often as an assertion, when I want to make
sure that an exception unsafe piece of code is not traversed by an
exception.

I expect quite a lot of code to be broken by such a change.

--
gpd


--
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@netlab.cs.rpi.edu]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html                      ]





Author: Richard Smith <richard@ex-parrot.com>
Date: Fri, 15 May 2009 12:07:06 CST
Raw View
Thanks for your reply.

On May 14, 4:19 am, goo...@dalvander.com wrote:

> In the "Destructors are Non-throwing" section, N2855 propose that all
> destructors be implicitly be declared `noexcept`. In order to allow
> throwing destructors, N2855 propose to use the syntax `throw(...)` to
> mean "this function can throw any exception".

Ah, I hadn't properly appreciated that paragraph.  Thanks for pointing
it out.   That makes a lot of sense, and throw(...) is certainly the
obvious syntax to indicate that the function (destructor) does not
implement a no-throw guarantee.

> > But there is also a lot of misinformation about them.  Eighteen months
> > ago I did some extensive research into adding no-throw ESs in the
> > lowest-level parts of a library I was writing.  I only considered
> > compilation with whatever the most recent version of GCC was at the
> > time.  My finding was that when applied judiciously, they could
> > noticeably reduce code size (by eliminating unused exception handling
> > paths) and lead to marginally better machine code being generated (by
> > freeing up a register and occasionally eliminating a test and jump).
>
> Any C++ compiler which always use `throw()` as an optimization
> opportunity are probaly flawed. Visual C++ is an example of one of
> these flawed compilers.

I'm sorry, I don't follow the logic here.  Suppose I have two
fragments of code:

   // version 1
   struct T { ~T(); };
   void f1();
   void g1() { T t; f1(); }

   // version 2
   struct T { ~T() throw(); };
   void f2() throw();
   void g2() { T t; f2(); }

Why should the compiler not optimise version 2 knowing that exceptions
never pass through bar2() while they can pass through bar1()?  If I
compile the two versions on ix86 with gcc-4.4.0 -O2, the machine code
for g1() is 48 bytes long whereas the code for g2() is 24 bytes.
Obviously this is rather silly example using just one compiler and one
set of optimisation settings, and one certainly shouldn't draw too
much from it.  Nevertheless, it does indicate that compilers can and
do optimise based on this.


> According to the existing C++ standard the following functions foo1
> and foo2 should behave equivalent.
>
> // may throw
> void bar();
>
> // using empty exception specification
> void foo1() throw() {
>   bar();
> }
>
> // using function try-block
> void foo2() try {
>   bar();
> } catch (...) {
>   std::unexpected();
> }

Not true.  std::unexpected() is permitted to exit by throwing an
exception [15.4/9; 15.5.2/2].  If it does so in foo1() then
std::terminate() is called; if it does so in foo2() then the exception
is propagated.  If you want something equivalent to foo1(), but
without using an ES, it would be:

   void foo2() try {
     bar();
   } catch (...) {
     try {
       std::unexpected();
     } catch (...) {
       std::terminate();
     }
   }


> > > - `throw()` to indicate that a function does not throw any exception,
> > > which is statically checked by the compiler. This would behave as
> > > `noexcept` in the N2855 proposal.
>
> > This is a breaking change.
>
> > What if someone has put a throw() specification on an existing
> > function that might throw?  Assuming the author knew what they were
> > doing, they presumably knew there were circumstances in which the
> > function could throw but had determined that there was no way for the
> > caller to safely handle the condition, and so would rather terminate
> > then and there.
>
> > Changing throw() to be statically checked would prevent their code
> > from compiling.
>
> Yes, that is true. A breaking change in order to ensure that the
> program isn't already made flawed by a flawed compiler.

This is a breaking change even for a fully-standards-compliant
compiler.  As a programmer I might decide to write

   Foo::~Foo() throw() {
     cleanup();
   }

I might know full well that, in certain circumstances cleanup() might
throw an exception.  I might have evaluated these circumstances
carefully, and I might have decided that the behaviour I want in those
circumstances is for the code to terminate.  And that is exactly what
this code does when compiled with a standards-compliant compiler.  (I
accept that Microsoft Visual C++ may do something else; but perhaps I
don't propose to use that compiler.)

Under your proposal, this code would cease to compile because there is
(we presume) no throw() ES on cleanup().  Now, instead of doing
something well-defined and quite possibly deliberately selected, it
now doesn't compile.  That is a breaking change.


> > > - `throw(...)` to indicate that a function may throw any exception.
> > > Main use for throwing move constructors and throwing destructors.
>
> > Why do you need a syntax for this?  If a function does not say that it
> > does not throw then it may throw.  This is true in C++03; why should
> > it be different in C++0x?
>
> See above.

Accepted.

> > > Unfortunately I don't have a really good idea to implement something
> > > similar to the `noexcept` block as described in N2855. Perhaps a `try`
> > > block without a `catch` handler, perhaps someone else has a better
> > > idea?
>
> > I'm not convinced that it is necessary at all.  If you want this
> > behaviour you can do it by re-factoring the enclosed code into a
> > separate function.  So, in the example in N2855, instead of:
>
> >    noexcept void f(double &x) {
> >      if (x > 0) {
> >        noexcept { x = sqrt(x); }
> >        // okay: if sqrt(x) throws, invokes undefined behavior
> >      }
> >    }
>
> > ... I would sooner see:
>
> >    void f(double &x) static throw() {
> >      if (x > 0)
> >        call_sqrt(x);
> >        // okay: call_sqrt cannot throw
> >    }
>
> >    void call_sqrt(double &x) throw() {
> >      x = sqrt(x);
> >      // okay: if sqrt(x) throws, invokes std::terminate
> >    }
>
> I beleive the purpose of the `noexcept` block as described in N2855 is
> so that developers does not need to write all these wrapper functions
> in order to support legacy functions, which does not have a exception
> specification, such as the C++ std lib and the C std lib.

Well, I'm sure that the C++ standard library would gain ESs (or
noexcept keywords) in appropriate places if N2855 is adopted.  In some
compilers, many C library functions already have them when compiled as
C++.  For example, using GNU Libc 2.8 and GCC 4.4.0, I see that sprintf
() has a throw() ES.

It may be useful for supporting other pre-C++0x libraries, though.

> > And, furthermore, we now have no undefined behaviour which has to be
> > a good thing.
>
> Undefined behavior isn't a bad thing as such. Many constructs in C++,
> such as cast operators `const_cast`, `static_cast` and
> `reinterpret_cast`, are powerful tools but may lead to undefined
> behavior if used incorrectly.

I'm not sure I'd say it isn't a bad thing: an occasionally necessary
evil, more like.  The difference being that I can't see how one might
implement those cast operators without any possibility of undefined
behaviour, without a significant run-time penalty.  (In principle they
could all be done with heavy use of RTTI, but that would be expensive
in the case where the cast is being used properly.)

By contrast, the undefined behaviour from a noexcept block that
actually throws can be eliminated with minimal run-time expense in the
case when it the block really doesn't throw.  In any decent compiler,
the default code path through

   void foo() { foo(); }

and

   void foo() throw() { foo(); }

are going to take very similar times.  (The speed optimisations I
spoke about earlier are very small: perhaps gaining the use of a
single register.  The code length might be longer, but this oughtn't
to have a big effect on run-time performance.)

> > However, /if/ some sort of noexcept block is deemed necessary, I quite
> > like your idea of reusing the try keyword.  But looking further at the
> > grammar, I don't think a keyword is needed at all -- why not /just/
> > write an ES (whether static or run-time) followed by a block?  E.g.,
> > the direct equivalent of the noexcept block proposed by N2855 would
> > be:
>
> >    throw() {
> >      x = sqrt(x);
> >    }
>
> That is using a keyword, just a different one. You propose `throw`, I
> propose `try`.

Well, yes and no.  The difference is that I'm reusing the whole of the
existing ES mechanism.  Hence

   throw(std::bad_alloc) {
     t = new T;
   }

would be legal (though likely of dubious use); as would be far more
useful static check suggestion I made:

   static throw() {
     f();  // Won't compile if f() might throw.
   }

The reason for preferring throw() { ... } to try { ... } is really
that it fits neatly and intuitively with the existing ES mechanism.
The 'logical' meaning for a bare try-block (i.e. without a catch)
would simply be the same as a ordinary compound statement (i.e.
block).  (This is why (IIRC, I don't have the book to hand) Stroustrup
says in D&E that the try keyword is actually syntactically
unnecessary, and that the language would still be unambiguous if it
were omitted.)

> That would add value. But on the other hand, a `const` block would
> also add value in non-const member functions.

If "we can't add feature X because feature Y might also be useful and
we're not currently proposing to add that" were a valid reason for
rejecting a new feature, the language would never evolve at all.

Richard Smith


--
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@netlab.cs.rpi.edu]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html                      ]





Author: google@dalvander.com
Date: Fri, 15 May 2009 15:13:20 CST
Raw View
Hi Giovanni, and thanks for your comments.

On May 15, 1:21 am, Giovanni Deretta <gpdere...@gmail.com> wrote:
> On May 14, 5:19 am, goo...@dalvander.com wrote:
>
> > Any C++ compiler which always use `throw()` as an optimization
> > opportunity are probaly flawed. Visual C++ is an example of one of
> > these flawed compilers.
>
> Why would optimizing using 'throw()' information is wrong in general?
> Broken compilers notwithstanding.

Sorry about the misunderstanding, using exception specification
information for optimization isn't wrong in general. What I meant was:
Any C++ compiler which *always* and *only* use `throw()` as an
optimization opportunity are probably flawed.

Visual C++, for instance, optimize all functions using `throw()`,
without adding the extra function try-block when needed. For instance
the following program will output 42 in Visual C++, where a standard
complaint compiler would generate code which will call
std::unexpected, which in turn will call std::terminate. Unfortunately
I don't have metrics, and have not looked into the documentation on
how other compilers behave.

#include <iostream>

void foo() throw()
{
    throw 42; // should call unexpected, which should call terminate
}

int main()
{
    try
    {
     foo();
    }
    catch (int i)
    {
     std::cout << i << "\n";
    }
}

> > Yes, that is true. A breaking change in order to ensure that the
> > program isn't already made flawed by a flawed compiler.
>
> How breaking correct code is better than fixing broken compilers?
> FWIW, I use throw() quite often as an assertion, when I want to make
> sure that an exception unsafe piece of code is not traversed by an
> exception.
>
> I expect quite a lot of code to be broken by such a change.

N2855 propose that all destructors be implicitly be declared
`noexcept`, and I agree. This on the other hand will break most, if
not all, code which have non-trivial destructors. My proposal is that
a simplified, but statically checked, exception specification should
be used instead of the new keyword `noexcept`. Unfortunately this will
break code as well.

> gpd

Regards,
Anders Dalvander

--
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@netlab.cs.rpi.edu]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html                      ]





Author: Giovanni Deretta <gpderetta@gmail.com>
Date: Sat, 16 May 2009 21:58:06 CST
Raw View
On May 15, 11:13 pm, goo...@dalvander.com wrote:
> Hi Giovanni, and thanks for your comments.
>
> On May 15, 1:21 am, Giovanni Deretta <gpdere...@gmail.com> wrote:
>
> > On May 14, 5:19 am, goo...@dalvander.com wrote:
>
> > > Any C++ compiler which always use `throw()` as an optimization
> > > opportunity are probaly flawed. Visual C++ is an example of one of
> > > these flawed compilers.
>
> > Why would optimizing using 'throw()' information is wrong in general?
> > Broken compilers notwithstanding.
>
> Sorry about the misunderstanding, using exception specification
> information for optimization isn't wrong in general. What I meant was:
> Any C++ compiler which *always* and *only* use `throw()` as an
> optimization opportunity are probably flawed.
>
>[ example of a compiler not following the standard sniped]

Such a compiler is broken in this regard and should be fixed.

> > > Yes, that is true. A breaking change in order to ensure that the
> > > program isn't already made flawed by a flawed compiler.
>
> > How breaking correct code is better than fixing broken compilers?
> > FWIW, I use throw() quite often as an assertion, when I want to make
> > sure that an exception unsafe piece of code is not traversed by an
> > exception.
>
> > I expect quite a lot of code to be broken by such a change.
>
> N2855 propose that all destructors be implicitly be declared
> `noexcept`, and I agree. This on the other hand will break most, if
> not all, code which have non-trivial destructors.

No, IIRC will only break code that actually throws from destructors.
As throwing from destructors is one of those thing that
Thou Shall Not Do (for example such an object cannot work with
standard containers), very little code will be broken in practice.
There are some idioms (unstoppable exceptions) that rely on throwing
destructors, but those can be fixed with throw(...).

> My proposal is that
> a simplified, but statically checked, exception specification should
> be used instead of the new keyword `noexcept`. Unfortunately this will
> break code as well.
>

I think that a statically checked empty exception specification would
be useful, but it should be in addition to noexcept and throw().

--
Giovanni P. Deretta


--
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@netlab.cs.rpi.edu]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html                      ]





Author: google@dalvander.com
Date: Sat, 16 May 2009 21:59:10 CST
Raw View
Hi Richard,

On May 15, 8:07 pm, Richard Smith <rich...@ex-parrot.com> wrote:
> On May 14, 4:19 am, goo...@dalvander.com wrote:
> > Any C++ compiler which always use `throw()` as an optimization
> > opportunity are probaly flawed. Visual C++ is an example of one of
> > these flawed compilers.
>
> I'm sorry, I don't follow the logic here.  Suppose I have two
> fragments of code:
>
>    // version 1
>    struct T { ~T(); };
>    void f1();
>    void g1() { T t; f1(); }
>
>    // version 2
>    struct T { ~T() throw(); };
>    void f2() throw();
>    void g2() { T t; f2(); }
>
> Why should the compiler not optimise version 2 knowing that exceptions
> never pass through bar2() while they can pass through bar1()?  If I
> compile the two versions on ix86 with gcc-4.4.0 -O2, the machine code
> for g1() is 48 bytes long whereas the code for g2() is 24 bytes.
> Obviously this is rather silly example using just one compiler and one
> set of optimisation settings, and one certainly shouldn't draw too
> much from it.  Nevertheless, it does indicate that compilers can and
> do optimise based on this.

I posted the following answer to Giovanni Deretta above:

Sorry about the misunderstanding, using exception specification
information for optimization isn't wrong in general. What I meant was:
Any C++ compiler which *always* and *only* use `throw()` as an
optimization opportunity are probably flawed.

Visual C++, for instance, optimize all functions using `throw()`,
without adding the extra function try-block when needed. For instance
the following program will output 42 in Visual C++, where a standard
complaint compiler would generate code which will call
std::unexpected, which in turn will call std::terminate. Unfortunately
I don't have metrics, and have not looked into the documentation on
how other compilers behave.

#include <iostream>

void foo() throw()
{
    throw 42; // should call unexpected, which should call terminate
}

int main()
{
    try
    {
     foo();
    }
    catch (int i)
    {
     std::cout << i << "\n";
    }
}

> > According to the existing C++ standard the following functions foo1
> > and foo2 should behave equivalent.
>
> > // may throw
> > void bar();
>
> > // using empty exception specification
> > void foo1() throw() {
> >   bar();
> > }
>
> > // using function try-block
> > void foo2() try {
> >   bar();
> > } catch (...) {
> >   std::unexpected();
> > }
>
> Not true.  std::unexpected() is permitted to exit by throwing an
> exception [15.4/9; 15.5.2/2].  If it does so in foo1() then
> std::terminate() is called; if it does so in foo2() then the exception
> is propagated.  If you want something equivalent to foo1(), but
> without using an ES, it would be:
>
>    void foo2() try {
>      bar();
>    } catch (...) {
>      try {
>        std::unexpected();
>      } catch (...) {
>        std::terminate();
>      }
>    }

Yes, you are correct. I forgot about the case when std::unexpected
throws an exception, thanks for pointing it out.

> > I beleive the purpose of the `noexcept` block as described in N2855 is
> > so that developers does not need to write all these wrapper functions
> > in order to support legacy functions, which does not have a exception
> > specification, such as the C++ std lib and the C std lib.
>
> Well, I'm sure that the C++ standard library would gain ESs (or
> noexcept keywords) in appropriate places if N2855 is adopted.  In some
> compilers, many C library functions already have them when compiled as
> C++.  For example, using GNU Libc 2.8 and GCC 4.4.0, I see that sprintf
> () has a throw() ES.

Good catch.

> This is a breaking change even for a fully-standards-compliant
> compiler.  As a programmer I might decide to write
>
>    Foo::~Foo() throw() {
>      cleanup();
>    }
>
> Under your proposal, this code would cease to compile because there is
> (we presume) no throw() ES on cleanup().  Now, instead of doing
> something well-defined and quite possibly deliberately selected, it
> now doesn't compile.  That is a breaking change.

N2855 propose that `noexcept` should behave as a statically checked
`throw()` and that all destructors be implicitly be declared
`noexcept`. This has the effect that most non-trivial destructors will
be broken, as they probably call functions which isn't declared
`noexcept`. N2855 give us a two options to manually fix this:

1) Using a `noexcept` block.
2) Change the signature of all the called functions, adding `noexcept`
to their declaration.

Both of these options require modifications to code in order to
compile, and thus this is a breaking change. The second option is
viral, and the same need to be done to all of the called functions
recursively.

My proposal derives from N2855, and is identical to N2855 in this
matter. The difference is that my proposal is to reuse existing
exception specification instead of adding a new keyword.

I really like the idea of having a statically checked no-throwing
destructor by default, but I cannot defend all these breaking changes
in N2855, or in my own derived proposal, and I would recommend that
N2855 (and my own proposal) is declined.

The issues brought forward in N2855 still need to be resolved in C+
+0x, but in another, non-breaking way.

Regards,
Anders Dalvander

--
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@netlab.cs.rpi.edu]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html                      ]





Author: joshuamaurice@gmail.com
Date: Mon, 4 May 2009 20:21:33 CST
Raw View
On May 3, 9:12 am, Sean Hunt <ride...@gmail.com> wrote:
> On May 1, 1:08 pm, goo...@dalvander.com wrote:
> > After reading N2855 - Rvalue References and Exception Safety (http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2009/n2855.html) I'm still
> > not confident that a new keyword `noexcept` would be optimal.
> > Especially when the proposal combines this new keyword with a somewhat
> > enhanced version of the already existing exception specification `throw
> > (...)` to explicitly indicate that a move constructor or a destructor
> > may throw exceptions. And at the same time propose to deprecate
> > exception specifications alltogether.
>
> > I think that it would be better to use a simplified exception
> > specification:
>
> > - `throw()` to indicate that a function does not throw any exception,
> > which is statically checked by the compiler. This would behave as
> > `noexcept` in the N2855 proposal.
> > - `throw(...)` to indicate that a function may throw any exception.
> > Main use for throwing move constructors and throwing destructors.
> > - Any other exception specification would be ill-formed and thus would
> > require a compiler diagnostic.
>
> > Update the new exception specification to behave as `noexcept` in
> > N2855, where needed.
>
> > Unfortunately I don't have a really good idea to implement something
> > similar to the `noexcept` block as described in N2855. Perhaps a `try`
> > block without a `catch` handler, perhaps someone else has a better
> > idea?
>
> > double sqrt(double); // may throw exceptions
>
> > void f(double &x) throw() {
> >    if (x > 0) {
> >      x = sqrt(x); // ill-formed: sqrt(x) might throw an exception!
> >    }
>
> > }
>
> > void g(double &x) throw() {
> >    if (x > 0) {
> >      try { x = sqrt(x); } // okay: if sqrt(x) throws, invokes undefined
> > behavior
> >    }
>
> > }
>
> > Rationale:
> > - Exception specification as of C++98/C++03 are considered broken and
> > discouraged. No need to deprecate, remove it entirely.
> > - `throw()` is already used by a lot of libraries, for example boost,
> > to indicate nothrow functions. With this proposal these functions
> > would behave as `noexcept` with no change.
> > - No new keywords are needed.
>
> > Remove the following:
>
> > exception-specification:
> >    throw ( [type-id-list]opt )
>
> > type-id-list:
> >    type-id
> >    type-id-list , type-id
>
> > Add the following:
>
> > no-throw-exception-specification:
> >    throw ( )
>
> > any-throw-exception-specification:
> >    throw ( ... )
>
> > What do you think?
>
> I'm personally of the opinion that /optional/ fully-checked exception
> specifications are definitely implementable in C++. I don't like the
> addition of the noexcept keyword apart from the existing system. There
> was a discussion where this was discussed in detail, but it's really
> hard to read and I don't have a link. I could probably rewrite it in a
> more exacting manner (and note that because of covariant exception
> specifications as they currently exist, this requires very little
> implementation work above absence or presence of exceptions) if there
> was interest, which there doesn't really seem to be, sadly.

In a similar vein as the OP, but perhaps more developed, I have this
proposal, which I initially described towards the end of this thread:

http://groups.google.com/group/comp.lang.c++.moderated/browse_thread/thread/b5c5b601f07ea2cc/3a13b77b9ff5db5b

Reproduced here is a complete, cleaned up version of what I was
thinking that day.

I think that statically checked exceptions would be exceedingly
useful. For backwards compatibility, we cannot (easily) change old-
style exception specifications to mean something else, so I proposed a
new syntax to accomplish this, let's say only_throws.

The basic idea is that the compiler will compile down object files so
that each function will have associated data:

-1- The list of the list of exceptions which can be raised inside the
function. Call this the "actual throws list". This is the worst case
scenario. It is the union of all types appearing in throws statements
in the function and a placeholder for delayed evaluation for all
function calls. It would not take into account control flow at all.
Even provably dead code contributes to this. However, try catch blocks
silence exceptions, and try catch blocks will be taken into account
when generating this list. Also, the new only_throws specification
(minus auto, if present) is also union-ed to produce this list. Ex:

void foo() only_throws(auto, long)
{   try
   {   throw (int)1;
       throw (short)1;
       bar();
   } catch (int ) { throw (unsigned)1; }
   throw (char)1;
   baz();
}

Function foo would be compiled down with the following data for 1
equal to:
long [union] ((int [union] short [union] throws of bar) [minus] int)
[union] unsigned [union] char [union] throws of baz
= long [union] short [union] (throws of bar [minus] int) [union]
unsigned [union] char [union] throws of baz

-2- A list of exceptions which the user declares as throwable with a
only_throws specification. Also, "auto" may appear in the only_throws
specification. auto means delayed evaluation; it expands to "whatever
the function can throw", the actual throws list as described in 1. A
function without a only_throws specification is equivalent to the
function with a only_throws(auto) specification. ex, the following
same-named function declarations are equivalent.
   void foo();
   void foo() only_throws(auto);

   void bar() only_throws(float, int, auto)
   void bar() only_throws(int, auto, float);

But the second of each pair of the following declarations would be ill-
formed:
   void foo();
   void foo() only_throws(int);

   void bar() only_throws(auto);
   void bar() only_throws(int);

   void baz() only_throws(auto, int);
   void baz() only_throws(auto, int, float);

Eventually, the linker will get the object files to link. It will
construct a call graph from these object files. It will do a bottom up
traversal, starting at the leafs. For each function foo:
- It evaluates actual throws list of function foo, using the evaluated
only_throws specifications for called functions.
- It then asserts that asserts that the actual throws list for
function foo is a subset of the only_throws specification of function
foo.
- It then evaluates the only_throws specification for this function
foo.

Note that cycles in the call graph cannot introduce additional
exception types being thrown, so when encountered, it can take this
into account to break the cycle.

Function pointers would need new treatment as well. Function pointers
would also have an optional only_throws specification. A function
pointer declaration without an only_throws specification is equivalent
to an only_throws(auto) specification, which means that it can throw
any exception type, in spite of whatever knowledge the compiler or
linker could ascertain. (Note that auto in this context means all
types whereas before it was a placeholder for delayed evaluation.
Perhaps a different keyword would be in order here, or we could
continue to pollute its meaning.)

Function pointers with different throws specifications are different
function types, so the following would be ill-formed
   void foo() only_throws(int);
   void bar() only throws(short);
   void baz()
   {   void (*x)() only_throws(int) = & foo; //correct
       void (*y)() only_throws(short) = & bar; //correct
       x = y; //ill formed, incompatible types
   }

Optionally: introduce implicit cast rules to cast from a function
pointer to another function pointer with a superset for its
only_throws specification, provable without any evaluation of auto.
Alternatively, allow such casts and have the compiler / linker assert
the cast is correct with expanded autos, requiring further data in the
object file (a line for each cast going from what to what).

Also, a couple of points made by Bart van Ingen Schenau in the linked
thread above.
- For internal linkage functions, the compiler would have to evaluate
the actual throws list, assert it is a subset of the only_throws
specification, and evaluate the only_throws specifications just like
the linker.
- Linkage from other languages or from implementations before this
proposal was implemented. Bart van Ingen Schenau suggested that C
linkage functions would be implicitly only_throw(), aka no throw. For C
++ object files compiled / linked without this support, they would
implicitly have a only_throws specification of all exception types,
like function pointers with a only_throws(auto). Other problems I
think should be easy enough to deal with.

Rationale for proposal:

For most code, exceptions continue to propagate silently. This is
exception's greatest feature, that you can decouple the normal code
path and the error code path. It is at best tedious to specify a
checked throws exception specification for every function, just like
it is to propagate up error return codes by forwarding them.

However, sometimes you want to be able to give constraints to the
error code path like you can with error return codes, such as at
external interfaces which you're implementing. The checked exception
specification only_throws does that. It gives a compile-time (or link-
time) guarantee that only exceptions of the listed type can be raised
from within that function and leave.

The current system is quite bad and unusable because the current
exception specifications are treated as runtime asserts. When combined
with exceptions which generally happen only rarely and in error cases,
this leads to rarely exercised and hard to exercise code paths, which
makes it quite easy to glance over or fail to test a possible throws
which can hit one of these runtime asserts and take down the process.
The static assert nature of this proposal is far better for developing
robust code while not having the downsides of checked exceptions as
implemented in other languages like Java.

I believe this proposal is perfectly backwards compatible with the old
C++ language definition (apart from the new keyword only_throws) and
implementations, although a new object format would have to be
developed to support this feature, the actual throws list and the
only_throws specification.

This requires linker support, and from my understanding, that is why
template "extern" suffered a horrible death, so this may be more of an
academic proposal at this point, but I would at least like this to get
out in case it ever becomes viable.

As noted above, the general use of this would be to leave most
functions as they are now, without a throws specification. Only at
certain critical junctures, such as external interfaces, would you add
an only_throws specification. Hopefully, QoI would be that the
compiler / linker would fail the compile / link and produce the call
graph between the failed only_throws specification and the actual
throw statement or opaque API function which propagated up to violate
it.

Under this scheme, all exceptions are checked exceptions. It is my
belief and observation that for C++ code, there are very few
exceptions that one would consider "unchecked exceptions". (Of the top
of my head, I can think of only two: bad_alloc and
thread_interrupted.) The argument for unchecked exceptions is
basically the same argument against checked exceptions in general
(varying only in degree): that it is tedious to write out throws
specifications for every single function. With my proposal, the number
of only_throws specifications would be quite small, and as such, I
believe the benefit of having all exceptions be statically checked
against only_throws specifications greatly outweighs the annoyance of
typing out these common exceptions for the small number of only_throw
specifications.


--
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@netlab.cs.rpi.edu]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html                      ]





Author: google@dalvander.com
Date: Tue, 5 May 2009 14:15:28 CST
Raw View
On May 5, 4:21 am, joshuamaur...@gmail.com wrote:
> I think that statically checked exceptions would be
> exceedingly useful.

I'm against checked exceptions as such, but the N2855 proposed a way
to distinct functions that may throw and functions that does not throw
in a rather beautiful way. What I didn't like with N2855 is that it
was introducing a new keyword and was inconsistent in the deprecation
of the existing exception specification.

> For backwards compatibility, we cannot (easily)
> change old-style exception specifications to mean
> something else, so I proposed a new syntax to
> accomplish this, let's say only_throws.

I don't see the need for a new keyword where the existing empty
exception specification `throw()` together with an extended throw-any
exception specification `throw(...)` would behave perfectly without
any new keywords.

Regards,
Anders Dalvander


--
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@netlab.cs.rpi.edu]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html                      ]





Author: joshuamaurice@gmail.com
Date: Tue, 5 May 2009 16:39:13 CST
Raw View
On May 5, 1:15 pm, goo...@dalvander.com wrote:
> On May 5, 4:21 am, joshuamaur...@gmail.com wrote:
>
> > I think that statically checked exceptions would be
> > exceedingly useful.
>
> I'm against checked exceptions as such, but the N2855 proposed a way
> to distinct functions that may throw and functions that does not throw
> in a rather beautiful way. What I didn't like with N2855 is that it
> was introducing a new keyword and was inconsistent in the deprecation
> of the existing exception specification.
>
> > For backwards compatibility, we cannot (easily)
> > change old-style exception specifications to mean
> > something else, so I proposed a new syntax to
> > accomplish this, let's say only_throws.
>
> I don't see the need for a new keyword where the existing empty
> exception specification `throw()` together with an extended throw-any
> exception specification `throw(...)` would behave perfectly without
> any new keywords.

Now, you do not believe there is value to statically checked
exceptions, so given that, yes, there is no need for a new keyword.
However, I disagree. I believe it would be useful to be able to
specify that an external interface can only throw a certain subset of
exceptions and have the compiler and linker verify this for you, ala:

namespace foo
{   class DomainSpecificError { public: std::string getErrorMessage
(); };
     void doOperation() only_throws(std::bad_alloc,
DomainSpecificError);
}

Also, with this proposal, you can avoid manually specifying anything
for the vast majority of functions, coding C++ as normal. Having to
specify a throws specification for every single function is generally
considered the only downside to statically checked exceptions as
implemented in other languages (ignoring implementation issues), which
my proposal nicely avoids.


--
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@netlab.cs.rpi.edu]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html                      ]





Author: Pavel Minaev <int19h@gmail.com>
Date: Wed, 6 May 2009 10:48:45 CST
Raw View
On May 5, 1:15 pm, goo...@dalvander.com wrote:
> I'm against checked exceptions as such, but the N2855 proposed a way
> to distinct functions that may throw and functions that does not throw
> in a rather beautiful way. What I didn't like with N2855 is that it
> was introducing a new keyword and was inconsistent in the deprecation
> of the existing exception specification.
>
> I don't see the need for a new keyword where the existing empty
> exception specification `throw()` together with an extended throw-any
> exception specification `throw(...)` would behave perfectly without
> any new keywords.

throw() already has a well-defined meaning in C++03. Redefining it to
mean an entirely different thing for C++0x - including affecting the
type system - doesn't sound like a good idea from compatibility point
of view.


--
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@netlab.cs.rpi.edu]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html                      ]





Author: google@dalvander.com
Date: Fri, 1 May 2009 13:08:57 CST
Raw View
After reading N2855 - Rvalue References and Exception Safety (http://
www.open-std.org/jtc1/sc22/wg21/docs/papers/2009/n2855.html) I'm still
not confident that a new keyword `noexcept` would be optimal.
Especially when the proposal combines this new keyword with a somewhat
enhanced version of the already existing exception specification `throw
(...)` to explicitly indicate that a move constructor or a destructor
may throw exceptions. And at the same time propose to deprecate
exception specifications alltogether.

I think that it would be better to use a simplified exception
specification:

- `throw()` to indicate that a function does not throw any exception,
which is statically checked by the compiler. This would behave as
`noexcept` in the N2855 proposal.
- `throw(...)` to indicate that a function may throw any exception.
Main use for throwing move constructors and throwing destructors.
- Any other exception specification would be ill-formed and thus would
require a compiler diagnostic.

Update the new exception specification to behave as `noexcept` in
N2855, where needed.

Unfortunately I don't have a really good idea to implement something
similar to the `noexcept` block as described in N2855. Perhaps a `try`
block without a `catch` handler, perhaps someone else has a better
idea?

double sqrt(double); // may throw exceptions

void f(double &x) throw() {
   if (x > 0) {
     x = sqrt(x); // ill-formed: sqrt(x) might throw an exception!
   }
}

void g(double &x) throw() {
   if (x > 0) {
     try { x = sqrt(x); } // okay: if sqrt(x) throws, invokes undefined
behavior
   }
}

Rationale:
- Exception specification as of C++98/C++03 are considered broken and
discouraged. No need to deprecate, remove it entirely.
- `throw()` is already used by a lot of libraries, for example boost,
to indicate nothrow functions. With this proposal these functions
would behave as `noexcept` with no change.
- No new keywords are needed.

Remove the following:

exception-specification:
   throw ( [type-id-list]opt )

type-id-list:
   type-id
   type-id-list , type-id

Add the following:

no-throw-exception-specification:
   throw ( )

any-throw-exception-specification:
   throw ( ... )

What do you think?

--
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@netlab.cs.rpi.edu]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html                      ]





Author: Sean Hunt <rideau3@gmail.com>
Date: Sun, 3 May 2009 10:12:30 CST
Raw View
On May 1, 1:08 pm, goo...@dalvander.com wrote:
> After reading N2855 - Rvalue References and Exception Safety (http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2009/n2855.html) I'm still
> not confident that a new keyword `noexcept` would be optimal.
> Especially when the proposal combines this new keyword with a somewhat
> enhanced version of the already existing exception specification `throw
> (...)` to explicitly indicate that a move constructor or a destructor
> may throw exceptions. And at the same time propose to deprecate
> exception specifications alltogether.
>
> I think that it would be better to use a simplified exception
> specification:
>
> - `throw()` to indicate that a function does not throw any exception,
> which is statically checked by the compiler. This would behave as
> `noexcept` in the N2855 proposal.
> - `throw(...)` to indicate that a function may throw any exception.
> Main use for throwing move constructors and throwing destructors.
> - Any other exception specification would be ill-formed and thus would
> require a compiler diagnostic.
>
> Update the new exception specification to behave as `noexcept` in
> N2855, where needed.
>
> Unfortunately I don't have a really good idea to implement something
> similar to the `noexcept` block as described in N2855. Perhaps a `try`
> block without a `catch` handler, perhaps someone else has a better
> idea?
>
> double sqrt(double); // may throw exceptions
>
> void f(double &x) throw() {
>    if (x > 0) {
>      x = sqrt(x); // ill-formed: sqrt(x) might throw an exception!
>    }
>
> }
>
> void g(double &x) throw() {
>    if (x > 0) {
>      try { x = sqrt(x); } // okay: if sqrt(x) throws, invokes undefined
> behavior
>    }
>
> }
>
> Rationale:
> - Exception specification as of C++98/C++03 are considered broken and
> discouraged. No need to deprecate, remove it entirely.
> - `throw()` is already used by a lot of libraries, for example boost,
> to indicate nothrow functions. With this proposal these functions
> would behave as `noexcept` with no change.
> - No new keywords are needed.
>
> Remove the following:
>
> exception-specification:
>    throw ( [type-id-list]opt )
>
> type-id-list:
>    type-id
>    type-id-list , type-id
>
> Add the following:
>
> no-throw-exception-specification:
>    throw ( )
>
> any-throw-exception-specification:
>    throw ( ... )
>
> What do you think?

I'm personally of the opinion that /optional/ fully-checked exception
specifications are definitely implementable in C++. I don't like the
addition of the noexcept keyword apart from the existing system. There
was a discussion where this was discussed in detail, but it's really
hard to read and I don't have a link. I could probably rewrite it in a
more exacting manner (and note that because of covariant exception
specifications as they currently exist, this requires very little
implementation work above absence or presence of exceptions) if there
was interest, which there doesn't really seem to be, sadly.

-Sean Hunt


--
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@netlab.cs.rpi.edu]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html                      ]