c++exception

How does std :: current_exception work?


Consider the following example:

class myclass
{
public:

    void f() const
    {
        std :: cout << "Hello from myclass!" << std :: endl;
    }
};

int main()
{
    std :: exception_ptr x;

    try
    {
        throw myclass();
    }
    catch(...)
    {
        x = std :: current_exception();
    }

    try
    {
        std :: rethrow_exception(x);
    }
    catch(const myclass & n)
    {
        n.f();
    }
}

Here I throw an object of class myclass and use std :: current_exception() in the catch(...) block to get a std :: exception_ptr. Later I use std :: rethrow_exception to get the exception thrown again and I catch it, this time with catch(const myclass &), that allows me to, e.g., call f() and get Hello from myclass! printed out.

Now. I kind of imagine how I could implement something similar to std :: exception_ptr: a pointer to a base class, from which a template wrapper for each possible type inherits, and some rethrow virtual method that allows to throw again the exception. So far so good.

Now, how does std :: current_exception() work? How is it implemented? I tried to inspect it using my Xcode but I got a nice flashing question mark: ?. Is this some kind of primitive in C++11? Like, e.g., decltype or sizeof? Or is there another way to access the exception hidden in that catch(...)?

For example, is there a way to just print out an exception, whatever it is? Something like:

template <typename type> void printme(const type & exception)
{
    try
    {
        throw exception;
    }
    catch(...)
    {
        std :: cout << /* SOME MAGIC HERE */ << std :: endl;
    }
}

I guess that std :: current_exception() would be using some similar way to access the exception, make a copy of it, and instantiate an appropriate std :: exception_ptr that can be later rethrown.

So am I missing something? I tried something trivial like catch(auto x) but it doesn't seem to do the job.


Solution

  • Before std::exception_ptr there was boost::exception_ptr that attempts to implement this functionality without direct language support and consequently requires more boilerplate code to work. With Boost, you have to wrap the exception at the point of throw with boost::enable_current_exception and throw the wrapper instead. I have not actually studied the implementation, but I imagine that the wrapper's copy constructor sets a thread-local pointer to point to itself (throw must copy its argument) which boost::current_exception then retrieves (possibly copying the exception to free store first, so that it can outlive the catch block). For catch (T x) to still work the wrapper has to wrap the original exception by inheriting from it, which makes it impossible to use with primitive types.

    Another way is to copy the original, set the thread-local pointer to point to the copy and throw the original. This way works for any throwable type but current_exception would refer to a different instance than the one used o initialize the catch parameter.

    Now std::exception_ptr has direct language support and does not have to jump through hoops or do excessive copying to achieve this but the basic idea is the same: store the exception in a type-erased container that can rethrow it at the point of throw and point to that.