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

A half serious proposal: overloaded destructors

0 views
Skip to first unread message

Nicola Musatti

unread,
Apr 22, 2002, 3:32:31 PM4/22/02
to
Hallo, everybody.
I hope this hasn't been already discussed to much. In his presentation
on error handling at the ACCU conference Andrei Alexandrescu showed an
example where it was important to know in a class destructor wheter
destruction was taking place due to normal end of scope or to stack
unwinding.

I just thought that this might be achieved by allowing destructors to
take one argument. Such destructors would only be called during stack
unwinding and would be passed the active exception.

This would allow an even more through exploitment of RAII, by providing
a mechanism to obtain automatic commit or rollback according to whether
an exception was thrown. Something like:

class Transaction {
public:
Transaction() { open(); }
~Transaction() { commit(); }
~Transaction(std::exception & e) { rollback(); }
};

Cheers,
Nicola Musatti

---
[ 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 ]

Alexander Terekhov

unread,
Apr 22, 2002, 5:02:00 PM4/22/02
to

Nicola Musatti wrote:
>
> Hallo, everybody.
> I hope this hasn't been already discussed to much. In his presentation
> on error handling at the ACCU conference Andrei Alexandrescu showed an
> example where it was important to know in a class destructor wheter
> destruction was taking place due to normal end of scope or to stack
> unwinding.
>
> I just thought that this might be achieved by allowing destructors to
> take one argument. Such destructors would only be called during stack
> unwinding and would be passed the active exception.
>
> This would allow an even more through exploitment of RAII, by providing
> a mechanism to obtain automatic commit or rollback according to whether
> an exception was thrown. Something like:
>
> class Transaction {
> public:
> Transaction() { open(); }
> ~Transaction() { commit(); }
> ~Transaction(std::exception & e) { rollback(); }
> };

How about ( 0.01 x "serious proposal" ;-)):

Transaction::~Transaction()
{

// Unwinding on exception propagation?
if ( std::unwinding( this ) ) {

// A) invoke rollback()/whatever

// B) have access to propagating exception:

const std::exception* e; // or "whatever" ptr

if ( 0 != (e = std::propagating_exception_ptr< BadThing >()) ) {

// Whatever

}
else if ( 0 != (e = std::propagating_exception_ptr< AnotherBadThing
>())

// Whatever

}

// Whatever

}
else { // "Normal" destruction

// A) invoke commit()/whatever

// B) Can't we really throw here?

if ( some_error ) {

// Search-for-handler/check Ex.Specs (if any)
// in the dynamic context
if ( std::expected_exception< BadThing >() )
throw BadThing( whatever );

// Whatever

}

// Whatever

}

}

Also:

http://groups.google.com/groups?as_umsgid=3C73AB86.99B8CBE0%40web.de

"....
[1] In addition to those two "future_std"
fancy functions I've mentioned before,
I think that something along the lines
of: (just ideas/food for thought)

future_std_local_context_template< int >
struct T;

future_std_local_context_template<>
struct T< 1 > { T() { context.blabla...; }
~T() { context.blabla...; }
};

future_std_local_context_template<>
struct T< 2 > { T() { context.blabla...; }
~T() { context.blabla...; }
};

void f()
{
blabla...;
future_std_local_context T< 1 > t1;
...
future_std_local_context T< 2 > t2;
}

might be rather handy too! ;-)"

http://groups.google.com/groups?as_umsgid=3CC1ABD8.971E71BF%40web.de

"....
Well, I mean basically something along the lines of:
(but with a constructor too ;-))


http://groups.google.com/groups?as_umsgid=3A9E5923.29E877B7%40dresdner-bank.com
(see "- Create a local instance with a destructor: ....")
...."

regards,
alexander.

Dave Harris

unread,
Apr 23, 2002, 5:09:47 PM4/23/02
to
obje...@divalsim.it (Nicola Musatti) wrote (abridged):

> I just thought that this might be achieved by allowing destructors to
> take one argument. Such destructors would only be called during stack
> unwinding and would be passed the active exception.

I have made a more general request for destructor arguments, as a
communication channel for derived classes and placement destruction. I
don't think anyone was very interested though.

Eg:
class Base {
public:
Base( bool shouldRegister=true ) {
if (!shouldRegister)
Singleton::instance().add( this );
}
~Base( bool shouldUnregister=true ) {
if (shouldUnregister)
Singleton::instance().remove( this );
}
};

class Derived: public Base {
public:
Derived() : Base(false) {
MySingleton::instance().add(this);
}
~Derived() : Base(false) {
MySingleton::instance().remove(this);
}
}

The idea here is that the Base class constructor registers itself by
default, but Derived classes can tell it not to if they have already made
their own arrangements. There is no good way to write this in C++ today,
because there is no good way for a derived destructor to communicate with
its Base one.

Dave Harris, Nottingham, UK | "Weave a circle round him thrice,
bran...@cix.co.uk | And close your eyes with holy dread,
| For he on honey dew hath fed
http://www.bhresearch.co.uk/ | And drunk the milk of Paradise."

xle...@qmailcomq.com

unread,
Apr 23, 2002, 8:07:28 PM4/23/02
to
Dave Harris <bran...@cix.co.uk> wrote:

> The idea here is that the Base class constructor registers itself by
> default, but Derived classes can tell it not to if they have already made
> their own arrangements. There is no good way to write this in C++ today,
> because there is no good way for a derived destructor to communicate with
> its Base one.

Why isn't calling Base::someMethod() in Derived::~Derived() a good way
to communicate? In ~Derived(), the Base class is still alive and well.

Leo

xle...@qmailcomq.com

unread,
Apr 23, 2002, 8:46:01 PM4/23/02
to
Nicola Musatti <obje...@divalsim.it> wrote:

> This would allow an even more through exploitment of RAII, by providing
> a mechanism to obtain automatic commit or rollback according to whether
> an exception was thrown. Something like:

> class Transaction {
> public:
> Transaction() { open(); }
> ~Transaction() { commit(); }
> ~Transaction(std::exception & e) { rollback(); }
> };

See 15.5.3 [except.uncaught]

Leo

Alexander Terekhov

unread,
Apr 23, 2002, 8:45:55 PM4/23/02
to

Dave Harris wrote:
>
> obje...@divalsim.it (Nicola Musatti) wrote (abridged):
> > I just thought that this might be achieved by allowing destructors to
> > take one argument. Such destructors would only be called during stack
> > unwinding and would be passed the active exception.
>
> I have made a more general request for destructor arguments, as a
> communication channel for derived classes and placement destruction. I
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

> don't think anyone was very interested though.
>
> Eg:
> class Base {
> public:
> Base( bool shouldRegister=true ) {
> if (!shouldRegister)
> Singleton::instance().add( this );
> }
> ~Base( bool shouldUnregister=true ) {
> if (shouldUnregister)
> Singleton::instance().remove( this );
> }
> };
>
> class Derived: public Base {
> public:
> Derived() : Base(false) {
> MySingleton::instance().add(this);
> }
> ~Derived() : Base(false) {
> MySingleton::instance().remove(this);
> }
> }

How about ( another 0.01 of "serious proposal" ;-)):

class Base {

// Just an example; m_blahblah would work as well,
// if needed. Even *pure-virtuals* would WORK!!!!
virtual bool shouldRegister() { return true; }
virtual bool shouldUnRegister() { return true; }

// future_std stuff
post Base() // post-constructor
{
// object is *fully constructed* here
// POLYMORPHIC BEHAVIOR *WORKS*!!!!

if ( shouldRegister() )
MySingleton::instance().add(this);

// whatever

}

// future_std stuff
pre ~Base() // pre-destructor
{
// object is *fully constructed* here
// POLYMORPHIC BEHAVIOR *WORKS*!!!!

if ( shouldUnRegister() )
MySingleton::instance().remove(this);

// whatever

}

public:

// conventional stuff
Base( /* whatever */ )
/* : whatever */ { /* whatever */ }

/* virtual */ ~Base() { /* whatever */ }

};

regards,
alexander.

Alexander Terekhov

unread,
Apr 24, 2002, 12:06:09 PM4/24/02
to

xle...@qmailcomq.com wrote:
>
> Nicola Musatti <obje...@divalsim.it> wrote:
>
> > This would allow an even more through exploitment of RAII, by providing
> > a mechanism to obtain automatic commit or rollback according to whether
> > an exception was thrown. Something like:

[...don't_like_it_sorry...]

> See 15.5.3 [except.uncaught]

See: http://groups.google.com/groups?as_umsgid=3CBC5F22.76708D0%40web.de

"....
lnxmuell:/usr/terekhov # ./e
throw 0
throw 1
throw 2
throw 3
throw 4
throw 5
throw 6
throw 7
throw 8
throw 9
Okay... ENOUGH active exceptions! ;-)
caught 9
caught 8
caught 7
caught 6
caught 5
caught 4
caught 3
caught 2
caught 1
caught 0
lnxmuell:/usr/terekhov # cat e.cpp
...."

regards,
alexander.

Nicola Musatti

unread,
Apr 24, 2002, 12:07:35 PM4/24/02
to

xle...@qmailcomq.com wrote:
>
> Nicola Musatti <obje...@divalsim.it> wrote:
>
> > This would allow an even more through exploitment of RAII, by providing
> > a mechanism to obtain automatic commit or rollback according to whether
> > an exception was thrown. Something like:
>
> > class Transaction {
> > public:
> > Transaction() { open(); }
> > ~Transaction() { commit(); }
> > ~Transaction(std::exception & e) { rollback(); }
> > };
>
> See 15.5.3 [except.uncaught]

I'm aware of this [i.e. std::uncaught_exception()], but I consider the
proposal above both more powerful and more elegant. Actually this idea
was fired by a comment by Andrei Alexandrescu on c.l.c++.m about how
objects and error handling apparently don't mix well.

Cheers,
Nicola Musatti

Nicola Musatti

unread,
Apr 24, 2002, 12:07:25 PM4/24/02
to

Alexander Terekhov wrote:
>
> Nicola Musatti wrote:
[...]


> > class Transaction {
> > public:
> > Transaction() { open(); }
> > ~Transaction() { commit(); }
> > ~Transaction(std::exception & e) { rollback(); }
> > };
>
> How about ( 0.01 x "serious proposal" ;-)):
>
> Transaction::~Transaction()
> {

> if ( std::unwinding( this ) ) {

> rollback();


> const std::exception* e; // or "whatever" ptr
> if ( 0 != (e = std::propagating_exception_ptr< BadThing >()) ) {
> // Whatever
> }
> else if ( 0 != (e = std::propagating_exception_ptr<

> AnotherBadThing >()))


> // Whatever
> }
> // Whatever
> }
> else {

> commit();


> if ( some_error ) {
> // Search-for-handler/check Ex.Specs (if any)
> // in the dynamic context
> if ( std::expected_exception< BadThing >() )
> throw BadThing( whatever );
> }
> }
> }

I slightly modified your example so that the two can be compared. In my
opinion the unwinding branch in your example can be more cleanly
implemented as a set of destructors overloaded on exception type.

On the other hand I'm aware that my proposal violates Bjarne
Stroustrup's suggestion that core language changes be kept to a minimum;
a point of view which I share by the way.

I'm not sure I understood correctly what your std::expected_exception()
is expected to do, so I'll withold my negative comments until I know
better :-)

Cheers,
Nicola Musatti

Dave Harris

unread,
Apr 24, 2002, 3:25:25 PM4/24/02
to
xle...@qmailcomq.com () wrote (abridged):

> Why isn't calling Base::someMethod() in Derived::~Derived() a good way
> to communicate?

Well, then Base::someMethod() needs to communicate with Base::~Base(),
because that is where the unregistering happens.

Perhaps you are thinking that this could be done through an instance
variable. That would work, but would add a memory cost to every instance
of the class.

Dave Harris, Nottingham, UK | "Weave a circle round him thrice,
bran...@cix.co.uk | And close your eyes with holy dread,
| For he on honey dew hath fed
http://www.bhresearch.co.uk/ | And drunk the milk of Paradise."

---

xle...@qmailcomq.com

unread,
Apr 24, 2002, 4:39:52 PM4/24/02
to
Alexander Terekhov <tere...@web.de> wrote:

>> See 15.5.3 [except.uncaught]

> See: http://groups.google.com/groups?as_umsgid=3CBC5F22.76708D0%40web.de


Cute, although an "abuse" of 15.2.3.
Is there a request to make std::uncaught_exception() return unsigned int
instead of bool?

Leo

xle...@qmailcomq.com

unread,
Apr 24, 2002, 4:46:23 PM4/24/02
to
Dave Harris <bran...@cix.co.uk> wrote:
> xle...@qmailcomq.com () wrote (abridged):
>> Why isn't calling Base::someMethod() in Derived::~Derived() a good way
>> to communicate?

> Well, then Base::someMethod() needs to communicate with Base::~Base(),
> because that is where the unregistering happens.

> Perhaps you are thinking that this could be done through an instance
> variable. That would work, but would add a memory cost to every instance
> of the class.

Not necessarily. static std::set<Base*> Base::UnregisterSet would do,

Leo

xle...@qmailcomq.com

unread,
Apr 24, 2002, 4:53:55 PM4/24/02
to
Nicola Musatti <obje...@divalsim.it> wrote:


