From ec93d058fe3a9588bf501dcfe69feded84da6889 Mon Sep 17 00:00:00 2001 From: Peter Dimov Date: Fri, 21 Sep 2018 03:16:55 +0300 Subject: [PATCH] Add a noexcept overload of message() taking a buffer --- .../boost/system/detail/generic_category.hpp | 46 +++++++--- .../system/detail/system_category_win32.hpp | 86 ++++++++++--------- include/boost/system/error_code.hpp | 82 ++++++++++++++++-- test/Jamfile.v2 | 56 ++++++------ test/error_category_test.cpp | 82 ++++++++++++++++++ test/generic_category_test.cpp | 41 +++++++++ 6 files changed, 310 insertions(+), 83 deletions(-) create mode 100644 test/error_category_test.cpp create mode 100644 test/generic_category_test.cpp 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..99d0990 100644 --- a/include/boost/system/detail/system_category_win32.hpp +++ b/include/boost/system/detail/system_category_win32.hpp @@ -24,66 +24,72 @@ namespace system namespace detail { -inline std::string system_category_message_win32( int ev ) +inline char const * system_category_message_win32( int ev, char * buffer, std::size_t len ) BOOST_NOEXCEPT { - using namespace boost::winapi; - - std::wstring buf( 128, wchar_t() ); - - for( ;; ) + if( len == 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 buffer; } - int num_chars = static_cast( buf.size() + 1 ) * 2; + if( len == 1 ) + { + buffer[0] = 0; + return buffer; + } - boost::winapi::LPSTR_ narrow_buffer = #if defined(__GNUC__) - (boost::winapi::LPSTR_)__builtin_alloca( num_chars ); +# define BOOST_SYSTEM_ALLOCA __builtin_alloca #else - (boost::winapi::LPSTR_)_alloca( num_chars ); +# define BOOST_SYSTEM_ALLOCA _alloca #endif - if( boost::winapi::WideCharToMultiByte( CP_ACP_, 0, buf.c_str(), -1, narrow_buffer, num_chars, NULL, NULL ) == 0 ) + 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 error"; } - std::string str( narrow_buffer ); + int r = boost::winapi::WideCharToMultiByte( CP_ACP_, 0, wbuffer, -1, buffer, static_cast( len ), NULL, NULL ); - while( !str.empty() && ( str[ str.size() - 1 ] == '\n' || str[ str.size() - 1 ] == '\r' ) ) + if( r == 0 ) { - str.erase( str.size() - 1 ); + return "Unknown error"; } - 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 ); + buffer[ --r ] = 0; } - return str; + if( r > 0 && buffer[ r-1 ] == '.' ) + { + buffer[ --r ] = 0; + } + + return buffer; +} + +inline std::string system_category_message_win32( int ev ) +{ + char buffer[ 256 ]; + return system_category_message_win32( ev, buffer, sizeof( 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 684cd47..e41309b 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 @@ -219,6 +220,8 @@ public: 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 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 +284,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 +300,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 +449,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 +580,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 +776,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 +830,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..893f364 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,33 @@ 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 ; # 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..63e813a --- /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 = -1; 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(); +}