c++exceptionboostboost-exception

Can I copy-construct a boost::exception with the error info?


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?


Solution

  • 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.