diff --git a/include/spdlog/fmt/bundled/args.h b/include/spdlog/fmt/bundled/args.h index 3ff47880..5e5f40f9 100644 --- a/include/spdlog/fmt/bundled/args.h +++ b/include/spdlog/fmt/bundled/args.h @@ -71,7 +71,7 @@ class dynamic_arg_list { * It can be implicitly converted into `fmt::basic_format_args` for passing * into type-erased formatting functions such as `fmt::vformat`. */ -template class dynamic_format_arg_store { +FMT_EXPORT template class dynamic_format_arg_store { private: using char_type = typename Context::char_type; @@ -212,7 +212,7 @@ template class dynamic_format_arg_store { } /// Returns the number of elements in the store. - size_t size() const noexcept { return data_.size(); } + auto size() const noexcept -> size_t { return data_.size(); } }; FMT_END_NAMESPACE diff --git a/include/spdlog/fmt/bundled/base.h b/include/spdlog/fmt/bundled/base.h index 87b3fd7c..42e192ac 100644 --- a/include/spdlog/fmt/bundled/base.h +++ b/include/spdlog/fmt/bundled/base.h @@ -21,7 +21,7 @@ #endif // The fmt library version in the form major * 10000 + minor * 100 + patch. -#define FMT_VERSION 110200 +#define FMT_VERSION 120000 // Detect compiler versions. #if defined(__clang__) && !defined(__ibmxl__) @@ -201,14 +201,6 @@ # define FMT_NODISCARD #endif -#ifdef FMT_DEPRECATED -// Use the provided definition. -#elif FMT_HAS_CPP14_ATTRIBUTE(deprecated) -# define FMT_DEPRECATED [[deprecated]] -#else -# define FMT_DEPRECATED /* deprecated */ -#endif - #if FMT_GCC_VERSION || FMT_CLANG_VERSION # define FMT_VISIBILITY(value) __attribute__((visibility(value))) #else @@ -260,7 +252,7 @@ FMT_PRAGMA_CLANG(diagnostic push) #ifndef FMT_BEGIN_NAMESPACE # define FMT_BEGIN_NAMESPACE \ namespace fmt { \ - inline namespace v11 { + inline namespace v12 { # define FMT_END_NAMESPACE \ } \ } @@ -356,6 +348,9 @@ template constexpr auto max_of(T a, T b) -> T { return a > b ? a : b; } +FMT_NORETURN FMT_API void assert_fail(const char* file, int line, + const char* message); + namespace detail { // Suppresses "unused variable" warnings with the method described in // https://herbsutter.com/2009/10/18/mailbag-shutting-up-compiler-warnings/. @@ -396,7 +391,7 @@ FMT_NORETURN FMT_API void assert_fail(const char* file, int line, # define FMT_ASSERT(condition, message) \ ((condition) /* void() fails with -Winvalid-constexpr on clang 4.0.1 */ \ ? (void)0 \ - : fmt::detail::assert_fail(__FILE__, __LINE__, (message))) + : ::fmt::assert_fail(__FILE__, __LINE__, (message))) #endif #ifdef FMT_USE_INT128 @@ -463,12 +458,13 @@ enum { use_utf8 = !FMT_WIN32 || is_utf8_enabled }; static_assert(!FMT_UNICODE || use_utf8, "Unicode support requires compiling with /utf-8"); -template constexpr const char* narrow(const T*) { return nullptr; } -constexpr FMT_ALWAYS_INLINE const char* narrow(const char* s) { return s; } +template constexpr auto narrow(T*) -> char* { return nullptr; } +constexpr FMT_ALWAYS_INLINE auto narrow(const char* s) -> const char* { + return s; +} template -FMT_CONSTEXPR auto compare(const Char* s1, const Char* s2, std::size_t n) - -> int { +FMT_CONSTEXPR auto compare(const Char* s1, const Char* s2, size_t n) -> int { if (!is_constant_evaluated() && sizeof(Char) == 1) return memcmp(s1, s2, n); for (; n != 0; ++s1, ++s2, --n) { if (*s1 < *s2) return -1; @@ -540,7 +536,7 @@ template class basic_string_view { FMT_CONSTEXPR20 basic_string_view(const Char* s) : data_(s) { #if FMT_HAS_BUILTIN(__builtin_strlen) || FMT_GCC_VERSION || FMT_CLANG_VERSION if (std::is_same::value && !detail::is_constant_evaluated()) { - size_ = __builtin_strlen(detail::narrow(s)); // strlen is not costexpr. + size_ = __builtin_strlen(detail::narrow(s)); // strlen is not constexpr. return; } #endif @@ -616,19 +612,6 @@ template class basic_string_view { using string_view = basic_string_view; -// DEPRECATED! Will be merged with is_char and moved to detail. -template struct is_xchar : std::false_type {}; -template <> struct is_xchar : std::true_type {}; -template <> struct is_xchar : std::true_type {}; -template <> struct is_xchar : std::true_type {}; -#ifdef __cpp_char8_t -template <> struct is_xchar : std::true_type {}; -#endif - -// Specifies if `T` is a character (code unit) type. -template struct is_char : is_xchar {}; -template <> struct is_char : std::true_type {}; - template class basic_appender; using appender = basic_appender; @@ -781,7 +764,7 @@ class basic_specs { (static_cast(p) << precision_shift); } - constexpr bool dynamic() const { + constexpr auto dynamic() const -> bool { return (data_ & (width_mask | precision_mask)) != 0; } @@ -921,14 +904,47 @@ 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 { +// Specifies if `T` is a code unit type. +template struct is_code_unit : std::false_type {}; +template <> struct is_code_unit : std::true_type {}; +template <> struct is_code_unit : std::true_type {}; +template <> struct is_code_unit : std::true_type {}; +template <> struct is_code_unit : std::true_type {}; +#ifdef __cpp_char8_t +template <> struct is_code_unit : bool_constant {}; +#endif + // Constructs fmt::basic_string_view from types implicitly convertible // to it, deducing Char. Explicitly convertible types such as the ones returned // from FMT_STRING are intentionally excluded. -template ::value)> +template ::value)> constexpr auto to_string_view(const Char* s) -> basic_string_view { return s; } @@ -1057,11 +1073,11 @@ template constexpr auto count() -> int { return (B1 ? 1 : 0) + count(); } -template constexpr auto count_named_args() -> int { - return count::value...>(); +template constexpr auto count_named_args() -> int { + return count::value...>(); } -template constexpr auto count_static_named_args() -> int { - return count::value...>(); +template constexpr auto count_static_named_args() -> int { + return count::value...>(); } template struct named_arg_info { @@ -1069,7 +1085,7 @@ template struct named_arg_info { int id; }; -// named_args is non-const to suppress a bogus -Wmaybe-uninitalized in gcc 13. +// named_args is non-const to suppress a bogus -Wmaybe-uninitialized in gcc 13. template FMT_CONSTEXPR void check_for_duplicate(named_arg_info* named_args, int named_arg_index, @@ -1173,7 +1189,7 @@ template struct type_mapper { static auto map(ubitint) -> conditional_t; - template ::value)> + template ::value)> static auto map(T) -> conditional_t< std::is_same::value || std::is_same::value, Char, void>; @@ -1679,12 +1695,12 @@ template struct arg_pack {}; template class format_string_checker { private: - type types_[max_of(1, NUM_ARGS)]; - named_arg_info named_args_[max_of(1, NUM_NAMED_ARGS)]; + type types_[max_of(1, NUM_ARGS)]; + named_arg_info named_args_[max_of(1, NUM_NAMED_ARGS)]; compile_parse_context context_; using parse_func = auto (*)(parse_context&) -> const Char*; - parse_func parse_funcs_[max_of(1, NUM_ARGS)]; + parse_func parse_funcs_[max_of(1, NUM_ARGS)]; public: template @@ -2033,6 +2049,17 @@ struct has_back_insert_iterator_container_append< .append(std::declval(), std::declval()))>> : std::true_type {}; +template +struct has_back_insert_iterator_container_insert_at_end : std::false_type {}; + +template +struct has_back_insert_iterator_container_insert_at_end< + OutputIt, InputIt, + void_t()) + .insert(get_container(std::declval()).end(), + std::declval(), + std::declval()))>> : std::true_type {}; + // An optimized version of std::copy with the output value type (T). template ::value&& @@ -2047,6 +2074,8 @@ FMT_CONSTEXPR20 auto copy(InputIt begin, InputIt end, OutputIt out) template ::value && !has_back_insert_iterator_container_append< + OutputIt, InputIt>::value && + has_back_insert_iterator_container_insert_at_end< OutputIt, InputIt>::value)> FMT_CONSTEXPR20 auto copy(InputIt begin, InputIt end, OutputIt out) -> OutputIt { @@ -2056,7 +2085,11 @@ FMT_CONSTEXPR20 auto copy(InputIt begin, InputIt end, OutputIt out) } template ::value)> + FMT_ENABLE_IF(!(is_back_insert_iterator::value && + (has_back_insert_iterator_container_append< + OutputIt, InputIt>::value || + has_back_insert_iterator_container_insert_at_end< + OutputIt, InputIt>::value)))> FMT_CONSTEXPR auto copy(InputIt begin, InputIt end, OutputIt out) -> OutputIt { while (begin != end) *out++ = static_cast(*begin++); return out; @@ -2176,7 +2209,7 @@ template class value { static_assert(N <= 64, "unsupported _BitInt"); } - template ::value)> + template ::value)> constexpr FMT_INLINE value(T x FMT_BUILTIN) : char_value(x) { static_assert( std::is_same::value || std::is_same::value, @@ -2252,7 +2285,7 @@ template class value { custom.value = const_cast(&x); #endif } - custom.format = format_custom>; + custom.format = format_custom; } template ())> @@ -2263,10 +2296,10 @@ template class value { } // Formats an argument of a custom type, such as a user-defined class. - template + template static void format_custom(void* arg, parse_context& parse_ctx, Context& ctx) { - auto f = Formatter(); + auto f = formatter(); parse_ctx.advance_to(f.parse(parse_ctx)); using qualified_type = conditional_t(), const T, T>; @@ -2293,35 +2326,14 @@ 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; } -template +template constexpr auto encode_types() -> unsigned long long { - return static_cast(stored_type_constant::value) | - (encode_types() << packed_arg_bits); + return static_cast(stored_type_constant::value) | + (encode_types() << packed_arg_bits); } template @@ -2338,8 +2350,9 @@ template struct named_arg_store { // args_[0].named_args points to named_args to avoid bloating format_args. - arg_t args[1 + NUM_ARGS]; - named_arg_info named_args[NUM_NAMED_ARGS]; + arg_t args[1u + NUM_ARGS]; + named_arg_info + named_args[static_cast(NUM_NAMED_ARGS)]; template FMT_CONSTEXPR FMT_ALWAYS_INLINE named_arg_store(T&... values) @@ -2358,8 +2371,8 @@ struct named_arg_store { } named_arg_store(const named_arg_store& rhs) = delete; - named_arg_store& operator=(const named_arg_store& rhs) = delete; - named_arg_store& operator=(named_arg_store&& rhs) = delete; + auto operator=(const named_arg_store& rhs) -> named_arg_store& = delete; + auto operator=(named_arg_store&& rhs) -> named_arg_store& = delete; operator const arg_t*() const { return args + 1; } }; @@ -2372,7 +2385,7 @@ struct format_arg_store { // +1 to workaround a bug in gcc 7.5 that causes duplicated-branches warning. using type = conditional_t[max_of(1, NUM_ARGS)], + arg_t[max_of(1, NUM_ARGS)], named_arg_store>; type args; }; @@ -2656,22 +2669,17 @@ class context { private: appender out_; format_args args_; - FMT_NO_UNIQUE_ADDRESS detail::locale_ref loc_; + FMT_NO_UNIQUE_ADDRESS locale_ref loc_; public: - /// The character type for the output. - using char_type = char; - + using char_type = char; ///< The character type for the output. using iterator = appender; using format_arg = basic_format_arg; - using parse_context_type FMT_DEPRECATED = parse_context<>; - template using formatter_type FMT_DEPRECATED = formatter; enum { builtin_types = FMT_BUILTIN_TYPES }; /// 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; @@ -2692,7 +2700,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 { @@ -2779,9 +2787,6 @@ template concept formattable = is_formattable, Char>::value; #endif -template -using has_formatter FMT_DEPRECATED = std::is_constructible>; - // A formatter specialization for natively supported types. template struct formatter fmt, T&&... args) { return fmt::println(stdout, fmt, static_cast(args)...); } -FMT_END_EXPORT FMT_PRAGMA_CLANG(diagnostic pop) FMT_PRAGMA_GCC(pop_options) +FMT_END_EXPORT FMT_END_NAMESPACE #ifdef FMT_HEADER_ONLY diff --git a/include/spdlog/fmt/bundled/chrono.h b/include/spdlog/fmt/bundled/chrono.h index e0c81589..a788d3ec 100644 --- a/include/spdlog/fmt/bundled/chrono.h +++ b/include/spdlog/fmt/bundled/chrono.h @@ -38,6 +38,7 @@ FMT_BEGIN_NAMESPACE // Copyright Paul Dreik 2019 namespace safe_duration_cast { +// DEPRECATED! template ::value && std::numeric_limits::is_signed == @@ -161,17 +162,6 @@ auto safe_duration_cast(std::chrono::duration from, int& ec) -> To { using From = std::chrono::duration; ec = 0; - if (std::isnan(from.count())) { - // nan in, gives nan out. easy. - return To{std::numeric_limits::quiet_NaN()}; - } - // maybe we should also check if from is denormal, and decide what to do about - // it. - - // +-inf should be preserved. - if (std::isinf(from.count())) { - return To{from.count()}; - } // the basic idea is that we need to convert from count() in the from type // to count() in the To type, by multiplying it with this: @@ -282,8 +272,6 @@ namespace detail { #define FMT_NOMACRO template struct null {}; -inline auto localtime_r FMT_NOMACRO(...) -> null<> { return null<>(); } -inline auto localtime_s(...) -> null<> { return null<>(); } inline auto gmtime_r(...) -> null<> { return null<>(); } inline auto gmtime_s(...) -> null<> { return null<>(); } @@ -326,7 +314,7 @@ inline auto get_classic_locale() -> const std::locale& { } template struct codecvt_result { - static constexpr const size_t max_size = 32; + static constexpr size_t max_size = 32; CodeUnit buf[max_size]; CodeUnit* end; }; @@ -443,11 +431,7 @@ auto duration_cast(std::chrono::duration from) -> To { using common_rep = typename std::common_type::type; - - int ec = 0; - auto count = safe_duration_cast::lossless_integral_conversion( - from.count(), ec); - if (ec) throw_duration_error(); + common_rep count = from.count(); // This conversion is lossless. // Multiply from.count() by factor and check for overflow. if (const_check(factor::num != 1)) { @@ -458,6 +442,7 @@ auto duration_cast(std::chrono::duration from) -> To { count *= factor::num; } if (const_check(factor::den != 1)) count /= factor::den; + int ec = 0; auto to = To(safe_duration_cast::lossless_integral_conversion( count, ec)); @@ -471,6 +456,8 @@ template ::value)> auto duration_cast(std::chrono::duration from) -> To { #if FMT_SAFE_DURATION_CAST + // Preserve infinity and NaN. + if (!isfinite(from.count())) return static_cast(from.count()); // Throwing version of safe_duration_cast is only available for // integer to integer or float to float casts. int ec; @@ -487,7 +474,7 @@ template ::value)> auto duration_cast(std::chrono::duration from) -> To { - // Mixed integer <-> float cast is not supported by safe_duration_cast. + // Mixed integer <-> float cast is not supported by safe duration_cast. return std::chrono::duration_cast(from); } @@ -501,86 +488,10 @@ auto to_time_t(sys_time time_point) -> std::time_t { .count(); } -namespace tz { - -// DEPRECATED! -struct time_zone { - template - auto to_sys(LocalTime) -> sys_time { - return {}; - } -}; -template auto current_zone(T...) -> time_zone* { - return nullptr; -} - -template void _tzset(T...) {} -} // namespace tz - -// DEPRECATED! -inline void tzset_once() { - static bool init = []() { - using namespace tz; - _tzset(); - return false; - }(); - ignore_unused(init); -} } // namespace detail FMT_BEGIN_EXPORT -/** - * Converts given time since epoch as `std::time_t` value into calendar time, - * expressed in local time. Unlike `std::localtime`, this function is - * thread-safe on most platforms. - */ -FMT_DEPRECATED inline auto localtime(std::time_t time) -> std::tm { - struct dispatcher { - std::time_t time_; - std::tm tm_; - - inline dispatcher(std::time_t t) : time_(t) {} - - inline auto run() -> bool { - using namespace fmt::detail; - return handle(localtime_r(&time_, &tm_)); - } - - inline auto handle(std::tm* tm) -> bool { return tm != nullptr; } - - inline auto handle(detail::null<>) -> bool { - using namespace fmt::detail; - return fallback(localtime_s(&tm_, &time_)); - } - - inline auto fallback(int res) -> bool { return res == 0; } - -#if !FMT_MSC_VERSION - inline auto fallback(detail::null<>) -> bool { - using namespace fmt::detail; - std::tm* tm = std::localtime(&time_); - if (tm) tm_ = *tm; - return tm != nullptr; - } -#endif - }; - dispatcher lt(time); - // Too big time values may be unsupported. - if (!lt.run()) FMT_THROW(format_error("time_t value out of range")); - return lt.tm_; -} - -#if FMT_USE_LOCAL_TIME -template -FMT_DEPRECATED auto localtime(std::chrono::local_time time) - -> std::tm { - using namespace std::chrono; - using namespace detail::tz; - return localtime(detail::to_time_t(current_zone()->to_sys(time))); -} -#endif - /** * Converts given time since epoch as `std::time_t` value into calendar time, * expressed in Coordinated Universal Time (UTC). Unlike `std::gmtime`, this @@ -652,7 +563,7 @@ inline void write_digit2_separated(char* buf, unsigned a, unsigned b, // Add ASCII '0' to each digit byte and insert separators. digits |= 0x3030003030003030 | (usep << 16) | (usep << 40); - constexpr const size_t len = 8; + constexpr size_t len = 8; if (const_check(is_big_endian())) { char tmp[len]; std::memcpy(tmp, &digits, len); @@ -1000,16 +911,16 @@ template struct has_tm_zone> : std::true_type {}; template ::value)> -bool set_tm_zone(T& time, char* tz) { +auto set_tm_zone(T& time, char* tz) -> bool { time.tm_zone = tz; return true; } template ::value)> -bool set_tm_zone(T&, char*) { +auto set_tm_zone(T&, char*) -> bool { return false; } -inline char* utc() { +inline auto utc() -> char* { static char tz[] = "UTC"; return tz; } @@ -2230,7 +2141,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/spdlog/fmt/bundled/color.h b/include/spdlog/fmt/bundled/color.h index 638f15b4..b69c1488 100644 --- a/include/spdlog/fmt/bundled/color.h +++ b/include/spdlog/fmt/bundled/color.h @@ -375,19 +375,17 @@ template struct ansi_color_escape { // 10 more. if (is_background) value += 10u; - size_t index = 0; - buffer[index++] = static_cast('\x1b'); - buffer[index++] = static_cast('['); + buffer[size++] = static_cast('\x1b'); + buffer[size++] = static_cast('['); if (value >= 100u) { - buffer[index++] = static_cast('1'); + buffer[size++] = static_cast('1'); value %= 100u; } - buffer[index++] = static_cast('0' + value / 10u); - buffer[index++] = static_cast('0' + value % 10u); + buffer[size++] = static_cast('0' + value / 10u); + buffer[size++] = static_cast('0' + value % 10u); - buffer[index++] = static_cast('m'); - buffer[index++] = static_cast('\0'); + buffer[size++] = static_cast('m'); return; } @@ -398,7 +396,7 @@ template struct ansi_color_escape { to_esc(color.r, buffer + 7, ';'); to_esc(color.g, buffer + 11, ';'); to_esc(color.b, buffer + 15, 'm'); - buffer[19] = static_cast(0); + size = 19; } FMT_CONSTEXPR ansi_color_escape(emphasis em) noexcept { uint8_t em_codes[num_emphases] = {}; @@ -411,26 +409,28 @@ template struct ansi_color_escape { if (has_emphasis(em, emphasis::conceal)) em_codes[6] = 8; if (has_emphasis(em, emphasis::strikethrough)) em_codes[7] = 9; - size_t index = 0; + buffer[size++] = static_cast('\x1b'); + buffer[size++] = static_cast('['); + for (size_t i = 0; i < num_emphases; ++i) { if (!em_codes[i]) continue; - buffer[index++] = static_cast('\x1b'); - buffer[index++] = static_cast('['); - buffer[index++] = static_cast('0' + em_codes[i]); - buffer[index++] = static_cast('m'); + buffer[size++] = static_cast('0' + em_codes[i]); + buffer[size++] = static_cast(';'); } - buffer[index++] = static_cast(0); + + buffer[size - 1] = static_cast('m'); } FMT_CONSTEXPR operator const Char*() const noexcept { return buffer; } FMT_CONSTEXPR auto begin() const noexcept -> const Char* { return buffer; } - FMT_CONSTEXPR20 auto end() const noexcept -> const Char* { - return buffer + basic_string_view(buffer).size(); + FMT_CONSTEXPR auto end() const noexcept -> const Char* { + return buffer + size; } private: static constexpr size_t num_emphases = 8; - Char buffer[7u + 3u * num_emphases + 1u]; + Char buffer[7u + 4u * num_emphases]; + size_t size = 0; static FMT_CONSTEXPR void to_esc(uint8_t c, Char* out, char delimiter) noexcept { diff --git a/include/spdlog/fmt/bundled/compile.h b/include/spdlog/fmt/bundled/compile.h index 08d9427f..f711ba41 100644 --- a/include/spdlog/fmt/bundled/compile.h +++ b/include/spdlog/fmt/bundled/compile.h @@ -22,8 +22,6 @@ FMT_EXPORT class compiled_string {}; template struct is_compiled_string : std::is_base_of {}; -namespace detail { - /** * Converts a string literal `s` into a format string that will be parsed at * compile time and converted into efficient formatting code. Requires C++17 @@ -41,18 +39,40 @@ namespace detail { # define FMT_COMPILE(s) FMT_STRING(s) #endif +/** + * Converts a string literal into a format string that will be parsed at + * compile time and converted into efficient formatting code. Requires support + * for class types in constant template parameters (a C++20 feature). + * + * **Example**: + * + * // Converts 42 into std::string using the most efficient method and no + * // runtime format string processing. + * using namespace fmt::literals; + * std::string s = fmt::format("{}"_cf, 42); + */ +#if FMT_USE_NONTYPE_TEMPLATE_ARGS +inline namespace literals { +template constexpr auto operator""_cf() { + return FMT_COMPILE(Str.data); +} +} // namespace literals +#endif + +namespace detail { + template -auto first(const T& value, const Tail&...) -> const T& { +constexpr auto first(const T& value, const Tail&...) -> const T& { return value; } #if defined(__cpp_if_constexpr) && defined(__cpp_return_type_deduction) -template struct type_list {}; +template struct type_list {}; // Returns a reference to the argument at index N from [first, rest...]. template -constexpr const auto& get([[maybe_unused]] const T& first, - [[maybe_unused]] const Args&... rest) { +constexpr auto get([[maybe_unused]] const T& first, + [[maybe_unused]] const Args&... rest) -> const auto& { static_assert(N < 1 + sizeof...(Args), "index is out of bounds"); if constexpr (N == 0) return first; @@ -84,8 +104,8 @@ FMT_CONSTEXPR auto get_arg_index_by_name(basic_string_view name) -> int { } template -constexpr int get_arg_index_by_name(basic_string_view name, - type_list) { +constexpr auto get_arg_index_by_name(basic_string_view name, + type_list) -> int { return get_arg_index_by_name(name); } @@ -105,8 +125,8 @@ template struct text { basic_string_view data; using char_type = Char; - template - constexpr OutputIt format(OutputIt out, const Args&...) const { + template + constexpr auto format(OutputIt out, const T&...) const -> OutputIt { return write(out, data); } }; @@ -115,8 +135,8 @@ template struct is_compiled_format> : std::true_type {}; template -constexpr text make_text(basic_string_view s, size_t pos, - size_t size) { +constexpr auto make_text(basic_string_view s, size_t pos, size_t size) + -> text { return {{&s[pos], size}}; } @@ -124,8 +144,8 @@ template struct code_unit { Char value; using char_type = Char; - template - constexpr OutputIt format(OutputIt out, const Args&...) const { + template + constexpr auto format(OutputIt out, const T&...) const -> OutputIt { *out++ = value; return out; } @@ -133,7 +153,7 @@ template struct code_unit { // This ensures that the argument type is convertible to `const T&`. template -constexpr const T& get_arg_checked(const Args&... args) { +constexpr auto get_arg_checked(const Args&... args) -> const T& { const auto& arg = detail::get(args...); if constexpr (detail::is_named_arg>()) { return arg.value; @@ -146,13 +166,13 @@ template struct is_compiled_format> : std::true_type {}; // A replacement field that refers to argument N. -template struct field { +template struct field { using char_type = Char; - template - constexpr OutputIt format(OutputIt out, const Args&... args) const { - const T& arg = get_arg_checked(args...); - if constexpr (std::is_convertible>::value) { + template + constexpr auto format(OutputIt out, const T&... args) const -> OutputIt { + const V& arg = get_arg_checked(args...); + if constexpr (std::is_convertible>::value) { auto s = basic_string_view(arg); return copy(s.begin(), s.end(), out); } else { @@ -170,10 +190,10 @@ template struct runtime_named_field { basic_string_view name; template - constexpr static bool try_format_argument( + constexpr static auto try_format_argument( OutputIt& out, // [[maybe_unused]] due to unused-but-set-parameter warning in GCC 7,8,9 - [[maybe_unused]] basic_string_view arg_name, const T& arg) { + [[maybe_unused]] basic_string_view arg_name, const T& arg) -> bool { if constexpr (is_named_arg::type>::value) { if (arg_name == arg.name) { out = write(out, arg.value); @@ -183,8 +203,8 @@ template struct runtime_named_field { return false; } - template - constexpr OutputIt format(OutputIt out, const Args&... args) const { + template + constexpr auto format(OutputIt out, const T&... args) const -> OutputIt { bool found = (try_format_argument(out, name, args) || ...); if (!found) { FMT_THROW(format_error("argument with specified name is not found")); @@ -197,17 +217,17 @@ template struct is_compiled_format> : std::true_type {}; // A replacement field that refers to argument N and has format specifiers. -template struct spec_field { +template struct spec_field { using char_type = Char; - formatter fmt; + formatter fmt; - template - constexpr FMT_INLINE OutputIt format(OutputIt out, - const Args&... args) const { + template + constexpr FMT_INLINE auto format(OutputIt out, const T&... args) const + -> OutputIt { const auto& vargs = fmt::make_format_args>(args...); basic_format_context ctx(out, vargs); - return fmt.format(get_arg_checked(args...), ctx); + return fmt.format(get_arg_checked(args...), ctx); } }; @@ -219,8 +239,8 @@ template struct concat { R rhs; using char_type = typename L::char_type; - template - constexpr OutputIt format(OutputIt out, const Args&... args) const { + template + constexpr auto format(OutputIt out, const T&... args) const -> OutputIt { out = lhs.format(out, args...); return rhs.format(out, args...); } @@ -230,14 +250,14 @@ template struct is_compiled_format> : std::true_type {}; template -constexpr concat make_concat(L lhs, R rhs) { +constexpr auto make_concat(L lhs, R rhs) -> concat { return {lhs, rhs}; } struct unknown_format {}; template -constexpr size_t parse_text(basic_string_view str, size_t pos) { +constexpr auto parse_text(basic_string_view str, size_t pos) -> size_t { for (size_t size = str.size(); pos != size; ++pos) { if (str[pos] == '{' || str[pos] == '}') break; } @@ -270,8 +290,8 @@ template struct parse_specs_result { enum { manual_indexing_id = -1 }; template -constexpr parse_specs_result parse_specs(basic_string_view str, - size_t pos, int next_arg_id) { +constexpr auto parse_specs(basic_string_view str, size_t pos, + int next_arg_id) -> parse_specs_result { str.remove_prefix(pos); auto ctx = compile_parse_context(str, max_value(), nullptr, next_arg_id); @@ -285,16 +305,16 @@ template struct arg_id_handler { arg_id_kind kind; arg_ref arg_id; - constexpr int on_auto() { + constexpr auto on_auto() -> int { FMT_ASSERT(false, "handler cannot be used with automatic indexing"); return 0; } - constexpr int on_index(int id) { + constexpr auto on_index(int id) -> int { kind = arg_id_kind::index; arg_id = arg_ref(id); return 0; } - constexpr int on_name(basic_string_view id) { + constexpr auto on_name(basic_string_view id) -> int { kind = arg_id_kind::name; arg_id = arg_ref(id); return 0; @@ -433,27 +453,28 @@ FMT_BEGIN_EXPORT #if defined(__cpp_if_constexpr) && defined(__cpp_return_type_deduction) -template ::value)> -FMT_INLINE std::basic_string format(const CompiledFormat& cf, - const Args&... args) { +FMT_INLINE FMT_CONSTEXPR_STRING auto format(const CompiledFormat& cf, + const T&... args) + -> std::basic_string { auto s = std::basic_string(); cf.format(std::back_inserter(s), args...); return s; } -template ::value)> -constexpr FMT_INLINE OutputIt format_to(OutputIt out, const CompiledFormat& cf, - const Args&... args) { +constexpr FMT_INLINE auto format_to(OutputIt out, const CompiledFormat& cf, + const T&... args) -> OutputIt { return cf.format(out, args...); } -template ::value)> -FMT_INLINE std::basic_string format(const S&, - Args&&... args) { +FMT_INLINE FMT_CONSTEXPR_STRING auto format(const S&, T&&... args) + -> std::basic_string { if constexpr (std::is_same::value) { constexpr auto str = basic_string_view(S()); if constexpr (str.size() == 2 && str[0] == '{' && str[1] == '}') { @@ -466,72 +487,97 @@ FMT_INLINE std::basic_string format(const S&, } } } - constexpr auto compiled = detail::compile(S()); + constexpr auto compiled = detail::compile(S()); if constexpr (std::is_same, detail::unknown_format>()) { return fmt::format( static_cast>(S()), - std::forward(args)...); + std::forward(args)...); } else { - return fmt::format(compiled, std::forward(args)...); + return fmt::format(compiled, std::forward(args)...); } } -template ::value)> -FMT_CONSTEXPR OutputIt format_to(OutputIt out, const S&, Args&&... args) { - constexpr auto compiled = detail::compile(S()); +FMT_CONSTEXPR auto format_to(OutputIt out, const S&, T&&... args) -> OutputIt { + constexpr auto compiled = detail::compile(S()); if constexpr (std::is_same, detail::unknown_format>()) { return fmt::format_to( out, static_cast>(S()), - std::forward(args)...); + std::forward(args)...); } else { - return fmt::format_to(out, compiled, std::forward(args)...); + return fmt::format_to(out, compiled, std::forward(args)...); } } #endif -template ::value)> -auto format_to_n(OutputIt out, size_t n, const S& fmt, Args&&... args) +auto format_to_n(OutputIt out, size_t n, const S& fmt, T&&... args) -> format_to_n_result { using traits = detail::fixed_buffer_traits; auto buf = detail::iterator_buffer(out, n); - fmt::format_to(std::back_inserter(buf), fmt, std::forward(args)...); + fmt::format_to(std::back_inserter(buf), fmt, std::forward(args)...); return {buf.out(), buf.count()}; } -template ::value)> -FMT_CONSTEXPR20 auto formatted_size(const S& fmt, const Args&... args) - -> size_t { +FMT_CONSTEXPR20 auto formatted_size(const S& fmt, T&&... args) -> size_t { auto buf = detail::counting_buffer<>(); - fmt::format_to(appender(buf), fmt, args...); + fmt::format_to(appender(buf), fmt, std::forward(args)...); return buf.count(); } -template ::value)> -void print(std::FILE* f, const S& fmt, const Args&... args) { +void print(std::FILE* f, const S& fmt, T&&... args) { auto buf = memory_buffer(); - fmt::format_to(appender(buf), fmt, args...); + fmt::format_to(appender(buf), fmt, std::forward(args)...); detail::print(f, {buf.data(), buf.size()}); } -template ::value)> -void print(const S& fmt, const Args&... args) { - print(stdout, fmt, args...); +void print(const S& fmt, T&&... args) { + print(stdout, fmt, std::forward(args)...); } -#if FMT_USE_NONTYPE_TEMPLATE_ARGS -inline namespace literals { -template constexpr auto operator""_cf() { - return FMT_COMPILE(Str.data); -} -} // namespace literals -#endif +template class static_format_result { + private: + char data[N]; + + public: + template ::value)> + explicit FMT_CONSTEXPR static_format_result(const S& fmt, T&&... args) { + *fmt::format_to(data, fmt, std::forward(args)...) = '\0'; + } + + auto str() const -> fmt::string_view { return {data, N - 1}; } + auto c_str() const -> const char* { return data; } +}; + +/** + * Formats arguments according to the format string `fmt_str` and produces + * a string of the exact required size at compile time. Both the format string + * and the arguments must be compile-time expressions. + * + * The resulting string can be accessed as a C string via `c_str()` or as + * a `fmt::string_view` via `str()`. + * + * **Example**: + * + * // Produces the static string "42" at compile time. + * static constexpr auto result = FMT_STATIC_FORMAT("{}", 42); + * const char* s = result.c_str(); + */ +#define FMT_STATIC_FORMAT(fmt_str, ...) \ + fmt::static_format_result< \ + fmt::formatted_size(FMT_COMPILE(fmt_str), __VA_ARGS__) + 1>( \ + FMT_COMPILE(fmt_str), __VA_ARGS__) FMT_END_EXPORT FMT_END_NAMESPACE diff --git a/include/spdlog/fmt/bundled/format-inl.h b/include/spdlog/fmt/bundled/format-inl.h index a1e01661..9d568dca 100644 --- a/include/spdlog/fmt/bundled/format-inl.h +++ b/include/spdlog/fmt/bundled/format-inl.h @@ -22,7 +22,7 @@ #include "format.h" -#if FMT_USE_LOCALE +#if FMT_USE_LOCALE && !defined(FMT_MODULE) # include #endif @@ -31,14 +31,49 @@ #endif FMT_BEGIN_NAMESPACE -namespace detail { +#ifndef FMT_CUSTOM_ASSERT_FAIL FMT_FUNC void assert_fail(const char* file, int line, const char* message) { // Use unchecked std::fprintf to avoid triggering another assertion when // writing to stderr fails. fprintf(stderr, "%s:%d: assertion failed: %s", file, line, message); abort(); } +#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 { FMT_FUNC void format_error_code(detail::buffer& out, int error_code, string_view message) noexcept { @@ -79,33 +114,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()); @@ -133,14 +141,13 @@ FMT_FUNC auto write_loc(appender out, loc_value value, } // namespace detail FMT_FUNC void report_error(const char* message) { -#if FMT_USE_EXCEPTIONS - // Use FMT_THROW instead of throw to avoid bogus unreachable code warnings - // from MSVC. - FMT_THROW(format_error(message)); -#else - fputs(message, stderr); - abort(); +#if FMT_MSC_VERSION || defined(__NVCC__) + // Silence unreachable code warnings in MSVC and NVCC because these + // are nearly impossible to fix in a generic code. + volatile bool b = true; + if (!b) return; #endif + FMT_THROW(format_error(message)); } template typename Locale::id format_facet::id; @@ -174,11 +181,11 @@ inline auto operator==(basic_fp x, basic_fp y) -> bool { } // Compilers should be able to optimize this into the ror instruction. -FMT_CONSTEXPR inline auto rotr(uint32_t n, uint32_t r) noexcept -> uint32_t { +FMT_INLINE auto rotr(uint32_t n, uint32_t r) noexcept -> uint32_t { r &= 31; return (n >> r) | (n << (32 - r)); } -FMT_CONSTEXPR inline auto rotr(uint64_t n, uint32_t r) noexcept -> uint64_t { +FMT_INLINE auto rotr(uint64_t n, uint32_t r) noexcept -> uint64_t { r &= 63; return (n >> r) | (n << (64 - r)); } @@ -275,7 +282,7 @@ template <> struct cache_accessor { static auto get_cached_power(int k) noexcept -> uint64_t { FMT_ASSERT(k >= float_info::min_k && k <= float_info::max_k, "k is out of range"); - static constexpr const uint64_t pow10_significands[] = { + static constexpr uint64_t pow10_significands[] = { 0x81ceb32c4b43fcf5, 0xa2425ff75e14fc32, 0xcad2f7f5359a3b3f, 0xfd87b5f28300ca0e, 0x9e74d1b791e07e49, 0xc612062576589ddb, 0xf79687aed3eec552, 0x9abe14cd44753b53, 0xc16d9a0095928a28, @@ -370,7 +377,7 @@ template <> struct cache_accessor { FMT_ASSERT(k >= float_info::min_k && k <= float_info::max_k, "k is out of range"); - static constexpr const uint128_fallback pow10_significands[] = { + static constexpr uint128_fallback pow10_significands[] = { #if FMT_USE_FULL_CACHE_DRAGONBOX {0xff77b1fcbebcdc4f, 0x25e8e89c13bb0f7b}, {0x9faacf3df73609b1, 0x77b191618c54e9ad}, @@ -1037,7 +1044,7 @@ template <> struct cache_accessor { #if FMT_USE_FULL_CACHE_DRAGONBOX return pow10_significands[k - float_info::min_k]; #else - static constexpr const uint64_t powers_of_5_64[] = { + static constexpr uint64_t powers_of_5_64[] = { 0x0000000000000001, 0x0000000000000005, 0x0000000000000019, 0x000000000000007d, 0x0000000000000271, 0x0000000000000c35, 0x0000000000003d09, 0x000000000001312d, 0x000000000005f5e1, @@ -1149,8 +1156,8 @@ auto is_left_endpoint_integer_shorter_interval(int exponent) noexcept -> bool { exponent <= case_shorter_interval_left_endpoint_upper_threshold; } -// Remove trailing zeros from n and return the number of zeros removed (float) -FMT_INLINE int remove_trailing_zeros(uint32_t& n, int s = 0) noexcept { +// Remove trailing zeros from n and return the number of zeros removed (float). +FMT_INLINE auto remove_trailing_zeros(uint32_t& n, int s = 0) noexcept -> int { FMT_ASSERT(n != 0, ""); // Modular inverse of 5 (mod 2^32): (mod_inv_5 * 5) mod 2^32 = 1. constexpr uint32_t mod_inv_5 = 0xcccccccd; @@ -1170,22 +1177,19 @@ FMT_INLINE int remove_trailing_zeros(uint32_t& n, int s = 0) noexcept { return s; } -// Removes trailing zeros and returns the number of zeros removed (double) -FMT_INLINE int remove_trailing_zeros(uint64_t& n) noexcept { +// Removes trailing zeros and returns the number of zeros removed (double). +FMT_INLINE auto remove_trailing_zeros(uint64_t& n) noexcept -> int { FMT_ASSERT(n != 0, ""); - // This magic number is ceil(2^90 / 10^8). - constexpr uint64_t magic_number = 12379400392853802749ull; - auto nm = umul128(n, magic_number); - // Is n is divisible by 10^8? - if ((nm.high() & ((1ull << (90 - 64)) - 1)) == 0 && nm.low() < magic_number) { + constexpr uint32_t ten_pow_8 = 100000000u; + if ((n % ten_pow_8) == 0) { // If yes, work with the quotient... - auto n32 = static_cast(nm.high() >> (90 - 64)); + auto n32 = static_cast(n / ten_pow_8); // ... and use the 32 bit variant of the function - int s = remove_trailing_zeros(n32, 8); + int num_zeros = remove_trailing_zeros(n32, 8); n = n32; - return s; + return num_zeros; } // If n is not divisible by 10^8, work with n itself. @@ -1210,7 +1214,7 @@ FMT_INLINE int remove_trailing_zeros(uint64_t& n) noexcept { // The main algorithm for shorter interval case template -FMT_INLINE decimal_fp shorter_interval_case(int exponent) noexcept { +FMT_INLINE auto shorter_interval_case(int exponent) noexcept -> decimal_fp { decimal_fp ret_value; // Compute k and beta const int minus_k = floor_log10_pow2_minus_log10_4_over_3(exponent); @@ -1454,8 +1458,8 @@ FMT_FUNC void vformat_to(buffer& buf, string_view fmt, format_args args, auto out = appender(buf); if (fmt.size() == 2 && equal2(fmt.data(), "{}")) return args.get(0).visit(default_arg_formatter{out}); - parse_format_string( - fmt, format_handler{parse_context(fmt), {out, args, loc}}); + parse_format_string(fmt, + format_handler<>{parse_context<>(fmt), {out, args, loc}}); } template struct span { @@ -1546,10 +1550,11 @@ template class glibc_file : public file_base { void advance_write_buffer(size_t size) { this->file_->_IO_write_ptr += size; } - bool needs_flush() const { + auto needs_flush() const -> bool { if ((this->file_->_flags & line_buffered) == 0) return false; char* end = this->file_->_IO_write_end; - return memchr(end, '\n', to_unsigned(this->file_->_IO_write_ptr - end)); + auto size = max_of(this->file_->_IO_write_ptr - end, 0); + return memchr(end, '\n', static_cast(size)); } void flush() { fflush_unlocked(this->file_); } @@ -1573,7 +1578,7 @@ template class apple_file : public file_base { void init_buffer() { if (this->file_->_p) return; // Force buffer initialization by placing and removing a char in a buffer. - putc_unlocked(0, this->file_); + if (!FMT_CLANG_ANALYZER) putc_unlocked(0, this->file_); --this->file_->_p; ++this->file_->_w; } @@ -1594,7 +1599,7 @@ template class apple_file : public file_base { this->file_->_w -= size; } - bool needs_flush() const { + auto needs_flush() const -> bool { if ((this->file_->_flags & line_buffered) == 0) return false; return memchr(this->file_->_p + this->file_->_w, '\n', to_unsigned(-this->file_->_w)); diff --git a/include/spdlog/fmt/bundled/format.h b/include/spdlog/fmt/bundled/format.h index 50e57144..c3a1bda0 100644 --- a/include/spdlog/fmt/bundled/format.h +++ b/include/spdlog/fmt/bundled/format.h @@ -44,6 +44,7 @@ # include // std::signbit # include // std::byte # include // uint32_t +# include // std::malloc, std::free # include // std::memcpy # include // std::numeric_limits # include // std::bad_alloc @@ -117,6 +118,41 @@ # define FMT_NOINLINE #endif +#ifdef FMT_DEPRECATED +// Use the provided definition. +#elif FMT_HAS_CPP14_ATTRIBUTE(deprecated) +# define FMT_DEPRECATED [[deprecated]] +#else +# define FMT_DEPRECATED /* deprecated */ +#endif + +// Detect constexpr std::string. +#if !FMT_USE_CONSTEVAL +# define FMT_USE_CONSTEXPR_STRING 0 +#elif defined(__cpp_lib_constexpr_string) && \ + __cpp_lib_constexpr_string >= 201907L +# if FMT_CLANG_VERSION && FMT_GLIBCXX_RELEASE +// clang + libstdc++ are able to work only starting with gcc13.3 +// https://gcc.gnu.org/bugzilla/show_bug.cgi?id=113294 +# if FMT_GLIBCXX_RELEASE < 13 +# define FMT_USE_CONSTEXPR_STRING 0 +# elif FMT_GLIBCXX_RELEASE == 13 && __GLIBCXX__ < 20240521 +# define FMT_USE_CONSTEXPR_STRING 0 +# else +# define FMT_USE_CONSTEXPR_STRING 1 +# endif +# else +# define FMT_USE_CONSTEXPR_STRING 1 +# endif +#else +# define FMT_USE_CONSTEXPR_STRING 0 +#endif +#if FMT_USE_CONSTEXPR_STRING +# define FMT_CONSTEXPR_STRING constexpr +#else +# define FMT_CONSTEXPR_STRING +#endif + // GCC 4.9 doesn't support qualified names in specializations. namespace std { template struct iterator_traits> { @@ -129,28 +165,19 @@ template struct iterator_traits> { }; } // namespace std -#ifndef FMT_THROW -# if FMT_USE_EXCEPTIONS -# if FMT_MSC_VERSION || defined(__NVCC__) -FMT_BEGIN_NAMESPACE -namespace detail { -template inline void do_throw(const Exception& x) { - // Silence unreachable code warnings in MSVC and NVCC because these - // are nearly impossible to fix in a generic code. - volatile bool b = true; - if (b) throw x; -} -} // namespace detail -FMT_END_NAMESPACE -# define FMT_THROW(x) detail::do_throw(x) -# else -# define FMT_THROW(x) throw x -# endif -# else -# define FMT_THROW(x) \ - ::fmt::detail::assert_fail(__FILE__, __LINE__, (x).what()) -# endif // FMT_USE_EXCEPTIONS -#endif // FMT_THROW +#ifdef FMT_THROW +// Use the provided definition. +#elif FMT_USE_EXCEPTIONS +# define FMT_THROW(x) throw x +#else +# define FMT_THROW(x) ::fmt::assert_fail(__FILE__, __LINE__, (x).what()) +#endif + +#ifdef __clang_analyzer__ +# define FMT_CLANG_ANALYZER 1 +#else +# define FMT_CLANG_ANALYZER 0 +#endif // Defining FMT_REDUCE_INT_INSTANTIATIONS to 1, will reduce the number of // integer formatter template instantiations to just one by only using the @@ -491,6 +518,11 @@ template constexpr auto to_pointer(OutputIt, size_t) -> T* { return nullptr; } +template FMT_CONSTEXPR auto to_pointer(T*& ptr, size_t n) -> T* { + T* begin = ptr; + ptr += n; + return begin; +} template FMT_CONSTEXPR20 auto to_pointer(basic_appender it, size_t n) -> T* { buffer& buf = get_container(it); @@ -526,6 +558,8 @@ FMT_CONSTEXPR auto fill_n(OutputIt out, Size count, const T& value) template FMT_CONSTEXPR20 auto fill_n(T* out, Size count, char value) -> T* { if (is_constant_evaluated()) return fill_n(out, count, value); + static_assert(sizeof(T) == 1, + "sizeof(T) must be 1 to use char for initialization"); std::memset(out, value, to_unsigned(count)); return out + count; } @@ -555,10 +589,10 @@ FMT_CONSTEXPR FMT_NOINLINE auto copy_noinline(InputIt begin, InputIt end, */ FMT_CONSTEXPR inline auto utf8_decode(const char* s, uint32_t* c, int* e) -> const char* { - constexpr const int masks[] = {0x00, 0x7f, 0x1f, 0x0f, 0x07}; - constexpr const uint32_t mins[] = {4194304, 0, 128, 2048, 65536}; - constexpr const int shiftc[] = {0, 18, 12, 6, 0}; - constexpr const int shifte[] = {0, 6, 4, 2, 0}; + constexpr int masks[] = {0x00, 0x7f, 0x1f, 0x0f, 0x07}; + constexpr uint32_t mins[] = {4194304, 0, 128, 2048, 65536}; + constexpr int shiftc[] = {0, 18, 12, 6, 0}; + constexpr int shifte[] = {0, 6, 4, 2, 0}; int len = "\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\0\0\0\0\0\0\0\0\2\2\2\2\3\3\4" [static_cast(*s) >> 3]; @@ -629,21 +663,9 @@ FMT_CONSTEXPR void for_each_codepoint(string_view s, F f) { } while (buf_ptr < buf + num_chars_left); } -template -inline auto compute_width(basic_string_view s) -> size_t { - return s.size(); -} - -// Computes approximate display width of a UTF-8 string. -FMT_CONSTEXPR inline auto compute_width(string_view s) -> size_t { - size_t num_code_points = 0; - // It is not a lambda for compatibility with C++14. - struct count_code_points { - size_t* count; - FMT_CONSTEXPR auto operator()(uint32_t cp, string_view) const -> bool { - *count += to_unsigned( - 1 + - (cp >= 0x1100 && +FMT_CONSTEXPR inline auto display_width_of(uint32_t cp) noexcept -> size_t { + return to_unsigned( + 1 + (cp >= 0x1100 && (cp <= 0x115f || // Hangul Jamo init. consonants cp == 0x2329 || // LEFT-POINTING ANGLE BRACKET cp == 0x232a || // RIGHT-POINTING ANGLE BRACKET @@ -661,32 +683,6 @@ FMT_CONSTEXPR inline auto compute_width(string_view s) -> size_t { (cp >= 0x1f300 && cp <= 0x1f64f) || // Supplemental Symbols and Pictographs: (cp >= 0x1f900 && cp <= 0x1f9ff)))); - return true; - } - }; - // We could avoid branches by using utf8_decode directly. - for_each_codepoint(s, count_code_points{&num_code_points}); - return num_code_points; -} - -template -inline auto code_point_index(basic_string_view s, size_t n) -> size_t { - return min_of(n, s.size()); -} - -// Calculates the index of the nth code point in a UTF-8 string. -inline auto code_point_index(string_view s, size_t n) -> size_t { - size_t result = s.size(); - const char* begin = s.begin(); - for_each_codepoint(s, [begin, &n, &result](uint32_t, string_view sv) { - if (n != 0) { - --n; - return true; - } - result = to_unsigned(sv.begin() - begin); - return false; - }); - return result; } template struct is_integral : std::is_integral {}; @@ -730,6 +726,9 @@ struct is_fast_float : bool_constant::is_iec559 && sizeof(T) <= sizeof(double)> {}; template struct is_fast_float : std::false_type {}; +template +using fast_float_t = conditional_t; + template using is_double_double = bool_constant::digits == 106>; @@ -738,18 +737,26 @@ using is_double_double = bool_constant::digits == 106>; #endif // An allocator that uses malloc/free to allow removing dependency on the C++ -// standard libary runtime. -template struct allocator { +// standard libary runtime. std::decay is used for back_inserter to be found by +// ADL when applied to memory_buffer. +template struct allocator : private std::decay { using value_type = T; - T* allocate(size_t n) { + auto allocate(size_t n) -> T* { FMT_ASSERT(n <= max_value() / sizeof(T), ""); - T* p = static_cast(malloc(n * sizeof(T))); + T* p = static_cast(std::malloc(n * sizeof(T))); if (!p) FMT_THROW(std::bad_alloc()); return p; } - void deallocate(T* p, size_t) { free(p); } + void deallocate(T* p, size_t) { std::free(p); } + + constexpr friend auto operator==(allocator, allocator) noexcept -> bool { + return true; // All instances of this allocator are equivalent. + } + constexpr friend auto operator!=(allocator, allocator) noexcept -> bool { + return false; + } }; } // namespace detail @@ -825,11 +832,32 @@ class basic_memory_buffer : public detail::buffer { FMT_CONSTEXPR20 ~basic_memory_buffer() { deallocate(); } private: + template :: + propagate_on_container_move_assignment::value)> + FMT_CONSTEXPR20 auto move_alloc(basic_memory_buffer& other) -> bool { + alloc_ = std::move(other.alloc_); + return true; + } + // If the allocator does not propagate then copy the data from other. + template :: + propagate_on_container_move_assignment::value)> + FMT_CONSTEXPR20 auto move_alloc(basic_memory_buffer& other) -> bool { + T* data = other.data(); + if (alloc_ == other.alloc_ || data == other.store_) return true; + size_t size = other.size(); + // Perform copy operation, allocators are different. + this->resize(size); + detail::copy(data, data + size, this->data()); + return false; + } + // Move data from other to this buffer. FMT_CONSTEXPR20 void move(basic_memory_buffer& other) { - alloc_ = std::move(other.alloc_); T* data = other.data(); size_t size = other.size(), capacity = other.capacity(); + if (!move_alloc(other)) return; if (data == other.store_) { this->set(store_, capacity); detail::copy(other.store_, other.store_ + size, store_); @@ -918,7 +946,7 @@ class string_buffer { inline string_buffer() : buf_(str_) {} inline operator writer() { return buf_; } - inline std::string& str() { return str_; } + inline auto str() -> std::string& { return str_; } }; template @@ -1044,7 +1072,7 @@ inline auto do_count_digits(uint64_t n) -> int { 10, 11, 11, 11, 12, 12, 12, 13, 13, 13, 13, 14, 14, 14, 15, 15, 15, 16, 16, 16, 16, 17, 17, 17, 18, 18, 18, 19, 19, 19, 19, 20}; auto t = bsr2log10[FMT_BUILTIN_CLZLL(n | 1) ^ 63]; - static constexpr const uint64_t zero_or_powers_of_10[] = { + static constexpr uint64_t zero_or_powers_of_10[] = { 0, 0, FMT_POWERS_OF_10(1U), FMT_POWERS_OF_10(1000000000ULL), 10000000000000000000ULL}; return t - (n < zero_or_powers_of_10[t]); @@ -1225,7 +1253,7 @@ FMT_CONSTEXPR auto do_format_base2e(int base_bits, Char* out, UInt value, out += size; do { const char* digits = upper ? "0123456789ABCDEF" : "0123456789abcdef"; - unsigned digit = static_cast(value & ((1 << base_bits) - 1)); + unsigned digit = static_cast(value & ((1u << base_bits) - 1)); *--out = static_cast(base_bits < 4 ? static_cast('0' + digit) : digits[digit]); } while ((value >>= base_bits) != 0); @@ -1283,10 +1311,11 @@ template class to_utf8 { explicit to_utf8(basic_string_view s, to_utf8_error_policy policy = to_utf8_error_policy::abort) { static_assert(sizeof(WChar) == 2 || sizeof(WChar) == 4, - "Expect utf16 or utf32"); - if (!convert(s, policy)) + "expected utf16 or utf32"); + if (!convert(s, policy)) { FMT_THROW(std::runtime_error(sizeof(WChar) == 2 ? "invalid utf16" : "invalid utf32")); + } } operator string_view() const { return string_view(&buffer_[0], size()); } auto size() const -> size_t { return buffer_.size() - 1; } @@ -1316,9 +1345,8 @@ template class to_utf8 { buf.append(string_view("\xEF\xBF\xBD")); --p; continue; - } else { - c = (c << 10) + static_cast(*p) - 0x35fdc00; } + c = (c << 10) + static_cast(*p) - 0x35fdc00; } if (c < 0x80) { buf.push_back(static_cast(c)); @@ -1343,7 +1371,7 @@ template class to_utf8 { }; // Computes 128-bit result of multiplication of two 64-bit unsigned integers. -inline auto umul128(uint64_t x, uint64_t y) noexcept -> uint128_fallback { +FMT_INLINE auto umul128(uint64_t x, uint64_t y) noexcept -> uint128_fallback { #if FMT_USE_INT128 auto p = static_cast(x) * static_cast(y); return {static_cast(p >> 64), static_cast(p)}; @@ -1487,6 +1515,13 @@ template constexpr auto exponent_bias() -> int { : std::numeric_limits::max_exponent - 1; } +FMT_CONSTEXPR inline auto compute_exp_size(int exp) -> int { + auto prefix_size = 2; // sign + 'e' + auto abs_exp = exp >= 0 ? exp : -exp; + if (abs_exp < 100) return prefix_size + 2; + return prefix_size + (abs_exp >= 1000 ? 4 : 3); +} + // Writes the exponent exp in the form "[+-]d{2,3}" to buffer. template FMT_CONSTEXPR auto write_exponent(int exp, OutputIt out) -> OutputIt { @@ -1519,7 +1554,7 @@ template struct basic_fp { F f; int e; - static constexpr const int num_significand_bits = + static constexpr int num_significand_bits = static_cast(sizeof(F) * num_bits()); constexpr basic_fp() : f(0), e(0) {} @@ -1612,6 +1647,15 @@ constexpr auto convert_float(T value) -> convert_float_result { return static_cast>(value); } +template +auto select(T true_value, F) -> T { + return true_value; +} +template +auto select(T, F false_value) -> F { + return false_value; +} + template FMT_CONSTEXPR FMT_NOINLINE auto fill(OutputIt it, size_t n, const basic_specs& specs) -> OutputIt { @@ -1808,16 +1852,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: @@ -1841,9 +1875,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; @@ -1861,7 +1893,7 @@ template class digit_grouping { return count; } - // Applies grouping to digits and write the output to out. + // Applies grouping to digits and writes the output to out. template auto apply(Out out, basic_string_view digits) const -> Out { auto num_digits = static_cast(digits.size()); @@ -1943,6 +1975,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&, @@ -1964,8 +1998,7 @@ FMT_CONSTEXPR auto make_write_int_arg(T value, sign s) prefix = 0x01000000 | '-'; abs_value = 0 - abs_value; } else { - constexpr const unsigned prefixes[4] = {0, 0, 0x1000000u | '+', - 0x1000000u | ' '}; + constexpr unsigned prefixes[4] = {0, 0, 0x1000000u | '+', 0x1000000u | ' '}; prefix = prefixes[static_cast(s)]; } return {abs_value, prefix}; @@ -2018,7 +2051,7 @@ FMT_CONSTEXPR FMT_INLINE auto write_int(OutputIt out, write_int_arg arg, const format_specs& specs) -> OutputIt { static_assert(std::is_same>::value, ""); - constexpr int buffer_size = num_bits(); + constexpr size_t buffer_size = num_bits(); char buffer[buffer_size]; if (is_constant_evaluated()) fill_n(buffer, buffer_size, '\0'); const char* begin = nullptr; @@ -2111,12 +2144,108 @@ FMT_CONSTEXPR FMT_INLINE auto write(OutputIt out, T value, } 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, + const format_specs& specs) -> OutputIt { + bool is_debug = specs.type() == presentation_type::debug; + if (specs.precision < 0 && specs.width == 0) { + auto&& it = reserve(out, s.size()); + return is_debug ? write_escaped_string(it, s) : copy(s, it); + } + + size_t display_width_limit = + specs.precision < 0 ? SIZE_MAX : to_unsigned(specs.precision); + size_t display_width = + !is_debug || specs.precision == 0 ? 0 : 1; // Account for opening '"'. + size_t size = !is_debug || specs.precision == 0 ? 0 : 1; + for_each_codepoint(s, [&](uint32_t cp, string_view sv) { + if (is_debug && needs_escape(cp)) { + counting_buffer buf; + write_escaped_cp(basic_appender(buf), + find_escape_result{sv.begin(), sv.end(), cp}); + // We're reinterpreting bytes as display width. That's okay + // because write_escaped_cp() only writes ASCII characters. + size_t cp_width = buf.count(); + if (display_width + cp_width <= display_width_limit) { + display_width += cp_width; + size += cp_width; + // If this is the end of the string, account for closing '"'. + if (display_width < display_width_limit && sv.end() == s.end()) { + ++display_width; + ++size; + } + return true; + } + + size += display_width_limit - display_width; + display_width = display_width_limit; + return false; + } + + size_t cp_width = display_width_of(cp); + if (cp_width + display_width <= display_width_limit) { + display_width += cp_width; + size += sv.size(); + // If this is the end of the string, account for closing '"'. + if (is_debug && display_width < display_width_limit && + sv.end() == s.end()) { + ++display_width; + ++size; + } + return true; + } + + return false; + }); + + struct bounded_output_iterator { + reserve_iterator underlying_iterator; + size_t bound; + + FMT_CONSTEXPR auto operator*() -> bounded_output_iterator& { return *this; } + FMT_CONSTEXPR auto operator++() -> bounded_output_iterator& { + return *this; + } + FMT_CONSTEXPR auto operator++(int) -> bounded_output_iterator& { + return *this; + } + FMT_CONSTEXPR auto operator=(char c) -> bounded_output_iterator& { + if (bound > 0) { + *underlying_iterator++ = c; + --bound; + } + return *this; + } + }; + + return write_padded( + out, specs, size, display_width, [=](reserve_iterator it) { + return is_debug + ? write_escaped_string(bounded_output_iterator{it, size}, s) + .underlying_iterator + : copy(s.data(), s.data() + size, it); + }); +} + +template ::value)> FMT_CONSTEXPR auto write(OutputIt out, basic_string_view s, const format_specs& specs) -> OutputIt { auto data = s.data(); auto size = s.size(); if (specs.precision >= 0 && to_unsigned(specs.precision) < size) - size = code_point_index(s, to_unsigned(specs.precision)); + size = to_unsigned(specs.precision); bool is_debug = specs.type() == presentation_type::debug; if (is_debug) { @@ -2125,22 +2254,19 @@ FMT_CONSTEXPR auto write(OutputIt out, basic_string_view s, size = buf.count(); } - size_t width = 0; - if (specs.width != 0) { - width = - is_debug ? size : compute_width(basic_string_view(data, size)); - } return write_padded( - out, specs, size, width, [=](reserve_iterator it) { + out, specs, size, [=](reserve_iterator it) { return is_debug ? write_escaped_string(it, s) : copy(data, data + size, it); }); } + template FMT_CONSTEXPR auto write(OutputIt out, basic_string_view s, const format_specs& specs, locale_ref) -> OutputIt { return write(out, s, specs); } + template FMT_CONSTEXPR auto write(OutputIt out, const Char* s, const format_specs& specs, locale_ref) -> OutputIt { @@ -2274,7 +2400,7 @@ inline auto write_significand(Char* out, UInt significand, int significand_size, int floating_size = significand_size - integral_size; for (int i = floating_size / 2; i > 0; --i) { out -= 2; - write2digits(out, static_cast(significand % 100)); + write2digits(out, static_cast(significand % 100)); significand /= 100; } if (floating_size % 2 != 0) { @@ -2328,93 +2454,80 @@ FMT_CONSTEXPR20 auto write_significand(OutputIt out, T significand, buffer.end(), out); } -template > -FMT_CONSTEXPR20 auto do_write_float(OutputIt out, const DecimalFP& f, - const format_specs& specs, sign s, - int exp_upper, locale_ref loc) -> OutputIt { - auto significand = f.significand; - int significand_size = get_significand_size(f); - const Char zero = static_cast('0'); - size_t size = to_unsigned(significand_size) + (s != sign::none ? 1 : 0); - using iterator = reserve_iterator; +// Numbers with exponents greater or equal to the returned value will use +// the exponential notation. +template FMT_CONSTEVAL auto exp_upper() -> int { + return std::numeric_limits::digits10 != 0 + ? min_of(16, std::numeric_limits::digits10 + 1) + : 16; +} - Char decimal_point = specs.localized() ? detail::decimal_point(loc) - : static_cast('.'); - - int output_exp = f.exponent + significand_size - 1; - auto use_exp_format = [=]() { - if (specs.type() == presentation_type::exp) return true; - if (specs.type() == presentation_type::fixed) return false; - // Use the fixed notation if the exponent is in [exp_lower, exp_upper), - // e.g. 0.0001 instead of 1e-04. Otherwise use the exponent notation. - const int exp_lower = -4; - return output_exp < exp_lower || - output_exp >= (specs.precision > 0 ? specs.precision : exp_upper); - }; - if (use_exp_format()) { - int num_zeros = 0; - if (specs.alt()) { - num_zeros = specs.precision - significand_size; - if (num_zeros < 0) num_zeros = 0; - size += to_unsigned(num_zeros); - } else if (significand_size == 1) { - decimal_point = Char(); - } - auto abs_output_exp = output_exp >= 0 ? output_exp : -output_exp; - int exp_digits = 2; - if (abs_output_exp >= 100) exp_digits = abs_output_exp >= 1000 ? 4 : 3; - - size += to_unsigned((decimal_point ? 1 : 0) + 2 + exp_digits); - char exp_char = specs.upper() ? 'E' : 'e'; - auto write = [=](iterator it) { - if (s != sign::none) *it++ = detail::getsign(s); - // Insert a decimal point after the first digit and add an exponent. - it = write_significand(it, significand, significand_size, 1, - decimal_point); - if (num_zeros > 0) it = detail::fill_n(it, num_zeros, zero); - *it++ = static_cast(exp_char); - return write_exponent(output_exp, it); - }; - return specs.width > 0 - ? write_padded(out, specs, size, write) - : base_iterator(out, write(reserve(out, size))); +// Use the fixed notation if the exponent is in [-4, exp_upper), +// e.g. 0.0001 instead of 1e-04. Otherwise use the exponent notation. +constexpr auto use_fixed(int exp, int exp_upper) -> bool { + return exp >= -4 && exp < exp_upper; +} + +template class fallback_digit_grouping { + public: + constexpr fallback_digit_grouping(locale_ref, bool) {} + + constexpr auto has_separator() const -> bool { return false; } + + constexpr auto count_separators(int) const -> int { return 0; } + + template + constexpr auto apply(Out out, basic_string_view) const -> Out { + return out; } +}; + +template +FMT_CONSTEXPR20 auto write_fixed(OutputIt out, const DecimalFP& f, + int significand_size, Char decimal_point, + const format_specs& specs, sign s, + locale_ref loc = {}) -> OutputIt { + using iterator = reserve_iterator; int exp = f.exponent + significand_size; + long long size = significand_size + (s != sign::none ? 1 : 0); if (f.exponent >= 0) { // 1234e5 -> 123400000[.0+] - size += to_unsigned(f.exponent); + size += f.exponent; int num_zeros = specs.precision - exp; abort_fuzzing_if(num_zeros > 5000); if (specs.alt()) { ++size; if (num_zeros <= 0 && specs.type() != presentation_type::fixed) num_zeros = 0; - if (num_zeros > 0) size += to_unsigned(num_zeros); + if (num_zeros > 0) size += num_zeros; } auto grouping = Grouping(loc, specs.localized()); - size += to_unsigned(grouping.count_separators(exp)); - return write_padded(out, specs, size, [&](iterator it) { - if (s != sign::none) *it++ = detail::getsign(s); - it = write_significand(it, significand, significand_size, - f.exponent, grouping); - if (!specs.alt()) return it; - *it++ = decimal_point; - return num_zeros > 0 ? detail::fill_n(it, num_zeros, zero) : it; - }); - } else if (exp > 0) { + size += grouping.count_separators(exp); + return write_padded( + out, specs, to_unsigned(size), [&](iterator it) { + if (s != sign::none) *it++ = detail::getsign(s); + it = write_significand(it, f.significand, significand_size, + f.exponent, grouping); + if (!specs.alt()) return it; + *it++ = decimal_point; + return num_zeros > 0 ? detail::fill_n(it, num_zeros, Char('0')) : it; + }); + } + if (exp > 0) { // 1234e-2 -> 12.34[0+] int num_zeros = specs.alt() ? specs.precision - significand_size : 0; - size += 1 + static_cast(max_of(num_zeros, 0)); + size += 1 + max_of(num_zeros, 0); auto grouping = Grouping(loc, specs.localized()); - size += to_unsigned(grouping.count_separators(exp)); - return write_padded(out, specs, size, [&](iterator it) { - if (s != sign::none) *it++ = detail::getsign(s); - it = write_significand(it, significand, significand_size, exp, - decimal_point, grouping); - return num_zeros > 0 ? detail::fill_n(it, num_zeros, zero) : it; - }); + size += grouping.count_separators(exp); + return write_padded( + out, specs, to_unsigned(size), [&](iterator it) { + if (s != sign::none) *it++ = detail::getsign(s); + it = write_significand(it, f.significand, significand_size, exp, + decimal_point, grouping); + return num_zeros > 0 ? detail::fill_n(it, num_zeros, Char('0')) : it; + }); } // 1234e-6 -> 0.001234 int num_zeros = -exp; @@ -2423,41 +2536,68 @@ FMT_CONSTEXPR20 auto do_write_float(OutputIt out, const DecimalFP& f, num_zeros = specs.precision; } bool pointy = num_zeros != 0 || significand_size != 0 || specs.alt(); - size += 1 + (pointy ? 1 : 0) + to_unsigned(num_zeros); - return write_padded(out, specs, size, [&](iterator it) { - if (s != sign::none) *it++ = detail::getsign(s); - *it++ = zero; - if (!pointy) return it; - *it++ = decimal_point; - it = detail::fill_n(it, num_zeros, zero); - return write_significand(it, significand, significand_size); - }); + size += 1 + (pointy ? 1 : 0) + num_zeros; + return write_padded( + out, specs, to_unsigned(size), [&](iterator it) { + if (s != sign::none) *it++ = detail::getsign(s); + *it++ = Char('0'); + if (!pointy) return it; + *it++ = decimal_point; + it = detail::fill_n(it, num_zeros, Char('0')); + return write_significand(it, f.significand, significand_size); + }); } -template class fallback_digit_grouping { - public: - constexpr fallback_digit_grouping(locale_ref, bool) {} - - constexpr auto has_separator() const -> bool { return false; } - - constexpr auto count_separators(int) const -> int { return 0; } - - template - constexpr auto apply(Out out, basic_string_view) const -> Out { - return out; - } -}; +template +FMT_CONSTEXPR20 auto do_write_float(OutputIt out, const DecimalFP& f, + const format_specs& specs, sign s, + int exp_upper, locale_ref loc) -> OutputIt { + Char point = specs.localized() ? detail::decimal_point(loc) : Char('.'); + int significand_size = get_significand_size(f); + int exp = f.exponent + significand_size - 1; + if (specs.type() == presentation_type::fixed || + (specs.type() != presentation_type::exp && + use_fixed(exp, specs.precision > 0 ? specs.precision : exp_upper))) { + return write_fixed(out, f, significand_size, point, specs, + s, loc); + } + + // Write value in the exponential format. + int num_zeros = 0; + long long size = significand_size + (s != sign::none ? 1 : 0); + if (specs.alt()) { + num_zeros = max_of(specs.precision - significand_size, 0); + size += num_zeros; + } else if (significand_size == 1) { + point = Char(); + } + size += (point ? 1 : 0) + compute_exp_size(exp); + char exp_char = specs.upper() ? 'E' : 'e'; + auto write = [=](reserve_iterator it) { + if (s != sign::none) *it++ = detail::getsign(s); + // Insert a decimal point after the first digit and add an exponent. + it = write_significand(it, f.significand, significand_size, 1, point); + if (num_zeros > 0) it = detail::fill_n(it, num_zeros, Char('0')); + *it++ = Char(exp_char); + return write_exponent(exp, it); + }; + auto usize = to_unsigned(size); + return specs.width > 0 + ? write_padded(out, specs, usize, write) + : base_iterator(out, write(reserve(out, usize))); +} template FMT_CONSTEXPR20 auto write_float(OutputIt out, const DecimalFP& f, const format_specs& specs, sign s, int exp_upper, locale_ref loc) -> OutputIt { if (is_constant_evaluated()) { - return do_write_float>(out, f, specs, s, - exp_upper, loc); + return do_write_float>(out, f, specs, s, + exp_upper, loc); } else { - return do_write_float(out, f, specs, s, exp_upper, loc); + return do_write_float>(out, f, specs, s, + exp_upper, loc); } } @@ -2488,7 +2628,7 @@ FMT_CONSTEXPR auto isfinite(T value) -> bool { } template ::value)> -FMT_INLINE FMT_CONSTEXPR bool signbit(T value) { +FMT_INLINE FMT_CONSTEXPR auto signbit(T value) -> bool { if (is_constant_evaluated()) { #ifdef __cpp_if_constexpr if constexpr (std::numeric_limits::is_iec559) { @@ -2728,7 +2868,7 @@ class bigint { bigits_.resize(to_unsigned(num_bigits + exp_difference)); for (int i = num_bigits - 1, j = i + exp_difference; i >= 0; --i, --j) bigits_[j] = bigits_[i]; - memset(bigits_.data(), 0, to_unsigned(exp_difference) * sizeof(bigit)); + fill_n(bigits_.data(), to_unsigned(exp_difference), 0U); exp_ -= exp_difference; } @@ -3289,17 +3429,12 @@ FMT_CONSTEXPR20 auto format_float(Float value, int precision, return exp; } -// Numbers with exponents greater or equal to the returned value will use -// the exponential notation. -template constexpr auto exp_upper() -> int { - return std::numeric_limits::digits10 != 0 - ? min_of(16, std::numeric_limits::digits10 + 1) - : 16; -} +template ::value)> +FMT_CONSTEXPR20 auto write(OutputIt out, T value, format_specs specs, + locale_ref loc = {}) -> OutputIt { + if (specs.localized() && write_loc(out, value, specs, loc)) return out; -template -FMT_CONSTEXPR20 auto write_float(OutputIt out, T value, format_specs specs, - locale_ref loc) -> OutputIt { // Use signbit because value < 0 is false for NaN. sign s = detail::signbit(value) ? sign::minus : specs.sign(); @@ -3312,15 +3447,14 @@ FMT_CONSTEXPR20 auto write_float(OutputIt out, T value, format_specs specs, if (specs.width != 0) --specs.width; } - constexpr int exp_upper = detail::exp_upper(); + const int exp_upper = detail::exp_upper(); int precision = specs.precision; if (precision < 0) { if (specs.type() != presentation_type::none) { precision = 6; } else if (is_fast_float::value && !is_constant_evaluated()) { // Use Dragonbox for the shortest format. - using floaty = conditional_t= sizeof(double), double, float>; - auto dec = dragonbox::to_decimal(static_cast(value)); + auto dec = dragonbox::to_decimal(static_cast>(value)); return write_float(out, dec, specs, s, exp_upper, loc); } } @@ -3352,38 +3486,77 @@ FMT_CONSTEXPR20 auto write_float(OutputIt out, T value, format_specs specs, return write_float(out, f, specs, s, exp_upper, loc); } -template ::value)> -FMT_CONSTEXPR20 auto write(OutputIt out, T value, format_specs specs, - locale_ref loc = {}) -> OutputIt { - return specs.localized() && write_loc(out, value, specs, loc) - ? out - : write_float(out, value, specs, loc); -} - template ::value)> FMT_CONSTEXPR20 auto write(OutputIt out, T value) -> OutputIt { if (is_constant_evaluated()) return write(out, value, format_specs()); auto s = detail::signbit(value) ? sign::minus : sign::none; - - constexpr auto specs = format_specs(); - using floaty = conditional_t= sizeof(double), double, float>; - using floaty_uint = typename dragonbox::float_info::carrier_uint; - floaty_uint mask = exponent_mask(); - if ((bit_cast(value) & mask) == mask) - return write_nonfinite(out, std::isnan(value), specs, s); - - auto dec = dragonbox::to_decimal(static_cast(value)); - return write_float(out, dec, specs, s, exp_upper(), {}); + auto mask = exponent_mask>(); + if ((bit_cast(value) & mask) == mask) + return write_nonfinite(out, std::isnan(value), {}, s); + + auto dec = dragonbox::to_decimal(static_cast>(value)); + auto significand = dec.significand; + int significand_size = count_digits(significand); + int exponent = dec.exponent + significand_size - 1; + if (use_fixed(exponent, detail::exp_upper())) { + return write_fixed>( + out, dec, significand_size, Char('.'), {}, s); + } + + // Write value in the exponential format. + const char* prefix = "e+"; + int abs_exponent = exponent; + if (exponent < 0) { + abs_exponent = -exponent; + prefix = "e-"; + } + auto has_decimal_point = significand_size != 1; + size_t size = std::is_pointer::value + ? 0u + : to_unsigned((s != sign::none ? 1 : 0) + significand_size + + (has_decimal_point ? 1 : 0) + + (abs_exponent >= 100 ? 5 : 4)); + if (auto ptr = to_pointer(out, size)) { + if (s != sign::none) *ptr++ = Char('-'); + if (has_decimal_point) { + auto begin = ptr; + ptr = format_decimal(ptr, significand, significand_size + 1); + *begin = begin[1]; + begin[1] = '.'; + } else { + *ptr++ = static_cast('0' + significand); + } + if (std::is_same::value) { + memcpy(ptr, prefix, 2); + ptr += 2; + } else { + *ptr++ = prefix[0]; + *ptr++ = prefix[1]; + } + if (abs_exponent >= 100) { + *ptr++ = static_cast('0' + abs_exponent / 100); + abs_exponent %= 100; + } + write2digits(ptr, static_cast(abs_exponent)); + return select::value>(ptr + 2, out); + } + auto it = reserve(out, size); + if (s != sign::none) *it++ = Char('-'); + // Insert a decimal point after the first digit and add an exponent. + it = write_significand(it, significand, significand_size, 1, + has_decimal_point ? Char('.') : Char()); + *it++ = Char('e'); + it = write_exponent(exponent, it); + return base_iterator(out, it); } template ::value && !is_fast_float::value)> inline auto write(OutputIt out, T value) -> OutputIt { - return write(out, value, format_specs()); + return write(out, value, {}); } template @@ -3523,32 +3696,18 @@ struct dynamic_spec_getter { } }; -template -FMT_CONSTEXPR auto get_arg(Context& ctx, ID id) -> basic_format_arg { - auto arg = ctx.arg(id); - if (!arg) report_error("argument not found"); - return arg; -} - template -FMT_CONSTEXPR int get_dynamic_spec( - arg_id_kind kind, const arg_ref& ref, - Context& ctx) { - FMT_ASSERT(kind != arg_id_kind::none, ""); +FMT_CONSTEXPR void handle_dynamic_spec( + arg_id_kind kind, int& value, + const arg_ref& ref, Context& ctx) { + if (kind == arg_id_kind::none) return; auto arg = kind == arg_id_kind::index ? ctx.arg(ref.index) : ctx.arg(ref.name); if (!arg) report_error("argument not found"); - unsigned long long value = arg.visit(dynamic_spec_getter()); - if (value > to_unsigned(max_value())) + unsigned long long result = arg.visit(dynamic_spec_getter()); + if (result > to_unsigned(max_value())) report_error("width/precision is out of range"); - return static_cast(value); -} - -template -FMT_CONSTEXPR void handle_dynamic_spec( - arg_id_kind kind, int& value, - const arg_ref& ref, Context& ctx) { - if (kind != arg_id_kind::none) value = get_dynamic_spec(kind, ref, ctx); + value = static_cast(result); } #if FMT_USE_NONTYPE_TEMPLATE_ARGS @@ -3586,7 +3745,7 @@ template struct udl_arg { }; #endif // FMT_USE_NONTYPE_TEMPLATE_ARGS -template struct format_handler { +template struct format_handler { parse_context parse_ctx; buffered_context ctx; @@ -3612,7 +3771,8 @@ template struct format_handler { auto on_format_specs(int id, const Char* begin, const Char* end) -> const Char* { - auto arg = get_arg(ctx, id); + auto arg = ctx.arg(id); + if (!arg) report_error("argument not found"); // Not using a visitor for custom types gives better codegen. if (arg.format_custom(begin, parse_ctx, ctx)) return parse_ctx.begin(); @@ -3632,6 +3792,7 @@ template struct format_handler { FMT_NORETURN void on_error(const char* message) { report_error(message); } }; +// It is used in format-inl.h and os.cc. using format_func = void (*)(detail::buffer&, int, const char*); FMT_API void do_report_error(format_func func, int error_code, const char* message) noexcept; @@ -3652,28 +3813,6 @@ FMT_CONSTEXPR auto native_formatter::format( specs_.precision_ref, ctx); 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>; -}; -template <> struct vformat_args { - using type = format_args; -}; - -template -void vformat_to(buffer& buf, basic_string_view fmt, - typename vformat_args::type args, locale_ref loc = {}) { - auto out = basic_appender(buf); - parse_format_string( - fmt, format_handler{parse_context(fmt), {out, args, loc}}); -} } // namespace detail FMT_BEGIN_EXPORT @@ -3685,19 +3824,16 @@ template class generic_context { private: OutputIt out_; basic_format_args args_; - detail::locale_ref loc_; + locale_ref loc_; public: using char_type = Char; using iterator = OutputIt; - using parse_context_type FMT_DEPRECATED = parse_context; - template - using formatter_type FMT_DEPRECATED = formatter; enum { builtin_types = FMT_BUILTIN_TYPES }; 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; @@ -3720,7 +3856,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 { @@ -3825,7 +3961,7 @@ struct formatter>> * auto s = fmt::format("{}", fmt::ptr(p)); */ template auto ptr(T p) -> const void* { - static_assert(std::is_pointer::value, ""); + static_assert(std::is_pointer::value, "fmt::ptr used with non-pointer"); return detail::bit_cast(p); } @@ -3850,13 +3986,14 @@ constexpr auto format_as(Enum e) noexcept -> underlying_t { } // namespace enums #ifdef __cpp_lib_byte -template <> struct formatter : formatter { +template +struct formatter : formatter { static auto format_as(std::byte b) -> unsigned char { return static_cast(b); } template auto format(std::byte b, Context& ctx) const -> decltype(ctx.out()) { - return formatter::format(format_as(b), ctx); + return formatter::format(format_as(b), ctx); } }; #endif @@ -4070,21 +4207,26 @@ class format_int { inline auto str() const -> std::string { return {str_, size()}; } }; -#define FMT_STRING_IMPL(s, base) \ - [] { \ - /* Use the hidden visibility as a workaround for a GCC bug (#1973). */ \ - /* Use a macro-like name to avoid shadowing warnings. */ \ - struct FMT_VISIBILITY("hidden") FMT_COMPILE_STRING : base { \ - using char_type = fmt::remove_cvref_t; \ - constexpr explicit operator fmt::basic_string_view() const { \ - return fmt::detail::compile_string_to_view(s); \ - } \ - }; \ - using FMT_STRING_VIEW = \ - fmt::basic_string_view; \ - fmt::detail::ignore_unused(FMT_STRING_VIEW(FMT_COMPILE_STRING())); \ - return FMT_COMPILE_STRING(); \ - }() +#if FMT_CLANG_ANALYZER +# define FMT_STRING_IMPL(s, base) s +#else +# define FMT_STRING_IMPL(s, base) \ + [] { \ + /* Use the hidden visibility as a workaround for a GCC bug (#1973). */ \ + /* Use a macro-like name to avoid shadowing warnings. */ \ + struct FMT_VISIBILITY("hidden") FMT_COMPILE_STRING : base { \ + using char_type = fmt::remove_cvref_t; \ + constexpr explicit operator fmt::basic_string_view() \ + const { \ + return fmt::detail::compile_string_to_view(s); \ + } \ + }; \ + using FMT_STRING_VIEW = \ + fmt::basic_string_view; \ + fmt::detail::ignore_unused(FMT_STRING_VIEW(FMT_COMPILE_STRING())); \ + return FMT_COMPILE_STRING(); \ + }() +#endif // FMT_CLANG_ANALYZER /** * Constructs a legacy compile-time format string from a string literal `s`. @@ -4140,46 +4282,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(); } @@ -4208,7 +4345,7 @@ FMT_NODISCARD FMT_INLINE auto format(format_string fmt, T&&... args) * std::string answer = fmt::to_string(42); */ template ::value)> -FMT_NODISCARD auto to_string(T value) -> std::string { +FMT_NODISCARD FMT_CONSTEXPR_STRING auto to_string(T value) -> std::string { // The buffer should be large enough to store the number including the sign // or "false" for bool. char buffer[max_of(detail::digits10() + 2, 5)]; @@ -4216,13 +4353,15 @@ FMT_NODISCARD auto to_string(T value) -> std::string { } template ::value)> -FMT_NODISCARD auto to_string(const T& value) -> std::string { +FMT_NODISCARD FMT_CONSTEXPR_STRING auto to_string(const T& value) + -> std::string { return to_string(format_as(value)); } template ::value && !detail::use_format_as::value)> -FMT_NODISCARD auto to_string(const T& value) -> std::string { +FMT_NODISCARD FMT_CONSTEXPR_STRING auto to_string(const T& value) + -> std::string { auto buffer = memory_buffer(); detail::write(appender(buffer), value); return {buffer.data(), buffer.size()}; diff --git a/include/spdlog/fmt/bundled/os.h b/include/spdlog/fmt/bundled/os.h index b2cc5e4b..40cdcdd4 100644 --- a/include/spdlog/fmt/bundled/os.h +++ b/include/spdlog/fmt/bundled/os.h @@ -29,7 +29,8 @@ # if (FMT_HAS_INCLUDE() || defined(__APPLE__) || \ defined(__linux__)) && \ (!defined(WINAPI_FAMILY) || \ - (WINAPI_FAMILY == WINAPI_FAMILY_DESKTOP_APP)) + (WINAPI_FAMILY == WINAPI_FAMILY_DESKTOP_APP)) && \ + !defined(__wasm__) # include // for O_RDONLY # define FMT_USE_FCNTL 1 # else diff --git a/include/spdlog/fmt/bundled/ostream.h b/include/spdlog/fmt/bundled/ostream.h index 7bec4efe..bf2371b7 100644 --- a/include/spdlog/fmt/bundled/ostream.h +++ b/include/spdlog/fmt/bundled/ostream.h @@ -33,8 +33,8 @@ FMT_BEGIN_NAMESPACE namespace detail { -// Generate a unique explicit instantion in every translation unit using a tag -// type in an anonymous namespace. +// Generate a unique explicit instantiation in every translation unit using a +// tag type in an anonymous namespace. namespace { struct file_access_tag {}; } // namespace diff --git a/include/spdlog/fmt/bundled/printf.h b/include/spdlog/fmt/bundled/printf.h index e7268401..cc066d8c 100644 --- a/include/spdlog/fmt/bundled/printf.h +++ b/include/spdlog/fmt/bundled/printf.h @@ -9,7 +9,7 @@ #define FMT_PRINTF_H_ #ifndef FMT_MODULE -# include // std::max +# include // std::find # include // std::numeric_limits #endif @@ -18,10 +18,6 @@ FMT_BEGIN_NAMESPACE FMT_BEGIN_EXPORT -template struct printf_formatter { - printf_formatter() = delete; -}; - template class basic_printf_context { private: basic_appender out_; @@ -33,8 +29,6 @@ template class basic_printf_context { public: using char_type = Char; - using parse_context_type = parse_context; - template using formatter_type = printf_formatter; enum { builtin_types = 1 }; /// Constructs a `printf_context` object. References to the arguments are @@ -46,7 +40,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); @@ -74,10 +68,9 @@ inline auto find(const char* first, const char* last, char value, // Checks if a value fits in int - used to avoid warnings about comparing // signed and unsigned integers. -template struct int_checker { +template struct int_checker { template static auto fits_in_int(T value) -> bool { - unsigned max = to_unsigned(max_value()); - return value <= max; + return value <= to_unsigned(max_value()); } inline static auto fits_in_int(bool) -> bool { return true; } }; @@ -95,7 +88,7 @@ struct printf_precision_handler { auto operator()(T value) -> int { if (!int_checker::is_signed>::fits_in_int(value)) report_error("number is too big"); - return (std::max)(static_cast(value), 0); + return max_of(static_cast(value), 0); } template ::value)> @@ -410,7 +403,9 @@ void vprintf(buffer& buf, basic_string_view format, arg_index = parse_ctx.next_arg_id(); else parse_ctx.check_arg_id(--arg_index); - return detail::get_arg(context, arg_index); + auto arg = context.arg(arg_index); + if (!arg) report_error("argument not found"); + return arg; }; const Char* start = parse_ctx.begin(); @@ -571,15 +566,19 @@ inline auto vsprintf(basic_string_view fmt, * * std::string message = fmt::sprintf("The answer is %d", 42); */ -template > -inline auto sprintf(const S& fmt, const T&... args) -> std::basic_string { - return vsprintf(detail::to_string_view(fmt), - fmt::make_format_args>(args...)); +template +inline auto sprintf(string_view fmt, const T&... args) -> std::string { + return vsprintf(fmt, make_printf_args(args...)); +} +template +FMT_DEPRECATED auto sprintf(basic_string_view fmt, const T&... args) + -> std::wstring { + return vsprintf(fmt, make_printf_args(args...)); } template -inline auto vfprintf(std::FILE* f, basic_string_view fmt, - typename vprintf_args::type args) -> int { +auto vfprintf(std::FILE* f, basic_string_view fmt, + typename vprintf_args::type args) -> int { auto buf = basic_memory_buffer(); detail::vprintf(buf, fmt, args); size_t size = buf.size(); @@ -596,17 +595,14 @@ inline auto vfprintf(std::FILE* f, basic_string_view fmt, * * fmt::fprintf(stderr, "Don't %s!", "panic"); */ -template > -inline auto fprintf(std::FILE* f, const S& fmt, const T&... args) -> int { - return vfprintf(f, detail::to_string_view(fmt), - make_printf_args(args...)); +template +inline auto fprintf(std::FILE* f, string_view fmt, const T&... args) -> int { + return vfprintf(f, fmt, make_printf_args(args...)); } - -template -FMT_DEPRECATED inline auto vprintf(basic_string_view fmt, - typename vprintf_args::type args) - -> int { - return vfprintf(stdout, fmt, args); +template +FMT_DEPRECATED auto fprintf(std::FILE* f, basic_string_view fmt, + const T&... args) -> int { + return vfprintf(f, fmt, make_printf_args(args...)); } /** @@ -621,11 +617,6 @@ template inline auto printf(string_view fmt, const T&... args) -> int { return vfprintf(stdout, fmt, make_printf_args(args...)); } -template -FMT_DEPRECATED inline auto printf(basic_string_view fmt, - const T&... args) -> int { - return vfprintf(stdout, fmt, make_printf_args(args...)); -} FMT_END_EXPORT FMT_END_NAMESPACE diff --git a/include/spdlog/fmt/bundled/ranges.h b/include/spdlog/fmt/bundled/ranges.h index 23ff7de0..24c61e93 100644 --- a/include/spdlog/fmt/bundled/ranges.h +++ b/include/spdlog/fmt/bundled/ranges.h @@ -11,7 +11,6 @@ #ifndef FMT_MODULE # include # include -# include # include # include # include @@ -31,7 +30,7 @@ template class is_map { template static void check(...); public: - static constexpr const bool value = + static constexpr bool value = !std::is_void(nullptr))>::value; }; @@ -40,17 +39,16 @@ template class is_set { template static void check(...); public: - static constexpr const bool value = + static constexpr bool value = !std::is_void(nullptr))>::value && !is_map::value; }; // C array overload -template +template auto range_begin(const T (&arr)[N]) -> const T* { return arr; } -template -auto range_end(const T (&arr)[N]) -> const T* { +template auto range_end(const T (&arr)[N]) -> const T* { return arr + N; } @@ -120,7 +118,7 @@ template class is_tuple_like_ { template static void check(...); public: - static constexpr const bool value = + static constexpr bool value = !std::is_void(nullptr))>::value; }; @@ -154,7 +152,7 @@ using tuple_index_sequence = make_index_sequence::value>; template ::value> class is_tuple_formattable_ { public: - static constexpr const bool value = false; + static constexpr bool value = false; }; template class is_tuple_formattable_ { template @@ -170,7 +168,7 @@ template class is_tuple_formattable_ { C>::value)...>{})); public: - static constexpr const bool value = + static constexpr bool value = decltype(check(tuple_index_sequence{}))::value; }; @@ -208,7 +206,7 @@ template using result_t = std::tuple, Char>...>; using std::get; -template +template auto get_formatters(index_sequence) -> result_t(std::declval()))...>; } // namespace tuple @@ -219,7 +217,7 @@ template struct range_reference_type_impl { using type = decltype(*detail::range_begin(std::declval())); }; -template struct range_reference_type_impl { +template struct range_reference_type_impl { using type = T&; }; @@ -281,14 +279,15 @@ template struct format_tuple_element { } // namespace detail +FMT_EXPORT template struct is_tuple_like { - static constexpr const bool value = + static constexpr bool value = detail::is_tuple_like_::value && !detail::is_range_::value; }; +FMT_EXPORT template struct is_tuple_formattable { - static constexpr const bool value = - detail::is_tuple_formattable_::value; + static constexpr bool value = detail::is_tuple_formattable_::value; }; template @@ -343,8 +342,9 @@ struct formatter struct is_range { - static constexpr const bool value = + static constexpr bool value = detail::is_range_::value && !detail::has_to_string_view::value; }; @@ -368,6 +368,7 @@ template struct conjunction : conditional_t, P1> {}; +FMT_EXPORT template struct range_formatter; @@ -670,7 +671,8 @@ struct formatter, Char> { } }; -template struct tuple_join_view : detail::view { +FMT_EXPORT +template struct tuple_join_view : detail::view { const Tuple& tuple; basic_string_view sep; @@ -685,15 +687,15 @@ template struct tuple_join_view : detail::view { # define FMT_TUPLE_JOIN_SPECIFIERS 0 #endif -template -struct formatter, Char, +template +struct formatter, Char, enable_if_t::value>> { FMT_CONSTEXPR auto parse(parse_context& ctx) -> const Char* { return do_parse(ctx, std::tuple_size()); } template - auto format(const tuple_join_view& value, + auto format(const tuple_join_view& value, FormatContext& ctx) const -> typename FormatContext::iterator { return do_format(value, ctx, std::tuple_size()); } @@ -725,14 +727,14 @@ struct formatter, Char, } template - auto do_format(const tuple_join_view&, FormatContext& ctx, + auto do_format(const tuple_join_view&, FormatContext& ctx, std::integral_constant) const -> typename FormatContext::iterator { return ctx.out(); } template - auto do_format(const tuple_join_view& value, FormatContext& ctx, + auto do_format(const tuple_join_view& value, FormatContext& ctx, std::integral_constant) const -> typename FormatContext::iterator { using std::get; @@ -754,7 +756,7 @@ template class is_container_adaptor_like { template static void check(...); public: - static constexpr const bool value = + static constexpr bool value = !std::is_void(nullptr))>::value; }; @@ -825,7 +827,7 @@ auto join(Range&& r, string_view sep) */ template ::value)> FMT_CONSTEXPR auto join(const Tuple& tuple, string_view sep) - -> tuple_join_view { + -> tuple_join_view { return {tuple, sep}; } diff --git a/include/spdlog/fmt/bundled/std.h b/include/spdlog/fmt/bundled/std.h index f43dc74d..5cf10618 100644 --- a/include/spdlog/fmt/bundled/std.h +++ b/include/spdlog/fmt/bundled/std.h @@ -15,15 +15,13 @@ # include # include # include -# include # include -# include +# include // std::reference_wrapper # include # include # include -# include -# include -# include +# include // std::type_info +# include // std::make_index_sequence // Check FMT_CPLUSPLUS to suppress a bogus warning in MSVC. # if FMT_CPLUSPLUS >= 201703L @@ -62,28 +60,27 @@ # endif #endif -// For older Xcode versions, __cpp_lib_xxx flags are inaccurately defined. -#ifndef FMT_CPP_LIB_FILESYSTEM -# ifdef __cpp_lib_filesystem -# define FMT_CPP_LIB_FILESYSTEM __cpp_lib_filesystem -# else -# define FMT_CPP_LIB_FILESYSTEM 0 -# endif +#ifdef FMT_CPP_LIB_FILESYSTEM +// Use the provided definition. +#elif defined(__cpp_lib_filesystem) +# define FMT_CPP_LIB_FILESYSTEM __cpp_lib_filesystem +#else +# define FMT_CPP_LIB_FILESYSTEM 0 #endif -#ifndef FMT_CPP_LIB_VARIANT -# ifdef __cpp_lib_variant -# define FMT_CPP_LIB_VARIANT __cpp_lib_variant -# else -# define FMT_CPP_LIB_VARIANT 0 -# endif +#ifdef FMT_CPP_LIB_VARIANT +// Use the provided definition. +#elif defined(__cpp_lib_variant) +# define FMT_CPP_LIB_VARIANT __cpp_lib_variant +#else +# define FMT_CPP_LIB_VARIANT 0 #endif -#if FMT_CPP_LIB_FILESYSTEM FMT_BEGIN_NAMESPACE - namespace detail { +#if FMT_CPP_LIB_FILESYSTEM + template auto get_path_string(const std::filesystem::path& p, const std::basic_string& native) { @@ -111,8 +108,168 @@ void write_escaped_path(basic_memory_buffer& quoted, } } +#endif // FMT_CPP_LIB_FILESYSTEM + +#if defined(__cpp_lib_expected) || FMT_CPP_LIB_VARIANT +template +auto write_escaped_alternative(OutputIt out, const T& v) -> OutputIt { + if constexpr (has_to_string_view::value) + return write_escaped_string(out, detail::to_string_view(v)); + if constexpr (std::is_same_v) return write_escaped_char(out, v); + return write(out, v); +} +#endif + +#if FMT_CPP_LIB_VARIANT + +template struct is_variant_like_ : std::false_type {}; +template +struct is_variant_like_> : std::true_type {}; + +template class is_variant_formattable { + template + static auto check(std::index_sequence) -> std::conjunction< + is_formattable, Char>...>; + + public: + static constexpr bool value = decltype(check( + std::make_index_sequence::value>()))::value; +}; + +#endif // FMT_CPP_LIB_VARIANT + +#if FMT_USE_RTTI + +template +auto write_demangled_name(OutputIt out, const std::type_info& ti) -> OutputIt { +# ifdef FMT_HAS_ABI_CXA_DEMANGLE + int status = 0; + size_t size = 0; + std::unique_ptr demangled_name_ptr( + abi::__cxa_demangle(ti.name(), nullptr, &size, &status), &std::free); + + string_view demangled_name_view; + if (demangled_name_ptr) { + demangled_name_view = demangled_name_ptr.get(); + + // Normalization of stdlib inline namespace names. + // libc++ inline namespaces. + // std::__1::* -> std::* + // std::__1::__fs::* -> std::* + // libstdc++ inline namespaces. + // std::__cxx11::* -> std::* + // std::filesystem::__cxx11::* -> std::filesystem::* + if (demangled_name_view.starts_with("std::")) { + char* begin = demangled_name_ptr.get(); + char* to = begin + 5; // std:: + for (char *from = to, *end = begin + demangled_name_view.size(); + from < end;) { + // This is safe, because demangled_name is NUL-terminated. + if (from[0] == '_' && from[1] == '_') { + char* next = from + 1; + while (next < end && *next != ':') next++; + if (next[0] == ':' && next[1] == ':') { + from = next + 2; + continue; + } + } + *to++ = *from++; + } + demangled_name_view = {begin, detail::to_unsigned(to - begin)}; + } + } else { + demangled_name_view = string_view(ti.name()); + } + return detail::write_bytes(out, demangled_name_view); +# elif FMT_MSC_VERSION + const string_view demangled_name(ti.name()); + for (size_t i = 0; i < demangled_name.size(); ++i) { + auto sub = demangled_name; + sub.remove_prefix(i); + if (sub.starts_with("enum ")) { + i += 4; + continue; + } + if (sub.starts_with("class ") || sub.starts_with("union ")) { + i += 5; + continue; + } + if (sub.starts_with("struct ")) { + i += 6; + continue; + } + if (*sub.begin() != ' ') *out++ = *sub.begin(); + } + return out; +# else + return detail::write_bytes(out, string_view(ti.name())); +# endif +} + +#endif // FMT_USE_RTTI + +template +struct has_flip : std::false_type {}; + +template +struct has_flip().flip())>> + : std::true_type {}; + +template struct is_bit_reference_like { + static constexpr bool value = std::is_convertible::value && + std::is_nothrow_assignable::value && + has_flip::value; +}; + +// Workaround for libc++ incompatibility with C++ standard. +// According to the Standard, `bitset::operator[] const` returns bool. +#if defined(_LIBCPP_VERSION) && !defined(FMT_IMPORT_STD) +template +struct is_bit_reference_like> { + static constexpr bool value = true; +}; +#endif + +template +struct has_format_as : std::false_type {}; +template +struct has_format_as()))>> + : std::true_type {}; + +template +struct has_format_as_member : std::false_type {}; +template +struct has_format_as_member< + T, void_t::format_as(std::declval()))>> + : std::true_type {}; + } // namespace detail +template +auto ptr(const std::unique_ptr& p) -> const void* { + return p.get(); +} +template auto ptr(const std::shared_ptr& p) -> const void* { + return p.get(); +} + +#if FMT_CPP_LIB_FILESYSTEM + +class path : public std::filesystem::path { + public: + auto display_string() const -> std::string { + const std::filesystem::path& base = *this; + return fmt::format(FMT_STRING("{}"), base); + } + auto system_string() const -> std::string { return string(); } + + auto generic_display_string() const -> std::string { + const std::filesystem::path& base = *this; + return fmt::format(FMT_STRING("{:g}"), base); + } + auto generic_system_string() const -> std::string { return generic_string(); } +}; + template struct formatter { private: format_specs specs_; @@ -162,39 +319,20 @@ template struct formatter { } }; -class path : public std::filesystem::path { - public: - auto display_string() const -> std::string { - const std::filesystem::path& base = *this; - return fmt::format(FMT_STRING("{}"), base); - } - auto system_string() const -> std::string { return string(); } - - auto generic_display_string() const -> std::string { - const std::filesystem::path& base = *this; - return fmt::format(FMT_STRING("{:g}"), base); - } - auto generic_system_string() const -> std::string { return generic_string(); } -}; - -FMT_END_NAMESPACE #endif // FMT_CPP_LIB_FILESYSTEM -FMT_BEGIN_NAMESPACE -template +template struct formatter, Char> : nested_formatter, Char> { private: - // Functor because C++11 doesn't support generic lambdas. + // This is a functor because C++11 doesn't support generic lambdas. struct writer { const std::bitset& bs; template FMT_CONSTEXPR auto operator()(OutputIt out) -> OutputIt { - for (auto pos = N; pos > 0; --pos) { + for (auto pos = N; pos > 0; --pos) out = detail::write(out, bs[pos - 1] ? Char('1') : Char('0')); - } - return out; } }; @@ -209,10 +347,8 @@ struct formatter, Char> template struct formatter : basic_ostream_formatter {}; -FMT_END_NAMESPACE #ifdef __cpp_lib_optional -FMT_BEGIN_NAMESPACE template struct formatter, Char, std::enable_if_t::value>> { @@ -251,30 +387,9 @@ struct formatter, Char, return detail::write(out, ')'); } }; -FMT_END_NAMESPACE #endif // __cpp_lib_optional -#if defined(__cpp_lib_expected) || FMT_CPP_LIB_VARIANT - -FMT_BEGIN_NAMESPACE -namespace detail { - -template -auto write_escaped_alternative(OutputIt out, const T& v) -> OutputIt { - if constexpr (has_to_string_view::value) - return write_escaped_string(out, detail::to_string_view(v)); - if constexpr (std::is_same_v) return write_escaped_char(out, v); - return write(out, v); -} - -} // namespace detail - -FMT_END_NAMESPACE -#endif - #ifdef __cpp_lib_expected -FMT_BEGIN_NAMESPACE - template struct formatter, Char, std::enable_if_t<(std::is_void::value || @@ -301,11 +416,9 @@ struct formatter, Char, return out; } }; -FMT_END_NAMESPACE #endif // __cpp_lib_expected #ifdef __cpp_lib_source_location -FMT_BEGIN_NAMESPACE template <> struct formatter { FMT_CONSTEXPR auto parse(parse_context<>& ctx) { return ctx.begin(); } @@ -323,42 +436,12 @@ template <> struct formatter { return out; } }; -FMT_END_NAMESPACE #endif #if FMT_CPP_LIB_VARIANT -FMT_BEGIN_NAMESPACE -namespace detail { - -template -using variant_index_sequence = - std::make_index_sequence::value>; - -template struct is_variant_like_ : std::false_type {}; -template -struct is_variant_like_> : std::true_type {}; - -// formattable element check. -template class is_variant_formattable_ { - template - static std::conjunction< - is_formattable, C>...> - check(std::index_sequence); - - public: - static constexpr const bool value = - decltype(check(variant_index_sequence{}))::value; -}; - -} // namespace detail template struct is_variant_like { - static constexpr const bool value = detail::is_variant_like_::value; -}; - -template struct is_variant_formattable { - static constexpr const bool value = - detail::is_variant_formattable_::value; + static constexpr bool value = detail::is_variant_like_::value; }; template struct formatter { @@ -374,10 +457,10 @@ template struct formatter { }; template -struct formatter< - Variant, Char, - std::enable_if_t, is_variant_formattable>>> { +struct formatter, + detail::is_variant_formattable>>> { FMT_CONSTEXPR auto parse(parse_context& ctx) -> const Char* { return ctx.begin(); } @@ -402,10 +485,9 @@ struct formatter< return out; } }; -FMT_END_NAMESPACE + #endif // FMT_CPP_LIB_VARIANT -FMT_BEGIN_NAMESPACE template <> struct formatter { private: format_specs specs_; @@ -459,101 +541,29 @@ template <> struct formatter { }; #if FMT_USE_RTTI -namespace detail { - -template -auto write_demangled_name(OutputIt out, const std::type_info& ti) -> OutputIt { -# ifdef FMT_HAS_ABI_CXA_DEMANGLE - int status = 0; - std::size_t size = 0; - std::unique_ptr demangled_name_ptr( - abi::__cxa_demangle(ti.name(), nullptr, &size, &status), &std::free); - - string_view demangled_name_view; - if (demangled_name_ptr) { - demangled_name_view = demangled_name_ptr.get(); - - // Normalization of stdlib inline namespace names. - // libc++ inline namespaces. - // std::__1::* -> std::* - // std::__1::__fs::* -> std::* - // libstdc++ inline namespaces. - // std::__cxx11::* -> std::* - // std::filesystem::__cxx11::* -> std::filesystem::* - if (demangled_name_view.starts_with("std::")) { - char* begin = demangled_name_ptr.get(); - char* to = begin + 5; // std:: - for (char *from = to, *end = begin + demangled_name_view.size(); - from < end;) { - // This is safe, because demangled_name is NUL-terminated. - if (from[0] == '_' && from[1] == '_') { - char* next = from + 1; - while (next < end && *next != ':') next++; - if (next[0] == ':' && next[1] == ':') { - from = next + 2; - continue; - } - } - *to++ = *from++; - } - demangled_name_view = {begin, detail::to_unsigned(to - begin)}; - } - } else { - demangled_name_view = string_view(ti.name()); - } - return detail::write_bytes(out, demangled_name_view); -# elif FMT_MSC_VERSION - const string_view demangled_name(ti.name()); - for (std::size_t i = 0; i < demangled_name.size(); ++i) { - auto sub = demangled_name; - sub.remove_prefix(i); - if (sub.starts_with("enum ")) { - i += 4; - continue; - } - if (sub.starts_with("class ") || sub.starts_with("union ")) { - i += 5; - continue; - } - if (sub.starts_with("struct ")) { - i += 6; - continue; - } - if (*sub.begin() != ' ') *out++ = *sub.begin(); - } - return out; -# else - return detail::write_bytes(out, string_view(ti.name())); -# endif -} - -} // namespace detail - -template -struct formatter { +template <> struct formatter { public: - FMT_CONSTEXPR auto parse(parse_context& ctx) -> const Char* { + FMT_CONSTEXPR auto parse(parse_context<>& ctx) -> const char* { return ctx.begin(); } template auto format(const std::type_info& ti, Context& ctx) const -> decltype(ctx.out()) { - return detail::write_demangled_name(ctx.out(), ti); + return detail::write_demangled_name(ctx.out(), ti); } }; -#endif +#endif // FMT_USE_RTTI -template +template struct formatter< - T, Char, // DEPRECATED! Mixing code unit types. + T, char, typename std::enable_if::value>::type> { private: bool with_typename_ = false; public: - FMT_CONSTEXPR auto parse(parse_context& ctx) -> const Char* { + FMT_CONSTEXPR auto parse(parse_context<>& ctx) -> const char* { auto it = ctx.begin(); auto end = ctx.end(); if (it == end || *it == '}') return it; @@ -570,43 +580,15 @@ struct formatter< auto out = ctx.out(); #if FMT_USE_RTTI if (with_typename_) { - out = detail::write_demangled_name(out, typeid(ex)); + out = detail::write_demangled_name(out, typeid(ex)); *out++ = ':'; *out++ = ' '; } #endif - return detail::write_bytes(out, string_view(ex.what())); + return detail::write_bytes(out, string_view(ex.what())); } }; -namespace detail { - -template -struct has_flip : std::false_type {}; - -template -struct has_flip().flip())>> - : std::true_type {}; - -template struct is_bit_reference_like { - static constexpr const bool value = - std::is_convertible::value && - std::is_nothrow_assignable::value && has_flip::value; -}; - -#ifdef _LIBCPP_VERSION - -// Workaround for libc++ incompatibility with C++ standard. -// According to the Standard, `bitset::operator[] const` returns bool. -template -struct is_bit_reference_like> { - static constexpr const bool value = true; -}; - -#endif - -} // namespace detail - // We can't use std::vector::reference and // std::bitset::reference because the compiler can't deduce Allocator and N // in partial specialization. @@ -621,14 +603,6 @@ struct formatter -auto ptr(const std::unique_ptr& p) -> const void* { - return p.get(); -} -template auto ptr(const std::shared_ptr& p) -> const void* { - return p.get(); -} - template struct formatter, Char, enable_if_t::value>> @@ -715,7 +689,11 @@ template struct formatter, Char> { template struct formatter, Char, - enable_if_t, Char>::value>> + // Guard against format_as because reference_wrapper is + // implicitly convertible to T&. + enable_if_t, Char>::value && + !detail::has_format_as::value && + !detail::has_format_as_member::value>> : formatter, Char> { template auto format(std::reference_wrapper ref, FormatContext& ctx) const @@ -725,4 +703,5 @@ struct formatter, Char, }; FMT_END_NAMESPACE + #endif // FMT_STD_H_ diff --git a/include/spdlog/fmt/bundled/xchar.h b/include/spdlog/fmt/bundled/xchar.h index 90994fff..9334b87f 100644 --- a/include/spdlog/fmt/bundled/xchar.h +++ b/include/spdlog/fmt/bundled/xchar.h @@ -55,6 +55,16 @@ inline auto write_loc(basic_appender out, loc_value value, #endif return false; } + +template +void vformat_to(buffer& buf, basic_string_view fmt, + basic_format_args> args, + locale_ref loc = {}) { + static_assert(!std::is_same::value, ""); + auto out = basic_appender(buf); + parse_format_string( + fmt, format_handler{parse_context(fmt), {out, args, loc}}); +} } // namespace detail FMT_BEGIN_EXPORT @@ -112,10 +122,6 @@ inline auto runtime(wstring_view s) -> runtime_format_string { return {{s}}; } -#ifdef __cpp_char8_t -template <> struct is_char : bool_constant {}; -#endif - template constexpr auto make_wformat_args(T&... args) -> decltype(fmt::make_format_args(args...)) { @@ -151,13 +157,13 @@ auto join(std::initializer_list list, wstring_view sep) template ::value)> auto join(const Tuple& tuple, basic_string_view sep) - -> tuple_join_view { + -> tuple_join_view { return {tuple, sep}; } template ::value)> auto vformat(basic_string_view fmt, - typename detail::vformat_args::type args) + basic_format_args> args) -> std::basic_string { auto buf = basic_memory_buffer(); detail::vformat_to(buf, fmt, args); @@ -187,24 +193,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, - typename detail::vformat_args::type args) +template , + FMT_ENABLE_IF(detail::is_exotic_char::value)> +inline auto vformat(locale_ref loc, const S& fmt, + basic_format_args> 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...)); @@ -215,7 +217,7 @@ template ::value&& detail::is_exotic_char::value)> auto vformat_to(OutputIt out, const S& fmt, - typename detail::vformat_args::type args) -> OutputIt { + basic_format_args> args) -> OutputIt { auto&& buf = detail::get_buffer(out); detail::vformat_to(buf, detail::to_string_view(fmt), args); return detail::get_iterator(buf, out); @@ -231,27 +233,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, - typename detail::vformat_args::type args) + detail::is_exotic_char::value)> +inline auto vformat_to(OutputIt out, locale_ref loc, const S& fmt, + basic_format_args> 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...)); } @@ -260,7 +259,7 @@ template ::value&& detail::is_exotic_char::value)> inline auto vformat_to_n(OutputIt out, size_t n, basic_string_view fmt, - typename detail::vformat_args::type args) + basic_format_args> args) -> format_to_n_result { using traits = detail::fixed_buffer_traits; auto buf = detail::iterator_buffer(out, n); @@ -331,18 +330,6 @@ inline auto format(text_style ts, wformat_string fmt, T&&... args) return fmt::vformat(ts, fmt, fmt::make_wformat_args(args...)); } -template -FMT_DEPRECATED void print(std::FILE* f, text_style ts, wformat_string fmt, - const T&... args) { - vprint(f, ts, fmt, fmt::make_wformat_args(args...)); -} - -template -FMT_DEPRECATED void print(text_style ts, wformat_string fmt, - const T&... args) { - return print(stdout, ts, fmt, args...); -} - inline void vprint(std::wostream& os, wstring_view fmt, wformat_args args) { auto buffer = basic_memory_buffer(); detail::vformat_to(buffer, fmt, args); diff --git a/src/bundled_fmtlib_format.cpp b/src/bundled_fmtlib_format.cpp index 267a8ed1..faab72e9 100644 --- a/src/bundled_fmtlib_format.cpp +++ b/src/bundled_fmtlib_format.cpp @@ -11,17 +11,17 @@ #include FMT_BEGIN_NAMESPACE -namespace detail { - -template FMT_API auto dragonbox::to_decimal(float x) noexcept -> dragonbox::decimal_fp; -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 +namespace detail { + +template FMT_API auto dragonbox::to_decimal(float x) noexcept -> dragonbox::decimal_fp; +template FMT_API auto dragonbox::to_decimal(double x) noexcept -> dragonbox::decimal_fp; + // Explicit instantiations for char. template FMT_API auto thousands_sep_impl(locale_ref) -> thousands_sep_result; @@ -30,17 +30,12 @@ template FMT_API auto decimal_point_impl(locale_ref) -> char; // DEPRECATED! template FMT_API void buffer::append(const char*, const char*); -// DEPRECATED! -template FMT_API void vformat_to(buffer&, - string_view, - typename vformat_args<>::type, - locale_ref); - // Explicit instantiations for wchar_t. template FMT_API auto thousands_sep_impl(locale_ref) -> thousands_sep_result; template FMT_API auto decimal_point_impl(locale_ref) -> wchar_t; +// DEPRECATED! template FMT_API void buffer::append(const wchar_t*, const wchar_t*); } // namespace detail