Replace FMT_THROW with FMT_ERROR

FMT_ERROR uses fmt::error_reporter_t, which enables user
to customize error handler.
This is especially useful when the exception is disabled.

Signed-off-by: Jiahao XU <Jiahao_XU@outlook.com>
This commit is contained in:
Jiahao XU 2021-02-25 19:15:36 +11:00
parent 578874033a
commit b23f48f297
No known key found for this signature in database
GPG Key ID: 28165BFE1CC855B2
7 changed files with 182 additions and 70 deletions

View File

@ -326,7 +326,7 @@ inline std::tm localtime(std::time_t time) {
};
dispatcher lt(time);
// Too big time values may be unsupported.
if (!lt.run()) FMT_THROW(format_error("time_t value out of range"));
if (!lt.run()) FMT_ERROR(Error_type::format_error, "time_t value out of range");
return lt.tm_;
}
@ -371,7 +371,7 @@ inline std::tm gmtime(std::time_t time) {
};
dispatcher gt(time);
// Too big time values may be unsupported.
if (!gt.run()) FMT_THROW(format_error("time_t value out of range"));
if (!gt.run()) FMT_ERROR(Error_type::format_error, "time_t value out of range");
return gt.tm_;
}
@ -537,7 +537,9 @@ FMT_CONSTEXPR const Char* parse_chrono_format(const Char* begin,
}
if (begin != ptr) handler.on_text(begin, ptr);
++ptr; // consume '%'
if (ptr == end) FMT_THROW(format_error("invalid format"));
if (ptr == end)
FMT_ERROR(Error_type::format_error, "invalid format",
basic_string_view<Char>(begin, static_cast<std::size_t>(end - begin)));
c = *ptr++;
switch (c) {
case '%':
@ -628,7 +630,9 @@ FMT_CONSTEXPR const Char* parse_chrono_format(const Char* begin,
break;
// Alternative representation:
case 'E': {
if (ptr == end) FMT_THROW(format_error("invalid format"));
if (ptr == end)
FMT_ERROR(Error_type::format_error, "invalid format",
basic_string_view<Char>(begin, static_cast<std::size_t>(end - begin)));
c = *ptr++;
switch (c) {
case 'c':
@ -641,12 +645,15 @@ FMT_CONSTEXPR const Char* parse_chrono_format(const Char* begin,
handler.on_loc_time(numeric_system::alternative);
break;
default:
FMT_THROW(format_error("invalid format"));
FMT_ERROR(Error_type::format_error, "invalid format",
basic_string_view<Char>(begin, static_cast<std::size_t>(end - begin)));
}
break;
}
case 'O':
if (ptr == end) FMT_THROW(format_error("invalid format"));
if (ptr == end)
FMT_ERROR(Error_type::format_error, "invalid format",
basic_string_view<Char>(begin, static_cast<std::size_t>(end - begin)));
c = *ptr++;
switch (c) {
case 'w':
@ -668,11 +675,13 @@ FMT_CONSTEXPR const Char* parse_chrono_format(const Char* begin,
handler.on_second(numeric_system::alternative);
break;
default:
FMT_THROW(format_error("invalid format"));
FMT_ERROR(Error_type::format_error, "invalid format",
basic_string_view<Char>(begin, static_cast<std::size_t>(end - begin)));
}
break;
default:
FMT_THROW(format_error("invalid format"));
FMT_ERROR(Error_type::format_error, "invalid format",
basic_string_view<Char>(begin, static_cast<std::size_t>(end - begin)));
}
begin = ptr;
}
@ -681,7 +690,7 @@ FMT_CONSTEXPR const Char* parse_chrono_format(const Char* begin,
}
struct chrono_format_checker {
FMT_NORETURN void report_no_date() { FMT_THROW(format_error("no date")); }
FMT_NORETURN void report_no_date() { FMT_ERROR(Error_type::format_error, "no date"); }
template <typename Char>
FMT_CONSTEXPR void on_text(const Char*, const Char*) {}
@ -771,7 +780,7 @@ template <typename To, typename FromRep, typename FromPeriod>
To fmt_safe_duration_cast(std::chrono::duration<FromRep, FromPeriod> from) {
int ec;
To to = safe_duration_cast::safe_duration_cast<To>(from, ec);
if (ec) FMT_THROW(format_error("cannot format duration"));
if (ec) FMT_ERROR(Error_type::format_error, "cannot format duration");
return to;
}
#endif
@ -1104,7 +1113,7 @@ struct formatter<std::chrono::duration<Rep, Period>, Char> {
return arg_ref_type(context.next_arg_id());
}
void on_error(const char* msg) { FMT_THROW(format_error(msg)); }
void on_error(const char* msg) { FMT_ERROR(Error_type::format_error, msg); }
FMT_CONSTEXPR void on_fill(basic_string_view<Char> fill) {
f.specs.fill = fill;
}

View File

@ -244,7 +244,7 @@ class text_style {
foreground_color = rhs.foreground_color;
} else if (rhs.set_foreground_color) {
if (!foreground_color.is_rgb || !rhs.foreground_color.is_rgb)
FMT_THROW(format_error("can't OR a terminal color"));
FMT_ERROR(Error_type::format_error, "can't OR a terminal color");
foreground_color.value.rgb_color |= rhs.foreground_color.value.rgb_color;
}
@ -253,7 +253,7 @@ class text_style {
background_color = rhs.background_color;
} else if (rhs.set_background_color) {
if (!background_color.is_rgb || !rhs.background_color.is_rgb)
FMT_THROW(format_error("can't OR a terminal color"));
FMT_ERROR(Error_type::format_error, "can't OR a terminal color");
background_color.value.rgb_color |= rhs.background_color.value.rgb_color;
}
@ -321,7 +321,7 @@ class text_style {
foreground_color = rhs.foreground_color;
} else if (rhs.set_foreground_color) {
if (!foreground_color.is_rgb || !rhs.foreground_color.is_rgb)
FMT_THROW(format_error("can't AND a terminal color"));
FMT_ERROR(Error_type::format_error, "can't AND a terminal color");
foreground_color.value.rgb_color &= rhs.foreground_color.value.rgb_color;
}
@ -330,7 +330,7 @@ class text_style {
background_color = rhs.background_color;
} else if (rhs.set_background_color) {
if (!background_color.is_rgb || !rhs.background_color.is_rgb)
FMT_THROW(format_error("can't AND a terminal color"));
FMT_ERROR(Error_type::format_error, "can't AND a terminal color");
background_color.value.rgb_color &= rhs.background_color.value.rgb_color;
}

View File

@ -164,7 +164,7 @@ FMT_FUNC void report_error(format_func func, int error_code,
inline void fwrite_fully(const void* ptr, size_t size, size_t count,
FILE* stream) {
size_t written = std::fwrite(ptr, size, count, stream);
if (written < count) FMT_THROW(system_error(errno, "cannot write to file"));
if (written < count) FMT_ERROR(Error_type::system_error, errno, "cannot write to file");
}
#ifndef FMT_STATIC_THOUSANDS_SEPARATOR
@ -2627,7 +2627,7 @@ template <> struct formatter<detail::bigint> {
FMT_FUNC detail::utf8_to_utf16::utf8_to_utf16(string_view s) {
for_each_codepoint(s, [this](uint32_t cp, int error) {
if (error != 0) FMT_THROW(std::runtime_error("invalid utf8"));
if (error != 0) FMT_ERROR(Error_type::runtime_error, "invalid utf8");
if (cp <= 0xFFFF) {
buffer_.push_back(static_cast<wchar_t>(cp));
} else {
@ -2663,7 +2663,7 @@ FMT_FUNC void format_system_error(detail::buffer<char>& out, int error_code,
}
FMT_FUNC void detail::error_handler::on_error(const char* message) {
FMT_THROW(format_error(message));
FMT_ERROR(Error_type::format_error, message);
}
FMT_FUNC void report_system_error(int error_code,

View File

@ -103,29 +103,53 @@
# endif
#endif
#ifndef FMT_THROW
# if FMT_EXCEPTIONS
# if FMT_MSC_VER || FMT_NVCC
#ifndef FMT_ERROR
FMT_BEGIN_NAMESPACE
namespace detail {
template <typename Exception> inline void do_throw(const Exception& x) {
// Silence unreachable code warnings in MSVC and NVCC because these
// are nearly impossible to fix in a generic code.
volatile bool b = true;
if (b) throw x;
}
} // namespace detail
enum class Error_type {
format_error,
system_error,
runtime_error,
# ifdef _WIN32
windows_error,
# endif
};
/**
* Use error_reporter_t as a customization point.
*/
template <Error_type error_type, typename = void> struct error_reporter_t;
template <typename void_type> struct error_reporter_t<Error_type::format_error, void_type> {
template <typename Char = char>
FMT_NORETURN void operator () (int line, const char *func, const char *file, const char *msg,
basic_string_view<Char> fmt = basic_string_view<Char>());
};
template <typename void_type> struct error_reporter_t<Error_type::system_error, void_type> {
template <typename ...Args>
FMT_NORETURN void operator () (int line, const char *func, const char *file,
int error_code, const char *msg,
const Args&... args);
};
template <typename void_type> struct error_reporter_t<Error_type::runtime_error, void_type> {
FMT_NORETURN void operator () (int line, const char *func, const char *file, const char *msg);
};
# ifdef __GNUC__
# define FMT_ERROR(error_type, ...) \
do { \
::fmt::error_reporter_t<(error_type)> error_reporter; \
error_reporter(__LINE__, __PRETTY_FUNCTION__, __FILE__, __VA_ARGS__); \
} while (0)
# else
# define FMT_ERROR(error_type, ...) \
do { \
::fmt::error_reporter_t<(error_type)> error_reporter; \
error_reporter(__LINE__, __FUNCTION__, __FILE__, __VA_ARGS__); \
} while (0)
# endif
FMT_END_NAMESPACE
# define FMT_THROW(x) detail::do_throw(x)
# else
# define FMT_THROW(x) throw x
# endif
# else
# define FMT_THROW(x) \
do { \
FMT_ASSERT(false, (x).what()); \
} while (false)
# endif
#endif
#if FMT_EXCEPTIONS
@ -940,7 +964,6 @@ class FMT_API format_error : public std::runtime_error {
};
namespace detail {
template <typename T>
using is_signed =
std::integral_constant<bool, std::numeric_limits<T>::is_signed ||
@ -1324,7 +1347,7 @@ template <typename Char> struct fill_t {
FMT_CONSTEXPR void operator=(basic_string_view<Char> s) {
auto size = s.size();
if (size > max_size) {
FMT_THROW(format_error("invalid fill"));
FMT_ERROR(Error_type::format_error, "invalid fill");
return;
}
for (size_t i = 0; i < size; ++i) data_[i] = s[i];
@ -1928,7 +1951,7 @@ template <typename OutputIt, typename Char, typename UInt> struct int_writer {
void on_chr() { out = write_char(out, static_cast<Char>(abs_value), specs); }
FMT_NORETURN void on_error() {
FMT_THROW(format_error("invalid type specifier"));
FMT_ERROR(Error_type::format_error, "invalid type specifier");
}
};
@ -2146,7 +2169,7 @@ OutputIt write(OutputIt out, T value, basic_format_specs<Char> specs,
int precision = specs.precision >= 0 || !specs.type ? specs.precision : 6;
if (fspecs.format == float_format::exp) {
if (precision == max_value<int>())
FMT_THROW(format_error("number is too big"));
FMT_ERROR(Error_type::format_error, "number is too big");
else
++precision;
}
@ -2282,7 +2305,7 @@ FMT_CONSTEXPR OutputIt write(OutputIt out, Char value) {
template <typename Char, typename OutputIt>
FMT_CONSTEXPR OutputIt write(OutputIt out, const Char* value) {
if (!value) {
FMT_THROW(format_error("string pointer is null"));
FMT_ERROR(Error_type::format_error, "string pointer is null");
} else {
auto length = std::char_traits<Char>::length(value);
out = write(out, basic_string_view<Char>(value, length));
@ -2442,7 +2465,7 @@ class arg_formatter_base {
void write(const Char* value) {
if (!value) {
FMT_THROW(format_error("string pointer is null"));
FMT_ERROR(Error_type::format_error, "string pointer is null");
} else {
auto length = std::char_traits<char_type>::length(value);
basic_string_view<char_type> sv(value, length);
@ -3546,6 +3569,65 @@ FMT_API void format_system_error(detail::buffer<char>& out, int error_code,
FMT_API void report_system_error(int error_code,
string_view message) FMT_NOEXCEPT;
/* Overload for format_error */
template <typename void_type>
template <typename Char>
FMT_NORETURN void error_reporter_t<Error_type::format_error, void_type>::operator () (
int line, const char *func, const char *file,
const char *msg, basic_string_view<Char> fmt) {
#if FMT_EXCEPTIONS
static_cast<void>(line);
static_cast<void>(func);
static_cast<void>(file);
static_cast<void>(fmt);
throw format_error(msg);
#else
if (fmt.size())
std::fprintf(stderr, "%d of %s, %s failed to format %s: %s\n",
line, func, file, fmt.data(), msg);
else
std::fprintf(stderr, "%d of %s, %s failed: %s\n",
line, func, file, msg);
std::terminate();
#endif
}
/* Overload for system_error */
template <typename void_type>
template <typename ...Args>
FMT_NORETURN void error_reporter_t<Error_type::system_error, void_type>::operator () (
int line, const char *func, const char *file,
int error_code, const char *msg, const Args&... args) {
#if FMT_EXCEPTIONS
static_cast<void>(line);
static_cast<void>(func);
static_cast<void>(file);
throw system_error(error_code, msg, args...);
#else
report_system_error(error_code, format(msg, args...));
std::terminate();
#endif
}
/* Overload for runtime_error */
template <typename void_type>
FMT_NORETURN void error_reporter_t<Error_type::runtime_error, void_type>::operator () (
int line, const char *func, const char *file,
const char *msg) {
#if FMT_EXCEPTIONS
static_cast<void>(line);
static_cast<void>(func);
static_cast<void>(file);
throw std::runtime_error(msg);
#else
std::fprintf(stderr, "Runtime error: %s\n", msg);
std::terminate();
#endif
}
/** Fast integer formatter. */
class format_int {
private:
@ -4123,7 +4205,7 @@ void vprint(std::FILE* f, basic_string_view<Char> format_str,
detail::vformat_to(buffer, format_str, args);
buffer.push_back(L'\0');
if (std::fputws(buffer.data(), f) == -1)
FMT_THROW(system_error(errno, "cannot write to file"));
FMT_ERROR(Error_type::system_error, errno, "cannot write to file");
}
template <typename Char, FMT_ENABLE_IF(std::is_same<Char, wchar_t>::value)>

View File

@ -198,6 +198,25 @@ class windows_error : public system_error {
// Can be used to report errors from destructors.
FMT_API void report_windows_error(int error_code,
string_view message) FMT_NOEXCEPT;
template <typename void_type> struct error_reporter_t<Error_type::windows_error, void_type> {
template <typename... Args>
FMT_NORETURN void operator () (int line, const char *func, const char *file,
int error_code, const char *message,
const Args&... args) {
#if FMT_EXCEPTIONS
static_cast<void>(line);
static_cast<void>(func);
static_cast<void>(file);
throw windows_error(error_code, message, args...);
#else
report_windows_error(error_code, format(message, args...));
std::terminate();
#endif
}
};
#endif // _WIN32
// A buffered file.
@ -469,7 +488,7 @@ class locale {
# else
locale_ = _create_locale(LC_NUMERIC, "C");
# endif
if (!locale_) FMT_THROW(system_error(errno, "cannot create locale"));
if (!locale_) FMT_ERROR(Error_type::system_error, errno, "cannot create locale");
}
~locale() { freelocale(locale_); }

View File

@ -39,13 +39,13 @@ class printf_precision_handler {
template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)>
int operator()(T value) {
if (!int_checker<std::numeric_limits<T>::is_signed>::fits_in_int(value))
FMT_THROW(format_error("number is too big"));
FMT_ERROR(Error_type::format_error, "number is too big");
return (std::max)(static_cast<int>(value), 0);
}
template <typename T, FMT_ENABLE_IF(!std::is_integral<T>::value)>
int operator()(T) {
FMT_THROW(format_error("precision is not integer"));
FMT_ERROR(Error_type::format_error, "precision is not integer");
return 0;
}
};
@ -167,13 +167,13 @@ template <typename Char> class printf_width_handler {
width = 0 - width;
}
unsigned int_max = max_value<int>();
if (width > int_max) FMT_THROW(format_error("number is too big"));
if (width > int_max) FMT_ERROR(Error_type::format_error, "number is too big");
return static_cast<unsigned>(width);
}
template <typename T, FMT_ENABLE_IF(!std::is_integral<T>::value)>
unsigned operator()(T) {
FMT_THROW(format_error("width is not integer"));
FMT_ERROR(Error_type::format_error, "width is not integer");
return 0;
}
};
@ -577,7 +577,9 @@ OutputIt basic_printf_context<OutputIt, Char>::format() {
}
// Parse type.
if (it == end) FMT_THROW(format_error("invalid format string"));
if (it == end)
FMT_ERROR(Error_type::format_error, "invalid format string",
basic_string_view<Char>(start, static_cast<std::size_t>(end - start)));
specs.type = static_cast<char>(*it++);
if (arg.is_integral()) {
// Normalize type.

View File

@ -75,8 +75,8 @@ FMT_BEGIN_NAMESPACE
#ifdef _WIN32
detail::utf16_to_utf8::utf16_to_utf8(wstring_view s) {
if (int error_code = convert(s)) {
FMT_THROW(windows_error(error_code,
"cannot convert string from UTF-16 to UTF-8"));
FMT_ERROR(Error_type::windows_error, error_code,
"cannot convert string from UTF-16 to UTF-8");
}
}
@ -154,14 +154,14 @@ buffered_file::buffered_file(cstring_view filename, cstring_view mode) {
FMT_RETRY_VAL(file_, FMT_SYSTEM(fopen(filename.c_str(), mode.c_str())),
nullptr);
if (!file_)
FMT_THROW(system_error(errno, "cannot open file {}", filename.c_str()));
FMT_ERROR(Error_type::system_error, errno, "cannot open file {}", filename.c_str());
}
void buffered_file::close() {
if (!file_) return;
int result = FMT_SYSTEM(fclose(file_));
file_ = nullptr;
if (result != 0) FMT_THROW(system_error(errno, "cannot close file"));
if (result != 0) FMT_ERROR(Error_type::system_error, errno, "cannot close file");
}
// A macro used to prevent expansion of fileno on broken versions of MinGW.
@ -169,7 +169,7 @@ void buffered_file::close() {
int buffered_file::fileno() const {
int fd = FMT_POSIX_CALL(fileno FMT_ARGS(file_));
if (fd == -1) FMT_THROW(system_error(errno, "cannot get file descriptor"));
if (fd == -1) FMT_ERROR(Error_type::system_error, errno, "cannot get file descriptor");
return fd;
}
@ -183,7 +183,7 @@ file::file(cstring_view path, int oflag) {
FMT_RETRY(fd_, FMT_POSIX_CALL(open(path.c_str(), oflag, mode)));
# endif
if (fd_ == -1)
FMT_THROW(system_error(errno, "cannot open file {}", path.c_str()));
FMT_ERROR(Error_type::system_error, errno, "cannot open file {}", path.c_str());
}
file::~file() FMT_NOEXCEPT {
@ -199,7 +199,7 @@ void file::close() {
// See http://linux.derkeiler.com/Mailing-Lists/Kernel/2005-09/3000.html
int result = FMT_POSIX_CALL(close(fd_));
fd_ = -1;
if (result != 0) FMT_THROW(system_error(errno, "cannot close file"));
if (result != 0) FMT_ERROR(Error_type::system_error, errno, "cannot close file");
}
long long file::size() const {
@ -213,7 +213,7 @@ long long file::size() const {
if (size_lower == INVALID_FILE_SIZE) {
DWORD error = GetLastError();
if (error != NO_ERROR)
FMT_THROW(windows_error(GetLastError(), "cannot get file size"));
FMT_ERROR(Error_type::windows_error, GetLastError(), "cannot get file size");
}
unsigned long long long_size = size_upper;
return (long_size << sizeof(DWORD) * CHAR_BIT) | size_lower;
@ -221,7 +221,7 @@ long long file::size() const {
using Stat = struct stat;
Stat file_stat = Stat();
if (FMT_POSIX_CALL(fstat(fd_, &file_stat)) == -1)
FMT_THROW(system_error(errno, "cannot get file attributes"));
FMT_ERROR(Error_type::system_error, errno, "cannot get file attributes");
static_assert(sizeof(long long) >= sizeof(file_stat.st_size),
"return type of file::size is not large enough");
return file_stat.st_size;
@ -231,14 +231,14 @@ long long file::size() const {
std::size_t file::read(void* buffer, std::size_t count) {
RWResult result = 0;
FMT_RETRY(result, FMT_POSIX_CALL(read(fd_, buffer, convert_rwcount(count))));
if (result < 0) FMT_THROW(system_error(errno, "cannot read from file"));
if (result < 0) FMT_ERROR(Error_type::system_error, errno, "cannot read from file");
return detail::to_unsigned(result);
}
std::size_t file::write(const void* buffer, std::size_t count) {
RWResult result = 0;
FMT_RETRY(result, FMT_POSIX_CALL(write(fd_, buffer, convert_rwcount(count))));
if (result < 0) FMT_THROW(system_error(errno, "cannot write to file"));
if (result < 0) FMT_ERROR(Error_type::system_error, errno, "cannot write to file");
return detail::to_unsigned(result);
}
@ -247,7 +247,7 @@ file file::dup(int fd) {
// http://pubs.opengroup.org/onlinepubs/009695399/functions/dup.html
int new_fd = FMT_POSIX_CALL(dup(fd));
if (new_fd == -1)
FMT_THROW(system_error(errno, "cannot duplicate file descriptor {}", fd));
FMT_ERROR(Error_type::system_error, errno, "cannot duplicate file descriptor {}", fd);
return file(new_fd);
}
@ -255,8 +255,8 @@ void file::dup2(int fd) {
int result = 0;
FMT_RETRY(result, FMT_POSIX_CALL(dup2(fd_, fd)));
if (result == -1) {
FMT_THROW(system_error(errno, "cannot duplicate file descriptor {} to {}",
fd_, fd));
FMT_ERROR(Error_type::system_error, errno, "cannot duplicate file descriptor {} to {}",
fd_, fd);
}
}
@ -281,7 +281,7 @@ void file::pipe(file& read_end, file& write_end) {
// http://pubs.opengroup.org/onlinepubs/009696799/functions/pipe.html
int result = FMT_POSIX_CALL(pipe(fds));
# endif
if (result != 0) FMT_THROW(system_error(errno, "cannot create pipe"));
if (result != 0) FMT_ERROR(Error_type::system_error, errno, "cannot create pipe");
// The following assignments don't throw because read_fd and write_fd
// are closed.
read_end = file(fds[0]);
@ -296,8 +296,8 @@ buffered_file file::fdopen(const char* mode) {
FILE* f = FMT_POSIX_CALL(fdopen(fd_, mode));
# endif
if (!f)
FMT_THROW(
system_error(errno, "cannot associate stream with file descriptor"));
FMT_ERROR(
Error_type::system_error, errno, "cannot associate stream with file descriptor");
buffered_file bf(f);
fd_ = -1;
return bf;
@ -310,7 +310,7 @@ long getpagesize() {
return si.dwPageSize;
# else
long size = FMT_POSIX_CALL(sysconf(_SC_PAGESIZE));
if (size < 0) FMT_THROW(system_error(errno, "cannot get memory page size"));
if (size < 0) FMT_ERROR(Error_type::system_error, errno, "cannot get memory page size");
return size;
# endif
}