Search code examples
c++boostc++11

Converting std::exception_ptr to boost::exception_ptr


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


Solution

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