diff --git a/include/fmt/core.h b/include/fmt/core.h index 6b956a6a..f893f865 100644 --- a/include/fmt/core.h +++ b/include/fmt/core.h @@ -1098,7 +1098,11 @@ struct format_args: basic_format_args { format_args(Args && ... arg) : basic_format_args(std::forward(arg)...) {} }; -typedef basic_format_args wformat_args; +struct wformat_args : basic_format_args { + template + wformat_args(Args && ... arg) + : basic_format_args(std::forward(arg)...) {} +}; namespace internal { template @@ -1154,6 +1158,7 @@ void arg(S, internal::named_arg) FMT_DELETED; enum color { black, red, green, yellow, blue, magenta, cyan, white }; FMT_API void vprint_colored(color c, string_view format, format_args args); +FMT_API void vprint_colored(color c, wstring_view format, wformat_args args); /** Formats a string and prints it to stdout using ANSI escape sequences to @@ -1167,6 +1172,12 @@ inline void print_colored(color c, string_view format_str, vprint_colored(c, format_str, make_format_args(args...)); } +template +inline void print_colored(color c, wstring_view format_str, + const Args & ... args) { + vprint_colored(c, format_str, make_format_args(args...)); +} + format_context::iterator vformat_to( internal::buffer &buf, string_view format_str, format_args args); wformat_context::iterator vformat_to( @@ -1193,6 +1204,17 @@ typename std::enable_if< return std::back_inserter(container); } +template +typename std::enable_if< + is_contiguous::value, std::back_insert_iterator>::type + vformat_to(std::back_insert_iterator out, + wstring_view format_str, wformat_args args) { + auto& container = internal::get_container(out); + internal::container_buffer buf(container); + vformat_to(buf, format_str, args); + return std::back_inserter(container); +} + std::string vformat(string_view format_str, format_args args); std::wstring vformat(wstring_view format_str, wformat_args args); @@ -1221,6 +1243,7 @@ inline std::wstring format(wstring_view format_str, const Args & ... args) { } FMT_API void vprint(std::FILE *f, string_view format_str, format_args args); +FMT_API void vprint(std::FILE *f, wstring_view format_str, wformat_args args); /** \rst @@ -1236,8 +1259,14 @@ inline void print(std::FILE *f, string_view format_str, const Args & ... args) { format_arg_store as(args...); vprint(f, format_str, as); } +template +inline void print(std::FILE *f, wstring_view format_str, const Args & ... args) { + format_arg_store as(args...); + vprint(f, format_str, as); +} FMT_API void vprint(string_view format_str, format_args args); +FMT_API void vprint(wstring_view format_str, wformat_args args); /** \rst @@ -1253,6 +1282,12 @@ inline void print(string_view format_str, const Args & ... args) { format_arg_store as(args...); vprint(format_str, as); } + +template +inline void print(wstring_view format_str, const Args & ... args) { + format_arg_store as(args...); + vprint(format_str, as); +} } // namespace fmt #endif // FMT_CORE_H_ diff --git a/include/fmt/format-inl.h b/include/fmt/format-inl.h index 52111a13..75b62ddc 100644 --- a/include/fmt/format-inl.h +++ b/include/fmt/format-inl.h @@ -96,6 +96,7 @@ inline int fmt_snprintf(char *buffer, size_t size, const char *format, ...) { #endif // defined(_WIN32) && defined(__MINGW32__) && !defined(__NO_ISOCEXT) const char RESET_COLOR[] = "\x1b[0m"; +const wchar_t WRESET_COLOR[] = L"\x1b[0m"; typedef void (*FormatFunc)(internal::buffer &, int, string_view); @@ -489,10 +490,20 @@ FMT_FUNC void vprint(std::FILE *f, string_view format_str, format_args args) { std::fwrite(buffer.data(), 1, buffer.size(), f); } +FMT_FUNC void vprint(std::FILE *f, wstring_view format_str, wformat_args args) { + wmemory_buffer buffer; + vformat_to(buffer, format_str, args); + std::fwrite(buffer.data(), sizeof(wchar_t), buffer.size(), f); +} + FMT_FUNC void vprint(string_view format_str, format_args args) { vprint(stdout, format_str, args); } +FMT_FUNC void vprint(wstring_view format_str, wformat_args args) { + vprint(stdout, format_str, args); +} + FMT_FUNC void vprint_colored(color c, string_view format, format_args args) { char escape[] = "\x1b[30m"; escape[3] = static_cast('0' + c); @@ -501,6 +512,14 @@ FMT_FUNC void vprint_colored(color c, string_view format, format_args args) { std::fputs(RESET_COLOR, stdout); } +FMT_FUNC void vprint_colored(color c, wstring_view format, wformat_args args) { + wchar_t escape[] = L"\x1b[30m"; + escape[3] = static_cast('0' + c); + std::fputws(escape, stdout); + vprint(format, args); + std::fputws(WRESET_COLOR, stdout); +} + FMT_FUNC locale locale_provider::locale() { return fmt::locale(); } } // namespace fmt diff --git a/include/fmt/format.h b/include/fmt/format.h index 267cca0d..89264541 100644 --- a/include/fmt/format.h +++ b/include/fmt/format.h @@ -3419,7 +3419,15 @@ inline typename std::enable_if< is_contiguous::value, std::back_insert_iterator>::type format_to(std::back_insert_iterator out, string_view format_str, const Args & ... args) { - return vformat_to(out, format_str, make_format_args(args...)); + return vformat_to(out, format_str, make_format_args(args...)); +} + +template +inline typename std::enable_if< + is_contiguous::value, std::back_insert_iterator>::type + format_to(std::back_insert_iterator out, + wstring_view format_str, const Args & ... args) { + return vformat_to(out, format_str, make_format_args(args...)); } template diff --git a/include/fmt/ostream.h b/include/fmt/ostream.h index b8a75b3a..55f534de 100644 --- a/include/fmt/ostream.h +++ b/include/fmt/ostream.h @@ -121,12 +121,14 @@ struct formatter +inline void vprint(std::basic_ostream &os, + basic_string_view format_str, + basic_format_args::type> args) { + basic_memory_buffer buffer; vformat_to(buffer, format_str, args); internal::write(os, buffer); } - /** \rst Prints formatted data to the stream *os*. @@ -139,8 +141,15 @@ inline void vprint(std::ostream &os, string_view format_str, format_args args) { template inline void print(std::ostream &os, string_view format_str, const Args & ... args) { - vprint(os, format_str, make_format_args(args...)); + vprint(os, format_str, make_format_args(args...)); } + +template +inline void print(std::wostream &os, wstring_view format_str, + const Args & ... args) { + vprint(os, format_str, make_format_args(args...)); +} + } // namespace fmt #endif // FMT_OSTREAM_H_ diff --git a/include/fmt/printf.h b/include/fmt/printf.h index 49586ef9..43fe65f2 100644 --- a/include/fmt/printf.h +++ b/include/fmt/printf.h @@ -151,7 +151,8 @@ class char_converter: public function { template typename std::enable_if::value>::type operator()(T value) { - arg_ = internal::make_arg(static_cast(value)); + typedef typename Context::char_type Char; + arg_ = internal::make_arg(static_cast(value)); } template @@ -223,11 +224,16 @@ class printf_arg_formatter: context_type &context_; - void write_null_pointer() { + void write_null_pointer(char) { this->spec().type_ = 0; this->write("(nil)"); } + void write_null_pointer(wchar_t) { + this->spec().type_ = 0; + this->write(L"(nil)"); + } + public: typedef typename base::format_specs format_specs; @@ -270,18 +276,29 @@ class printf_arg_formatter: if (value) base::operator()(value); else if (this->spec().type_ == 'p') - write_null_pointer(); + write_null_pointer(char_type()); else this->write("(null)"); return this->out(); } + /** Formats a null-terminated wide C string. */ + iterator operator()(const wchar_t *value) { + if (value) + base::operator()(value); + else if (this->spec().type_ == 'p') + write_null_pointer(char_type()); + else + this->write(L"(null)"); + return this->out(); + } + /** Formats a pointer. */ iterator operator()(const void *value) { if (value) return base::operator()(value); this->spec().type_ = 0; - write_null_pointer(); + write_null_pointer(char_type()); return this->out(); } @@ -518,7 +535,7 @@ void basic_printf_context::format() { spec.type_ = 'd'; break; case 'c': - // TODO: handle wchar_t + // TODO: handle wchar_t better? visit(internal::char_converter(arg), arg); break; } @@ -551,6 +568,7 @@ inline format_arg_store::type, Args...> args...); } typedef basic_format_args::type> printf_args; +typedef basic_format_args::type> wprintf_args; inline std::string vsprintf(string_view format, printf_args args) { memory_buffer buffer; @@ -573,9 +591,7 @@ inline std::string sprintf(string_view format_str, const Args & ... args) { make_format_args::type>(args...)); } -inline std::wstring vsprintf( - wstring_view format, - basic_format_args::type> args) { +inline std::wstring vsprintf(wstring_view format, wprintf_args args) { wmemory_buffer buffer; printf(buffer, format, args); return to_string(buffer); @@ -583,17 +599,19 @@ inline std::wstring vsprintf( template inline std::wstring sprintf(wstring_view format_str, const Args & ... args) { - auto vargs = make_format_args< - typename printf_context::type>(args...); - return vsprintf(format_str, vargs); + return vsprintf(format_str, + make_format_args::type>(args...)); } -inline int vfprintf(std::FILE *f, string_view format, printf_args args) { - memory_buffer buffer; +template +inline int vfprintf(std::FILE *f, basic_string_view format, + basic_format_args>::type> args) { + basic_memory_buffer buffer; printf(buffer, format, args); std::size_t size = buffer.size(); return std::fwrite( - buffer.data(), 1, size, f) < size ? -1 : static_cast(size); + buffer.data(), sizeof(Char), size, f) < size ? -1 : static_cast(size); } /** @@ -612,10 +630,21 @@ inline int fprintf(std::FILE *f, string_view format_str, const Args & ... args) return vfprintf(f, format_str, vargs); } +template +inline int fprintf(std::FILE *f, wstring_view format_str, + const Args & ... args) { + return vfprintf(f, format_str, + make_format_args::type>(args...)); +} + inline int vprintf(string_view format, printf_args args) { return vfprintf(stdout, format, args); } +inline int vprintf(wstring_view format, wprintf_args args) { + return vfprintf(stdout, format, args); +} + /** \rst Prints formatted data to ``stdout``. @@ -631,6 +660,12 @@ inline int printf(string_view format_str, const Args & ... args) { make_format_args::type>(args...)); } +template +inline int printf(wstring_view format_str, const Args & ... args) { + return vprintf(format_str, + make_format_args::type>(args...)); +} + inline int vfprintf(std::ostream &os, string_view format_str, printf_args args) { memory_buffer buffer; @@ -639,6 +674,14 @@ inline int vfprintf(std::ostream &os, string_view format_str, return static_cast(buffer.size()); } +inline int vfprintf(std::wostream &os, wstring_view format_str, + wprintf_args args) { + wmemory_buffer buffer; + printf(buffer, format_str, args); + internal::write(os, buffer); + return static_cast(buffer.size()); +} + /** \rst Prints formatted data to the stream *os*. @@ -655,6 +698,14 @@ inline int fprintf(std::ostream &os, string_view format_str, typename printf_context::type>(args...); return vfprintf(os, format_str, vargs); } + +template +inline int fprintf(std::wostream &os, wstring_view format_str, + const Args & ... args) { + auto vargs = make_format_args< + typename printf_context::type>(args...); + return vfprintf(os, format_str, vargs); +} } // namespace fmt #endif // FMT_PRINTF_H_ diff --git a/src/format.cc b/src/format.cc index 41746612..23747bc2 100644 --- a/src/format.cc +++ b/src/format.cc @@ -34,7 +34,8 @@ template FMT_API wchar_t internal::thousands_sep(locale_provider *lp); template void basic_fixed_buffer::grow(std::size_t); -template void internal::arg_map::init(const wformat_args &args); +template void internal::arg_map::init( + const basic_format_args &args); template FMT_API int internal::char_traits::format_float( wchar_t *buffer, std::size_t size, const wchar_t *format, diff --git a/test/ostream-test.cc b/test/ostream-test.cc index 1570f5ff..bdb81651 100644 --- a/test/ostream-test.cc +++ b/test/ostream-test.cc @@ -30,12 +30,19 @@ std::ostream &operator<<(std::ostream &os, TestEnum) { return os << "TestEnum"; } +std::wostream &operator<<(std::wostream &os, TestEnum) { + return os << L"TestEnum"; +} + enum TestEnum2 {A}; TEST(OStreamTest, Enum) { EXPECT_FALSE((fmt::internal::convert_to_int::value)); EXPECT_EQ("TestEnum", fmt::format("{}", TestEnum())); EXPECT_EQ("0", fmt::format("{}", A)); + EXPECT_FALSE((fmt::internal::convert_to_int::value)); + EXPECT_EQ(L"TestEnum", fmt::format(L"{}", TestEnum())); + EXPECT_EQ(L"0", fmt::format(L"{}", A)); } typedef fmt::back_insert_range range; @@ -100,6 +107,9 @@ TEST(OStreamTest, Print) { std::ostringstream os; fmt::print(os, "Don't {}!", "panic"); EXPECT_EQ("Don't panic!", os.str()); + std::wostringstream wos; + fmt::print(wos, L"Don't {}!", L"panic"); + EXPECT_EQ(L"Don't panic!", wos.str()); } TEST(OStreamTest, WriteToOStream) { diff --git a/test/printf-test.cc b/test/printf-test.cc index c6e56700..3f877898 100644 --- a/test/printf-test.cc +++ b/test/printf-test.cc @@ -26,6 +26,12 @@ std::string make_positional(fmt::string_view format) { return s; } +std::wstring make_positional(fmt::wstring_view format) { + std::wstring s(format.data(), format.size()); + s.replace(s.find(L'%'), 1, L"%1$"); + return s; +} + #define EXPECT_PRINTF(expected_output, format, arg) \ EXPECT_EQ(expected_output, fmt::sprintf(format, arg)) \ << "format: " << format; \ @@ -33,6 +39,7 @@ std::string make_positional(fmt::string_view format) { TEST(PrintfTest, NoArgs) { EXPECT_EQ("test", fmt::sprintf("test")); + EXPECT_EQ(L"test", fmt::sprintf(L"test")); } TEST(PrintfTest, Escape) { @@ -41,6 +48,11 @@ TEST(PrintfTest, Escape) { EXPECT_EQ("% after", fmt::sprintf("%% after")); EXPECT_EQ("before % after", fmt::sprintf("before %% after")); EXPECT_EQ("%s", fmt::sprintf("%%s")); + EXPECT_EQ(L"%", fmt::sprintf(L"%%")); + EXPECT_EQ(L"before %", fmt::sprintf(L"before %%")); + EXPECT_EQ(L"% after", fmt::sprintf(L"%% after")); + EXPECT_EQ(L"before % after", fmt::sprintf(L"before %% after")); + EXPECT_EQ(L"%s", fmt::sprintf(L"%%s")); } TEST(PrintfTest, PositionalArgs) { @@ -408,7 +420,8 @@ TEST(PrintfTest, Char) { int max = std::numeric_limits::max(); EXPECT_PRINTF(fmt::format("{}", static_cast(max)), "%c", max); //EXPECT_PRINTF("x", "%lc", L'x'); - // TODO: test wchar_t + EXPECT_PRINTF(L"x", L"%c", L'x'); + EXPECT_PRINTF(fmt::format(L"{}", static_cast(max)), L"%c", max); } TEST(PrintfTest, String) { @@ -416,7 +429,10 @@ TEST(PrintfTest, String) { const char *null_str = 0; EXPECT_PRINTF("(null)", "%s", null_str); EXPECT_PRINTF(" (null)", "%10s", null_str); - // TODO: wide string + EXPECT_PRINTF(L"abc", L"%s", L"abc"); + const wchar_t *null_wstr = 0; + EXPECT_PRINTF(L"(null)", L"%s", null_wstr); + EXPECT_PRINTF(L" (null)", L"%10s", null_wstr); } TEST(PrintfTest, Pointer) { @@ -430,6 +446,16 @@ TEST(PrintfTest, Pointer) { EXPECT_PRINTF(fmt::format("{:p}", s), "%p", s); const char *null_str = 0; EXPECT_PRINTF("(nil)", "%p", null_str); + + p = &n; + EXPECT_PRINTF(fmt::format(L"{}", p), L"%p", p); + p = 0; + EXPECT_PRINTF(L"(nil)", L"%p", p); + EXPECT_PRINTF(L" (nil)", L"%10p", p); + const wchar_t *w = L"test"; + EXPECT_PRINTF(fmt::format(L"{:p}", w), L"%p", w); + const wchar_t *null_wstr = 0; + EXPECT_PRINTF(L"(nil)", L"%p", null_wstr); } TEST(PrintfTest, Location) {