diff --git a/doc/api.md b/doc/api.md index b6742a6c..cb4b379a 100644 --- a/doc/api.md +++ b/doc/api.md @@ -415,11 +415,11 @@ locale: that take `std::locale` as a parameter. The locale type is a template parameter to avoid the expensive `` include. -::: format(const Locale&, format_string, T&&...) +::: format(locale_ref, format_string, T&&...) -::: format_to(OutputIt, const Locale&, format_string, T&&...) +::: format_to(OutputIt, locale_ref, format_string, T&&...) -::: formatted_size(const Locale&, format_string, T&&...) +::: formatted_size(locale_ref, format_string, T&&...) ### Legacy Compile-Time Checks diff --git a/include/fmt/base.h b/include/fmt/base.h index dde9b241..98129330 100644 --- a/include/fmt/base.h +++ b/include/fmt/base.h @@ -902,6 +902,29 @@ template class parse_context { FMT_CONSTEXPR void check_dynamic_spec(int arg_id); }; +#ifndef FMT_USE_LOCALE +# define FMT_USE_LOCALE (FMT_OPTIMIZE_SIZE <= 1) +#endif + +// A type-erased reference to std::locale to avoid the heavy include. +class locale_ref { +#if FMT_USE_LOCALE + private: + const void* locale_; // A type-erased pointer to std::locale. + + public: + constexpr locale_ref() : locale_(nullptr) {} + + template + locale_ref(const Locale& loc); + + inline explicit operator bool() const noexcept { return locale_ != nullptr; } +#endif // FMT_USE_LOCALE + + public: + template auto get() const -> Locale; +}; + FMT_END_EXPORT namespace detail { @@ -2301,27 +2324,6 @@ struct is_output_iterator< enable_if_t&>()++), T>::value>> : std::true_type {}; -#ifndef FMT_USE_LOCALE -# define FMT_USE_LOCALE (FMT_OPTIMIZE_SIZE <= 1) -#endif - -// A type-erased reference to an std::locale to avoid a heavy include. -class locale_ref { -#if FMT_USE_LOCALE - private: - const void* locale_; // A type-erased pointer to std::locale. - - public: - constexpr locale_ref() : locale_(nullptr) {} - template locale_ref(const Locale& loc); - - inline explicit operator bool() const noexcept { return locale_ != nullptr; } -#endif // FMT_USE_LOCALE - - public: - template auto get() const -> Locale; -}; - template constexpr auto encode_types() -> unsigned long long { return 0; } @@ -2665,7 +2667,7 @@ class context { private: appender out_; format_args args_; - FMT_NO_UNIQUE_ADDRESS detail::locale_ref loc_; + FMT_NO_UNIQUE_ADDRESS locale_ref loc_; public: using char_type = char; ///< The character type for the output. @@ -2675,8 +2677,7 @@ class context { /// Constructs a `context` object. References to the arguments are stored /// in the object so make sure they have appropriate lifetimes. - FMT_CONSTEXPR context(iterator out, format_args args, - detail::locale_ref loc = {}) + FMT_CONSTEXPR context(iterator out, format_args args, locale_ref loc = {}) : out_(out), args_(args), loc_(loc) {} context(context&&) = default; context(const context&) = delete; @@ -2697,7 +2698,7 @@ class context { // Advances the begin iterator to `it`. FMT_CONSTEXPR void advance_to(iterator) {} - FMT_CONSTEXPR auto locale() const -> detail::locale_ref { return loc_; } + FMT_CONSTEXPR auto locale() const -> locale_ref { return loc_; } }; template struct runtime_format_string { diff --git a/include/fmt/chrono.h b/include/fmt/chrono.h index 6f9a363e..da40e70f 100644 --- a/include/fmt/chrono.h +++ b/include/fmt/chrono.h @@ -2230,7 +2230,7 @@ template struct formatter { detail::handle_dynamic_spec(specs.dynamic_width(), specs.width, width_ref_, ctx); - auto loc_ref = specs.localized() ? ctx.locale() : detail::locale_ref(); + auto loc_ref = specs.localized() ? ctx.locale() : locale_ref(); detail::get_locale loc(static_cast(loc_ref), loc_ref); auto w = detail::tm_writer, Char, Duration>( loc, out, tm, subsecs); diff --git a/include/fmt/format-inl.h b/include/fmt/format-inl.h index 1304a91a..527a5bb1 100644 --- a/include/fmt/format-inl.h +++ b/include/fmt/format-inl.h @@ -41,6 +41,38 @@ FMT_FUNC void assert_fail(const char* file, int line, const char* message) { } #endif +#if FMT_USE_LOCALE +namespace detail { +using std::locale; +using std::numpunct; +using std::use_facet; +} // namespace detail + +template > +locale_ref::locale_ref(const Locale& loc) : locale_(&loc) { + static_assert(std::is_same::value, ""); +} +#else +namespace detail { +struct locale {}; +template struct numpunct { + auto grouping() const -> std::string { return "\03"; } + auto thousands_sep() const -> Char { return ','; } + auto decimal_point() const -> Char { return '.'; } +}; +template Facet use_facet(locale) { return {}; } +} // namespace detail +#endif // FMT_USE_LOCALE + +template auto locale_ref::get() const -> Locale { + using namespace detail; + static_assert(std::is_same::value, ""); +#if FMT_USE_LOCALE + if (locale_) return *static_cast(locale_); +#endif + return locale(); +} + namespace detail { // DEPRECATED! @@ -87,33 +119,6 @@ inline void fwrite_all(const void* ptr, size_t count, FILE* stream) { FMT_THROW(system_error(errno, FMT_STRING("cannot write to file"))); } -#if FMT_USE_LOCALE -using std::locale; -using std::numpunct; -using std::use_facet; - -template -locale_ref::locale_ref(const Locale& loc) : locale_(&loc) { - static_assert(std::is_same::value, ""); -} -#else -struct locale {}; -template struct numpunct { - auto grouping() const -> std::string { return "\03"; } - auto thousands_sep() const -> Char { return ','; } - auto decimal_point() const -> Char { return '.'; } -}; -template Facet use_facet(locale) { return {}; } -#endif // FMT_USE_LOCALE - -template auto locale_ref::get() const -> Locale { - static_assert(std::is_same::value, ""); -#if FMT_USE_LOCALE - if (locale_) return *static_cast(locale_); -#endif - return locale(); -} - template FMT_FUNC auto thousands_sep_impl(locale_ref loc) -> thousands_sep_result { auto&& facet = use_facet>(loc.get()); diff --git a/include/fmt/format.h b/include/fmt/format.h index e3b85feb..9c0056db 100644 --- a/include/fmt/format.h +++ b/include/fmt/format.h @@ -1846,16 +1846,6 @@ FMT_CONSTEXPR auto write_char(OutputIt out, Char value, return it; }); } -template -FMT_CONSTEXPR auto write(OutputIt out, Char value, const format_specs& specs, - locale_ref loc = {}) -> OutputIt { - // char is formatted as unsigned char for consistency across platforms. - using unsigned_type = - conditional_t::value, unsigned char, unsigned>; - return check_char_specs(specs) - ? write_char(out, value, specs) - : write(out, static_cast(value), specs, loc); -} template class digit_grouping { private: @@ -1879,9 +1869,7 @@ template class digit_grouping { } public: - template ::value)> - explicit digit_grouping(Locale loc, bool localized = true) { + explicit digit_grouping(locale_ref loc, bool localized = true) { if (!localized) return; auto sep = thousands_sep(loc); grouping_ = sep.grouping; @@ -1981,6 +1969,8 @@ auto write_int(OutputIt out, UInt value, unsigned prefix, // Writes a localized value. FMT_API auto write_loc(appender out, loc_value value, const format_specs& specs, locale_ref loc) -> bool; +auto write_loc(basic_appender out, loc_value value, + const format_specs& specs, locale_ref loc) -> bool; #endif template inline auto write_loc(OutputIt, const loc_value&, const format_specs&, @@ -2147,6 +2137,17 @@ FMT_CONSTEXPR FMT_INLINE auto write(OutputIt out, T value, return write_int(out, make_write_int_arg(value, specs.sign()), specs); } +template +FMT_CONSTEXPR auto write(OutputIt out, Char value, const format_specs& specs, + locale_ref loc = {}) -> OutputIt { + // char is formatted as unsigned char for consistency across platforms. + using unsigned_type = + conditional_t::value, unsigned char, unsigned>; + return check_char_specs(specs) + ? write_char(out, value, specs) + : write(out, static_cast(value), specs, loc); +} + template ::value)> FMT_CONSTEXPR auto write(OutputIt out, basic_string_view s, @@ -3819,12 +3820,6 @@ FMT_CONSTEXPR auto native_formatter::format( return write(ctx.out(), val, specs, ctx.locale()); } -// DEPRECATED! https://github.com/fmtlib/fmt/issues/4292. -template -struct is_locale : std::false_type {}; -template -struct is_locale> : std::true_type {}; - // DEPRECATED! template struct vformat_args { using type = basic_format_args>; @@ -3851,7 +3846,7 @@ template class generic_context { private: OutputIt out_; basic_format_args args_; - detail::locale_ref loc_; + locale_ref loc_; public: using char_type = Char; @@ -3863,7 +3858,7 @@ template class generic_context { constexpr generic_context(OutputIt out, basic_format_args args, - detail::locale_ref loc = {}) + locale_ref loc = {}) : out_(out), args_(args), loc_(loc) {} generic_context(generic_context&&) = default; generic_context(const generic_context&) = delete; @@ -3886,7 +3881,7 @@ template class generic_context { if (!detail::is_back_insert_iterator()) out_ = it; } - constexpr auto locale() const -> detail::locale_ref { return loc_; } + constexpr auto locale() const -> locale_ref { return loc_; } }; class loc_value { @@ -4307,46 +4302,41 @@ FMT_API void format_system_error(detail::buffer& out, int error_code, // Can be used to report errors from destructors. FMT_API void report_system_error(int error_code, const char* message) noexcept; -template ::value)> -inline auto vformat(const Locale& loc, string_view fmt, format_args args) +inline auto vformat(locale_ref loc, string_view fmt, format_args args) -> std::string { auto buf = memory_buffer(); - detail::vformat_to(buf, fmt, args, detail::locale_ref(loc)); + detail::vformat_to(buf, fmt, args, loc); return {buf.data(), buf.size()}; } -template ::value)> -FMT_INLINE auto format(const Locale& loc, format_string fmt, T&&... args) +template +FMT_INLINE auto format(locale_ref loc, format_string fmt, T&&... args) -> std::string { return vformat(loc, fmt.str, vargs{{args...}}); } -template ::value)> -auto vformat_to(OutputIt out, const Locale& loc, string_view fmt, - format_args args) -> OutputIt { +auto vformat_to(OutputIt out, locale_ref loc, string_view fmt, format_args args) + -> OutputIt { auto&& buf = detail::get_buffer(out); - detail::vformat_to(buf, fmt, args, detail::locale_ref(loc)); + detail::vformat_to(buf, fmt, args, loc); return detail::get_iterator(buf, out); } -template ::value&& - detail::is_locale::value)> -FMT_INLINE auto format_to(OutputIt out, const Locale& loc, - format_string fmt, T&&... args) -> OutputIt { +template ::value)> +FMT_INLINE auto format_to(OutputIt out, locale_ref loc, format_string fmt, + T&&... args) -> OutputIt { return fmt::vformat_to(out, loc, fmt.str, vargs{{args...}}); } -template ::value)> -FMT_NODISCARD FMT_INLINE auto formatted_size(const Locale& loc, +template +FMT_NODISCARD FMT_INLINE auto formatted_size(locale_ref loc, format_string fmt, T&&... args) -> size_t { auto buf = detail::counting_buffer<>(); - detail::vformat_to(buf, fmt.str, vargs{{args...}}, - detail::locale_ref(loc)); + detail::vformat_to(buf, fmt.str, vargs{{args...}}, loc); return buf.count(); } diff --git a/include/fmt/printf.h b/include/fmt/printf.h index e7268401..ca184739 100644 --- a/include/fmt/printf.h +++ b/include/fmt/printf.h @@ -46,7 +46,7 @@ template class basic_printf_context { auto out() -> basic_appender { return out_; } void advance_to(basic_appender) {} - auto locale() -> detail::locale_ref { return {}; } + auto locale() -> locale_ref { return {}; } auto arg(int id) const -> basic_format_arg { return args_.get(id); diff --git a/include/fmt/xchar.h b/include/fmt/xchar.h index c0240a3e..df5dc926 100644 --- a/include/fmt/xchar.h +++ b/include/fmt/xchar.h @@ -183,24 +183,20 @@ auto format(const S& fmt, T&&... args) -> std::basic_string { fmt::make_format_args>(args...)); } -template , - FMT_ENABLE_IF(detail::is_locale::value&& - detail::is_exotic_char::value)> -inline auto vformat(const Locale& loc, const S& fmt, +template , + FMT_ENABLE_IF(detail::is_exotic_char::value)> +inline auto vformat(locale_ref loc, const S& fmt, typename detail::vformat_args::type args) -> std::basic_string { auto buf = basic_memory_buffer(); - detail::vformat_to(buf, detail::to_string_view(fmt), args, - detail::locale_ref(loc)); + detail::vformat_to(buf, detail::to_string_view(fmt), args, loc); return {buf.data(), buf.size()}; } -template , - FMT_ENABLE_IF(detail::is_locale::value&& - detail::is_exotic_char::value)> -inline auto format(const Locale& loc, const S& fmt, T&&... args) + FMT_ENABLE_IF(detail::is_exotic_char::value)> +inline auto format(locale_ref loc, const S& fmt, T&&... args) -> std::basic_string { return vformat(loc, detail::to_string_view(fmt), fmt::make_format_args>(args...)); @@ -227,27 +223,24 @@ inline auto format_to(OutputIt out, const S& fmt, T&&... args) -> OutputIt { fmt::make_format_args>(args...)); } -template , FMT_ENABLE_IF(detail::is_output_iterator::value&& - detail::is_locale::value&& - detail::is_exotic_char::value)> -inline auto vformat_to(OutputIt out, const Locale& loc, const S& fmt, + detail::is_exotic_char::value)> +inline auto vformat_to(OutputIt out, locale_ref loc, const S& fmt, typename detail::vformat_args::type args) -> OutputIt { auto&& buf = detail::get_buffer(out); - vformat_to(buf, detail::to_string_view(fmt), args, detail::locale_ref(loc)); + vformat_to(buf, detail::to_string_view(fmt), args, loc); return detail::get_iterator(buf, out); } -template , bool enable = detail::is_output_iterator::value && - detail::is_locale::value && detail::is_exotic_char::value> -inline auto format_to(OutputIt out, const Locale& loc, const S& fmt, - T&&... args) -> - typename std::enable_if::type { +inline auto format_to(OutputIt out, locale_ref loc, const S& fmt, T&&... args) + -> typename std::enable_if::type { return vformat_to(out, loc, detail::to_string_view(fmt), fmt::make_format_args>(args...)); } diff --git a/src/format.cc b/src/format.cc index 3ccd8068..50e267c5 100644 --- a/src/format.cc +++ b/src/format.cc @@ -8,6 +8,12 @@ #include "fmt/format-inl.h" FMT_BEGIN_NAMESPACE + +#if FMT_USE_LOCALE +template FMT_API locale_ref::locale_ref(const std::locale& loc); +template FMT_API auto locale_ref::get() const -> std::locale; +#endif + namespace detail { template FMT_API auto dragonbox::to_decimal(float x) noexcept @@ -15,12 +21,6 @@ template FMT_API auto dragonbox::to_decimal(float x) noexcept template FMT_API auto dragonbox::to_decimal(double x) noexcept -> dragonbox::decimal_fp; -#if FMT_USE_LOCALE -// DEPRECATED! locale_ref in the detail namespace -template FMT_API locale_ref::locale_ref(const std::locale& loc); -template FMT_API auto locale_ref::get() const -> std::locale; -#endif - // Explicit instantiations for char. template FMT_API auto thousands_sep_impl(locale_ref) diff --git a/test/xchar-test.cc b/test/xchar-test.cc index a823ca06..b0646da0 100644 --- a/test/xchar-test.cc +++ b/test/xchar-test.cc @@ -379,7 +379,7 @@ TEST(locale_test, int_formatter) { f.parse(parse_ctx); auto buf = fmt::memory_buffer(); fmt::basic_format_context format_ctx( - fmt::appender(buf), {}, fmt::detail::locale_ref(loc)); + fmt::appender(buf), {}, fmt::locale_ref(loc)); f.format(12345, format_ctx); EXPECT_EQ(fmt::to_string(buf), "12,345"); }