From 0dd91e20d5e8c7c41154acbb4fbe6b9d37688ea3 Mon Sep 17 00:00:00 2001 From: Victor Zverovich Date: Mon, 17 May 2021 20:47:38 -0700 Subject: [PATCH] Add wchar.h for wide char overloads --- CMakeLists.txt | 3 +- include/fmt/core.h | 77 ++++++++++++++++++++++---------------------- include/fmt/format.h | 10 ++++++ include/fmt/wchar.h | 39 ++++++++++++++++++++++ test/CMakeLists.txt | 1 + test/core-test.cc | 5 --- test/format-test.cc | 2 -- test/wchar-test.cc | 15 +++++++++ 8 files changed, 105 insertions(+), 47 deletions(-) create mode 100644 include/fmt/wchar.h create mode 100644 test/wchar-test.cc diff --git a/CMakeLists.txt b/CMakeLists.txt index b2985a7d..a74ba6db 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -187,7 +187,8 @@ endfunction() # Define the fmt library, its includes and the needed defines. add_headers(FMT_HEADERS args.h chrono.h color.h compile.h core.h format.h - format-inl.h locale.h os.h ostream.h printf.h ranges.h) + format-inl.h locale.h os.h ostream.h printf.h ranges.h + wchar.h) if (FMT_OS) set(FMT_SOURCES src/format.cc src/os.cc) else() diff --git a/include/fmt/core.h b/include/fmt/core.h index 8cb07584..60d1e764 100644 --- a/include/fmt/core.h +++ b/include/fmt/core.h @@ -392,8 +392,8 @@ FMT_CONSTEXPR typename std::make_unsigned::type to_unsigned(Int value) { FMT_MSC_WARNING(suppress : 4566) constexpr unsigned char micro[] = "\u00B5"; -template constexpr bool is_unicode() { - return FMT_UNICODE || sizeof(Char) != 1 || +constexpr bool is_utf8() { + return FMT_UNICODE || (sizeof(micro) == 3 && micro[0] == 0xC2 && micro[1] == 0xB5); } FMT_END_DETAIL_NAMESPACE @@ -2895,12 +2895,12 @@ FMT_INLINE auto vformat( return detail::vformat(to_string_view(format_str), args); } -#if FMT_COMPILE_TIME_CHECKS template class basic_format_string { private: basic_string_view str; public: +#if FMT_COMPILE_TIME_CHECKS template consteval basic_format_string(const char (&s)[N]) : str(s) { if constexpr (detail::count_named_args() == 0) { @@ -2909,21 +2909,29 @@ template class basic_format_string { detail::parse_format_string(string_view(s, N), checker(s, {})); } } +#endif template )> - basic_format_string(const T& s) : str(s) {} + FMT_ENABLE_IF(std::is_constructible, + const T&>::value)> + basic_format_string(const T& s) : str(s) { + static_assert( + detail::count< + (std::is_base_of>::value && + std::is_reference::value)...>() == 0, + "passing views as lvalues is disallowed"); + detail::check_format_string(s); + } - operator basic_string_view() const { return str; } + FMT_INLINE operator basic_string_view() const { return str; } }; +#if FMT_GCC_VERSION && FMT_GCC_VERSION < 409 +// Workaround broken conversion on older gcc. +template using format_string = string_view; +#else template -using format_string = basic_format_string...>; - -template -FMT_INLINE auto format(format_string str, T&&... args) -> std::string { - return detail::vformat(str, make_format_args(args...)); -} +using format_string = basic_format_string...>; #endif /** @@ -2936,15 +2944,9 @@ FMT_INLINE auto format(format_string str, T&&... args) -> std::string { std::string message = fmt::format("The answer is {}", 42); \endrst */ -// Pass char_t as a default template parameter instead of using -// std::basic_string> to reduce the symbol size. -template , - FMT_ENABLE_IF(!FMT_COMPILE_TIME_CHECKS || - !std::is_same::value)> -FMT_INLINE auto format(const S& format_str, Args&&... args) - -> std::basic_string { - const auto& vargs = fmt::make_args_checked(format_str, args...); - return detail::vformat(to_string_view(format_str), vargs); +template +FMT_INLINE auto format(format_string str, T&&... args) -> std::string { + return detail::vformat(str, make_format_args(args...)); } FMT_API void vprint(string_view, format_args); @@ -2952,8 +2954,8 @@ FMT_API void vprint(std::FILE*, string_view, format_args); /** \rst - Formats ``args`` according to specifications in ``format_str`` and writes the - output to the file ``f``. Strings are assumed to be Unicode-encoded unless the + Formats ``args`` according to specifications in ``str`` and writes the + output to the file ``f``. Strings are assumed to be in UTF-8 unless the ``FMT_UNICODE`` macro is set to 0. **Example**:: @@ -2961,32 +2963,29 @@ FMT_API void vprint(std::FILE*, string_view, format_args); fmt::print(stderr, "Don't {}!", "panic"); \endrst */ -template > -inline void print(std::FILE* f, const S& format_str, Args&&... args) { - const auto& vargs = fmt::make_args_checked(format_str, args...); - return detail::is_unicode() - ? vprint(f, to_string_view(format_str), vargs) - : detail::vprint_mojibake(f, to_string_view(format_str), vargs); +template +inline void print(std::FILE* f, format_string str, T&&... args) { + const auto& vargs = make_format_args(args...); + return detail::is_utf8() ? vprint(f, str, vargs) + : detail::vprint_mojibake(f, str, vargs); } /** \rst - Formats ``args`` according to specifications in ``format_str`` and writes - the output to ``stdout``. Strings are assumed to be Unicode-encoded unless - the ``FMT_UNICODE`` macro is set to 0. + Formats ``args`` according to specifications in ``str`` and writes the output + to ``stdout``. Strings are assumed to be in UTF-8 unless the ``FMT_UNICODE`` + macro is set to 0. **Example**:: fmt::print("Elapsed time: {0:.2f} seconds", 1.23); \endrst */ -template > -inline void print(const S& format_str, Args&&... args) { - const auto& vargs = fmt::make_args_checked(format_str, args...); - return detail::is_unicode() - ? vprint(to_string_view(format_str), vargs) - : detail::vprint_mojibake(stdout, to_string_view(format_str), - vargs); +template +inline void print(format_string str, T&&... args) { + const auto& vargs = make_format_args(args...); + return detail::is_utf8() ? vprint(str, vargs) + : detail::vprint_mojibake(stdout, str, vargs); } FMT_MODULE_EXPORT_END diff --git a/include/fmt/format.h b/include/fmt/format.h index 7a61bd3c..44f90489 100644 --- a/include/fmt/format.h +++ b/include/fmt/format.h @@ -2658,6 +2658,16 @@ auto detail::vformat( return to_string(buffer); } +// Pass char_t as a default template parameter instead of using +// std::basic_string> to reduce the symbol size. +template , + FMT_ENABLE_IF(!std::is_same::value)> +FMT_INLINE auto format(const S& format_str, Args&&... args) + -> std::basic_string { + const auto& vargs = fmt::make_args_checked(format_str, args...); + return detail::vformat(to_string_view(format_str), vargs); +} + template ::value)> void vprint(std::FILE* f, basic_string_view format_str, wformat_args args) { diff --git a/include/fmt/wchar.h b/include/fmt/wchar.h new file mode 100644 index 00000000..8b447ef7 --- /dev/null +++ b/include/fmt/wchar.h @@ -0,0 +1,39 @@ +// Formatting library for C++ - experimental wchar_t support +// +// Copyright (c) 2012 - present, Victor Zverovich +// All rights reserved. +// +// For the license information refer to format.h. + +#ifndef FMT_WCHAR_H_ +#define FMT_WCHAR_H_ + +#include "format.h" + +namespace fmt { + +#if FMT_GCC_VERSION && FMT_GCC_VERSION < 409 +// Workaround broken conversion on older gcc. +template using wformat_string = wstring_view; +#else +template +using wformat_string = basic_format_string...>; +#endif + +template +constexpr format_arg_store make_wformat_args( + const Args&... args) { + return {args...}; +} + +template +void print(std::FILE* f, wformat_string str, T&&... args) { + return vprint(f, wstring_view(str), make_wformat_args(args...)); +} + +template void print(wformat_string str, T&&... args) { + return vprint(wstring_view(str), make_wformat_args(args...)); +} +} // namespace fmt + +#endif // FMT_WCHAR_H_ diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index f1dcf16a..39a088cb 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -72,6 +72,7 @@ add_fmt_test(compile-test) add_fmt_test(printf-test) add_fmt_test(ranges-test) add_fmt_test(scan-test) +add_fmt_test(wchar-test) if (NOT MSVC) # FMT_ENFORCE_COMPILE_STRING is not supported under MSVC due to compiler bugs. diff --git a/test/core-test.cc b/test/core-test.cc index 76b20a3e..7c861684 100644 --- a/test/core-test.cc +++ b/test/core-test.cc @@ -590,11 +590,6 @@ TEST(core_test, to_string_view_foreign_strings) { EXPECT_EQ(type, fmt::detail::type::string_type); } -TEST(core_test, format_foreign_strings) { - using namespace test_ns; - EXPECT_EQ(fmt::format(test_string("{}"), 42), "42"); -} - struct implicitly_convertible_to_string { operator std::string() const { return "foo"; } }; diff --git a/test/format-test.cc b/test/format-test.cc index 997b628f..e5f3d98c 100644 --- a/test/format-test.cc +++ b/test/format-test.cc @@ -1580,8 +1580,6 @@ TEST(format_test, print) { EXPECT_WRITE(stdout, fmt::print("Don't {}!", "panic"), "Don't panic!"); EXPECT_WRITE(stderr, fmt::print(stderr, "Don't {}!", "panic"), "Don't panic!"); - // Check that the wide print overload compiles. - if (fmt::detail::const_check(false)) fmt::print(L"test"); } TEST(format_test, variadic) { diff --git a/test/wchar-test.cc b/test/wchar-test.cc new file mode 100644 index 00000000..aaac87cf --- /dev/null +++ b/test/wchar-test.cc @@ -0,0 +1,15 @@ +// Formatting library for C++ - formatting library tests +// +// Copyright (c) 2012 - present, Victor Zverovich +// All rights reserved. +// +// For the license information refer to format.h. + +#include "fmt/wchar.h" + +#include "gtest/gtest.h" + +TEST(wchar_test, print) { + // Check that the wide print overload compiles. + if (fmt::detail::const_check(false)) fmt::print(L"test"); +}