mirror of
https://github.com/fmtlib/fmt.git
synced 2024-11-24 03:00:16 +00:00
Don't throw exceptions from error formatting functions. Gracefully fallback to a less descriptive message instead.
This commit is contained in:
parent
82d4d11c11
commit
22f75d8b6d
88
format.cc
88
format.cc
@ -136,13 +136,33 @@ struct IntChecker<true> {
|
|||||||
|
|
||||||
const char RESET_COLOR[] = "\x1b[0m";
|
const char RESET_COLOR[] = "\x1b[0m";
|
||||||
|
|
||||||
typedef void (*FormatFunc)(fmt::Writer &, int , fmt::StringRef);
|
typedef void (*FormatFunc)(fmt::Writer &, int, fmt::StringRef);
|
||||||
|
|
||||||
|
// TODO: test
|
||||||
|
void format_error_code(fmt::Writer &out, int error_code,
|
||||||
|
fmt::StringRef message) FMT_NOEXCEPT(true) {
|
||||||
|
// Report error code making sure that the output fits into
|
||||||
|
// INLINE_BUFFER_SIZE to avoid dynamic memory allocation and potential
|
||||||
|
// bad_alloc.
|
||||||
|
out.clear();
|
||||||
|
static const char SEP[] = ": ";
|
||||||
|
static const char ERROR[] = "error ";
|
||||||
|
// SEP and ERROR add two terminating null characters, so subtract 1 as
|
||||||
|
// we need only one.
|
||||||
|
fmt::internal::IntTraits<int>::MainType ec_value = error_code;
|
||||||
|
std::size_t error_code_size =
|
||||||
|
sizeof(SEP) + sizeof(ERROR) + fmt::internal::count_digits(ec_value) - 1;
|
||||||
|
if (message.size() < fmt::internal::INLINE_BUFFER_SIZE - error_code_size)
|
||||||
|
out << message << SEP;
|
||||||
|
out << ERROR << error_code;
|
||||||
|
assert(out.size() <= fmt::internal::INLINE_BUFFER_SIZE);
|
||||||
|
}
|
||||||
|
|
||||||
void report_error(FormatFunc func,
|
void report_error(FormatFunc func,
|
||||||
int error_code, fmt::StringRef message) FMT_NOEXCEPT(true) {
|
int error_code, fmt::StringRef message) FMT_NOEXCEPT(true) {
|
||||||
try {
|
try {
|
||||||
fmt::Writer full_message;
|
fmt::Writer full_message;
|
||||||
func(full_message, error_code, message); // TODO: make sure this doesn't throw
|
func(full_message, error_code, message);
|
||||||
std::fwrite(full_message.c_str(), full_message.size(), 1, stderr);
|
std::fwrite(full_message.c_str(), full_message.size(), 1, stderr);
|
||||||
std::fputc('\n', stderr);
|
std::fputc('\n', stderr);
|
||||||
} catch (...) {}
|
} catch (...) {}
|
||||||
@ -453,28 +473,31 @@ int fmt::internal::safe_strerror(
|
|||||||
}
|
}
|
||||||
|
|
||||||
void fmt::internal::format_system_error(
|
void fmt::internal::format_system_error(
|
||||||
fmt::Writer &out, int error_code, fmt::StringRef message) {
|
fmt::Writer &out, int error_code,
|
||||||
Array<char, INLINE_BUFFER_SIZE> buffer;
|
fmt::StringRef message) FMT_NOEXCEPT(true) {
|
||||||
buffer.resize(INLINE_BUFFER_SIZE);
|
try {
|
||||||
char *system_message = 0;
|
Array<char, INLINE_BUFFER_SIZE> buffer;
|
||||||
for (;;) {
|
buffer.resize(INLINE_BUFFER_SIZE);
|
||||||
system_message = &buffer[0];
|
char *system_message = 0;
|
||||||
int result = safe_strerror(error_code, system_message, buffer.size());
|
for (;;) {
|
||||||
if (result == 0)
|
system_message = &buffer[0];
|
||||||
break;
|
int result = safe_strerror(error_code, system_message, buffer.size());
|
||||||
if (result != ERANGE) {
|
if (result == 0) {
|
||||||
// Can't get error message, report error code instead.
|
out << message << ": " << system_message;
|
||||||
out << message << ": error code = " << error_code;
|
return;
|
||||||
return;
|
}
|
||||||
|
if (result != ERANGE)
|
||||||
|
break; // Can't get error message, report error code instead.
|
||||||
|
buffer.resize(buffer.size() * 2);
|
||||||
}
|
}
|
||||||
buffer.resize(buffer.size() * 2);
|
} catch (...) {}
|
||||||
}
|
format_error_code(out, error_code, message);
|
||||||
out << message << ": " << system_message;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
void fmt::internal::format_windows_error(
|
void fmt::internal::format_windows_error(
|
||||||
fmt::Writer &out, int error_code, fmt::StringRef message) {
|
fmt::Writer &out, int error_code,
|
||||||
|
fmt::StringRef message) FMT_NOEXCEPT(true) {
|
||||||
class String {
|
class String {
|
||||||
private:
|
private:
|
||||||
LPWSTR str_;
|
LPWSTR str_;
|
||||||
@ -485,19 +508,20 @@ void fmt::internal::format_windows_error(
|
|||||||
LPWSTR *ptr() { return &str_; }
|
LPWSTR *ptr() { return &str_; }
|
||||||
LPCWSTR c_str() const { return str_; }
|
LPCWSTR c_str() const { return str_; }
|
||||||
};
|
};
|
||||||
String system_message;
|
try {
|
||||||
if (FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER |
|
String system_message;
|
||||||
FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, 0,
|
if (FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER |
|
||||||
error_code, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
|
FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, 0,
|
||||||
reinterpret_cast<LPWSTR>(system_message.ptr()), 0, 0)) {
|
error_code, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
|
||||||
UTF16ToUTF8 utf8_message;
|
reinterpret_cast<LPWSTR>(system_message.ptr()), 0, 0)) {
|
||||||
if (!utf8_message.convert(system_message.c_str())) {
|
UTF16ToUTF8 utf8_message;
|
||||||
out << message << ": " << utf8_message;
|
if (utf8_message.convert(system_message.c_str()) == ERROR_SUCCESS) {
|
||||||
return;
|
out << message << ": " << utf8_message;
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
} catch (...) {}
|
||||||
// Can't get error message, report error code instead.
|
format_error_code(out, error_code, message);
|
||||||
out << message << ": error code = " << error_code;
|
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@ -1190,14 +1214,12 @@ void fmt::BasicFormatter<Char>::format(
|
|||||||
|
|
||||||
void fmt::report_system_error(
|
void fmt::report_system_error(
|
||||||
int error_code, fmt::StringRef message) FMT_NOEXCEPT(true) {
|
int error_code, fmt::StringRef message) FMT_NOEXCEPT(true) {
|
||||||
// FIXME: format_system_error may throw
|
|
||||||
report_error(internal::format_system_error, error_code, message);
|
report_error(internal::format_system_error, error_code, message);
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
void fmt::report_windows_error(
|
void fmt::report_windows_error(
|
||||||
int error_code, fmt::StringRef message) FMT_NOEXCEPT(true) {
|
int error_code, fmt::StringRef message) FMT_NOEXCEPT(true) {
|
||||||
// FIXME: format_windows_error may throw
|
|
||||||
report_error(internal::format_windows_error, error_code, message);
|
report_error(internal::format_windows_error, error_code, message);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
15
format.h
15
format.h
@ -307,7 +307,7 @@ class Array {
|
|||||||
grow(capacity);
|
grow(capacity);
|
||||||
}
|
}
|
||||||
|
|
||||||
void clear() { size_ = 0; }
|
void clear() FMT_NOEXCEPT(true) { size_ = 0; }
|
||||||
|
|
||||||
void push_back(const T &value) {
|
void push_back(const T &value) {
|
||||||
if (size_ == capacity_)
|
if (size_ == capacity_)
|
||||||
@ -528,7 +528,8 @@ class UTF16ToUTF8 {
|
|||||||
std::string str() const { return std::string(&buffer_[0], size()); }
|
std::string str() const { return std::string(&buffer_[0], size()); }
|
||||||
|
|
||||||
// Performs conversion returning a system error code instead of
|
// Performs conversion returning a system error code instead of
|
||||||
// throwing exception on error.
|
// throwing exception on conversion error. This method may still throw
|
||||||
|
// in case of memory allocation error.
|
||||||
int convert(WStringRef s);
|
int convert(WStringRef s);
|
||||||
};
|
};
|
||||||
#endif
|
#endif
|
||||||
@ -545,12 +546,12 @@ class UTF16ToUTF8 {
|
|||||||
int safe_strerror(int error_code,
|
int safe_strerror(int error_code,
|
||||||
char *&buffer, std::size_t buffer_size) FMT_NOEXCEPT(true);
|
char *&buffer, std::size_t buffer_size) FMT_NOEXCEPT(true);
|
||||||
|
|
||||||
void format_system_error(
|
void format_system_error(fmt::Writer &out, int error_code,
|
||||||
fmt::Writer &out, int error_code, fmt::StringRef message);
|
fmt::StringRef message) FMT_NOEXCEPT(true);
|
||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
void format_windows_error(
|
void format_windows_error(fmt::Writer &out, int error_code,
|
||||||
fmt::Writer &out, int error_code, fmt::StringRef message);
|
fmt::StringRef message) FMT_NOEXCEPT(true);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Computes max(Arg, 1) at compile time. It is used to avoid errors about
|
// Computes max(Arg, 1) at compile time. It is used to avoid errors about
|
||||||
@ -1518,7 +1519,7 @@ class BasicWriter {
|
|||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
void clear() { buffer_.clear(); }
|
void clear() FMT_NOEXCEPT(true) { buffer_.clear(); }
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename Char>
|
template <typename Char>
|
||||||
|
@ -471,8 +471,11 @@ void check_throw_error(int error_code, FormatErrorMessage format) {
|
|||||||
TEST(UtilTest, FormatSystemError) {
|
TEST(UtilTest, FormatSystemError) {
|
||||||
fmt::Writer message;
|
fmt::Writer message;
|
||||||
fmt::internal::format_system_error(message, EDOM, "test");
|
fmt::internal::format_system_error(message, EDOM, "test");
|
||||||
EXPECT_EQ(fmt::format("test: {}",
|
EXPECT_EQ(fmt::format("test: {}", get_system_error(EDOM)), message.str());
|
||||||
get_system_error(EDOM)), message.str());
|
message.clear();
|
||||||
|
fmt::internal::format_system_error(
|
||||||
|
message, EDOM, fmt::StringRef("x", std::numeric_limits<size_t>::max()));
|
||||||
|
EXPECT_EQ(fmt::format("error {}", EDOM), message.str());
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(UtilTest, SystemError) {
|
TEST(UtilTest, SystemError) {
|
||||||
@ -504,6 +507,11 @@ TEST(UtilTest, FormatWindowsError) {
|
|||||||
actual_message, ERROR_FILE_EXISTS, "test");
|
actual_message, ERROR_FILE_EXISTS, "test");
|
||||||
EXPECT_EQ(fmt::format("test: {}", utf8_message.str()),
|
EXPECT_EQ(fmt::format("test: {}", utf8_message.str()),
|
||||||
actual_message.str());
|
actual_message.str());
|
||||||
|
actual_message.clear();
|
||||||
|
fmt::internal::format_windows_error(
|
||||||
|
actual_message, ERROR_FILE_EXISTS,
|
||||||
|
fmt::StringRef("x", std::numeric_limits<size_t>::max()));
|
||||||
|
EXPECT_EQ(fmt::format("error {}", ERROR_FILE_EXISTS), message.str());
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(UtilTest, WindowsError) {
|
TEST(UtilTest, WindowsError) {
|
||||||
|
Loading…
Reference in New Issue
Block a user