Topic: std0X::expected_exception<T>() [repost]


Author: dave@boost-consulting.com (David Abrahams)
Date: Sat, 14 Jun 2003 00:30:14 +0000 (UTC)
Raw View
terekhov@web.de (Alexander Terekhov) writes:

> David Abrahams wrote:
> [...]
>> >      if (!std::expected_exception<T>()) {
>> >        throw T(/*...*/);
>> >      }
>> >
>> >  is equivalent to:
>> >
>> >      if (!std::expected_exception<T>()) {
>> >        try {
>> >          throw T(/*...*/);
>> >        }
>> >        catch(const T&) {
>> >          std::unexpected();
>> >        }
>> >      }
>> >  ...."
>> >
>> >
>> > Would you vote against it? Why?
>>
>> I appreciate what you're trying to do, and I would certainly vote for
>> std::expected_exception by itself if implementors tell me it's
>> feasible.  There are two problems I see:
>>
>>    1. I'm pretty sure it's not feasible to implement for compilers
>>       with dumb SJLJ-style EH (c.f. MSVC and quite a few GCCs - do all
>>       GCC3.x use the new ABI? I think they don't).  If I'm right,
>>       voting for it would force ABI breakage for those
>>       implementations.  I'm not sure how acceptable that is for C++0X,
>>       but I'm guessing it would send customers screaming.
>
> Well, nobody's complaining thus far. ;-) ABI breakage aside for a
> moment, it can be implemented even with SJLJ-style EH, I think.

How?  On what do you base that assessment?

>>    2. You appear to be slipping in a subtle change in semantics for
>>       throwing an exception for which there is no handler: you are
>>       requiring that there is NO unwinding (except, possibly, the
>>       variables in the throw's enclosing block (?)), and that
>
> No unwinding at all.
>
>>       unexpected is unconditionally called immediately.  I think
>>       that's a separate issue and should be considered separately.
>>       I'm not sure whether it's acceptable to change the semantics in
>>       that way.
>
> Why are you not sure whether it's acceptable?

Some people may be counting on their implementation's current
behavior, and vendors may be unwilling to change that underneath their
customers.  It's not a question of whether it's acceptable to *me*,
really.

> Note that the current semantics that require std::unexpected() and
> terminate() handlers "fly together with an unexpected exception" way
> up to the injected catch(...) in the function-try-block (of a
> fucntion with an ES)

That's a very poetic description, but I can't tell what it means.
Would you mind using the accepted terminology?

> is nothing but a violation of RAII "principles"

Which principles are those?

> and is a rather serious defect on its own, I'd say.
>
> The only explanation that I have for the current silliness is that
> Stroustrup&Co were designing it under the "assumption" that <quote>
> On other systems, it is architecturally close to impossible not to
> invoke the destructors while searching for a handler </quote> (Pg.
> 381, TC++PL SE).

I believe that is the case with SJLJ-style EH.

> Interestingly enough, he also writes <quote> When an exception is
> caught, the exact point where it was thrown is generally not known.
> This represents a loss of information compared to what a debugger
> might know about the state of a program. In some C++ development
> environments, for some programs, and for some people, it might
> therefore be preferable not to catch exceptions from which the
> program isn't designed to recover. </quote> And how does this fit
> together with catch(...)-injected exception specifications?

What does that mean?

> Note that debugging aside, mandatory 2-phase EH would also
> facilitate robust and reliable "failover"...

Isn't 2-phase EH something yet again completely different from what
you've been discussing here so far?

> and faster running
> programs: I mean optimizations for throw()-nothing calls AND
> implicit "throw nothing" regions that do contain some throwing calls
> but can be compiled as throw()-nothing code due to the knowledge
> [based on ESpecs of enclosing function(s)] that those exceptions are
> totally unexpected and will never propagate even if something wrong
> happens (something unexpected [e.g. std::logic_error ;-) ] gets
> thrown).

Those optimizations are already possible.
>
> It's really time to mandate the mandatory 2-phase EH in Std. C++.
>
> Well, the question is whether there's a realistic chance of that
> happening -- is there any wiliness to do it on the part of the
> committee members (details aside for a moment)? Please let me
> know...

How should I know?  It's not something I've been polling people on.
My guess is most of them don't even understand the problem (if there
is one).  The way to fix that, as I've told you more times than I can
count, is to write a DR or a paper where all your ideas and arguments
are collected cogently in one static place.  Even if I get to the
point of understanding your POV in this NG thread, it's very likely
that I'll forget the details of the issue over the next week.

> we could then discuss some details (if the answer is yes), I
> guess. ;-)

I'm not going to waste my time talking about details until there's a
paper, I think.

--
Dave Abrahams
Boost Consulting
www.boost-consulting.com

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]





