Newer
Older
Import / applications / MakePDF / Framework / Expected.h
#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