I think I've found a solution to noexcept!

111 views
Skip to first unread message

DeMarcus

unread,
Apr 18, 2011, 10:06:08 AM4/18/11
to

Hi!

I really like the N3248 paper because it discusses noexcept from a very
good perspective.

http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2011/n3248.pdf

There has been so much talk about the technical details around noexcept
but N3248 approaches noexcept from a user angle; it asks how noexcept
will be used in everyday programming in general and unit testing in
particular.

I have been thinking myself what the use case for noexcept would be.
Following is a suggestion on how to use noexcept but I haven't
investigated the technical details, so please halt me when I provide
something infeasible.


First of all I'm for statically checked noexcept (don't halt me yet) for
the plain reason that it should be a tool helping us in the design phase
of an application.

I tried to understand what problems noexcept will cause, i.e. what
started this argue about static/dynamic checking. In both camps we seem
to agree that everything with noexcept is fine until we get a failure.
So what failures do we get? I made a list with some that I could come up
with.

* Failure to meet preconditions, postconditions or invariants.
* Failure to allocate memory.
* Exhausting the stack.
* Segmentation fault.
* Division by zero.
* Failure to find an item in a list.
* File permission denied.
* Network down.
* etc.

Then I divided these problems into four categories.

* Expected failures (failure to find an item in a list, file permission
denied, network down, etc.)
* System failures (memory allocation, exhausting the stack)
* Usage failures (preconditions)
* Design failures (postconditions, invariants, segmentation fault,
division by zero)

If we look at these categories the /system failures/ can never be caught
in compile-time. Neither can /usage failures/ but with means of unit
tests we can prevent quite a lot of them from propagating into our code.
As for /design failures/, we can use the compiler to catch a lot of
errors, like for instance prohibit assigning NULL to a reference, using
const to prevent postcondition failures, and, according to Abrahams'
strong exception safety guarantee, using noexcept to prevent us from
operate on inconsistent objects. The latter can be achieved if we at
compile-time prohibit throwing of /expected failures/ in noexcept functions.

I think everyone agrees statically checking for design flaws is good,
the problem we face is that when we eventually get an error in run-time,
we have different opinions how to deal with them.

