Boost logo

Boost :

From: Alexander Terekhov (terekhov_at_[hidden])
Date: 2002-08-20 10:27:05


Peter Dimov wrote:
[...]
> Whether you spell your try block as try {} or as "throw()" doesn't really
> matter.

< Forward Inline >

-------- Original Message --------
Message-ID: <3D625EDA.20377582_at_[hidden]>
From: Alexander Terekhov <terekhov_at_[hidden]>
Subject: Re: High level thread design question
Newsgroups: comp.programming.threads
Date: Tue, 20 Aug 2002 17:23:06 +0200

Peter Dimov wrote:
>
> Alexander Terekhov <terekhov_at_[hidden]> wrote in message news:<3D613D44.9B67916_at_[hidden]>...
> > Peter Dimov wrote:
> > [...]
> > > What is brain dead, the concept or the implementation?
> >
> > The concept. I, personally, really don't like the idea of propagating
> > std::logic_error (and alike beasts) across threads on join -- ideally,
> > I want it to kill the process at throw point, unless someone really
> > wants to pretend that s/he can "recover" from it and catch it for that
> > or some other (most likely silly) reason;
>
> You can put an exception specification that prohibits std::logic_error
> on the threadproc (and hope that it doesn't unwind ;-) .)
                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

Peter, I'm going to IGNORE your wink. ;-) That's rather serious
issue/design flaw in the current C++ language, I believe strongly.

To others: IT'S HOPELESS! Unfortunately, brain-dead unwinding on
ES violations IS THE STANDARD REQUIRED "FEATURE" in the current
C++ language ("The C++ Programming Language", 3rd Edition, Bjarne
Stroustrup [but annotations are mine]):

</quote>

14.6 Exception Specifications

Throwing or catching an exception affects the way a function
relates to other functions. It can therefore be worthwhile to
specify the set of exceptions that might be thrown as part of
the function declaration.

For example:

  void f(int a) throw ( x2, x3 );

This specifies that f() may throw only exceptions x2, x3, and
exceptions derived from these types, but no others. When a
function specifies what exceptions it might throw, it effectively
offers a guarantee to its callers. If during execution that
function does something that tries to abrogate the guarantee,
the attempt will be transformed into a call of std::unexpected().
The default meaning of unexpected() is std::terminate(), which
in turn normally calls abort(); see §9.4.1.1 for details.

In effect,

  void f() throw ( x2, x3 )
  {
    // stuff
  }

is equivalent to:
^^^^^^^^^^^^^^^^^ <annotation>

                   a) IT IS NOT "is equivalent to:" -- pls see the
                      next annotation below (it's simply the second
                      major flaw; after the general idea to always
                      unwind unexpected things... partially hiding
                      behind rather silly "On other systems, it is
                      architecturally close to impossible not to
                      invoke the destructors while searching for a
                      handler" argument[1]. (quoted from: Section
                      14.7, Uncaught Exceptions, TC++PL)

                   b) copying/constructing temporary ("x2" or "x1")
                      caught exception object aside; BTW, I really
                      prefer to catch "const refs" (for "class type"
                      exceptions)

                  </annotation>

  void f()
  try
  ^^^ <annotation>

       that's "function-try-block" -- pretty useless thing, and
       only good for "translating"/"logging"/"attach something to"
       exceptions thrown from c-tors and d-tors (but d-tors should
       normally NOT throw)... And, of course, it *WORKS JUST FINE*
       w.r.t. brain-dead catch(...)->unexpected() unwinding on ES
       violations! ;-) Well, actually, it's >>UTTERLY BRAIN-DEAD<<
       (and IS NOT "is equivalent to:", to begin with) given that
       the current language simply BREAKS "RAII model" with respect
       to user specified (using RAII do/undo objects) terminate()
       and unexpected() handlers (when hitting ES "barriers";
       "implementation-defined" unwinding on uncaught exceptions
       aside for a moment):

       http://groups.google.com/groups?selm=m.collett-E982F8.12450216072002%40lust.ihug.co.nz
       http://groups.google.com/groups?threadm=3D3547BE.3045A2A6%40web.de
       (Subject: Re: Is internal catch-clause rethrow standard?)

      </annotation>
  {
    // stuff
  }
  catch (x2) { throw; } // re-throw
  catch (x3) { throw; } // re-throw
  catch (...) {
     std::unexpected(); // unexpected() will not return
  }

The most important advantage is that the function declaration
belongs to an interface that is visible to its callers. Function
definitions, on the other hand, are not universally available.
Even when we do have access to the source code of all our
libraries, we strongly prefer not to have to look at it very
often. In addition, a function with an exception-specification
is shorter and clearer than the equivalent hand-written version.

A function declared without an exception-specification is
assumed to throw every exception.

For example:

  int f(); // can throw any exception

A function that will throw no exceptions can be declared with
an empty list:

  int g() throw(); // no exception thrown

</quote>

regards,
alexander.

[1] http://groups.google.com/groups?threadm=3C91397A.9FC5F5A4%40web.de
     (Subject: Re: C++ and threads)

P.S. http://lists.boost.org/MailArchives/boost/msg34156.php
     ([boost] Re: Attempting resolution of Threads & Exceptions Issue)

"....
 In my view, there should be NO difference whatsoever between
 "no-handler-found" and hitting some exception propagation barrier
 [ES, d-tor that is unwound itself, etc.] -- that everything should
 result in calling unexpected() AT THROW POINT (no stack unwinding;
 two phase processing).
 ...."


Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk