class GAGenome {
virtual void method(){};
};
template <class T>
class GAArray {
};
template <class T>
class GA1DArrayGenome : public GAArray<T>, public GAGenome {
};
int main() {
GA1DArrayGenome<float> genome;
const GAGenome & reference = genome;
auto cast = dynamic_cast<const GA1DArrayGenome<int> &>(reference);
}
This obviously wrong program (since template parameter is different) crashes with
terminate called after throwing an instance of 'std::bad_cast'
what(): std::bad_cast
Aborted (core dumped)
Is there a way how to get precise diagnostics of what went wrong, beyond the runtime error message? Something, that can point out the int/float mistake to me? I am looking for a descriptive error message like
const GA1DArrayGenome<float> & cannot be cast to const GA1DArrayGenome<int> &
Even better, since C++ types can get hairy at times, the tool could notice the precise discrepancy in the template parameter.
You can also abandon direct uses of dynamic_cast and wrap it in your own template machinery:
#include <sstream>
class my_bad_cast: public std::bad_cast {
public:
my_bad_cast(char const* s, char const* d): _source(s), _destination(d) {
#ifdef WITH_BETTER_WHAT
try {
std::ostringstream oss;
oss << "Could not cast '" << _source
<< "' into '" << _destination << "'";
_what = oss.str();
} catch (...) {
_what.clear();
}
#endif
}
char const* source() const { return _source; }
char const* destination() const { return _destination; }
#ifdef WITH_BETTER_WHAT
virtual char const* what() const noexcept {
return not _what.empty() ? _what.c_str() : std::bad_cast::what();
}
#endif
private:
char const* _source;
char const* _destination;
#ifdef WITH_BETTER_WHAT
std::string _what;
#endif
// you can even add a stack trace
};
template <typename D, typename S>
D my_dynamic_cast(S&& s) {
try {
return dynamic_cast<D>(std::forward<S>(s));
} catch(std::bad_cast const&) {
throw my_bad_cast(typeid(S).name(), typeid(D).name());
}
}
You can load your program (compiled with debug information, e.g. -g in gcc and glang) in gbd, tell gdb to catch exceptions with catch throw and then look at the call stack to see exactly where the exception was thrown.
std::bad_cast is thrown when a dynamic_cast fails at runtime.
Related
I read several discussion on the use of exception and error code and when each of them is suitable.
I believe in my case exceptions are better, but I have one specific problem:
if I throw a std::runtime_error("this is an error") and then I catch it, I can not find what was the error, I just understand that there was an error.
What I am looking is an exception that I can throw and add an error code to it so later I can check the error code when I catch it, some thing such as this:
enum errorCodes
{
error_1,
error_2,
error_3
}
try
{
throw std::runtime_error(error_1,"can not do the job!");
}
catch (std::runtime_error & err)
{
switch (err.errorCode)
{
case error_1:
// I can not contunue, so re throw it
rethrow err;
case error_2:
// it is not important for me, so I can continue
break;
case error_3:
//Oh, I need to do something before continue
re_init();
break;
default:
rethrow err;
}
I can write such exception, but before doing this, I am wondering if such type of exception exist in STL or BOOST?
Is there any better way of doing this ( I am not interested to throw different type of exception, so catching different type of exception is not a solution from my point of view.
Yes, there's std::system_error. It's derived from std::runtime_error. It's pretty Unix-land-oriented but it does support error codes in general, and I suggest that you use it that way.
The following code demonstrates how to define one's own error category for application specific error codes:
#include <string>
#include <system_error>
#include <typeinfo>
namespace my{
struct Error_code
{
enum Enum
{
error_1 = 101,
error_2 = 102,
error_3 = 103
};
};
class App_error_category
: public std::error_category
{
using Base = std::error_category;
public:
auto name() const noexcept
-> char const*
override
{ return "App error"; }
auto default_error_condition( int const code ) const noexcept
-> std::error_condition
override
{ (void) code; return {}; }
auto equivalent( int const code, std::error_condition const& condition ) const noexcept
-> bool
override
{ (void) code; (void) condition; return false; }
// The intended functionality of this func is pretty unclear.
// It apparently can't do its job (properly) in the general case.
auto equivalent( std::error_code const& code, int const condition ) const noexcept
-> bool
override
{ return Base::equivalent( code, condition ); }
auto message( int const condition ) const
-> std::string
override
{ return "An application error occurred, code = " + std::to_string( condition ); }
constexpr
App_error_category(): Base{} {}
};
auto app_error_category()
-> App_error_category const&
{
static App_error_category the_instance;
return the_instance;
}
class App_error
: public std::system_error
{
using Base = std::system_error;
public:
auto app_error_code() const
-> Error_code::Enum
{ return static_cast<Error_code::Enum>( code().value() ); }
App_error( Error_code::Enum const code )
: Base{ code, app_error_category() }
{}
App_error( Error_code::Enum const code, std::string const& description )
: Base{ code, app_error_category(), description }
{}
};
} // namespace my
void foo()
{
try
{
throw my::App_error( my::Error_code::error_1, "can not do the job!" );
}
catch( my::App_error const& x )
{
switch( x.app_error_code() )
{
case my::Error_code::error_1:
// I can not contunue, so re throw it
throw;
case my::Error_code::error_2:
// it is not important for me, so I can continue
break;
case my::Error_code::error_3:
//Oh, I need to do something before continue
//re_init();
break;
}
}
}
#include <iostream>
#include <stdlib.h> // EXIT_SUCCESS, EXIT_FAILURE
using namespace std;
auto main()
-> int
{
try
{
foo();
return EXIT_SUCCESS;
}
catch( exception const& x )
{
cerr << "!" << x.what() << endl;
}
return EXIT_FAILURE;
}
With both Visual C++ 2015 and MingW g++ 6.4.0 this produces the output
!can not do the job!: An application error occurred, code = 101
In general it's more practical to define specific exception classes than to use error codes. However, for system error codes it's more practical to just pass these codes up with the exception. And for that the complexity shown above can be avoided, since the std::system_category is then eminently suitable.
In short, the complexity here stems from your requirement to not go with the flow, but in a direction somewhat against the current.
Consider using std::system_error out of the box.
Note that using simply throw; without an argument is a far better way of re-throwing your exception (you seem to be using throw err;) since the former will throw the exception by reference rather than by value: the latter can cause object slicing.
I'd like to implement an Exception class in C++ that mimics the one from .NET framework (and Java has something similar too), for the following purposes:
Exception chaining: I'd like to implement the concept of "exception translation", when exceptions caught at higher levels wrap and "translate" the lower level exceptions, also preserving these lower-level exceptions somehow (in the InnerException member, in this case). For this, there should be some mechanism to store inner exceptions along with each exception thrown at the upper level. InnerException member provides this in the implementation below.
Exception inheritance: there should be possible to derive IoException from Exception, and SerialPortException from IoException, for example. While this seems trivial, there should be ability to identify the type of caught exceptions dynamically (e.g. for logging purposes, or to display to user), preferably without the overhead of RTTI and typeid.
This is the sample exception handling logic I'd like to make possible:
try
{
try
{
try
{
throw ThirdException(L"this should be ThirdException");
}
catch(Exception &ex)
{
throw SubException(L"this should be SubException", ex);
}
}
catch(Exception &ex)
{
throw SubException(L"this should be SubException again", ex);
}
}
catch(Exception &ex)
{
throw Exception(L"and this should be Exception", ex);
}
and when catching the "outer-most" exception in the upmost layer I'd like to be able to parse and format whole exception chain through the InnerException member, to display something like this:
I've come up with the following implementation so far:
Small note: CString is Microsoft-specific string class (just for the people not familiar with Visual C++ stuff).
class Exception
{
protected:
Exception(const Exception&) {};
Exception& operator= (const Exception&) {};
public:
Exception(const CString &message) : InnerException(0), Message(message) {}
Exception(const CString &message, const Exception &innerException) : InnerException(innerException.Clone()), Message(message) {}
virtual CString GetExceptionName() const { return L"Exception"; }
virtual Exception *Clone() const
{
Exception *ex = new Exception(this->Message);
ex->InnerException = this->InnerException ? this->InnerException->Clone() : 0;
return ex;
}
public:
virtual ~Exception() { if (InnerException) delete InnerException; }
CString Message;
const Exception *InnerException;
};
Now what do we have here. Copy constructor and assignment operator are made protected to prevent copying. Each object will "own" its inner exception object (and delete it in destructor), so default shallow-copying would be unacceptable. Then we have two pretty standard-looking constructors and virtual destructor that deletes the InnerException object. Clone() virtual method is responsible for deep-copying the objects, primarily for storing the inner exception object (see the second constructor). And finally GetExceptionName() virtual method provides the cheap alternative to RTTI for identification of exception class names (I don't think this looks cool but I couldn't come up with better solution; for comparison: in .NET one could simply use someException.GetType().Name).
Now this does the job. But... I don't like this solution for one particular reason: the amount of coding needed for each derived class. Consider I have to derive SubException class, which provides absolutely zero additions to the base class functionality, it just provides the custom name ("SubException", which might be "IoException", "ProjectException", ...) to differentiate it for its usage scenario. I have to provide almost same amount of code for each of such exception class. Here it is:
class SubException : public Exception
{
protected:
SubException(const SubException& source) : Exception(source) {};
SubException& operator= (const SubException&) {};
public:
SubException(const CString &message) : Exception(message) {};
SubException(const CString &message, const Exception &innerException) : Exception(message, innerException) {};
virtual CString GetExceptionName() const { return L"SubException"; }
virtual Exception *Clone() const
{
SubException *ex = new SubException(this->Message);
ex->InnerException = this->InnerException ? this->InnerException->Clone() : 0;
return ex;
}
};
I don't like the fact that I have to provide protected copy constructor and assignment operator each time, I don't like the fact that I have to clone the Clone method each time, duplicating even the code of copying the base members (InnerException...), simply... I don't think this is the elegant solution. But I was unable to think of better one. Do you have any ideas how to implement this concept "properly"? Or maybe this is the best implementation of this concept that is possible in C++? Or maybe I'm doing this completely wrong?
P.S.: I know there exist some mechanisms in C++11 (also in Boost) for this purpose (exception chaining) with some new exception classes, but I'm primarily interested in custom, "old-C++-compatible" ways. But it would be good, in addition, if someone could provide any code in C++11 that accomplishes the same.
C++11 already has nested_exception. There was a talk about exceptions in C++03 and C++11 at Boostcon/C++Next 2012. Videos are on youtube:
http://www.youtube.com/watch?v=N9bR0ztmmEQ&feature=plcp
http://www.youtube.com/watch?v=UiZfODgB-Oc&feature=plcp
There is a lot of extra code, but the good thing is it's really EASY extra code that doesn't change at all from class to class, so it's possible to preprocessor macro it.
#define SUB_EXCEPTION(ClassName, BaseName) \
class ClassName : public BaseName\
{\
protected:\
\
ClassName(const ClassName& source) : BaseName(source) {};\
ClassName& operator= (const ClassName&) {};\
\
public:\
\
ClassName(const CString &message) : BaseName(message) {};\
ClassName(const CString &message, const BaseName &innerException) : BaseName(message, innerException) {};\
\
virtual CString GetExceptionName() const { return L"ClassName"; }\
\
virtual BaseName *Clone() const\
{\
ClassName *ex = new ClassName(this->Message);\
ex->InnerException = this->InnerException ? this->InnerException->Clone() : 0;\
return ex;\
}\
};
Then you can define various utility exceptions by just doing:
SUB_EXCEPTION(IoException, Exception);
SUB_EXCEPTION(SerialPortException, IoException);
Please don't follow boost::exception approach. Boost::exception is for different use case - in particular it's usefull when you want to collect precise exception context scatered over call stack. Consider the following example:
#include "TSTException.hpp"
struct DerivedException: TST::Exception {};
int main() try
{
try
{
try
{
try
{
throw std::runtime_error("initial exception");
}
catch(...)
{
throw TST::Exception("chaining without context info");
}
}
catch(...)
{
TST_THROW("hello world" << '!');
}
}
catch(...)
{
TST_THROW_EX(DerivedException, "another exception");
}
}
catch(const TST::Exception& ex)
{
cout << "diagnostics():\n" << ex;
}
catch(const std::exception& ex)
{
cout << "what(): " << ex.what() << endl;
}
The "exception chaining" solution as I understand it should produce output similar to this:
$ ./test
diagnostics():
Exception: another exception raised from [function: int main() at main.cpp:220]
Exception: hello world! raised from [function: int main() at main.cpp:215]
Exception: chaining without context info raised from [function: unknown_function at unknown_file:0]
Exception: initial exception
As you see there are exceptions chained to each other and diagnostic output contains all exceptions with context information and optional stack trace (not shown here, because it's compiler/platform dependent).
"Exception chaining" can be naturally achieved using new C++11 error handling features (std::current_exception or std::nested_exception). Here is implementation of TSTException.hpp (please bear with more source code):
#include <iostream>
#include <sstream>
#include <stdexcept>
#include <exception>
#include <vector>
#include <string>
#include <memory>
#include <boost/current_function.hpp>
#include <boost/foreach.hpp>
using namespace std;
namespace TST
{
class Exception: virtual public std::exception
{
public:
class Context
{
public:
Context():
file_("unknown_file"),
line_(0),
function_("unknown_function")
{}
Context(const char* file, int line, const char* function):
file_(file? file: "unknown_file"),
line_(line),
function_(function? function: "unknown_function")
{}
const char* file() const { return file_; }
int line() const { return line_; }
const char* function() const { return function_; }
private:
const char* file_;
int line_;
const char* function_;
};
typedef std::vector<std::string> Stacktrace;
//...
Exception()
{
initStacktraceAndNestedException();
}
explicit Exception(const std::string& message, const Context&& context = Context()):
message_(message),
context_(context)
{
message.c_str();
initStacktraceAndNestedException();
}
~Exception() throw() {}
//...
void setContext(const Context& context) { context_ = context; }
void setMessage(const std::string& message) { (message_ = message).c_str(); }
const char* what() const throw () { return message_.c_str(); }
void diagnostics(std::ostream& os) const;
protected:
const Context& context() const { return context_; }
const std::exception_ptr& nested() const { return nested_; }
const std::shared_ptr<Stacktrace>& stacktrace() const { return stacktrace_; }
const std::string& message() const { return message_; }
private:
void initStacktraceAndNestedException();
void printStacktrace(std::ostream& os) const;
std::string message_;
Context context_;
std::shared_ptr<Stacktrace> stacktrace_;
std::exception_ptr nested_;
};
std::ostream& operator<<(std::ostream& os, const Exception& ex)
{
ex.diagnostics(os);
return os;
}
std::ostream& operator<<(std::ostream& os, const Exception::Context& context)
{
return os << "[function: " << context.function()
<< " at " << context.file() << ':' << context.line() << ']';
}
void Exception::diagnostics(std::ostream& os) const
{
os << "Exception: " << what() << " raised from " << context_ << '\n';
if (const bool haveNestedException = nested_ != std::exception_ptr())
{
try
{
std::rethrow_exception(nested_);
}
catch(const TST::Exception& ex)
{
if(stacktrace_ && !ex.stacktrace())//if nested exception doesn't have stacktrace then we print what we have here
printStacktrace(os);
os << ex;
}
catch(const std::exception& ex)
{
if(stacktrace_)
printStacktrace(os);
os << "Exception: " << ex.what() << '\n';
}
catch(...)
{
if(stacktrace_)
printStacktrace(os);
os << "Unknown exception\n";
}
}
else if(stacktrace_)
{
printStacktrace(os);
}
}
void Exception::printStacktrace(std::ostream& os) const
{
if(!stacktrace_)
{
os << "No stack trace\n";
return;
}
os << "Stack trace:";
BOOST_FOREACH(const auto& frame, *stacktrace_)
{
os << '\n' << frame;
}
os << '\n';
}
void Exception::initStacktraceAndNestedException()
{
nested_ = std::current_exception();
if(const bool haveNestedException = nested_ != std::exception_ptr())
{
try
{
throw;
}
catch(const TST::Exception& ex)
{
if(ex.stacktrace())
{
stacktrace_ = ex.stacktrace();
return;
}
}
catch(...) {}
}
/*TODO: setStacktrace(...); */
}
}//namespace TST
#ifdef TST_THROW_EX_WITH_CONTEXT
#error "TST_THROW_EX_WITH_CONTEXT is already defined. Consider changing its name"
#endif /*TST_THROW_EX_WITH_CONTEXT*/
#define TST_THROW_EX_WITH_CONTEXT( \
CTX_FILE, CTX_LINE, CTX_FUNCTION, EXCEPTION, MESSAGE) \
do \
{ \
EXCEPTION newEx; \
{ \
std::ostringstream strm; \
strm << MESSAGE; \
newEx.setMessage(strm.str()); \
} \
newEx.setContext( \
TST::Exception::Context( \
CTX_FILE, CTX_LINE, CTX_FUNCTION)); \
throw newEx; \
} \
while(0)
#ifdef TST_THROW_EX
#error "TST_THROW_EX is already defined. Consider changing its name"
#endif /*TST_THROW_EX*/
#define TST_THROW_EX(EXCEPTION, MESSAGE) \
TST_THROW_EX_WITH_CONTEXT(__FILE__, __LINE__, BOOST_CURRENT_FUNCTION, EXCEPTION, MESSAGE)
#ifdef TST_THROW
#error "TST_THROW is already defined. Consider changing its name"
#endif /*TST_THROW*/
#define TST_THROW(MESSAGE) \
TST_THROW_EX(TST::Exception, MESSAGE)
I use compiler with partial C++11 support (gcc 4.4.7) so you can see some old style peaces of code here. Just for reference you can use the following compilation parameters to build this example (-rdynamic is for stack trace):
g++ main.cpp TSTException.hpp -rdynamic -o test -std=c++0x
Few years ago I wrote this: Unchaining Chained Exceptions in C++
Basically, the exceptions are not nested inside each other, because it would be difficult to catch the original one, but another mechanism keeps track of all the functions visited by the exception while it travels to its catch point.
A revisited version of that can be found in the library Imebra on Bitbucket, here and here.
Now I would rewrite that with some improvements (e.g. use local thread storage to keep the stack trace).
Using this approach allows you to catch the original exception that was thrown, but to still have the stack trace and possibly other information added by the functions visited by the exception while it travels back to the catch statement.
The new expression in the try block throws a bad_allocexception in my computer.
Note that the catch clause receives an exception object by value, not by reference. How come e.what() prints "bad allocation" ? I thought it would be sliced.
#include <iostream>
int main()
{
try
{
int* p = new int[0x1F000000];
}
catch(std::exception e)
{
std::cout << e.what() << std::endl;
}
}
Visual Studio (Dinkumware?) uses an implementation of std::exception that contains internal storage† for the message. (Complete with a non-standard constructor that accepts a string.)
Because of this, no virtual dispatch is actually needed to get the error message, it survives any slicing.
A more orthodox implementation would indeed print a generic exception message, because the derived object was sliced off. (Effectively, MS has made std::exception and std::runtime_error equivalent. There's nothing wrong with this, since the return value of std::exception::what is implementation-defined, but it explains your results.)
†Internal storage here is used loosely. It doesn't have an internal buffer, but it has a const char* and a bool. The const char* points to the message (the return value of what()), and the bool is a flag determining if the buffer should be deleted.
It's like this:
class msvc_exception // for exposition
{
public:
msvc_exception(const char* msg) :
mMsg(msg),
mDoDelete(false)
{}
msvc_exception(const std::string& msg) :
mMsg(copy_string(msg)),
mDoDelete(true)
{}
virtual ~msvc_exception()
{
if (mDoDelete)
delete [] mMsg;
}
virtual const char* what() const throw()
{
return mMsg ? mMsg : "unknown";
}
private:
const char* copy_string(const std::string& str)
{
const char* result = new char[str.size() + 1];
std::copy(str.begin(), str.end(), result);
result[str.size()] = 0; // null-terminate
return result;
}
};
You see now that bad_alloc works like this:
class msvc_bad_alloc : // for exposition
public msvc_exception
{
public:
msvc_bad_alloc() :
msvc_exception("bad_alloc") // note: a static string, no dynamic storage
{}
};
Slicing doesn't affect the message because the message "exists" in the base class.
Other compilers, like GCC and LLVM, implement it a bit more straight-forwardly:
class orthodox_exception
{
public:
orthodox_exception(){}
virtual ~orthodox_exception() {}
virtual const char* what() const throw()
{
return "orthodox_exception";
}
};
class orthodox_bad_alloc :
public orthodox_exception
{
public:
const char* what() const throw()
{
return "orthodox_bad_alloc";
}
};
Here, slicing would affect your outcome. (That said, after all this: always catch by reference.)
I have a singleton (I know that is a bad pattern). To control the cleaning process, I'm using a shared pointer. The relevant code is:
#ifndef _GLOBAL_LOG_H_
#define _GLOBAL_LOG_H_
namespace glog{
class CGlobalLog;
typedef boost::shared_ptr<CGlobalLog> globalLogPtr;
class CGlobalLog
{
private:
static globalLogPtr m_instance;
LogLevel minimiumLogLevel;
CGlobalLog(void);
static void deleter(CGlobalLog *ptr){
try{
delete ptr;
}
catch(std:: e)
{
std::cout << e.what() << "\n";
}
}
static void create() { m_instance.reset( new CGlobalLog, &CGlobalLog::deleter ); }
void addMessage_(const std::string& appender, LogLevel level /*= LOGLEVEL_INFO*/,const char* msg, va_list args );
~CGlobalLog(void);
public:
static globalLogPtr& getInstance();
void addMessage(const std::string& message, std::string appender, LogLevel level = LOGLEVEL_INFO);
};
globalLogPtr CGlobalLog::m_instance;
};
#endif // _GLOBAL_LOG_H_
The program works fine, but when program finish, an unhandled exception is thrown in this point:
static void deleter(CGlobalLog *ptr){
try{
delete ptr; //<-- Unhandled exception
}
catch(std:: e)
{
std::cout << e.what() << "\n";
}
}
The catch doesn't catch the exception so I don't know what to do to profile my error. The exact code where error is throw is a boost library file checked_delete.hpp, here:
// verify that types are complete for increased safety
template<class T> inline void checked_delete(T * x)
{
// intentionally complex - simplification causes regressions
typedef char type_must_be_complete[ sizeof(T)? 1: -1 ];
(void) sizeof(type_must_be_complete);
delete x;
}
How do I need to locate this error? Some ideas?
Thanks!!!
I generally don't expect to see a shared pointer on a singleton. Just returning a reference to your singleton and never keeping a reference to it laying around is a good practice.
struct Foo {
static Foo &instance() {
static Foo foo;
return foo;
}
};
struct Bar {
void someMethod() {
Foo &foo = Foo::instance(); // just grab a reference every time you need it
// ...
}
};
If you wish to keep the shared pointer and need to clean up resources in manual way, create a tear down method. The boost::shared_ptr will clean up the memory eventually.
Personally, I think using a shared pointer externally is inferior. I wrote some code to demonstrate a tear down and it didn't seem generally applicable without knowing why you need one.
If you want an explicit delete, then write one.
struct Foo {
static Foo *foo = 0;
static Foo &instance() {
if (!foo)
throw std::logic_error("Already deleted");
return *foo;
}
static void Init() {
if (foo)
throw std::logic_error("Already created");
foo = new Foo;
}
static void Destroy() {
if (!foo)
throw std::logic_error("Already deleted");
delete foo;
foo = 0;
}
};
In the case of logging, the logic errors should be superfluous. If logging isn't valid when you ask for it, then it is unlikely that your application is in a valid state.
I have recently started using boost::exception. Now I would like to use boost::errinfo_nested_exception to print information about the cause of the error. The problem is I can't figure out how to get information from the cause. I have tried the following with no success:
#include <iostream>
#include <boost/exception/all.hpp>
struct myex : public virtual boost::exception {};
int main()
{
myex cause;
cause << boost::errinfo_file_name("causefile.cpp");
try {
myex ex;
ex << boost::errinfo_nested_exception(boost::copy_exception(cause));
throw ex;
}
catch (myex& e) {
// Here I would like to extract file name from cause and print
// it in a nice way, but I cant figure out what to do with a
// boost::exception_ptr.
const boost::exception_ptr* c =
boost::get_error_info<boost::errinfo_nested_exception>(e);
// I cant do this:
// const std::string* file = boost::get_error_info<boost::errinfo_file_name>(*c);
// Nor this:
// const std::string* file = boost::get_error_info<boost::errinfo_file_name>(**c);
// This works fine and the nested exception is there, but that's not what I want.
std::cout << boost::diagnostic_information(e) << std::endl;
}
return 0;
}
You need to rethrow the nested exception and examine that:
const boost::exception_ptr* c =
boost::get_error_info<boost::errinfo_nested_exception>(e);
if(c) try {
boost::rethrow_exception(*c);
} catch(boost::exception const& e) { // or a type derived from it
const std::string* file = boost::get_error_info<boost::errinfo_file_name>(e);
// ...
} catch(...) {
// presumably you don't want the exception to escape if it is
// not derived from boost::exception
}
I personally use a get_error_info wrapper that returns the result of boost::get_error_info<some_error_info>(e), or if nothing is found the result of get_error_info<some_error_info>(nested) (recursive call here) or 0 if there is no nested exception (or it is not error_info-enabled).
Alternatively/as a complement, you can factor the checking code above (the different catch clauses) in a function:
std::string const* // or return a tuple of what you examined etc.
examine_exception()
{
try {
throw; // precondition: an exception is active
} catch(boost::exception const& e) {
// as above
return ...;
}
}
boost::diagnostic_information is the correct way to get a description afaik.
But you could also overload to_string for boost::error_info(T):
http://svn.boost.org/svn/boost/trunk/boost/exception/errinfo_errno.hpp