#include <system_error>
#include <type_traits>
#include <new>

// #define USE_OUTCOME 
// #define USE_ERROR

// BOOST_NOINLINE ---------------------------------------------//
// Macro to use in place of 'inline' to prevent a function to be inlined
#if !defined(BOOST_NOINLINE)
#  if defined(_MSC_VER)
#    define BOOST_NOINLINE __declspec(noinline)
#  elif defined(__GNUC__) && __GNUC__ > 3
     // Clang also defines __GNUC__ (as 4)
#    if defined(__CUDACC__)
       // nvcc doesn't always parse __noinline__, 
       // see: https://s...content-available-to-author-only...t.org/trac/boost/ticket/9392
#      define BOOST_NOINLINE __attribute__ ((noinline))
#    else
#      define BOOST_NOINLINE __attribute__ ((__noinline__))
#    endif
#  else
#    define BOOST_NOINLINE
#  endif
#endif

#ifdef USE_OUTCOME
#include <boost/outcome/monad.hpp>

using namespace BOOST_OUTCOME_V1_NAMESPACE;
#else

class not_an_error_category_impl : public std::error_category
{
public:
    virtual const char* name() const noexcept { return ""; }
    virtual std::string message( int ev ) const { return std::string{}; }
    virtual std::error_condition default_error_condition( int ev ) const noexcept {
        return std::error_condition( ev, *this );
    }
};

not_an_error_category_impl instance;

inline const std::error_category& not_an_error_category()
{
    return instance;
}

template< typename T >
struct result {
    typedef typename std::aligned_storage<sizeof( T ), alignof(T)>::type value_store;
    value_store value;
    std::error_code error;

    result( const T& x ) {
        error.assign( 0, not_an_error_category() );
        new(&value) T( x );
    }

    result( T&& x ) {
        error.assign( 0, not_an_error_category() );
        new(&value) T( std::move( x ) );
    }

    result( std::error_code x ) : error( x ) {
    }

    ~result() {
        destroy<T>();
    }

    result( const result& rhs ) {
        copy<T>( rhs );
    }

    result( result&& rhs ) {
        move<T>( std::move( rhs ) );
    }

    explicit operator bool() const {
        return (&error.category() == &not_an_error_category());
    }

    T& get() {
        return *reinterpret_cast<T*>(&value);
    }

    template< typename U >
    typename std::enable_if<std::is_trivially_destructible<U>::value>::type destroy() {}
    template< typename U >
    typename std::enable_if<!std::is_trivially_destructible<U>::value>::type destroy() {
        if ( *this ) {
            get().~T();
        }
    }

    template< typename U >
    typename std::enable_if<std::is_trivially_copyable<U>::value>::type copy( const result<T>& rhs ) {
        value = rhs.value;
        error = rhs.error;
    }
    template< typename U >
    typename std::enable_if<!std::is_trivially_copyable<U>::value>::type copy( const result<T>& rhs ) {
        destroy<T>();
        if ( rhs ) {
            new(&value) T( rhs.value );
        }
        error = rhs.error;
    }

    template< typename U >
    typename std::enable_if<std::is_trivially_move_constructible<U>::value>::type move( result<T>&& rhs ) {
        value = rhs.value;
        error = rhs.error;
    }
    template< typename U >
    typename std::enable_if<!std::is_trivially_move_constructible<U>::value>::type move( result<T>&& rhs ) {
        destroy<T>();
        if ( rhs ) {
            new(&value) T( std::move( rhs.value ) );
        }
        error = rhs.error;
    }
};
#endif

BOOST_NOINLINE result<int> bar()
{
#ifdef USE_ERROR
    return std::make_error_code( std::errc::bad_address );
#else
    return 1;
#endif
}

int main()
{
    result<int> _foo = bar();
    int foo = 0;
    if ( _foo ) {
        foo = _foo.get();
    }
    return foo;
}