> xle...@qmailcomq.com wrote:
>>
>> Nicola Musatti <obje...@divalsim.it> wrote:
>>
>> > This would allow an even more through exploitment of RAII, by providing
>> > a mechanism to obtain automatic commit or rollback according to whether
>> > an exception was thrown. Something like:
>>
>> > class Transaction {
>> > public:
>> > Transaction() { open(); }
>> > ~Transaction() { commit(); }
>> > ~Transaction(std::exception & e) { rollback(); }
>> > };
>>
>> See 15.5.3 [except.uncaught]

> I'm aware of this [i.e. std::uncaught_exception()], but I consider the
> proposal above both more powerful and more elegant. Actually this idea

If the standard requires all thrown objects to derive from std::exception,
maybe. But I do not believe it ever will.

> was fired by a comment by Andrei Alexandrescu on c.l.c++.m about how
> objects and error handling apparently don't mix well.

....don't mix well in C++ as defined by the current standard, I presume?
I believe the current situation is a (more or less) reasonable tradeoff
between flexibility and implementability, modulo some small mistakes,
like std::uncaught_exception() returning bool instead of something
more useful, like
a) type_info of the object being thrown,
so that the destructor may choose to compare it to relevant typeids and
retrieve the object being thrown type-safely using some other
(templated, as Alexander Terekhov proposed) function, and/or

b) exception nesting level (I'd like to see that trick being used in
real life, though). Otherwise, if someone wants full visibility
of the call stack, exception stack, etc., as they say,
"If you want Forth, you know where to find it." :-)

Leo
missing typeid(exception) in catch(...).

Alexander Terekhov

unread,
Apr 25, 2002, 12:59:42 PM4/25/02
to

Nicola Musatti wrote:
[...]

> I slightly modified your example so that the two can be compared. In my
> opinion the unwinding branch in your example can be more cleanly
> implemented as a set of destructors overloaded on exception type.

What do you mean with "more cleanly implemented" and why
"a set of destructors overloaded on exception type" is
better than a single destructor (and/or pre-destructor)
capable (optionally) of "branching" on:

A) *any* object (including "this") being "unwound" or not;

B) type and info of the exception being propagated,
even if "this" object itself IS NOT "unwound";
this would work even in a constructor (and/or
post-constructor) if that info is needed for
some reason there.

BTW, check out this proposal/idea too:

http://groups.google.com/groups?as_umsgid=3BC7214E.CDD9CF8E%40iobox.com
(Sergey P. Derevyago: "~A(bool in_stack_unwinding)")

I don't like it either. ;-)

> On the other hand I'm aware that my proposal violates Bjarne
> Stroustrup's suggestion that core language changes be kept to a minimum;
> a point of view which I share by the way.

Yep.

> I'm not sure I understood correctly what your std::expected_exception()
> is expected to do, so I'll withold my negative comments until I know
> better :-)

See: http://www.codesourcery.com/cxx-abi/abi-eh.html

std::expected_exception() is meant to perform search/check
for Ex.Spec violation and collision with already active
exception. Basically, if it returns false (for a given type)
then throwing it as exception is guaranteed to end up in
unexpected() or terminate() altogether. More on this,
including archaic "setjmp"-based exception impls without
"search phase only":

http://groups.google.com/groups?as_umsgid=3C91397A.9FC5F5A4%40web.de
(see "OK, you probably mean something along the lines of: ....",
and please don't miss "P.S." section ;-))

regards,
alexander.

Sean Parent

unread,
Apr 25, 2002, 1:38:21 PM4/25/02
to
There are two problems with uncaught_exception().

The first, is that it doesn't supply information about whether a particular
object is being destructed because of an unwind or just because it fell out
of scope. This is because several exceptions may be in flight or because the
object is declared inside a destructor that is being unwound.

The second problem is that it doesn't give any way to recover the exception,
or to modify it. I would like to extend the original proposal to the
following:

class Transaction
{
public:
~Transaction() { commit(); }
catch ~Transaction(std::exception &e) { rollback(); }
catch ~Transaction(...) { rollback(); }
};

Within a catch destructor the exception is considered "caught". Returning
from the destructor will call throw to re-throw the current exception. The
exception can also be rethrown (using throw) from within the destructor, or
a new exception can be thrown. Which destructor to invoke is determined in
the same way which catch clause to execute is. First match wins.

The case I would like to use this for is to "decorate" exception objects
with context information about what is currently happening. Although this
can be done with try / catch clauses, doing so is more cumbersome than using
objects. For example, compare the following code:

void Do_Foo()
{
doing_t doing("Doing Foo");
// ... Do stuff
}

VS.

void Do_Foo()
{
doing_t doing("Doing Foo");
try
{
// ... Do stuff
}
catch (...)
{
doing.decorate_exception(); // will re-throw
}
}

Now consider how much more complex this can get if Foo already has some
catch clauses. I either have to carefully consider where to call
decorate_exception() or I have to nest my try blocks.

Sean


in article qDmx8.7104$T_.1...@iad-read.news.verio.net,
xle...@qmailcomq.com at xle...@qmailcomq.com wrote on 4/23/02 5:46 PM:

Pavel Kuznetsov

unread,
Apr 25, 2002, 1:38:23 PM4/25/02
to
Nicola Musatti (obje...@divalsim.it) wrote:

NM> (...) allowing destructors
NM> to take one argument. Such destructors would only be called during
NM> stack unwinding and would be passed the active exception.

NM> class Transaction {
NM> public:
NM> Transaction() { open(); }
NM> ~Transaction() { commit(); }
NM> ~Transaction(std::exception & e) { rollback(); }
NM> };

I like this. I am just curious how to interpret destructors
with several arguments, especially if proposed extention
applies to ellipsis destructors.

--
Pavel

Alexander Terekhov

unread,
Apr 25, 2002, 3:00:08 PM4/25/02
to

xle...@qmailcomq.com wrote:
>
> Alexander Terekhov <tere...@web.de> wrote:
>
> >> See 15.5.3 [except.uncaught]
>
> > See: http://groups.google.com/groups?as_umsgid=3CBC5F22.76708D0%40web.de

[..."Okay... ENOUGH active exceptions! ;-)"...]

> Cute, although an "abuse" of 15.2.3.

Uhmm, why/what "abuse"?

> Is there a request to make std::uncaught_exception() return unsigned int
> instead of bool?

http://groups.google.com/groups?selm=9u8c51%24ij9%241%40news.tuwien.ac.at
( Christopher Eltschka: "Instead returning a bool, return an int telling
_how many_ exceptions are pending.")

I think that it would NOT hurt to have something like that...
but "bool unwinding(obj_ptr)" + "bool expected_ex<ex_t>()" +
"propagating_ex_ptr_or_ref< ex_t >()" + post-/pre- c-/d-tors"
would provide MUCH MORE value/"simplify" a lot of things quite
considerably, I believe. ;-)

regards,
alexander.

Alexander Terekhov

unread,
Apr 25, 2002, 5:12:40 PM4/25/02
to

Alexander Terekhov wrote:
>
> xle...@qmailcomq.com wrote:
> >
> > Alexander Terekhov <tere...@web.de> wrote:
> >
> > >> See 15.5.3 [except.uncaught]
> >
> > > See: http://groups.google.com/groups?as_umsgid=3CBC5F22.76708D0%40web.de
>
> [..."Okay... ENOUGH active exceptions! ;-)"...]
>
> > Cute, although an "abuse" of 15.2.3.
>
> Uhmm, why/what "abuse"?
>
> > Is there a request to make std::uncaught_exception() return unsigned int
> > instead of bool?
>
> http://groups.google.com/groups?selm=9u8c51%24ij9%241%40news.tuwien.ac.at
> ( Christopher Eltschka: "Instead returning a bool, return an int telling
> _how many_ exceptions are pending.")
>
> I think that it would NOT hurt to have something like that...
> but "bool unwinding(obj_ptr)" + "bool expected_ex<ex_t>()" +
> "propagating_ex_ptr_or_ref< ex_t >()" + post-/pre- c-/d-tors"
> would provide MUCH MORE value/"simplify" a lot of things quite
> considerably, I believe. ;-)

Heck! Why not: (my "8 o'clock"-wish list; just ideas ;-))

1. size_t "unwinding(obj_ptr)" (template/macro) -> 0/scope;

2. size_t uncaught_exception_scope() -> 0/scope;

3. "uncaught_ex_ptr_or_ref<T>(size_t scope = 0)" (ref.version throws)
("scope = 0" means "current"; in effect: uncaught_exception_scope());

4. "bool expected_exception<T>()";

5. "post-/pre- c-/d-tors";

6. "local context" templates:
http://groups.google.com/groups?selm=3CC4733A.822D7A07%40web.de

7. fix unneeded/harmful "unwinding" problems
(Ex.specs/"implementation-defined",etc.);

8. deprecate uncaught_exception().

Any objections or am I missing something, folks? ;-) ;-)

xle...@qmailcomq.com

unread,
Apr 26, 2002, 2:31:54 PM4/26/02
to
Alexander Terekhov <tere...@web.de> wrote:

>> > > See: http://groups.google.com/groups?as_umsgid=3CBC5F22.76708D0%40web.de
>>
>> [..."Okay... ENOUGH active exceptions! ;-)"...]
>>
>> > Cute, although an "abuse" of 15.2.3.
>>
>> Uhmm, why/what "abuse"?

Because for me it looks like the standard does not "realize" the possibility
of nested exceptions: first, because there is no mention of possibility
of nested stack unwinding, even in a Note; and second,
because uncaught_exception() does return bool and not unsigned.
I quoted the word "abuse", because the standard does not go as
far as directing all exceptions during stack unwinding to terminate()
outright, so your example is not the abuse of its letter, just of its spirit.



>> I think that it would NOT hurt to have something like that...
>> but "bool unwinding(obj_ptr)" + "bool expected_ex<ex_t>()" +
>> "propagating_ex_ptr_or_ref< ex_t >()" + post-/pre- c-/d-tors"
>> would provide MUCH MORE value/"simplify" a lot of things quite
>> considerably, I believe. ;-)

> Heck! Why not: (my "8 o'clock"-wish list; just ideas ;-))

> 1. size_t "unwinding(obj_ptr)" (template/macro) -> 0/scope;

Where should the unwinding scope count stop? At the first non-unwinding
dtor, or not?

> 2. size_t uncaught_exception_scope() -> 0/scope;

> 3. "uncaught_ex_ptr_or_ref<T>(size_t scope = 0)" (ref.version throws)
> ("scope = 0" means "current"; in effect: uncaught_exception_scope());

What is the return type of it?

> 4. "bool expected_exception<T>()";

How do you check the current exception, the type of which you do not know
statically, against it?

> 5. "post-/pre- c-/d-tors";

Wouldn't an additional level of object wrapping give you the same?

This is probably too much work implementation-wise.

> 7. fix unneeded/harmful "unwinding" problems
> (Ex.specs/"implementation-defined",etc.);

> 8. deprecate uncaught_exception().

> Any objections or am I missing something, folks? ;-) ;-)

I believe that functions with a typeid/type_info argument
would be better than templated functions (or a good addition to them);
I want typeid(thrown_object) in catch(...).

Leo

Alexander Terekhov

unread,
Apr 26, 2002, 2:32:24 PM4/26/02
to

xle...@qmailcomq.com wrote:
[...]

> Leo
> missing typeid(exception) in catch(...).

Perhaps this could help:

void oper(); // could throw anything... incl. "SIGSEGV"-like stuff!

void checked_oper() throw(A,B,C) // etc. -- all EXPECTED/GOOD ones
{ oper(); }

/**/
try {

checked_oper();

}
catch( ... ) {

handleKnownExpectedExceptions( /**/ );

}
/**/

void handleKnownExpectedExceptions( /**/ )
{
try { throw; }
catch( const A& a ) { /**/ }
catch( const B& b ) { /**/ }
catch( const C& c ) { /**/ }
catch( ... ) { /* all others (if any) */ }
}

Or do you mean something (as it seems to me)
LESS weird and MORE "generic" from future_std
namespace: ;-)

(missing items from my new "10 o'clock"-wish list; just ideas ;-))

9. size_t caught_exception_scope() -> 0/scope;
("throw;" -- re-throw is OK "if (caught_exception_scope())")

10. "caught_ex_ptr_or_ref<T>(size_t scope = 0)" (ref.version throws)
("scope = 0" means "current"; in effect: caught_exception_scope());

Any comments (other than "Huh? Why do you need this?" -- I
don't really know it myself currently; perhaps "just for"
symmetry w.r.t. uncaught_blah_blah stuff), folks? ;-) ;-)

regards,
alexander.

---

Dave Harris

unread,
Apr 26, 2002, 3:36:06 PM4/26/02
to
tere...@web.de (Alexander Terekhov) wrote (abridged):

> How about ( another 0.01 of "serious proposal" ;-)):
> [post-destructor]

That would probably satisfy the efficiency concern. However,
post-destructors are a new feature which make the language more
complicated and harder to understand.

I see allowing destructors to take arguments as making the language more
uniform. Every other kind of function can take arguments; destructors are
currently a special case. It is an arbitrary and unnecessary limitation.
To me, getting rid of that limitation makes the language simpler and
easier to teach.

That it has practical uses is a bonus :-)

Dave Harris, Nottingham, UK | "Weave a circle round him thrice,
bran...@cix.co.uk | And close your eyes with holy dread,
| For he on honey dew hath fed
http://www.bhresearch.co.uk/ | And drunk the milk of Paradise."

