[Development] Proposal: adding Q_DECL_NOEXCEPT to many methods

Marc Mutz marc.mutz at kdab.com
Fri Aug 3 00:25:04 CEST 2012


Hi Thiago,

On Thursday August 2 2012, Thiago Macieira wrote:
> The benefits are:
>  - callers do not need to emit exception handlers around such functions
>  - the compiler may assume that no exception unexpectedly happens inside
> that function body, foregoing exception handlers inside it as well.

AFAIU, the compiler needs to make sure that std::terminate is called if an 
unexpected exception is thrown, which would require the equivalent of a 
function-try-catch block where the catch block calls std::terminate().

Do you have a reference?

> The first behaviour is present with a C++03's empty exception specification
> (i.e., throw() in the function declaration),

AFAIU, that's only true for MSVC, and counter to what C++03 actually mandated.

> but the second behaviour is 
> new in C++11. In the previous standard, the compiler was forced to emit
> exception code for the case when exceptions did happen even when they
> shouldn't. For that reason, the C++03 exception specification is
> deprecated.

Ok, here I'm with you again.

Btw, I've done some very rough tests:

What I set out to check was whether calling non-noexcept functions from 
noexcept ones incurs a penalty for the std::terminate call that is required 
if a noexcept function would leak an exception (it can't, it must invoke 
std::terminate() instead, and the GCC compilate works as expected in that 
respect, when I make foo() below throw) vs. if the compiler can prove that 
all operations are noexcept.

What I found was that a declaration
   int foo() noexcept;
vs.
   int foo();
doesn't influence the assembly at all (expected, the exception must be 
swallowed inside foo(), not, as with throw() by the caller of foo()).

But
   int foo();
   int bar() noexcept { const int f = foo(); return 2*f;}
seems to introduce a new entry in the gcc_except_table (that's expected, too. 
bar() needs to be prepared for foo() to throw and call std::terminate() in 
response) whereas
   int foo() noexcept;
   int bar() noexcept { const int f = foo(); return 2*f; }
does not (also expected, as the compile can now prove that the body of bar() 
can't throw; the responsibility of preventing exceptions from exiting foo() 
is now with foo()'s implementation).

The function itself is unchanged, though. I don't know enough compiler 
internals to properly interpret this result :)

In a similar test,
   int foo();
   int bar() noexcept { const int f = foo(); return 2*f; }
also adds an entry to the exception table, compared to
   int foo();
   int bar() { const int f = foo(); return 2*f; }
which doesn't. What's more interesting is that I get with -m64 -O2 and no -g 
an .o size difference of 1360 bytes for the second vs. 1576 for the first 
variant. This size difference is much bigger than I would have expected from 
the .s-file diff. For comparision, the size for both bar() and foo() noexcept 
is also 1360.

If there really is a 200 byte overhead per nonexcept function calling 
non-noexcept functions, we'd need to avoid marking functions noexcept that, 
say, Q_ASSERT(), e.g., even though we could probably live with the 
std::terminate that's called if Q_ASSERT() throws (at that point, it's clear 
that the assertion failed, if not from the resulting backtrace) :)

Even if it isn't a full 200 bytes overhead, we should make qt_assert() 
noexcept before putting it on, say, QMutex::lock() which calls it.

Thanks,
Marc

-- 
Marc Mutz <marc.mutz at kdab.com> | Senior Software Engineer
KDAB (Deutschland) GmbH & Co.KG, a KDAB Group Company
www.kdab.com || Germany +49-30-521325470 || Sweden (HQ) +46-563-540090
KDAB - Qt Experts - Platform-Independent Software Solutions



More information about the Development mailing list