4

I'm integrating boost::exception into existing code. Some of the code now uses BOOST_THROW_EXCEPTION, but some might still throw standard std::exception.

I want to add error_info at an intermediary catch site. According to the docs, if the exception is a boost::exception, I can just do this:

try {
    do_something()
}
catch( boost::exception & e ) {
    e << boost::errinfo_file_name(file_name);
    throw;
}

But that will only add the info to the boost exceptions. I want to add it to the std::exception as well. What's the cleanest way to do that? Here's one way, but it results in some code duplication:

try {
    do_something()
}
catch( boost::exception & e ) {
    e << boost::errinfo_file_name(file_name);
    throw;
}
catch( std::exception & e ) {
    throw enable_error_info(e) << boost::errinfo_file_name(file_name);
}

Is there a method that's equivalent to "give me the current exception as a boost exception, or create a boost exception out of it if it isn't one"?

EDIT: boost::enable_error_info() kinda does that but returns a copy of the original exception, which slices off the boost::exception part of the exception I'm catching. Case in point:

int main()
{
    try {
        try {
            BOOST_THROW_EXCEPTION( std::runtime_error( "foo" ) );
        }
        catch( std::exception & e ) {
            std::cerr << e.what() << std::endl; // "foo" 
            if( const char* const* function = boost::get_error_info<boost::throw_function>(e) ) std::cerr << *function << std::endl; // "int main()"
            throw boost::enable_error_info(e) << boost::errinfo_file_name("bar");
        }
    }
    catch( std::exception & e ) {
        std::cerr << e.what() << std::endl; // "std::exception" 
        if( const char* const* function = boost::get_error_info<boost::throw_function>(e) ) std::cerr << *function << std::endl; // NOTHING
    }

    return 0;
}

EDIT: I tried using boost::current_exception(), and it slices things off too. Basically, any attempt to copy an exception will loose some of the data because of the slicing caused by multiple inheritance. Same reason as why the docs say you should always rethrow using throw instead of throw e. So I really don't want to incur any copying unless it's necessary.

Ideally, I'd like to write the following, where current_exception_as_boost_exception() returns a reference to the current exception if it is already a boost::exception, otherwise returns the result of calling boost::enable_error_info on it.

try {
    do_something()
}
catch( std::exception & e ) {
    throw current_exception_as_boost_exception() << boost::errinfo_file_name(file_name);
}

Is that what boost::enable_current_exception is for? It's really unclear what its purpose is, and it's not used in any of the tutorials.

user1924406
  • 111
  • 1
  • 8
  • 4
    You seem to say that `enable_error_info()` works but is "overkill." Who cares if it's overkill, since exceptions should normally not be happening, so performance is not really a concern? – John Zwinck Oct 21 '14 at 02:23
  • I'm not so much concerned about performance than about the possibility of loosing some information during the copy. `boost::copy_exception()` (I'm assuming this is what `enable_error_info()` uses) may loose type information in the process (see http://stackoverflow.com/questions/9973499/why-do-i-lose-type-information-when-using-boostcopy-exception). So I'd rather avoid the copy unless I have some guarantee that `enable_error_info()` is not going to drop anything. – user1924406 Oct 21 '14 at 03:32

1 Answers1

1

Here is a solution that does what I want. But if feels like I'm reinventing something here. Isn't there a built-in way to achieve the same thing?

struct rethrow
{
    rethrow()
    {
        try{ throw; }
        // Already a boost::exception
        catch( boost::exception& ) {} 
        // Something else. Make it a boost::exception
        catch( ... ) { ptr = boost::current_exception(); } 
    }

    template<class T> 
    rethrow const& operator<<( const T& t ) const
    {
        try
        {
            re();
        }
        catch( boost::exception& e )
        {
            e << t;
        }
        return *this;
    }

    ~rethrow()
    {
        re();
    }

private:
    void re() const
    {
        if( !ptr ) throw;
        else boost::rethrow_exception( ptr );
    }

    boost::exception_ptr ptr; 
};


int main()
{
    try {
        try {
            throw std::runtime_error( "foo" ); // or BOOST_THROW_EXCEPTION( std::runtime_error( "foo" ) );
        }
        catch( std::exception & e ) {
            rethrow() << boost::errinfo_file_name("bar");
        }
    }
    catch( std::exception & e ) {
        std::cerr << __LINE__ << ": caught " << e.what() << std::endl; // "caught foo"
        if( const char* const* function = boost::get_error_info<boost::throw_function>(e) ) std::cerr << __LINE__ << ": throw from " << *function << std::endl; // "throw from int main()" (when using BOOST_THROW_EXCEPTION)
        if( std::string const* fileName = boost::get_error_info<boost::errinfo_file_name>(e) ) std::cerr << __LINE__ << ": trying to open " << *fileName << std::endl; // "trying to open bar"
    }

    return 0;
}
user1924406
  • 111
  • 1
  • 8