The big reason (correct me if I'm wrong) why some people want
dynamically checked noexcept is that so many functions may throw
std::bad_alloc which makes it difficult for us to use statically checked
noexcept. All of us would agree on having statically checked noexcept if
it wasn't for std::bad_alloc. Am I right?

Now let's go back and sort this out by means of our failure categories.

Here's my solution that I strongly believe will make both the static and
dynamic camps happy.

-------------------------------------------------------------
A function is ill-formed if declared with noexcept and calls a
non-noexcept function or throws an exception *except* for three
exception types; std::bad_alloc, std::runtime_error and std::logic_error.

In case std::bad_alloc, std::runtime_error, std::logic_error are thrown,
the handler std::atnoexcept will be called. The default behavior for
std::atnoexcept is to abort (if a new handler is set it will behave
similar to std::set_unexpected, see below). The stack is unwound for
custom handlers.
-------------------------------------------------------------

What have we achieved with this solution?

1. In our semantics contract we promise our users that a noexcept
function will never fail except in case of the three severe non-expected
circumstances:
- System failure "force majeure" when we get std::bad_alloc.
- Usage failure that should result in a std::runtime_error
- Design failure that should result in a std::logic_error.

2. We can still have the sought-after try/catch optimization (no
try/catch-block overhead) since it's the compiler, and not the run-time,
that checks for these severe exceptions.

3. The stack is unwound at an appropriate place so those using the
default std::atnoexcept handler with std::abort will have the stack
untouched for debugging, while others with customized handlers can go on
with a valid stack.

4. We can meet the mentioned unit test document N3248. See below.


Now, let's put this into effect and see how we can use this in practice.

First, for convenience, create three new custom made types of assertions:

// Usage failure assertion.
void precondition( bool expression ) noexcept
{
if( !expression )
throw std::runtime_error();
}

// Design failure assertion.
void invariant( bool expression ) noexcept
{
if( !expression )
throw std::logic_error();
}

// Design failure assertion.
class Postcondition
{
public:
Postcondition( bool expression ) noexcept
: expression_(expression)
{
}

~Postcondition() noexcept
{
if( !expression_ )
throw std::logic_error();
}

private:
bool expression_;
};


Then create our own std::atnoexcept handler to allow the three
exceptions std::bad_alloc, std::runtime_error and std::logic_error to pass.

static void myNothrowHandler() noexcept
{
// Just pass on the errors.
try
{
throw;
}
catch( std::bad_alloc& )
{
throw;
}
catch( std::runtime_error& )
{
throw;
}
catch( std::logic_error& )
{
throw;
}

// std::terminate automatically called if
// we reach here.
}


Now we can use this to create a first pilot case.

void mustNeverThrow( int position ) noexcept
{
precondition( position < 42 );

// Create a vector of size 42.
// May throw std::bad_alloc but that's legal.
std::vector<int> vec( 42 );

// Throws no exception.
vec[position] = -2;

// May throw std::out_of_range, but that's legal
// since it's derived from std::logic_error.
int i = vec.at( position );

invariant( i == -2 );

// For old functions we have to embrace them
// within try/catch(...)
int result;
try
{
result = oldSquareRoot( i );
}
catch(...)
{
// Something is wrong, oldSquareRoot should
// not throw.
throw std::logic_error();
}
invariant( result != -1 );

// We must embrace the throwing function
// connectToInternet() otherwise this
// mustNeverThrow() function will be ill-formed.
try
{
connectToInternet();
}
catch( NetworkDownException& )
{
// We can never prohibit people from swallowing
// exceptions, but at least this empty catch
// block should give a hint that the design is
// bad and that connectToInternet() maybe doesn't
// belong here.
}
}

int main()
{
std::atnoexcept( myNothrowHandler );

// Now we can handle the three failure types within
// our unit tests and/or application. And if we
// prefer an immediate termination or want to debug
// the code with a debugger we just remove or comment
// out our handler above.
try
{
mustNeverThrow( 15 );
}
catch( std::bad_alloc& )
{
std::cout << "System failure" <<
"Arrange more memory and try again";
}
catch( std::runtime_error& )
{
std::cout << "Usage failure" <<
"Email the users of this function";
}
catch( std::logic_error& )
{
std::cout << "Design failure" <<
"Email the programmer of this function";
}

return 0;
}


Do you think this is feasible?
Please comment.


Thanks,
Daniel

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

Thomas Richter

unread,
Apr 18, 2011, 7:48:33 PM4/18/11
to

DeMarcus schrieb:

>
> Now let's go back and sort this out by means of our failure categories.
> Here's my solution that I strongly believe will make both the static and
> dynamic camps happy.
> -------------------------------------------------------------
> A function is ill-formed if declared with noexcept and calls a
> non-noexcept function or throws an exception *except* for three
> exception types; std::bad_alloc, std::runtime_error and std::logic_error.
> In case std::bad_alloc, std::runtime_error, std::logic_error are thrown,
> the handler std::atnoexcept will be called. The default behavior for
> std::atnoexcept is to abort (if a new handler is set it will behave
> similar to std::set_unexpected, see below). The stack is unwound for
> custom handlers.
> -------------------------------------------------------------
> What have we achieved with this solution?

To be honest, this solution doesn't really address the problem noexcept is trying to address. The reason, to my understanding, what noexcept is good for is to provide compilers with an optimization strategy not to generate exception unwinding information for cases where exception unwinding is not needed. What you are proposing here implies that, actually, such an optimization cannot be done anymore because, even though "noexcept" is there, the compiler *can no longer* assume that no exception will ever leave the function. But this is exactly what noexcept is good for.

Essentially, to implement what you propose above, the compiler would need to generate a "hidden" try-catch block that catches the three mentioned exceptions, triggers the exception handler, and forwards all other exceptions to a handler. But this is actually the worst possible solution because it makes things *even slower* and creates *more* overhead instead of less, thus defeats the purpose of noexcept, and
it doesn't improve the situation over "throw()".

Greetings,
Thomas

Martin B.

unread,
Apr 18, 2011, 7:48:53 PM4/18/11
to

On 18.04.2011 16:06, DeMarcus wrote:
>
> Hi!
>
> I really like the N3248 paper because it discusses noexcept from a very
> good perspective.
>
> http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2011/n3248.pdf
>
> There has been so much talk about the technical details around noexcept
> but N3248 approaches noexcept from a user angle; it asks how noexcept
> will be used in everyday programming in general and unit testing in
> particular.
>
> I have been thinking myself what the use case for noexcept would be.
> Following is a suggestion on how to use noexcept but I haven't
> investigated the technical details, so please halt me when I provide
> something infeasible.
>
>
> First of all I'm for statically checked noexcept (don't halt me yet) for
> the plain reason that it should be a tool helping us in the design phase
> of an application.
>
> I tried to understand what problems noexcept will cause, i.e. what
> started this argue about static/dynamic checking. ...

