diff --git a/include/boost/system/detail/error_category.hpp b/include/boost/system/detail/error_category.hpp index 8b5ba51..b91c193 100644 --- a/include/boost/system/detail/error_category.hpp +++ b/include/boost/system/detail/error_category.hpp @@ -172,6 +172,7 @@ namespace detail static const boost::ulong_long_type generic_category_id = ( boost::ulong_long_type( 0xB2AB117A ) << 32 ) + 0x257EDF0D; static const boost::ulong_long_type system_category_id = ( boost::ulong_long_type( 0x8FAFD21E ) << 32 ) + 0x25C5E09B; +static const boost::ulong_long_type interop_category_id = ( boost::ulong_long_type( 0x943F2817 ) << 32 ) + 0xFD3A8FAF; BOOST_SYSTEM_CONSTEXPR inline bool failed_impl( int ev, error_category const & cat ) { diff --git a/include/boost/system/detail/error_code.hpp b/include/boost/system/detail/error_code.hpp index 2089ecb..97da236 100644 --- a/include/boost/system/detail/error_code.hpp +++ b/include/boost/system/detail/error_code.hpp @@ -10,15 +10,24 @@ // // See library home page at http://www.boost.org/libs/system +#include #include #include #include +#include +#include #include -#include +#include +#include #include #include #include #include +#include + +#if defined(BOOST_SYSTEM_HAS_SYSTEM_ERROR) +# include +#endif namespace boost { @@ -35,26 +44,70 @@ namespace system // and error_code containing a pointer to an object of a type derived // from error_category. +namespace detail +{ + +#if defined(BOOST_SYSTEM_HAS_SYSTEM_ERROR) + +typedef std::error_code std_error_code; + +#else + +class std_error_code +{ +private: + + struct category_type; + + int val_; + category_type const* cat_; + +public: + + int value() const { return val_; } + category_type const& category() const { return *cat_; } +}; + +#endif + +} // namespace detail + class error_code { private: - int val_; - bool failed_; - const error_category * cat_; + struct data + { + int val_; + const error_category * cat_; + }; + + union + { + data d1_; + unsigned char d2_[ sizeof(detail::std_error_code) ]; + }; + + // 0: default constructed, d1_ value initialized + // 1: holds std::error_code in d2_ + // 2: holds error code in d1_, failed == false + // 3: holds error code in d1_, failed == true + unsigned flags_; public: // constructors: BOOST_SYSTEM_CONSTEXPR error_code() BOOST_NOEXCEPT: - val_( 0 ), failed_( false ), cat_( &system_category() ) + d1_(), flags_( 0 ) { } BOOST_SYSTEM_CONSTEXPR error_code( int val, const error_category & cat ) BOOST_NOEXCEPT: - val_( val ), failed_( detail::failed_impl( val, cat ) ), cat_( &cat ) + d1_(), flags_( 2 + detail::failed_impl( val, cat ) ) { + d1_.val_ = val; + d1_.cat_ = &cat; } template BOOST_SYSTEM_CONSTEXPR error_code( ErrorCodeEnum e, @@ -63,13 +116,21 @@ public: *this = make_error_code( e ); } +#if defined(BOOST_SYSTEM_HAS_SYSTEM_ERROR) + + error_code( std::error_code const& ec ) BOOST_NOEXCEPT: + flags_( 1 ) + { + ::new( d2_ ) std::error_code( ec ); + } + +#endif + // modifiers: BOOST_SYSTEM_CONSTEXPR void assign( int val, const error_category & cat ) BOOST_NOEXCEPT { - val_ = val; - failed_ = detail::failed_impl( val, cat ); - cat_ = &cat; + *this = error_code( val, cat ); } template @@ -82,48 +143,107 @@ public: BOOST_SYSTEM_CONSTEXPR void clear() BOOST_NOEXCEPT { - val_ = 0; - failed_ = false; - cat_ = &system_category(); + *this = error_code(); } // observers: BOOST_SYSTEM_CONSTEXPR int value() const BOOST_NOEXCEPT { - return val_; + if( flags_ != 1 ) + { + return d1_.val_; + } + else + { + detail::std_error_code const& ec = reinterpret_cast( d2_ ); + return ec.value() + static_cast( reinterpret_cast( &ec.category() ) % 1073741789 /* 2^30-35, prime*/ ); + } } BOOST_SYSTEM_CONSTEXPR const error_category & category() const BOOST_NOEXCEPT { - return *cat_; + if( flags_ == 0 ) + { + return system_category(); + } + else if( flags_ == 1 ) + { + return detail::interop_category(); + } + else + { + return *d1_.cat_; + } } + // deprecated? error_condition default_error_condition() const BOOST_NOEXCEPT { - return cat_->default_error_condition( value() ); + return category().default_error_condition( value() ); } std::string message() const { - return cat_->message( value() ); +#if defined(BOOST_SYSTEM_HAS_SYSTEM_ERROR) + + if( flags_ == 1 ) + { + detail::std_error_code const& ec = reinterpret_cast( d2_ ); + return ec.message(); + } + +#endif + + return category().message( value() ); } char const * message( char * buffer, std::size_t len ) const BOOST_NOEXCEPT { - return cat_->message( value(), buffer, len ); +#if defined(BOOST_SYSTEM_HAS_SYSTEM_ERROR) + if( flags_ == 1 ) + { +#if !defined(BOOST_NO_EXCEPTIONS) + try +#endif + { + detail::std_error_code const& ec = reinterpret_cast( d2_ ); + detail::snprintf( buffer, len, "%s", ec.message().c_str() ); + return buffer; + } +#if !defined(BOOST_NO_EXCEPTIONS) + catch( ... ) + { + } +#endif + } +#endif + + return category().message( value(), buffer, len ); } BOOST_SYSTEM_CONSTEXPR bool failed() const BOOST_NOEXCEPT { - return failed_; + if( flags_ == 0 ) + { + return false; + } + else if( flags_ == 1 ) + { + detail::std_error_code const& ec = reinterpret_cast( d2_ ); + return ec.value() != 0; + } + else + { + return (flags_ & 1) != 0; + } } #if !defined(BOOST_NO_CXX11_EXPLICIT_CONVERSION_OPERATORS) BOOST_SYSTEM_CONSTEXPR explicit operator bool() const BOOST_NOEXCEPT // true if error { - return failed_; + return failed(); } #else @@ -133,12 +253,12 @@ public: BOOST_SYSTEM_CONSTEXPR operator unspecified_bool_type() const BOOST_NOEXCEPT // true if error { - return failed_? unspecified_bool_true: 0; + return failed()? unspecified_bool_true: 0; } BOOST_SYSTEM_CONSTEXPR bool operator!() const BOOST_NOEXCEPT // true if no error { - return !failed_; + return !failed(); } #endif @@ -150,37 +270,80 @@ public: BOOST_SYSTEM_CONSTEXPR inline friend bool operator==( const error_code & lhs, const error_code & rhs ) BOOST_NOEXCEPT { - return lhs.val_ == rhs.val_ && *lhs.cat_ == *rhs.cat_; + return lhs.value() == rhs.value() && lhs.category() == rhs.category(); } BOOST_SYSTEM_CONSTEXPR inline friend bool operator<( const error_code & lhs, const error_code & rhs ) BOOST_NOEXCEPT { - return *lhs.cat_ < *rhs.cat_ || ( *lhs.cat_ == *rhs.cat_ && lhs.val_ < rhs.val_ ); + return lhs.category() < rhs.category() || (lhs.category() == rhs.category() && lhs.value() < rhs.value()); + } + + BOOST_SYSTEM_CONSTEXPR inline friend bool operator!=( const error_code & lhs, const error_code & rhs ) BOOST_NOEXCEPT + { + return !( lhs == rhs ); } #if defined(BOOST_SYSTEM_HAS_SYSTEM_ERROR) operator std::error_code () const { - return std::error_code( value(), category() ); + if( flags_ == 1 ) + { + return reinterpret_cast( d2_ ); + } + else if( flags_ == 0 ) + { + //return std::error_code(); + return std::error_code( 0, boost::system::system_category() ); + } + else + { + return std::error_code( d1_.val_, *d1_.cat_ ); + } + } + + operator std::error_code () + { + return const_cast( *this ); + } + + template::value>::type> + operator T& () + { + if( flags_ != 1 ) + { + std::error_code e2( *this ); + ::new( d2_ ) std::error_code( e2 ); + flags_ = 1; + } + + return reinterpret_cast( d2_ ); } #endif + + template + inline friend std::basic_ostream& + operator<< (std::basic_ostream& os, error_code const & ec) + { +#if defined(BOOST_SYSTEM_HAS_SYSTEM_ERROR) + + if( ec.flags_ == 1 ) + { + detail::std_error_code const& e2 = reinterpret_cast( ec.d2_ ); + os << "std:" << e2.category().name() << ':' << e2.value(); + } + else +#endif + { + os << ec.category().name() << ':' << ec.value(); + } + + return os; + } }; -BOOST_SYSTEM_CONSTEXPR inline bool operator!=( const error_code & lhs, const error_code & rhs ) BOOST_NOEXCEPT -{ - return !( lhs == rhs ); -} - -template - inline std::basic_ostream& - operator<< (std::basic_ostream& os, error_code const & ec) -{ - os << ec.category().name() << ':' << ec.value(); - return os; -} - inline std::size_t hash_value( error_code const & ec ) { error_category const & cat = ec.category(); diff --git a/include/boost/system/detail/interop_category.hpp b/include/boost/system/detail/interop_category.hpp new file mode 100644 index 0000000..9755ad5 --- /dev/null +++ b/include/boost/system/detail/interop_category.hpp @@ -0,0 +1,107 @@ +#ifndef BOOST_SYSTEM_DETAIL_INTEROP_CATEGORY_HPP_INCLUDED +#define BOOST_SYSTEM_DETAIL_INTEROP_CATEGORY_HPP_INCLUDED + +// Copyright Beman Dawes 2006, 2007 +// Copyright Christoper Kohlhoff 2007 +// Copyright Peter Dimov 2017, 2018, 2021 +// +// 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) +// +// See library home page at http://www.boost.org/libs/system + +#include +#include +#include +#include + +namespace boost +{ + +namespace system +{ + +namespace detail +{ + +// interop_error_category, used for std::error_code + +#if ( defined( BOOST_GCC ) && BOOST_GCC >= 40600 ) || defined( BOOST_CLANG ) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wnon-virtual-dtor" +#endif + +class BOOST_SYMBOL_VISIBLE interop_error_category: public error_category +{ +public: + + BOOST_SYSTEM_CONSTEXPR interop_error_category() BOOST_NOEXCEPT: + error_category( detail::interop_category_id ) + { + } + + const char * name() const BOOST_NOEXCEPT BOOST_OVERRIDE + { + return "std:unknown"; + } + + std::string message( int ev ) const BOOST_OVERRIDE; + char const * message( int ev, char * buffer, std::size_t len ) const BOOST_NOEXCEPT BOOST_OVERRIDE; +}; + +#if ( defined( BOOST_GCC ) && BOOST_GCC >= 40600 ) || defined( BOOST_CLANG ) +#pragma GCC diagnostic pop +#endif + +inline char const * interop_error_category::message( int ev, char * buffer, std::size_t len ) const BOOST_NOEXCEPT +{ + detail::snprintf( buffer, len, "Unknown interop error %d", ev ); + return buffer; +} + +inline std::string interop_error_category::message( int ev ) const +{ + char buffer[ 48 ]; + return message( ev, buffer, sizeof( buffer ) ); +} + +// interop_category() + +#if defined(BOOST_SYSTEM_HAS_CONSTEXPR) + +template struct BOOST_SYMBOL_VISIBLE interop_cat_holder +{ + static constexpr interop_error_category instance{}; +}; + +// Before C++17 it was mandatory to redeclare all static constexpr +#if defined(BOOST_NO_CXX17_INLINE_VARIABLES) +template constexpr interop_error_category interop_cat_holder::instance; +#endif + +constexpr error_category const & interop_category() BOOST_NOEXCEPT +{ + return interop_cat_holder::instance; +} + +#else // #if defined(BOOST_SYSTEM_HAS_CONSTEXPR) + +#if !defined(__SUNPRO_CC) // trailing __global is not supported +inline error_category const & interop_category() BOOST_NOEXCEPT BOOST_SYMBOL_VISIBLE; +#endif + +inline error_category const & interop_category() BOOST_NOEXCEPT +{ + static const detail::interop_error_category instance; + return instance; +} + +#endif // #if defined(BOOST_SYSTEM_HAS_CONSTEXPR) + +} // namespace detail + +} // namespace system + +} // namespace boost + +#endif // #ifndef BOOST_SYSTEM_DETAIL_INTEROP_CATEGORY_HPP_INCLUDED diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 4dd3ab8..2d29e40 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -89,3 +89,7 @@ boost_test(TYPE run SOURCES linux_error_test.cpp) boost_test(TYPE link SOURCES errc_test3.cpp) boost_test(TYPE run SOURCES snprintf_test.cpp) + +boost_test(TYPE run SOURCES std_interop_test2.cpp) +boost_test(TYPE run SOURCES std_interop_test3.cpp) +boost_test(TYPE run SOURCES std_interop_test4.cpp) diff --git a/test/Jamfile.v2 b/test/Jamfile.v2 index 3061abe..9940d36 100644 --- a/test/Jamfile.v2 +++ b/test/Jamfile.v2 @@ -93,3 +93,7 @@ run linux_error_test.cpp ; link errc_test3.cpp ; run snprintf_test.cpp ; + +run std_interop_test2.cpp ; +run std_interop_test3.cpp ; +run std_interop_test4.cpp ; diff --git a/test/std_interop_test2.cpp b/test/std_interop_test2.cpp new file mode 100644 index 0000000..a1dccd0 --- /dev/null +++ b/test/std_interop_test2.cpp @@ -0,0 +1,88 @@ +// Copyright 2021 Peter Dimov. +// Distributed under the Boost Software License, Version 1.0. +// http://www.boost.org/LICENSE_1_0.txt + +#include +#include +#include +#include +#include +#include + +#if !defined(BOOST_SYSTEM_HAS_SYSTEM_ERROR) + +BOOST_PRAGMA_MESSAGE( "BOOST_SYSTEM_HAS_SYSTEM_ERROR not defined, test will be skipped" ) +int main() {} + +#else + +#include + +void f1( std::error_code ec, int value, std::error_category const& category ) +{ + BOOST_TEST_EQ( ec.value(), value ); + BOOST_TEST_EQ( &ec.category(), &category ); +} + +void f2( std::error_code const& ec, int value, std::error_category const& category ) +{ + BOOST_TEST_EQ( ec.value(), value ); + BOOST_TEST_EQ( &ec.category(), &category ); +} + +int main() +{ + { + boost::system::error_code e1; + boost::system::error_code e2( e1 ); + + f1( e1, e1.value(), e1.category() ); + f2( e1, e1.value(), e1.category() ); + + BOOST_TEST_EQ( e1, e2 ); + } + + { + boost::system::error_code e1( 0, boost::system::system_category() ); + boost::system::error_code e2( e1 ); + + f1( e1, e1.value(), e1.category() ); + f2( e1, e1.value(), e1.category() ); + + BOOST_TEST_EQ( e1, e2 ); + } + + { + boost::system::error_code e1( 5, boost::system::system_category() ); + boost::system::error_code e2( e1 ); + + f1( e1, e1.value(), e1.category() ); + f2( e1, e1.value(), e1.category() ); + + BOOST_TEST_EQ( e1, e2 ); + } + + { + boost::system::error_code e1( 0, boost::system::generic_category() ); + boost::system::error_code e2( e1 ); + + f1( e1, e1.value(), e1.category() ); + f2( e1, e1.value(), e1.category() ); + + BOOST_TEST_EQ( e1, e2 ); + } + + { + boost::system::error_code e1( ENOENT, boost::system::generic_category() ); + boost::system::error_code e2( e1 ); + + f1( e1, e1.value(), e1.category() ); + f2( e1, e1.value(), e1.category() ); + + BOOST_TEST_EQ( e1, e2 ); + } + + return boost::report_errors(); +} + +#endif diff --git a/test/std_interop_test3.cpp b/test/std_interop_test3.cpp new file mode 100644 index 0000000..9584bd7 --- /dev/null +++ b/test/std_interop_test3.cpp @@ -0,0 +1,63 @@ +// Copyright 2021 Peter Dimov. +// Distributed under the Boost Software License, Version 1.0. +// http://www.boost.org/LICENSE_1_0.txt + +#include +#include +#include +#include + +#if !defined(BOOST_SYSTEM_HAS_SYSTEM_ERROR) + +BOOST_PRAGMA_MESSAGE( "BOOST_SYSTEM_HAS_SYSTEM_ERROR not defined, test will be skipped" ) +int main() {} + +#else + +#include + +int main() +{ + { + std::error_code e1; + boost::system::error_code e2 = e1; + std::error_code e3 = e2; + + BOOST_TEST_EQ( e1, e3 ); + } + + { + std::error_code e1( 5, std::system_category() ); + boost::system::error_code e2 = e1; + std::error_code e3 = e2; + + BOOST_TEST_EQ( e1, e3 ); + } + + { + std::error_code e1( 0, std::generic_category() ); + boost::system::error_code e2 = e1; + std::error_code e3 = e2; + + BOOST_TEST_EQ( e1, e3 ); + } + + { + std::error_code e1( ENOENT, std::generic_category() ); + boost::system::error_code e2 = e1; + std::error_code e3 = e2; + + BOOST_TEST_EQ( e1, e3 ); + } + + { + boost::system::error_code e2 = make_error_code( std::errc::no_such_file_or_directory ); + std::error_code e3 = e2; + + BOOST_TEST_EQ( make_error_code( std::errc::no_such_file_or_directory ), e3 ); + } + + return boost::report_errors(); +} + +#endif diff --git a/test/std_interop_test4.cpp b/test/std_interop_test4.cpp new file mode 100644 index 0000000..ab74e3f --- /dev/null +++ b/test/std_interop_test4.cpp @@ -0,0 +1,89 @@ +// Copyright 2021 Peter Dimov. +// Distributed under the Boost Software License, Version 1.0. +// http://www.boost.org/LICENSE_1_0.txt + +#include +#include +#include +#include + +#if !defined(BOOST_SYSTEM_HAS_SYSTEM_ERROR) + +BOOST_PRAGMA_MESSAGE( "BOOST_SYSTEM_HAS_SYSTEM_ERROR not defined, test will be skipped" ) +int main() {} + +#else + +#include + +void f( std::error_code& e1, std::error_code e2 ) +{ + e1 = e2; +} + +int main() +{ + { + boost::system::error_code e1; + std::error_code e2; + + f( e1, e2 ); + + BOOST_TEST_EQ( e1, boost::system::error_code( e2 ) ); + + std::error_code e3( e1 ); + BOOST_TEST_EQ( e3, e2 ); + } + + { + boost::system::error_code e1; + std::error_code e2( 0, std::system_category() ); + + f( e1, e2 ); + + BOOST_TEST_EQ( e1, boost::system::error_code( e2 ) ); + + std::error_code e3( e1 ); + BOOST_TEST_EQ( e3, e2 ); + } + + { + boost::system::error_code e1; + std::error_code e2( 5, std::system_category() ); + + f( e1, e2 ); + + BOOST_TEST_EQ( e1, boost::system::error_code( e2 ) ); + + std::error_code e3( e1 ); + BOOST_TEST_EQ( e3, e2 ); + } + + { + boost::system::error_code e1; + std::error_code e2( 0, std::generic_category() ); + + f( e1, e2 ); + + BOOST_TEST_EQ( e1, boost::system::error_code( e2 ) ); + + std::error_code e3( e1 ); + BOOST_TEST_EQ( e3, e2 ); + } + + { + boost::system::error_code e1; + std::error_code e2( ENOENT, std::generic_category() ); + + f( e1, e2 ); + + BOOST_TEST_EQ( e1, boost::system::error_code( e2 ) ); + + std::error_code e3( e1 ); + BOOST_TEST_EQ( e3, e2 ); + } + + return boost::report_errors(); +} + +#endif