#ifndef BOOST_LEAF_RESULT_HPP_INCLUDED #define BOOST_LEAF_RESULT_HPP_INCLUDED // Copyright 2018-2023 Emil Dotchevski and Reverge Studios, Inc. // 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) #include #include #include #include #include #include namespace boost { namespace leaf { namespace leaf_detail { class dynamic_allocator; } //////////////////////////////////////// class bad_result: public std::exception, public error_id { char const * what() const noexcept final override { return "boost::leaf::bad_result"; } public: explicit bad_result( error_id id ) noexcept: error_id(id) { BOOST_LEAF_ASSERT(value()); } }; //////////////////////////////////////// namespace leaf_detail { template ::value> struct result_value_printer; template struct result_value_printer { template static void print( std::basic_ostream & s, T const & x ) { (void) (s << x); } }; template struct result_value_printer { template static void print( std::basic_ostream & s, T const & ) { (void) (s << "{not printable}"); } }; template void print_result_value( std::basic_ostream & s, T const & x ) { result_value_printer::print(s, x); } } //////////////////////////////////////// namespace leaf_detail { template struct stored { using type = T; using value_no_ref = T; using value_no_ref_const = T const; using value_cref = T const &; using value_ref = T &; using value_rv_cref = T const &&; using value_rv_ref = T &&; static value_no_ref_const * cptr( type const & v ) noexcept { return &v; } static value_no_ref * ptr( type & v ) noexcept { return &v; } }; template struct stored { using type = std::reference_wrapper; using value_no_ref = T; using value_no_ref_const = T; using value_ref = T &; using value_cref = T &; using value_rv_ref = T &; using value_rv_cref = T &; static value_no_ref_const * cptr( type const & v ) noexcept { return &v.get(); } static value_no_ref * ptr( type const & v ) noexcept { return &v.get(); } }; class result_discriminant { int state_; public: enum kind_t { err_id_zero = 0, err_id = 1, err_id_capture_list = 2, val = 3 }; explicit result_discriminant( error_id id ) noexcept: state_(id.value()) { BOOST_LEAF_ASSERT(state_ == 0 || (state_&3) == 1); BOOST_LEAF_ASSERT(kind()==err_id_zero || kind()==err_id); } #if BOOST_LEAF_CFG_CAPTURE explicit result_discriminant( int err_id, leaf_detail::capture_list const & ) noexcept: state_((err_id&~3) | 2) { BOOST_LEAF_ASSERT((err_id&3) == 1); BOOST_LEAF_ASSERT(kind() == err_id_capture_list); } #endif struct kind_val { }; explicit result_discriminant( kind_val ) noexcept: state_(val) { BOOST_LEAF_ASSERT((state_&3) == 3); BOOST_LEAF_ASSERT(kind()==val); } kind_t kind() const noexcept { return kind_t(state_&3); } error_id get_error_id() const noexcept { BOOST_LEAF_ASSERT(kind()==err_id_zero || kind()==err_id || kind()==err_id_capture_list); return make_error_id(int((state_&~3)|1)); } }; } //////////////////////////////////////// template class BOOST_LEAF_SYMBOL_VISIBLE BOOST_LEAF_ATTRIBUTE_NODISCARD result { template friend class result; friend class leaf_detail::dynamic_allocator; #if BOOST_LEAF_CFG_CAPTURE using capture_list = leaf_detail::capture_list; #endif using result_discriminant = leaf_detail::result_discriminant; using stored_type = typename leaf_detail::stored::type; using value_no_ref = typename leaf_detail::stored::value_no_ref; using value_no_ref_const = typename leaf_detail::stored::value_no_ref_const; using value_ref = typename leaf_detail::stored::value_ref; using value_cref = typename leaf_detail::stored::value_cref; using value_rv_ref = typename leaf_detail::stored::value_rv_ref; using value_rv_cref = typename leaf_detail::stored::value_rv_cref; union { stored_type stored_; #if BOOST_LEAF_CFG_CAPTURE mutable capture_list cap_; #endif }; result_discriminant what_; struct error_result { error_result( error_result && ) = default; error_result( error_result const & ) = delete; error_result & operator=( error_result const & ) = delete; result & r_; error_result( result & r ) noexcept: r_(r) { } template operator result() noexcept { result_discriminant const what = r_.what_; switch( what.kind() ) { case result_discriminant::val: return result(error_id()); case result_discriminant::err_id_capture_list: #if BOOST_LEAF_CFG_CAPTURE return result(what.get_error_id().value(), std::move(r_.cap_)); #else BOOST_LEAF_ASSERT(0); // Possible ODR violation. #endif case result_discriminant::err_id_zero: case result_discriminant::err_id: return result(what.get_error_id()); } } operator error_id() noexcept { result_discriminant const what = r_.what_; return what.kind() == result_discriminant::val? error_id() : what.get_error_id(); } }; void destroy() const noexcept { switch(this->what_.kind()) { case result_discriminant::err_id_zero: case result_discriminant::err_id: break; case result_discriminant::err_id_capture_list: #if BOOST_LEAF_CFG_CAPTURE cap_.~capture_list(); #else BOOST_LEAF_ASSERT(0); // Possible ODR violation. #endif break; case result_discriminant::val: stored_.~stored_type(); } } template result_discriminant move_from( result && x ) noexcept { auto x_what = x.what_; switch(x_what.kind()) { case result_discriminant::err_id_zero: case result_discriminant::err_id: break; case result_discriminant::err_id_capture_list: #if BOOST_LEAF_CFG_CAPTURE (void) new(&cap_) capture_list(std::move(x.cap_)); #else BOOST_LEAF_ASSERT(0); // Possible ODR violation. #endif break; case result_discriminant::val: (void) new(&stored_) stored_type(std::move(x.stored_)); } return x_what; } error_id get_error_id() const noexcept { BOOST_LEAF_ASSERT(what_.kind() != result_discriminant::val); return what_.get_error_id(); } stored_type const * get() const noexcept { return has_value() ? &stored_ : nullptr; } stored_type * get() noexcept { return has_value() ? &stored_ : nullptr; } protected: #if BOOST_LEAF_CFG_CAPTURE result( int err_id, leaf_detail::capture_list && cap ) noexcept: cap_(std::move(cap)), what_(err_id, cap) { } #endif void enforce_value_state() const { switch( what_.kind() ) { case result_discriminant::err_id_capture_list: #if BOOST_LEAF_CFG_CAPTURE cap_.unload(what_.get_error_id().value()); #else BOOST_LEAF_ASSERT(0); // Possible ODR violation. #endif case result_discriminant::err_id_zero: case result_discriminant::err_id: ::boost::leaf::leaf_detail::throw_exception_impl(bad_result(get_error_id())); case result_discriminant::val: break; } } template void move_assign( result && x ) noexcept { destroy(); what_ = move_from(std::move(x)); } public: using value_type = T; // NOTE: Copy constructor implicitly deleted. result( result && x ) noexcept: what_(move_from(std::move(x))) { } template ::value>::type> result( result && x ) noexcept: what_(move_from(std::move(x))) { } result(): stored_(stored_type()), what_(result_discriminant::kind_val{}) { } result( value_no_ref && v ) noexcept: stored_(std::forward(v)), what_(result_discriminant::kind_val{}) { } result( value_no_ref const & v ): stored_(v), what_(result_discriminant::kind_val{}) { } template::value && sizeof...(A) >= 2>::type> result( A && ... a ) noexcept: stored_(std::forward(a)...), what_(result_discriminant::kind_val{}) { } result( error_id err ) noexcept: what_(err) { } #if defined(BOOST_STRICT_CONFIG) || !defined(__clang__) // This should be the default implementation, but std::is_convertible // breaks under COMPILER=/usr/bin/clang++ CXXSTD=11 clang 3.3. // On the other hand, the workaround exposes a rather severe bug in //__GNUC__ under 11: https://github.com/boostorg/leaf/issues/25. // SFINAE: T can be initialized with an A, e.g. result("literal"). template::value && std::is_convertible::value>::type> result( A && a ) noexcept: stored_(std::forward(a)), what_(result_discriminant::kind_val{}) { } #else private: static int init_T_with_A( T && ); public: // SFINAE: T can be initialized with an A, e.g. result("literal"). template result( A && a, decltype(init_T_with_A(std::forward(a))) * = nullptr ): stored_(std::forward(a)), what_(result_discriminant::kind_val{}) { } #endif #if BOOST_LEAF_CFG_STD_SYSTEM_ERROR result( std::error_code const & ec ) noexcept: what_(error_id(ec)) { } template ::value, int>::type> result( Enum e ) noexcept: what_(error_id(e)) { } #endif ~result() noexcept { destroy(); } // NOTE: Assignment operator implicitly deleted. result & operator=( result && x ) noexcept { move_assign(std::move(x)); return *this; } template ::value>::type> result & operator=( result && x ) noexcept { move_assign(std::move(x)); return *this; } bool has_value() const noexcept { return what_.kind() == result_discriminant::val; } bool has_error() const noexcept { return !has_value(); } explicit operator bool() const noexcept { return has_value(); } #ifdef BOOST_LEAF_NO_CXX11_REF_QUALIFIERS value_cref value() const { enforce_value_state(); return stored_; } value_ref value() { enforce_value_state(); return stored_; } #else value_cref value() const & { enforce_value_state(); return stored_; } value_ref value() & { enforce_value_state(); return stored_; } value_rv_cref value() const && { enforce_value_state(); return std::move(stored_); } value_rv_ref value() && { enforce_value_state(); return std::move(stored_); } #endif value_no_ref_const * operator->() const noexcept { return has_value() ? leaf_detail::stored::cptr(stored_) : nullptr; } value_no_ref * operator->() noexcept { return has_value() ? leaf_detail::stored::ptr(stored_) : nullptr; } #ifdef BOOST_LEAF_NO_CXX11_REF_QUALIFIERS value_cref operator*() const noexcept { auto p = get(); BOOST_LEAF_ASSERT(p != nullptr); return *p; } value_ref operator*() noexcept { auto p = get(); BOOST_LEAF_ASSERT(p != nullptr); return *p; } #else value_cref operator*() const & noexcept { auto p = get(); BOOST_LEAF_ASSERT(p != nullptr); return *p; } value_ref operator*() & noexcept { auto p = get(); BOOST_LEAF_ASSERT(p != nullptr); return *p; } value_rv_cref operator*() const && noexcept { auto p = get(); BOOST_LEAF_ASSERT(p != nullptr); return std::move(*p); } value_rv_ref operator*() && noexcept { auto p = get(); BOOST_LEAF_ASSERT(p != nullptr); return std::move(*p); } #endif error_result error() noexcept { return error_result{*this}; } template error_id load( Item && ... item ) noexcept { return error_id(error()).load(std::forward(item)...); } void unload() { #if BOOST_LEAF_CFG_CAPTURE if( what_.kind() == result_discriminant::err_id_capture_list ) cap_.unload(what_.get_error_id().value()); #endif } template void print( std::basic_ostream & os ) const { result_discriminant const what = what_; if( what.kind() == result_discriminant::val ) leaf_detail::print_result_value(os, value()); else { error_id const err_id = what.get_error_id(); os << "Error ID " << err_id; if( what.kind() == result_discriminant::err_id_capture_list ) { #if BOOST_LEAF_CFG_CAPTURE # if BOOST_LEAF_CFG_DIAGNOSTICS cap_.print(os, ". Captured error objects:\n", err_id.value()); # endif #else BOOST_LEAF_ASSERT(0); // Possible ODR violation. #endif } } } template friend std::ostream & operator<<( std::basic_ostream & os, result const & r ) { r.print(os); return os; } }; //////////////////////////////////////// namespace leaf_detail { struct void_ { }; } template <> class BOOST_LEAF_SYMBOL_VISIBLE BOOST_LEAF_ATTRIBUTE_NODISCARD result: result { template friend class result; friend class leaf_detail::dynamic_allocator; using result_discriminant = leaf_detail::result_discriminant; using void_ = leaf_detail::void_; using base = result; #if BOOST_LEAF_CFG_CAPTURE result( int err_id, leaf_detail::capture_list && cap ) noexcept: base(err_id, std::move(cap)) { } #endif public: using value_type = void; // NOTE: Copy constructor implicitly deleted. result( result && x ) noexcept: base(std::move(x)) { } result() noexcept { } result( error_id err ) noexcept: base(err) { } #if BOOST_LEAF_CFG_STD_SYSTEM_ERROR result( std::error_code const & ec ) noexcept: base(ec) { } template ::value, int>::type> result( Enum e ) noexcept: base(e) { } #endif ~result() noexcept { } // NOTE: Assignment operator implicitly deleted. result & operator=( result && x ) noexcept { base::move_assign(std::move(x)); return *this; } void value() const { base::enforce_value_state(); } void const * operator->() const noexcept { return base::operator->(); } void * operator->() noexcept { return base::operator->(); } void operator*() const noexcept { BOOST_LEAF_ASSERT(has_value()); } template void print( std::basic_ostream & os ) const { if( what_.kind() == result_discriminant::val ) os << "No error"; else os << *static_cast(this); } template friend std::ostream & operator<<( std::basic_ostream & os, result const & r ) { r.print(os); return os; } using base::operator=; using base::operator bool; using base::get_error_id; using base::error; using base::load; using base::unload; }; //////////////////////////////////////// template struct is_result_type; template struct is_result_type>: std::true_type { }; } } #endif