---

Dave Harris

unread,
Apr 27, 2002, 8:16:31 PM4/27/02
to
xle...@qmailcomq.com () wrote (abridged):

> > Perhaps you are thinking that this could be done through an instance
> > variable. That would work, but would add a memory cost to every
> > instance of the class.
>
> Not necessarily. static std::set<Base*> Base::UnregisterSet would do,

If I understand you correctly (and your comments are rather enigmatic),
that would merely move the memory overhead to another place, bloat it
further and add some speed overhead too. All to recover some information
which was already known at compile-time.

Dave Harris, Nottingham, UK | "Weave a circle round him thrice,
bran...@cix.co.uk | And close your eyes with holy dread,
| For he on honey dew hath fed
http://www.bhresearch.co.uk/ | And drunk the milk of Paradise."

---

Alexander Terekhov

unread,
Apr 27, 2002, 8:17:18 PM4/27/02
to

xle...@qmailcomq.com wrote:
[...]

> > 1. size_t "unwinding(obj_ptr)" (template/macro) -> 0/scope;
>
> Where should the unwinding scope count stop? At the first non-unwinding
> dtor, or not?

On corresponding *catch handler* entry, which will actually
create another *caught exception scope* (vs. "uncaught" or
"unwinding" scope).

> > 2. size_t uncaught_exception_scope() -> 0/scope;
>
> > 3. "uncaught_ex_ptr_or_ref<T>(size_t scope = 0)" (ref.version throws)
> > ("scope = 0" means "current"; in effect: uncaught_exception_scope());
>
> What is the return type of it?

T pointer or reference (not sure about "const" vs. "non-const").

> > 4. "bool expected_exception<T>()";
>
> How do you check the current exception, the type of which you do not know
> statically, against it?
>
> > 5. "post-/pre- c-/d-tors";
>
> Wouldn't an additional level of object wrapping give you the same?

Well, sort-of-yes (using factories/"final" wrappers, etc.) But, IMO
it would MUCH simpler and really convenient for *various* things, to
have a second post-construction and pre-destruction invocation chain
(of post-constructors and pre-destructors) performed automatically
(if post/pre- stuff is defined) by the implementation; including
"proper" exception "handling", BTW (I mean e.g. if post-constructor
throws then the whole object should be "eliminated"/cleaned up via
corresponding (if any) pre-destructor(s) first and "normal"
destruction phase next, etc.).

> > 6. "local context" templates:
> > http://groups.google.com/groups?selm=3CC4733A.822D7A07%40web.de
>
> This is probably too much work implementation-wise.

Maybe. Don't know.

regards,
alexander.

xle...@qmailcomq.com

unread,
Apr 29, 2002, 7:30:59 AM4/29/02
to
Dave Harris <bran...@cix.co.uk> wrote:
>> > Perhaps you are thinking that this could be done through an instance
>> > variable. That would work, but would add a memory cost to every
>> > instance of the class.
>>
>> Not necessarily. static std::set<Base*> Base::UnregisterSet would do,

> If I understand you correctly (and your comments are rather enigmatic),
> that would merely move the memory overhead to another place, bloat it
> further and add some speed overhead too. All to recover some information

I merely demonstrated that the "per-instance" memory overhead can be avoided.

> which was already known at compile-time.

I agree completely that if dtors are allowed to have arguments,
the implementation will be more efficient. But as the desired functionality
can be _easily_ achieved using existing means, and with different
memory/speed tradeoffs, the pressure to have the feature
in the language is less than for a feature that is more sorely missed.

Leo

Michiel Salters

unread,
Apr 29, 2002, 7:31:25 AM4/29/02
to
Alexander Terekhov <tere...@web.de> wrote in message news:<3CCA9C3B...@web.de>...
> xle...@qmailcomq.com wrote:

[ SNIP ]

> > > 5. "post-/pre- c-/d-tors";
> >
> > Wouldn't an additional level of object wrapping give you the same?
>
> Well, sort-of-yes (using factories/"final" wrappers, etc.) But, IMO
> it would MUCH simpler and really convenient for *various* things, to
> have a second post-construction and pre-destruction invocation chain
> (of post-constructors and pre-destructors) performed automatically
> (if post/pre- stuff is defined) by the implementation; including
> "proper" exception "handling", BTW (I mean e.g. if post-constructor
> throws then the whole object should be "eliminated"/cleaned up via
> corresponding (if any) pre-destructor(s) first and "normal"
> destruction phase next, etc.).

Using a wrapper<T> : public T construct, you can have your cake and
eat it too. wrapper<T>::wrapper is a post-ctor for T::T, and the dtor
works too. In addition, throwing does the logical thing and does NOT
call the wrapper dtor ( because the wrapper ctor didn't finish ), but
it does call T::~T. In addition, wrapper<T> IS-A T, so they can be
used interchangably.

Now, why is this more convenient? Because it works with existing
compilers, doesn't introduce additional special functions, it
can be used to implement post-post-ctors, and you can have multiple
wrappers of a single base class.

This is more than theory; in my current project a number of classes
use a wrapper to start and stop their thread_main. It's easy.

Regards,
--
Michiel Salters

Nicola Musatti

unread,
Apr 29, 2002, 7:33:18 AM4/29/02
to

xle...@qmailcomq.com wrote:
>
> Nicola Musatti <obje...@divalsim.it> wrote:

[...]


> >> > ~Transaction(std::exception & e) { rollback(); }
> >> > };
> >>
> >> See 15.5.3 [except.uncaught]
>
> > I'm aware of this [i.e. std::uncaught_exception()], but I consider the
> > proposal above both more powerful and more elegant. Actually this idea
>
> If the standard requires all thrown objects to derive from std::exception,
> maybe. But I do not believe it ever will.

Sorry, I put std::exception in my example, but I would actually allow
overloading of destructors on any type, so that the best fit according
to overload resolution would be called or the standard destructor if no
other destructor matches.

> > was fired by a comment by Andrei Alexandrescu on c.l.c++.m about how
> > objects and error handling apparently don't mix well.
>
> ....don't mix well in C++ as defined by the current standard, I presume?
> I believe the current situation is a (more or less) reasonable tradeoff
> between flexibility and implementability

I may be wrong, but I believe that compilers already have to generate
code to perform stack unwinding. The only required changes would be to
choose the correct destructor and to pass the current exception as an
argument. I don't believe this to pose any problem of implementability.

> like std::uncaught_exception() returning bool instead of something
> more useful, like
> a) type_info of the object being thrown,
> so that the destructor may choose to compare it to relevant typeids and
> retrieve the object being thrown type-safely using some other
> (templated, as Alexander Terekhov proposed) function

A rather clumsy and error prone mechanism, if you ask me.

Cheers,
Nicola Musatti

Nicola Musatti

unread,
Apr 29, 2002, 7:44:12 AM4/29/02
to

Sean Parent wrote:
[...]


> I would like to extend the original proposal to the following:
>
> class Transaction
> {
> public:
> ~Transaction() { commit(); }
> catch ~Transaction(std::exception &e) { rollback(); }
> catch ~Transaction(...) { rollback(); }
> };

Unless you are proposing an alternative behaviour for something like

~Transaction(std::exception &e) { rollback(); }

the "catch" keyword is not necessary.

> Within a catch destructor the exception is considered "caught". Returning
> from the destructor will call throw to re-throw the current exception. The
> exception can also be rethrown (using throw) from within the destructor, or
> a new exception can be thrown.

My attitude is more conservative: I don't believe that the semantics of
stack unwinding should be modified except for the fact that the
destructor becomes capable of accessing the exception object which
caused its execution.

By the way, I see how the ellipsis version could be useful, but I don't
think I would allow it. First, because it would overload the ellipsis
with a new meaning in the context of function call arguments, which I
don't think is a good thing. Second, because I feel that programmers
using this mechanism should at least make sure they know what they're
throwing.

> Which destructor to invoke is determined in
> the same way which catch clause to execute is. First match wins.

I think I'd use the rules for overload resolution instead, so as not to
make destructors too different from other functions.



> The case I would like to use this for is to "decorate" exception objects
> with context information about what is currently happening.

Exactly.

Cheers,
Nicola Musatti

Nicola Musatti

unread,
Apr 29, 2002, 7:44:21 AM4/29/02
to

Alexander Terekhov wrote:
[...]


> Any objections or am I missing something, folks? ;-) ;-)

I disagree in principle with your proposals. Requiring a very extensive
API to interact with exception handling / stack unwinding is in my
opinion missing the point. The things you describe could be achieved
much more easily by completely forsaking exceptions and returning class
instances from functions instead.

Cheers,
Nicola Musatti

Alexander Terekhov

unread,
Apr 29, 2002, 12:57:49 PM4/29/02
to

Dave Harris wrote:
>
> tere...@web.de (Alexander Terekhov) wrote (abridged):
> > How about ( another 0.01 of "serious proposal" ;-)):
> > [post-destructor]
>
> That would probably satisfy the efficiency concern. However,
> post-destructors

Nah, *pre-destructor* (and *post-constructor*).

> are a new feature which make the language more
> complicated and harder to understand.

Absolutely *NOT TRUE*, I believe. On the contrary,
the *current* way of just "lying" w.r.t. polymorphic
behavior (vf/ti/dc) of objects under construction
and destruction is "complicated and harder to
understand" and REALLY ERROR-PRONE, to begin with,
IMHO. If anything, post-/pre stuff would partly address
THAT "problem" (and make many other things "simple",
easy to maintain, etc.[1]), I believe strongly.

regards,
alexander.

[1] For example:

http://www.cs.umd.edu/~pugh/java/memoryModel/archive/1038.html
http://www.cs.umd.edu/~pugh/java/memoryModel/archive/1040.html
(see "or, even better: g) ....."

xle...@qmailcomq.com

unread,
Apr 29, 2002, 2:28:38 PM4/29/02
to
Alexander Terekhov <tere...@web.de> wrote:

> xle...@qmailcomq.com wrote:
> [...]
>> Leo
>> missing typeid(exception) in catch(...).

> Perhaps this could help:

[skipped, it would not help]

> Or do you mean something (as it seems to me)
> LESS weird and MORE "generic" from future_std
> namespace: ;-)

What I want is simply something like

catch(...) {
cerr << "An unexpected exception with typeID " <<
exception_type_info_stack.top.name() << " has been caught\n";
}

Or, in the least, to pass the type_info into unexpected() and
terminate().

> (missing items from my new "10 o'clock"-wish list; just ideas ;-))
>
> 9. size_t caught_exception_scope() -> 0/scope;
> ("throw;" -- re-throw is OK "if (caught_exception_scope())")

> 10. "caught_ex_ptr_or_ref<T>(size_t scope = 0)" (ref.version throws)
> ("scope = 0" means "current"; in effect: caught_exception_scope());

Let's separate concepts from the implementation and reformulate the wishes:

1. Given a static type T or a type_info (that's my wish) of type T,
a way exists to find out whether throwing an instance of T from the
current dynamic scope will be caught, or result in unexpected(),
or in terminate().

2. At any point in the program the stack of in-flight exception
objects, including their typeids, is available for inspection
(and the stack top - for modification, with some restrictions).

3. At any point in the program the stack of object pointers for which
the unwind dtors are currently being called is available for inspection.

I bet I missed something for which pre-ctors and post-dtors are needed.

Leo

Nicola Musatti

unread,
Apr 29, 2002, 2:30:00 PM4/29/02
to

Pavel Kuznetsov wrote:
[...]


> I like this. I am just curious how to interpret destructors
> with several arguments, especially if proposed extention
> applies to ellipsis destructors.

In the specific context of stack unwinding I don't believe that
destructors with multiple arguments should be allowed, because they
wouldn't make sense. On the other hand if you accept Dave Harris's
proposal from another subthread, multiple argument destructors could be
used in explicit calls and in "finalization" lists, e.g.:

~Derived::Derived() : ~Base(someArgument) {}

Cheers,
Nicola Musatti

Sean Parent

unread,
Apr 29, 2002, 2:30:17 PM4/29/02
to
in article 3CCD2EB3...@divalsim.it, Nicola Musatti at
obje...@divalsim.it wrote on 4/29/02 4:44 AM:

> My attitude is more conservative: I don't believe that the semantics of
> stack unwinding should be modified except for the fact that the
> destructor becomes capable of accessing the exception object which
> caused its execution.

>> The case I would like to use this for is to "decorate" exception objects


>> with context information about what is currently happening.
>
> Exactly.

I think access to the exception should include being able to modify the
exception. You agree with the goal, but if there isn't a way to decorate an
exception that is in-flight if you can't throw a new exception. Without
this, you are forced to use try / catch to achieve this behavior. That is
the equivalent of forcing people to use try / catch for managing resources.
Ideally, try / catch is only used in cases where the propagation of they
exception may actually be halted.

I also think uncaught_exception() should be deprecated from the language
unless someone can tell me what useful information it provides.

It does not tell if a particular object is being unwound so it isn't useful
for commit / rollback transaction handling.

It does not tell you if you can call throw naked, in order to decode an
exception.

Changing the result to an int really doesn't provide any additional useful
information. Some of the information that Alexander is proposing I can see
being useful - although I'm not convinced of it's safety (and he seems not
to be convinced of the safety of mine).

In suedo code - my proposal would be the equivalent of:

{
Object object;
try
{
// Do stuff.
}
catch (ExeptionType value)
{
object.~Object(value);
throw;
}
object.~Object();
}

It seems quite safe and flexible. A catching destructor could never be hit
for a heap allocated object, because heap allocated objects are not unwound.

Sean


--
Sean Parent
Sr. Computer Scientist II
Advanced Technology Group
Adobe Systems Incorporated
spa...@adobe.com

Nicola Musatti

unread,
Apr 29, 2002, 2:30:07 PM4/29/02
to

Dave Harris wrote:
[...]


> I see allowing destructors to take arguments as making the language more
> uniform. Every other kind of function can take arguments; destructors are
> currently a special case. It is an arbitrary and unnecessary limitation.
> To me, getting rid of that limitation makes the language simpler and
> easier to teach.

To me it's more a question of moving the distinction elsewhere. In
general you don't explicitly call destructors, so your mechanism is not
(= cannot be) general. You could provide arguments in your
pseudo-initialization list or in an explicit call following placement
new, but not in the general case. I cannot see how this makes the
language easier to teach.

Cheers,
Nicola Musatti

Sean Parent

unread,
Apr 29, 2002, 2:33:17 PM4/29/02
to
in article 3CCD2EB3...@divalsim.it, Nicola Musatti at
obje...@divalsim.it wrote on 4/29/02 4:44 AM:

> My attitude is more conservative: I don't believe that the semantics of


> stack unwinding should be modified except for the fact that the
> destructor becomes capable of accessing the exception object which
> caused its execution.

>> The case I would like to use this for is to "decorate" exception objects


>> with context information about what is currently happening.
>
> Exactly.

I think access to the exception should include being able to modify the

Sean

---

Dave Harris

unread,
Apr 30, 2002, 2:43:46 PM4/30/02
to
obje...@divalsim.it (Nicola Musatti) wrote (abridged):

> To me it's more a question of moving the distinction elsewhere. In
> general you don't explicitly call destructors, so your mechanism is not
> (= cannot be) general. You could provide arguments in your
> pseudo-initialization list or in an explicit call following placement
> new, but not in the general case. I cannot see how this makes the
> language easier to teach.

It's similar to passing extra arguments to operator++(). If we do ++x,
then there is no way to pass an argument, and if we do x++, an argument is
passed but we can't control what it is. However, if we write
x.operator++() we can put an argument in the brackets. (I don't recall
whether we can pass more than one; I hope we can.)

In the same way, the "delete p" syntax doesn't have a space for arguments,
but p->~X() does. I don't think this is difficult to teach because it is
kinda obvious from the syntactic form whether an argument-list is
possible.

To put it another way, I see "delete x" as not having an argument list,
and p->~X() has having an empty argument list. Whenever an empty argument
list is allowed, a non-empty one should also be allowed.

Dave Harris, Nottingham, UK | "Weave a circle round him thrice,
bran...@cix.co.uk | And close your eyes with holy dread,
| For he on honey dew hath fed
http://www.bhresearch.co.uk/ | And drunk the milk of Paradise."

---

Alexander Terekhov

unread,
Apr 30, 2002, 2:51:34 PM4/30/02
to

xle...@qmailcomq.com wrote:
>
> Alexander Terekhov <tere...@web.de> wrote:
>
> > xle...@qmailcomq.com wrote:
> > [...]
> >> Leo
> >> missing typeid(exception) in catch(...).
>
> > Perhaps this could help:
>
> [skipped, it would not help]
>
> > Or do you mean something (as it seems to me)
> > LESS weird and MORE "generic" from future_std
> > namespace: ;-)
>
> What I want is simply something like
>
> catch(...) {
> cerr << "An unexpected exception with typeID " <<
> exception_type_info_stack.top.name() << " has been caught\n";
> }
>
> Or, in the least, to pass the type_info into unexpected() and
> terminate().

Why do you need this (is this really better than complete
core-/crash-dump-like things recorded at throw point)?

> > (missing items from my new "10 o'clock"-wish list; just ideas ;-))
> >
> > 9. size_t caught_exception_scope() -> 0/scope;
> > ("throw;" -- re-throw is OK "if (caught_exception_scope())")
>
> > 10. "caught_ex_ptr_or_ref<T>(size_t scope = 0)" (ref.version throws)
> > ("scope = 0" means "current"; in effect: caught_exception_scope());
>
> Let's separate concepts from the implementation and reformulate the wishes:

OK.

> 1. Given a static type T or a type_info (that's my wish) of type T,
> a way exists to find out whether throwing an instance of T from the
> current dynamic scope will be caught, or result in unexpected(),
> or in terminate().

Yep.

> 2. At any point in the program the stack of in-flight exception
> objects, including their typeids, is available for inspection
> (and the stack top - for modification, with some restrictions).

Yup, but why "stack top" only?

Also, I'd add:

2a. At any point in the program the stack of CAUGHT exception


objects, including their typeids, is available for inspection

(?and modification?).

> 3. At any point in the program the stack of object pointers for which
> the unwind dtors are currently being called is available for inspection.

Yep.

> I bet I missed something for which pre-ctors and post-dtors are needed.

Basically the idea is that *each* class in the hierarchy
of the most-derived (i.e. dynamic-type, the one we could
instantiate) object can *optionally* have ONE post-ctor
and *optionally* ONE pre-dtor. On construction, after the
usual constructor invocation chain, post-constructors
should fire (in the same order as constructor *bodies*;
base classes first); THEY could then freely publish this,
(without any danger that some vf. callback could hit an
"incomplete" object, start/join threads, etc) use/call "this"
virtuals themself, query typeid/dynamic_cast -- FULLY use
the polymorphic behavior. On destruction phase, basically
"the same" stuff, but pre-destructors *first* (prior to
destructors and in the same order).

Also, I'd add:

4. Make RAII even less cumbersome; heck, why do we need to pass
ANYTHING to the "do/undo" local RAII objects -- everything is in
the local context already! So, why not just make it a-sort-of-
template thing?!

5. It seems to me that Sean needs something to "intercept"
exceptions in the d-tors. AFAICT, his catching (re-throw or
throw-something-else) d-tors DO INTERCEPT exceptions
(throw-something-else case) AND/OR even prompt unneeded/harmful
unwinding (e.g. re-throw case with no "real" catch handler
present at all) via "injecting" implicit "catch" clauses.
Or am I again missing something, Sean? Well, if someone really
need it, I'd probaly do it via yet another "replacing_throw
throw-expression;" sort-of new thing (to explicitly replace
some *expected* exception with a new one; completelly get rid
of old "uncaught"-but-expected one).

regards,
alexander.

Nicola Musatti

unread,
Apr 30, 2002, 2:51:42 PM4/30/02
to

Sean Parent wrote:
[...]


> I think access to the exception should include being able to modify the
> exception. You agree with the goal, but if there isn't a way to decorate an
> exception that is in-flight if you can't throw a new exception. Without
> this, you are forced to use try / catch to achieve this behavior. That is
> the equivalent of forcing people to use try / catch for managing resources.
> Ideally, try / catch is only used in cases where the propagation of they
> exception may actually be halted.

It may not be evident in my initial example, but the main reason for my
proposal is actually to allow piggybacking of additional information
onto the current active exception from within a destructor called during
stack unwinding.

> I also think uncaught_exception() should be deprecated from the language
> unless someone can tell me what useful information it provides.
>
> It does not tell if a particular object is being unwound so it isn't useful
> for commit / rollback transaction handling.
>
> It does not tell you if you can call throw naked, in order to decode an
> exception.

My point is actually that you shouldn't need to do these things; I can't
help feeling that these manipulations are just clumsy workarounds for
badly designed programs. The solution to these problems should lie in
architecture, not in trying to find out what happened after the fact.

The only problem I personally have with the current try/catch mechanism
is that it makes it somewhat hard to build reusable error handling
solutions, due to its inherent stack oriented locality, and it is
towards solving this problem that I presented my proposal. By the way, I
feel that your example below illustrates my issues very well :-)

Consider:

try {
start_transaction();
// ...
commit();
}
catch (ExceptionA & a) {
rollback();
// do something appropriate to ExceptionA
}
catch (ExceptionB & b) {
rollback();
// do something appropriate to ExceptionB
}
catch (...) {
rollback();
}

Written in this way you have to repeat rollback in each catch block; not
only that but if you have to catch the same exception types and you want
to handle them exactly as you did here, but the transaction is
different, you have to either essentially repeat the code above or to
write a rather complicated wrapper.

One solution to this problem could be the "throw in the try block"
idiom, even though I'm not sure whether it's really supposed to work:

try {
start_transaction();
commit();
}
catch(...) {
rollback();
handle_exceptions();
}

where

void handle_exceptions() {
try {
throw;
}
catch (ExceptionA & a) {
// do something appropriate to ExceptionA
}
catch (ExceptionB & b) {
// do something appropriate to ExceptionB
}
catch (...) {
// is there anything you can do here?
}
}

[...]


>
> {
> Object object;
> try
> {
> // Do stuff.
> }
> catch (ExeptionType value)
> {
> object.~Object(value);
> throw;
> }
> object.~Object();
> }

As to your proposal, it's not clear to me whether you propose that
destructors be called explicitly. If you don't our proposals are
actually identical except for minor syntactic details. If you do I
consider mine better :-)

Cheers,
Nicola Musatti

Hillel Y. Sims

unread,
May 1, 2002, 1:13:52 PM5/1/02
to

<xle...@qmailcomq.com> wrote in message
news:Lxly8.7185$T_.1...@iad-read.news.verio.net...

>
> catch(...) {
> cerr << "An unexpected exception with typeID " <<
> exception_type_info_stack.top.name() << " has been caught\n";
> }

Doesn't this require the type to have a vtable in order to pull out RTTI
information? "catch(...)" may catch types that do not have vtables.

>
> Or, in the least, to pass the type_info into unexpected() and
> terminate().

(same as above)

