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

Proposal: catch member functions

5 views
Skip to first unread message

Niklas Borson

unread,
Mar 8, 2002, 9:57:24 AM3/8/02
to
I remember hearing a while back that people might actually be more
than usually open to new ideas for language features at this time.
With that in mind, here is my suggestion: It would be useful if
objects could catch exceptions.

I will first briefly summarize the idea, then give some examples
of why it might be useful, and finally will fill in some of the
details.


BRIEF SUMMARY

Briefly, the idea is that a class could have special member functions
named catch. (This would not break existing code for obvious reasons.)
If an object is about to be destroyed during exception unwind, the
run-time mechanism searches for any applicable catch methods (in order
of declaration) before calling the object's destructor. If one is
found, the catch method is called, and can (1) return normally, in
which case normal execution resumes after the object is destroyed;
(2) rethrow the exception; or (3) throw a different exception.


APPLICATIONS

To see one possible application of this idea, consider the following
function, which enforces its exception guarantee by using try...catch
blocks:

void MyFunc() throw(MyException)
{
try {
... // code that might throw anything
}
catch (MyException& x) {
throw;
}
catch (std::exception& x) {
throw MyException(x);
}
catch (...) {
throw MyException();
}
}

The above works, but obscures the main intent of the function with a
bunch of boilerplate code designed to enforce what you might call an
"exception policy". It would be better if we could encapsulate the
exception policy using a class, such as the following:

template<class Ex>
class ExceptionFilter {
public:
catch(Ex& x)
{
throw;
}
catch(std::exception& x)
{
throw Ex(x);
}
catch (...)
{
throw Ex();
}
};

Given such a class, we could rewrite the function as follows:

void MyFunc() throw(MyException)
{
ExceptionFilter<MyException> ex;

... // code that might throw anything
}

There are many variations on this idea, such as functions that
can't throw but need to return error information in some other
form -- for example, COM methods or any other functions callable
from C. I could go on, but you get the idea.


SUBOBJECTS WITH CATCH METHODS

If an object has members and bases with catch methods, each member
and base is given an opportunity to catch the exception in order of
destruction. If a subobject catches an exception and then throws or
rethrows an exception, other subjects that are destroyed later have
the opportunity to catch the new or rethrown exception.

For example, here is a rather lame class with two member objects
that have catch methods:

class CompoundExceptionHandler {
private:
class Member1 {
public:
catch (...)
{
throw std::exception("Member1");
}
} member1;

class Member2 {
public:
catch (...)
{
throw std::exception("Member2");
}
} member2;
};

Now consider a hypothetical function that instantiates the above
exception object:

void func()
{
CompoundExceptionHandler ex;

// code that might throw...
}

Now suppose an exception is thrown in the body of the function.
During unwind, the members of ex are destroyed in reverse order
of declaration so ex.member2's catch method is called first. It
throws, after which ex.member2 is destroyed and the exception
propagates out to the next exception frame, which in this case
means ex.member1's catch method is called. The above function
is thus equivalent to the following:

void func()
{
try {
try {
// code that might throw...
}
catch (...) {
throw std::exception("Member2");
}
}
catch (...) {
throw std::exception("Member1");
}
}

This particular example obviously makes no sense; it merely
illustrates how subobjects with catch methods would work.


RETURN VALUES

Return values present a rather tricky problem. If an object
that is declared at outermost scope within a function catches
an exception, handles it, and returns normally, what does the
function return? Here's one example that illustrates the
problem:

bool MyFunc(const char* ps) throw()
{
ExceptionHandler ex;
throw std::exception("Houston, we have a problem.");
std::string s(ps);
return !s.empty();
}

Now assume the ExcetionHandler class has a catch method that
catches this particular exception and returns. This means normal
execution should resume at the point right after the 'ex' object
is destroyed. But this bypasses the return statement so what do
we return from the function?!

