Discussion:
Exception specifications on functions useless?
(too old to reply)
Lubos Lunak
2012-03-20 21:50:02 UTC
Permalink
Hello,

I've just found out that exception specification on functions in LO have been
just pretty comments, for about 10 years.

Just to make sure what I'm talking about, it's the throw() in e.g. this

void foo( int a, void* b ) throw( css::uno::RuntimeException );

which says what exceptions a function can throw.

I found out by compiling LO with Clang, running it and having it crash where
gcc-compiler version had no problem. Stephan Bergmann and repo history
revealed that in 2001 -fno-enforce-eh-specs was added to gcc's flags, turning
off code generated that ensures these specifications are actually followed
(and MSVC has reportedly never cared). Clang does not have the option. I've
already actually fixed the problem (in
vbahelper/inc/vbahelper/vbaccesshelper.hxx) and it appears the specifications
somehow magically haven't rotten, as now LO runs fine for me (as far as I can
say, I can't test everything).

That however brings up the question of what the specification are for in the
codebase. The 'Deprecated Exception Specifications, Added noexcept' part of
http://herbsutter.com/2010/03/13/trip-report-march-2010-iso-c-standards-meeting/
is quite interesting read, the summary is:

- the specifications enforce that disallowed exceptions are not thrown at
runtime, adding code to each such function to check it, thus actually making
the performance worse

- boost, and pretty much everybody else it seems, do not consider it worth the
hassle of specifying what a function may or may not throw, except for
flagging a non-inline method as not throwing any exception at all as a means
of optimization for places where such functions are called

- C++11 deprecates it and instead introduces a noexcept keyword which forbids
the function to throw anything

I'm not strongly biased either way, but what we have right now are really
just pretty comments on functions. I think we should either say that we use
the specifications, in which case -fno-enforce-sh-specs should not be used at
least in debug builds, or we can say we follow the C++11/Boost/etc. trend and
not use them, in which case we can have one macro for portable way of saying
noexcept and have an EasyHack for removing the other specifications. BTW, it
should be also noted that SAL_THROW() expands to nothing with gcc.

Opinions on this?
--
Lubos Lunak
***@suse.cz
Tor Lillqvist
2012-03-21 07:40:32 UTC
Permalink
 Opinions on this?
