45

This is the code I have.

try
{
// code throws potentially unknown exception
}
catch (...)
{
    std::exception_ptr eptr =  std::current_exception();
        // then what ?
}

Ideally, I would like to get the string associated to the exception if it is a std::exception.

Ram
  • 3,045
  • 3
  • 27
  • 42
  • 1
    There is a proposal to add type introspection for std::exception_ptr: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p0933r0.pdf – Emile Cormier Oct 07 '20 at 23:51

4 Answers4

33

// then what ?

here is what:

#include <exception>
#include <stdexcept>
#include <iostream>
#include <string>

std::string what(const std::exception_ptr &eptr = std::current_exception())
{
    if (!eptr) { throw std::bad_exception(); }

    try { std::rethrow_exception(eptr); }
    catch (const std::exception &e) { return e.what()   ; }
    catch (const std::string    &e) { return e          ; }
    catch (const char           *e) { return e          ; }
    catch (...)                     { return "who knows"; }
}

int main()
{
    try { throw std::runtime_error("it's success!"); }
    catch (...) { std::cerr << "Here is WHAT happened: " << what() << std::endl;  }

    try { throw 42; } catch (...) { std::cerr << "and now what: " << what() << std::endl;  }
}

what it prints:

Here is WHAT happened: it's success!
and now what: who knows

http://coliru.stacked-crooked.com/a/1851d2ab9faa3a24

so this allows to get what in the catch-all clause.

but what if exception is nested??? here is what:

std::string what(const std::exception_ptr &eptr = std::current_exception());

template <typename T>
std::string nested_what(const T &e)
{
    try         { std::rethrow_if_nested(e); }
    catch (...) { return " (" + what(std::current_exception()) + ")"; }
    return {};
}

std::string what(const std::exception_ptr &eptr)
{
    if (!eptr) { throw std::bad_exception(); }

    try { std::rethrow_exception(eptr); }
    catch (const std::exception &e) { return e.what() + nested_what(e); }
    catch (const std::string    &e) { return e          ; }
    catch (const char           *e) { return e          ; }
    catch (...)                     { return "who knows"; }
}

using example from here:

#include <fstream>

...

// sample function that catches an exception and wraps it in a nested exception
void open_file(const std::string& s)
{
    try {
        std::ifstream file(s);
        file.exceptions(std::ios_base::failbit);
    } catch(...) {
        std::throw_with_nested( std::runtime_error("Couldn't open " + s) );
    }
}

// sample function that catches an exception and wraps it in a nested exception
void run()
{
    try {
        open_file("nonexistent.file");
    } catch(...) {
        std::throw_with_nested( std::runtime_error("run() failed") );
    }
}

int main()
{
    try { throw std::runtime_error("success!"); }
    catch (...) { std::cerr << "Here is WHAT happened: \"" << what() << '\"' << std::endl;  }

    try { run(); }
    catch (...) { std::cerr << "what happened for run: \""  << what() << '\"' << std::endl;  }
}

what is printed:

Here is WHAT happened: "success!"
what happened for run: "run() failed (Couldn't open nonexistent.file (basic_ios::clear))"

http://coliru.stacked-crooked.com/a/901a0c19297f02b5

but what if recursion too deep? what if stackoverflow? optimized what:

#include <typeinfo>

template <typename T>
std::exception_ptr get_nested(const T &e)
{
    try
    {
        auto &nested = dynamic_cast<const std::nested_exception&>(e);
        return nested.nested_ptr();
    }
    catch (const std::bad_cast &)
        { return nullptr; }
}

#if 0 // alternative get_nested
    std::exception_ptr get_nested()
    {
        try                                    { throw                ; }
        catch (const std::nested_exception &e) { return e.nested_ptr(); }
        catch (...)                            { return nullptr       ; }
    }
#endif

std::string what(std::exception_ptr eptr = std::current_exception())
{
    if (!eptr) { throw std::bad_exception(); }

    std::string whaaat;
    std::size_t num_nested = 0;
    next:
    {
        try
        {
            std::exception_ptr yeptr;
            std::swap(eptr, yeptr);
            std::rethrow_exception(yeptr);
        }
        catch (const std::exception &e) { whaaat += e.what()   ; eptr = get_nested(e); }
        catch (const std::string    &e) { whaaat += e          ; }
        catch (const char           *e) { whaaat += e          ; }
        catch (...)                     { whaaat += "who knows"; }

        if (eptr) { whaaat += " ("; num_nested++; goto next; }
    }
    whaaat += std::string(num_nested, ')');
    return whaaat;
}

the same whats:

Here is WHAT happened: "success!"
here is what: "run() failed (Couldn't open nonexistent.file (basic_ios::clear))"

http://coliru.stacked-crooked.com/a/32ec5af5b1d43453

UPD

The similar functionality can be implemented in C++03 by using a trick that allows to rethrow current exception outside of catch block: https://stackoverflow.com/a/3641809/5447906

