Skip to content

Commit

Permalink
Replace fmt::system_error with std::system_error
Browse files Browse the repository at this point in the history
  • Loading branch information
vitaut committed May 7, 2021
1 parent 4b885c8 commit 16f2ef9
Show file tree
Hide file tree
Showing 13 changed files with 81 additions and 248 deletions.
110 changes: 10 additions & 100 deletions include/fmt/format-inl.h
Original file line number Diff line number Diff line change
Expand Up @@ -27,11 +27,6 @@

#include "format.h"

// Dummy implementations of strerror_r and strerror_s called if corresponding
// system functions are not available.
inline fmt::detail::null<> strerror_r(int, char*, ...) { return {}; }
inline fmt::detail::null<> strerror_s(char*, size_t, ...) { return {}; }

FMT_BEGIN_NAMESPACE
namespace detail {

Expand All @@ -57,75 +52,6 @@ inline int fmt_snprintf(char* buffer, size_t size, const char* format, ...) {
# define FMT_SNPRINTF fmt_snprintf
#endif // _MSC_VER

// A portable thread-safe version of strerror.
// Sets buffer to point to a string describing the error code.
// This can be either a pointer to a string stored in buffer,
// or a pointer to some static immutable string.
// Returns one of the following values:
// 0 - success
// ERANGE - buffer is not large enough to store the error message
// other - failure
// Buffer should be at least of size 1.
inline int safe_strerror(int error_code, char*& buffer, size_t buffer_size) {
FMT_ASSERT(buffer != nullptr && buffer_size != 0, "invalid buffer");

class dispatcher {
private:
int error_code_;
char*& buffer_;
size_t buffer_size_;

// A noop assignment operator to avoid bogus warnings.
void operator=(const dispatcher&) {}

// Handle the result of XSI-compliant version of strerror_r.
int handle(int result) {
// glibc versions before 2.13 return result in errno.
return result == -1 ? errno : result;
}

// Handle the result of GNU-specific version of strerror_r.
FMT_MAYBE_UNUSED
int handle(char* message) {
// If the buffer is full then the message is probably truncated.
if (message == buffer_ && strlen(buffer_) == buffer_size_ - 1)
return ERANGE;
buffer_ = message;
return 0;
}

// Handle the case when strerror_r is not available.
FMT_MAYBE_UNUSED
int handle(detail::null<>) {
return fallback(strerror_s(buffer_, buffer_size_, error_code_));
}

// Fallback to strerror_s when strerror_r is not available.
FMT_MAYBE_UNUSED
int fallback(int result) {
// If the buffer is full then the message is probably truncated.
return result == 0 && strlen(buffer_) == buffer_size_ - 1 ? ERANGE
: result;
}

#if !FMT_MSC_VER
// Fallback to strerror if strerror_r and strerror_s are not available.
int fallback(detail::null<>) {
errno = 0;
buffer_ = strerror(error_code_);
return errno;
}
#endif

public:
dispatcher(int err_code, char*& buf, size_t buf_size)
: error_code_(err_code), buffer_(buf), buffer_size_(buf_size) {}

int run() { return handle(strerror_r(error_code_, buffer_, buffer_size_)); }
};
return dispatcher(error_code, buffer, buffer_size).run();
}

FMT_FUNC void format_error_code(detail::buffer<char>& out, int error_code,
string_view message) FMT_NOEXCEPT {
// Report error code making sure that the output fits into
Expand All @@ -150,7 +76,7 @@ FMT_FUNC void format_error_code(detail::buffer<char>& out, int error_code,
}

FMT_FUNC void report_error(format_func func, int error_code,
string_view message) FMT_NOEXCEPT {
const char* message) FMT_NOEXCEPT {
memory_buffer full_message;
func(full_message, error_code, message);
// Don't use fwrite_fully because the latter may throw.
Expand Down Expand Up @@ -202,16 +128,12 @@ template <typename Char> FMT_FUNC Char decimal_point_impl(locale_ref) {

#if !FMT_MSC_VER
FMT_API FMT_FUNC format_error::~format_error() FMT_NOEXCEPT = default;
FMT_API FMT_FUNC system_error::~system_error() FMT_NOEXCEPT = default;
#endif

FMT_FUNC void system_error::init(int err_code, string_view format_str,
format_args args) {
error_code_ = err_code;
memory_buffer buffer;
format_system_error(buffer, err_code, vformat(format_str, args));
std::runtime_error& base = *this;
base = std::runtime_error(to_string(buffer));
FMT_FUNC std::system_error vsystem_error(int error_code, string_view format_str,
format_args args) {
auto ec = std::error_code(error_code, std::generic_category());
return std::system_error(ec, vformat(format_str, args));
}

namespace detail {
Expand Down Expand Up @@ -2634,23 +2556,11 @@ FMT_FUNC detail::utf8_to_utf16::utf8_to_utf16(string_view s) {
}

FMT_FUNC void format_system_error(detail::buffer<char>& out, int error_code,
string_view message) FMT_NOEXCEPT {
const char* message) FMT_NOEXCEPT {
FMT_TRY {
memory_buffer buf;
buf.resize(inline_buffer_size);
for (;;) {
char* system_message = &buf[0];
int result =
detail::safe_strerror(error_code, system_message, buf.size());
if (result == 0) {
format_to(detail::buffer_appender<char>(out), FMT_STRING("{}: {}"),
message, system_message);
return;
}
if (result != ERANGE)
break; // Can't get error message, report error code instead.
buf.resize(buf.size() * 2);
}
auto ec = std::error_code(error_code, std::generic_category());
write(std::back_inserter(out), std::system_error(ec, message).what());
return;
}
FMT_CATCH(...) {}
format_error_code(out, error_code, message);
Expand All @@ -2661,7 +2571,7 @@ FMT_FUNC void detail::error_handler::on_error(const char* message) {
}

FMT_FUNC void report_system_error(int error_code,
fmt::string_view message) FMT_NOEXCEPT {
const char* message) FMT_NOEXCEPT {
report_error(format_system_error, error_code, message);
}

Expand Down
96 changes: 38 additions & 58 deletions include/fmt/format.h
Original file line number Diff line number Diff line change
Expand Up @@ -37,10 +37,11 @@
#include <cmath> // std::signbit
#include <cstdint>
#include <cwchar>
#include <limits> // std::numeric_limits
#include <memory> // std::uninitialized_copy
#include <stdexcept> // std::runtime_error
#include <utility> // std::swap
#include <limits> // std::numeric_limits
#include <memory> // std::uninitialized_copy
#include <stdexcept> // std::runtime_error
#include <system_error> // std::system_error
#include <utility> // std::swap

#include "core.h"

Expand Down Expand Up @@ -2675,90 +2676,69 @@ FMT_CONSTEXPR void handle_dynamic_spec(int& value,
}
}

using format_func = void (*)(detail::buffer<char>&, int, string_view);
using format_func = void (*)(detail::buffer<char>&, int, const char*);

FMT_API void format_error_code(buffer<char>& out, int error_code,
string_view message) FMT_NOEXCEPT;

FMT_API void report_error(format_func func, int error_code,
string_view message) FMT_NOEXCEPT;
const char* message) FMT_NOEXCEPT;
} // namespace detail
FMT_MODULE_EXPORT_BEGIN

template <typename OutputIt, typename Char>
using arg_formatter FMT_DEPRECATED_ALIAS =
detail::arg_formatter<OutputIt, Char>;

/**
An error returned by an operating system or a language runtime,
for example a file opening error.
*/
FMT_CLASS_API
class FMT_API system_error : public std::runtime_error {
private:
void init(int err_code, string_view format_str, format_args args);
FMT_API std::system_error vsystem_error(int error_code, string_view format_str,
format_args args);

protected:
int error_code_;

system_error() : std::runtime_error(""), error_code_(0) {}
/**
\rst
Constructs :class:`std::system_error` with a message formatted with
``fmt::format(message, args...)``.
*error_code* is a system error code as given by ``errno``.
public:
/**
\rst
Constructs a :class:`fmt::system_error` object with a description
formatted with `fmt::format_system_error`. *message* and additional
arguments passed into the constructor are formatted similarly to
`fmt::format`.
**Example**::
// This throws a system_error with the description
// cannot open file 'madeup': No such file or directory
// or similar (system message may vary).
const char *filename = "madeup";
std::FILE *file = std::fopen(filename, "r");
if (!file)
throw fmt::system_error(errno, "cannot open file '{}'", filename);
\endrst
*/
template <typename... Args>
system_error(int error_code, string_view message, const Args&... args)
: std::runtime_error("") {
init(error_code, message, make_format_args(args...));
}
system_error(const system_error&) = default;
system_error& operator=(const system_error&) = default;
system_error(system_error&&) = default;
system_error& operator=(system_error&&) = default;
~system_error() FMT_NOEXCEPT FMT_OVERRIDE FMT_MSC_DEFAULT;
**Example**::
int error_code() const { return error_code_; }
};
// This throws std::system_error with the description
// cannot open file 'madeup': No such file or directory
// or similar (system message may vary).
const char *filename = "madeup";
std::FILE *file = std::fopen(filename, "r");
if (!file)
throw fmt::system_error(errno, "cannot open file '{}'", filename);
\endrst
*/
template <typename... Args>
std::system_error system_error(int error_code, string_view message,
const Args&... args) {
return vsystem_error(error_code, message, make_format_args(args...));
}

/**
\rst
Formats an error returned by an operating system or a language runtime,
for example a file opening error, and writes it to *out* in the following
form:
Formats an error message for an error returned by an operating system or a
language runtime, for example a file opening error, and writes it to *out*.
The format is the same as the one used by ``std::system_error(ec, message)``
where ``ec`` is ``std::error_code(error_code, std::generic_category()})``.
It is implementation-defined but normally looks like:
.. parsed-literal::
*<message>*: *<system-message>*
where *<message>* is the passed message and *<system-message>* is
the system message corresponding to the error code.
where *<message>* is the passed message and *<system-message>* is the system
message corresponding to the error code.
*error_code* is a system error code as given by ``errno``.
If *error_code* is not a valid error code such as -1, the system message
may look like "Unknown error -1" and is platform-dependent.
\endrst
*/
FMT_API void format_system_error(detail::buffer<char>& out, int error_code,
string_view message) FMT_NOEXCEPT;
const char* message) FMT_NOEXCEPT;

// Reports a system error without throwing an exception.
// Can be used to report errors from destructors.
FMT_API void report_system_error(int error_code,
string_view message) FMT_NOEXCEPT;
const char* message) FMT_NOEXCEPT;

/** Fast integer formatter. */
class format_int {
Expand Down
6 changes: 3 additions & 3 deletions include/fmt/os.h
Original file line number Diff line number Diff line change
Expand Up @@ -152,7 +152,7 @@ class utf16_to_utf8 {
};

FMT_API void format_windows_error(buffer<char>& out, int error_code,
string_view message) FMT_NOEXCEPT;
const char* message) FMT_NOEXCEPT;
} // namespace detail

FMT_API std::system_error vwindows_error(int error_code, string_view format_str,
Expand All @@ -174,7 +174,7 @@ FMT_API std::system_error vwindows_error(int error_code, string_view format_str,
**Example**::
// This throws a windows_error with the description
// This throws a system_error with the description
// cannot open file 'madeup': The system cannot find the file specified.
// or similar (system message may vary).
const char *filename = "madeup";
Expand All @@ -195,7 +195,7 @@ std::system_error windows_error(int error_code, string_view message,
// Reports a Windows error without throwing an exception.
// Can be used to report errors from destructors.
FMT_API void report_windows_error(int error_code,
string_view message) FMT_NOEXCEPT;
const char* message) FMT_NOEXCEPT;
#endif // _WIN32

// std::system is not available on some platforms such as iOS (#2248).
Expand Down
5 changes: 2 additions & 3 deletions src/os.cc
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ std::system_error vwindows_error(int err_code, string_view format_str,
}

void detail::format_windows_error(detail::buffer<char>& out, int error_code,
string_view message) FMT_NOEXCEPT {
const char* message) FMT_NOEXCEPT {
FMT_TRY {
wmemory_buffer buf;
buf.resize(inline_buffer_size);
Expand Down Expand Up @@ -135,8 +135,7 @@ void detail::format_windows_error(detail::buffer<char>& out, int error_code,
format_error_code(out, error_code, message);
}

void report_windows_error(int error_code,
fmt::string_view message) FMT_NOEXCEPT {
void report_windows_error(int error_code, const char* message) FMT_NOEXCEPT {
report_error(detail::format_windows_error, error_code, message);
}
#endif // _WIN32
Expand Down
34 changes: 0 additions & 34 deletions test/format-impl-test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -304,40 +304,6 @@ TEST(fp_test, grisu_format_compiles_with_on_ieee_double) {
format_float(0.42, -1, fmt::detail::float_specs(), buf);
}

TEST(format_impl_test, strerror) {
char* message = nullptr;
char buffer[256];
EXPECT_ASSERT(fmt::detail::safe_strerror(EDOM, message = nullptr, 0),
"invalid buffer");
EXPECT_ASSERT(fmt::detail::safe_strerror(EDOM, message = buffer, 0),
"invalid buffer");
buffer[0] = 'x';
#if defined(_GNU_SOURCE) && !defined(__COVERITY__)
// Use invalid error code to make sure that safe_strerror returns an error
// message in the buffer rather than a pointer to a static string.
int error_code = -1;
#else
int error_code = EDOM;
#endif

int result = fmt::detail::safe_strerror(error_code, message = buffer, 256);
EXPECT_EQ(result, 0);
size_t message_size = std::strlen(message);
EXPECT_GE(255u, message_size);
EXPECT_EQ(get_system_error(error_code), message);

// safe_strerror never uses buffer on MinGW.
#if !defined(__MINGW32__) && !defined(__sun)
result =
fmt::detail::safe_strerror(error_code, message = buffer, message_size);
EXPECT_EQ(ERANGE, result);
result = fmt::detail::safe_strerror(error_code, message = buffer, 1);
EXPECT_EQ(buffer, message); // Message should point to buffer.
EXPECT_EQ(ERANGE, result);
EXPECT_STREQ("", message);
#endif
}

TEST(format_impl_test, format_error_code) {
std::string msg = "error 42", sep = ": ";
{
Expand Down
Loading

0 comments on commit 16f2ef9

Please sign in to comment.