diff --git a/include/boost/variant2/expected.hpp b/include/boost/variant2/expected.hpp new file mode 100644 index 0000000..f6651f3 --- /dev/null +++ b/include/boost/variant2/expected.hpp @@ -0,0 +1,367 @@ +#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 + +class bad_expected_access: public std::exception +{ +private: + + std::string msg_; + +public: + + bad_expected_access() noexcept + { + } + + template explicit bad_expected_access( mp_identity ) noexcept: msg_( "bad_expected_access: " + boost::core::demangle( typeid(E).name() ) ) + { + } + + char const * what() const noexcept + { + return msg_.empty()? "bad_expected_access": msg_.c_str(); + } +}; + +// expected + +template void throw_on_unexpected( E const & /*e*/ ) +{ + throw bad_expected_access( mp_identity() ); +} + +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( mp_identity() ); + } +} + +template class expected +{ +private: + + variant v_; + +private: + + void _bad_access() const + { + mp_for_index>( v_.index(), [&]( auto I ) + { + if( I == 0 ) + { + throw bad_expected_access( mp_identity() ); + } + else + { + throw_on_unexpected( get(v_) ); + } + }); + } + +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_for_index>( v_.index(), [&]( auto I ) { + + return _remap_error( I, f, get(v_) ); + + }); + } + + expected remap_errors() + { + using R = expected; + + auto f = []( auto const& e ){ return make_error_code(e); }; + + return mp_for_index>( v_.index(), [&]( auto I ) { + + return _remap_error( I, f, get(v_) ); + + }); + } +}; + +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