Boost logo

Boost :

Subject: [boost] Any interest in trap_exceptions functionality?
From: Andy Venikov (avenikov_at_[hidden])
Date: 2010-07-30 16:21:13


Hi,
  I often find myself wanting a generic way to catch and process exceptions.
   For example, imaging having to export "C" interface from a C++
library. You would create a bunch of extern "C" functions that would
invoke the right C++ code. One of the things that needs to be done in
every such function is to catch and process all exceptions. Most of the
exceptions-handling code would look the same or very similar (the same
list of exceptions to catch, same way to handle given exception types).
   C++ to C is not the only place where you'd need this. Most of the
module boundaries might want to trap exceptions.
   The first thing that developers do in these cases is they start
generalizing the handling part. I.e., you'd create a separate
function/functor that would handle an exception of a certain kind.
Unfortunately it still leaves you with a barage of try/catch()
statements in every function that doesn't want to leak exceptions. This
creates not only the unpleasant look of a lot of catches (one might
argue this point), but it creates a lot of repetition - you need to
create catch statement for every exception you want to handle.
   It would be nice if I could assemble all the exceptions I want to
handle (honoring their order) and the way to handle them in one single
place under a single object. And then invoke that object on all
functions that need to be called.
   I recently took a crack at this task and came up with a generic way
to trap exception.

Here's a brief API:

trap_exceptions(<list-of-exception-handlers>);

The previous line would create a callable object.
The parameters are a variable number of exceptions handlers that could
be either function pointers, references to functions or functors. One
thing they have in common though is the signature: the signature should
be "void(T)", where T is the type of the exception that the handler
will handle. This "T" type is what the trap_exceptions-created callable
object will use in the "catch" statements. Other important notes: the
order of handlers is important as this will be the order of "catch"
statements (i.e., trap_exceptions(HandleBase, HandleDerived) will hide
all Derived exceptions). Whether the exception is caught by reference or
by value is also controlled by the handler's signature. Signature
"void()" means catch all exceptions indiscriminately. Specifying an
empty parameter list will also mean to catch all exceptions
indiscriminately.

  The result of the trap_exceptions call can be assigned to a variable:

auto ex_trap = trap_exceptions(HandleEx1, HandleEx2, HandleEx3);

//BOOST_AUTO can be used too. And I think it will be possible to use
//it without registering HandleEx1 etc.)

This object can now be used on multiple functions of any arity and any
return type. Functors are supported too.

ex_trap(func1); //will create a delayed-call object that upon invocation
will call func1 and catch exceptions Ex1, Ex2 and Ex3

So, if func1 has signature int(int, char*, float), then

ex_trap(func1)(1, "abc", .5);

will call function func1 with the given set of arguments and catch
afore-mentioned exceptions.

The return type undergoes a significant transformation though.
Since the return from this delayed function call now returns more
information than before (the additional part being whether an exception
has been thrown or not), this additional information is conveyed through
the use of boost::optional. In the case of ex_trap+func1, the result
will be boost::optional<int>. Functions/functors returning void have a
special treatment though since unfortunately it's illegal to instantiate
boost::optional<void>. For functions that return void, the return type
of the delayed call will be a simple bool.

There are some return types that have a special value that indicates
"error" and could be used to indicate that an exception occured. In this
case, just for convenience purposes, it'll be possible to specify this
value and the return type will become the return type of the inner
function, not optional<T>. I.e:

//Let's say that -1 can be used to indicate that an exception has
//occured, then

ex_trap(func1, -1)(1, "abc", .5);//will have a return type of int.
                   //-1 will be returned if an exception has been thrown

and now all places that had

return func1(1, "abc", .5);

could simply be replaced with

return ex_trap(func1, -1)(1, "abc", .5); //without using boost::optional

I think one other use of this functionality can be in functional-style
programming in C++. While it's trivial to convert syntactic constructs
like "if", "while", "switch", and "for" to functional representations
(i.e. if_(f1(), f2(), f3()); which means
if (f1())
    f2();
else
   f3();
), it's a lot harder to create a functional representation of try/catch
construct. trap_exceptions is an attempt at that.
To illustrate that point more, say we have a tuple (or a fusion-like
container) which contains a collection of functions/functors. Using
fusion::transform it's possible to add certain qualities to these
functions. With trap_exceptions, it'll be possible to add a "no
exception leak" quality to this set of functions.

Do you think a functionality like that will be useful for boost?

Any comments are appreciated.

Thanks,
    Andy.

P.S. Currently I implemented this functionality in terms of C++0x. But I
think it should be possible to use Boost to make it C++03 compatible.


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