
On Wed, May 13, 2009 at 1:32 AM, Adam Badura <abadura@o2.pl> wrote:
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.
I think you can use http://gnuwin32.sourceforge.net/packages/patch.htm to apply patch files on Windows.
I haven't seen yet the changes but are they so complicated that you have to think whether they are worth applying?
Not complicated, but it seems to me that it is unlikely that anyone would need these changes in practice and if that's the case I'd rather not commit them. Consider the downsides of having to document the clone_base type; after all, once compilers start supporting exception_ptr natively, all of these concerns will disappear.
Was your observation mostly theoretical, or was it based on trouble you had with existing code?
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.
Here's how I handle this type of errors -- an example of calling TerminateProcess: struct exception_base: virtual std::exception, virtual boost::exception { }; struct win32_error: virtual exception_base { }; typedef boost::error_info<struct tag_win_last_error,unsigned> win32_last_error; typedef boost::error_info<struct tag_c_function,char const *> c_function; .... if( !TerminateProcess(hProcess,uExitCode) ) BOOST_THROW_EXCEPTION( win32_error() << win32_last_error(GetLastError()) << c_function("TerminateProcess")); A main advantage of Boost Exception is that your exception types don't have to have any data members or constructors; see http://www.boost.org/doc/libs/release/libs/exception/doc/exception_types_as_....
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.
My approach is to write wrappers for WIN32 (and other C functions) I call, which throw exceptions on failure. So, instead of calling TerminateProcess, I call win32_TerminateProcess, which always throws on error and adds the relevant error code, etc. Your concern about possibly forgetting to add the error code is valid, but if you call C functions directly (without a wrapper to throw on failure) you have the problem that users might forget to check for errors altogether -- and thus forget to throw, which is a bigger problem than forgetting to add the error code to the exception. If you have a few common items that should all be added to a particular class of exceptions, you can use boost::tuple of boost::error_info objects of types that don't have default constructors, and while you can still "forget" to add the tuple itself, you can't forget to add a particular member of the tuple (I think). See http://www.boost.org/doc/libs/release/libs/exception/doc/tuple_operator_shl...., and "Adding Grouped Data to Exceptions" in http://www.boost.org/doc/libs/release/libs/exception/doc/tutorial_transporti....
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.
In my experience most legacy code doesn't use virtual inheritance in exception types. If that's the case, I don't see a reason to change it to use virtual inheritance.
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.
Wrapping is not a good idea. If a module can handle an exception -- great, if not -- it shouldn't interfere (by changing the original exception's type.)
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.
See http://www.boost.org/doc/libs/release/libs/exception/doc/tutorial_diagnostic.... Emil Dotchevski Reverge Studios, Inc. http://www.revergestudios.com/reblog/index.php?n=ReCode