Community
  • 1
  • 1
anton_rh
  • 8,226
  • 7
  • 45
  • 73
  • 1
    Instead of `dynamic_cast` - `catch bad_cast` - `return nullptr`, you should just `dynamic_cast` a pointer instead of a reference. – Miral Dec 12 '17 at 05:04
  • Did you mean to use `nested_what` instead of `what` in the second code block? – Nic Jun 12 '18 at 23:56
  • You last code sample has a `num_nested` counter which you increment but you never test it. Probably useless or you meant to break the loop at a certain number? Also it would be easy to remove your `goto` using a `for(;;)` loop and `break` when `eptr == nullptr`. – Alexis Wilke Sep 06 '18 at 18:48
  • Why do we need to rethrow it? – zwcloud Oct 26 '18 at 07:02
  • Actually a great answer, and a very useful helper; – mzoll Oct 18 '22 at 09:07
25
try
{
   std::rethrow_exception(eptr);
}
catch (const std::exception& e)
{
   std::cerr << e.what() << std::endl;
}

http://en.cppreference.com/w/cpp/error/exception_ptr

ForEveR
  • 55,233
  • 2
  • 119
  • 133
  • 7
    You should probably do something about the case where the exception does *not* derive from `std::exception` as well... – Kerrek SB Jan 09 '13 at 10:18
  • 1
    Well in OP’s case that is highly redundant since OP is already in a `catch` block. And in general I don’t think this would work since it could interfere with an already thrown exception. – Konrad Rudolph Jan 09 '13 at 10:19
  • @KerrekSB yeah, may be we should catch any exception and print something about "unknown error"... KonradRudolph anyway, there is no other case by standard. We can write function and call it after live an try/catch block. – ForEveR Jan 09 '13 at 10:26
  • 1
    In the end it seems to OP didn't even need a `std::exception_ptr` at all. – Christian Rau Jan 09 '13 at 10:42
14

Using std::current_exception seems a bit over the top in your case, since you don't seem to want to store or copy the std::exception_ptr for later processing (which is its only intent, it doesn't help with gaining additional information about an unknown exception in any way). If you just want to treat the case of a std::exception, what about the simple:

try
{
    // code throws potentially unknown exception
}
catch (const std::exception &e)
{
    std::cerr << e.what() << '\n';  // or whatever
}
catch (...)
{
    // well ok, still unknown what to do now, 
    // but a std::exception_ptr doesn't help the situation either.
    std::cerr << "unknown exception\n";
}
Christian Rau
  • 45,360
  • 10
  • 108
  • 185
  • 1
    Then what exactly is the real use of std::exception_ptr ? – Ram Jan 09 '13 at 10:32
  • 13
    @Ram Its use is for storing and copying an exception (which may have an arbitrary type). It is kind of an owning smart pointer. Imagine it to be like a `std::shared_ptr`, just that it works for any exception of any type (which is why it doesn't provide any type information either). This comes in especially useful for propagating exceptions between threads, where e.g. a `std::promise` needs to *store* an occured exception to be rethrown later when trying to access a `std::future`'s value in another thread. – Christian Rau Jan 09 '13 at 10:39
  • 2
    @Ram In the end it is useful whenever you want to store a thrown exception for later use (as normally it will be destroyed after the catch block ends). See [this](http://en.cppreference.com/w/cpp/error/exception_ptr) and its related pages. – Christian Rau Jan 09 '13 at 10:40
  • 3
    "seems a bit over the top in your case..." which doesn't change that it could easily be an MCVE, or a snippet for the sake of having a snippet. This doesn't answer the question, which is _really_ frustrating to those of us who are looking for that. – Nic Jun 12 '18 at 23:55
  • @NicHartley Well, for that you still have the other answer, which gives the straight to the point answer but on the other hand fails the specific use-case of the question. That's the joy of multiple valid answers. \o/ – Christian Rau Nov 27 '18 at 13:15
  • 1
    This will print (at least) the exception type: std::cout << (eptr ? eptr.__cxa_exception_type()->name() : "null") << std::endl; – Djibril NDIAYE Feb 10 '19 at 18:41
  • 1
    @H_He Those underlines...don't look *remotely* standard-compliant. Is this some kind of implementation interna? – Christian Rau Feb 11 '19 at 20:13
  • @Christian Rau you are right. It works for me on linux but not windows. – Djibril NDIAYE Feb 28 '19 at 17:21
1

Not the best solution in my opinion but seems to work.

try
{
// code throws potentially unknown exception
}
catch (const std::exception& e)
{
   std::cerr << e.what() << std::endl;
}
catch (...)
{
    std::exception_ptr eptr =  std::current_exception();
        // then what ?
    LogUnknownException();
}

Thanks to ForEveR for the initial solution but I am not sure if I want to throw again within the catch block.

Ram
  • 3,045
  • 3
  • 27
  • 42