The only solution I can think of is that the catch method must
return the value. This suggests the following rule:
(1) If a catch method can return (i.e., at least one control
path does not throw or rethrow) then the catch method
must declare a return type. (Note: for this purpose void
is a return type.)
(2) If a function F has a return type other than void and
instantiates a local variable of type T, then if any catch
methods of T or its members or bases declare a return type
the type must not be void and must be convertable to the
return type of F.

Note that all of the above could be determined at compile time,
and anything done using exception handler objects could be
written using try...catch statements within the body of a
function itself. This implies that the actual run-time mechanism
for exceptions need not change.


MISCELLANEOUS

Only automatic variables (and their bases and members) could
catch exceptions. Static objects and objects allocated on the
free store could not. For example, if a local variable of type
Z has a destructor that deletes a pointer to X, any possible
catch methods implemented by X would not be called in the event
of an exception. This restriction is necessary for several
reasons, one being that the defintion of X might not be accessible
to the function that instantiates Z, which means the compiler
would not be able to determine what exception handlers exist
at compile time.

The access rules for catch methods would be somewhat similar
to destructors. For a function to instantiate a /local/ variable,
all catch methods would have to be accessible to the function.
For example, you couldn't intantiate a local variable with a
private catch method.


CONCLUSION

I realize this would be a non-trivial language change, but
believe it would be quite useful. It seems to me catch methods
could help automate exception handling in much the way RAII
has enabled us to automate resource allocation.

[ Send an empty e-mail to c++-...@netlab.cs.rpi.edu for info ]
[ about comp.lang.c++.moderated. First time posters: do this! ]

Hyman Rosen

unread,
Mar 8, 2002, 8:52:51 PM3/8/02
to
Niklas Borson wrote:
> The above works, but obscures the main intent of the function with a
> bunch of boilerplate code designed to enforce what you might call an
> "exception policy". It would be better if we could encapsulate the
> exception policy

There's a standard idiom for encapsulating
exception policies - use a function.

void HandleException() throw(MyException)
{
try {
// Rethrow current exception
throw;


}
catch (MyException& x) {
throw;
}
catch (std::exception& x) {
throw MyException(x);
}
catch (...) {
throw MyException();
}
}

