diff --git a/include/boost/system/detail/generic_category.hpp b/include/boost/system/detail/generic_category.hpp index 6ef3326..e3792c9 100644 --- a/include/boost/system/detail/generic_category.hpp +++ b/include/boost/system/detail/generic_category.hpp @@ -20,45 +20,71 @@ namespace detail #if defined(__GLIBC__) -// std::strerror is not thread-safe on glibc (for no reason) -// glibc also has two incompatible strerror_r definitions (for no reason) +// glibc has two incompatible strerror_r definitions -inline char const * strerror_r_helper( char const * r, char const * ) +inline char const * strerror_r_helper( char const * r, char const * ) BOOST_NOEXCEPT { return r; } -inline char const * strerror_r_helper( int r, char const * buffer ) +inline char const * strerror_r_helper( int r, char const * buffer ) BOOST_NOEXCEPT { return r == 0? buffer: "Unknown error"; } +inline char const * generic_error_category_message( int ev, char * buffer, std::size_t len ) BOOST_NOEXCEPT +{ + return strerror_r_helper( strerror_r( ev, buffer, len ), buffer ); +} + inline std::string generic_error_category_message( int ev ) { char buffer[ 128 ]; - return strerror_r_helper( strerror_r( ev, buffer, sizeof( buffer ) ), buffer ); + return generic_error_category_message( ev, buffer, sizeof( buffer ) ); } #else // std::strerror is thread-safe on everything else, incl. Windows -inline std::string generic_error_category_message( int ev ) -{ # if defined( BOOST_MSVC ) # pragma warning( push ) # pragma warning( disable: 4996 ) # endif +inline std::string generic_error_category_message( int ev ) +{ char const * m = std::strerror( ev ); + return m? m: "Unknown error"; +} + +inline char const * generic_error_category_message( int ev, char * buffer, std::size_t len ) BOOST_NOEXCEPT +{ + if( len == 0 ) + { + return buffer; + } + + if( len == 1 ) + { + buffer[0] = 0; + return buffer; + } + + char const * m = std::strerror( ev ); + + if( m == 0 ) return "Unknown error"; + + std::strncpy( buffer, m, len - 1 ); + buffer[ len-1 ] = 0; + + return buffer; +} # if defined( BOOST_MSVC ) # pragma warning( pop ) # endif - return m? m: "Unknown error"; -} - #endif } // namespace detail diff --git a/include/boost/system/detail/system_category_win32.hpp b/include/boost/system/detail/system_category_win32.hpp index 66b8e1e..4739b89 100644 --- a/include/boost/system/detail/system_category_win32.hpp +++ b/include/boost/system/detail/system_category_win32.hpp @@ -12,6 +12,8 @@ #include #include #include +#include +#include // @@ -24,66 +26,167 @@ namespace system namespace detail { +#if ( defined(_MSC_VER) && _MSC_VER < 1900 ) || ( defined(__MINGW32__) && !defined(__MINGW64_VERSION_MAJOR) ) + +inline char const * unknown_message_win32( int ev, char * buffer, std::size_t len ) +{ +# if defined( BOOST_MSVC ) +# pragma warning( push ) +# pragma warning( disable: 4996 ) +# endif + + _snprintf( buffer, len - 1, "Unknown error (%d)", ev ); + + buffer[ len - 1 ] = 0; + return buffer; + +# if defined( BOOST_MSVC ) +# pragma warning( pop ) +# endif +} + +#else + +inline char const * unknown_message_win32( int ev, char * buffer, std::size_t len ) +{ + std::snprintf( buffer, len, "Unknown error (%d)", ev ); + return buffer; +} + +#endif + +inline char const * system_category_message_win32( int ev, char * buffer, std::size_t len ) BOOST_NOEXCEPT +{ + if( len == 0 ) + { + return buffer; + } + + if( len == 1 ) + { + buffer[0] = 0; + return buffer; + } + +#if defined(__GNUC__) +# define BOOST_SYSTEM_ALLOCA __builtin_alloca +#else +# define BOOST_SYSTEM_ALLOCA _alloca +#endif + + wchar_t * wbuffer = static_cast( BOOST_SYSTEM_ALLOCA( len * sizeof( wchar_t ) ) ); + +#undef BOOST_SYSTEM_ALLOCA + + using namespace boost::winapi; + + DWORD_ retval = boost::winapi::FormatMessageW( + FORMAT_MESSAGE_FROM_SYSTEM_ | FORMAT_MESSAGE_IGNORE_INSERTS_, + NULL, + ev, + MAKELANGID_( LANG_NEUTRAL_, SUBLANG_DEFAULT_ ), // Default language + wbuffer, + static_cast( len ), + NULL + ); + + if( retval == 0 ) + { + return unknown_message_win32( ev, buffer, len ); + } + + int r = boost::winapi::WideCharToMultiByte( CP_ACP_, 0, wbuffer, -1, buffer, static_cast( len ), NULL, NULL ); + + if( r == 0 ) + { + return unknown_message_win32( ev, buffer, len ); + } + + --r; // exclude null terminator + + while( r > 0 && ( buffer[ r-1 ] == '\n' || buffer[ r-1 ] == '\r' ) ) + { + buffer[ --r ] = 0; + } + + if( r > 0 && buffer[ r-1 ] == '.' ) + { + buffer[ --r ] = 0; + } + + return buffer; +} + +struct local_free +{ + void * p_; + + ~local_free() + { + boost::winapi::LocalFree( p_ ); + } +}; + +inline std::string unknown_message_win32( int ev ) +{ + char buffer[ 38 ]; + return unknown_message_win32( ev, buffer, sizeof( buffer ) ); +} + inline std::string system_category_message_win32( int ev ) { using namespace boost::winapi; - std::wstring buf( 128, wchar_t() ); + wchar_t * lpMsgBuf = 0; - for( ;; ) + DWORD_ retval = boost::winapi::FormatMessageW( + FORMAT_MESSAGE_ALLOCATE_BUFFER_ | FORMAT_MESSAGE_FROM_SYSTEM_ | FORMAT_MESSAGE_IGNORE_INSERTS_, + NULL, + ev, + MAKELANGID_( LANG_NEUTRAL_, SUBLANG_DEFAULT_ ), // Default language + (LPWSTR_) &lpMsgBuf, + 0, + NULL + ); + + if( retval == 0 ) { - DWORD_ retval = boost::winapi::FormatMessageW( - FORMAT_MESSAGE_FROM_SYSTEM_ | FORMAT_MESSAGE_IGNORE_INSERTS_, - NULL, - ev, - MAKELANGID_( LANG_NEUTRAL_, SUBLANG_DEFAULT_ ), // Default language - &buf[0], - static_cast( buf.size() ), - NULL - ); - - if( retval > 0 ) - { - buf.resize(retval); - break; - } - else if( boost::winapi::GetLastError() != ERROR_INSUFFICIENT_BUFFER_ ) - { - return "Unknown error"; - } - else - { - buf.resize( buf.size() + buf.size() / 2 ); - } + return unknown_message_win32( ev ); } - int num_chars = static_cast( buf.size() + 1 ) * 2; + local_free lf_ = { lpMsgBuf }; - boost::winapi::LPSTR_ narrow_buffer = -#if defined(__GNUC__) - (boost::winapi::LPSTR_)__builtin_alloca( num_chars ); -#else - (boost::winapi::LPSTR_)_alloca( num_chars ); -#endif + int r = boost::winapi::WideCharToMultiByte( CP_ACP_, 0, lpMsgBuf, -1, 0, 0, NULL, NULL ); - if( boost::winapi::WideCharToMultiByte( CP_ACP_, 0, buf.c_str(), -1, narrow_buffer, num_chars, NULL, NULL ) == 0 ) + if( r == 0 ) { - return "Unknown error"; + return unknown_message_win32( ev ); } - std::string str( narrow_buffer ); + std::string buffer( r, char() ); - while( !str.empty() && ( str[ str.size() - 1 ] == '\n' || str[ str.size() - 1 ] == '\r' ) ) + r = boost::winapi::WideCharToMultiByte( CP_ACP_, 0, lpMsgBuf, -1, &buffer[0], r, NULL, NULL ); + + if( r == 0 ) { - str.erase( str.size() - 1 ); + return unknown_message_win32( ev ); } - if( str.size() && str[ str.size() - 1 ] == '.' ) + --r; // exclude null terminator + + while( r > 0 && ( buffer[ r-1 ] == '\n' || buffer[ r-1 ] == '\r' ) ) { - str.erase( str.size() - 1 ); + --r; } - return str; + if( r > 0 && buffer[ r-1 ] == '.' ) + { + --r; + } + + buffer.resize( r ); + + return buffer; } inline error_condition system_category_default_error_condition_win32( int ev ) BOOST_NOEXCEPT diff --git a/include/boost/system/error_code.hpp b/include/boost/system/error_code.hpp index f5a4918..a740dfd 100644 --- a/include/boost/system/error_code.hpp +++ b/include/boost/system/error_code.hpp @@ -17,6 +17,7 @@ #include #include #include +#include // TODO: undef these macros if not already defined #include @@ -213,12 +214,13 @@ public: virtual const char * name() const BOOST_NOEXCEPT = 0; - virtual std::string message( int ev ) const = 0; - virtual error_condition default_error_condition( int ev ) const BOOST_NOEXCEPT; virtual bool equivalent( int code, const error_condition & condition ) const BOOST_NOEXCEPT; virtual bool equivalent( const error_code & code, int condition ) const BOOST_NOEXCEPT; + virtual std::string message( int ev ) const = 0; + virtual char const * message( int ev, char * buffer, std::size_t len ) const BOOST_NOEXCEPT; + BOOST_SYSTEM_CONSTEXPR bool operator==( const error_category & rhs ) const BOOST_NOEXCEPT { return rhs.id_ == 0? this == &rhs: id_ == rhs.id_; @@ -281,6 +283,7 @@ public: } std::string message( int ev ) const; + char const * message( int ev, char * buffer, std::size_t len ) const BOOST_NOEXCEPT; }; class BOOST_SYMBOL_VISIBLE system_error_category: public error_category @@ -296,8 +299,10 @@ public: return "system"; } - std::string message( int ev ) const; error_condition default_error_condition( int ev ) const BOOST_NOEXCEPT; + + std::string message( int ev ) const; + char const * message( int ev, char * buffer, std::size_t len ) const BOOST_NOEXCEPT; }; } // namespace detail @@ -443,6 +448,11 @@ public: return m_cat->message( value() ); } + char const * message( char * buffer, std::size_t len ) const BOOST_NOEXCEPT + { + return m_cat->message( value(), buffer, len ); + } + #if !defined(BOOST_NO_CXX11_EXPLICIT_CONVERSION_OPERATORS) BOOST_SYSTEM_CONSTEXPR explicit operator bool() const BOOST_NOEXCEPT // true if error @@ -569,6 +579,11 @@ public: return m_cat->message( value() ); } + char const * message( char * buffer, std::size_t len ) const BOOST_NOEXCEPT + { + return m_cat->message( value(), buffer, len ); + } + #if !defined(BOOST_NO_CXX11_EXPLICIT_CONVERSION_OPERATORS) BOOST_SYSTEM_CONSTEXPR explicit operator bool() const BOOST_NOEXCEPT // true if error @@ -760,6 +775,47 @@ inline bool error_category::equivalent( const error_code & code, int condition ) return *this == code.category() && code.value() == condition; } +inline char const * error_category::message( int ev, char * buffer, std::size_t len ) const BOOST_NOEXCEPT +{ + if( len == 0 ) + { + return buffer; + } + + if( len == 1 ) + { + buffer[0] = 0; + return buffer; + } + +#if !defined(BOOST_NO_EXCEPTIONS) + try +#endif + { + std::string m = this->message( ev ); + +# if defined( BOOST_MSVC ) +# pragma warning( push ) +# pragma warning( disable: 4996 ) +# endif + + std::strncpy( buffer, m.c_str(), len - 1 ); + buffer[ len-1 ] = 0; + +# if defined( BOOST_MSVC ) +# pragma warning( pop ) +# endif + + return buffer; + } +#if !defined(BOOST_NO_EXCEPTIONS) + catch( ... ) + { + return "Message text unavailable"; + } +#endif +} + } // namespace system } // namespace boost @@ -773,34 +829,49 @@ inline std::string boost::system::detail::generic_error_category::message( int e return generic_error_category_message( ev ); } +inline char const * boost::system::detail::generic_error_category::message( int ev, char * buffer, std::size_t len ) const BOOST_NOEXCEPT +{ + return generic_error_category_message( ev, buffer, len ); +} + // system_error_category implementation #if defined(BOOST_WINDOWS_API) #include +inline boost::system::error_condition boost::system::detail::system_error_category::default_error_condition( int ev ) const BOOST_NOEXCEPT +{ + return system_category_default_error_condition_win32( ev ); +} + inline std::string boost::system::detail::system_error_category::message( int ev ) const { return system_category_message_win32( ev ); } -inline boost::system::error_condition boost::system::detail::system_error_category::default_error_condition( int ev ) const BOOST_NOEXCEPT +inline char const * boost::system::detail::system_error_category::message( int ev, char * buffer, std::size_t len ) const BOOST_NOEXCEPT { - return system_category_default_error_condition_win32( ev ); + return system_category_message_win32( ev, buffer, len ); } #else // #if defined(BOOST_WINDOWS_API) #include +inline boost::system::error_condition boost::system::detail::system_error_category::default_error_condition( int ev ) const BOOST_NOEXCEPT +{ + return system_category_default_error_condition_posix( ev ); +} + inline std::string boost::system::detail::system_error_category::message( int ev ) const { return generic_error_category_message( ev ); } -inline boost::system::error_condition boost::system::detail::system_error_category::default_error_condition( int ev ) const BOOST_NOEXCEPT +inline char const * boost::system::detail::system_error_category::message( int ev, char * buffer, std::size_t len ) const BOOST_NOEXCEPT { - return system_category_default_error_condition_posix( ev ); + return generic_error_category_message( ev, buffer, len ); } #endif // #if defined(BOOST_WINDOWS_API) diff --git a/test/Jamfile.v2 b/test/Jamfile.v2 index 9e5beb7..7fbff70 100644 --- a/test/Jamfile.v2 +++ b/test/Jamfile.v2 @@ -1,6 +1,7 @@ # Boost System Library test Jamfile # Copyright Beman Dawes 2003, 2006 +# Copyright 2017, 2018 Peter Dimov # Distributed under the Boost Software License, Version 1.0. # See accompanying file LICENSE_1_0.txt or http://www.boost.org/LICENSE_1_0.txt @@ -13,7 +14,6 @@ import os ; project : requirements /boost/system//boost_system - msvc:on ; lib throw_test : throw_test.cpp : shared:THROW_DYN_LINK=1 ; @@ -98,33 +98,34 @@ else } } - test-suite "system" - : [ system-run error_code_test.cpp ] - [ system-run error_code_user_test.cpp ] - [ system-run system_error_test.cpp ] - [ run dynamic_link_test.cpp throw_test - : : : shared : throw_test_shared - ] - [ system-run initialization_test.cpp ] - [ run header_only_test.cpp - : : : -/boost/system//boost_system - ] - [ run header_only_test.cpp - : : : -/boost/system//boost_system BOOST_NO_ANSI_APIS : header_only_test_no_ansi - ] - [ run config_test.cpp - : : : always_show_run_output - ] - [ system-run- std_interop_test.cpp ] - [ system-run std_mismatch_test.cpp ] - [ system-run single_instance_test.cpp single_instance_1.cpp single_instance_2.cpp ] - [ run single_instance_test.cpp single_instance_lib1 single_instance_lib2 : : : static : single_instance_lib_static ] - [ run single_instance_test.cpp single_instance_lib1 single_instance_lib2 : : : shared : single_instance_lib_shared ] - [ system-run before_main_test.cpp ] - [ run-fail throws_assign_fail.cpp ] - [ system-run- constexpr_test.cpp ] - [ system-run win32_hresult_test.cpp ] - ; +system-run error_code_test.cpp ; +system-run error_code_user_test.cpp ; +system-run system_error_test.cpp ; + +run dynamic_link_test.cpp throw_test : : : shared : throw_test_shared ; + +system-run initialization_test.cpp ; + +run header_only_test.cpp : : : -/boost/system//boost_system ; +run header_only_test.cpp : : : -/boost/system//boost_system BOOST_NO_ANSI_APIS : header_only_test_no_ansi ; + +run config_test.cpp : : : always_show_run_output ; + +system-run- std_interop_test.cpp ; +system-run std_mismatch_test.cpp ; + +system-run single_instance_test.cpp single_instance_1.cpp single_instance_2.cpp ; +run single_instance_test.cpp single_instance_lib1 single_instance_lib2 : : : static : single_instance_lib_static ; +run single_instance_test.cpp single_instance_lib1 single_instance_lib2 : : : shared : single_instance_lib_shared ; + +system-run before_main_test.cpp ; +run-fail throws_assign_fail.cpp ; +system-run- constexpr_test.cpp ; +system-run win32_hresult_test.cpp ; + +system-run error_category_test.cpp ; +system-run generic_category_test.cpp ; +system-run system_category_test.cpp ; # Quick (CI) test run quick.cpp ; diff --git a/test/error_category_test.cpp b/test/error_category_test.cpp new file mode 100644 index 0000000..a403fc3 --- /dev/null +++ b/test/error_category_test.cpp @@ -0,0 +1,82 @@ + +// Copyright 2018 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 + +// See library home page at http://www.boost.org/libs/system + +// Avoid spurious VC++ warnings +# define _CRT_SECURE_NO_WARNINGS + +#include +#include +#include + +// + +namespace sys = boost::system; + +class user_category: public sys::error_category +{ +public: + + virtual const char * name() const BOOST_NOEXCEPT + { + return "user"; + } + + virtual std::string message( int ev ) const + { + char buffer[ 256 ]; + std::sprintf( buffer, "user message %d", ev ); + + return buffer; + } + + using sys::error_category::message; +}; + +static user_category s_cat_1; +static user_category s_cat_2; + +int main() +{ + // default_error_condition + + BOOST_TEST( s_cat_1.default_error_condition( 1 ) == sys::error_condition( 1, s_cat_1 ) ); + BOOST_TEST( s_cat_2.default_error_condition( 2 ) == sys::error_condition( 2, s_cat_2 ) ); + + // equivalent + + BOOST_TEST( s_cat_1.equivalent( 1, sys::error_condition( 1, s_cat_1 ) ) ); + BOOST_TEST( !s_cat_1.equivalent( 1, sys::error_condition( 2, s_cat_1 ) ) ); + BOOST_TEST( !s_cat_1.equivalent( 1, sys::error_condition( 2, s_cat_2 ) ) ); + + // the other equivalent + + BOOST_TEST( s_cat_1.equivalent( sys::error_code( 1, s_cat_1 ), 1 ) ); + BOOST_TEST( !s_cat_1.equivalent( sys::error_code( 1, s_cat_1 ), 2 ) ); + BOOST_TEST( !s_cat_1.equivalent( sys::error_code( 1, s_cat_2 ), 1 ) ); + + // message + + { + char buffer[ 256 ]; + BOOST_TEST_CSTR_EQ( s_cat_1.message( 1, buffer, sizeof( buffer ) ), s_cat_1.message( 1 ).c_str() ); + } + + { + char buffer[ 4 ]; + BOOST_TEST_CSTR_EQ( s_cat_1.message( 1, buffer, sizeof( buffer ) ), "use" ); + } + + // == + + BOOST_TEST_NOT( s_cat_1 == s_cat_2 ); + BOOST_TEST( s_cat_1 != s_cat_2 ); + + return boost::report_errors(); +} diff --git a/test/generic_category_test.cpp b/test/generic_category_test.cpp new file mode 100644 index 0000000..0d53815 --- /dev/null +++ b/test/generic_category_test.cpp @@ -0,0 +1,41 @@ + +// Copyright 2018 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 + +// See library home page at http://www.boost.org/libs/system + +// Avoid spurious VC++ warnings +# define _CRT_SECURE_NO_WARNINGS + +#include +#include +#include + +// + +namespace sys = boost::system; + +int main() +{ + sys::error_category const & cat = sys::generic_category(); + + // message + + for( int i = -2; i < 1024; ++i ) + { + { + BOOST_TEST_CSTR_EQ( cat.message( i ).c_str(), std::strerror( i ) ); + } + + { + char buffer[ 256 ]; + BOOST_TEST_CSTR_EQ( cat.message( i, buffer, sizeof( buffer ) ), std::strerror( i ) ); + } + } + + return boost::report_errors(); +} diff --git a/test/system_category_test.cpp b/test/system_category_test.cpp new file mode 100644 index 0000000..f6aa615 --- /dev/null +++ b/test/system_category_test.cpp @@ -0,0 +1,116 @@ + +// Copyright 2018 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 + +// See library home page at http://www.boost.org/libs/system + +// Avoid spurious VC++ warnings +# define _CRT_SECURE_NO_WARNINGS + +#include +#include +#include +#include + +// + +#if defined(BOOST_WINDOWS_API) + +#include + +std::string sys_strerror( int ev ) +{ + void * lpMsgBuf = 0; + + DWORD retval = FormatMessageA( + FORMAT_MESSAGE_ALLOCATE_BUFFER | + FORMAT_MESSAGE_FROM_SYSTEM | + FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, + ev, + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + (LPSTR) &lpMsgBuf, + 0, + NULL + ); + + struct local_free + { + void * p_; + + ~local_free() + { + LocalFree( p_ ); + } + }; + + local_free lf_ = { lpMsgBuf }; + + if( retval == 0 ) + { + char buffer[ 38 ]; + + std::sprintf( buffer, "Unknown error (%d)", ev ); + return buffer; + } + + std::string str( static_cast( lpMsgBuf ) ); + + while( !str.empty() && (str[str.size()-1] == '\n' || str[str.size()-1] == '\r') ) + { + str.erase( str.size()-1 ); + } + + if( !str.empty() && str[str.size()-1] == '.' ) + { + str.erase( str.size()-1 ); + } + + return str; +} + +#else + +std::string sys_strerror( int ev ) +{ + return std::strerror( ev ); +} + +#endif + +// + +namespace sys = boost::system; + +static void test_message( sys::error_category const & cat, int ev ) +{ + BOOST_TEST_EQ( cat.message( ev ), sys_strerror( ev ) ); + + char buffer[ 2048 ]; // yes, really + BOOST_TEST_CSTR_EQ( cat.message( ev, buffer, sizeof( buffer ) ), sys_strerror( ev ).c_str() ); +} + +int main() +{ + sys::error_category const & cat = sys::system_category(); + + // message + + for( int i = -2; i < 1024; ++i ) + { + test_message( cat, i ); + } + + test_message( cat, 5810 ); + + for( int i = 10000; i < 11032; ++i ) + { + test_message( cat, i ); + } + + return boost::report_errors(); +}