Thread-safe alternative to strerror()

Uses:
  - strerror_s() on Windows;
  - the GNU strerror_r() on non-Windows systems where _GNU_SOURCE is
    defined (which is currently the case when the GNU libstdc++ is used,
    even if one doesn't explicitely define _GNU_SOURCE, cf.
    <https://gcc.gnu.org/onlinedocs/libstdc++/faq.html#faq.predefined>);
  - the XSI-compliant strerror_r() on other systems, as long as
    _POSIX_C_SOURCE >= 200112L (otherwise, the compilation will abort
    due to a #error preprocessor instruction).
This commit is contained in:
Florent Rougon 2016-05-08 21:45:01 +02:00
parent 616826ab69
commit 757970fe41
3 changed files with 78 additions and 0 deletions

View File

@ -24,11 +24,14 @@
#include <cstring>
#include <sstream>
#include <algorithm>
#include <string.h> // strerror_r() and strerror_s()
#include <errno.h>
#include "strutils.hxx"
#include <simgear/debug/logstream.hxx>
#include <simgear/package/md5.h>
#include <simgear/compiler.h> // SG_WINDOWS
using std::string;
using std::vector;
@ -597,6 +600,49 @@ string sanitizePrintfFormat(const string& input)
return input;
}
std::string error_string(int errnum)
{
char buf[512]; // somewhat arbitrary...
// This could be simplified with C11 (annex K, optional...), which offers:
//
// errno_t strerror_s( char *buf, rsize_t bufsz, errno_t errnum );
// size_t strerrorlen_s( errno_t errnum );
#if defined(SG_WINDOWS)
errno_t retcode;
// Always makes the string in 'buf' null-terminated
retcode = strerror_s(buf, sizeof(buf), errnum);
#elif defined(_GNU_SOURCE)
return std::string(strerror_r(errnum, buf, sizeof(buf)));
#elif _POSIX_C_SOURCE >= 200112L
int retcode;
// POSIX.1-2001 and POSIX.1-2008
retcode = strerror_r(errnum, buf, sizeof(buf));
#else
#error "Could not find a thread-safe alternative to strerror()."
#endif
#if !defined(_GNU_SOURCE)
if (retcode) {
std::string msg = "unable to get error message for a given error number";
// C++11 would make this shorter with std::to_string()
std::ostringstream ostr;
ostr << errnum;
#if !defined(SG_WINDOWS)
if (retcode == ERANGE) { // more specific error message in this case
msg = std::string("buffer too small to hold the error message for "
"the specified error number");
}
#endif
throw sg_error(msg, ostr.str());
}
return std::string(buf);
#endif // !defined(_GNU_SOURCE)
}
} // end namespace strutils
} // end namespace simgear

View File

@ -216,6 +216,14 @@ namespace simgear {
*/
std::string sanitizePrintfFormat(const std::string& input);
/**
* Get the message corresponding to a given value of errno.
*
* Similar to strerror(), except it should be thread-safe and returns an
* std::string.
*/
std::string error_string(int errnum);
} // end namespace strutils
} // end namespace simgear

View File

@ -2,6 +2,9 @@
#define BOOST_TEST_MODULE misc
#include <BoostTestTargetConfig.h>
#include <errno.h>
#include <stdlib.h> // _set_errno() on Windows
#include <fstream> // std::ifstream
#include <simgear/compiler.h>
#include "strutils.hxx"
@ -78,3 +81,24 @@ BOOST_AUTO_TEST_CASE( md5_hex )
// md5
BOOST_CHECK_EQUAL(strutils::md5("test"), "098f6bcd4621d373cade4e832627b4f6");
}
BOOST_AUTO_TEST_CASE( error_string )
{
#if defined(SG_WINDOWS)
_set_errno(0);
#else
errno = 0;
#endif
std::ifstream f("/\\/non-existent/file/a8f7bz97-3ffe-4f5b-b8db-38ccurJL-");
#if defined(SG_WINDOWS)
errno_t saved_errno = errno;
#else
int saved_errno = errno;
#endif
BOOST_CHECK(!f.is_open());
BOOST_CHECK_NE(saved_errno, 0);
BOOST_CHECK_GT(strutils::error_string(saved_errno).size(), 0);
}