Sorry, I only skimmed parts of the post, but I would highlight one paragraph:

> The big reason (correct me if I'm wrong) why some people want
> dynamically checked noexcept is that so many functions may throw
> std::bad_alloc which makes it difficult for us to use statically checked
> noexcept. All of us would agree on having statically checked noexcept if
> it wasn't for std::bad_alloc. Am I right?
>

I sincerely hope that dynamic runtime exceptions were *not* the reason to not have noexcept statically enforced. They certainly are *not* a good argument to me.

I asked [elsewhere][1] that I would find it highly interesting why the committee scraped the `noexcept` block and I really hope it got nothing to do with bad_alloc:

| * Rationale: Why was the noexcept-block,
| as proposed in [N2855][5], scraped?
| This block would have made it trivial
| for compilers to statically "enforce"
| `noexcept`, just as they "enforce"
| `const` at the moment.

[1] : http://groups.google.com/group/comp.lang.c++.moderated/browse_thread/thread/fe0aff369259d1c7

cheers,
Martin

--
Stop Software Patents
http://petition.stopsoftwarepatents.eu/841006602158/
http://www.ffii.org/

Zeljko Vrba

unread,
Apr 19, 2011, 4:39:11 PM4/19/11
to

On 2011-04-18, Thomas Richter <th...@math.tu-berlin.de> wrote:
>
> To be honest, this solution doesn't really address the problem noexcept is
> trying to address. The reason, to my understanding, what noexcept is good for
> is to provide compilers with an optimization strategy not to generate
> exception unwinding information for cases where exception unwinding is not
> needed. What you are proposing here implies that, actually, such an
>
But what was the need for such optimization? I had the impression that all
modern compilers already generated zero-overhead code in the case no exception
was thrown.

DeMarcus

unread,
Apr 19, 2011, 4:39:22 PM4/19/11
to

On 04/19/2011 01:48 AM, Thomas Richter wrote:
>
> DeMarcus schrieb:
>>
>> Now let's go back and sort this out by means of our failure categories.
>> Here's my solution that I strongly believe will make both the static and
>> dynamic camps happy.
>> -------------------------------------------------------------
>> A function is ill-formed if declared with noexcept and calls a
>> non-noexcept function or throws an exception *except* for three
>> exception types; std::bad_alloc, std::runtime_error and std::logic_error.
>> In case std::bad_alloc, std::runtime_error, std::logic_error are thrown,
>> the handler std::atnoexcept will be called. The default behavior for
>> std::atnoexcept is to abort (if a new handler is set it will behave
>> similar to std::set_unexpected, see below). The stack is unwound for
>> custom handlers.
>> -------------------------------------------------------------
>> What have we achieved with this solution?
>
> To be honest, this solution doesn't really address the problem noexcept
> is trying to address. The reason, to my understanding, what noexcept is
> good for is to provide compilers with an optimization strategy not to
> generate exception unwinding information for cases where exception
> unwinding is not needed.

To my understanding it was Douglas Gregor and David Abrahams that proposed noexecpt as a tool to provide strong exception safety guarantee.

http://www.open-std.org/JTC1/SC22/WG21/docs/papers/2009/n2855.html

Then some people got so keen on the optimization opportunities so they just tossed out everything that had to do with exception safety guarantees but kept the keyword noexcept.

> What you are proposing here implies that,
> actually, such an optimization cannot be done anymore because, even
> though "noexcept" is there, the compiler *can no longer* assume that no
> exception will ever leave the function. But this is exactly what
> noexcept is good for.
>
> Essentially, to implement what you propose above, the compiler would
> need to generate a "hidden" try-catch block that catches the three
> mentioned exceptions, triggers the exception handler, and forwards all
> other exceptions to a handler. But this is actually the worst possible
> solution because it makes things *even slower* and creates *more*
> overhead instead of less, thus defeats the purpose of noexcept, and
> it doesn't improve the situation over "throw()".
>

The try-catch block doesn't need to look for the three exceptions, it just has to look for /any/ exception since it can only be one of the three since all others have been caught in compile-time.

Those three exceptions are forwarded to a handler and we don't need optimized code there since we're dealing with such a heavy failure that should soon terminate anyway.

SG

unread,
Apr 19, 2011, 4:43:02 PM4/19/11
to

On 19 Apr., 01:48, Martin B. wrote:
> [...]

> I sincerely hope that dynamic runtime exceptions were *not* the
> reason to not have noexcept statically enforced. They certainly
> are *not* a good argument to me.
>
> I asked [elsewhere][1] that I would find it highly interesting
> why the committee scraped the `noexcept` block and I really
> hope it got nothing to do with bad_alloc:
>
> | * Rationale: Why was the noexcept-block,
> | as proposed in [N2855][5], scraped?
> | This block would have made it trivial
> | for compilers to statically "enforce"
> | `noexcept`, just as they "enforce"
> | `const` at the moment.
>
> [1] :http://groups.google.com/group/comp.lang.c++.moderated/browse_thread/...

Well, I can't speak for everybody else, but there is one thing that
bugs me about the noexcept-block proposal: It's that it forces us to
create a new block. This has effects on how and where you define,
initialize and assign variables. Example:

double sqrt(double);

double foo(double x) noexcept
{
if (x<=0) return 0;
double t;
noexcept { t=sqrt(x); }
return 3*t+2;
}

On one hand, it's desirable to use very small noexcept-blocks in order
to benefit from static checking. On the other hand, small blocks may
force us to replace initialization with assignment (see for 't'). To
be honest, this is not a very satisfactory solution, in my opinion.

my 2 cents,
SG


--

Andrew

unread,
Apr 19, 2011, 4:41:24 PM4/19/11
to

On Apr 18, 11:48 pm, "Martin B." <0xCDCDC...@gmx.at> wrote:
>> The big reason (correct me if I'm wrong) why some people want
>> dynamically checked noexcept is that so many functions may throw
>> std::bad_alloc which makes it difficult for us to use statically checked
>> noexcept. All of us would agree on having statically checked noexcept if
>> it wasn't for std::bad_alloc. Am I right?

No.

> I sincerely hope that dynamic runtime exceptions were *not* the reason to not have noexcept statically enforced. They certainly are *not* a good argument to me.

Indeed. AFAIK that was not the reason.

> I asked [elsewhere][1] that I would find it highly interesting why the committee scraped the `noexcept` block and I really hope it got nothing to do with bad_alloc:

Several years ago I was really really keen on full static exception
checking and wrote a proposal about it. Download it from
http://www.andrewpetermarlow.co.uk/goodies/proposal.pdf. In there you
will see that there are all sorts of other difficulties in static
checking. For example, what about arithmetic exceptions such as divide
by zero? With the Microsoft compiler and SEH (Structured Exception
Handling) these get converted into C++ exceptions. My proposal has
other examples. Eventually, because of these issues (and others) I
abandoned the proposal. Then noexcept appeared and now people are
thinking about these things all over again....

Regards,

Andrew Marlow


--

Goran

unread,
Apr 19, 2011, 4:40:33 PM4/19/11
to

On Apr 19, 1:48 am, Thomas Richter <t...@math.tu-berlin.de> wrote:
> DeMarcus schrieb:
>
>
>
>> Now let's go back and sort this out by means of our failure categories.
>> Here's my solution that I strongly believe will make both the static and
>> dynamic camps happy.
>> -------------------------------------------------------------
>> A function is ill-formed if declared with noexcept and calls a
>> non-noexcept function or throws an exception *except* for three
>> exception types; std::bad_alloc, std::runtime_error and std::logic_error.
>> In case std::bad_alloc, std::runtime_error, std::logic_error are thrown,
>> the handler std::atnoexcept will be called. The default behavior for
>> std::atnoexcept is to abort (if a new handler is set it will behave
>> similar to std::set_unexpected, see below). The stack is unwound for
>> custom handlers.
>> -------------------------------------------------------------
>> What have we achieved with this solution?
>
> To be honest, this solution doesn't really address the problem
> noexcept is trying to address. The reason, to my understanding,
> what noexcept is good for is to provide compilers with an
> optimization strategy not to generate exception unwinding
> information for cases where exception unwinding is not needed.

+1 from me for that. In the absence of statically-checked nothrow
(current situation, I believe), I'd be happy if even the requirement
to call std::terminate was lifted, too if that could make called
nothrow function smaller, too (I think it should, might be wrong).

I think, we want both fullspeedflybaby and statically-checked nothrow.
(Having the latter easily implies the former). But latter isn't
feasible. So let's go for the former. If, in future, we do get the
latter, all the better, and no-one is harmed (bar code that does
nothrow, but __can__ throw, and that could be rightfully corrected
then).

Goran.

DeMarcus

unread,
Apr 20, 2011, 3:26:41 PM4/20/11
to

It sounds good but I'm not too convinced that everything will be happy
days like that.

People spend man-years to track down bugs. What we need is more means to
to make robust code rather than an optimization "flag" that in current
form may cause even more bugs due to total absence of usage guidelines.

Francis Glassborow

unread,
Apr 20, 2011, 3:24:08 PM4/20/11
to

On 19/04/2011 21:39, Zeljko Vrba wrote:
> On 2011-04-18, Thomas Richter<th...@math.tu-berlin.de> wrote:
>>
>> To be honest, this solution doesn't really address the problem noexcept
is
>> trying to address. The reason, to my understanding, what noexcept is good
for
>> is to provide compilers with an optimization strategy not to generate
>> exception unwinding information for cases where exception unwinding is
not
>> needed. What you are proposing here implies that, actually, such an
>>
> But what was the need for such optimization? I had the impression that
all
> modern compilers already generated zero-overhead code in the case no
exception
> was thrown.
>

I think you have missed the point. If no exception can be thrown there
are further possible optimisations (or so I have been told).

Thomas Richter

unread,
Apr 20, 2011, 6:56:46 PM4/20/11
to

Am 19.04.2011 22:41, schrieb Andrew:

>> I asked [elsewhere][1] that I would find it highly interesting why the committee scraped the `noexcept` block and I really hope it got nothing to do with bad_alloc:
>
> Several years ago I was really really keen on full static exception
> checking and wrote a proposal about it. Download it from
> http://www.andrewpetermarlow.co.uk/goodies/proposal.pdf. In there you
> will see that there are all sorts of other difficulties in static
> checking. For example, what about arithmetic exceptions such as divide
> by zero? With the Microsoft compiler and SEH (Structured Exception
> Handling) these get converted into C++ exceptions.

Why should a proposal be stopped on the basis of knowing a compiler that does not implement the standard correctly? It is not a good example to begin with.

Greetings,
Thomas

restor

unread,
Apr 20, 2011, 6:58:16 PM4/20/11
to

> Well, I can't speak for everybody else, but there is one thing that
> bugs me about the noexcept-block proposal: It's that it forces us to
> create a new block. This has effects on how and where you define,
> initialize and assign variables. Example:
>
> double sqrt(double);
>
> double foo(double x) noexcept
> {
> if (x<=0) return 0;
> double t;
> noexcept { t=sqrt(x); }
> return 3*t+2;
> }
>
> On one hand, it's desirable to use very small noexcept-blocks in order
> to benefit from static checking. On the other hand, small blocks may
> force us to replace initialization with assignment (see for 't'). To
> be honest, this is not a very satisfactory solution, in my opinion.

I find something else that bugs me in the noexcept blocks. It is more
about the keyword than about the block itself. noexcept would just
mean two opposite things: at function level it says "warn me if I
might throw an exception", at block level it says "do not warn me if I
might throw an exception". How do you explain that to novices? C++ has
already a bad reputation for overloading its keywords (I know it is
necessary for compatibility), and the above block would not help the
situation.

Regards,
&rzej

DeMarcus

unread,
Apr 21, 2011, 7:38:58 AM4/21/11
to

On 2011-04-21 00:58, restor wrote:
>
>> Well, I can't speak for everybody else, but there is one thing that
>> bugs me about the noexcept-block proposal: It's that it forces us to
>> create a new block. This has effects on how and where you define,
>> initialize and assign variables. Example:
>>
>> double sqrt(double);
>>
>> double foo(double x) noexcept
>> {
>> if (x<=0) return 0;
>> double t;
>> noexcept { t=sqrt(x); }
>> return 3*t+2;
>> }
>>
>> On one hand, it's desirable to use very small noexcept-blocks in order
>> to benefit from static checking. On the other hand, small blocks may
>> force us to replace initialization with assignment (see for 't'). To
>> be honest, this is not a very satisfactory solution, in my opinion.
>
> I find something else that bugs me in the noexcept blocks. It is more
> about the keyword than about the block itself. noexcept would just
> mean two opposite things: at function level it says "warn me if I
> might throw an exception", at block level it says "do not warn me if I
> might throw an exception". How do you explain that to novices? C++ has
> already a bad reputation for overloading its keywords (I know it is
> necessary for compatibility), and the above block would not help the
> situation.
>

Very good point!

I guess, if we would have statically checked noexcept, the noexcept
block would just have to be replaced with

try
{
t=sqrt(x);
}
catch(...)
{
assert(false && "Check the implementation of sqrt");
}

It's cumbersome and it doesn't solve SG:s important issue, but it will
be correct.

DeMarcus

unread,
Apr 21, 2011, 7:38:45 AM4/21/11
to

> Several years ago I was really really keen on full static exception
> checking and wrote a proposal about it. Download it from
> http://www.andrewpetermarlow.co.uk/goodies/proposal.pdf. In there you
> will see that there are all sorts of other difficulties in static
> checking. For example, what about arithmetic exceptions such as divide
> by zero? With the Microsoft compiler and SEH (Structured Exception
> Handling) these get converted into C++ exceptions. My proposal has
> other examples. Eventually, because of these issues (and others) I
> abandoned the proposal. Then noexcept appeared and now people are
> thinking about these things all over again....
>

I read your paper and I have a couple of comments and a couple of
questions. (Note, below we only consider the strong exception safety
part of noexcept, not the optimization feature)

My comments are:

---
The main difficulties with having static checking of /thrown/ exceptions
is that it may not fit everyone's failure handling strategies.
Especially as you say in page 2; "It is not a good idea to try to make
every function a fire-wall". For some developers it could be good but
for many developers it becomes clumsy and may lead to exception
swallowing bugs.

However, with noexcept we are now talking about /not/ throwing
exceptions and the only places where it becomes clumsy are places where
the design is not proper and should be corrected. Those who find that
statically checked noexcept is breaking their code are not forced to use
it, but when they finally decide to try it they will realize that their
design becomes accurate no matter what failure handling strategy they're
using.

The best example I've seen so far against static checking is the struct
XY example at page 4, but that can also be remedied by the following facts:

1. You just can't make a constructor noexcept if any of your members has
a non-noexcept constructor or destructor. It's a limitation, yes, but I
get the feeling that this is a rare case. Even if you somehow in the
constructor of XY could catch the exceptions from any of its members,
the XY object would probably not be very valid afterward anyway.

2. As for templates they should be exception neutral (deduced) which
means that if any member has a non-noexcept constructor or destructor,
the whole template cannot have noexcept constructor. Otherwise it has a
noexcept constructor.

---
You give an example of division-by-zero as problematic (page 4) since
some compilers convert it to an exception. This would not be a problem
in my solution since such failure should be treated as a
std::logic_error which is allowed to pass. You can even add your own
signal handler to catch segmentation fault and convert it to a
std::logic_error and it would be treated appropriately.

---

My questions are:

---
Have you encountered more examples against statically checked exceptions
apart from the ones in your paper?

---
At page 6 you clearly declare that std::runtime_error should not pass
through the static checking. In my solution (the first post in this
thread) I see it necessary for std::runtime_error to pass the static
checking since that would help noexcept focus on the strong exception
safety guarantee.

Why were you against letting std::runtime_error pass, and what do you
think about my failure handling philosophy?

---


Thanks,
Daniel

Seungbeom Kim

unread,
Apr 22, 2011, 11:03:09 PM4/22/11
to

On 2011-04-20 15:58, restor wrote:
>
> I find something else that bugs me in the noexcept blocks. It is more
> about the keyword than about the block itself. noexcept would just
> mean two opposite things: at function level it says "warn me if I
> might throw an exception", at block level it says "do not warn me if I
> might throw an exception". How do you explain that to novices? C++ has
> already a bad reputation for overloading its keywords (I know it is
> necessary for compatibility), and the above block would not help the
> situation.

It's not that I like the situation, but I could probably explain it
this way: noexcept means "*ensure* no exception occurs" at function level,
and "*assume* no exception occurs" at block level.

I begin to understand recently that noexcept is not just about exceptions,
but about failures in general, which implies that it should really have
been named "nofail" or something like that.

--
Seungbeom Kim

Thomas Richter

unread,
Apr 27, 2011, 9:15:47 PM4/27/11
to

Am 23.04.2011 05:03, schrieb Seungbeom Kim:

> I begin to understand recently that noexcept is not just about exceptions,
> but about failures in general, which implies that it should really have
> been named "nofail" or something like that.

Is it? I don't read this in the specs. You can certainly use it as such, in your code, but given that it is not statically checked, how much does it help?

What is illegal with the following:

/*
** Returns false in case of failure:
*/
bool performAction(...) noexcept;

I don't think the language has anything to say about this.

Greetings,
Thomas


--

DeMarcus

unread,
Apr 28, 2011, 8:59:40 PM4/28/11
to

On 2011-04-28 03:15, Thomas Richter wrote:
>
> Am 23.04.2011 05:03, schrieb Seungbeom Kim:
>
>> I begin to understand recently that noexcept is not just about
>> exceptions,
>> but about failures in general, which implies that it should really have
>> been named "nofail" or something like that.
>
> Is it? I don't read this in the specs. You can certainly use it as such,
> in your code, but given that it is not statically checked, how much does
> it help?
>

In the beginning noexcept was proposed by David Abrahams as a tool to ensure the strong exception safety guarantee, but in the latest draft it has nothing to do with that, no.

> What is illegal with the following:
>
> /*
> ** Returns false in case of failure:
> */
> bool performAction(...) noexcept;
>
> I don't think the language has anything to say about this.
>

With the latest draft focusing on some optimization, this code is perfectly legal. If you want to use noexcept to guarantee strong exception safety then I wouldn't recommend to do the above.

By the way, I still don't understand what optimization we get from using noexcept. Does anyone know how it works under the hood?

dietma...@gmail.com

unread,
Apr 29, 2011, 7:12:43 PM4/29/11
to
On Apr 29, 1:59 am, DeMarcus <use_my_alias_h...@hotmail.com> wrote:
> By the way, I still don't understand what optimization we get from using noexcept. Does anyone know how it works under the hood?

My understanding is that noexcept on its own actually doesn't make
the code any faster! All this does it to arrange for std::terminate()
to be called if an exception escapes a noexcept function and to
make it detectable that an expression using a particular function
will never throw. The former is rather a pessimization and the later
has no impact on the code - unless, of course, the information about
operations not throwing is put to good use!

The main example of using the knowledge whether an expression might
throw or not is increasing the capacity of a std::vector<T>: if
during this operation an exception occurs, the original vector is
supposed to stay unchanged, i.e. conceptually increasing the
capacity is done in multiple steps:

1. allocate memory for the new capacity
2. copy the elements from the original memory to the new location
3. make the new sequence the actual sequence
4. destroy the objects in the original sequence
5. release the original memory

However, copying/destroying objects is supposedly more expensive
than just moving the original objects into the new location, i.e.
something along the linese of:

1. allocate memory for the new capacity
2. move the elements from the original memory to the new location
3. make the new sequence the actual sequence
4. nothing to do here
5. release the original memory

However, without the ability to check if moving objects won't throw
(and the default behaviorur for moving move-unaware objects is to
just copy them which typically can throw at least std::bad_alloc),
the move approach cannot possibly be taken advantage of: if moving
of any but the first objects from the orginal to the new sequence
threw an exception, we have to assume that at least the first object
in the original sequence has to be restored. However, since moving
from the original to the new sequence threw it seems quite possible
that moving from the new sequence to the original may very well
throw as well. That is, if moving may throw we may not be able to
restore the original sequence.

Using noexcept expression like in the code below should enable
choosing (at compile time) whether the object can be moved or have
to be copied (this code just demonstrates the choice between moving
and copying; real code would need to add a few more considerations;
also, I currently don't have a compiler which accepts this code to
verify that it actually does what I think it does...):

template <typename T>
auto transfer(T& s, void* t)
-> typename std::enable_if<noexcept(new(t) T(std::move(s)))>::type
{
std::cout << "moving\n";
new(t) T(std::move(s));
}

template <typename T>
auto transfer(T& s, void* t)
-> typename std::enable_if<!noexcept(new(t)
T(std::move(s)))>::type
{
std::cout << "copying\n";
new(target) T(s);

SG

unread,
Apr 29, 2011, 7:39:47 PM4/29/11
to
On 29 Apr., 02:59, DeMarcus wrote:
>
> In the beginning noexcept was proposed by David Abrahams as a
> tool to ensure the strong exception safety guarantee, but in
> the latest draft it has nothing to do with that, no.

It still does. His example of a growing vector uses
std::move_if_noexcept for conditionally moving elements from a smaller
memory block to a larger one. This way, he can make the strong
exception guarantee. std::move_if_noexcept relies on exception
specifications and type traits. So, yes, noexcept (and related things)
still allow you to make the strong guarantee while exploiting non-
throwing move ctors.

> By the way, I still don't understand what optimization we get from
> using noexcept. Does anyone know how it works under the hood?

noexcept(true) differs from throw() in that stack unwinding is not
required. This allows compilers to omit some exception handling code
in functions that don't invoke any non-noexcept(true) functions.
(That's my current understanding and not necessarily correct). I think
this has been said else-thread already.

SG

Martin B.

unread,
Apr 29, 2011, 7:38:37 PM4/29/11
to
On 29.04.2011 02:59, DeMarcus wrote:
>
> On 2011-04-28 03:15, Thomas Richter wrote:
>>
>> Am 23.04.2011 05:03, schrieb Seungbeom Kim:
>>
>>> I begin to understand recently that noexcept is not just about
>>> exceptions,
>>> but about failures in general, which implies that it should really have
>>> been named "nofail" or something like that.
>>
>> Is it? I don't read this in the specs. You can certainly use it as such,
>> in your code, but given that it is not statically checked, how much does
>> it help?
>>
>
> In the beginning noexcept was proposed by David Abrahams as a tool to
> ensure the strong exception safety guarantee, but in the latest draft it
> has nothing to do with that, no.
>

I assume that with "in the beginning" you are referring to paper [N2855
- Rvalue References and Exception Safety] where Douglas Gregor and David
Abrahams proposed: ...

The noexcept Specifier

We propose the addition of a new declaration specifier,
noexcept, that indicates that the function it applies to
does not throw any exceptions.

...

The noexcept block states that no exceptions will be thrown by
the code within its compound statement. An exception that
escapes from a noexcept block results in undefined behavior.

It later was used in the paper [N3050 - Allowing Move Constructors to
Throw] by Abrahams, Sharoni, Gregor where the noexcept expression was
used to allow for std::move_if_noexcept.

*I fail to see* how their original proposal was semantically
significantly different from what we got in the standard now. (But note
that I did not read the papers very thoroughly, so I may have missed
something.)


>> What is illegal with the following:
>>
>> /*
>> ** Returns false in case of failure:
>> */
>> bool performAction(...) noexcept;
>>
>> I don't think the language has anything to say about this.
>>
>
> With the latest draft focusing on some optimization, this code is
> perfectly legal. If you want to use noexcept to guarantee strong
> exception safety then I wouldn't recommend to do the above.
>

I am not sure about "focusing on optimization".

As David Svoboda puts it in [N3166 - Destructors default to noexcept]
wrt. to destructors and noexcept:

Having destructors default to noexcept
improves the overall security of a program.
... then the destructor's throw causes
immediate program termination, whether or not
it is being executed during exceptional ...
Thus this change serves to increase the abnormal
termination rate of programs with throwing
destructors ... This will result in clearer code
that terminates less often.

While I'm not sure I 100% agree with the above, the fact that noexcept
violations terminate() the program can apparently be seen as a positive.

*If* noexcept violation would cause UB, the committee could be blamed
for focusing on performance, but since it's specified to call
terminate(), I would say it actually helps enforce correct program
behaviour.


> By the way, I still don't understand what optimization we get from using
> noexcept. Does anyone know how it works under the hood?
>

There are two different points:

* It allows for move_if_noexcept, thereby enabling library code to
choose the most efficient solution.
* It doesn't require stack unwinding, which *may* allow compilers to
skip generating the code needed for stack unwinding.

cheers,
Martin

Joshua Maurice

unread,
Apr 29, 2011, 8:19:42 PM4/29/11
to

On Apr 19, 1:39 pm, Zeljko Vrba <mordor.nos...@fly.srk.fer.hr> wrote:

> On 2011-04-18, Thomas Richter <t...@math.tu-berlin.de> wrote:
>
>> To be honest, this solution doesn't really address the problem noexcept is
>> trying to address. The reason, to my understanding, what noexcept is good for
>> is to provide compilers with an optimization strategy not to generate
>> exception unwinding information for cases where exception unwinding is not
>> needed. What you are proposing here implies that, actually, such an
>
> But what was the need for such optimization? I had the impression that all
> modern compilers already generated zero-overhead code in the case no exception
> was thrown.

Sadly, not the case. As far as I can tell from my limited testing,
half of the available compilers for basic desktop OSs, unix-like and
windows, do not implement exceptions with zero overhead.

Reply all
Reply to author
Forward
0 new messages