Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Converting std::exception_ptr to boost::exception_ptr

Tags:

c++

c++11

boost

I want to use boost::promise::set_exception() that expects a boost::exception_ptr. The problem is that boost:exception_ptr seems to work correctly only if I wrap all my throws with enable_current_exception and I want to avoid that. (I wouldn't be able to do that for 3rd party libraries anyway.) I use std::exception_ptr/std::current_exception throughout my code, so I am looking for a way to pass std::exception_ptr where a boost:exception_ptr is expected. Something that does the following, but compiles:

boost::exception_ptr convert(std::exception_ptr ex) {
    try {
        std::rethrow_exception(ex);
    }
    catch(auto& ex) {
        try {
            throw boost::enable_current_exception(ex);
        }
        catch (...) {
            return boost::current_exception();
        }
    }
}

Do you have any idea how to do that?

Context:

I need boost::future::then(), so using a std::promise is unfortunately not an option (at least at the moment)

If you know a way to make boost::exception_ptr to rely on gcc 4.8 compiler support instead of enable_current_exception that would be an acceptable solution, as well

like image 969
kispaljr Avatar asked Feb 25 '14 09:02

kispaljr


2 Answers

Unfortunately, I don't think this is possible. However, I can offer you three possible solutions, sorted by convenience:

1. Catch a std::exception

boost::exception_ptr convert(std::exception_ptr ex)
{
    try {
        std::rethrow_exception(ex);
    } catch (const std::exception& e) {
        try {
            throw boost::enable_current_exception(e);
        } catch (...) {
            return boost::current_exception();
        }
    } catch (...) {
        try {
            throw boost::enable_current_exception(std::runtime_error("Unknown exception."));
        } catch (...) {
            return boost::current_exception();
        }
    }
}

int main()
{
    std::exception_ptr sep;

    try {
        throw std::runtime_error("hello world");
    } catch (...) {
        sep = std::current_exception();
    }

    boost::exception_ptr bep = convert(sep);

    try {
        boost::rethrow_exception(bep);
    } catch (const std::exception& e) {
        std::cout << e.what() << std::endl;
    }

}

This prints "std::exception" instead of "hello world", since information from derived classes (in this case, std::runtime_error) will be sliced away.

2. Throw the std::exception_ptr directly

boost::exception_ptr convert(std::exception_ptr ex)
{
    try {
        throw boost::enable_current_exception(ex);
    } catch (...) {
        return boost::current_exception();
    }
}

int main()
{
    std::exception_ptr sep;

    try {
        throw std::runtime_error("hello world");
    } catch (...) {
        sep = std::current_exception();
    }

    boost::exception_ptr bep = convert(sep);

    try {
        boost::rethrow_exception(bep);
    } catch (const std::exception_ptr& ep) {
        try {
            std::rethrow_exception(ep);
        } catch (const std::exception& e) {
            std::cout << e.what() << std::endl;
        }
    }

}

This version prints "hello world", albeit at the cost of an extra try/catch block. If error handling is done at a central location, maybe displaying a dialog box, I'd go for this solution. Until the boost authors add a constructor from std::exception_ptr to boost::exception_ptr, that's as good as it gets, I'm afraid.

Use boost::packaged_task instead of boost::promise

If you can live with using packaged_task, this solution works:

#define BOOST_THREAD_VERSION 4
#include <boost/thread.hpp>

int main()
{
    boost::packaged_task<int()> pt([] () -> int {
        throw std::runtime_error("hello world");
    });
    boost::future<int> f1 = pt.get_future();
    boost::future<int> f2 = f1.then([] (boost::future<int> f) {
        return f.get() + 1;
    });

    boost::thread(std::move(pt)).detach();

    try {
        int res = f2.get();
    } catch (const std::runtime_error& e) {
        std::cout << e.what() << std::endl;
    }

}

Prints "hello world" and allows you to use fut.then().

like image 164
marton78 Avatar answered Nov 10 '22 04:11

marton78


I found not the best solution, but it works.

  1. Crete template specialization.
namespace boost::exception_detail
{
template<>
class clone_impl< std::exception_ptr > : public clone_base
{
   std::exception_ptr mExceptionPtr;

public:
   clone_impl( std::exception_ptr exception_ptr )
      : mExceptionPtr{ std::move( exception_ptr ) }
   {
   }
   clone_base const* clone() const
   {
      return new clone_impl( *this );
   }

   void rethrow() const
   {
      std::rethrow_exception( mExceptionPtr );
   }
};

int clone_current_exception_non_intrusive( clone_base const*& cloned )
{
   cloned = new clone_impl< std::exception_ptr >{ std::current_exception() };
   return clone_current_exception_result::success;
}
} 
  1. Add BOOST_ENABLE_NON_INTRUSIVE_EXCEPTION_PTR definition
like image 1
Maxim Kapitonov Avatar answered Nov 10 '22 03:11

Maxim Kapitonov