>
> 1. Given a static type T or a type_info (that's my wish) of type T,
> a way exists to find out whether throwing an instance of T from the
> current dynamic scope will be caught, or result in unexpected(),
> or in terminate().

If static type checking, wouldn't that just imply compile-time ES checking?
(which might be cool, sure) If dynamic checking, see above.

>
> 2. At any point in the program the stack of in-flight exception
> objects, including their typeids, is available for inspection
> (and the stack top - for modification, with some restrictions).

I think there is really sort of only one current exception at any time
(stack frame). Alexander Terekhov's trick of recursive throw/catch seems to
show multiple exceptions active at one time, but (someone called it "abuse"
of rules) I think there's still only really sort of one "active" at each
stage -- if it ever escaped to an outer layer where the other exception was
already active, the program would terminate. So c++ only wants to deal with
one outstanding exception per stack frame (nesting is ok, but can't see
outer layers)

>
> 3. At any point in the program the stack of object pointers for which
> the unwind dtors are currently being called is available for inspection.

??

hys

--
Hillel Y. Sims
hsims AT factset.com

xle...@qmailcomq.com

unread,
May 1, 2002, 5:08:13 PM5/1/02
to
Hillel Y. Sims <use...@phatbasset.com> wrote:

>> catch(...) {
>> cerr << "An unexpected exception with typeID " <<
>> exception_type_info_stack.top.name() << " has been caught\n";
>> }

> Doesn't this require the type to have a vtable in order to pull out RTTI
> information? "catch(...)" may catch types that do not have vtables.

Right... And how does that work, exactly?

>> 1. Given a static type T or a type_info (that's my wish) of type T,
>> a way exists to find out whether throwing an instance of T from the
>> current dynamic scope will be caught, or result in unexpected(),
>> or in terminate().

> If static type checking, wouldn't that just imply compile-time ES checking?
> (which might be cool, sure) If dynamic checking, see above.

The compiler may be allowed to optimize the check, but in general
even the static type checking cannot be done across compilation units.

>> 2. At any point in the program the stack of in-flight exception
>> objects, including their typeids, is available for inspection
>> (and the stack top - for modification, with some restrictions).

> I think there is really sort of only one current exception at any time
> (stack frame). Alexander Terekhov's trick of recursive throw/catch seems to
> show multiple exceptions active at one time, but (someone called it "abuse"

That was me.

> of rules) I think there's still only really sort of one "active" at each
> stage -- if it ever escaped to an outer layer where the other exception was

Right, but there may still be more than one in-flight _exception objects_,
as demonstrated by the example.

> already active, the program would terminate. So c++ only wants to deal with
> one outstanding exception per stack frame (nesting is ok, but can't see
> outer layers)

It may be argued one way or another; I tried to reformulate Alexander's
wishes in a more abstract form.

>> 3. At any point in the program the stack of object pointers for which
>> the unwind dtors are currently being called is available for inspection.

> ??

So a dtor or a function called from it is able to find out whether the
dtor is called as part of stack unwinding (and if yes, whether it is
the top-level unwinding or a nested unwinding - rollbacks may fail as well,
right?)

Leo

xle...@qmailcomq.com

unread,
May 1, 2002, 6:59:08 PM5/1/02
to
Nicola Musatti <obje...@divalsim.it> wrote:

> One solution to this problem could be the "throw in the try block"
> idiom, even though I'm not sure whether it's really supposed to work:

> try {
> start_transaction();
> commit();
> }
> catch(...) {
> rollback();
> handle_exceptions();
> }

> where

> void handle_exceptions() {
> try {
> throw;
> }
> catch (ExceptionA & a) {
> // do something appropriate to ExceptionA
> }
> catch (ExceptionB & b) {
> // do something appropriate to ExceptionB
> }
> catch (...) {
> // is there anything you can do here?
> }
> }

It is supposed to work by the lack of a mention otherwise in the standard,
and it does work.

Leo

xle...@qmailcomq.com

unread,
May 2, 2002, 1:07:14 PM5/2/02
to
Alexander Terekhov <tere...@web.de> wrote:

>> What I want is simply something like
>>
>> catch(...) {
>> cerr << "An unexpected exception with typeID " <<
>> exception_type_info_stack.top.name() << " has been caught\n";
>> }
>>
>> Or, in the least, to pass the type_info into unexpected() and
>> terminate().

> Why do you need this (is this really better than complete
> core-/crash-dump-like things recorded at throw point)?

You never know what the 3rd-party/legacy software might do. In any case,
if the information is present, it should be possible to display it.

>> 2. At any point in the program the stack of in-flight exception
>> objects, including their typeids, is available for inspection
>> (and the stack top - for modification, with some restrictions).

> Yup, but why "stack top" only?

So that the type of an exception does not change as a result of handling
of _another_, nested, exception.

> Also, I'd add:

> 2a. At any point in the program the stack of CAUGHT exception
> objects, including their typeids, is available for inspection
> (?and modification?).

Right, although most of the functionality (except typeids in case of ...)
can be constructed using existing means.

>> I bet I missed something for which pre-ctors and post-dtors are needed.

> Basically the idea is that *each* class in the hierarchy
> of the most-derived (i.e. dynamic-type, the one we could
> instantiate) object can *optionally* have ONE post-ctor
> and *optionally* ONE pre-dtor. On construction, after the
> usual constructor invocation chain, post-constructors
> should fire (in the same order as constructor *bodies*;
> base classes first); THEY could then freely publish this,

What's wrong with

class C : public B {
C(...) {
...
finalize();
}
void finalize() {
B::finalize();
// whatever
}
}

Except that the call to finalize() must be put manually into all the
ctors, and the order of calls to base class(es) in finalize() must
be manually synchronized with the hierarchy.

> (without any danger that some vf. callback could hit an
> "incomplete" object, start/join threads, etc) use/call "this"
> virtuals themself, query typeid/dynamic_cast -- FULLY use
> the polymorphic behavior.

This can be done after the opening brace of the most derived constructor.
Or is there a catch?

> On destruction phase, basically
> "the same" stuff, but pre-destructors *first* (prior to
> destructors and in the same order).

Same here. Minor inconvenience compared to other things.

Leo

Alexander Terekhov

unread,
May 2, 2002, 1:07:27 PM5/2/02
to

"Hillel Y. Sims" wrote:

[...RTTI/vtables...]

> > 1. Given a static type T or a type_info (that's my wish) of type T,
> > a way exists to find out whether throwing an instance of T from the
> > current dynamic scope will be caught, or result in unexpected(),
> > or in terminate().
>
> If static type checking,

No; "dynamic" checking.

> wouldn't that just imply compile-time ES checking?

Compile{-link}-time ES checking is completely different
"beast". It could indeed warn/help w.r.t. perhaps "missing"
catch-clauses, but that would NOT mean that ALL possibly
thrown exceptions should ALWAYS be caught -- there always
be UNEXPECTED exceptions only good (if thrown) to produce
a nice "core-dumped" message.

> (which might be cool, sure) If dynamic checking, see above.

See above what (what does this have to do with RTTI/vtables)?

> > 2. At any point in the program the stack of in-flight exception
> > objects, including their typeids, is available for inspection
> > (and the stack top - for modification, with some restrictions).
>
> I think there is really sort of only one current exception at any time
> (stack frame). Alexander Terekhov's trick of recursive throw/catch seems to
> show multiple exceptions active at one time, but (someone called it "abuse"
> of rules) I think there's still only really sort of one "active" at each
> stage -- if it ever escaped to an outer layer where the other exception was
> already active, the program would terminate. So c++ only wants to deal with
> one outstanding exception per stack frame (nesting is ok, but can't see
> outer layers)

Nobody's been arguing that C++ should want to deal
with multiple-all-flying-together exceptions! I've
been "arguing" all along that C++ should deal with
(read: provide access means):

1. <thread-specific> SINGLE STACK of exceptions [a) and b)
COULD even be interleaved]:

a) uncaught (in-flight) ones;

b) caught-but-still-active (in the
sense of being re-throwable).

2. <thread-specific> SINGLE STACK of objects under destruction due
to unwinding caused by ALL those in-flight exceptions.

3. Mechanism for dynamic exception checking (expected_exception<T>()).

4. Better/easier to use local context RAII objects.

5. Better/easier to use means to exploit TRUE polymorphic behavior
"under" construction and destruction -- pre/post stuff.

6. Get rid of USELESS and JUST-CONFUSING std::uncaught_exception().

> > 3. At any point in the program the stack of object pointers for which
> > the unwind dtors are currently being called is available for inspection.
>
> ??

see above (2.). ;-)

regards,
alexander.

Pavel Kuznetsov

unread,
May 2, 2002, 4:06:13 PM5/2/02
to
Nicola Musatti (obje...@divalsim.it) wrote:

NM> In the specific context of stack unwinding I don't believe that
NM> destructors with multiple arguments should be allowed, because
NM> they wouldn't make sense.

There could be an idea that destructor with multiple parameters
is intended for handling any of exceptions of listed in parameter
list. And ellipsis destructor is perfectly fitting here.

NM> On the other hand if you accept Dave Harris's proposal from
NM> another subthread, multiple argument destructors could be
NM> used in explicit calls and in "finalization" lists, e.g.:

NM> ~Derived::Derived() : ~Base(someArgument) {}

It seems that those proposals are not mixing well.
Am I wrong about that?

--
Pavel

John Nagle

unread,
May 6, 2002, 3:53:27 AM5/6/02
to
The real issue here is that a destructor can't
assume that the class invariant is true.

John Nagle
Animats

xle...@qmailcomq.com

unread,
May 7, 2002, 11:31:57 PM5/7/02
to
John Nagle <na...@animats.com> wrote:
> The real issue here is that a destructor can't

Which class' destructor? The base one, or the derived one?

> assume that the class invariant is true.

The class invariant does not get violated by the mere entry into the
destructor, does it? And if it does not, a call to the finalizer
can be inserted at the beginning of the dtor's code.

Leo

Robert Klemme

unread,
May 8, 2002, 12:08:30 PM5/8/02
to

how about:

class Transaction {
bool success;
public:
Transaction() : success(false) { open(); }
~Transaction() { if ( succes ) commit(); else rollback(); }

void setSuccess(bool s) { success = s; }
};


void foo() throws ArbitraryExceptions {
Transactin t;

// do stuff that can throw ArbitraryExceptions
t.setSuccess( true );

// automatic destruction doing commit() or rollback()
}

regards

robert

--
Robert Klemme
software engineer
--------------------------------------------------------------------------------
myview technologies GmbH & Co. KG
Lindberghring 1 ~ 33142 Büren ~ Germany
Fon: +49-2955-7433-20 Fax: +49-2955-7433-99
--------------------------------------------------------------------------------

Nicola Musatti

unread,
May 9, 2002, 2:45:09 PM5/9/02
to

Robert Klemme wrote:
>
> how about:
>
> class Transaction {
> bool success;
> public:
> Transaction() : success(false) { open(); }
> ~Transaction() { if ( succes ) commit(); else rollback(); }
>
> void setSuccess(bool s) { success = s; }
> };
>
> void foo() throws ArbitraryExceptions {
> Transactin t;
>
> // do stuff that can throw ArbitraryExceptions
> t.setSuccess( true );
>
> // automatic destruction doing commit() or rollback()
> }

This obviously works and there are different similar solution;
personally I think I'd prefer an explicit commit() with a possibly
implicit rollback(). Commit's success is marked as you did with a
boolean member, to be checked before rolling back in the destructor.

I realize that my example does not really convey the motivation for my
proposal, that is to provide an object oriented approach to exception
handling. The advantages would be a more generalized form of RAII,
capable of handling errors and not just being error neutral, and easier
factoring of generic aspects of error handling, like augmenting
exceptions with local context information.

Cheers,
Nicola Musatti

Robert Klemme

unread,
May 10, 2002, 11:26:47 AM5/10/02
to

Nicola Musatti schrieb:


> This obviously works and there are different similar solution;

thanks.

> I realize that my example does not really convey the motivation for my
> proposal, that is to provide an object oriented approach to exception
> handling. The advantages would be a more generalized form of RAII,
> capable of handling errors and not just being error neutral, and easier
> factoring of generic aspects of error handling, like augmenting
> exceptions with local context information.

since i currently do not have a clear idea what might be the
benefits of what you're proposing: would you mind coming up with
a more adequate example? thank you!

regards

robert

--
Robert Klemme
software engineer
--------------------------------------------------------------------------------
myview technologies GmbH & Co. KG
Lindberghring 1 ~ 33142 Büren ~ Germany
Fon: +49-2955-7433-20 Fax: +49-2955-7433-99
--------------------------------------------------------------------------------

---

Nicola Musatti

unread,
May 13, 2002, 1:35:47 PM5/13/02
to

Robert Klemme wrote:
[...]


> since i currently do not have a clear idea what might be the
> benefits of what you're proposing: would you mind coming up with
> a more adequate example? thank you!

Alright. There are (at least) two aspects to exception handling which
require intercepting an active exception: one is performing local error
related actions, such as rollback of transactional operations, and the
other is manipulation of the exception object, possibly to supply it
with information on the local context. In my opinion the currently
available mechanisms make it difficult to design a well structured
solution to the combined problems.

As an example of the first problem consider a transactional operation
which we might model as follows:

class Transaction {
bool done;
public:
Transaction() : done(false) { open(); }
~Transaction() { if ( ! done ) commit(); }
void commit() { done = true; /* ... */ }
void rollback() { done = true; /* ... */ }
};

Normal activity can be handled by applying the RAII idiom. On the other
hand it is not currently possible to know within a destructor whether it
was called during the normal flow of execution or during stack
unwinding, so rollback must be called explicitly (you can switch things
around so that rollback is called in the destructor, but one of commit
and rollback must be called explicitly).

Consider the following try/catch block:

try {
// whatever
}
catch ( ExceptionA & a ) {
// (A)
}
catch ( Exception & b ) {
// (B)
}
catch ( ... ) {
// (C)
}

Transaction::rollback() must be called at points (A), (B) and (C). A
couple of times two many for what is conceptually a single call. You can
eliminate the extra calls only if you assume that either all caught
exceptions will descend from the same base class or that you are not
interested in the exception object. This may not be acceptable if, as
assumed above, you need to manipulate the thrown objects; moreover, do
we really want to give up static type checking for exceptions?

One possible solution is to define a handler function as follows:

void exception_handler() {
try {
throw;
}
catch ( ExceptionA & a ) {
// (A)
}
catch ( Exception & b ) {
// (B)
}
}

This can (must, actually) be called within the catch(...) block of the
original statement and lets us remove the other two catch blocks.

In this way you can separate exception object manipulation from
transaction cleanup. However I can't say I like to populate my
application with functions that have to be called within catch blocks
and cause the application to crash if you call them elsewhere.

My proposal solves this problem in a safer and more object oriented way;
the transaction class would become:

class Transaction {
public:
Transaction() : { open(); }
~Transaction() { commit(); }
~Transaction(ExceptionA & a) { rollback(); /* handle a */ }
~Transaction(ExceptionB & b) { rollback(); /* handle b */ }
void commit() { /* ... */ }
void rollback() { /* ... */ }
};

It remains to be established whether we want to introduce a special
notation analogous the catch(...), as ~Transaction(...), or if the
parameterless destructor should be called in this case.

I hope this was clearer.

Cheers,
Nicola Musatti

Hillel Y. Sims

unread,
May 14, 2002, 11:47:59 AM5/14/02
to
Couldn't you just do it all this way?

class Transaction {
bool done;
public:
Transaction() : done(false) { open(); }

~Transaction() { if (!done) rollback(); }


void commit() { done = true; /* ... */ }
void rollback() { done = true; /* ... */ }
};

try {
Transaction t;
// whatever
t.commit(); // explicit commit!
}
catch (ExceptionA& a) {
// handle error only
// no need to rollback
}
catch (ExceptionB& b) {
// handle error only
// no need to rollback
}
// no need at all for dangerous/undesirable catch(...)

hys

"Nicola Musatti" <obje...@divalsim.it> wrote in message
news:3CDF9DAE...@divalsim.it...

--


Hillel Y. Sims
hsims AT factset.com

---

Nicola Musatti

unread,
May 15, 2002, 12:43:22 PM5/15/02
to

"Hillel Y. Sims" wrote:
>
> Couldn't you just do it all this way?
>
> class Transaction {
> bool done;
> public:
> Transaction() : done(false) { open(); }
> ~Transaction() { if (!done) rollback(); }
> void commit() { done = true; /* ... */ }
> void rollback() { done = true; /* ... */ }
> };

You omitted this part of my previous message:

~Transaction(ExceptionA & a) { rollback(); /* handle a */ }
~Transaction(ExceptionB & b) { rollback(); /* handle b */ }

^
This is the problem I'm also trying to solve.

