#ifndef BOOST_VARIANT2_EXPECTED_HPP_INCLUDED #define BOOST_VARIANT2_EXPECTED_HPP_INCLUDED // Copyright 2017 Peter Dimov. // // Distributed under the Boost Software License, Version 1.0. // // See accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt #ifndef BOOST_VARIANT2_VARIANT_HPP_INCLUDED #include #endif #include #include #include #include #include // namespace boost { namespace variant2 { // unexpected_ template using unexpected_ = variant; // bad_expected_access template class bad_expected_access; template<> class bad_expected_access: public std::exception { private: std::string msg_; public: bad_expected_access() noexcept { } explicit bad_expected_access( std::string&& msg ) noexcept: msg_( std::move(msg) ) // extension { } char const * what() const noexcept { return msg_.empty()? "bad_expected_access<>": msg_.c_str(); } }; namespace detail { template::value>> std::string add_value( E const& /*e*/ ) { return std::string(); } template::value>> std::string add_value( E const& e ) { return ": " + std::to_string( static_cast(e) ); } } // namespace detail template class bad_expected_access: public bad_expected_access { private: E e_; public: explicit bad_expected_access( E const& e ) : bad_expected_access( "bad_expected_access<" + boost::core::demangle( typeid(E).name() ) + ">" + variant2::detail::add_value( e ) ), e_( e ) { } E error() const { return e_; } }; // throw_on_unexpected template void throw_on_unexpected( E const& /*e*/ ) { } void throw_on_unexpected( std::error_code const & e ) { throw std::system_error( e ); } void throw_on_unexpected( std::exception_ptr const & e ) { if( e ) { std::rethrow_exception( e ); } else { throw bad_expected_access<>( "bad_expected_access<>: null exception_ptr" ); } } // expected template class expected; template struct is_expected: std::false_type {}; template struct is_expected>: std::true_type {}; template class expected { private: variant v_; private: void _bad_access() const { mp_with_index>( v_.index(), [&]( auto I ) { if( I == 0 ) { throw bad_expected_access<>( "bad_expected_access<>: value present on error request" ); } else { auto const & e = get(v_); throw_on_unexpected( e ); throw bad_expected_access>( e ); } }); } public: // value constructors constexpr expected() noexcept( std::is_nothrow_default_constructible::value ) { } constexpr expected( T const& t ) noexcept( std::is_nothrow_copy_constructible::value ): v_( in_place_index<0>, t ) { } constexpr expected( T && t ) noexcept( std::is_nothrow_move_constructible::value ): v_( in_place_index<0>, std::move(t) ) { } // template constexpr expected( U && u ); where U in E...? // in-place constructor? // unexpected constructor template..., mp_contains, E2>...>, void>> constexpr expected( unexpected_ const & x ): v_( x ) { } template..., mp_contains, E2>...>, void>> constexpr expected( unexpected_ && x ): v_( std::move(x) ) { } // conversion constructor template..., mp_contains, E2>...>, void>> constexpr expected( expected const & x ): v_( x.v_ ) { } template..., mp_contains, E2>...>, void>> constexpr expected( expected && x ): v_( std::move(x.v_) ) { } // emplace template void emplace( A&&... a ) { v_.emplace( std::forward(a)... ); } template void emplace( std::initializer_list il, A&&... a ) { v_.emplace( il, std::forward(a)... ); } // swap void swap( expected & r ) noexcept( noexcept( v_.swap( r.v_ ) ) ) { v_.swap( r.v_ ); } // value queries constexpr bool has_value() const noexcept { return v_.index() == 0; } constexpr explicit operator bool() const noexcept { return v_.index() == 0; } // checked value access constexpr T& value() & { if( !has_value() ) { _bad_access(); } return *get_if<0>(&v_); } constexpr T const& value() const& { if( !has_value() ) { _bad_access(); } return *get_if<0>(&v_); } constexpr T&& value() && { return std::move( value() ); } constexpr T const&& value() const&& { return std::move( value() ); } // unchecked value access T* operator->() noexcept { return get_if<0>(&v_); } T const* operator->() const noexcept { return get_if<0>(&v_); } T& operator*() & noexcept { T* p = get_if<0>(&v_); assert( p != 0 ); return *p; } T const& operator*() const & noexcept { T const* p = get_if<0>(&v_); assert( p != 0 ); return *p; } T&& operator*() && noexcept { return std::move(**this); } T const&& operator*() const && noexcept { return std::move(**this); } // error queries template constexpr bool has_error() const noexcept { using I = mp_find; return v_.index() == I::value; } constexpr bool has_error() const noexcept { static_assert( sizeof...(E) == 1, "has_error() is only valid when there is a single E" ); return has_error>(); } // error access unexpected_ unexpected() const { if( has_value() ) { _bad_access(); } return v_.template subset(); } template constexpr E2 error() const noexcept { using I = mp_find; if( v_.index() != I::value ) { _bad_access(); } return get( v_ ); } constexpr mp_first error() const noexcept { static_assert( sizeof...(E) == 1, "error() is only valid when there is a single E" ); return error>(); } // error mapping private: template struct Qret { template using fn = decltype( std::declval()( std::declval()... ) ); }; template using remapped = mp_append, mp_unique, mp_list>>>; template static R _remap_error( mp_size_t, F && f, V && v ) { // return R( std::forward(f)( std::forward(v) ) ); auto e = std::forward(f)( std::forward(v) ); return unexpected_{ e }; } template static R _remap_error( mp_size_t<0>, F && /*f*/, V && v ) { return R( std::forward(v) ); } public: template remapped remap_errors( F && f ) const { using R = remapped; return mp_with_index>( v_.index(), [&]( auto I ) { return this->_remap_error( I, f, get(v_) ); }); } expected remap_errors() const { using R = expected; auto f = []( auto const& e ){ return make_error_code(e); }; return mp_with_index>( v_.index(), [&]( auto I ) { return this->_remap_error( I, f, get(v_) ); }); } // then private: template using then_result_ = decltype( std::declval()( std::declval() ) ); template> using then_result = mp_if, R, expected>; public: template then_result then( F && f ) const { if( has_value() ) { return std::forward(f)( **this ); } else { return unexpected(); } } template then_result operator>>( F && f ) const { if( has_value() ) { return std::forward(f)( **this ); } else { return unexpected(); } } }; template inline constexpr bool operator==( expected const & x1, expected const & x2 ) { return x1.v_ == x2.v_; } template inline constexpr bool operator!=( expected const & x1, expected const & x2 ) { return x1.v_ != x2.v_; } template inline void swap( expected & x1, expected & x2 ) noexcept( noexcept( x1.swap( x2 ) ) ) { x1.swap( x2 ); } } // namespace variant2 } // namespace boost #endif // #ifndef BOOST_VARIANT2_EXPECTED_HPP_INCLUDED