Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

std0X::expected_exception<T>()

8 views
Skip to first unread message

Alexander Terekhov

unread,
Jun 6, 2003, 5:06:11 PM6/6/03
to

"....
template<class T> bool expected_exception() throw();
Returns: true if matching handler is found.
Notes: if expected_exception<T>() returns false then
throwing T (at the point of expected_exception<T>()
invocation) will result in the invocation of unexpected()
handler with an exception consider caught. In effect,
(in this new edition of this International Standard:-)

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?

TIA.

Well, "the context" is this:

http://lists.boost.org/MailArchives/boost/msg48162.php
http://lists.boost.org/MailArchives/boost/msg48211.php
([boost] Re: Exception handling... it's time to fix the
http://www.boost.org/more/error_handling.html)

regards,
alexander.

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

David Abrahams

unread,
Jun 8, 2003, 9:39:52 PM6/8/03
to
tere...@web.de (Alexander Terekhov) writes:

> "....
> template<class T> bool expected_exception() throw();
> Returns: true if matching handler is found.
> Notes: if expected_exception<T>() returns false then
> throwing T (at the point of expected_exception<T>()
> invocation) will result in the invocation of unexpected()
> handler with an exception consider caught. In effect,
> (in this new edition of this International Standard:-)
>
> 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.

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
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.

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

Balog Pal

unread,
Jun 11, 2003, 2:04:09 PM6/11/03
to

"Alexander Terekhov" <tere...@web.de> wrote in message
news:3EE08DDC...@web.de...

> template<class T> bool expected_exception() throw();
> Returns: true if matching handler is found.

Is that really useful?

Suppose I have a fragment:

try { foo(); }
catch(...)
{
Rollback();
throw;
}

your stuff is called from some descendant of foo(). it will detect any
exception az expected. (do you intend that? there IS a handler)

Then probably throw something that may end up unhandled.

Paul

Alexander Terekhov

unread,
Jun 11, 2003, 2:33:24 PM6/11/03
to

Balog Pal wrote:
>
> "Alexander Terekhov" <tere...@web.de> wrote in message
> news:3EE08DDC...@web.de...
>
> > template<class T> bool expected_exception() throw();
> > Returns: true if matching handler is found.
>
> Is that really useful?
>
> Suppose I have a fragment:
>
> try { foo(); }
> catch(...)
> {
> Rollback();
> throw;
> }

Rather: <http://google.com/groups?selm=3EC382B0.BE8CD854%40web.de>

template<class _FwdIt, class _Tval> inline
void _Uninit_fill(_FwdIt _First, _FwdIt _Last, const _Tval& _Val,
_Nonscalar_ptr_iterator_tag)
{ // copy _Val throughout raw [_First, _Last), arbitrary type
_FwdIt _Next = _First;
try {
for (; _First != _Last; ++_First)
_Construct(&*_First, _Val);
}
action_on_propagation_of(...) { /* THIS DOESN'T CATCH *UNEXPECTED* EXCEPTIONS */
/* THIS DOES RETHROW EXCEPTIONS AUTOMATICALLY */
/* NOTHING ELSE CAN BE THROWN FROM THIS SCOPE */
for (; _Next != _First; ++_Next)
_Destroy(&*_Next);
}
}

Clarification 1: <http://google.com/groups?selm=3D8F4994.39A7596C%40web.de>

Okay, "a point" is this: Dinkum C++ Library [The Standard C++ Library
implementation] uses catch(...)/rethrow... and THIS {currently} doesn't
work on many threaded C++ implementations [on AIX, Solaris, etc.] w.r.t.
thread cancellation and thread exit (pthread_exit()) "exceptions". RAII
"workarounds" (even for cleanup-only) would really help here. Another
problem is that catch(...)/rethrow (w/o ES-like protection) DOES "steal"
exceptions and provoke UNNEEDED unwinding. That's also BAD. "Soft-catch"/
action_on_propagation_of(<whatever>) would probably solve this in a
somewhat better way than "traditional" RAII.

Clarification 2: <http://google.com/groups?selm=3EBBD992.763C6E7A%40web.de>

It is NOT 'finally' because it fires only when some matching exception
is raised within its try-block scope AND there's a matching handler
found *somewhere else* in the surrounding dynamic context. AFAICS,
'finally' doesn't work that way. The "action" thing can even examine
the propagating exception(e.g. using try { throw; } catch... technique)
and perhaps even append some info to it (catching by reference). The
key point is that "THIS DOESN'T CATCH *UNEXPECTED* EXCEPTIONS". You can
think of it as a sort of WEAK (it should have been that way from the
beginning) function-try-block handler of some constructor or destructor
because such handlers also rethrow exceptions "automatically". WEAK
simply means that it can't be a propagation target because it's just
not "visible" in the search phase.

>
> your stuff is called from some descendant of foo(). it will detect any
> exception az expected. (do you intend that? there IS a handler)

I can protect my code from stealing UNEXPECTED exceptions by caller's
catch(...), catch(const std::exception&), whatever "catch-everything"
things via imposing restrictive exception specifications ON MY CODE.

I mean: <http://google.com/groups?selm=c29b5e33.0202161451.2ef75f1f%40posting.google.com>

void oper(); // could throws anything

void checked_oper_call() throw( SOME_KNOWN_EXPECTED_EXCEPTIONS )
{ oper(); }

try {
checked_oper_call();
}
catch( ... ) // I know what I am doing/getting here,
// just do not want to write many
// catch clauses ;-)
{
//...
}

>
> Then probably throw something that may end up unhandled.

That's what you've coded. If you don't like it then don't code it.

regards,
alexander.

Alexander Terekhov

unread,
Jun 11, 2003, 3:24:50 PM6/11/03
to

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.

>
> 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? 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) is
nothing but a violation of RAII "principles" 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).

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? Note
that debugging aside, mandatory 2-phase EH would also facilitate
robust and reliable "failover"... 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).

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... we could then discuss some details (if the answer is yes),
I guess. ;-)

regards,
alexander.

David Abrahams

unread,
Jun 13, 2003, 2:54:15 PM6/13/03
to
tere...@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

---

David Abrahams

unread,
Jun 13, 2003, 8:30:14 PM6/13/03
to

tere...@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

---

Alexander Terekhov

unread,
Jun 14, 2003, 3:43:48 PM6/14/03
to

David Abrahams wrote: ....

In a couple of days from now I'll try to address some of your points (in
a reply to your "[repost]" message). For now, let me put it this way:

#include <stdio.h>

// volatile aside for a moment...

#define TRY { \
int __handlers_registered = 0; \
do { \
if (__handlers_registered) { \

#define CATCH(__URL) \
} \
if (!__handlers_registered) { \
printf("record %s\n", __URL); \
} \
else { \
printf("unwind %s\n", __URL); \
} \
{ \

#define ENDTRY \
} \
} while (!__handlers_registered++); \
}; \

int main() {
printf("\nDear David, Herb, and/or \"whoever interested\",\n\n");
TRY {
printf("please follow ALL the embedded links and try to...\n");
}
CATCH("http://google.com/groups?threadm=3EE9F54A.EC545F67%40web.de") {
}
ENDTRY
printf("once/iff you'll have some spare time, of course.\n\nTIA.\n");
}

regards,
alexander.

Alexander Terekhov

unread,
Jun 16, 2003, 9:38:08 PM6/16/03
to

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.

0 new messages