12

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?

sbi
  • 219,715
  • 46
  • 258
  • 445
  • perhaps related http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2006/n2106.html and http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2179.html – sehe Apr 16 '15 at 12:04
  • 2
    OK, so for some inexplicable reason (inexplicable for me, anyway), the issue goes away when I derive `exception` ___non-virtually___ from `boost::exception`. I am baffled and would love to hear an explanation. – sbi Apr 16 '15 at 12:08
  • @sehe: I have skimmed through the documents but didn't see anything relevant. What are you hinting at? – sbi Apr 16 '15 at 12:09
  • looks like cloning is explicitly not a feature, with reasoning why (e.g. "Since the clone function is just a stopgap until 0x" [...] "No - but exceptions (only) will get a special built-in clone mechanism (n2179)" [here](http://boost.2283326.n4.nabble.com/Boost-Exception-and-support-for-cloning-td2635258.html)) hinting that it's solved in other ways. Reading it again it looks like `boost::copy_exception` might be involved (see also [Luc's answer](http://stackoverflow.com/a/9974050/85371)) – sehe Apr 16 '15 at 12:14
  • @sehe: But I am not cloning (i.e., creating a copy of an object of a derived class through a base class reference), but am simply and plainly copying an existing concrete object. – sbi Apr 16 '15 at 12:55
  • 3
    Can you provide a MCVE? It's difficult to make sense of this question. – Barry Apr 19 '15 at 23:35
  • Can you please give us a full working example code, std_lib_util::exception is unknown to me and if you try to look in reosultion problems this is of interest. In my opinion the boost::diagnostic_information has trouble to get the boost exception type from your inheritance hierarchy. – Sven Apr 20 '15 at 10:58
  • @Barry: Please see [my comment here](http://stackoverflow.com/questions/29673516/can-i-copy-construct-a-boostexception-with-the-error-info#comment48499442_29747354). I suppose it's a bug in the old boost library we're currently stuck with. Hopefully we can upgrade to an up-to-date version at the end of the year. – sbi May 12 '15 at 17:00
  • @Sven: Please see the above comment. (I am sure there's very good reasons for not being able to address two users in the same comment.) – sbi May 12 '15 at 17:00

1 Answers1

3

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:

  • g++ (Ubuntu 4.9.2-0ubuntu1~14.04) 4.9.2
  • boost 1.55

I think the problem is that you output diagnostic information inside the constructor and tag_test_int is not set jet.

WaeCo
  • 1,155
  • 1
  • 10
  • 21
  • So I am back home, but the time's up on this one. So rather than wasting half of the bounty, I am giving it to this answer, as it is the only one I got. I will go and look at the code when I find the time at work to do so and try to figure out how my code differs from yours. Thanks to you and everybody else who looked into this! – sbi Apr 26 '15 at 20:46
  • I finally took the time to hunt this down. [This code](http://coliru.stacked-crooked.com/a/12273f417d917419) works as expected on coliru, but it does not work on my platform. I can't help but assume that this is because we are stuck with boost 1.52, which supposedly has a bug. (We are also using an older compiler, but I doubt that this would cause such an error.) Since there's nothing we can do, we have, for the time being, to derive non-virtually. _Thanks again for your effort!_ – sbi May 12 '15 at 16:57
  • I have boost 1.48 on my dev system and it works like on cliru. So maybe you have to have a look at your compiler, mine was gcc version 4.6.3 (Ubuntu/Linaro 4.6.3-1ubuntu5). – Sven May 13 '15 at 13:01