I am in favour of removing the throw specifications. Especially after
reading your link (and what it links to, like
http://www.gotw.ca/publications/mill22.htm ).

I think a large part of our hackers, certainly of new ones, might have
more experience of Java than C++. They might think that C++ is more
like Java, and that the throw clauses are crucial to be exactly right
(for some value of "right"), and then waste time. (Or am I completely
confuse?)

And then there are the hackers (like me) who don't bother to wonder
about it, and pay no attention to whether some function claims to
potentially throw something or not, and just copy boilerplate as they
think necessary when applicable. For the benefit of both classes of
hackers it would be cleaner to just remove the throw specifications.

--tml
Stephan Bergmann
2012-03-21 09:15:46 UTC
Permalink
Post by Tor Lillqvist
I think a large part of our hackers, certainly of new ones, might have
more experience of Java than C++. They might think that C++ is more
like Java, and that the throw clauses are crucial to be exactly right
(for some value of "right"), and then waste time. (Or am I completely
confuse?)
Somewhat confused, I'd say. At least if we would utilize
standards-compliant compiler environments, exception specifications need
to be exactly right, same as in Java. (Though without Java's feature of
having this statically checked by the compiler.)
Post by Tor Lillqvist
And then there are the hackers (like me) who don't bother to wonder
about it, and pay no attention to whether some function claims to
potentially throw something or not, and just copy boilerplate as they
think necessary when applicable.
This attitude scares me, btw.

Stephan
Stephan Bergmann
2012-03-21 09:04:23 UTC
Permalink
Post by Lubos Lunak
I've just found out that exception specification on functions in LO have been
just pretty comments, for about 10 years.
[...]
Post by Lubos Lunak
I found out by compiling LO with Clang, running it and having it crash where
gcc-compiler version had no problem. Stephan Bergmann and repo history
revealed that in 2001 -fno-enforce-eh-specs was added to gcc's flags, turning
off code generated that ensures these specifications are actually followed
(and MSVC has reportedly never cared). Clang does not have the option. I've
already actually fixed the problem (in
vbahelper/inc/vbahelper/vbaccesshelper.hxx) and it appears the specifications
somehow magically haven't rotten, as now LO runs fine for me (as far as I can
say, I can't test everything).
IIRC, Sun Studio enforced std::unexpected semantics until the last day
we actively used it, so effectively the code had only about a one-year
chance to rot after all.
Post by Lubos Lunak
I'm not strongly biased either way, but what we have right now are really
just pretty comments on functions. I think we should either say that we use
the specifications, in which case -fno-enforce-sh-specs should not be used at
least in debug builds, or we can say we follow the C++11/Boost/etc. trend and
not use them, in which case we can have one macro for portable way of saying
noexcept and have an EasyHack for removing the other specifications. BTW, it
should be also noted that SAL_THROW() expands to nothing with gcc.
I'm undecided about the state of C++ exceptions.

On the one hand, the design of C++ exceptions has severe limitations:

* Exceptions are not properly integrated with the type system. A
function type definition cannot have an exception specification.
Function templates cannot abstract over exception specifications.

* The old std::unexpected design easily pessimizes implementations of
functions with exception specifications. (The new noexcept design
mitigates that, at the cost of weakening the static information
available about a program, see below.)

Those limitations diminish the usefulness of exception specifications in
practice, hence the trend to ban them.

On the other hand, writing robust programs requires robust foundations.
It is my firm belief that we need more statically derivable knowledge
about programs, not less. For client code to interact with a function,
it is important to know as precise a specification of the function as
possible. What are its accepted inputs, how and with which values can
it return, what are its side effects. And the exceptions thrown by the
function make no exception here. If it shall be possible to
programmatically react to exceptions thrown by a function, it is
important to know what exceptions the function can throw and what the
program state will be in such a case. And it is advantageous for a
language to be able to enforce and verify as much static knowledge as
possible. After all, "well-typed programs can't go wrong." In that
light, the fundamental Java exception design IMO makes a lot of sense,
distinguishing between checked exceptions (that are supposed to be
handled programmatically) and unchecked ones (that act more like
asserts, or are intended to bring down the complete (sub-)system).

So if we follow the trend and ban old-style dynamic exception
specifications from our C++ code base, it would IMO be nice to
nevertheless have information about functions' exception behaviour
around, in a form suitable for mechanical verification. (For example by
disabling GCC's -fno-enforce-eh-specs for --enable-dbgutil builds.) But
maybe that's only a hopeless pipe dream of mine, anyway...

Stephan
Caolán McNamara
2012-03-21 14:33:29 UTC
Permalink
it would IMO be nice to nevertheless have information about functions'
exception behaviour around, in a form suitable for mechanical
verification. (For example by disabling GCC's -fno-enforce-eh-specs
for --enable-dbgutil builds.) But maybe that's only a hopeless pipe
dream of mine, anyway...
Aren't we already doomed in the sense that if we take any signature
like..

virtual sal_Bool SAL_CALL foo(void) throw( uno::RuntimeException )

then if that foo is implemented using various bits of boost magic or
stl, and there's surely thousands that are, then they can throw loads of
stuff, under various edge-conditions, which are unrelated to
uno::RuntimeException unless foo does a pile of catching and converting
std::exceptions into uno::RuntimeException ?

Anyway, I'm a fan of a binary "throws no exceptions", "can throw any
exception" given the direction of the C++11 tide.

C.
Stephan Bergmann
2012-03-21 15:12:35 UTC
Permalink
Post by Caolán McNamara
it would IMO be nice to nevertheless have information about functions'
exception behaviour around, in a form suitable for mechanical
verification. (For example by disabling GCC's -fno-enforce-eh-specs
for --enable-dbgutil builds.) But maybe that's only a hopeless pipe
dream of mine, anyway...
Aren't we already doomed in the sense that if we take any signature
like..
virtual sal_Bool SAL_CALL foo(void) throw( uno::RuntimeException )
then if that foo is implemented using various bits of boost magic or
stl, and there's surely thousands that are, then they can throw loads of
stuff, under various edge-conditions, which are unrelated to
uno::RuntimeException unless foo does a pile of catching and converting
std::exceptions into uno::RuntimeException ?
Yes, sure. It is a design error of the UNO C++ language binding that
all those functions do not additionally contain certain std exceptions
(bad_alloc, runtime_error, ios_base::failure, probably) in their
exceptions specifications. (And one can argue that it is a design error
of C++ to not offer a distinction between checked and unchecked
exceptions, a la Java.) Witness the handful of places in the code
(mostly written by myself, I guess) that *do* translate std::bad_alloc
into a UNO RuntimeException -- an arguably misguided approach I quickly
gave up on again. ;)

But that's a good argument that simply "disabling GCC's
-fno-enforce-eh-specs for --enable-dbgutil builds" wouldn't quite cut it.

Stephan
Bjoern Michaelsen
2012-03-21 10:23:39 UTC
Permalink
Hi all,
Post by Lubos Lunak
- boost, and pretty much everybody else it seems, do not consider it worth the
hassle of specifying what a function may or may not throw, except for
flagging a non-inline method as not throwing any exception at all as a means
of optimization for places where such functions are called
For the most part I would agree here. C++ exceptions are broken by design.
Allowing any kind of object (not even exception) to be thrown by default in a
function adds another _untyped_ return path (as if every function would return
a void* in addition to its return code). Which is exactly what you do _not_
want in a strongly typed environment. Since we are using bazillions of external
libraries, we have no way to fix this even if we would try to.

IMHO exceptions in C++ are a failed implementation, and if there is any general
advise about them, it is: "Avoid using C++ exceptions".

Best,

Bjoern

P.S.: see also http://yosefk.com/c++fqa/exceptions.html
Stephan Bergmann
2012-03-21 10:54:09 UTC
Permalink
Post by Bjoern Michaelsen
For the most part I would agree here. C++ exceptions are broken by design.
Allowing any kind of object (not even exception) to be thrown by default in a
function adds another _untyped_ return path (as if every function would return
a void* in addition to its return code). Which is exactly what you do _not_
want in a strongly typed environment. Since we are using bazillions of external
libraries, we have no way to fix this even if we would try to.
IMHO exceptions in C++ are a failed implementation, and if there is any general
advise about them, it is: "Avoid using C++ exceptions".
Couldn't agree less. ;)

That C++ can throw arbitrary types, not restricted to some exception
class hierarchy is rather orthogonal to the issues discussed so far in
this thread. It does *not* add "another _untyped_ return path," at
least not any more so than it would do if throwable types in C++ were
restricted to some specific subset.

And "avoid using C++ exceptions" is throwing out the baby with the
bath-water. They are not ideal, but neither are the alternatives.

Stephan
Bjoern Michaelsen
2012-03-21 11:28:35 UTC
Permalink
It does *not* add "another _untyped_ return path," at least not any more so
than it would do if throwable types in C++ were restricted to some specific
subset.
Thats exactly the point. In a statically typed environment the default would
have needed to be "cant throw anything, unless explicitly stating otherwise".

Best,

Bjoern
Stephan Bergmann
2012-03-21 11:33:13 UTC
Permalink
Post by Bjoern Michaelsen
It does *not* add "another _untyped_ return path," at least not any more so
than it would do if throwable types in C++ were restricted to some specific
subset.
Thats exactly the point. In a statically typed environment the default would
have needed to be "cant throw anything, unless explicitly stating otherwise".
But that is completely unrelated to whether you can only throw
derivations of std::exception or also things like int 5. I think I lost
you.

Stephan
Bjoern Michaelsen
2012-03-21 12:16:15 UTC
Permalink
Post by Stephan Bergmann
But that is completely unrelated to whether you can only throw
derivations of std::exception or also things like int 5.
Yes, sorry for mixing that in. The problem is can-throw-by-default. If one
would be required to spec what one would throw (java checked exceptions like),
one would at least _see_ if something can throw something overly generic (the
equivalent of an void*) and could take precautions.

Best,

Bjoern
Michael Stahl
2012-03-21 10:37:26 UTC
Permalink
Post by Lubos Lunak
Hello,
I've just found out that exception specification on functions in LO have been
just pretty comments, for about 10 years.
Just to make sure what I'm talking about, it's the throw() in e.g. this
void foo( int a, void* b ) throw( css::uno::RuntimeException );
which says what exceptions a function can throw.
I found out by compiling LO with Clang, running it and having it crash where
gcc-compiler version had no problem. Stephan Bergmann and repo history
revealed that in 2001 -fno-enforce-eh-specs was added to gcc's flags, turning
off code generated that ensures these specifications are actually followed
(and MSVC has reportedly never cared).
it used to work like this:
- MSVC apparently doesn't support enforcing the exception
specifications as mandated by the C++ standard at all
- for GCC they were explicitly disabled, at least on Linux
- for SunStudio they were actually enforced and incorrect ones lead to
immediate abort via std::terminate (which is why i have fixed
a lot of incorrect exception specifications in the code over the
years, working mostly on Solaris)
Post by Lubos Lunak
Clang does not have the option. I've
already actually fixed the problem (in
vbahelper/inc/vbahelper/vbaccesshelper.hxx) and it appears the specifications
somehow magically haven't rotten, as now LO runs fine for me (as far as I can
say, I can't test everything).
That however brings up the question of what the specification are for in the
codebase. The 'Deprecated Exception Specifications, Added noexcept' part of
http://herbsutter.com/2010/03/13/trip-report-march-2010-iso-c-standards-meeting/
- the specifications enforce that disallowed exceptions are not thrown at
runtime, adding code to each such function to check it, thus actually making
the performance worse
- boost, and pretty much everybody else it seems, do not consider it worth the
hassle of specifying what a function may or may not throw, except for
flagging a non-inline method as not throwing any exception at all as a means
of optimization for places where such functions are called
- C++11 deprecates it and instead introduces a noexcept keyword which forbids
the function to throw anything
I'm not strongly biased either way, but what we have right now are really
just pretty comments on functions. I think we should either say that we use
the specifications, in which case -fno-enforce-sh-specs should not be used at
least in debug builds, or we can say we follow the C++11/Boost/etc. trend and
not use them, in which case we can have one macro for portable way of saying
noexcept and have an EasyHack for removing the other specifications. BTW, it
should be also noted that SAL_THROW() expands to nothing with gcc.
Opinions on this?
exception specifications in C++ are pretty useless because they are only
checked at runtime, not statically, and the only handling that is
realistically possible for violations is to abort the program.

it may be a good idea to annotate functions that are guaranteed not to
throw because that could enable some micro-optimizations from the C++
implementation, but other than that it doesn't make sense for us to use
these.

for the case where it is a good idea to check exceptions (e.g. Java UNO
bridge, which cannot throw exceptions to the Java side that are not
declared on the interface), a runtime catch-all check should be
sufficient also.

but i wonder if there is any compatibility concern here; i'd be more
comfortable if we only removed the exceptions specifications for LO4 and
not earlier.

(now i'll actually go read the article you linked...)
Stephan Bergmann
2012-03-21 11:02:48 UTC
Permalink
Post by Michael Stahl
for the case where it is a good idea to check exceptions (e.g. Java UNO
bridge, which cannot throw exceptions to the Java side that are not
declared on the interface), a runtime catch-all check should be
sufficient also.
Yep, guards to catch attempts of throwing anything not allowed by UNO
across a bridge are generally in place.
Post by Michael Stahl
but i wonder if there is any compatibility concern here; i'd be more
comfortable if we only removed the exceptions specifications for LO4 and
not earlier.
Removing exception specifications from the C++ headers generated by
cppumaker and from C++ classes defined in the URE interface should
actually be compatible.

Stephan
Lubos Lunak
2012-04-02 09:59:11 UTC
Permalink
Post by Lubos Lunak
- C++11 deprecates it and instead introduces a noexcept keyword which
forbids the function to throw anything
I'm not strongly biased either way, but what we have right now are really
just pretty comments on functions. I think we should either say that we use
the specifications, in which case -fno-enforce-sh-specs should not be used
at least in debug builds, or we can say we follow the C++11/Boost/etc.
trend and not use them, in which case we can have one macro for portable
way of saying noexcept and have an EasyHack for removing the other
specifications. BTW, it should be also noted that SAL_THROW() expands to
nothing with gcc.
Opinions on this?
Could this be please added to the next TSC agenda? It doesn't look to me like
there's a clear consensus, and not caring much myself about either
possibility, I don't want to do any change. I'll at most
remove -fno-enforce-eh-specs from --enable-dbgutil builds if there's no
resolution on this, so that clang is not the only compiler to find mistakes.
--
Lubos Lunak
***@suse.cz
Michael Meeks
2012-04-02 16:57:59 UTC
Permalink
Could this be please added to the next TSC agenda ?
Yep, pushed & will invite you to the meeting :-)

ATB,

Michael.
--
***@suse.com <><, Pseudo Engineer, itinerant idiot
Continue reading on narkive:
Loading...