#include <exception>
#include <stdexcept>
#include <cstddef>
#include <cstdint>
#include <cstdlib>
#include <iostream>
#include <chrono>

using error_type = std::exception_ptr;

struct ResultBase
{
    bool hasError() const
    {
        return *reinterpret_cast<const bool*>(this);
    }

    std::exception_ptr error() const
    {
        return *reinterpret_cast<const error_type*>(
            reinterpret_cast<const char*>(this)
            + sizeof(std::max_align_t));
    }

protected:
    ResultBase() { }
};

template <class T>
struct Result : ResultBase
{
    Result(error_type error)
        : mHasError(true) { new (&mError) error_type(error); }

    Result(T value)
        : mHasError(false) { new (&mValue) T(value); }

    ~Result()
    {
        if (mHasError)
            mError.~error_type();
        else
            mValue.~T();
    }

    void setError(error_type error)
    {
        if (mHasError) {
            mError = error;
        } else {
            mValue.~T();
            new (&mError) error_type(error);
            mHasError = true;
        }
    }

    void setValue(T value)
    {
        if (mHasError) {
            mError.~error_type();
            new (&mValue) T(value);
            mHasError = false;
        } else {
            mValue = value;
        }
    }

private:
    union {
        bool mHasError;
        std::max_align_t mAligner;
    };
    union {
        error_type mError;
        T mValue;
    };
};

static_assert(std::is_standard_layout<Result<int>>::value, "");

void check(bool condition)
{
    if (!condition) std::terminate();
}

class ResultBase2
{
public:
    virtual ~ResultBase2() {}

    virtual bool hasError() const = 0;

    virtual std::exception_ptr error() const = 0;

protected:
    ResultBase2() {}
};

template <class T>
class Result2 : public ResultBase2
{
public:
    Result2(error_type error)
    {
        this->construct(error);
    }
    Result2(T value)
    {
        this->construct(value);
    }

    ~Result2()
    {
        if (this->mHasError)
            this->mData.mError.~error_type();
        else
            this->mData.mValue.~T();
    }

    bool hasError() const override { return mHasError; }

    std::exception_ptr error() const override { return mData.mError; }

    void setError(error_type error)
    {
        if (this->mHasError)
        {
            this->mData.mError = error;
        }
        else
        {
            this->mData.mValue.~T();
            this->construct(error);
        }
    }

    void setValue(T value)
    {
        if (not this->mHasError)
        {
            this->mData.mValue = value;
        }
        else
        {
            this->mData.mError.~error_type();
            this->construct(value);
        }
    }

private:
    bool mHasError;
    union Data
    {
        Data() {}
        ~Data() {}

        error_type mError;
        T mValue;
    } mData;

    void construct(error_type error)
    {
        mHasError = true;
        new (&mData.mError) error_type(error);
    }
    void construct(T value)
    {
        mHasError = false;
        new (&mData.mValue) T(value);
    }
};

class ResultBase3;
struct ResultBase3Vtable
{
	bool (*hasError)(const ResultBase3&);
	error_type (*error)(const ResultBase3&);
};

class ResultBase3
{
public:
    bool hasError() const { return vtable->hasError(*this); }

    std::exception_ptr error() const { return vtable->error(*this); }

protected:
    ResultBase3(ResultBase3Vtable* vtable) : vtable(vtable) {}
private:
	ResultBase3Vtable* vtable;
};

template <class T>
class Result3 : public ResultBase3
{
public:
    Result3(error_type error) : ResultBase3(&Result3<T>::vtable)
    {
        this->construct(error);
    }
    Result3(T value) : ResultBase3(&Result3<T>::vtable)
    {
        this->construct(value);
    }

    ~Result3()
    {
        if (this->mHasError)
            this->mData.mError.~error_type();
        else
            this->mData.mValue.~T();
    }

    bool hasError() const { return mHasError; }

    std::exception_ptr error() const { return mData.mError; }

    void setError(error_type error)
    {
        if (this->mHasError)
        {
            this->mData.mError = error;
        }
        else
        {
            this->mData.mValue.~T();
            this->construct(error);
        }
    }

    void setValue(T value)
    {
        if (not this->mHasError)
        {
            this->mData.mValue = value;
        }
        else
        {
            this->mData.mError.~error_type();
            this->construct(value);
        }
    }

private:
    bool mHasError;
    union Data
    {
        Data() {}
        ~Data() {}

        error_type mError;
        T mValue;
    } mData;

    void construct(error_type error)
    {
        mHasError = true;
        new (&mData.mError) error_type(error);
    }
    void construct(T value)
    {
        mHasError = false;
        new (&mData.mValue) T(value);
    }
    
    static bool hasErrorVTable(const ResultBase3& result)
    {
    	return static_cast<const Result3&>(result).hasError();
    }
    static error_type errorVTable(const ResultBase3& result)
    {
    	return static_cast<const Result3&>(result).error();
    }
    static ResultBase3Vtable vtable;
};

template <typename T>
ResultBase3Vtable Result3<T>::vtable{
	&Result3<T>::hasErrorVTable,	
	&Result3<T>::errorVTable,	
};


template <typename Base, typename Result>
void f(const Base& alias, Result& r)
{
    check(!alias.hasError());

    r.setError(std::exception_ptr());
    check(alias.hasError());
    check(alias.error() == nullptr);

    r.setValue(1);
    check(!alias.hasError());

    r.setError(std::make_exception_ptr(std::runtime_error("!")));
    check(alias.hasError());
    check(alias.error() != nullptr);
}

template <std::size_t N, typename Operation>
auto checkTime(Operation op)
{
	using namespace std::chrono;
	high_resolution_clock::time_point t1 = high_resolution_clock::now();
	for (std::size_t i = 0u; i < N; ++i)
		op();
	 high_resolution_clock::time_point t2 = high_resolution_clock::now();
	 auto duration = std::chrono::duration_cast<std::chrono::microseconds>( t2 - t1 ).count();
	return duration;
}

int main()
{
    std::cout << checkTime<1>([] {  Result<int> r(0); f(r, r); }) << std::endl;
    std::cout << checkTime<1>([] {  Result2<int> r(0); f(r, r); }) << std::endl;
    std::cout << checkTime<1>([] {  Result3<int> r(0); f(r, r); }) << std::endl;

    std::cout << sizeof(Result<int>) << std::endl;
    std::cout << sizeof(Result2<int>) << std::endl;
    std::cout << sizeof(Result3<int>) << std::endl;
}
