Consider the following code employing boost's exception class:
class exception : virtual public boost::exception {
// ...
};
template<typename Exc>
class exception_impl : virtual public std::exception
, public Exc {
public:
exception_impl(const Exc& exc) : Exc(exc) {}
virtual const char* what() const throw() {return "blah";}
};
(In reality this code is more complex. For example, exception_impl
only derives from std::exception
if the latter isn't already a direct or indirect base class of Exc
. But this just distracts from the problem I have, so I have skipped over it.)
Given this, I can now derive my own exception classes:
class some_exception : public exception {
// ...
};
And use them:
struct tag_test_int;
typedef boost::error_info<tag_test_int,int> test_int_info;
void f()
{
boost::throw_exception( exception_impl<some_exception>() << test_int_info(42) );
}
However, it turns out that the resulting exception does not have the test_int_info
object. So I changed the exception_impl
constructor to provide some diagnostic information:
exception_impl(const Exc& exc)
: Exc(exc) {
std::cerr << "========================================================================\nexc:\n";
std::cerr << boost::diagnostic_information(exc);
std::cerr << "========================================================================\n*this:\n";
std::cerr << boost::diagnostic_information(*this);
std::cerr << "========================================================================\n";
}
This indeed shows that the information gets lost when I copy the Exc
object into the exception_impl
base class object:
======================================================================== exc: Throw location unknown (consider using BOOST_THROW_EXCEPTION) Dynamic exception type: some_exception [tag_test_int*] = 42 ======================================================================== *this: Throw location unknown (consider using BOOST_THROW_EXCEPTION) Dynamic exception type: exception_impl std::exception::what: "blah"
IIRC, exception objects have to be copyable according to the standard and, disregarding possible optimizations, the result of a throw expression is copied. So boost's exceptions must be copyable and they certainly do not lose their information along the way. I must be missing something fairly obvious here.
What am I doing wrong?
Works perfecly fine for me:
(I had to add a default constructor to exception_impl)
#include <iostream>
#include <exception>
#include <boost/exception/all.hpp>
using std::cout;
class myException : public virtual boost::exception {
};
template <class T>
class exception_impl : public virtual std::exception, public T {
public:
exception_impl() {}
exception_impl(const T& ex) : T(ex) {}
virtual const char* what() const throw() {return "blah";}
};
class some_exception : public myException {
};
struct tag_test_int;
typedef boost::error_info<tag_test_int,int> test_int_info;
void f()
{
boost::throw_exception( exception_impl<some_exception>() << test_int_info(42) );
}
int main() {
try {
f();
} catch (boost::exception& e) {
cout << boost::diagnostic_information(e);
}
return 0;
}
Output:
Throw location unknown (consider using BOOST_THROW_EXCEPTION)
Dynamic exception type: N5boost16exception_detail10clone_implI14exception_implI14some_exceptionEEE
std::exception::what: blah
[P12tag_test_int] = 42
Compiled using:
I think the problem is that you output diagnostic information inside the constructor and tag_test_int is not set jet.