Probably I am not the first person finding out that std::exception_ptr
could be used to implement an any
type (performance considerations being put aside), as it is probably the only type in C++ that can hold anything. Googling did not, however, bring any result in this direction.
Does anybody know whether the following approach has been used anywhere for anything useful?
#include <exception>
#include <iostream>
struct WrongTypeError : std::exception { };
class Any {
public:
template <class T>
void set (T t) {
try { throw t; }
catch (...) { m_contained = std::current_exception(); }
}
template <class T>
T const & get () {
try { std::rethrow_exception (m_contained); }
catch (T const & t) { return t; }
catch (...) { throw WrongTypeError {}; }
}
private:
std::exception_ptr m_contained = nullptr;
};
int main () {
auto a = Any {};
a.set (7);
std::cout << a.get<int> () << std::endl;
a.set (std::string {"Wonderful weather today"});
std::cout << a.get<std::string> () << std::endl;
return 0;
}
A default-constructed std::exception_ptr is a null pointer; it does not point to an exception object. Two instances of std::exception_ptr compare equal only if they are both null or both point at the same exception object. std::exception_ptr is not implicitly convertible to any arithmetic, enumeration, or pointer type.
Smart pointer type that can refer to exception objects. It is a shared pointer-like type: The pointed exception is guaranteed to remain valid for as long as at least one exception_ptr points to it, potentially extending its lifetime beyond the scope of a catch statement or across threads. Being default-constructed (acquiring a null-pointer value).
If a secondary thread throws an exception, that exception becomes the current exception. By analogy to the real world, the current exception is said to be in flight. The current exception is in flight from the time it is thrown until the exception handler that catches it returns.
The Microsoft C++ compiler (MSVC) supports transporting an exception from one thread to another. Transporting exceptions enables you to catch an exception in one thread and then make the exception appear to be thrown in a different thread.
as it is probably the only type in C++ that can hold anything.
I'm afraid this is not the case. boost::any can hold any type, and even copies (assume the type is copyable) it correctly as well. It is implemented (broadly speaking) using a base class and a templated child:
class any_base {
...
}
template <class T>
class any_holder : public any_base
{
private:
T m_data;
}
From this you can imagine that you can stuff any type into an any_holder (with the right interface), and then you can hold an any_holder by pointer to any_base. This technique is a type of type erasure; once we have an any_base pointer we are holding an object but don't know anything about the type. You could say this is total type erasure, something like std::function provides partial type erasure (and may use similar techniques under the hood, I'm not sure off the top of my head).
boost::any provides additional interface to support its usage of holding any type, and it probably provides better performance as throwing exceptions is crazy slow. Also, as I mentioned before, it correctly copies the underlying object, which is pretty cool. exception_ptr is a shared ownership pointer, so I believe it makes shallow copies instead.
Boost any website: http://www.boost.org/doc/libs/1_59_0/doc/html/any.html
It's being considered for the standard I believe: http://en.cppreference.com/w/cpp/experimental/any
It seems like the implementation is similar to boost but adds a small object optimization.
exception_ptr is quite a strange beast as far as I can tell, I've come across it before and googled it and there's surprisingly little information out there. I'm pretty sure however that it's magical, i.e. it cannot be implemented in userspace. I say this because when you throw it, the type seems to magically unerase itself, this isn't generally possible.
You are certainly the first person I have come across who has thought of it.
I'm definitely impressed by your lateral thinking skills :)
There is a problem with this approach however, (other than the obvious performance problem).
This stems from the fact that throw is permitted to make a copy of the object thrown.
Firstly this places a restriction on what you may store in your 'any' class, secondly it will have further performance implications and thirdly, each time you access your object, the compiler is not obliged to give you the same one. It's allowed to give you a copy. This means that at the very least you should only store immutable objects this way. (Note: when I say 'should' I really mean 'absolutely should not!') :)
You could get around this by crafting a class that allocates memory to store the object, records it's type and deletes it properly... But if you did that you'd be better off without the exception complication anyway.
In any case, this is what boost::any does under the covers.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With