From 2f4f49fd60cc5b04ad56aabc291a13c59e76de0d Mon Sep 17 00:00:00 2001 From: Victor Zverovich Date: Tue, 18 Jul 2017 19:40:48 -0700 Subject: [PATCH] Switch from cstring_view to string_view --- fmt/format.cc | 10 +- fmt/format.h | 340 ++++++++++++++++++++-------------- fmt/ostream.cc | 2 +- fmt/ostream.h | 4 +- fmt/posix.cc | 4 +- fmt/posix.h | 52 +++++- fmt/printf.cc | 4 +- fmt/printf.h | 135 +++++++------- fmt/time.h | 16 +- test/custom-formatter-test.cc | 2 +- test/format-test.cc | 20 +- 11 files changed, 344 insertions(+), 245 deletions(-) diff --git a/fmt/format.cc b/fmt/format.cc index 1647664f..339138f8 100644 --- a/fmt/format.cc +++ b/fmt/format.cc @@ -213,7 +213,7 @@ void report_error(FormatFunc func, int error_code, } // namespace FMT_FUNC void system_error::init( - int err_code, cstring_view format_str, args args) { + int err_code, string_view format_str, args args) { error_code_ = err_code; memory_buffer buffer; format_system_error(buffer, err_code, vformat(format_str, args)); @@ -337,7 +337,7 @@ FMT_FUNC int internal::utf16_to_utf8::convert(wstring_view s) { } FMT_FUNC void windows_error::init( - int err_code, cstring_view format_str, args args) { + int err_code, string_view format_str, args args) { error_code_ = err_code; memory_buffer buffer; internal::format_windows_error(buffer, err_code, vformat(format_str, args)); @@ -419,17 +419,17 @@ FMT_FUNC void report_windows_error( } #endif -FMT_FUNC void vprint(std::FILE *f, cstring_view format_str, args args) { +FMT_FUNC void vprint(std::FILE *f, string_view format_str, args args) { memory_buffer buffer; vformat_to(buffer, format_str, args); std::fwrite(buffer.data(), 1, buffer.size(), f); } -FMT_FUNC void vprint(cstring_view format_str, args args) { +FMT_FUNC void vprint(string_view format_str, args args) { vprint(stdout, format_str, args); } -FMT_FUNC void vprint_colored(Color c, cstring_view format, args args) { +FMT_FUNC void vprint_colored(Color c, string_view format, args args) { char escape[] = "\x1b[30m"; escape[3] = static_cast('0' + c); std::fputs(escape, stdout); diff --git a/fmt/format.h b/fmt/format.h index 71b49e2c..9ee8d820 100644 --- a/fmt/format.h +++ b/fmt/format.h @@ -474,59 +474,15 @@ class basic_string_view { typedef basic_string_view string_view; typedef basic_string_view wstring_view; -/** - \rst - A reference to a null-terminated string. It can be constructed from a C - string or ``std::string``. - - You can use one of the following typedefs for common character types: - - +---------------+-----------------------------+ - | Type | Definition | - +===============+=============================+ - | cstring_view | basic_cstring_view | - +---------------+-----------------------------+ - | wcstring_view | basic_cstring_view | - +---------------+-----------------------------+ - - This class is most useful as a parameter type to allow passing - different types of strings to a function, for example:: - - template - std::string format(cstring_view format_str, const Args & ... args); - - format("{}", 42); - format(std::string("{}"), 42); - \endrst - */ -template -class basic_cstring_view { - private: - const Char *data_; - - public: - /** Constructs a string reference object from a C string. */ - basic_cstring_view(const Char *s) : data_(s) {} - - /** - \rst - Constructs a string reference from an ``std::string`` object. - \endrst - */ - basic_cstring_view(const std::basic_string &s) : data_(s.c_str()) {} - - /** Returns the pointer to a C string. */ - const Char *c_str() const { return data_; } -}; - -typedef basic_cstring_view cstring_view; -typedef basic_cstring_view wcstring_view; - /** A formatting error such as invalid format string. */ class format_error : public std::runtime_error { public: - explicit format_error(cstring_view message) - : std::runtime_error(message.c_str()) {} + explicit format_error(const char *message) + : std::runtime_error(message) {} + + explicit format_error(const std::string &message) + : std::runtime_error(message) {} + ~format_error() throw(); }; @@ -1197,7 +1153,6 @@ template <> constexpr Type gettype() { return CSTRING; } template <> constexpr Type gettype() { return CSTRING; } template <> constexpr Type gettype() { return STRING; } template <> constexpr Type gettype() { return STRING; } -template <> constexpr Type gettype() { return CSTRING; } template <> constexpr Type gettype() { return TSTRING; } template <> constexpr Type gettype() { return TSTRING; } template <> constexpr Type gettype() { return TSTRING; } @@ -1335,7 +1290,6 @@ class value { FMT_MAKE_VALUE(const unsigned char *, ustring.value, CSTRING) FMT_MAKE_STR_VALUE(const std::string &, STRING) FMT_MAKE_STR_VALUE(string_view, STRING) - FMT_MAKE_VALUE_(cstring_view, string.value, CSTRING, value.c_str()) #define FMT_MAKE_WSTR_VALUE(Type, TYPE) \ value(typename wchar_helper::supported value) { \ @@ -1978,18 +1932,112 @@ class arg_formatter_base { } }; -template -class context_base { +template +class null_terminating_iterator; + +template +const Char *pointer_from(null_terminating_iterator it); + +// An iterator that produces a null terminator on *end. +template +class null_terminating_iterator { + public: + typedef std::ptrdiff_t difference_type; + + null_terminating_iterator() : ptr_(0), end_(0) {} + + null_terminating_iterator(const Char *ptr, const Char *end) + : ptr_(ptr), end_(end) {} + + Char operator*() const { + return ptr_ != end_ ? *ptr_ : 0; + } + + null_terminating_iterator operator++() { + ++ptr_; + return *this; + } + + null_terminating_iterator operator++(int) { + null_terminating_iterator result(*this); + ++ptr_; + return result; + } + + null_terminating_iterator operator--() { + --ptr_; + return *this; + } + + null_terminating_iterator operator+(difference_type n) { + return null_terminating_iterator(ptr_ + n, end_); + } + + null_terminating_iterator operator+=(difference_type n) { + ptr_ += n; + return *this; + } + + difference_type operator-(null_terminating_iterator other) const { + return ptr_ - other.ptr_; + } + + bool operator!=(null_terminating_iterator other) const { + return ptr_ != other.ptr_; + } + + bool operator>=(null_terminating_iterator other) const { + return ptr_ >= other.ptr_; + } + + friend const Char *pointer_from(null_terminating_iterator it); + private: const Char *ptr_; + const Char *end_; +}; + +template < + typename T, + typename Char, + typename std::enable_if< + std::is_same>::value, int>::type = 0> +null_terminating_iterator to_iterator(basic_string_view v) { + const Char *s = v.data(); + return null_terminating_iterator(s, s + v.size()); +} + +template < + typename T, + typename Char, + typename std::enable_if::value, int>::type = 0> +const Char *to_iterator(const basic_string_view v) { + return v.data(); +} + +template +const T *pointer_from(const T *p) { return p; } + +template +const Char *pointer_from(null_terminating_iterator it) { + return it.ptr_; +} + +template +class context_base { + public: + typedef null_terminating_iterator iterator; + + private: + iterator pos_; basic_args args_; int next_arg_index_; protected: typedef basic_arg format_arg; - context_base(const Char *format_str, basic_args args) - : ptr_(format_str), args_(args), next_arg_index_(0) {} + context_base(basic_string_view format_str, basic_args args) + : pos_(to_iterator(format_str)), args_(args), next_arg_index_(0) {} ~context_base() {} basic_args args() const { return args_; } @@ -2027,8 +2075,8 @@ class context_base { } public: - // Returns a pointer to the current position in the format string. - const Char *&ptr() { return ptr_; } + // Returns an iterator to the current position in the format string. + iterator &pos() { return pos_; } }; } // namespace internal @@ -2091,13 +2139,14 @@ class basic_context : stored in the object so make sure they have appropriate lifetimes. \endrst */ - basic_context(const Char *format_str, basic_args args) + basic_context( + basic_string_view format_str, basic_args args) : Base(format_str, args) {} // Parses argument id and returns corresponding argument. format_arg parse_arg_id(); - using Base::ptr; + using Base::pos; }; /** @@ -2106,7 +2155,7 @@ class basic_context : */ class system_error : public std::runtime_error { private: - void init(int err_code, cstring_view format_str, args args); + void init(int err_code, string_view format_str, args args); protected: int error_code_; @@ -2133,7 +2182,7 @@ class system_error : public std::runtime_error { \endrst */ template - system_error(int error_code, cstring_view message, const Args & ... args) + system_error(int error_code, string_view message, const Args & ... args) : std::runtime_error("") { init(error_code, message, make_args(args...)); } @@ -2392,7 +2441,7 @@ class basic_writer { Writes *value* to the buffer. \endrst */ - void write(fmt::basic_string_view value) { + void write(basic_string_view value) { const Char *str = value.data(); buffer_.append(str, str + value.size()); } @@ -2794,7 +2843,7 @@ FMT_API void report_system_error(int error_code, /** A Windows error. */ class windows_error : public system_error { private: - FMT_API void init(int error_code, cstring_view format_str, args args); + FMT_API void init(int error_code, string_view format_str, args args); public: /** @@ -2826,7 +2875,7 @@ class windows_error : public system_error { \endrst */ template - windows_error(int error_code, cstring_view message, const Args & ... args) { + windows_error(int error_code, string_view message, const Args & ... args) { init(error_code, message, make_args(args...)); } }; @@ -2840,7 +2889,7 @@ FMT_API void report_windows_error(int error_code, enum Color { BLACK, RED, GREEN, YELLOW, BLUE, MAGENTA, CYAN, WHITE }; -FMT_API void vprint_colored(Color c, cstring_view format, args args); +FMT_API void vprint_colored(Color c, string_view format, args args); /** Formats a string and prints it to stdout using ANSI escape sequences @@ -2849,36 +2898,36 @@ FMT_API void vprint_colored(Color c, cstring_view format, args args); print_colored(fmt::RED, "Elapsed time: {0:.2f} seconds", 1.23); */ template -inline void print_colored(Color c, cstring_view format_str, +inline void print_colored(Color c, string_view format_str, const Args & ... args) { vprint_colored(c, format_str, make_args(args...)); } template -void vformat_to(basic_buffer &buffer, basic_cstring_view format_str, +void vformat_to(basic_buffer &buffer, basic_string_view format_str, basic_args args); -inline void vformat_to(buffer &buf, cstring_view format_str, args args) { +inline void vformat_to(buffer &buf, string_view format_str, args args) { vformat_to>(buf, format_str, args); } -inline void vformat_to(wbuffer &buf, wcstring_view format_str, wargs args) { +inline void vformat_to(wbuffer &buf, wstring_view format_str, wargs args) { vformat_to>(buf, format_str, args); } template -inline void format_to(buffer &buf, cstring_view format_str, +inline void format_to(buffer &buf, string_view format_str, const Args & ... args) { vformat_to(buf, format_str, make_args(args...)); } template -inline void format_to(wbuffer &buf, wcstring_view format_str, +inline void format_to(wbuffer &buf, wstring_view format_str, const Args & ... args) { vformat_to(buf, format_str, make_args(args...)); } -inline std::string vformat(cstring_view format_str, args args) { +inline std::string vformat(string_view format_str, args args) { memory_buffer buffer; vformat_to(buffer, format_str, args); return to_string(buffer); @@ -2894,22 +2943,22 @@ inline std::string vformat(cstring_view format_str, args args) { \endrst */ template -inline std::string format(cstring_view format_str, const Args & ... args) { +inline std::string format(string_view format_str, const Args & ... args) { return vformat(format_str, make_args(args...)); } -inline std::wstring vformat(wcstring_view format_str, wargs args) { +inline std::wstring vformat(wstring_view format_str, wargs args) { wmemory_buffer buffer; vformat_to(buffer, format_str, args); return to_string(buffer); } template -inline std::wstring format(wcstring_view format_str, const Args & ... args) { +inline std::wstring format(wstring_view format_str, const Args & ... args) { return vformat(format_str, make_args(args...)); } -FMT_API void vprint(std::FILE *f, cstring_view format_str, args args); +FMT_API void vprint(std::FILE *f, string_view format_str, args args); /** \rst @@ -2921,12 +2970,12 @@ FMT_API void vprint(std::FILE *f, cstring_view format_str, args args); \endrst */ template -inline void print(std::FILE *f, cstring_view format_str, +inline void print(std::FILE *f, string_view format_str, const Args & ... args) { vprint(f, format_str, make_args(args...)); } -FMT_API void vprint(cstring_view format_str, args args); +FMT_API void vprint(string_view format_str, args args); /** \rst @@ -2938,7 +2987,7 @@ FMT_API void vprint(cstring_view format_str, args args); \endrst */ template -inline void print(cstring_view format_str, const Args & ... args) { +inline void print(string_view format_str, const Args & ... args) { vprint(format_str, make_args(args...)); } @@ -3092,21 +3141,22 @@ inline bool is_name_start(Char c) { return ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z') || '_' == c; } -// Parses an unsigned integer advancing s to the end of the parsed input. -// This function assumes that the first character of s is a digit. -template -unsigned parse_nonnegative_int(const Char *&s) { - assert('0' <= *s && *s <= '9'); +// Parses an unsigned integer advancing it to the end of the parsed input. +// This function assumes that the first character of it is a digit and a +// presence of a non-digit character at the end. +template +unsigned parse_nonnegative_int(Iterator &it) { + assert('0' <= *it && *it <= '9'); unsigned value = 0; do { - unsigned new_value = value * 10 + (*s++ - '0'); + unsigned new_value = value * 10 + (*it++ - '0'); // Check if value wrapped around. if (new_value < value) { value = (std::numeric_limits::max)(); break; } value = new_value; - } while ('0' <= *s && *s <= '9'); + } while ('0' <= *it && *it <= '9'); // Convert to unsigned to prevent a warning. unsigned max_int = (std::numeric_limits::max)(); if (value > max_int) @@ -3138,15 +3188,15 @@ struct is_unsigned { } }; -template -void check_sign(const Char *&s, const basic_arg &arg) { - char sign = static_cast(*s); +template +void check_sign(Iterator &it, const basic_arg &arg) { + char sign = static_cast(*it); require_numeric_argument(arg, sign); if (visit(is_unsigned(), arg)) { FMT_THROW(format_error(fmt::format( "format specifier '{}' requires signed argument", sign))); } - ++s; + ++it; } template @@ -3228,25 +3278,26 @@ template inline typename basic_context::format_arg basic_context::parse_arg_id() { format_arg arg; - const Char *&s = this->ptr(); - if (!internal::is_name_start(*s)) { + auto &it = this->pos(); + if (!internal::is_name_start(*it)) { const char *error = 0; - arg = *s < '0' || *s > '9' ? + arg = *it < '0' || *it > '9' ? this->next_arg(error) : - get_arg(internal::parse_nonnegative_int(s), error); + get_arg(internal::parse_nonnegative_int(it), error); if (error) { FMT_THROW(format_error( - *s != '}' && *s != ':' ? "invalid format string" : error)); + *it != '}' && *it != ':' ? "invalid format string" : error)); } return arg; } - const Char *start = s; + auto start = it; Char c; do { - c = *++s; + c = *++it; } while (internal::is_name_start(c) || ('0' <= c && c <= '9')); const char *error = 0; - arg = get_arg(basic_string_view(start, s - start), error); + arg = get_arg(basic_string_view( + internal::pointer_from(start), it - start), error); if (error) FMT_THROW(format_error(error)); return arg; @@ -3256,15 +3307,15 @@ inline typename basic_context::format_arg template void do_format_arg(basic_buffer &buffer, const basic_arg& arg, Context &ctx) { - const Char *&s = ctx.ptr(); + auto &it = ctx.pos(); basic_format_specs spec; - if (*s == ':') { + if (*it == ':') { if (visit(internal::custom_formatter(buffer, ctx), arg)) return; - ++s; + ++it; // Parse fill and alignment. - if (Char c = *s) { - const Char *p = s + 1; + if (Char c = *it) { + auto p = it + 1; spec.align_ = ALIGN_DEFAULT; do { switch (*p) { @@ -3282,57 +3333,57 @@ void do_format_arg(basic_buffer &buffer, const basic_arg& arg, break; } if (spec.align_ != ALIGN_DEFAULT) { - if (p != s) { + if (p != it) { if (c == '}') break; if (c == '{') FMT_THROW(format_error("invalid fill character '{'")); - s += 2; + it += 2; spec.fill_ = c; - } else ++s; + } else ++it; if (spec.align_ == ALIGN_NUMERIC) internal::require_numeric_argument(arg, '='); break; } - } while (--p >= s); + } while (--p >= it); } // Parse sign. - switch (*s) { + switch (*it) { case '+': - internal::check_sign(s, arg); + internal::check_sign(it, arg); spec.flags_ |= SIGN_FLAG | PLUS_FLAG; break; case '-': - internal::check_sign(s, arg); + internal::check_sign(it, arg); spec.flags_ |= MINUS_FLAG; break; case ' ': - internal::check_sign(s, arg); + internal::check_sign(it, arg); spec.flags_ |= SIGN_FLAG; break; } - if (*s == '#') { + if (*it == '#') { internal::require_numeric_argument(arg, '#'); spec.flags_ |= HASH_FLAG; - ++s; + ++it; } // Parse zero flag. - if (*s == '0') { + if (*it == '0') { internal::require_numeric_argument(arg, '0'); spec.align_ = ALIGN_NUMERIC; spec.fill_ = '0'; - ++s; + ++it; } // Parse width. - if ('0' <= *s && *s <= '9') { - spec.width_ = internal::parse_nonnegative_int(s); - } else if (*s == '{') { - ++s; + if ('0' <= *it && *it <= '9') { + spec.width_ = internal::parse_nonnegative_int(it); + } else if (*it == '{') { + ++it; auto width_arg = ctx.parse_arg_id(); - if (*s++ != '}') + if (*it++ != '}') FMT_THROW(format_error("invalid format string")); ulong_long width = visit(internal::width_handler(), width_arg); if (width > (std::numeric_limits::max)()) @@ -3341,15 +3392,15 @@ void do_format_arg(basic_buffer &buffer, const basic_arg& arg, } // Parse precision. - if (*s == '.') { - ++s; + if (*it == '.') { + ++it; spec.precision_ = 0; - if ('0' <= *s && *s <= '9') { - spec.precision_ = internal::parse_nonnegative_int(s); - } else if (*s == '{') { - ++s; + if ('0' <= *it && *it <= '9') { + spec.precision_ = internal::parse_nonnegative_int(it); + } else if (*it == '{') { + ++it; auto precision_arg = ctx.parse_arg_id(); - if (*s++ != '}') + if (*it++ != '}') FMT_THROW(format_error("invalid format string")); ulong_long precision = visit(internal::precision_handler(), precision_arg); @@ -3367,11 +3418,11 @@ void do_format_arg(basic_buffer &buffer, const basic_arg& arg, } // Parse type. - if (*s != '}' && *s) - spec.type_ = static_cast(*s++); + if (*it != '}' && *it) + spec.type_ = static_cast(*it++); } - if (*s != '}') + if (*it != '}') FMT_THROW(format_error("missing '}' in format string")); // Format argument. @@ -3380,28 +3431,29 @@ void do_format_arg(basic_buffer &buffer, const basic_arg& arg, /** Formats arguments and writes the output to the buffer. */ template -void vformat_to(basic_buffer &buffer, basic_cstring_view format_str, +void vformat_to(basic_buffer &buffer, basic_string_view format_str, basic_args args) { - basic_context ctx(format_str.c_str(), args); - const Char *&s = ctx.ptr(); - const Char *start = s; - while (*s) { - Char c = *s++; + basic_context ctx(format_str, args); + auto &it = ctx.pos(); + auto start = it; + using internal::pointer_from; + while (*it) { + Char c = *it++; if (c != '{' && c != '}') continue; - if (*s == c) { - buffer.append(start, s); - start = ++s; + if (*it == c) { + buffer.append(pointer_from(start), pointer_from(it)); + start = ++it; continue; } if (c == '}') FMT_THROW(format_error("unmatched '}' in format string")); - buffer.append(start, s - 1); + buffer.append(pointer_from(start), pointer_from(it) - 1); do_format_arg(buffer, ctx.parse_arg_id(), ctx); - if (*s != '}') + if (*it != '}') FMT_THROW(format_error(fmt::format("unknown format specifier"))); - start = ++s; + start = ++it; } - buffer.append(start, s); + buffer.append(pointer_from(start), pointer_from(it)); } } // namespace fmt diff --git a/fmt/ostream.cc b/fmt/ostream.cc index b6696b16..8fb717bd 100644 --- a/fmt/ostream.cc +++ b/fmt/ostream.cc @@ -27,7 +27,7 @@ FMT_FUNC void write(std::ostream &os, buffer &buf) { } } -FMT_FUNC void vprint(std::ostream &os, cstring_view format_str, args args) { +FMT_FUNC void vprint(std::ostream &os, string_view format_str, args args) { memory_buffer buffer; vformat_to(buffer, format_str, args); internal::write(os, buffer); diff --git a/fmt/ostream.h b/fmt/ostream.h index 39606c7e..5a011f5a 100644 --- a/fmt/ostream.h +++ b/fmt/ostream.h @@ -90,7 +90,7 @@ void format_value(basic_buffer &buf, const T &value, buf, internal::make_arg< basic_context >(str), ctx); } -FMT_API void vprint(std::ostream &os, cstring_view format_str, args args); +FMT_API void vprint(std::ostream &os, string_view format_str, args args); /** \rst @@ -102,7 +102,7 @@ FMT_API void vprint(std::ostream &os, cstring_view format_str, args args); \endrst */ template -inline void print(std::ostream &os, cstring_view format_str, +inline void print(std::ostream &os, string_view format_str, const Args & ... args) { vprint(os, format_str, make_args(args...)); } diff --git a/fmt/posix.cc b/fmt/posix.cc index 1e5aca9d..5c37de6a 100644 --- a/fmt/posix.cc +++ b/fmt/posix.cc @@ -72,7 +72,7 @@ fmt::BufferedFile::BufferedFile( fmt::cstring_view filename, fmt::cstring_view mode) { FMT_RETRY_VAL(file_, FMT_SYSTEM(fopen(filename.c_str(), mode.c_str())), 0); if (!file_) - throw system_error(errno, "cannot open file {}", filename); + throw system_error(errno, "cannot open file {}", filename.c_str()); } void fmt::BufferedFile::close() { @@ -103,7 +103,7 @@ fmt::File::File(fmt::cstring_view path, int oflag) { FMT_RETRY(fd_, FMT_POSIX_CALL(open(path.c_str(), oflag, mode))); #endif if (fd_ == -1) - throw system_error(errno, "cannot open file {}", path); + throw system_error(errno, "cannot open file {}", path.c_str()); } fmt::File::~File() FMT_NOEXCEPT { diff --git a/fmt/posix.h b/fmt/posix.h index 39fa8d26..c0cb2b62 100644 --- a/fmt/posix.h +++ b/fmt/posix.h @@ -66,6 +66,54 @@ namespace fmt { +/** + \rst + A reference to a null-terminated string. It can be constructed from a C + string or ``std::string``. + + You can use one of the following typedefs for common character types: + + +---------------+-----------------------------+ + | Type | Definition | + +===============+=============================+ + | cstring_view | basic_cstring_view | + +---------------+-----------------------------+ + | wcstring_view | basic_cstring_view | + +---------------+-----------------------------+ + + This class is most useful as a parameter type to allow passing + different types of strings to a function, for example:: + + template + std::string format(cstring_view format_str, const Args & ... args); + + format("{}", 42); + format(std::string("{}"), 42); + \endrst + */ +template +class basic_cstring_view { + private: + const Char *data_; + + public: + /** Constructs a string reference object from a C string. */ + basic_cstring_view(const Char *s) : data_(s) {} + + /** + \rst + Constructs a string reference from an ``std::string`` object. + \endrst + */ + basic_cstring_view(const std::basic_string &s) : data_(s.c_str()) {} + + /** Returns the pointer to a C string. */ + const Char *c_str() const { return data_; } +}; + +typedef basic_cstring_view cstring_view; +typedef basic_cstring_view wcstring_view; + // An error code. class ErrorCode { private: @@ -166,12 +214,12 @@ public: // of MinGW that define fileno as a macro. int (fileno)() const; - void vprint(cstring_view format_str, const args &args) { + void vprint(string_view format_str, const args &args) { fmt::vprint(file_, format_str, args); } template - inline void print(cstring_view format_str, const Args & ... args) { + inline void print(string_view format_str, const Args & ... args) { vprint(format_str, make_args(args...)); } }; diff --git a/fmt/printf.cc b/fmt/printf.cc index 13e4ed82..53d4c0b9 100644 --- a/fmt/printf.cc +++ b/fmt/printf.cc @@ -3,9 +3,9 @@ namespace fmt { template -void printf(basic_writer &w, basic_cstring_view format, args args); +void printf(basic_writer &w, basic_string_view format, args args); -FMT_FUNC int vfprintf(std::FILE *f, cstring_view format, printf_args args) { +FMT_FUNC int vfprintf(std::FILE *f, string_view format, printf_args args) { memory_buffer buffer; printf(buffer, format, args); std::size_t size = buffer.size(); diff --git a/fmt/printf.h b/fmt/printf.h index 0c37ea00..7bb27a89 100644 --- a/fmt/printf.h +++ b/fmt/printf.h @@ -307,17 +307,18 @@ class printf_context : typedef internal::context_base Base; typedef typename Base::format_arg format_arg; typedef basic_format_specs format_specs; + typedef typename Base::iterator iterator; - void parse_flags(format_specs &spec, const Char *&s); + void parse_flags(format_specs &spec, iterator &it); // Returns the argument with specified index or, if arg_index is equal // to the maximum unsigned value, the next argument. format_arg get_arg( - const Char *s, + iterator it, unsigned arg_index = (std::numeric_limits::max)()); // Parses argument index, flags and width and returns the argument index. - unsigned parse_header(const Char *&s, format_specs &spec); + unsigned parse_header(iterator &it, format_specs &spec); public: /** @@ -327,18 +328,18 @@ class printf_context : appropriate lifetimes. \endrst */ - explicit printf_context(basic_cstring_view format_str, + explicit printf_context(basic_string_view format_str, basic_args args) - : Base(format_str.c_str(), args) {} + : Base(format_str, args) {} /** Formats stored arguments and writes the output to the buffer. */ FMT_API void format(basic_buffer &buffer); }; template -void printf_context::parse_flags(format_specs &spec, const Char *&s) { +void printf_context::parse_flags(format_specs &spec, iterator &it) { for (;;) { - switch (*s++) { + switch (*it++) { case '-': spec.align_ = ALIGN_LEFT; break; @@ -355,7 +356,7 @@ void printf_context::parse_flags(format_specs &spec, const Char *&s) { spec.flags_ |= HASH_FLAG; break; default: - --s; + --it; return; } } @@ -363,27 +364,27 @@ void printf_context::parse_flags(format_specs &spec, const Char *&s) { template typename printf_context::format_arg printf_context::get_arg( - const Char *s, unsigned arg_index) { - (void)s; + iterator it, unsigned arg_index) { + (void)it; const char *error = 0; format_arg arg = arg_index == std::numeric_limits::max() ? this->next_arg(error) : Base::get_arg(arg_index - 1, error); if (error) - FMT_THROW(format_error(!*s ? "invalid format string" : error)); + FMT_THROW(format_error(!*it ? "invalid format string" : error)); return arg; } template unsigned printf_context::parse_header( - const Char *&s, format_specs &spec) { + iterator &it, format_specs &spec) { unsigned arg_index = std::numeric_limits::max(); - Char c = *s; + Char c = *it; if (c >= '0' && c <= '9') { // Parse an argument index (if followed by '$') or a width possibly // preceded with '0' flag(s). - unsigned value = internal::parse_nonnegative_int(s); - if (*s == '$') { // value is an argument index - ++s; + unsigned value = internal::parse_nonnegative_int(it); + if (*it == '$') { // value is an argument index + ++it; arg_index = value; } else { if (c == '0') @@ -396,49 +397,51 @@ unsigned printf_context::parse_header( } } } - parse_flags(spec, s); + parse_flags(spec, it); // Parse width. - if (*s >= '0' && *s <= '9') { - spec.width_ = internal::parse_nonnegative_int(s); - } else if (*s == '*') { - ++s; - spec.width_ = visit(internal::PrintfWidthHandler(spec), get_arg(s)); + if (*it >= '0' && *it <= '9') { + spec.width_ = internal::parse_nonnegative_int(it); + } else if (*it == '*') { + ++it; + spec.width_ = visit(internal::PrintfWidthHandler(spec), get_arg(it)); } return arg_index; } template void printf_context::format(basic_buffer &buffer) { - const Char *start = this->ptr(); - const Char *s = start; - while (*s) { - Char c = *s++; + auto start = this->pos(); + auto it = start; + using internal::pointer_from; + while (*it) { + Char c = *it++; if (c != '%') continue; - if (*s == c) { - buffer.append(start, s); - start = ++s; + if (*it == c) { + buffer.append(pointer_from(start), pointer_from(it)); + start = ++it; continue; } - buffer.append(start, s - 1); + buffer.append(pointer_from(start), pointer_from(it) - 1); format_specs spec; spec.align_ = ALIGN_RIGHT; // Parse argument index, flags and width. - unsigned arg_index = parse_header(s, spec); + unsigned arg_index = parse_header(it, spec); // Parse precision. - if (*s == '.') { - ++s; - if ('0' <= *s && *s <= '9') { - spec.precision_ = static_cast(internal::parse_nonnegative_int(s)); - } else if (*s == '*') { - ++s; - spec.precision_ = visit(internal::PrintfPrecisionHandler(), get_arg(s)); + if (*it == '.') { + ++it; + if ('0' <= *it && *it <= '9') { + spec.precision_ = static_cast(internal::parse_nonnegative_int(it)); + } else if (*it == '*') { + ++it; + spec.precision_ = + visit(internal::PrintfPrecisionHandler(), get_arg(it)); } } - format_arg arg = get_arg(s, arg_index); + format_arg arg = get_arg(it, arg_index); if (spec.flag(HASH_FLAG) && visit(internal::IsZeroInt(), arg)) spec.flags_ &= ~internal::to_unsigned(HASH_FLAG); if (spec.fill_ == '0') { @@ -450,41 +453,41 @@ void printf_context::format(basic_buffer &buffer) { // Parse length and convert the argument to the required type. using internal::convert_arg; - switch (*s++) { + switch (*it++) { case 'h': - if (*s == 'h') - convert_arg(arg, *++s); + if (*it == 'h') + convert_arg(arg, *++it); else - convert_arg(arg, *s); + convert_arg(arg, *it); break; case 'l': - if (*s == 'l') - convert_arg(arg, *++s); + if (*it == 'l') + convert_arg(arg, *++it); else - convert_arg(arg, *s); + convert_arg(arg, *it); break; case 'j': - convert_arg(arg, *s); + convert_arg(arg, *it); break; case 'z': - convert_arg(arg, *s); + convert_arg(arg, *it); break; case 't': - convert_arg(arg, *s); + convert_arg(arg, *it); break; case 'L': // printf produces garbage when 'L' is omitted for long double, no // need to do the same. break; default: - --s; - convert_arg(arg, *s); + --it; + convert_arg(arg, *it); } // Parse type. - if (!*s) + if (!*it) FMT_THROW(format_error("invalid format string")); - spec.type_ = static_cast(*s++); + spec.type_ = static_cast(*it++); if (arg.is_integral()) { // Normalize type. switch (spec.type_) { @@ -498,12 +501,12 @@ void printf_context::format(basic_buffer &buffer) { } } - start = s; + start = it; // Format argument. visit(AF(buffer, spec), arg); } - buffer.append(start, s); + buffer.append(pointer_from(start), pointer_from(it)); } // Formats a value. @@ -514,14 +517,14 @@ void format_value(basic_buffer &buf, const T &value, } template -void printf(basic_buffer &buf, basic_cstring_view format, +void printf(basic_buffer &buf, basic_string_view format, basic_args> args) { printf_context(format, args).format(buf); } typedef basic_args> printf_args; -inline std::string vsprintf(cstring_view format, printf_args args) { +inline std::string vsprintf(string_view format, printf_args args) { memory_buffer buffer; printf(buffer, format, args); return to_string(buffer); @@ -537,24 +540,24 @@ inline std::string vsprintf(cstring_view format, printf_args args) { \endrst */ template -inline std::string sprintf(cstring_view format_str, const Args & ... args) { +inline std::string sprintf(string_view format_str, const Args & ... args) { return vsprintf(format_str, make_args>(args...)); } inline std::wstring vsprintf( - wcstring_view format, basic_args> args) { + wstring_view format, basic_args> args) { wmemory_buffer buffer; printf(buffer, format, args); return to_string(buffer); } template -inline std::wstring sprintf(wcstring_view format_str, const Args & ... args) { +inline std::wstring sprintf(wstring_view format_str, const Args & ... args) { auto vargs = make_args>(args...); return vsprintf(format_str, vargs); } -FMT_API int vfprintf(std::FILE *f, cstring_view format, printf_args args); +FMT_API int vfprintf(std::FILE *f, string_view format, printf_args args); /** \rst @@ -566,12 +569,12 @@ FMT_API int vfprintf(std::FILE *f, cstring_view format, printf_args args); \endrst */ template -inline int fprintf(std::FILE *f, cstring_view format_str, const Args & ... args) { +inline int fprintf(std::FILE *f, string_view format_str, const Args & ... args) { auto vargs = make_args>(args...); return vfprintf(f, format_str, vargs); } -inline int vprintf(cstring_view format, printf_args args) { +inline int vprintf(string_view format, printf_args args) { return vfprintf(stdout, format, args); } @@ -585,11 +588,11 @@ inline int vprintf(cstring_view format, printf_args args) { \endrst */ template -inline int printf(cstring_view format_str, const Args & ... args) { +inline int printf(string_view format_str, const Args & ... args) { return vprintf(format_str, make_args>(args...)); } -inline int vfprintf(std::ostream &os, cstring_view format_str, printf_args args) { +inline int vfprintf(std::ostream &os, string_view format_str, printf_args args) { memory_buffer buffer; printf(buffer, format_str, args); internal::write(os, buffer); @@ -606,7 +609,7 @@ inline int vfprintf(std::ostream &os, cstring_view format_str, printf_args args) \endrst */ template -inline int fprintf(std::ostream &os, cstring_view format_str, +inline int fprintf(std::ostream &os, string_view format_str, const Args & ... args) { auto vargs = make_args>(args...); return vfprintf(os, format_str, vargs); diff --git a/fmt/time.h b/fmt/time.h index c4700fbc..02d84c8d 100644 --- a/fmt/time.h +++ b/fmt/time.h @@ -16,17 +16,19 @@ namespace fmt { void format_value(buffer &buf, const std::tm &tm, context &ctx) { - const char *&s = ctx.ptr(); - if (*s == ':') - ++s; - const char *end = s; + auto &it = ctx.pos(); + if (*it == ':') + ++it; + auto end = it; while (*end && *end != '}') ++end; if (*end != '}') FMT_THROW(format_error("missing '}' in format string")); memory_buffer format; - format.append(s, end + 1); - format[format.size() - 1] = '\0'; + format.reserve(end - it + 1); + using internal::pointer_from; + format.append(pointer_from(it), pointer_from(end)); + format.push_back('\0'); std::size_t start = buf.size(); for (;;) { std::size_t size = buf.capacity() - start; @@ -45,7 +47,7 @@ void format_value(buffer &buf, const std::tm &tm, context &ctx) { const std::size_t MIN_GROWTH = 10; buf.reserve(buf.capacity() + (size > MIN_GROWTH ? size : MIN_GROWTH)); } - s = end; + it = end; } } diff --git a/test/custom-formatter-test.cc b/test/custom-formatter-test.cc index 0e976791..e189fa4d 100644 --- a/test/custom-formatter-test.cc +++ b/test/custom-formatter-test.cc @@ -45,7 +45,7 @@ class CustomPrintfArgFormatter : public printf_arg_formatter { } }; -std::string custom_vformat(fmt::cstring_view format_str, fmt::args args) { +std::string custom_vformat(fmt::string_view format_str, fmt::args args) { fmt::memory_buffer buffer; // Pass custom argument formatter as a template arg to vwrite. fmt::vformat_to(buffer, format_str, args); diff --git a/test/format-test.cc b/test/format-test.cc index 7906d96a..49611eb8 100644 --- a/test/format-test.cc +++ b/test/format-test.cc @@ -57,7 +57,6 @@ using fmt::basic_writer; using fmt::format; using fmt::format_error; using fmt::string_view; -using fmt::cstring_view; using fmt::memory_buffer; using fmt::wmemory_buffer; using fmt::fill; @@ -145,11 +144,6 @@ TEST(StringViewTest, ConvertToString) { EXPECT_EQ("abc", s); } -TEST(CStringViewTest, Ctor) { - EXPECT_STREQ("abc", cstring_view("abc").c_str()); - EXPECT_STREQ("defg", cstring_view(std::string("defg")).c_str()); -} - #if FMT_USE_TYPE_TRAITS TEST(WriterTest, NotCopyConstructible) { EXPECT_FALSE(std::is_copy_constructible >::value); @@ -464,7 +458,7 @@ TEST(FormatterTest, ArgErrors) { template struct TestFormat { template - static std::string format(fmt::cstring_view format_str, const Args & ... args) { + static std::string format(fmt::string_view format_str, const Args & ... args) { return TestFormat::format(format_str, N - 1, args...); } }; @@ -472,7 +466,7 @@ struct TestFormat { template <> struct TestFormat<0> { template - static std::string format(fmt::cstring_view format_str, const Args & ... args) { + static std::string format(fmt::string_view format_str, const Args & ... args) { return fmt::format(format_str, args...); } }; @@ -1233,10 +1227,6 @@ TEST(FormatterTest, FormatStringView) { EXPECT_EQ("test", format("{0}", string_view("test"))); } -TEST(FormatterTest, FormatCStringView) { - EXPECT_EQ("test", format("{0}", cstring_view("test"))); -} - void format_value(fmt::buffer &buf, const Date &d, fmt::context &) { fmt::format_to(buf, "{}-{}-{}", d.year(), d.month(), d.day()); } @@ -1512,7 +1502,7 @@ class MockArgFormatter : public fmt::internal::arg_formatter_base { void operator()(fmt::internal::custom_value) {} }; -void custom_vformat(fmt::cstring_view format_str, fmt::args args) { +void custom_vformat(fmt::string_view format_str, fmt::args args) { fmt::memory_buffer buffer; fmt::vformat_to(buffer, format_str, args); } @@ -1526,3 +1516,7 @@ void custom_format(const char *format_str, const Args & ... args) { TEST(FormatTest, CustomArgFormatter) { custom_format("{}", 42); } + +TEST(FormatTest, NonNullTerminatedFormatString) { + EXPECT_EQ("42", format(string_view("{}foo", 2), 42)); +}