diff --git a/include/fmt/chrono.h b/include/fmt/chrono.h index 39b5d33c..3cc4b1aa 100644 --- a/include/fmt/chrono.h +++ b/include/fmt/chrono.h @@ -15,7 +15,6 @@ #include #include "format.h" -#include "locale.h" FMT_BEGIN_NAMESPACE diff --git a/include/fmt/color.h b/include/fmt/color.h index 4fadb596..7fa5490e 100644 --- a/include/fmt/color.h +++ b/include/fmt/color.h @@ -582,8 +582,8 @@ inline std::basic_string vformat( template > inline std::basic_string format(const text_style& ts, const S& format_str, const Args&... args) { - return vformat(ts, to_string_view(format_str), - fmt::make_args_checked(format_str, args...)); + return fmt::vformat(ts, to_string_view(format_str), + fmt::make_args_checked(format_str, args...)); } /** diff --git a/include/fmt/format.h b/include/fmt/format.h index 2a342e90..5a2ff985 100644 --- a/include/fmt/format.h +++ b/include/fmt/format.h @@ -624,6 +624,11 @@ void iterator_buffer::flush() { this->clear(); out_ = copy_str(data_, data_ + this->limit(size), out_); } + +template +struct is_locale : std::false_type {}; +template +struct is_locale> : std::true_type {}; } // namespace detail FMT_MODULE_EXPORT_BEGIN @@ -2208,6 +2213,15 @@ template struct udl_arg { # endif #endif // FMT_USE_USER_DEFINED_LITERALS +template +std::basic_string vformat( + const Locale& loc, basic_string_view format_str, + basic_format_args>> args) { + basic_memory_buffer buffer; + detail::vformat_to(buffer, format_str, args, detail::locale_ref(loc)); + return {buffer.data(), buffer.size()}; +} + using format_func = void (*)(detail::buffer&, int, const char*); FMT_API void format_error_code(buffer& out, int error_code, @@ -2771,6 +2785,19 @@ auto format(const S& format_str, Args&&... args) -> std::basic_string { return vformat(to_string_view(format_str), vargs); } +template ::value)> +inline std::string vformat(const Locale& loc, string_view fmt, + format_args args) { + return detail::vformat(loc, fmt, args); +} + +template ::value)> +inline std::string format(const Locale& loc, format_string fmt, + T&&... args) { + return vformat(loc, fmt, fmt::make_format_args(args...)); +} + template , FMT_ENABLE_IF(detail::is_string::value)> inline void vformat_to( @@ -2809,6 +2836,25 @@ inline auto format_to(OutputIt out, const S& fmt, Args&&... args) -> OutputIt { return vformat_to(out, to_string_view(fmt), vargs); } +template ::value&& + detail::is_locale::value)> +auto vformat_to(const Locale& loc, OutputIt out, string_view fmt, + format_args args) -> OutputIt { + using detail::get_buffer; + auto&& buf = get_buffer(out); + detail::vformat_to(buf, string_view(fmt), args, detail::locale_ref(loc)); + return detail::get_iterator(buf); +} + +template ::value&& + detail::is_locale::value)> +FMT_INLINE auto format_to(OutputIt out, const Locale& loc, + format_string fmt, T&&... args) -> OutputIt { + return vformat_to(loc, out, fmt, fmt::make_format_args(args...)); +} + template ::value && !std::is_same::value)> diff --git a/include/fmt/locale.h b/include/fmt/locale.h index 9bf46d58..6c3b1e11 100644 --- a/include/fmt/locale.h +++ b/include/fmt/locale.h @@ -1,67 +1,2 @@ -// Formatting library for C++ - std::locale support -// -// Copyright (c) 2012 - present, Victor Zverovich -// All rights reserved. -// -// For the license information refer to format.h. - -#ifndef FMT_LOCALE_H_ -#define FMT_LOCALE_H_ - -#include - -#include "format.h" - -FMT_BEGIN_NAMESPACE - -namespace detail { -template -std::basic_string vformat( - const std::locale& loc, basic_string_view format_str, - basic_format_args>> args) { - basic_memory_buffer buffer; - detail::vformat_to(buffer, format_str, args, detail::locale_ref(loc)); - return fmt::to_string(buffer); -} -} // namespace detail - -FMT_MODULE_EXPORT_BEGIN - -template > -inline std::basic_string vformat( - const std::locale& loc, const S& format_str, - basic_format_args>> args) { - return detail::vformat(loc, to_string_view(format_str), args); -} - -template > -inline std::basic_string format(const std::locale& loc, - const S& format_str, Args&&... args) { - return detail::vformat(loc, to_string_view(format_str), - fmt::make_args_checked(format_str, args...)); -} - -template , - FMT_ENABLE_IF(detail::is_output_iterator::value)> -inline OutputIt vformat_to( - OutputIt out, const std::locale& loc, const S& format_str, - basic_format_args>> args) { - auto&& buf = detail::get_buffer(out); - vformat_to(buf, to_string_view(format_str), args, detail::locale_ref(loc)); - return detail::get_iterator(buf); -} - -template >::value> -inline auto format_to(OutputIt out, const std::locale& loc, const S& format_str, - Args&&... args) -> - typename std::enable_if::type { - const auto& vargs = fmt::make_args_checked(format_str, args...); - return vformat_to(out, loc, to_string_view(format_str), vargs); -} - -FMT_MODULE_EXPORT_END -FMT_END_NAMESPACE - -#endif // FMT_LOCALE_H_ +#include "wchar.h" +#warning fmt/locale.h is deprecated, include fmt/format.h or fmt/wchar.h instead diff --git a/include/fmt/wchar.h b/include/fmt/wchar.h index 53c95f37..72b05e22 100644 --- a/include/fmt/wchar.h +++ b/include/fmt/wchar.h @@ -64,6 +64,50 @@ arg_join join(std::initializer_list list, return join(std::begin(list), std::end(list), sep); } +template , + FMT_ENABLE_IF(detail::is_locale::value && + !std::is_same::value)> +inline std::basic_string vformat( + const Locale& loc, const S& format_str, + basic_format_args>> args) { + return detail::vformat(loc, to_string_view(format_str), args); +} + +template , + FMT_ENABLE_IF(detail::is_locale::value && + !std::is_same::value)> +inline std::basic_string format(const Locale& loc, const S& format_str, + Args&&... args) { + return detail::vformat(loc, to_string_view(format_str), + fmt::make_args_checked(format_str, args...)); +} + +template , + FMT_ENABLE_IF(detail::is_output_iterator::value&& + detail::is_locale::value && + !std::is_same::value)> +inline OutputIt vformat_to( + OutputIt out, const Locale& loc, const S& format_str, + basic_format_args>> args) { + auto&& buf = detail::get_buffer(out); + vformat_to(buf, to_string_view(format_str), args, detail::locale_ref(loc)); + return detail::get_iterator(buf); +} + +template , + bool enable = detail::is_output_iterator::value&& + detail::is_locale::value && + !std::is_same::value> +inline auto format_to(OutputIt out, const Locale& loc, const S& format_str, + Args&&... args) -> + typename std::enable_if::type { + const auto& vargs = fmt::make_args_checked(format_str, args...); + return vformat_to(out, loc, to_string_view(format_str), vargs); +} + inline void vprint(std::FILE* f, wstring_view fmt, wformat_args args) { wmemory_buffer buffer; detail::vformat_to(buffer, fmt, args); @@ -91,7 +135,6 @@ template void print(wformat_string fmt, T&&... args) { template inline std::wstring to_wstring(const T& value) { return format(FMT_STRING(L"{}"), value); } - FMT_MODULE_EXPORT_END FMT_END_NAMESPACE diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index bc5de207..a7d5a95c 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -72,7 +72,6 @@ endif () if (NOT (MSVC AND BUILD_SHARED_LIBS)) add_fmt_test(format-impl-test HEADER_ONLY header-only-test.cc) endif () -add_fmt_test(locale-test) add_fmt_test(ostream-test) add_fmt_test(compile-test) add_fmt_test(printf-test) diff --git a/test/enforce-checks-test.cc b/test/enforce-checks-test.cc index a2056b22..4f5fd361 100644 --- a/test/enforce-checks-test.cc +++ b/test/enforce-checks-test.cc @@ -11,7 +11,6 @@ #include "fmt/chrono.h" #include "fmt/color.h" #include "fmt/format.h" -#include "fmt/locale.h" #include "fmt/ostream.h" #include "fmt/ranges.h" #include "fmt/wchar.h" diff --git a/test/locale-test.cc b/test/locale-test.cc deleted file mode 100644 index f2cdd335..00000000 --- a/test/locale-test.cc +++ /dev/null @@ -1,168 +0,0 @@ -// Formatting library for C++ - locale tests -// -// Copyright (c) 2012 - present, Victor Zverovich -// All rights reserved. -// -// For the license information refer to format.h. - -#include "fmt/locale.h" - -#include - -#include "gmock/gmock.h" - -using fmt::detail::max_value; - -#ifndef FMT_STATIC_THOUSANDS_SEPARATOR -template struct numpunct : std::numpunct { - protected: - Char do_decimal_point() const override { return '?'; } - std::string do_grouping() const override { return "\03"; } - Char do_thousands_sep() const override { return '~'; } -}; - -template struct no_grouping : std::numpunct { - protected: - Char do_decimal_point() const override { return '.'; } - std::string do_grouping() const override { return ""; } - Char do_thousands_sep() const override { return ','; } -}; - -template struct special_grouping : std::numpunct { - protected: - Char do_decimal_point() const override { return '.'; } - std::string do_grouping() const override { return "\03\02"; } - Char do_thousands_sep() const override { return ','; } -}; - -template struct small_grouping : std::numpunct { - protected: - Char do_decimal_point() const override { return '.'; } - std::string do_grouping() const override { return "\01"; } - Char do_thousands_sep() const override { return ','; } -}; - -TEST(locale_test, double_decimal_point) { - auto loc = std::locale(std::locale(), new numpunct()); - EXPECT_EQ("1?23", fmt::format(loc, "{:L}", 1.23)); - EXPECT_EQ("1?230000", fmt::format(loc, "{:Lf}", 1.23)); -} - -TEST(locale_test, format) { - auto loc = std::locale(std::locale(), new numpunct()); - EXPECT_EQ("1234567", fmt::format(std::locale(), "{:L}", 1234567)); - EXPECT_EQ("1~234~567", fmt::format(loc, "{:L}", 1234567)); - EXPECT_EQ("-1~234~567", fmt::format(loc, "{:L}", -1234567)); - EXPECT_EQ("-256", fmt::format(loc, "{:L}", -256)); - fmt::format_arg_store as{1234567}; - EXPECT_EQ("1~234~567", fmt::vformat(loc, "{:L}", fmt::format_args(as))); - auto s = std::string(); - fmt::format_to(std::back_inserter(s), loc, "{:L}", 1234567); - EXPECT_EQ("1~234~567", s); - - auto no_grouping_loc = std::locale(std::locale(), new no_grouping()); - EXPECT_EQ("1234567", fmt::format(no_grouping_loc, "{:L}", 1234567)); - - auto special_grouping_loc = - std::locale(std::locale(), new special_grouping()); - EXPECT_EQ("1,23,45,678", fmt::format(special_grouping_loc, "{:L}", 12345678)); - EXPECT_EQ("12,345", fmt::format(special_grouping_loc, "{:L}", 12345)); - - auto small_grouping_loc = - std::locale(std::locale(), new small_grouping()); - EXPECT_EQ("4,2,9,4,9,6,7,2,9,5", - fmt::format(small_grouping_loc, "{:L}", max_value())); -} - -TEST(locale_test, format_detault_align) { - auto loc = std::locale({}, new special_grouping()); - EXPECT_EQ(" 12,345", fmt::format(loc, "{:8L}", 12345)); -} - -TEST(locale_test, format_plus) { - auto loc = std::locale({}, new special_grouping()); - EXPECT_EQ("+100", fmt::format(loc, "{:+L}", 100)); -} - -TEST(locale_test, wformat) { - auto loc = std::locale(std::locale(), new numpunct()); - EXPECT_EQ(L"1234567", fmt::format(std::locale(), L"{:L}", 1234567)); - EXPECT_EQ(L"1~234~567", fmt::format(loc, L"{:L}", 1234567)); - using wcontext = fmt::buffer_context; - fmt::format_arg_store as{1234567}; - EXPECT_EQ(L"1~234~567", - fmt::vformat(loc, L"{:L}", fmt::basic_format_args(as))); - EXPECT_EQ(L"1234567", fmt::format(std::locale("C"), L"{:L}", 1234567)); - - auto no_grouping_loc = std::locale(std::locale(), new no_grouping()); - EXPECT_EQ(L"1234567", fmt::format(no_grouping_loc, L"{:L}", 1234567)); - - auto special_grouping_loc = - std::locale(std::locale(), new special_grouping()); - EXPECT_EQ(L"1,23,45,678", - fmt::format(special_grouping_loc, L"{:L}", 12345678)); - - auto small_grouping_loc = - std::locale(std::locale(), new small_grouping()); - EXPECT_EQ(L"4,2,9,4,9,6,7,2,9,5", - fmt::format(small_grouping_loc, L"{:L}", max_value())); -} - -TEST(locale_test, double_formatter) { - auto loc = std::locale(std::locale(), new special_grouping()); - auto f = fmt::formatter(); - auto parse_ctx = fmt::format_parse_context("L"); - f.parse(parse_ctx); - char buf[10] = {}; - fmt::basic_format_context format_ctx( - buf, {}, fmt::detail::locale_ref(loc)); - *f.format(12345, format_ctx) = 0; - EXPECT_STREQ("12,345", buf); -} - -FMT_BEGIN_NAMESPACE -template struct formatter, charT> { - private: - detail::dynamic_format_specs specs_; - - public: - FMT_CONSTEXPR typename basic_format_parse_context::iterator parse( - basic_format_parse_context& ctx) { - using handler_type = - detail::dynamic_specs_handler>; - detail::specs_checker handler(handler_type(specs_, ctx), - detail::type::string_type); - auto it = parse_format_specs(ctx.begin(), ctx.end(), handler); - detail::parse_float_type_spec(specs_, ctx.error_handler()); - return it; - } - - template - typename FormatContext::iterator format(const std::complex& c, - FormatContext& ctx) { - detail::handle_dynamic_spec( - specs_.precision, specs_.precision_ref, ctx); - auto specs = std::string(); - if (specs_.precision > 0) specs = fmt::format(".{}", specs_.precision); - if (specs_.type) specs += specs_.type; - auto real = fmt::format(ctx.locale().template get(), - "{:" + specs + "}", c.real()); - auto imag = fmt::format(ctx.locale().template get(), - "{:" + specs + "}", c.imag()); - auto fill_align_width = std::string(); - if (specs_.width > 0) fill_align_width = fmt::format(">{}", specs_.width); - return format_to(ctx.out(), runtime("{:" + fill_align_width + "}"), - c.real() != 0 ? fmt::format("({}+{}i)", real, imag) - : fmt::format("{}i", imag)); - } -}; -FMT_END_NAMESPACE - -TEST(locale_test, complex) { - std::string s = fmt::format("{}", std::complex(1, 2)); - EXPECT_EQ(s, "(1+2i)"); - EXPECT_EQ(fmt::format("{:.2f}", std::complex(1, 2)), "(1.00+2.00i)"); - EXPECT_EQ(fmt::format("{:8}", std::complex(1, 2)), " (1+2i)"); -} - -#endif // FMT_STATIC_THOUSANDS_SEPARATOR diff --git a/test/wchar-test.cc b/test/wchar-test.cc index 38804723..c3d54d3b 100644 --- a/test/wchar-test.cc +++ b/test/wchar-test.cc @@ -7,8 +7,12 @@ #include "fmt/wchar.h" +#include + #include "gtest/gtest.h" +using fmt::detail::max_value; + // std::is_constructible is broken in MSVC until version 2015. #if !FMT_MSC_VER || FMT_MSC_VER >= 1900 struct explicitly_convertible_to_wstring_view { @@ -82,3 +86,157 @@ TEST(wchar_test, join) { } TEST(wchar_test, to_wstring) { EXPECT_EQ(L"42", fmt::to_wstring(42)); } + +#ifndef FMT_STATIC_THOUSANDS_SEPARATOR +template struct numpunct : std::numpunct { + protected: + Char do_decimal_point() const override { return '?'; } + std::string do_grouping() const override { return "\03"; } + Char do_thousands_sep() const override { return '~'; } +}; + +template struct no_grouping : std::numpunct { + protected: + Char do_decimal_point() const override { return '.'; } + std::string do_grouping() const override { return ""; } + Char do_thousands_sep() const override { return ','; } +}; + +template struct special_grouping : std::numpunct { + protected: + Char do_decimal_point() const override { return '.'; } + std::string do_grouping() const override { return "\03\02"; } + Char do_thousands_sep() const override { return ','; } +}; + +template struct small_grouping : std::numpunct { + protected: + Char do_decimal_point() const override { return '.'; } + std::string do_grouping() const override { return "\01"; } + Char do_thousands_sep() const override { return ','; } +}; + +TEST(locale_test, double_decimal_point) { + auto loc = std::locale(std::locale(), new numpunct()); + EXPECT_EQ("1?23", fmt::format(loc, "{:L}", 1.23)); + EXPECT_EQ("1?230000", fmt::format(loc, "{:Lf}", 1.23)); +} + +TEST(locale_test, format) { + auto loc = std::locale(std::locale(), new numpunct()); + EXPECT_EQ("1234567", fmt::format(std::locale(), "{:L}", 1234567)); + EXPECT_EQ("1~234~567", fmt::format(loc, "{:L}", 1234567)); + EXPECT_EQ("-1~234~567", fmt::format(loc, "{:L}", -1234567)); + EXPECT_EQ("-256", fmt::format(loc, "{:L}", -256)); + fmt::format_arg_store as{1234567}; + EXPECT_EQ("1~234~567", fmt::vformat(loc, "{:L}", fmt::format_args(as))); + auto s = std::string(); + fmt::format_to(std::back_inserter(s), loc, "{:L}", 1234567); + EXPECT_EQ("1~234~567", s); + + auto no_grouping_loc = std::locale(std::locale(), new no_grouping()); + EXPECT_EQ("1234567", fmt::format(no_grouping_loc, "{:L}", 1234567)); + + auto special_grouping_loc = + std::locale(std::locale(), new special_grouping()); + EXPECT_EQ("1,23,45,678", fmt::format(special_grouping_loc, "{:L}", 12345678)); + EXPECT_EQ("12,345", fmt::format(special_grouping_loc, "{:L}", 12345)); + + auto small_grouping_loc = + std::locale(std::locale(), new small_grouping()); + EXPECT_EQ("4,2,9,4,9,6,7,2,9,5", + fmt::format(small_grouping_loc, "{:L}", max_value())); +} + +TEST(locale_test, format_detault_align) { + auto loc = std::locale({}, new special_grouping()); + EXPECT_EQ(" 12,345", fmt::format(loc, "{:8L}", 12345)); +} + +TEST(locale_test, format_plus) { + auto loc = std::locale({}, new special_grouping()); + EXPECT_EQ("+100", fmt::format(loc, "{:+L}", 100)); +} + +TEST(locale_test, wformat) { + auto loc = std::locale(std::locale(), new numpunct()); + EXPECT_EQ(L"1234567", fmt::format(std::locale(), L"{:L}", 1234567)); + EXPECT_EQ(L"1~234~567", fmt::format(loc, L"{:L}", 1234567)); + using wcontext = fmt::buffer_context; + fmt::format_arg_store as{1234567}; + EXPECT_EQ(L"1~234~567", + fmt::vformat(loc, L"{:L}", fmt::basic_format_args(as))); + EXPECT_EQ(L"1234567", fmt::format(std::locale("C"), L"{:L}", 1234567)); + + auto no_grouping_loc = std::locale(std::locale(), new no_grouping()); + EXPECT_EQ(L"1234567", fmt::format(no_grouping_loc, L"{:L}", 1234567)); + + auto special_grouping_loc = + std::locale(std::locale(), new special_grouping()); + EXPECT_EQ(L"1,23,45,678", + fmt::format(special_grouping_loc, L"{:L}", 12345678)); + + auto small_grouping_loc = + std::locale(std::locale(), new small_grouping()); + EXPECT_EQ(L"4,2,9,4,9,6,7,2,9,5", + fmt::format(small_grouping_loc, L"{:L}", max_value())); +} + +TEST(locale_test, double_formatter) { + auto loc = std::locale(std::locale(), new special_grouping()); + auto f = fmt::formatter(); + auto parse_ctx = fmt::format_parse_context("L"); + f.parse(parse_ctx); + char buf[10] = {}; + fmt::basic_format_context format_ctx( + buf, {}, fmt::detail::locale_ref(loc)); + *f.format(12345, format_ctx) = 0; + EXPECT_STREQ("12,345", buf); +} + +FMT_BEGIN_NAMESPACE +template struct formatter, charT> { + private: + detail::dynamic_format_specs specs_; + + public: + FMT_CONSTEXPR typename basic_format_parse_context::iterator parse( + basic_format_parse_context& ctx) { + using handler_type = + detail::dynamic_specs_handler>; + detail::specs_checker handler(handler_type(specs_, ctx), + detail::type::string_type); + auto it = parse_format_specs(ctx.begin(), ctx.end(), handler); + detail::parse_float_type_spec(specs_, ctx.error_handler()); + return it; + } + + template + typename FormatContext::iterator format(const std::complex& c, + FormatContext& ctx) { + detail::handle_dynamic_spec( + specs_.precision, specs_.precision_ref, ctx); + auto specs = std::string(); + if (specs_.precision > 0) specs = fmt::format(".{}", specs_.precision); + if (specs_.type) specs += specs_.type; + auto real = fmt::format(ctx.locale().template get(), + fmt::runtime("{:" + specs + "}"), c.real()); + auto imag = fmt::format(ctx.locale().template get(), + fmt::runtime("{:" + specs + "}"), c.imag()); + auto fill_align_width = std::string(); + if (specs_.width > 0) fill_align_width = fmt::format(">{}", specs_.width); + return format_to(ctx.out(), runtime("{:" + fill_align_width + "}"), + c.real() != 0 ? fmt::format("({}+{}i)", real, imag) + : fmt::format("{}i", imag)); + } +}; +FMT_END_NAMESPACE + +TEST(locale_test, complex) { + std::string s = fmt::format("{}", std::complex(1, 2)); + EXPECT_EQ(s, "(1+2i)"); + EXPECT_EQ(fmt::format("{:.2f}", std::complex(1, 2)), "(1.00+2.00i)"); + EXPECT_EQ(fmt::format("{:8}", std::complex(1, 2)), " (1+2i)"); +} + +#endif // FMT_STATIC_THOUSANDS_SEPARATOR