>
> try {
> Transaction t;
> // whatever
> t.commit(); // explicit commit!
> }
> catch (ExceptionA& a) {
> // handle error only
> // no need to rollback
> }
> catch (ExceptionB& b) {
> // handle error only
> // no need to rollback
> }
> // no need at all for dangerous/undesirable catch(...)

With my solution I can avoid repeating *also* the above catch clauses,
without recurring to the dangerous "throw in the try block" trick.

Cheers,
Nicola Musatti

Robert Klemme

unread,
May 15, 2002, 1:58:04 PM5/15/02
to

hello nicola,

thank you for the lengthy elaboration. please see my comments
below.

Nicola Musatti schrieb:


> Alright. There are (at least) two aspects to exception handling which
> require intercepting an active exception: one is performing local error
> related actions, such as rollback of transactional operations, and the
> other is manipulation of the exception object, possibly to supply it
> with information on the local context.

as far as i can see the rest of your posting deals with item one,
"rollback of transactional operations".

regarding item two, "manipulation of the exception object", i
think this is the wrong way to go. rather exceptions should be
caught and maybe included in another exception that is thrown
from the handler. as discussed in comp.object in two different
threads recently, declaring an exception of an underlying
application layer breaks encapsulation. so you have to
catch-retrhow anyway to hide underlying layers from a package's
clients.

> In my opinion the currently
> available mechanisms make it difficult to design a well structured
> solution to the combined problems.

disagree. see below.

> Normal activity can be handled by applying the RAII idiom. On the other
> hand it is not currently possible to know within a destructor whether it
> was called during the normal flow of execution or during stack
> unwinding,

in this case this is not needed as i try to demonstrate. in
which cases do you need it really?

> so rollback must be called explicitly (you can switch things
> around so that rollback is called in the destructor, but one of commit
> and rollback must be called explicitly).

[snip]


> Transaction::rollback() must be called at points (A), (B) and (C). A
> couple of times two many for what is conceptually a single call. You can
> eliminate the extra calls only if you assume that either all caught
> exceptions will descend from the same base class or that you are not
> interested in the exception object.

there is an obvious third solution that you mentioned already:
switch it so the destructor invokes rollback(). why? there is
only one exit from a successfull operation but several exits (via
the catch blocks) in case of error. i prefer to explicitely
commit a transaction if all went well anyway and in this case
your multiple invokation problem is solved with this easily.

and you can even spare the "done" flag if you let the destructor
invoke rollback() always. i think, that's the most reasonable
approach since it makes commit explicit and undo implicit -
especially when there are errors. i find this simpler, cleaner
and more robust.

// my version


class Transaction {
public:
Transaction() { open(); }

~Transaction() { rollback(); }


void commit() { /* ... */ }
void rollback() { /* ... */ }

private:
void open();
};

> In this way you can separate exception object manipulation from
> transaction cleanup. However I can't say I like to populate my
> application with functions that have to be called within catch blocks
> and cause the application to crash if you call them elsewhere.

agree.

> It remains to be established whether we want to introduce a special
> notation analogous the catch(...), as ~Transaction(...), or if the
> parameterless destructor should be called in this case.
>
> I hope this was clearer.

well, i'm still missing an example where only your solution would
help. as a rule of thumb i would solve problems at hand with
what is there instead of augmenting the language with new syntax
and semantic. in this case it's easy without your suggestion as
i hope to have demonstrated.

regards

robert

Adin Hunter Baber

unread,
May 15, 2002, 3:27:38 PM5/15/02
to
Originally, I started reading this thread after Alexander Terekhov
pointed me to it. From another thread
http://groups.google.com/groups?hl=en&threadm=mjolnir_DELETE_-6E2CEB.1731
1707052002%40newsfeed.slurp.net in comp.lang.c++, Alexander Terekhov
figured I would be interested in this one, and he was 100% correct.

I looked at the problems and came up with the following ideas:
1) We want to know if a destructor has been called because of a standard
exit from a code block or because of an exception.
2) We want to know what the current exception is.
3) We may also want to manipulate an exception, so as to modify / add
data to it.

I have gathered from this thread that one problem of the current
implementation is that std::uncaught_exception() simply does not give us
enough information.

The following code has the following concepts / notes in it:
1) All exceptions are registered in a pseudo stack (i.e. global array
with global index pointer) called the exceptionTrackingArray. The array
actually contains pointers to the exceptions.
2) Caveat: Each exception class, with the exception of the two
meta-exceptions MUST be derived from my Exception class for this to work.
3) Caveat: At most 256 exceptions may be in-flight. I chose this number
for three reasons: It's a nice binary number :), the extra memory used
is 1k, which isn't much, 256 in flight exceptions should be enough for
any real application (I could be wrong here).
4) If more than 256 exceptions are some how thrown, an
ExcessiveExceptions exception is thrown. I put this in mainly so that
if someone actually has a program that exceeds 256 in flight exceptions,
they have a debugging tool. Otherwise, everyone should be deeply
suspicious of any code that has catch(ExcessiveExceptions &ee).
5) If the exception stack is not unwound in the reverse order of
creation, an ImproperUnwindingStopEverything exception is thrown. This
will more than likely cause abort() to be called.
6) uncaughtException() now returns true if there is one or more active
exceptions.
7) getTopException() returns a pointer to (drum roll) the most recently
thrown exception.
8) The Exception class handles its registration / deregistration with
the exceptionTrackingArray.
9) Any exception classes derived from Exception will have registration /
deregistration automatically taken care of (yeah! for OOP).
10) Some people may be upset that the Exception class's destructor may
throw. Please note the name od the exception.
11) Note that the assignment operator does not have anything to do with
registration / deregistration. If it manipulates the localIndex, or
tries to provide registration / deregistration, then chaos will ensue
(or more likely ImproperUnwindingStopEverything will be thrown).
12) I should make a function that takes an exception, and see if it's a
specific type.
13) The control structure should probably be placed into a class,
possibly with a singleton model. OTOH, in a multi-threaded environment,
we may want to give each thread its own stack.

Here is the code and some example tests. All of this compiled on
CodeWarrior 7.2 for Mac OS X. (Adin stands back and waits for the
butane and matches ;p)
BTW, I left the debug / testing ostreams in the code. anytime an
Exception is constructed, it will output it was a normal or copy
construction, as well as its index on the exceptionTrackingArray. Its
destructor will likewise output a message.

// --------------------------------------------------
// File : NewExceptionControl.hpp
// --------------------------------------------------
#ifndef ADIN_NEW_EXCEPTION_CONTROL_HPP
#define ADIN_NEW_EXCEPTION_CONTROL_HPP

namespace Adin {

class Exception;
typedef Exception* ExceptionPtr;

// not inherited from Exception since that would cause other problems
class ExcessiveExceptions
{
public:
ExcessiveExceptions() throw()
{}

ExcessiveExceptions(const ExcessiveExceptions &)
{}

virtual ~ExcessiveExceptions() throw()
{}

ExcessiveExceptions& operator = (const ExcessiveExceptions &)
{ return *this; }

virtual const char* what() const throw()
{ return "Excessive number of Exceptions."; }
private:
};

// not inherited from Exception since that would cause other problems
class ImproperUnwindingStopEverything
{
public:
ImproperUnwindingStopEverything() throw ()
{}

ImproperUnwindingStopEverything(const
ImproperUnwindingStopEverything &)
{}

virtual ~ImproperUnwindingStopEverything() throw()
{}

ImproperUnwindingStopEverything& operator = (const
ImproperUnwindingStopEverything &)
{ return *this; }

virtual const char* what() const throw()
{ return "Multiple exceptions were being resolved in the
wrong order."; }
private:

};

const int maxExceptions = 256;

ExceptionPtr exceptionTrackingArray[maxExceptions];

int nextExceptionIndex = 0;

bool uncaughtException();
ExceptionPtr getTopException();


inline bool uncaughtException()
{
return (nextExceptionIndex > 0);
}

ExceptionPtr getTopException()
{
if(nextExceptionIndex > 0)
return exceptionTrackingArray[nextExceptionIndex - 1];
return NULL;
}

}// end namespace Adin

#endif

// --------------------------------------------------
// File NewException.hpp
// --------------------------------------------------
#ifndef ADIN_NEW_EXCEPTION_HPP
#define ADIN_NEW_EXCEPTION_HPP

#include "NewExceptionControl.hpp"
#include <iostream>

namespace Adin {

class Exception
{
public :
Exception() throw(ExcessiveExceptions)
{
if(nextExceptionIndex >= maxExceptions)
throw ExcessiveExceptions();
localIndex = nextExceptionIndex++;
exceptionTrackingArray[localIndex] = this;
std::cout << "Exception " << localIndex << " constructed\n";
}

Exception(const Exception &) throw(ExcessiveExceptions)
{
if(nextExceptionIndex >= maxExceptions)
throw ExcessiveExceptions();
localIndex = nextExceptionIndex++;
exceptionTrackingArray[localIndex] = this;
std::cout << "Exception " << localIndex << " copy
constructed\n";
}

Exception& operator = (const Exception &)
{
return *this;
}

virtual ~Exception () throw(ImproperUnwindingStopEverything)
{
if((nextExceptionIndex - 1) != localIndex)
throw ImproperUnwindingStopEverything();
else
nextExceptionIndex--;
std::cout << "Exception " << localIndex << " destructed\n";
}

virtual const char* what() const throw()
{ return "Exception"; }

bool topException()
{ return (localIndex == (nextExceptionIndex - 1)); }

int index()
{ return localIndex; }



private:
int localIndex;
};

}// end namespace Adin
#endif

// --------------------------------------------------
// Test1
// --------------------------------------------------
#include <iostream>
#include "NewException.hpp"

class MyExcep : public Adin::Exception
{
public:
MyExcep(char *newMessage)
: message(newMessage)
{ std::cout << "MyExcep constructed\n"; }

MyExcep(const MyExcep &me)
: message(me.message)
{ std::cout << "MyExcep copy constructed\n"; }

virtual ~MyExcep()
{ std::cout << "MyExcep destructed : "; }

MyExcep& operator = (const MyExcep &me)
{
message = me.message;
return *this;
}

virtual const char* what() const
{ return message; }
private:
char *message;
};

int main()
{
using namespace std;
using namespace Adin;

try {
try {
throw MyExcep("hmmm");

}
catch(MyExcep me)
{
cout << me.what() << "\n";
throw;
}
catch(Exception &e)
{
cout << e.what() << "\n";
Exception e2;
e2 = e;
throw;
}
catch(...)
{
cout << "got here" << endl;
}
}
catch(Exception &e)
{
cout << "Outer Exception : " << e.what() << "\n";
}
catch(...)
{
cout << "unexpected exception\n";
}

return 0;
}
// --------------------------------------------------
// output for test one
// --------------------------------------------------

Exception 0 constructed
MyExcep constructed
Exception 1 constructed
MyExcep copy constructed
hmmm
MyExcep destructed : Exception 1 destructed
Outer Exception : hmmm
MyExcep destructed : Exception 0 destructed


// --------------------------------------------------
// simple roll back / commit in the destructor
// --------------------------------------------------
#include <iostream>
#include <sioux.h>
#include "NewException.hpp"

using namespace std;
using namespace Adin;

class SomeExcep : public Exception
{};

template<class TheException>
class Simple
{
public:
// uses implicit default constructor
// uses implicit copy constructor

~Simple()
{
if(uncaughtException())
{
// this needs to be cleaned up
// or made into a global function
Exception *e = getTopException();
TheException *te = NULL;
te = dynamic_cast<TheException*>(e);
if(te)
std::cout << "we detected an exception of the
desired type\n";
else
std::cout << "not the desired exception type\n";
}
else
{
std::cout << "standard exit\n";
}
}
// uses implicit assignment operator

private:

};

int main()
{

try {
Simple<SomeExcep> s;
throw Exception();
}
catch(Exception &e)
{
cout << e.what() << " : ";
}
try {
Simple<SomeExcep> s;
throw SomeExcep();
}
catch(Exception &e)
{
cout << e.what() << " : ";
}
try {
Simple<SomeExcep> s;
// don't throw
}
catch(Exception &e)
{
cout << "we shouldn't be here\n";
}


return 0;
}

// --------------------------------------------------
// output simple rollback / commit
// --------------------------------------------------
Exception 0 constructed
not the desired exception type
Exception : Exception 0 destructed
Exception 0 constructed
we detected an exception of the desired type
Exception : Exception 0 destructed
standard exit

// --------------------------------------------------
// Terekhov Test
// --------------------------------------------------
#include <iostream>
#include "NewException.hpp"

using namespace std;
using namespace Adin;

int ex_count = 0;

int foo();

struct object
{
~object() { foo(); }
};

int main()
{
foo();
return 0;
}

int foo()
{
int ex = ex_count++;
try {
if ( ex < 10 )
{
// Nah, I want MORE active exceptions ;-)
object obj;
//cout << "throw " << ex << endl;
throw Exception();
}
else
{
cout << "Okay... ENOUGH active exceptions! ;-)" << endl;
}
}
catch(Exception &e)
{
//cout << "caught " << e.index() << endl;
}
return ex;
}
// --------------------------------------------------
// Output for Terekhov Test
// --------------------------------------------------
Exception 0 constructed
Exception 1 constructed
Exception 2 constructed
Exception 3 constructed
Exception 4 constructed
Exception 5 constructed
Exception 6 constructed
Exception 7 constructed
Exception 8 constructed
Exception 9 constructed
Okay... ENOUGH active exceptions! ;-)
Exception 9 destructed
Exception 8 destructed
Exception 7 destructed
Exception 6 destructed
Exception 5 destructed
Exception 4 destructed
Exception 3 destructed
Exception 2 destructed
Exception 1 destructed
Exception 0 destructed