Author: terekhov@web.de (Alexander Terekhov)
Date: Tue, 17 Jun 2003 01:38:08 +0000 (UTC)
Raw View
David Abrahams wrote:
[...]
> >> I appreciate what you're trying to do, and I would certainly vote for
> >> std::expected_exception by itself if implementors tell me it's
> >> feasible.  There are two problems I see:
> >>
> >>    1. I'm pretty sure it's not feasible to implement for compilers
> >>       with dumb SJLJ-style EH (c.f. MSVC and quite a few GCCs - do all
> >>       GCC3.x use the new ABI? I think they don't).  If I'm right,
> >>       voting for it would force ABI breakage for those
> >>       implementations.  I'm not sure how acceptable that is for C++0X,
> >>       but I'm guessing it would send customers screaming.
> >
> > Well, nobody's complaining thus far. ;-) ABI breakage aside for a
> > moment, it can be implemented even with SJLJ-style EH, I think.
>
> How?  On what do you base that assessment?

I base that assessment on a thought that it shall be possible to
enter "try" scope AFTER registration of catch-handler identities.

[...]
> > Note that the current semantics that require std::unexpected() and
> > terminate() handlers "fly together with an unexpected exception" way
> > up to the injected catch(...) in the function-try-block (of a
> > fucntion with an ES)
>
> That's a very poetic description, but I can't tell what it means.
> Would you mind using the accepted terminology?

The problem is in the "if called by the implementation" clause in
18.6.2.4/2 -- "Calls the unexpected_handler function in effect
immediately after evaluating the throw-expression (18.6.2.2)".
There's a similar problem for terminate() in 18.6.3.3/2. Details
can be found in the c.l.c++.mod thread <http://tinyurl.com/btje>.

>
> > is nothing but a violation of RAII "principles"
>
> Which principles are those?

Under the current rules you can't really use RAII "activation"
objects for unexpected() and/or terminate() handlers. Pls see
the illustration in the thread referenced above. Note that both
Stroustroup (in TCPL) and Sutter (in his gotw and article on ES)
fail to mention that rather important "aspect", AFAICS.

>
> > and is a rather serious defect on its own, I'd say.
> >
> > The only explanation that I have for the current silliness is that
> > Stroustrup&Co were designing it under the "assumption" that <quote>
> > On other systems, it is architecturally close to impossible not to
> > invoke the destructors while searching for a handler </quote> (Pg.
> > 381, TC++PL SE).
>
> I believe that is the case with SJLJ-style EH.

On what do you base that assessment? ;-)

>
> > Interestingly enough, he also writes <quote> When an exception is
> > caught, the exact point where it was thrown is generally not known.
> > This represents a loss of information compared to what a debugger
> > might know about the state of a program. In some C++ development
> > environments, for some programs, and for some people, it might
> > therefore be preferable not to catch exceptions from which the
> > program isn't designed to recover. </quote> And how does this fit
> > together with catch(...)-injected exception specifications?
>
> What does that mean?

That means that exception specs "inject" catch(...) -- the thing I
just hate (a few "exceptions" aside).

>
> > Note that debugging aside, mandatory 2-phase EH would also
> > facilitate robust and reliable "failover"...
>
> Isn't 2-phase EH something yet again completely different from what
> you've been discussing here so far?

No. It all boils down to 2-phase EH.

>
> > and faster running
> > programs: I mean optimizations for throw()-nothing calls AND
> > implicit "throw nothing" regions that do contain some throwing calls
> > but can be compiled as throw()-nothing code due to the knowledge
> > [based on ESpecs of enclosing function(s)] that those exceptions are
> > totally unexpected and will never propagate even if something wrong
> > happens (something unexpected [e.g. std::logic_error ;-) ] gets
> > thrown).
>
> Those optimizations are already possible.

You certainly can't take any advantage from the lack of "stack
unwinding" inside, say, throw()-nothing routines that can include
things like vector::at() or whatever that can throw something you
really don't want to catch. The problem is that the current rules
REQUIRE "stack unwinding"... which I want to forbid. More on this
can be found in the "MEMORY ACCESS ORDER" section of the following
paper:

http://www.computer.org/concurrency/pd2000/p4072abs.htm
(C++ Exception Handling, Christophe de Dinechin,
 IEEE Concurrency October-December 2000 (Vol. 8, No. 4))

I mean: <quote> Of course, if f or g can throw exceptions, then
the destructor has to see the object's correct value, and the
invocation of the destructor can occur at any time. Therefore, the
compiler must generate code that writes the value of the object to
memory in the original source order with respect to function calls.
In practice, this last effect and its variants tends to be the most
significant, because it affects memory accesses (which are
expensive on today's microprocessors), and it occurs whenever
exceptions are enabled (regardless of whether there are exception-
related constructs in the code) </quote>.

regards,
alexander.

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html                       ]