#ifndef BOOST_LEAF_RESULT_HPP_INCLUDED #define BOOST_LEAF_RESULT_HPP_INCLUDED // Copyright 2018-2025 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 namespace boost { namespace leaf { //////////////////////////////////////// class bad_result: public std::exception { char const * what() const noexcept override { return "boost::leaf::bad_result"; } }; //////////////////////////////////////// namespace 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, 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)); } }; // class result_discriminant } // namespace detail //////////////////////////////////////// template class BOOST_LEAF_ATTRIBUTE_NODISCARD result { template friend class result; #if BOOST_LEAF_CFG_CAPTURE friend class detail::dynamic_allocator; using capture_list = detail::capture_list; #endif using result_discriminant = detail::result_discriminant; using stored_type = typename detail::stored::type; using value_no_ref = typename detail::stored::value_no_ref; using value_no_ref_const = typename detail::stored::value_no_ref_const; using value_ref = typename detail::stored::value_ref; using value_cref = typename detail::stored::value_cref; using value_rv_ref = typename detail::stored::value_rv_ref; using value_rv_cref = typename 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(auto k = 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 default: BOOST_LEAF_ASSERT(k == result_discriminant::err_id); (void) k; case result_discriminant::err_id_zero: return result(what.get_error_id()); } } operator error_id() const noexcept { result_discriminant const what = r_.what_; return what.kind() == result_discriminant::val? error_id() : what.get_error_id(); } }; void destroy() const noexcept { switch(auto k = this->what_.kind()) { default: BOOST_LEAF_ASSERT(k == result_discriminant::err_id); (void) k; case result_discriminant::err_id_zero: 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(auto k = x_what.kind()) { default: BOOST_LEAF_ASSERT(k == result_discriminant::err_id); (void) k; case result_discriminant::err_id_zero: 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, 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: throw_exception(get_error_id(), bad_result{}); case result_discriminant::val: break; } } template void move_assign( result && x ) noexcept { destroy(); what_ = move_from(std::move(x)); } template error_id output_error_to(Encoder & e) const { static_assert(std::is_base_of::value, "Encoder must derive from detail::encoder"); result_discriminant const what = what_; BOOST_LEAF_ASSERT(what.kind() != result_discriminant::val); error_id const err_id = what.get_error_id(); detail::serialize_(e, err_id); return err_id; } template void output_capture_to(Encoder & e, error_id err_id) const { static_assert(std::is_base_of::value, "Encoder must derive from detail::encoder"); if( what_.kind() == result_discriminant::err_id_capture_list ) { #if BOOST_LEAF_CFG_CAPTURE cap_.serialize_to(e, err_id); #else BOOST_LEAF_ASSERT(0); // Possible ODR violation. #endif } } template void print_error( DiagnosticsWriter & dw ) const { static_assert(std::is_base_of::value, "DiagnosticsWriter must derive from detail::encoder"); error_id err_id = output_error_to(dw); dw.set_prefix(", captured -> "); dw.set_delimiter(", "); output_capture_to(dw, err_id); } 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 // #if defined(BOOST_STRICT_CONFIG) || !defined(__clang__) 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 // #else (#if defined(BOOST_STRICT_CONFIG) || !defined(__clang__)) #if BOOST_LEAF_CFG_STD_SYSTEM_ERROR result( std::error_code const & ec ) noexcept(!BOOST_LEAF_CFG_CAPTURE): what_(error_id(ec)) { } template ::value, int>::type> result( Enum e ) noexcept(!BOOST_LEAF_CFG_CAPTURE): what_(error_id(e)) { } #endif // #if BOOST_LEAF_CFG_STD_SYSTEM_ERROR ~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 // #ifdef BOOST_LEAF_NO_CXX11_REF_QUALIFIERS 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 // #else (#ifdef BOOST_LEAF_NO_CXX11_REF_QUALIFIERS) value_no_ref_const * operator->() const noexcept { return has_value() ? detail::stored::cptr(stored_) : nullptr; } value_no_ref * operator->() noexcept { return has_value() ? 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 // #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; } 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 // #else (#ifdef BOOST_LEAF_NO_CXX11_REF_QUALIFIERS) error_result error() noexcept { return error_result{*this}; } template error_id load( Item && ... item ) noexcept(!BOOST_LEAF_CFG_CAPTURE) { 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 serialize_to(Encoder & e) const { detail::encoder_adaptor ea(e); if( what_.kind() == result_discriminant::val ) detail::serialize_(ea, value()); else output_capture_to(ea, output_error_to(ea)); } template friend std::ostream & operator<<( std::basic_ostream & os, result const & r ) { detail::diagnostics_writer w(os); w.set_prefix(": "); if( r ) { os << "Success"; detail::serialize_(w, r.value()); } else { os << "Failure"; r.print_error(w); } return os; } }; // template result //////////////////////////////////////// namespace detail { struct void_ { }; } template <> class BOOST_LEAF_ATTRIBUTE_NODISCARD result: result { template friend class result; using result_discriminant = detail::result_discriminant; using void_ = detail::void_; using base = result; #if BOOST_LEAF_CFG_CAPTURE friend class detail::dynamic_allocator; result( int err_id, 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(!BOOST_LEAF_CFG_CAPTURE): base(ec) { } template ::value, int>::type> result( Enum e ) noexcept(!BOOST_LEAF_CFG_CAPTURE): base(e) { } #endif // #if BOOST_LEAF_CFG_STD_SYSTEM_ERROR ~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 serialize_to(Encoder & e) const { if( !*this ) { detail::encoder_adaptor ea(e); output_error_to(ea); } } template friend std::ostream & operator<<( std::basic_ostream & os, result const & r ) { if( r ) os << "Success"; else { detail::diagnostics_writer w(os); w.set_prefix(": "); os << "Failure"; r.print_error(w); } return os; } using base::operator=; using base::operator bool; using base::get_error_id; using base::error; using base::load; using base::unload; }; // result specialization for void //////////////////////////////////////// template struct is_result_type; template struct is_result_type>: std::true_type { }; } } // namespace boost::leaf #endif // #ifndef BOOST_LEAF_RESULT_HPP_INCLUDED