void MyFunc() throw(MyException)
{
try {
// code which might throw anything
} catch(...) {
HandleException();

Garry Lancaster

unread,
Mar 9, 2002, 8:02:22 AM3/9/02
to
Hyman Rosen:

> There's a standard idiom for encapsulating
> exception policies - use a function.
>
> void HandleException() throw(MyException)
> {
[snip exception handling code]

> }
>
> void MyFunc() throw(MyException)
> {
> try {
> // code which might throw anything
> } catch(...) {
> HandleException();
> }
> }

It's a good idiom. However a word of warning to
anyone hoping to use it on MSVC6: this compiler's
non-standard treatment of exceptions prevents the
idiom from working. Haven't tried it on 7 yet.

Kind regards

Garry Lancaster
Codemill Ltd
Visit our web site at http://www.codemill.net

Roger Orr

unread,
Mar 9, 2002, 6:02:06 PM3/9/02
to
Garry Lancaster wrote

>Hyman Rosen:
> > There's a standard idiom for encapsulating
> > exception policies - use a function.
> >
> > void HandleException() throw(MyException)
> > {
>[snip exception handling code]
> > }
> >
> > void MyFunc() throw(MyException)
> > {
> > try {
> > // code which might throw anything
> > } catch(...) {
> > HandleException();
> > }
> > }
>
>It's a good idiom. However a word of warning to
>anyone hoping to use it on MSVC6: this compiler's
>non-standard treatment of exceptions prevents the
>idiom from working. Haven't tried it on 7 yet.


I have, and it does appear that MSVC 7 finally fixes the problem :)
- which was that the destructor for the re-thrown object was called twice.

However throw specs are still not implemented :(

I do prefer the look of MyFunc in the OP's version:-

> void MyFunc() throw(MyException)
> {
> ExceptionFilter<MyException> ex;

> ... // code that might throw anything
> }

since the try ... catch's are removed. This seems better to me since
the purpose of the filter is simply to change the type of the exception
and not to modify the flow of control.

Sadly you can't do exception mapping in a destructor since too many
bits are missing. When a dtor is called during stack unwinding:-
(a) you can only detect the presence of an uncaught exception -
and nothing else
(b) destructors can't throw during stack unwinding

It appears that the 'unexpected_handler' function was supposed to
help in this case. I wrote a simple class which calls
set_unexpected in the ctor and resets it in the dtor:-

template <class Ex>
class ExceptionFilter
{

private:
std::unexpected_handler previousHandler;
public:
ExceptionFilter()
{
previous = std::set_unexpected( f );
}
~ExceptionFilter()
{
std::set_unexpected( previousHandler );
}
static void f()
{
throw Ex();
}
};

and MyFunc as in the OP

However I can't get this to work properly - the destructor seems to be
called
_before_ the call to unexpected is made (at least on gcc).

Roger Orr
--
MVP in C++ at www.brainbench.com

Martin Vuille

unread,
Mar 9, 2002, 8:42:35 PM3/9/02
to
"Garry Lancaster" <glanc...@ntlworld.com> wrote in news:3Ali8.1247
$y76.2...@news6-win.server.ntlworld.com:

> It's a good idiom. However a word of warning to
> anyone hoping to use it on MSVC6: this compiler's
> non-standard treatment of exceptions prevents the
> idiom from working.

Could you please elaborate. It seems to me I've seen this
done without any ill effect.

MV

Niklas Borson

unread,
Mar 10, 2002, 5:06:29 AM3/10/02
to
Hyman Rosen <hyr...@mail.com> wrote in message news:<3C88E4CC...@mail.com>...

> Niklas Borson wrote:
> > The above works, but obscures the main intent of the function with a
> > bunch of boilerplate code designed to enforce what you might call an
> > "exception policy". It would be better if we could encapsulate the
> > exception policy
>
> There's a standard idiom for encapsulating
> exception policies - use a function.
>
> void HandleException() throw(MyException)
> {
> try {
> // Rethrow current exception
> throw;
> }
> catch (MyException& x) {
> throw;
> }
> catch (std::exception& x) {
> throw MyException(x);
> }
> catch (...) {
> throw MyException();
> }
> }
>
> void MyFunc() throw(MyException)
> {
> try {
> // code which might throw anything
> } catch(...) {
> HandleException();
> }
> }

That's very cool -- and new to me, I must confess! I still think
exception handler objects would be a nice convenience, but since
I don't expect such a language change any time soon, I expect to
find this idiom very useful in the mean time. Thanks for sharing!

Garry Lancaster

unread,
Mar 10, 2002, 9:27:50 AM3/10/02
to
Garry Lancaster:

> > It's a good idiom. However a word of warning to
> > anyone hoping to use it on MSVC6: this compiler's
> > non-standard treatment of exceptions prevents the
> > idiom from working.

Martin Vuille:


> Could you please elaborate. It seems to me I've seen this
> done without any ill effect.

Sure. Build and run the following program:

<program>
#include <iostream>
using namespace std;

class Ex
{
public:
explicit Ex() { cout << "Ctor.\n"; }
Ex(const Ex&) { cout << "Copy ctor.\n"; }
~Ex() { cout << "Dtor.\n"; }
};

void Handler();

int main(int argc, char* argv[])
{
try
{
cout << "About to throw.\n";
throw Ex();
}
catch(...)
{
cout << "Caught.\n";
Handler();
}
return 0;
}

void Handler()
{
try
{
cout << "About to throw again.\n";
throw;
}
catch(...)
{
cout << "Caught again.\n";
}
}
</program>

At least on MSVC6 SP5 you get

About to throw.
Ctor.
Caught.
About to throw again.
Caught again.
Dtor.
Dtor.

In other words, as Roger Orr already mentioned, the
exception's destructor gets called twice. Nasty.

Kind regards

Garry Lancaster
Codemill Ltd
Visit our web site at http://www.codemill.net

0 new messages