Boost logo

Boost :

Subject: Re: [boost] [exception] Virtual inheritance with no defaultconstructors
From: Adam Badura (abadura_at_[hidden])
Date: 2009-05-13 04:32:49


> I came up with a solution which would involve renaming
> boost::exception_detail::clone_base to boost::exception_clone_base,
> documenting its requirements, and modifying
> boost::enable_current_exception to be able to detect if the type of
> its argument derives from boost::exception_clone_base. In that case,
> it wouldn't wrap it, and thus it is up to the user-defined exception
> type to initialize any virtual bases, including the ones that can't be
> default-initialized.

Good idea. Should have thought about it myself. Somehow I failed to do this.

> See the attached patch -- I've only tested with MSVC but it should
> work with other compilers too. I'm not committing this to trunk yet.
> It seems acceptable, but I'm not sure if this extra complication is
> worth the trouble. I doubt that user code that requires a similar
> workaround exists in practice.

    How to apply the patch to normally downloaded Boost? We use an official
release (1.39) which we build ourselves. And the use it. Is is enough to
replace the original files with those in the patch?
    We work on MS VC as well (2005 with SP).

    I haven't seen yet the changes but are they so complicated that you have
to think whether they are worth applying?

> Was your observation mostly theoretical, or was it based on trouble
> you had with existing code?

    Currently I am introducing exception handling into already existing
project (rather large but not extremely large). The project used exceptions
before but we wanted to establish a common philosophy and policies for
throwing, catching handling and so on, refractor the code appropriately and
provide documentation for all this.
    So I started playing with the task. I decided to use Boost.Exception.
The arbitrary data injection was what convinced me (however I am somewhat
afraid catastrophic cases when std::bad_alloc is thrown during data
injection and original exception is lost).
    One of exceptions I defined was WinAPIException which was constructible
from DWORD error code. I wanted it to return the stored error code and
return retrieved error message upon request basing on the error code. This
cannot be provided without initializing the stored error code and thus my
post.

    The solutions I had were:

1)

    To inherit from WinAPIException non-virtually. This seemed rather safe
as I suspected that likelihood that a class will in result inherit from
WinAPIException more then once is rather low and the base classes like
std::exception and boost::exception would be inherited virtually anyway.
    But this seemed inconsistent and does not go well with other components.
I had for example CombinedException which took as template parameters some
exception classes and inherit from them all. This was useful since I had
clear separation on logic errors and runtime errors (expressed in
appropriate base class) and sometimes it was not known for an exception
class whether it will be thrown in logic error or runtime error until it was
actually thrown.
    The WinAPIException is a good example. It might be due to an WinAPI
error like ERROR_INVALID_HANDLE which is (usually) due to a logic error or a
ERROR_FILE_NOT_FOUND which is (usually) due to a runtime error.
    This could be decided only in place the exception is thrown so I had
something like:

if ( errorCode == ERROR_INVALID_HANDLE )
    throw CombinedException<
        MyComponentException,
        WinAPIException,
        LogicException
>( WinAPIException( errorCode ) );
else if ( errorCode == ERROR_FILE_NOT_FOUND )
    throw CombinedException<
        MyComponentException,
        WinAPIException,
        IOException,
        RuntimeException
>( WinAPIException( errorCode ) );
else ....

    (The code skips additional staff like using boost::throw_exception,
adding function/file name/line number data and so on...)
    The CombinedException class is build using Boost.Preprocessor and it can
copy-construct any subset of base classes (the caller knows which subset is
required - in this case only WinAPIException). It saves us from defining an
enormous number of exception classes or reducing type information (tagging)
of the thrown exceptions.
    And to sum up having to inherit from WinAPIException non-virtually makes
the CombinedException more complicated as it has to somehow know which
classes are to be inherited virtually and which not.

2)

    Skip having non-trivial WinAPIException and use the Boost.Exception data
injection alone.
    This makes however throwing much more complicated. How can I be sure
that thrower will in fact inject the error code? I cannot. As I don't like
trusting and hoping for the caller to remember to do the right thing I would
have to provide some functions/macros to do that. This again makes it a
little bit more complicated.
    Also this does not go well with legacy code/third party code/different
policy code which might use non-trivial exceptions types. We would not be
able to use them in such case. And I don't like when some policy does not
give options.

    However note that I still experiment with the code trying to establish
the policy. I still have to answer some questions. General requirements are
to (random order):
1) Make declaring exceptions classes simple and short when no additional
data is required. Empty classes (with base classes) are great here and data
injection goes well with it.
2) Make throwing expressions short and simple. Here data injection and
composites like the mentioned CombinedException are not good at all (just
look at the above example code!).
3) Support exception wrapping on the module boundary (so that module has to
care only for exceptions of the modules it uses directly) - and this I still
don't know how to do as I have no good ideas for wrapping which will not
loose a lot of data on the original exception.
4) Go well with somewhat different approaches to exceptions from "read-only
modules".
5) Provide as much diagnostic data as possible. Currently it seems that we
will almost never use any data in exceptions for other then diagnostic
reasons. But that still has to be investigated.

    Adam Badura


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