// --------------------------------------------------
// Output for Terekhov Test
// After changing the catch to catch by value copying, not by reference
// --------------------------------------------------
Exception 0 constructed
Exception 1 constructed
Exception 2 constructed
Exception 3 constructed
Exception 4 constructed
Exception 5 constructed
Exception 6 constructed
Exception 7 constructed
Exception 8 constructed
Exception 9 constructed
Okay... ENOUGH active exceptions! ;-)
Exception 10 copy constructed
Exception 10 destructed
Exception 9 destructed
Exception 9 copy constructed
Exception 9 destructed
Exception 8 destructed
Exception 8 copy constructed
Exception 8 destructed
Exception 7 destructed
Exception 7 copy constructed
Exception 7 destructed
Exception 6 destructed
Exception 6 copy constructed
Exception 6 destructed
Exception 5 destructed
Exception 5 copy constructed
Exception 5 destructed
Exception 4 destructed
Exception 4 copy constructed
Exception 4 destructed
Exception 3 destructed
Exception 3 copy constructed
Exception 3 destructed
Exception 2 destructed
Exception 2 copy constructed
Exception 2 destructed
Exception 1 destructed
Exception 1 copy constructed
Exception 1 destructed
Exception 0 destructed

xle...@qmailcomq.com

unread,
May 15, 2002, 5:25:14 PM5/15/02
to
Nicola Musatti <Nicola....@r-it.it> wrote:


> With my solution I can avoid repeating *also* the above catch clauses,
> without recurring to the dangerous "throw in the try block" trick.

Why do you keep calling it "the dangerous trick"? It is perfectly legal,
AFAICRTS.

Leo

Tom Puverle

unread,
May 15, 2002, 7:06:15 PM5/15/02
to
Could you please elaborate on the following points please:

- How does your overloaded destructor method determine that the object was
not created during stack unwinding (i.e. an exception was already active and
it may not be necessary to follow the "exceptional" path)

- How does the proposal differ from this code:

MyClass::~MyClass()
{
if( std::uncaught_exception() )
{
try
{
throw;
}//try
catch( SomeException & ex )
{
/** Do something */
}//catch
catch( SomeOtherException & ex )
{
/** Do something */
}
/** etc. */
}//if
else
{
/** Do whatever - No exception active */
}//else

}//dtor

Adin Hunter Baber

unread,
May 16, 2002, 5:19:20 AM5/16/02
to
In article <abulkj$odu$1...@pegasus.csx.cam.ac.uk>,
"Tom Puverle" <tp...@cam.ac.uk> wrote:

> Could you please elaborate on the following points please:
>
> - How does your overloaded destructor method determine that the object was
> not created during stack unwinding (i.e. an exception was already active and
> it may not be necessary to follow the "exceptional" path)

Could you give a concrete example of what you mean please?

>
> - How does the proposal differ from this code:
>
> MyClass::~MyClass()
> {
> if( std::uncaught_exception() )
> {
> try
> {
> throw;
> }//try
> catch( SomeException & ex )
> {
> /** Do something */
> }//catch
> catch( SomeOtherException & ex )
> {
> /** Do something */
> }
> /** etc. */
> }//if
> else
> {
> /** Do whatever - No exception active */
> }//else
>
> }//dtor

For me, this code didn't work. When the throw was excuted in side the
destructor, the application died. I ran it through my debugger, and an
exception was thrown before MyClass's destructor was called.

I layed out the code like this:
try {
MyClass my;
throw SomeException();
}
catch(...)
{}

Does this MyClass example work for anyone else?
If it does, why was this thread started? ;p

Hillel Y. Sims

unread,
May 16, 2002, 7:50:33 AM5/16/02
to

"Nicola Musatti" <Nicola....@R-it.it> wrote in message
news:3CE22451...@R-it.it...

>
>
> "Hillel Y. Sims" wrote:
> >
> > class Transaction {
> > bool done;
> > public:
> > Transaction() : done(false) { open(); }
> > ~Transaction() { if (!done) rollback(); }
> > void commit() { done = true; /* ... */ }
> > void rollback() { done = true; /* ... */ }
> > };
>
> You omitted this part of my previous message:
>
> ~Transaction(ExceptionA & a) { rollback(); /* handle a */ }
> ~Transaction(ExceptionB & b) { rollback(); /* handle b */ }
> ^
> This is the problem I'm also trying to solve.
>

Yes, I omitted that because I don't think it is really needed - the rollback
is implicit in my version for all exceptional destructions. You must perform
explicit commit (which seems like the most reasonable semantics to me for
commit/rollback-type code anyhow), in which case there is no need to provide
for explicit rollback in multiple exit paths. As far as error-handling
concerns,

try {
Transaction t;
// do stuff
t.commit();
}
catch (ExceptionA& a) {
// handle a
}
catch (ExceptionB& b) {
// handle b
}

> With my solution I can avoid repeating *also* the above catch clauses,
> without recurring to the dangerous "throw in the try block" trick.

Your special destructor functions do not really avoid repeating the catch
clauses, since you are attempting to do the same error handling in both of
them too; in fact you repeat "rollback()" twice, but you don't need explicit
rollback at all when you use explicit non-exceptional commit instead. As far
as having multiple catch blocks, you can use the "try {throw;} catch (etc)"
technique (which is not unsafe when done from a normal catch block) to
consolidate error-handling into one function anyhow.

try {
Transaction t;
// do stuff
t.commit();
}
catch (TransExceptionBase&) {
handle_trans_exceptions();
}

void handle_trans_exceptions()
{
try {
throw;
}
catch (ExceptionA& a) {
// handle a
}
catch (ExceptionB& b) {
// handle b
}
}

(I thought it was the going idea that error-handling is best handled on a
procedural, not per-object, basis? Did I misinterpret something?)

hys

--
Hillel Y. Sims
hsims AT factset.com

---

Hillel Y. Sims

unread,
May 16, 2002, 7:50:35 AM5/16/02
to

"Tom Puverle" <tp...@cam.ac.uk> wrote in message
news:abulkj$odu$1...@pegasus.csx.cam.ac.uk...

> - How does the proposal differ from this code:
>
> MyClass::~MyClass()
> {
> if( std::uncaught_exception() )
> {
> try
> {
> throw;
> }//try
> catch( SomeException & ex )
> {
> /** Do something */
> }//catch
> catch( SomeOtherException & ex )
> {
> /** Do something */
> }
> /** etc. */
> }//if
> else
> {
> /** Do whatever - No exception active */
> }//else
>
> }//dtor
>

I didn't really read his proposal in much detail so I can't comment on that,
but this particular snippet has been established as illegal - can't access
(any) current active exception object(s) from a dtor

hys

--
Hillel Y. Sims
hsims AT factset.com

---

Nicola Musatti

unread,
May 16, 2002, 1:13:33 PM5/16/02
to

xle...@qmailcomq.com wrote:
>
> Nicola Musatti <Nicola....@r-it.it> wrote:
>
> > With my solution I can avoid repeating *also* the above catch clauses,
> > without recurring to the dangerous "throw in the try block" trick.
>
> Why do you keep calling it "the dangerous trick"? It is perfectly legal,
> AFAICRTS.

What happens if you call the following function outside a catch block?

void f() {
try { throw; }
catch (...) {}
}

How can you keep people from doing it?

Cheers,
Nicola Musatti

xle...@qmailcomq.com

unread,
May 16, 2002, 3:34:46 PM5/16/02
to
Nicola Musatti <Nicola....@r-it.it> wrote:

>> > With my solution I can avoid repeating *also* the above catch clauses,
>> > without recurring to the dangerous "throw in the try block" trick.
>>
>> Why do you keep calling it "the dangerous trick"? It is perfectly legal,
>> AFAICRTS.

> What happens if you call the following function outside a catch block?

> void f() {
> try { throw; }
> catch (...) {}
> }

terminate() is called.

> How can you keep people from doing it?

>From doing what? The contract for f(), as written, is to be only called
from within a catch block, and for that catch block - not to have throw;
after f() had been called. What's so dangerous? IMHO, it is as dangerous
as dereferencing a pointer without comparing it to 0 - if it is not
supposed to be 0 by contract, it is ok.

Leo

Tom Puverle

unread,
May 16, 2002, 3:07:30 PM5/16/02
to
> For me, this code didn't work. When the throw was excuted in side the
> destructor, the application died. I ran it through my debugger, and an
> exception was thrown before MyClass's destructor was called.

Yes, sorry. And I thought I was being clever at 12:10 pm :) Doesn't
happen...
I think you are right. The worst thing is that I just noticed there is a
newsgroup discussing this already: "Alexadrescu at ACCU 2002 Conference"...
Not only was I not the first to have that idea, I was also the next one to
get it wrong! :)
Apologies,
Tom

Nicola Musatti

unread,
May 16, 2002, 3:07:53 PM5/16/02
to

"Hillel Y. Sims" wrote:
>
> "Nicola Musatti" <Nicola....@R-it.it> wrote in message
> news:3CE22451...@R-it.it...

[...]


> As far as error-handling concerns,
>
> try {
> Transaction t;
> // do stuff
> t.commit();
> }
> catch (ExceptionA& a) {
> // handle a
> }
> catch (ExceptionB& b) {
> // handle b
> }
>
> > With my solution I can avoid repeating *also* the above catch clauses,
> > without recurring to the dangerous "throw in the try block" trick.
>
> Your special destructor functions do not really avoid repeating the catch
> clauses, since you are attempting to do the same error handling in both of
> them too; in fact you repeat "rollback()" twice,

This can be avoided by designing a class hyerarchy appropriately, even
though it might prove overkill.

> but you don't need explicit
> rollback at all when you use explicit non-exceptional commit instead. As far
> as having multiple catch blocks, you can use the "try {throw;} catch (etc)"
> technique (which is not unsafe when done from a normal catch block) to
> consolidate error-handling into one function anyhow.

This is exactly what is dangerous: you have a function that can only be
used in the "exceptional" context of catch clauses.

[...]


> (I thought it was the going idea that error-handling is best handled on a
> procedural, not per-object, basis? Did I misinterpret something?)

Is it better or is it just that there is no alternative?

Cheers,
Nicola Musatti

Allan W

unread,
May 16, 2002, 4:55:18 PM5/16/02
to
Nicola Musatti <obje...@divalsim.it> wrote

> There are (at least) two aspects to exception handling which
> require intercepting an active exception: one is performing local error
> related actions, such as rollback of transactional operations, and the
> other is manipulation of the exception object, possibly to supply it
> with information on the local context. In my opinion the currently
> available mechanisms make it difficult to design a well structured
> solution to the combined problems.
>
> As an example of the first problem consider a transactional operation
> which we might model as follows:
>
> class Transaction {
> bool done;
> public:
> Transaction() : done(false) { open(); }
> ~Transaction() { if ( ! done ) commit(); }
> void commit() { done = true; /* ... */ }
> void rollback() { done = true; /* ... */ }
> };
>
> Normal activity can be handled by applying the RAII idiom. On the other
> hand it is not currently possible to know within a destructor whether it
> was called during the normal flow of execution or during stack
> unwinding, so rollback must be called explicitly (you can switch things
> around so that rollback is called in the destructor, but one of commit
> and rollback must be called explicitly).

I think it makes sense to make commit() explicit -- you know when your
normal processing is done, and adding a commit() there seems natural.
Plus, you only need to code it in one place, making most of what you
say below irrelevant.

Try this alternative then:

try {
try {
// Whatever...
tran.commit(); // If this needs to be explicit
} catch(...) {
// Something went wrong. If rollback() needs to
// be explicit, do it here -- then re-throw to
// perform more explicit error-checking.
tran.rollback(); // Something went wrong


throw;
}
}
catch ( ExceptionA & a ) {

// No need to call rollback


}
catch ( Exception & b ) {

// No need to call rollback
}
catch ( ... ) {
// No need to call rollback
}


> My proposal solves this problem in a safer and more object oriented way;
> the transaction class would become:
>
> class Transaction {
> public:
> Transaction() : { open(); }
> ~Transaction() { commit(); }
> ~Transaction(ExceptionA & a) { rollback(); /* handle a */ }
> ~Transaction(ExceptionB & b) { rollback(); /* handle b */ }
> void commit() { /* ... */ }
> void rollback() { /* ... */ }
> };
>
> It remains to be established whether we want to introduce a special
> notation analogous the catch(...), as ~Transaction(...), or if the
> parameterless destructor should be called in this case.

This is a big change. It doesn't seem compelling to me since we have
viable alternatives today -- but then, I haven't read the whole
thread (54 messages and growing!), so I might have missed something
that would change my mind.

Hillel Y. Sims

unread,
May 17, 2002, 12:36:08 PM5/17/02
to

"Nicola Musatti" <Nicola....@R-it.it> wrote in message
news:3CE3AACF...@R-it.it...

>
> This can be avoided by designing a class hyerarchy appropriately, even
> though it might prove overkill.
>
Yes, see "explicit commit / implicit rollback" in the last few messages. :-)

