#ifndef EXPECTED_H
#define EXPECTED_H
#include <exception>
///
/// Expected ScopeGuard11
/// From here: http://channel9.msdn.com/Shows/Going+Deep/C-and-Beyond-2012-Andrei-Alexandrescu-Systematic-Error-Handling-in-C
///
template <class T>
class Expected
{
union {
T ham;
std::exception_ptr spam;
};
bool gotHam;
Expected() {} // used internally
public:
Expected(const T& rhs) : ham(rhs), gotHam(true) {}
Expected(T&& rhs) : ham(std::move(rhs)), gotHam(true) {}
Expected(const Expected& rhs) : gotHam(rhs.gotHam) {
if (gotHam) new(&ham) T(rhs.ham);
else new(&spam) std::exception_ptr(rhs.spam);
}
Expected(Expected&& rhs) : gotHam(rhs.gotHam) {
if (gotHam) new(&ham) T(std::move(rhs.ham));
else new(&spam) std::exception_ptr(std::move(rhs.spam));
}
void swap(Expected& rhs) {
if (gotHam) {
if (rhs.gotHam) {
using std::swap; // like this to do arg dependant lookup
swap(ham, rhs.ham);
}
else {
auto t = std::move(rhs.spam);
new(&rhs.ham) T(std::move(ham));
new(&spam) std::exception_ptr(t);
std::swap(gotHam, rhs.gotHam);
}
}
else {
if (rhs.gotHam) {
rhs.swap(*this);
}
else {
spam.swap(rhs.spam);
std::swap(gotHam, rhs.gotHam); // is this needed?
}
}
}
~Expected() {
using std::exception_ptr;
if (gotHam) ham.~T();
else spam.~exception_ptr();
}
template<class E>
static Expected<T> fromException(const E& exception)
{
if (typeid(exception) != typeid(E)) {
throw std::invalid_argument("slicing detected");
}
return fromException(std::make_exception_ptr(exception));
}
static Expected<T> fromException(std::exception_ptr p)
{
Expected<T> result;
result.gotHam = false;
new(&result.spam) std::exception_ptr(std::move(p));
return result;
}
static Expected<T> fromException()
{
return fromException(std::current_exception());
}
bool valid() const {
return gotHam;
}
T& get() {
if (!gotHam) std::rethrow_exception(spam);
return ham;
}
const T& get() const {
if (!gotHam) std::rethrow_exception(spam);
return ham;
}
template<class E>
bool hasException() const {
try {
if (!gotHam) std::rethrow_exception(spam);
}
catch (const E& object) {
return true;
}
catch (...) {
}
return false;
}
template<class F>
static Expected fromCode(F fun) {
try {
return Expected(fun());
}
catch (...) {
return fromException();
}
}
};
///
/// Example usage
///
Expected<int> parseInt(const std::string& s)
{
int result;
bool nonDigit = false, tooManyDigits = false;
//...
if (nonDigit) {
return Expected<int>::fromException(
std::invalid_argument("not a number"));
}
//...
if (tooManyDigits) {
return Expected<int>::fromException(
std::out_of_range("overflow"));
}
//...
return result;
}
void testExpected()
{
auto r = Expected<std::string>::fromCode([] {
// ...
});
std::string s;
auto x = parseInt(s); // won't throw
if (!x.valid()) {
if (x.hasException<std::invalid_argument>()) {
// no digits
// ...
}
x.get(); // it will 're'throw
}
}
#endif // EXPECTED_H