From b23f48f297b0047dde3e3af5118d9887244eb0c4 Mon Sep 17 00:00:00 2001 From: Jiahao XU Date: Thu, 25 Feb 2021 19:15:36 +1100 Subject: [PATCH] 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 --- include/fmt/chrono.h | 31 +++++---- include/fmt/color.h | 8 +-- include/fmt/format-inl.h | 6 +- include/fmt/format.h | 138 +++++++++++++++++++++++++++++++-------- include/fmt/os.h | 21 +++++- include/fmt/printf.h | 12 ++-- src/os.cc | 36 +++++----- 7 files changed, 182 insertions(+), 70 deletions(-) diff --git a/include/fmt/chrono.h b/include/fmt/chrono.h index 645b06c3..da70cf8b 100644 --- a/include/fmt/chrono.h +++ b/include/fmt/chrono.h @@ -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(begin, static_cast(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(begin, static_cast(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(begin, static_cast(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(begin, static_cast(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(begin, static_cast(end - begin))); } break; default: - FMT_THROW(format_error("invalid format")); + FMT_ERROR(Error_type::format_error, "invalid format", + basic_string_view(begin, static_cast(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 FMT_CONSTEXPR void on_text(const Char*, const Char*) {} @@ -771,7 +780,7 @@ template To fmt_safe_duration_cast(std::chrono::duration from) { int ec; To to = safe_duration_cast::safe_duration_cast(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, 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 fill) { f.specs.fill = fill; } diff --git a/include/fmt/color.h b/include/fmt/color.h index 24ccedac..da73a79a 100644 --- a/include/fmt/color.h +++ b/include/fmt/color.h @@ -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; } diff --git a/include/fmt/format-inl.h b/include/fmt/format-inl.h index be1ddf40..650d0cf5 100644 --- a/include/fmt/format-inl.h +++ b/include/fmt/format-inl.h @@ -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 { 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(cp)); } else { @@ -2663,7 +2663,7 @@ FMT_FUNC void format_system_error(detail::buffer& 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, diff --git a/include/fmt/format.h b/include/fmt/format.h index 44c2a3b4..9fa04093 100644 --- a/include/fmt/format.h +++ b/include/fmt/format.h @@ -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 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 struct error_reporter_t; + +template struct error_reporter_t { + template + FMT_NORETURN void operator () (int line, const char *func, const char *file, const char *msg, + basic_string_view fmt = basic_string_view()); +}; + +template struct error_reporter_t { + template + FMT_NORETURN void operator () (int line, const char *func, const char *file, + int error_code, const char *msg, + const Args&... args); +}; + +template struct error_reporter_t { + 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 using is_signed = std::integral_constant::is_signed || @@ -1324,7 +1347,7 @@ template struct fill_t { FMT_CONSTEXPR void operator=(basic_string_view 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 struct int_writer { void on_chr() { out = write_char(out, static_cast(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 specs, int precision = specs.precision >= 0 || !specs.type ? specs.precision : 6; if (fspecs.format == float_format::exp) { if (precision == max_value()) - 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 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::length(value); out = write(out, basic_string_view(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::length(value); basic_string_view sv(value, length); @@ -3546,6 +3569,65 @@ FMT_API void format_system_error(detail::buffer& out, int error_code, FMT_API void report_system_error(int error_code, string_view message) FMT_NOEXCEPT; +/* Overload for format_error */ +template +template +FMT_NORETURN void error_reporter_t::operator () ( + int line, const char *func, const char *file, + const char *msg, basic_string_view fmt) { +#if FMT_EXCEPTIONS + static_cast(line); + static_cast(func); + static_cast(file); + static_cast(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 +template +FMT_NORETURN void error_reporter_t::operator () ( + int line, const char *func, const char *file, + int error_code, const char *msg, const Args&... args) { +#if FMT_EXCEPTIONS + static_cast(line); + static_cast(func); + static_cast(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 +FMT_NORETURN void error_reporter_t::operator () ( + int line, const char *func, const char *file, + const char *msg) { +#if FMT_EXCEPTIONS + static_cast(line); + static_cast(func); + static_cast(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 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 ::value)> diff --git a/include/fmt/os.h b/include/fmt/os.h index 73e421ad..25695f83 100644 --- a/include/fmt/os.h +++ b/include/fmt/os.h @@ -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 struct error_reporter_t { + template + 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(line); + static_cast(func); + static_cast(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_); } diff --git a/include/fmt/printf.h b/include/fmt/printf.h index b95b56cf..34f5cb9c 100644 --- a/include/fmt/printf.h +++ b/include/fmt/printf.h @@ -39,13 +39,13 @@ class printf_precision_handler { template ::value)> int operator()(T value) { if (!int_checker::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(value), 0); } template ::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 class printf_width_handler { width = 0 - width; } unsigned int_max = max_value(); - 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(width); } template ::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::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(start, static_cast(end - start))); specs.type = static_cast(*it++); if (arg.is_integral()) { // Normalize type. diff --git a/src/os.cc b/src/os.cc index 68500245..9b17a99b 100644 --- a/src/os.cc +++ b/src/os.cc @@ -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 }