diff --git a/README.rst b/README.rst index a55318f1..714c5cbd 100644 --- a/README.rst +++ b/README.rst @@ -34,6 +34,7 @@ Features for older compilers. * Clean warning-free codebase even on high warning levels (-Wall -Wextra -pedantic). +* Support for wide strings. See the `documentation `__ for more details. diff --git a/format.h b/format.h index c4900217..d32ea51b 100644 --- a/format.h +++ b/format.h @@ -105,6 +105,41 @@ inline int IsInf(double x) { return !_finite(x); } #endif // _MSC_VER +template +struct CharTraits; + +template <> +struct CharTraits { + template + static int FormatFloat(char *buffer, std::size_t size, + const char *format, unsigned width, int precision, T value) { + if (width == 0) { + return precision < 0 ? + FMT_SNPRINTF(buffer, size, format, value) : + FMT_SNPRINTF(buffer, size, format, precision, value); + } + return precision < 0 ? + FMT_SNPRINTF(buffer, size, format, width, value) : + FMT_SNPRINTF(buffer, size, format, width, precision, value); + } +}; + +template <> +struct CharTraits { + template + static int FormatFloat(wchar_t *buffer, std::size_t size, + const wchar_t *format, unsigned width, int precision, T value) { + if (width == 0) { + return precision < 0 ? + swprintf(buffer, size, format, value) : + swprintf(buffer, size, format, precision, value); + } + return precision < 0 ? + swprintf(buffer, size, format, width, value) : + swprintf(buffer, size, format, width, precision, value); + } +}; + // A simple array for POD types with the first SIZE elements stored in // the object itself. It supports a subset of std::vector's operations. template @@ -244,9 +279,10 @@ class FormatterProxy; Format(Format("{{}}")) << 42; \endrst */ -class StringRef { +template +class BasicStringRef { private: - const char *data_; + const Char *data_; mutable std::size_t size_; public: @@ -255,32 +291,38 @@ class StringRef { If `size` is zero, which is the default, the size is computed with `strlen`. */ - StringRef(const char *s, std::size_t size = 0) : data_(s), size_(size) {} + BasicStringRef(const Char *s, std::size_t size = 0) : data_(s), size_(size) {} /** Constructs a string reference from an `std::string` object. */ - StringRef(const std::string &s) : data_(s.c_str()), size_(s.size()) {} + BasicStringRef(const std::basic_string &s) + : data_(s.c_str()), size_(s.size()) {} /** Converts a string reference to an `std::string` object. */ - operator std::string() const { return std::string(data_, size()); } + operator std::basic_string() const { + return std::basic_string(data_, size()); + } /** Returns the pointer to a C string. */ - const char *c_str() const { return data_; } + const Char *c_str() const { return data_; } /** Returns the string size. */ std::size_t size() const { - if (size_ == 0) size_ = std::strlen(data_); + if (size_ == 0) size_ = std::char_traits::length(data_); return size_; } }; +typedef BasicStringRef StringRef; +typedef BasicStringRef WStringRef; + class FormatError : public std::runtime_error { public: explicit FormatError(const std::string &message) @@ -729,8 +771,8 @@ void BasicWriter::FormatDouble( // Build format string. enum { MAX_FORMAT_SIZE = 10}; // longest format: %#-*.*Lg - char format[MAX_FORMAT_SIZE]; - char *format_ptr = format; + Char format[MAX_FORMAT_SIZE]; + Char *format_ptr = format; *format_ptr++ = '%'; unsigned width_for_sprintf = width; if (spec.hash_flag()) @@ -755,18 +797,9 @@ void BasicWriter::FormatDouble( // Format using snprintf. for (;;) { std::size_t size = buffer_.capacity() - offset; - int n = 0; Char *start = &buffer_[offset]; - if (width_for_sprintf == 0) { - n = precision < 0 ? - FMT_SNPRINTF(start, size, format, value) : - FMT_SNPRINTF(start, size, format, precision, value); - } else { - n = precision < 0 ? - FMT_SNPRINTF(start, size, format, width_for_sprintf, value) : - FMT_SNPRINTF(start, size, format, width_for_sprintf, - precision, value); - } + int n = internal::CharTraits::FormatFloat( + start, size, format, width_for_sprintf, precision, value); if (n >= 0 && offset + n < buffer_.capacity()) { if (sign) { if ((spec.align() != ALIGN_RIGHT && spec.align() != ALIGN_DEFAULT) || @@ -953,7 +986,8 @@ class BasicFormatter { // This method is private to disallow formatting of wide characters. // If you want to output a wide character cast it to integer type. // Do not implement! - Arg(wchar_t value); + // TODO + //Arg(wchar_t value); public: Type type; @@ -1048,14 +1082,14 @@ class BasicFormatter { args_.push_back(&arg); } - void ReportError(const char *s, StringRef message) const; + void ReportError(const Char *s, StringRef message) const; - unsigned ParseUInt(const char *&s) const; + unsigned ParseUInt(const Char *&s) const; // Parses argument index and returns an argument with this index. - const Arg &ParseArgIndex(const char *&s); + const Arg &ParseArgIndex(const Char *&s); - void CheckSign(const char *&s, const Arg &arg); + void CheckSign(const Char *&s, const Arg &arg); void DoFormat(); @@ -1122,14 +1156,8 @@ inline std::basic_string str(const BasicWriter &f) { template inline const Char *c_str(const BasicWriter &f) { return f.c_str(); } -std::string str(internal::FormatterProxy p); -const char *c_str(internal::FormatterProxy p); - namespace internal { -using fmt::str; -using fmt::c_str; - template class FormatterProxy { private: @@ -1160,6 +1188,14 @@ inline const char *c_str(internal::FormatterProxy p) { return p.Format()->c_str(); } +inline std::wstring str(internal::FormatterProxy p) { + return p.Format()->str(); +} + +inline const wchar_t *c_str(internal::FormatterProxy p) { + return p.Format()->c_str(); +} + /** A formatting action that does nothing. */ @@ -1181,7 +1217,7 @@ class NoAction { struct PrintError { void operator()(const fmt::Writer &w) const { - std::cerr << "Error: " << w.str() << std::endl; + fmt::Print("Error: {}\n") << w.str(); } }; @@ -1204,10 +1240,10 @@ class Formatter : private Action, public BasicFormatter { Formatter& operator=(const Formatter &); struct Proxy { - const char *format; + const Char *format; Action action; - Proxy(const char *fmt, Action a) : format(fmt), action(a) {} + Proxy(const Char *fmt, Action a) : format(fmt), action(a) {} }; public: @@ -1220,7 +1256,7 @@ class Formatter : private Action, public BasicFormatter { examples of action classes. \endrst */ - explicit Formatter(StringRef format, Action a = Action()) + explicit Formatter(BasicStringRef format, Action a = Action()) : Action(a), BasicFormatter(writer_, format.c_str()), inactive_(false) { } @@ -1277,6 +1313,10 @@ inline Formatter<> Format(StringRef format) { return Formatter<>(format); } +inline Formatter Format(WStringRef format) { + return Formatter(format); +} + /** A formatting action that writes formatted output to stdout. */ class Write { public: @@ -1297,7 +1337,7 @@ inline Formatter Print(StringRef format) { // FormatError reporting unmatched '{'. The idea is that unmatched '{' // should override other errors. template -void BasicFormatter::ReportError(const char *s, StringRef message) const { +void BasicFormatter::ReportError(const Char *s, StringRef message) const { for (int num_open_braces = num_open_braces_; *s; ++s) { if (*s == '{') { ++num_open_braces; @@ -1312,7 +1352,7 @@ void BasicFormatter::ReportError(const char *s, StringRef message) const { // 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 BasicFormatter::ParseUInt(const char *&s) const { +unsigned BasicFormatter::ParseUInt(const Char *&s) const { assert('0' <= *s && *s <= '9'); unsigned value = 0; do { @@ -1326,7 +1366,7 @@ unsigned BasicFormatter::ParseUInt(const char *&s) const { template inline const typename BasicFormatter::Arg - &BasicFormatter::ParseArgIndex(const char *&s) { + &BasicFormatter::ParseArgIndex(const Char *&s) { unsigned arg_index = 0; if (*s < '0' || *s > '9') { if (*s != '}' && *s != ':') @@ -1350,7 +1390,7 @@ inline const typename BasicFormatter::Arg } template -void BasicFormatter::CheckSign(const char *&s, const Arg &arg) { +void BasicFormatter::CheckSign(const Char *&s, const Arg &arg) { if (arg.type > LAST_NUMERIC_TYPE) { ReportError(s, Format("format specifier '{0}' requires numeric argument") << *s); @@ -1369,7 +1409,7 @@ void BasicFormatter::DoFormat() { next_arg_index_ = 0; const Char *s = start; typedef internal::Array::INLINE_BUFFER_SIZE> Buffer; - Writer &writer = *writer_; + BasicWriter &writer = *writer_; while (*s) { char c = *s++; if (c != '{' && c != '}') continue; @@ -1392,7 +1432,7 @@ void BasicFormatter::DoFormat() { // Parse fill and alignment. if (char c = *s) { - const char *p = s + 1; + const Char *p = s + 1; spec.align_ = ALIGN_DEFAULT; do { switch (*p) { diff --git a/format_test.cc b/format_test.cc index 6c28e1dd..2c82be97 100644 --- a/format_test.cc +++ b/format_test.cc @@ -1028,6 +1028,10 @@ TEST(FormatterTest, CustomFormat) { EXPECT_EQ("42", str(Format("{0}") << Answer())); } +TEST(FormatterTest, WideFormatString) { + EXPECT_EQ(L"42", str(Format(L"{}") << 42)); +} + TEST(FormatterTest, FormatStringFromSpeedTest) { EXPECT_EQ("1.2340000000:0042:+3.13:str:0x3e8:X:%", str(Format("{0:0.10f}:{1:04}:{2:+g}:{3}:{4}:{5}:%")