>
> This is exactly what is dangerous: you have a function that can only be
> used in the "exceptional" context of catch clauses.

<quote>
<xle...@qmailcomq.com> wrote in message
news:mRSE8.7635$T_.1...@iad-read.news.verio.net...


> >From doing what? The contract for f(), as written, is to be only called
> from within a catch block, and for that catch block - not to have throw;
> after f() had been called. What's so dangerous? IMHO, it is as dangerous
> as dereferencing a pointer without comparing it to 0 - if it is not
> supposed to be 0 by contract, it is ok.

</quote>

I agree. Many things are only appropriate to be used in certain contexts.

(Also, it seems less dangerous than having to put explicit rollback in
multiple places.)

>
> [...]
> > (I thought it was the going idea that error-handling is best handled on
a
> > procedural, not per-object, basis? Did I misinterpret something?)
>
> Is it better or is it just that there is no alternative?

I guess there's no alternative, but it just doesn't seem to be a big problem
to me either.. One line of explicit 'commit' makes the whole problem go
away.

thanks,
hys

--
Hillel Y. Sims
hsims AT factset.com

---

Adin Hunter Baber

unread,
May 17, 2002, 1:00:38 PM5/17/02
to
This is an update to my previous proposal.

The big point is that you are able to retrieve a thrown exception
anytime between is construction and destruction.

First off, I found one dangerous flaw in my old implementation. If
someone creates the exception before throwing it, the exception will
throw an ImproperUnwindingStopEverything exception. Here is the code
that does this:

try {
Exception e;
throw e;
} // we will die here when e is destructed

One solution to this was to never preconstruct an exception, which is
probably inefficient anyway. This has been taken care of in the new
version.

Instead of using an array to hold all pending exceptions, I put them
into a double linked list. This had two effects:
a) We no longer have an excessive number of exceptions (relatively
speaking :). Thus that type of exception has been removed.
b) The ImproperUnwinding exception has also been removed.

So the following points are now modified:
1) All exceptions are now registered in a double link list.
2) All exceptions must still be derived from Exception.
3) There can now be "infinite" exceptions.
4) No longer applicable.
5) No longer applicable.
6) Not changed.
7) Not changed.
8) It is now a double linked list, not an array.
9) Not changed.
10) The Exception classes destructor no longer throws.
11) No longer applicable.
12) There is now a function that takes a pointer to an exception, and
will tell you if it is a specific type.
13) The control structure is still a global non-class implementation.
Additionally, since a double linked list is being use, it CANNOT be used
with multiple threaded applications.

The new version of the code has the following functions:
bool uncaughtException();
// returns true if there are any active exceptions
// can be fooled still with dirty tricks
ExceptionPtr getCurrentException();
// returns a pointer to the most recently activated exception

template<class TheType> bool isExceptionType();
// checks to see if the current exception is a specific type
template<class TheType, class TheException> bool
isExceptionType(TheException *u);
// checks to see if the exception passed into the function is a
specific type

This version is vulnerable to deliberate and malicious tricks. For
example
{// non trying code block
Exception e;
if(uncaughtException()) // will be true even if no
cout << "What the???";// exceptions have been thrown
}
Additionally, getCurrentException() would return the above false
exception. Avoiding silly/malicious coding techniques will avoid these
problems though. The only time an Exception or a derivative should be
used is when it is thrown.

OTOH, this is simply an attempt to address some issues of exceptions.
This could be made more robust by having the standard require the
compilers to handle the registration / deregistration when ever it
encounters a throw :)

Here's one way to use this code in a destructor:

template<class TheException>
class SimpleRollBack
{
public:
~Simple()
{
if(uncaughtException())
{
if(isExceptionType<TheException>())


std::cout << "we detected an exception of the
desired type\n";
else
std::cout << "not the desired exception type\n";
}
else
{
std::cout << "standard exit\n";
}
}

private:
};


Here's the new version of the code. Happily, it is now in one file.

// ------------------------------------------------------------
// NewException.hpp
// ------------------------------------------------------------

#ifndef ADIN_NEW_EXCEPTION_HPP
#define ADIN_NEW_EXCEPTION_HPP
#include <iostream>
namespace Adin {

class Exception;
typedef Exception* ExceptionPtr;

namespace PrivateExceptionControl {
ExceptionPtr currentException = NULL;
}// namespace PrivateExceptionControl

class Exception
{
public :
Exception() throw()
: prevException(PrivateExceptionControl::currentException),
nextException(NULL) // we are always adding to the top of
the list
{
PrivateExceptionControl::currentException = this;
if(prevException)
prevException->nextException = this;
std::cout << "Exception " << (void*)this << " constructed\n";
}
Exception(const Exception &) throw()
: prevException(PrivateExceptionControl::currentException),
nextException(NULL)
{
PrivateExceptionControl::currentException = this;
if(prevException)
prevException->nextException = this;
std::cout << "Exception " << (void*)this << " copy

constructed\n";
}
Exception& operator = (const Exception &)
{ return *this; }

virtual ~Exception() throw()
{
if(PrivateExceptionControl::currentException == this)
PrivateExceptionControl::currentException =
prevException;
if(prevException)
prevException->nextException = nextException;
if(nextException)
nextException->prevException = prevException;
std::cout << "Exception " << (void*)this << " destructed\n";


}
virtual const char* what() const throw()
{ return "Exception"; }

private:
Exception *prevException;
Exception *nextException;
};

bool uncaughtException();
ExceptionPtr getCurrentException();

template<class TheType> bool isExceptionType();
template<class TheType, class TheException> bool
isExceptionType(TheException *u);

inline bool uncaughtException()
{ return (PrivateExceptionControl::currentException); }

ExceptionPtr getCurrentException()
{ return PrivateExceptionControl::currentException; }

template<class TheType>
bool isExceptionType()
{ return isExceptionType<TheType>(getCurrentException()); }

template<class TheType, class TheException>
bool isExceptionType(TheException *u)
{
const TheType *testType(dynamic_cast<TheType*>(u));
return testType ? true : false;
}

}// end namespace Adin
#endif

---

Alexander Terekhov

unread,
May 20, 2002, 1:22:59 PM5/20/02
to

"Hillel Y. Sims" wrote:
[...]

> I guess there's no alternative, but it just doesn't seem to be a big problem
> to me either.. One line of explicit 'commit' makes the whole problem go
> away.

That's quite correct. However, "one line" of explicit
"unwinding(this)" would make ZILLIONS explicit 'commit'
('bool commit_done', etc.) lines go away (*worldwide*,
I mean ;-)). Heck, think of global warming, etc.! ;-)

Oh, and, probably (unless I'm missing something) would
even FIX the standard ~sentry()-thing[1], already used
in "many" (only hell knows how many, I guess) places/
lines currently all over the blue planet Earth. Pretty
much the same applies to the rest of items on the "10
o'clock"-wish list, IMHO:

http://groups.google.com/groups?selm=3CC86F8C.94EFB52C%40web.de
http://groups.google.com/groups?selm=3CC97E16.3191C096%40web.de

regards,
alexander.

P.S. Leo, do you have some REAL email-address? ;-)

[1] http://groups.google.com/groups?selm=3CDBD7E2.EA14D26D%40web.de

Nicola Musatti

unread,
May 20, 2002, 1:47:46 PM5/20/02
to

xle...@qmailcomq.com wrote:
[...]


> >From doing what? The contract for f(), as written, is to be only called
> from within a catch block, and for that catch block - not to have throw;
> after f() had been called. What's so dangerous? IMHO, it is as dangerous
> as dereferencing a pointer without comparing it to 0 - if it is not
> supposed to be 0 by contract, it is ok.

In my opinion the danger stems from the fact that this is a very unusual
situation: I'm convinced that very few people realize that "throw;"
statements do not have to be physically contained in a catch block, so
it's quite possible that the function's contract be misunderstood. While
I couldn't hold it against a junior (well, even not so junior)
programmer not being aware of this, I certainly would eat alive any
programmer not checking pointers where they need to :-)

Cheers,
Nicola Musatti

Nicola Musatti

unread,
May 21, 2002, 2:35:59 PM5/21/02
to

Allan W wrote:
>
> Nicola Musatti <obje...@divalsim.it> wrote
[...]


> > Normal activity can be handled by applying the RAII idiom. On the other
> > hand it is not currently possible to know within a destructor whether it
> > was called during the normal flow of execution or during stack
> > unwinding, so rollback must be called explicitly (you can switch things
> > around so that rollback is called in the destructor, but one of commit
> > and rollback must be called explicitly).
>
> I think it makes sense to make commit() explicit -- you know when your
> normal processing is done, and adding a commit() there seems natural.
> Plus, you only need to code it in one place, making most of what you
> say below irrelevant.

I tend to agree. I only just realized that the message you replied to is
missing one half of my argument! More later.

[...]


> > void exception_handler() {
> > try {
> > throw;
> > }
> > catch ( ExceptionA & a ) {
> > // (A)
> > }
> > catch ( Exception & b ) {
> > // (B)
> > }
> > }

[...]


>
> Try this alternative then:
>
> try {
> try {
> // Whatever...
> tran.commit(); // If this needs to be explicit
> } catch(...) {
> // Something went wrong. If rollback() needs to
> // be explicit, do it here -- then re-throw to
> // perform more explicit error-checking.
> tran.rollback(); // Something went wrong
> throw;
> }
> }
> catch ( ExceptionA & a ) {
> // No need to call rollback
> }
> catch ( Exception & b ) {
> // No need to call rollback
> }
> catch ( ... ) {
> // No need to call rollback
> }

Your example is perfect where the handling of commit/rollback is
concerned. The only advantage my exception_handler() has is that it lets
you separate the commit/rollback part from the exception management part
where I would expect reporting (to the user or the developer) would be
handled. This is the part of the argument that was missing from my
previous message. Some blunder, given that this reporting part was the
original motivation for my proposal.

> > My proposal solves this problem in a safer and more object oriented way;
> > the transaction class would become:
> >
> > class Transaction {
> > public:
> > Transaction() : { open(); }
> > ~Transaction() { commit(); }
> > ~Transaction(ExceptionA & a) { rollback(); /* handle a */ }
> > ~Transaction(ExceptionB & b) { rollback(); /* handle b */ }
> > void commit() { /* ... */ }
> > void rollback() { /* ... */ }
> > };
> >
> > It remains to be established whether we want to introduce a special
> > notation analogous the catch(...), as ~Transaction(...), or if the
> > parameterless destructor should be called in this case.
>
> This is a big change. It doesn't seem compelling to me since we have
> viable alternatives today -- but then, I haven't read the whole
> thread (54 messages and growing!), so I might have missed something
> that would change my mind.

Actually I agree with you. I believe that wanting to change the language
just to solve the problem at hand is a serious conceptual mistake, which
is why I consider this proposal only "half serious". However from the
length of the thread I draw the impression that there's a feeling around
that exception management is an area of the language that needs to
evolve, even though there is little agreement on how. On the other hand
nobody wrote that my proposal is the one, true solution. Rats :-)

Cheers,
Nicola Musatti

xle...@qmailcomq.com

unread,
May 22, 2002, 4:07:07 PM5/22/02
to
Nicola Musatti <Nicola....@r-it.it> wrote:

> xle...@qmailcomq.com wrote:
> [...]
>> >From doing what? The contract for f(), as written, is to be only called
>> from within a catch block, and for that catch block - not to have throw;
>> after f() had been called. What's so dangerous? IMHO, it is as dangerous
>> as dereferencing a pointer without comparing it to 0 - if it is not
>> supposed to be 0 by contract, it is ok.

> In my opinion the danger stems from the fact that this is a very unusual
> situation: I'm convinced that very few people realize that "throw;"
> statements do not have to be physically contained in a catch block, so
> it's quite possible that the function's contract be misunderstood. While

This is subjective. I do not equate a possibility of confusion to danger.

> I couldn't hold it against a junior (well, even not so junior)
> programmer not being aware of this, I certainly would eat alive any
> programmer not checking pointers where they need to :-)

I certainly would eat alive any programmer checking pointers where they
were told (by contract) that they do not need to. :-)

Leo

--
Omit x's and q's to reply.

Robert Klemme

unread,
May 22, 2002, 4:11:54 PM5/22/02
to

Adin Hunter Baber schrieb:


>
> Originally, I started reading this thread after Alexander Terekhov
> pointed me to it. From another thread
> http://groups.google.com/groups?hl=en&threadm=mjolnir_DELETE_-6E2CEB.1731
> 1707052002%40newsfeed.slurp.net in comp.lang.c++, Alexander Terekhov
> figured I would be interested in this one, and he was 100% correct.
>
> I looked at the problems and came up with the following ideas:
> 1) We want to know if a destructor has been called because of a standard
> exit from a code block or because of an exception.
> 2) We want to know what the current exception is.
> 3) We may also want to manipulate an exception, so as to modify / add
> data to it.

disagree. the problem to look at really is this: we want to do
different exit action depending on normal or error exit. what
you mention are already technical solutions to the real problem.

the exception type can be determinded by catching it.

as i stated elsewhere manipulation of an exception is nothing i
would advocat. the originator of an exception should be the only
instance that manipulates it. all others should only read it.
if you need to add information, you should wrap the exception
inside another exception. you have to do this anyway if you want
to keep a module's interface clean.

regards

robert

0 new messages