diff --git a/doc/index.rst b/doc/index.rst index 51d331e0..c80be732 100644 --- a/doc/index.rst +++ b/doc/index.rst @@ -63,9 +63,8 @@ System Errors .. doxygenclass:: fmt::SystemError :members: -.. doxygenfunction:: fmt::ThrowSystemError - -.. doxygenfunction:: fmt::ThrowWinError +.. doxygenclass:: fmt::WindowsError + :members: Format String Syntax -------------------- diff --git a/format.cc b/format.cc index e92bb32b..6a3e2aa2 100644 --- a/format.cc +++ b/format.cc @@ -138,6 +138,15 @@ fmt::ULongLong GetIntValue(const fmt::internal::ArgInfo &arg) { int fmt::internal::SignBitNoInline(double value) { return SignBit(value); } +void fmt::SystemError::init( + int error_code, StringRef format_str, const ArgList &args) { + error_code_ = error_code; + Writer w; + internal::FormatSystemErrorMessage(w, error_code, format(format_str, args)); + std::runtime_error &base = *this; + base = std::runtime_error(w.str()); +} + template int fmt::internal::CharTraits::FormatFloat( char *buffer, std::size_t size, const char *format, @@ -238,6 +247,15 @@ int fmt::internal::UTF16ToUTF8::Convert(fmt::WStringRef s) { return 0; } +void fmt::WindowsError::init( + int error_code, StringRef format_str, const ArgList &args) { + error_code_ = error_code; + Writer w; + internal::FormatWinErrorMessage(w, error_code, format(format_str, args)); + std::runtime_error &base = *this; + base = std::runtime_error(w.str()); +} + #endif int fmt::internal::StrError( @@ -411,7 +429,7 @@ void fmt::BasicWriter::FormatDouble(T value, const FormatSpec &spec) { --size; ++nan; } - CharPtr out = FormatString(nan, size, spec); + CharPtr out = write_str(nan, size, spec); if (sign) *out = sign; return; @@ -426,7 +444,7 @@ void fmt::BasicWriter::FormatDouble(T value, const FormatSpec &spec) { --size; ++inf; } - CharPtr out = FormatString(inf, size, spec); + CharPtr out = write_str(inf, size, spec); if (sign) *out = sign; return; @@ -518,7 +536,7 @@ void fmt::BasicWriter::FormatDouble(T value, const FormatSpec &spec) { template template -void fmt::BasicWriter::FormatString( +void fmt::BasicWriter::write_str( const Arg::StringValue &str, const FormatSpec &spec) { if (spec.type_ && spec.type_ != 's') internal::ReportUnknownType(spec.type_, "string"); @@ -530,7 +548,7 @@ void fmt::BasicWriter::FormatString( if (*s) size = std::char_traits::length(s); } - FormatString(s, size, spec); + write_str(s, size, spec); } template @@ -819,10 +837,10 @@ void fmt::internal::PrintfParser::Format( break; } case Arg::STRING: - writer.FormatString(arg.string, spec); + writer.write_str(arg.string, spec); break; case Arg::WSTRING: - writer.FormatString(arg.wstring, spec); + writer.write_str(arg.wstring, spec); break; case Arg::POINTER: if (spec.type_ && spec.type_ != 'p') @@ -1043,10 +1061,10 @@ void fmt::BasicWriter::FormatParser::Format( break; } case Arg::STRING: - writer.FormatString(arg.string, spec); + writer.write_str(arg.string, spec); break; case Arg::WSTRING: - writer.FormatString(arg.wstring, spec); + writer.write_str(arg.wstring, spec); break; case Arg::POINTER: if (spec.type_ && spec.type_ != 'p') @@ -1068,12 +1086,6 @@ void fmt::BasicWriter::FormatParser::Format( writer.buffer_.append(start, s); } -void fmt::SystemErrorSink::operator()(const fmt::Writer &w) const { - Writer message; - internal::FormatSystemErrorMessage(message, error_code_, w.c_str()); - throw SystemError(message.c_str(), error_code_); -} - void fmt::ReportSystemError( int error_code, fmt::StringRef message) FMT_NOEXCEPT(true) { // FIXME: FormatSystemErrorMessage may throw diff --git a/format.h b/format.h index 33ce9667..0122f47e 100644 --- a/format.h +++ b/format.h @@ -739,22 +739,62 @@ class BasicArg : public Action, public internal::ArgInfo { template inline ArgInfo make_arg(const T &arg) { return BasicArg(arg); } + +class SystemErrorBase : public std::runtime_error { +public: + SystemErrorBase() : std::runtime_error("") {} +}; } // namespace internal /** - An error returned by an operating system or a language runtime, - for example a file opening error. + An argument list. */ -class SystemError : public std::runtime_error { +class ArgList { private: - int error_code_; + const internal::ArgInfo *args_; + std::size_t size_; + +public: + ArgList() : size_(0) {} + ArgList(const internal::ArgInfo *args, std::size_t size) + : args_(args), size_(size) {} + + /** + Returns the list size (the number of arguments). + */ + std::size_t size() const { return size_; } + + /** + Returns the argument at specified index. + */ + const internal::ArgInfo &operator[](std::size_t index) const { + return args_[index]; + } +}; + +namespace internal { +// Printf format string parser. +template +class PrintfParser { + private: + ArgList args_; + int next_arg_index_; + + typedef ArgInfo Arg; + + void ParseFlags(FormatSpec &spec, const Char *&s); + + // Parses argument index, flags and width and returns the parsed + // argument index. + unsigned ParseHeader(const Char *&s, FormatSpec &spec, const char *&error); + + const ArgInfo &HandleArgIndex(unsigned arg_index, const char *&error); public: - SystemError(StringRef message, int error_code) - : std::runtime_error(message), error_code_(error_code) {} - - int error_code() const { return error_code_; } + void Format(BasicWriter &writer, + BasicStringRef format, const ArgList &args); }; +} // namespace internal enum Alignment { ALIGN_DEFAULT, ALIGN_LEFT, ALIGN_RIGHT, ALIGN_CENTER, ALIGN_NUMERIC @@ -982,56 +1022,6 @@ inline StrFormatSpec pad( return StrFormatSpec(str, width, fill); } -/** - An argument list. - */ -class ArgList { - private: - const internal::ArgInfo *args_; - std::size_t size_; - -public: - ArgList() : size_(0) {} - ArgList(const internal::ArgInfo *args, std::size_t size) - : args_(args), size_(size) {} - - /** - Returns the list size (the number of arguments). - */ - std::size_t size() const { return size_; } - - /** - Returns the argument at specified index. - */ - const internal::ArgInfo &operator[](std::size_t index) const { - return args_[index]; - } -}; - -namespace internal { -// Printf format string parser. -template -class PrintfParser { - private: - ArgList args_; - int next_arg_index_; - - typedef ArgInfo Arg; - - void ParseFlags(FormatSpec &spec, const Char *&s); - - // Parses argument index, flags and width and returns the parsed - // argument index. - unsigned ParseHeader(const Char *&s, FormatSpec &spec, const char *&error); - - const ArgInfo &HandleArgIndex(unsigned arg_index, const char *&error); - - public: - void Format(BasicWriter &writer, - BasicStringRef format, const ArgList &args); -}; -} // namespace internal - // Generates a comma-separated list with results of applying f to numbers 0..n-1. # define FMT_GEN(n, f) FMT_GEN##n(f) # define FMT_GEN1(f) f(0) @@ -1060,7 +1050,19 @@ class PrintfParser { }; \ func(arg1, ArgList(arg_array, sizeof...(Args))); \ } + +// Defines a variadic constructor. +# define FMT_VARIADIC_CTOR(ctor, func, arg0_type, arg1_type) \ + template \ + ctor(arg0_type arg0, arg1_type arg1, const Args & ... args) { \ + const internal::ArgInfo arg_array[fmt::internal::NonZero::VALUE] = { \ + fmt::internal::make_arg(args)... \ + }; \ + func(arg0, arg1, ArgList(arg_array, sizeof...(Args))); \ + } + #else + # define FMT_MAKE_REF(n) fmt::internal::make_arg(v##n) // Defines a wrapper for a function taking one argument of type arg_type // and n additional arguments of arbitrary types. @@ -1078,6 +1080,26 @@ class PrintfParser { FMT_WRAP1(func, arg_type, 5) FMT_WRAP1(func, arg_type, 6) \ FMT_WRAP1(func, arg_type, 7) FMT_WRAP1(func, arg_type, 8) \ FMT_WRAP1(func, arg_type, 9) FMT_WRAP1(func, arg_type, 10) + +# define FMT_CTOR(ctor, func, arg0_type, arg1_type, n) \ + template \ + ctor(arg0_type arg0, arg1_type arg1, FMT_GEN(n, FMT_MAKE_ARG)) { \ + const fmt::internal::ArgInfo args[] = {FMT_GEN(n, FMT_MAKE_REF)}; \ + func(arg0, arg1, fmt::ArgList(args, sizeof(args) / sizeof(*args))); \ + } + +// Emulates a variadic function returning void on a pre-C++11 compiler. +# define FMT_VARIADIC_CTOR(ctor, func, arg0_type, arg1_type) \ + FMT_CTOR(ctor, func, arg0_type, arg1_type, 1) \ + FMT_CTOR(ctor, func, arg0_type, arg1_type, 2) \ + FMT_CTOR(ctor, func, arg0_type, arg1_type, 3) \ + FMT_CTOR(ctor, func, arg0_type, arg1_type, 4) \ + FMT_CTOR(ctor, func, arg0_type, arg1_type, 5) \ + FMT_CTOR(ctor, func, arg0_type, arg1_type, 6) \ + FMT_CTOR(ctor, func, arg0_type, arg1_type, 7) \ + FMT_CTOR(ctor, func, arg0_type, arg1_type, 8) \ + FMT_CTOR(ctor, func, arg0_type, arg1_type, 9) \ + FMT_CTOR(ctor, func, arg0_type, arg1_type, 10) #endif // Generates a comma-separated list with results of applying f to pairs @@ -1102,6 +1124,37 @@ class PrintfParser { #define FMT_FOR_EACH10(f, x0, x1, x2, x3, x4, x5, x6, x7, x8, x9) \ FMT_FOR_EACH9(f, x0, x1, x2, x3, x4, x5, x6, x7, x8), f(x9, 9) +/** +An error returned by an operating system or a language runtime, +for example a file opening error. +*/ +class SystemError : public internal::SystemErrorBase { + private: + typedef char Char; // For FMT_VARIADIC_CTOR. + + void init(int error_code, StringRef format_str, const ArgList &args); + + protected: + int error_code_; + + public: + /** + \rst + Constructs a :cpp:class:`fmt::SystemError` object with the description + of the form "**: **", where ** is the + formatted message and ** is the system message corresponding + to the error code. + *error_code* is a system error code as given by ``errno``. + \endrst + */ + SystemError(int error_code, StringRef message) { + init(error_code, message, ArgList()); + } + FMT_VARIADIC_CTOR(SystemError, init, int, StringRef) + + int error_code() const { return error_code_; } +}; + /** \rst This template provides operations for formatting and writing data into @@ -1187,13 +1240,13 @@ class BasicWriter { template void FormatDouble(T value, const FormatSpec &spec); - // Formats a string. + // Writes a formatted string. template - CharPtr FormatString( + CharPtr write_str( const StringChar *s, std::size_t size, const AlignSpec &spec); template - void FormatString( + void write_str( const Arg::StringValue &str, const FormatSpec &spec); // This method is private to disallow writing a wide string to a @@ -1378,12 +1431,12 @@ class BasicWriter { template BasicWriter &operator<<(const StrFormatSpec &spec) { const StringChar *s = spec.str(); - FormatString(s, std::char_traits::length(s), spec); + write_str(s, std::char_traits::length(s), spec); return *this; } void write(const std::basic_string &s, const FormatSpec &spec) { - FormatString(s.data(), s.size(), spec); + write_str(s.data(), s.size(), spec); } void clear() { buffer_.clear(); } @@ -1415,7 +1468,7 @@ inline void BasicWriter::Clear() { clear(); } template template -typename BasicWriter::CharPtr BasicWriter::FormatString( +typename BasicWriter::CharPtr BasicWriter::write_str( const StringChar *s, std::size_t size, const AlignSpec &spec) { CharPtr out = CharPtr(); if (spec.width() > size) { @@ -1807,19 +1860,16 @@ class SystemErrorSink { public: explicit SystemErrorSink(int error_code) : error_code_(error_code) {} - void operator()(const Writer &w) const; + void operator()(const Writer &w) const { + throw SystemError(error_code_, "{}", w.c_str()); + } }; #endif -/** - \rst - Formats a message and throws :cpp:class:`fmt::SystemError` with - the description of the form "**: **", - where ** is the formatted message and ** - is the system message corresponding to the error code. - *error_code* is a system error code as given by ``errno``. - \endrst - */ +FMT_DEPRECATED(Formatter ThrowSystemError( + int error_code, StringRef format)); + +// This function is deprecated. Use fmt::SystemError instead. inline Formatter ThrowSystemError( int error_code, StringRef format) { Formatter f(format, SystemErrorSink(error_code)); @@ -1844,16 +1894,31 @@ class WinErrorSink { }; /** - \rst - Formats a message and throws :cpp:class:`fmt::SystemError` with - the description of the form "**: **", - where ** is the formatted message and ** - is the system message corresponding to the error code. - *error_code* is a Windows error code as given by ``GetLastError``. + A Windows error. +*/ +class WindowsError : public SystemError { + private: + void init(int error_code, StringRef format_str, const ArgList &args); - This function is only available on Windows. - \endrst - */ + public: + /** + \rst + Constructs a :cpp:class:`fmt::WindowsError` object with the description + of the form "**: **", where ** is the + formatted message and ** is the system message corresponding + to the error code. + *error_code* is a Windows error code as given by ``GetLastError``. + \endrst + */ + WindowsError(int error_code, StringRef message) { + init(error_code, message, ArgList()); + } + FMT_VARIADIC_CTOR(WindowsError, init, int, StringRef) +}; + +FMT_DEPRECATED(Formatter ThrowWinError(int error_code, StringRef format)); + +// This function is deprecated. Use WindowsError instead. inline Formatter ThrowWinError(int error_code, StringRef format) { Formatter f(format, WinErrorSink(error_code)); return f; @@ -1875,7 +1940,7 @@ class FileSink { void operator()(const BasicWriter &w) const { if (std::fwrite(w.data(), w.size(), 1, file_) == 0) - ThrowSystemError(errno, "cannot write to file"); + throw SystemError(errno, "cannot write to file"); } }; diff --git a/posix.cc b/posix.cc index 2d3a85d3..8de279ce 100644 --- a/posix.cc +++ b/posix.cc @@ -70,13 +70,13 @@ void fmt::BufferedFile::close() { int result = FMT_SYSTEM(fclose(file_)); file_ = 0; if (result != 0) - fmt::ThrowSystemError(errno, "cannot close file"); + throw SystemError(errno, "cannot close file"); } int fmt::BufferedFile::fileno() const { int fd = FMT_POSIX_CALL(fileno(file_)); if (fd == -1) - fmt::ThrowSystemError(errno, "cannot get file descriptor"); + throw SystemError(errno, "cannot get file descriptor"); return fd; } @@ -89,7 +89,7 @@ fmt::File::File(const char *path, int oflag) { FMT_RETRY(fd_, FMT_POSIX_CALL(open(path, oflag, mode))); #endif if (fd_ == -1) - fmt::ThrowSystemError(errno, "cannot open file {}") << path; + throw SystemError(errno, "cannot open file {}", path); } fmt::File::~File() FMT_NOEXCEPT(true) { @@ -107,14 +107,14 @@ void fmt::File::close() { int result = FMT_POSIX_CALL(close(fd_)); fd_ = -1; if (result != 0) - fmt::ThrowSystemError(errno, "cannot close file"); + throw SystemError(errno, "cannot close file"); } std::streamsize fmt::File::read(void *buffer, std::size_t count) { std::streamsize result = 0; FMT_RETRY(result, FMT_POSIX_CALL(read(fd_, buffer, ConvertRWCount(count)))); if (result == -1) - fmt::ThrowSystemError(errno, "cannot read from file"); + throw SystemError(errno, "cannot read from file"); return result; } @@ -122,7 +122,7 @@ std::streamsize fmt::File::write(const void *buffer, std::size_t count) { std::streamsize result = 0; FMT_RETRY(result, FMT_POSIX_CALL(write(fd_, buffer, ConvertRWCount(count)))); if (result == -1) - fmt::ThrowSystemError(errno, "cannot write to file"); + throw SystemError(errno, "cannot write to file"); return result; } @@ -131,7 +131,7 @@ fmt::File fmt::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::ThrowSystemError(errno, "cannot duplicate file descriptor {}") << fd; + throw SystemError(errno, "cannot duplicate file descriptor {}", fd); return File(new_fd); } @@ -139,8 +139,8 @@ void fmt::File::dup2(int fd) { int result = 0; FMT_RETRY(result, FMT_POSIX_CALL(dup2(fd_, fd))); if (result == -1) { - fmt::ThrowSystemError(errno, - "cannot duplicate file descriptor {} to {}") << fd_ << fd; + throw SystemError(errno, + "cannot duplicate file descriptor {} to {}", fd_, fd); } } @@ -167,7 +167,7 @@ void fmt::File::pipe(File &read_end, File &write_end) { int result = FMT_POSIX_CALL(pipe(fds)); #endif if (result != 0) - fmt::ThrowSystemError(errno, "cannot create pipe"); + throw SystemError(errno, "cannot create pipe"); // The following assignments don't throw because read_fd and write_fd // are closed. read_end = File(fds[0]); @@ -177,10 +177,8 @@ void fmt::File::pipe(File &read_end, File &write_end) { fmt::BufferedFile fmt::File::fdopen(const char *mode) { // Don't retry as fdopen doesn't return EINTR. FILE *f = FMT_POSIX_CALL(fdopen(fd_, mode)); - if (!f) { - fmt::ThrowSystemError(errno, - "cannot associate stream with file descriptor"); - } + if (!f) + throw SystemError(errno, "cannot associate stream with file descriptor"); BufferedFile file(f); fd_ = -1; return file; diff --git a/test/format-test.cc b/test/format-test.cc index c18ae7a1..49072cf2 100644 --- a/test/format-test.cc +++ b/test/format-test.cc @@ -1369,7 +1369,7 @@ TEST(FormatterTest, FormatExamples) { EXPECT_SYSTEM_ERROR({ FILE *f = fopen(filename, "r"); if (!f) - fmt::ThrowSystemError(errno, "Cannot open file '{}'") << filename; + throw fmt::SystemError(errno, "Cannot open file '{}'", filename); }, error_code, "Cannot open file 'nonexistent'"); } diff --git a/test/gtest-extra-test.cc b/test/gtest-extra-test.cc index 1e1a4b81..5b191c5f 100644 --- a/test/gtest-extra-test.cc +++ b/test/gtest-extra-test.cc @@ -107,7 +107,7 @@ void ThrowException() { } void ThrowSystemError() { - fmt::ThrowSystemError(EDOM, "test"); + throw fmt::SystemError(EDOM, "test"); } // Tests that when EXPECT_THROW_MSG fails, it evaluates its message argument @@ -236,12 +236,11 @@ TEST(ExpectThrowTest, DoesNotGenerateUnreachableCodeWarning) { // EXPECT_SYSTEM_ERROR macro. TEST(ExpectSystemErrorTest, DoesNotGenerateUnreachableCodeWarning) { int n = 0; - EXPECT_SYSTEM_ERROR(throw fmt::SystemError( - FormatSystemErrorMessage(EDOM, "test"), EDOM), EDOM, "test"); + EXPECT_SYSTEM_ERROR(throw fmt::SystemError(EDOM, "test"), EDOM, "test"); EXPECT_NONFATAL_FAILURE(EXPECT_SYSTEM_ERROR(n++, EDOM, ""), ""); EXPECT_NONFATAL_FAILURE(EXPECT_SYSTEM_ERROR(throw 1, EDOM, ""), ""); EXPECT_NONFATAL_FAILURE(EXPECT_SYSTEM_ERROR( - throw fmt::SystemError("aaa", EDOM), EDOM, "bbb"), ""); + throw fmt::SystemError(EDOM, "aaa"), EDOM, "bbb"), ""); } TEST(AssertionSyntaxTest, ExceptionAssertionBehavesLikeSingleStatement) { diff --git a/test/gtest-extra.cc b/test/gtest-extra.cc index 3485047a..3717645d 100644 --- a/test/gtest-extra.cc +++ b/test/gtest-extra.cc @@ -38,7 +38,7 @@ void OutputRedirect::Flush() { int result = 0; FMT_RETRY(result, fflush(file_)); if (result != 0) - fmt::ThrowSystemError(errno, "cannot flush stream"); + throw fmt::SystemError(errno, "cannot flush stream"); } void OutputRedirect::Restore() { diff --git a/test/util-test.cc b/test/util-test.cc index d1d2cd9a..baf79666 100644 --- a/test/util-test.cc +++ b/test/util-test.cc @@ -163,9 +163,9 @@ TEST(UtilTest, StrError) { } TEST(UtilTest, SystemError) { - fmt::SystemError e(StringRef("test"), 42); - EXPECT_STREQ("test", e.what()); - EXPECT_EQ(42, e.error_code()); + fmt::SystemError e(EDOM, "test"); + EXPECT_EQ(fmt::format("test: {}", GetSystemErrorMessage(EDOM)), e.what()); + EXPECT_EQ(EDOM, e.error_code()); } typedef void (*FormatErrorMessage)( @@ -173,7 +173,7 @@ typedef void (*FormatErrorMessage)( template void CheckErrorSink(int error_code, FormatErrorMessage format) { - fmt::SystemError error("", 0); + fmt::SystemError error(0, ""); Sink sink(error_code); fmt::Writer w; w << "test"; @@ -188,12 +188,11 @@ void CheckErrorSink(int error_code, FormatErrorMessage format) { EXPECT_EQ(error_code, error.error_code()); } -template -void CheckThrowError(int error_code, FormatErrorMessage format, - fmt::Formatter (*throw_error)(int error_code, StringRef format)) { - fmt::SystemError error("", 0); +template +void CheckThrowError(int error_code, FormatErrorMessage format) { + fmt::SystemError error(0, ""); try { - throw_error(error_code, "test {}") << "error"; + throw Error(error_code, "test {}", "error"); } catch (const fmt::SystemError &e) { error = e; } @@ -216,8 +215,7 @@ TEST(UtilTest, SystemErrorSink) { } TEST(UtilTest, ThrowSystemError) { - CheckThrowError(EDOM, - fmt::internal::FormatSystemErrorMessage, fmt::ThrowSystemError); + CheckThrowError(EDOM, fmt::internal::FormatSystemErrorMessage); } TEST(UtilTest, ReportSystemError) {