diff --git a/include/spdlog/fmt/bundled/base.h b/include/spdlog/fmt/bundled/base.h index b886317d..87b3fd7c 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 110104 +#define FMT_VERSION 110200 // Detect compiler versions. #if defined(__clang__) && !defined(__ibmxl__) @@ -209,20 +209,6 @@ # define FMT_DEPRECATED /* deprecated */ #endif -#ifdef FMT_ALWAYS_INLINE -// Use the provided definition. -#elif FMT_GCC_VERSION || FMT_CLANG_VERSION -# define FMT_ALWAYS_INLINE inline __attribute__((always_inline)) -#else -# define FMT_ALWAYS_INLINE inline -#endif -// A version of FMT_ALWAYS_INLINE to prevent code bloat in debug mode. -#ifdef NDEBUG -# define FMT_INLINE FMT_ALWAYS_INLINE -#else -# define FMT_INLINE inline -#endif - #if FMT_GCC_VERSION || FMT_CLANG_VERSION # define FMT_VISIBILITY(value) __attribute__((visibility(value))) #else @@ -249,6 +235,28 @@ # define FMT_MSC_WARNING(...) #endif +// Enable minimal optimizations for more compact code in debug mode. +FMT_PRAGMA_GCC(push_options) +#if !defined(__OPTIMIZE__) && !defined(__CUDACC__) && !defined(FMT_MODULE) +FMT_PRAGMA_GCC(optimize("Og")) +# define FMT_GCC_OPTIMIZED +#endif +FMT_PRAGMA_CLANG(diagnostic push) + +#ifdef FMT_ALWAYS_INLINE +// Use the provided definition. +#elif FMT_GCC_VERSION || FMT_CLANG_VERSION +# define FMT_ALWAYS_INLINE inline __attribute__((always_inline)) +#else +# define FMT_ALWAYS_INLINE inline +#endif +// A version of FMT_ALWAYS_INLINE to prevent code bloat in debug mode. +#if defined(NDEBUG) || defined(FMT_GCC_OPTIMIZED) +# define FMT_INLINE FMT_ALWAYS_INLINE +#else +# define FMT_INLINE inline +#endif + #ifndef FMT_BEGIN_NAMESPACE # define FMT_BEGIN_NAMESPACE \ namespace fmt { \ @@ -297,13 +305,6 @@ using unused = int[]; \ (void)unused { 0, (expr, 0)... } -// Enable minimal optimizations for more compact code in debug mode. -FMT_PRAGMA_GCC(push_options) -#if !defined(__OPTIMIZE__) && !defined(__CUDACC__) && !defined(FMT_MODULE) -FMT_PRAGMA_GCC(optimize("Og")) -#endif -FMT_PRAGMA_CLANG(diagnostic push) - FMT_BEGIN_NAMESPACE // Implementations of enable_if_t and other metafunctions for older systems. @@ -325,8 +326,8 @@ using underlying_t = typename std::underlying_type::type; template using decay_t = typename std::decay::type; using nullptr_t = decltype(nullptr); -#if FMT_GCC_VERSION && FMT_GCC_VERSION < 500 -// A workaround for gcc 4.9 to make void_t work in a SFINAE context. +#if (FMT_GCC_VERSION && FMT_GCC_VERSION < 500) || FMT_MSC_VERSION +// A workaround for gcc 4.9 & MSVC v141 to make void_t work in a SFINAE context. template struct void_t_impl { using type = void; }; @@ -526,20 +527,20 @@ template class basic_string_view { constexpr basic_string_view() noexcept : data_(nullptr), size_(0) {} - /// Constructs a string reference object from a C string and a size. + /// Constructs a string view object from a C string and a size. constexpr basic_string_view(const Char* s, size_t count) noexcept : data_(s), size_(count) {} constexpr basic_string_view(nullptr_t) = delete; - /// Constructs a string reference object from a C string. + /// Constructs a string view object from a C string. #if FMT_GCC_VERSION FMT_ALWAYS_INLINE #endif 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) { - size_ = __builtin_strlen(detail::narrow(s)); + if (std::is_same::value && !detail::is_constant_evaluated()) { + size_ = __builtin_strlen(detail::narrow(s)); // strlen is not costexpr. return; } #endif @@ -548,7 +549,7 @@ template class basic_string_view { size_ = len; } - /// Constructs a string reference from a `std::basic_string` or a + /// Constructs a string view from a `std::basic_string` or a /// `std::basic_string_view` object. template ::value&& std::is_same< @@ -585,7 +586,6 @@ template class basic_string_view { return starts_with(basic_string_view(s)); } - // Lexicographically compare this string reference to other. FMT_CONSTEXPR auto compare(basic_string_view other) const -> int { int result = detail::compare(data_, other.data_, min_of(size_, other.size_)); @@ -616,7 +616,7 @@ template class basic_string_view { using string_view = basic_string_view; -/// Specifies if `T` is an extended character type. Can be specialized by users. +// 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 {}; @@ -625,7 +625,7 @@ template <> struct is_xchar : std::true_type {}; template <> struct is_xchar : std::true_type {}; #endif -// DEPRECATED! Will be replaced with an alias to prevent specializations. +// Specifies if `T` is a character (code unit) type. template struct is_char : is_xchar {}; template <> struct is_char : std::true_type {}; @@ -1032,6 +1032,11 @@ enum { struct view {}; +template +struct is_view : std::false_type {}; +template +struct is_view> : std::is_base_of {}; + template struct named_arg; template struct is_named_arg : std::false_type {}; template struct is_static_named_arg : std::false_type {}; @@ -1064,6 +1069,16 @@ template struct named_arg_info { int id; }; +// named_args is non-const to suppress a bogus -Wmaybe-uninitalized in gcc 13. +template +FMT_CONSTEXPR void check_for_duplicate(named_arg_info* named_args, + int named_arg_index, + basic_string_view arg_name) { + for (int i = 0; i < named_arg_index; ++i) { + if (named_args[i].name == arg_name) report_error("duplicate named arg"); + } +} + template ::value)> void init_named_arg(named_arg_info*, int& arg_index, int&, const T&) { ++arg_index; @@ -1071,6 +1086,7 @@ void init_named_arg(named_arg_info*, int& arg_index, int&, const T&) { template ::value)> void init_named_arg(named_arg_info* named_args, int& arg_index, int& named_arg_index, const T& arg) { + check_for_duplicate(named_args, named_arg_index, arg.name); named_args[named_arg_index++] = {arg.name, arg_index++}; } @@ -1084,12 +1100,13 @@ template ::value)> FMT_CONSTEXPR void init_static_named_arg(named_arg_info* named_args, int& arg_index, int& named_arg_index) { + check_for_duplicate(named_args, named_arg_index, T::name); named_args[named_arg_index++] = {T::name, arg_index++}; } // To minimize the number of types we need to deal with, long is translated // either to int or to long long depending on its size. -enum { long_short = sizeof(long) == sizeof(int) }; +enum { long_short = sizeof(long) == sizeof(int) && FMT_BUILTIN_TYPES }; using long_type = conditional_t; using ulong_type = conditional_t; @@ -1706,7 +1723,17 @@ class format_string_checker { -> const Char* { context_.advance_to(begin); if (id >= 0 && id < NUM_ARGS) return parse_funcs_[id](context_); - while (begin != end && *begin != '}') ++begin; + + // If id is out of range, it means we do not know the type and cannot parse + // the format at compile time. Instead, skip over content until we finish + // the format spec, accounting for any nested replacements. + for (int bracket_count = 0; + begin != end && (bracket_count > 0 || *begin != '}'); ++begin) { + if (*begin == '{') + ++bracket_count; + else if (*begin == '}') + --bracket_count; + } return begin; } @@ -2703,7 +2730,7 @@ template struct fstring { template FMT_CONSTEVAL FMT_ALWAYS_INLINE fstring(const char (&s)[N]) : str(s, N - 1) { using namespace detail; - static_assert(count<(std::is_base_of>::value && + static_assert(count<(is_view>::value && std::is_reference::value)...>() == 0, "passing views as lvalues is disallowed"); if (FMT_USE_CONSTEVAL) parse_format_string(s, checker(s, arg_pack())); diff --git a/include/spdlog/fmt/bundled/chrono.h b/include/spdlog/fmt/bundled/chrono.h index 50c777c8..e0c81589 100644 --- a/include/spdlog/fmt/bundled/chrono.h +++ b/include/spdlog/fmt/bundled/chrono.h @@ -22,21 +22,6 @@ #include "format.h" -namespace fmt_detail { -struct time_zone { - template - auto to_sys(T) - -> std::chrono::time_point { - return {}; - } -}; -template inline auto current_zone(T...) -> time_zone* { - return nullptr; -} - -template inline void _tzset(T...) {} -} // namespace fmt_detail - FMT_BEGIN_NAMESPACE // Enable safe chrono durations, unless explicitly disabled. @@ -435,14 +420,11 @@ auto write(OutputIt out, const std::tm& time, const std::locale& loc, return write_encoded_tm_str(out, string_view(buf.data(), buf.size()), loc); } -template -struct is_same_arithmetic_type - : public std::integral_constant::value && - std::is_integral::value) || - (std::is_floating_point::value && - std::is_floating_point::value)> { -}; +template +using is_similar_arithmetic_type = + bool_constant<(std::is_integral::value && std::is_integral::value) || + (std::is_floating_point::value && + std::is_floating_point::value)>; FMT_NORETURN inline void throw_duration_error() { FMT_THROW(format_error("cannot format duration")); @@ -501,9 +483,9 @@ auto duration_cast(std::chrono::duration from) -> To { #endif } -template < - typename To, typename FromRep, typename FromPeriod, - FMT_ENABLE_IF(!is_same_arithmetic_type::value)> +template ::value)> auto duration_cast(std::chrono::duration from) -> To { // Mixed integer <-> float cast is not supported by safe_duration_cast. return std::chrono::duration_cast(from); @@ -519,12 +501,30 @@ auto to_time_t(sys_time time_point) -> std::time_t { .count(); } -// Workaround a bug in libstdc++ which sets __cpp_lib_chrono to 201907 without -// providing current_zone(): https://github.com/fmtlib/fmt/issues/4160. -template FMT_CONSTEXPR auto has_current_zone() -> bool { - using namespace std::chrono; - using namespace fmt_detail; - return !std::is_same::value; +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 @@ -535,7 +535,7 @@ FMT_BEGIN_EXPORT * expressed in local time. Unlike `std::localtime`, this function is * thread-safe on most platforms. */ -inline auto localtime(std::time_t time) -> std::tm { +FMT_DEPRECATED inline auto localtime(std::time_t time) -> std::tm { struct dispatcher { std::time_t time_; std::tm tm_; @@ -572,11 +572,11 @@ inline auto localtime(std::time_t time) -> std::tm { } #if FMT_USE_LOCAL_TIME -template ())> -inline auto localtime(std::chrono::local_time time) -> std::tm { +template +FMT_DEPRECATED auto localtime(std::chrono::local_time time) + -> std::tm { using namespace std::chrono; - using namespace fmt_detail; + using namespace detail::tz; return localtime(detail::to_time_t(current_zone()->to_sys(time))); } #endif @@ -911,7 +911,14 @@ template struct null_chrono_spec_handler { FMT_CONSTEXPR void on_tz_name() { unsupported(); } }; -struct tm_format_checker : null_chrono_spec_handler { +class tm_format_checker : public null_chrono_spec_handler { + private: + bool has_timezone_ = false; + + public: + constexpr explicit tm_format_checker(bool has_timezone) + : has_timezone_(has_timezone) {} + FMT_NORETURN inline void unsupported() { FMT_THROW(format_error("no format")); } @@ -949,8 +956,12 @@ struct tm_format_checker : null_chrono_spec_handler { FMT_CONSTEXPR void on_24_hour_time() {} FMT_CONSTEXPR void on_iso_time() {} FMT_CONSTEXPR void on_am_pm() {} - FMT_CONSTEXPR void on_utc_offset(numeric_system) {} - FMT_CONSTEXPR void on_tz_name() {} + FMT_CONSTEXPR void on_utc_offset(numeric_system) { + if (!has_timezone_) FMT_THROW(format_error("no timezone")); + } + FMT_CONSTEXPR void on_tz_name() { + if (!has_timezone_) FMT_THROW(format_error("no timezone")); + } }; inline auto tm_wday_full_name(int wday) -> const char* { @@ -980,24 +991,27 @@ inline auto tm_mon_short_name(int mon) -> const char* { } template -struct has_member_data_tm_gmtoff : std::false_type {}; +struct has_tm_gmtoff : std::false_type {}; template -struct has_member_data_tm_gmtoff> - : std::true_type {}; +struct has_tm_gmtoff> : std::true_type {}; -template -struct has_member_data_tm_zone : std::false_type {}; +template struct has_tm_zone : std::false_type {}; template -struct has_member_data_tm_zone> - : std::true_type {}; +struct has_tm_zone> : std::true_type {}; -inline void tzset_once() { - static bool init = []() { - using namespace fmt_detail; - _tzset(); - return false; - }(); - ignore_unused(init); +template ::value)> +bool set_tm_zone(T& time, char* tz) { + time.tm_zone = tz; + return true; +} +template ::value)> +bool set_tm_zone(T&, char*) { + return false; +} + +inline char* utc() { + static char tz[] = "UTC"; + return tz; } // Converts value to Int and checks that it's in the range [0, upper). @@ -1005,7 +1019,7 @@ template ::value)> inline auto to_nonnegative_int(T value, Int upper) -> Int { if (!std::is_unsigned::value && (value < 0 || to_unsigned(value) > to_unsigned(upper))) { - FMT_THROW(fmt::format_error("chrono value is out of range")); + FMT_THROW(format_error("chrono value is out of range")); } return static_cast(value); } @@ -1090,7 +1104,7 @@ void write_fractional_seconds(OutputIt& out, Duration d, int precision = -1) { // Format subseconds which are given as a floating point type with an // appropriate number of digits. We cannot pass the Duration here, as we -// explicitly need to pass the Rep value in the chrono_formatter. +// explicitly need to pass the Rep value in the duration_formatter. template void write_floating_seconds(memory_buffer& buf, Duration duration, int num_fractional_digits = -1) { @@ -1124,7 +1138,7 @@ class tm_writer { static constexpr int days_per_week = 7; const std::locale& loc_; - const bool is_classic_; + bool is_classic_; OutputIt out_; const Duration* subsecs_; const std::tm& tm_; @@ -1160,8 +1174,8 @@ class tm_writer { } auto tm_hour12() const noexcept -> int { - const auto h = tm_hour(); - const auto z = h < 12 ? h : h - 12; + auto h = tm_hour(); + auto z = h < 12 ? h : h - 12; return z == 0 ? 12 : z; } @@ -1177,11 +1191,11 @@ class tm_writer { // Algorithm: https://en.wikipedia.org/wiki/ISO_week_date. auto iso_year_weeks(long long curr_year) const noexcept -> int { - const auto prev_year = curr_year - 1; - const auto curr_p = + auto prev_year = curr_year - 1; + auto curr_p = (curr_year + curr_year / 4 - curr_year / 100 + curr_year / 400) % days_per_week; - const auto prev_p = + auto prev_p = (prev_year + prev_year / 4 - prev_year / 100 + prev_year / 400) % days_per_week; return 52 + ((curr_p == 4 || prev_p == 3) ? 1 : 0); @@ -1191,15 +1205,15 @@ class tm_writer { days_per_week; } auto tm_iso_week_year() const noexcept -> long long { - const auto year = tm_year(); - const auto w = iso_week_num(tm_yday(), tm_wday()); + auto year = tm_year(); + auto w = iso_week_num(tm_yday(), tm_wday()); if (w < 1) return year - 1; if (w > iso_year_weeks(year)) return year + 1; return year; } auto tm_iso_week_of_year() const noexcept -> int { - const auto year = tm_year(); - const auto w = iso_week_num(tm_yday(), tm_wday()); + auto year = tm_year(); + auto w = iso_week_num(tm_yday(), tm_wday()); if (w < 1) return iso_year_weeks(year - 1); if (w > iso_year_weeks(year)) return 1; return w; @@ -1236,9 +1250,8 @@ class tm_writer { uint32_or_64_or_128_t n = to_unsigned(year); const int num_digits = count_digits(n); if (negative && pad == pad_type::zero) *out_++ = '-'; - if (width > num_digits) { + if (width > num_digits) out_ = detail::write_padding(out_, pad, width - num_digits); - } if (negative && pad != pad_type::zero) *out_++ = '-'; out_ = format_decimal(out_, n, num_digits); } @@ -1259,45 +1272,22 @@ class tm_writer { write2(static_cast(offset % 60)); } - template ::value)> - void format_utc_offset_impl(const T& tm, numeric_system ns) { + template ::value)> + void format_utc_offset(const T& tm, numeric_system ns) { write_utc_offset(tm.tm_gmtoff, ns); } - template ::value)> - void format_utc_offset_impl(const T& tm, numeric_system ns) { -#if defined(_WIN32) && defined(_UCRT) - tzset_once(); - long offset = 0; - _get_timezone(&offset); - if (tm.tm_isdst) { - long dstbias = 0; - _get_dstbias(&dstbias); - offset += dstbias; - } - write_utc_offset(-offset, ns); -#else - if (ns == numeric_system::standard) return format_localized('z'); - - // Extract timezone offset from timezone conversion functions. - std::tm gtm = tm; - std::time_t gt = std::mktime(>m); - std::tm ltm = gmtime(gt); - std::time_t lt = std::mktime(<m); - long long offset = gt - lt; - write_utc_offset(offset, ns); -#endif + template ::value)> + void format_utc_offset(const T&, numeric_system ns) { + write_utc_offset(0, ns); } - template ::value)> - void format_tz_name_impl(const T& tm) { - if (is_classic_) - out_ = write_tm_str(out_, tm.tm_zone, loc_); - else - format_localized('Z'); + template ::value)> + void format_tz_name(const T& tm) { + out_ = write_tm_str(out_, tm.tm_zone, loc_); } - template ::value)> - void format_tz_name_impl(const T&) { - format_localized('Z'); + template ::value)> + void format_tz_name(const T&) { + out_ = std::copy_n(utc(), 3, out_); } void format_localized(char format, char modifier = 0) { @@ -1408,8 +1398,8 @@ class tm_writer { out_ = copy(std::begin(buf) + offset, std::end(buf), out_); } - void on_utc_offset(numeric_system ns) { format_utc_offset_impl(tm_, ns); } - void on_tz_name() { format_tz_name_impl(tm_); } + void on_utc_offset(numeric_system ns) { format_utc_offset(tm_, ns); } + void on_tz_name() { format_tz_name(tm_); } void on_year(numeric_system ns, pad_type pad) { if (is_classic_ || ns == numeric_system::standard) @@ -1483,11 +1473,10 @@ class tm_writer { void on_day_of_year(pad_type pad) { auto yday = tm_yday() + 1; auto digit1 = yday / 100; - if (digit1 != 0) { + if (digit1 != 0) write1(digit1); - } else { + else out_ = detail::write_padding(out_, pad); - } write2(yday % 100, pad); } @@ -1624,18 +1613,16 @@ template ::value)> inline auto get_milliseconds(std::chrono::duration d) -> std::chrono::duration { - // this may overflow and/or the result may not fit in the - // target type. + // This may overflow and/or the result may not fit in the target type. #if FMT_SAFE_DURATION_CAST - using CommonSecondsType = + using common_seconds_type = typename std::common_type::type; - const auto d_as_common = detail::duration_cast(d); - const auto d_as_whole_seconds = + auto d_as_common = detail::duration_cast(d); + auto d_as_whole_seconds = detail::duration_cast(d_as_common); - // this conversion should be nonproblematic - const auto diff = d_as_common - d_as_whole_seconds; - const auto ms = - detail::duration_cast>(diff); + // This conversion should be nonproblematic. + auto diff = d_as_common - d_as_whole_seconds; + auto ms = detail::duration_cast>(diff); return ms; #else auto s = detail::duration_cast(d); @@ -1707,32 +1694,28 @@ class get_locale { } }; -template -struct chrono_formatter { - FormatContext& context; - OutputIt out; - int precision; - bool localized = false; +template +struct duration_formatter { + using iterator = basic_appender; + iterator out; // rep is unsigned to avoid overflow. using rep = conditional_t::value && sizeof(Rep) < sizeof(int), unsigned, typename make_unsigned_or_unchanged::type>; rep val; + int precision; + locale_ref locale; + bool localized = false; using seconds = std::chrono::duration; seconds s; using milliseconds = std::chrono::duration; bool negative; - using char_type = typename FormatContext::char_type; - using tm_writer_type = tm_writer; + using tm_writer_type = tm_writer; - chrono_formatter(FormatContext& ctx, OutputIt o, - std::chrono::duration d) - : context(ctx), - out(o), - val(static_cast(d.count())), - negative(false) { + duration_formatter(iterator o, std::chrono::duration d, + locale_ref loc) + : out(o), val(static_cast(d.count())), locale(loc), negative(false) { if (d.count() < 0) { val = 0 - val; negative = true; @@ -1746,19 +1729,16 @@ struct chrono_formatter { // returns true if nan or inf, writes to out. auto handle_nan_inf() -> bool { - if (isfinite(val)) { - return false; - } + if (isfinite(val)) return false; if (isnan(val)) { write_nan(); return true; } // must be +-inf - if (val > 0) { - write_pinf(); - } else { - write_ninf(); - } + if (val > 0) + std::copy_n("inf", 3, out); + else + std::copy_n("-inf", 4, out); return true; } @@ -1786,10 +1766,9 @@ struct chrono_formatter { } void write_sign() { - if (negative) { - *out++ = '-'; - negative = false; - } + if (!negative) return; + *out++ = '-'; + negative = false; } void write(Rep value, int width, pad_type pad = pad_type::zero) { @@ -1801,24 +1780,22 @@ struct chrono_formatter { if (width > num_digits) { out = detail::write_padding(out, pad, width - num_digits); } - out = format_decimal(out, n, num_digits); + out = format_decimal(out, n, num_digits); } void write_nan() { std::copy_n("nan", 3, out); } - void write_pinf() { std::copy_n("inf", 3, out); } - void write_ninf() { std::copy_n("-inf", 4, out); } template void format_tm(const tm& time, Callback cb, Args... args) { if (isnan(val)) return write_nan(); - get_locale loc(localized, context.locale()); + get_locale loc(localized, locale); auto w = tm_writer_type(loc, out, time); (w.*cb)(args...); out = w.out(); } - void on_text(const char_type* begin, const char_type* end) { - copy(begin, end, out); + void on_text(const Char* begin, const Char* end) { + copy(begin, end, out); } // These are not implemented because durations don't have date information. @@ -1888,13 +1865,12 @@ struct chrono_formatter { write_floating_seconds(buf, std::chrono::duration(val), precision); if (negative) *out++ = '-'; - if (buf.size() < 2 || buf[1] == '.') { + if (buf.size() < 2 || buf[1] == '.') out = detail::write_padding(out, pad); - } - out = copy(buf.begin(), buf.end(), out); + out = copy(buf.begin(), buf.end(), out); } else { write(second(), 2, pad); - write_fractional_seconds( + write_fractional_seconds( out, std::chrono::duration(val), precision); } return; @@ -1936,12 +1912,10 @@ struct chrono_formatter { void on_duration_value() { if (handle_nan_inf()) return; write_sign(); - out = format_duration_value(out, val, precision); + out = format_duration_value(out, val, precision); } - void on_duration_unit() { - out = format_duration_unit(out); - } + void on_duration_unit() { out = format_duration_unit(out); } }; } // namespace detail @@ -2011,12 +1985,11 @@ class year_month_day { constexpr auto month() const noexcept -> fmt::month { return month_; } constexpr auto day() const noexcept -> fmt::day { return day_; } }; -#endif +#endif // __cpp_lib_chrono >= 201907 template struct formatter : private formatter { private: - bool localized_ = false; bool use_tm_formatter_ = false; public: @@ -2024,8 +1997,7 @@ struct formatter : private formatter { auto it = ctx.begin(), end = ctx.end(); if (it != end && *it == 'L') { ++it; - localized_ = true; - return it; + this->set_localized(); } use_tm_formatter_ = it != end && *it != '}'; return use_tm_formatter_ ? formatter::parse(ctx) : it; @@ -2036,7 +2008,7 @@ struct formatter : private formatter { auto time = std::tm(); time.tm_wday = static_cast(wd.c_encoding()); if (use_tm_formatter_) return formatter::format(time, ctx); - detail::get_locale loc(localized_, ctx.locale()); + detail::get_locale loc(this->localized(), ctx.locale()); auto w = detail::tm_writer(loc, ctx.out(), time); w.on_abbr_weekday(); return w.out(); @@ -2070,7 +2042,6 @@ struct formatter : private formatter { template struct formatter : private formatter { private: - bool localized_ = false; bool use_tm_formatter_ = false; public: @@ -2078,8 +2049,7 @@ struct formatter : private formatter { auto it = ctx.begin(), end = ctx.end(); if (it != end && *it == 'L') { ++it; - localized_ = true; - return it; + this->set_localized(); } use_tm_formatter_ = it != end && *it != '}'; return use_tm_formatter_ ? formatter::parse(ctx) : it; @@ -2090,7 +2060,7 @@ struct formatter : private formatter { auto time = std::tm(); time.tm_mon = static_cast(static_cast(m)) - 1; if (use_tm_formatter_) return formatter::format(time, ctx); - detail::get_locale loc(localized_, ctx.locale()); + detail::get_locale loc(this->localized(), ctx.locale()); auto w = detail::tm_writer(loc, ctx.out(), time); w.on_abbr_month(); return w.out(); @@ -2154,7 +2124,6 @@ struct formatter, Char> { format_specs specs_; detail::arg_ref width_ref_; detail::arg_ref precision_ref_; - bool localized_ = false; basic_string_view fmt_; public: @@ -2177,7 +2146,7 @@ struct formatter, Char> { it = detail::parse_precision(it, end, specs_, precision_ref_, ctx); } if (it != end && *it == 'L') { - localized_ = true; + specs_.set_localized(); ++it; } end = detail::parse_chrono_format(it, end, checker); @@ -2204,11 +2173,10 @@ struct formatter, Char> { out = detail::format_duration_value(out, d.count(), precision); detail::format_duration_unit(out); } else { - using chrono_formatter = - detail::chrono_formatter; - auto f = chrono_formatter(ctx, out, d); + auto f = + detail::duration_formatter(out, d, ctx.locale()); f.precision = precision; - f.localized = localized_; + f.localized = specs_.localized(); detail::parse_chrono_format(begin, end, f); } return detail::write( @@ -2220,9 +2188,38 @@ template struct formatter { private: format_specs specs_; detail::arg_ref width_ref_; + basic_string_view fmt_ = + detail::string_literal(); protected: - basic_string_view fmt_; + auto localized() const -> bool { return specs_.localized(); } + FMT_CONSTEXPR void set_localized() { specs_.set_localized(); } + + FMT_CONSTEXPR auto do_parse(parse_context& ctx, bool has_timezone) + -> const Char* { + auto it = ctx.begin(), end = ctx.end(); + if (it == end || *it == '}') return it; + + it = detail::parse_align(it, end, specs_); + if (it == end) return it; + + Char c = *it; + if ((c >= '0' && c <= '9') || c == '{') { + it = detail::parse_width(it, end, specs_, width_ref_, ctx); + if (it == end) return it; + } + + if (*it == 'L') { + specs_.set_localized(); + ++it; + } + + end = detail::parse_chrono_format(it, end, + detail::tm_format_checker(has_timezone)); + // Replace the default format string only if the new spec is not empty. + if (end != it) fmt_ = {it, detail::to_unsigned(end - it)}; + return end; + } template auto do_format(const std::tm& tm, FormatContext& ctx, @@ -2233,10 +2230,10 @@ template struct formatter { detail::handle_dynamic_spec(specs.dynamic_width(), specs.width, width_ref_, ctx); - auto loc_ref = ctx.locale(); + auto loc_ref = specs.localized() ? ctx.locale() : detail::locale_ref(); detail::get_locale loc(static_cast(loc_ref), loc_ref); - auto w = - detail::tm_writer(loc, out, tm, subsecs); + auto w = detail::tm_writer, Char, Duration>( + loc, out, tm, subsecs); detail::parse_chrono_format(fmt_.begin(), fmt_.end(), w); return detail::write( ctx.out(), basic_string_view(buf.data(), buf.size()), specs); @@ -2244,22 +2241,7 @@ template struct formatter { public: FMT_CONSTEXPR auto parse(parse_context& ctx) -> const Char* { - auto it = ctx.begin(), end = ctx.end(); - if (it == end || *it == '}') return it; - - it = detail::parse_align(it, end, specs_); - if (it == end) return it; - - Char c = *it; - if ((c >= '0' && c <= '9') || c == '{') { - it = detail::parse_width(it, end, specs_, width_ref_, ctx); - if (it == end) return it; - } - - end = detail::parse_chrono_format(it, end, detail::tm_format_checker()); - // Replace the default format string only if the new spec is not empty. - if (end != it) fmt_ = {it, detail::to_unsigned(end - it)}; - return end; + return do_parse(ctx, detail::has_tm_gmtoff::value); } template @@ -2269,10 +2251,11 @@ template struct formatter { } }; +// DEPRECATED! Reversed order of template parameters. template -struct formatter, Char> : formatter { - FMT_CONSTEXPR formatter() { - this->fmt_ = detail::string_literal(); +struct formatter, Char> : private formatter { + FMT_CONSTEXPR auto parse(parse_context& ctx) -> const Char* { + return this->do_parse(ctx, true); } template @@ -2283,6 +2266,7 @@ struct formatter, Char> : formatter { if (detail::const_check( period::num == 1 && period::den == 1 && !std::is_floating_point::value)) { + detail::set_tm_zone(tm, detail::utc()); return formatter::format(tm, ctx); } Duration epoch = val.time_since_epoch(); @@ -2290,11 +2274,13 @@ struct formatter, Char> : formatter { epoch - detail::duration_cast(epoch)); if (subsecs.count() < 0) { auto second = detail::duration_cast(std::chrono::seconds(1)); - if (tm.tm_sec != 0) + if (tm.tm_sec != 0) { --tm.tm_sec; - else + } else { tm = gmtime(val - second); - subsecs += detail::duration_cast(std::chrono::seconds(1)); + detail::set_tm_zone(tm, detail::utc()); + } + subsecs += second; } return formatter::do_format(tm, ctx, &subsecs); } @@ -2312,23 +2298,29 @@ struct formatter, Char> }; template -struct formatter, Char> : formatter { - FMT_CONSTEXPR formatter() { - this->fmt_ = detail::string_literal(); +struct formatter, Char> + : private formatter { + FMT_CONSTEXPR auto parse(parse_context& ctx) -> const Char* { + return this->do_parse(ctx, false); } template auto format(local_time val, FormatContext& ctx) const -> decltype(ctx.out()) { + auto time_since_epoch = val.time_since_epoch(); + auto seconds_since_epoch = + detail::duration_cast(time_since_epoch); + // Use gmtime to prevent time zone conversion since local_time has an + // unspecified time zone. + std::tm t = gmtime(seconds_since_epoch.count()); using period = typename Duration::period; if (period::num == 1 && period::den == 1 && !std::is_floating_point::value) { - return formatter::format(localtime(val), ctx); + return formatter::format(t, ctx); } - auto epoch = val.time_since_epoch(); - auto subsecs = detail::duration_cast( - epoch - detail::duration_cast(epoch)); - return formatter::do_format(localtime(val), ctx, &subsecs); + auto subsecs = + detail::duration_cast(time_since_epoch - seconds_since_epoch); + return formatter::do_format(t, ctx, &subsecs); } }; diff --git a/include/spdlog/fmt/bundled/color.h b/include/spdlog/fmt/bundled/color.h index 2faaf3a0..638f15b4 100644 --- a/include/spdlog/fmt/bundled/color.h +++ b/include/spdlog/fmt/bundled/color.h @@ -190,11 +190,11 @@ enum class emphasis : uint8_t { // rgb is a struct for red, green and blue colors. // Using the name "rgb" makes some editors show the color in a tooltip. struct rgb { - FMT_CONSTEXPR rgb() : r(0), g(0), b(0) {} - FMT_CONSTEXPR rgb(uint8_t r_, uint8_t g_, uint8_t b_) : r(r_), g(g_), b(b_) {} - FMT_CONSTEXPR rgb(uint32_t hex) + constexpr rgb() : r(0), g(0), b(0) {} + constexpr rgb(uint8_t r_, uint8_t g_, uint8_t b_) : r(r_), g(g_), b(b_) {} + constexpr rgb(uint32_t hex) : r((hex >> 16) & 0xFF), g((hex >> 8) & 0xFF), b(hex & 0xFF) {} - FMT_CONSTEXPR rgb(color hex) + constexpr rgb(color hex) : r((uint32_t(hex) >> 16) & 0xFF), g((uint32_t(hex) >> 8) & 0xFF), b(uint32_t(hex) & 0xFF) {} @@ -205,97 +205,135 @@ struct rgb { namespace detail { -// color is a struct of either a rgb color or a terminal color. +// A bit-packed variant of an RGB color, a terminal color, or unset color. +// see text_style for the bit-packing scheme. struct color_type { - FMT_CONSTEXPR color_type() noexcept : is_rgb(), value{} {} - FMT_CONSTEXPR color_type(color rgb_color) noexcept : is_rgb(true), value{} { - value.rgb_color = static_cast(rgb_color); + constexpr color_type() noexcept = default; + constexpr color_type(color rgb_color) noexcept + : value_(static_cast(rgb_color) | (1 << 24)) {} + constexpr color_type(rgb rgb_color) noexcept + : color_type(static_cast( + (static_cast(rgb_color.r) << 16) | + (static_cast(rgb_color.g) << 8) | rgb_color.b)) {} + constexpr color_type(terminal_color term_color) noexcept + : value_(static_cast(term_color) | (3 << 24)) {} + + constexpr auto is_terminal_color() const noexcept -> bool { + return (value_ & (1 << 25)) != 0; } - FMT_CONSTEXPR color_type(rgb rgb_color) noexcept : is_rgb(true), value{} { - value.rgb_color = (static_cast(rgb_color.r) << 16) | - (static_cast(rgb_color.g) << 8) | rgb_color.b; - } - FMT_CONSTEXPR color_type(terminal_color term_color) noexcept - : is_rgb(), value{} { - value.term_color = static_cast(term_color); + + constexpr auto value() const noexcept -> uint32_t { + return value_ & 0xFFFFFF; } - bool is_rgb; - union color_union { - uint8_t term_color; - uint32_t rgb_color; - } value; + + constexpr color_type(uint32_t value) noexcept : value_(value) {} + + uint32_t value_ = 0; }; } // namespace detail /// A text style consisting of foreground and background colors and emphasis. class text_style { + // The information is packed as follows: + // ┌──┐ + // │ 0│─┐ + // │..│ ├── foreground color value + // │23│─┘ + // ├──┤ + // │24│─┬── discriminator for the above value. 00 if unset, 01 if it's + // │25│─┘ an RGB color, or 11 if it's a terminal color (10 is unused) + // ├──┤ + // │26│──── overflow bit, always zero (see below) + // ├──┤ + // │27│─┐ + // │..│ │ + // │50│ │ + // ├──┤ │ + // │51│ ├── background color (same format as the foreground color) + // │52│ │ + // ├──┤ │ + // │53│─┘ + // ├──┤ + // │54│─┐ + // │..│ ├── emphases + // │61│─┘ + // ├──┤ + // │62│─┬── unused + // │63│─┘ + // └──┘ + // The overflow bits are there to make operator|= efficient. + // When ORing, we must throw if, for either the foreground or background, + // one style specifies a terminal color and the other specifies any color + // (terminal or RGB); in other words, if one discriminator is 11 and the + // other is 11 or 01. + // + // We do that check by adding the styles. Consider what adding does to each + // possible pair of discriminators: + // 00 + 00 = 000 + // 01 + 00 = 001 + // 11 + 00 = 011 + // 01 + 01 = 010 + // 11 + 01 = 100 (!!) + // 11 + 11 = 110 (!!) + // In the last two cases, the ones we want to catch, the third bit——the + // overflow bit——is set. Bingo. + // + // We must take into account the possible carry bit from the bits + // before the discriminator. The only potentially problematic case is + // 11 + 00 = 011 (a carry bit would make it 100, not good!), but a carry + // bit is impossible in that case, because 00 (unset color) means the + // 24 bits that precede the discriminator are all zero. + // + // This test can be applied to both colors simultaneously. + public: FMT_CONSTEXPR text_style(emphasis em = emphasis()) noexcept - : set_foreground_color(), set_background_color(), ems(em) {} - - FMT_CONSTEXPR auto operator|=(const text_style& rhs) -> text_style& { - if (!set_foreground_color) { - set_foreground_color = rhs.set_foreground_color; - foreground_color = rhs.foreground_color; - } else if (rhs.set_foreground_color) { - if (!foreground_color.is_rgb || !rhs.foreground_color.is_rgb) - report_error("can't OR a terminal color"); - foreground_color.value.rgb_color |= rhs.foreground_color.value.rgb_color; - } - - if (!set_background_color) { - set_background_color = rhs.set_background_color; - background_color = rhs.background_color; - } else if (rhs.set_background_color) { - if (!background_color.is_rgb || !rhs.background_color.is_rgb) - report_error("can't OR a terminal color"); - background_color.value.rgb_color |= rhs.background_color.value.rgb_color; - } + : style_(static_cast(em) << 54) {} - ems = static_cast(static_cast(ems) | - static_cast(rhs.ems)); + FMT_CONSTEXPR auto operator|=(text_style rhs) -> text_style& { + if (((style_ + rhs.style_) & ((1ULL << 26) | (1ULL << 53))) != 0) + report_error("can't OR a terminal color"); + style_ |= rhs.style_; return *this; } - friend FMT_CONSTEXPR auto operator|(text_style lhs, const text_style& rhs) + friend FMT_CONSTEXPR auto operator|(text_style lhs, text_style rhs) -> text_style { return lhs |= rhs; } + FMT_CONSTEXPR auto operator==(text_style rhs) const noexcept -> bool { + return style_ == rhs.style_; + } + + FMT_CONSTEXPR auto operator!=(text_style rhs) const noexcept -> bool { + return !(*this == rhs); + } + FMT_CONSTEXPR auto has_foreground() const noexcept -> bool { - return set_foreground_color; + return (style_ & (1 << 24)) != 0; } FMT_CONSTEXPR auto has_background() const noexcept -> bool { - return set_background_color; + return (style_ & (1ULL << 51)) != 0; } FMT_CONSTEXPR auto has_emphasis() const noexcept -> bool { - return static_cast(ems) != 0; + return (style_ >> 54) != 0; } FMT_CONSTEXPR auto get_foreground() const noexcept -> detail::color_type { FMT_ASSERT(has_foreground(), "no foreground specified for this style"); - return foreground_color; + return style_ & 0x3FFFFFF; } FMT_CONSTEXPR auto get_background() const noexcept -> detail::color_type { FMT_ASSERT(has_background(), "no background specified for this style"); - return background_color; + return (style_ >> 27) & 0x3FFFFFF; } FMT_CONSTEXPR auto get_emphasis() const noexcept -> emphasis { FMT_ASSERT(has_emphasis(), "no emphasis specified for this style"); - return ems; + return static_cast(style_ >> 54); } private: - FMT_CONSTEXPR text_style(bool is_foreground, - detail::color_type text_color) noexcept - : set_foreground_color(), set_background_color(), ems() { - if (is_foreground) { - foreground_color = text_color; - set_foreground_color = true; - } else { - background_color = text_color; - set_background_color = true; - } - } + FMT_CONSTEXPR text_style(uint64_t style) noexcept : style_(style) {} friend FMT_CONSTEXPR auto fg(detail::color_type foreground) noexcept -> text_style; @@ -303,23 +341,19 @@ class text_style { friend FMT_CONSTEXPR auto bg(detail::color_type background) noexcept -> text_style; - detail::color_type foreground_color; - detail::color_type background_color; - bool set_foreground_color; - bool set_background_color; - emphasis ems; + uint64_t style_ = 0; }; /// Creates a text style from the foreground (text) color. FMT_CONSTEXPR inline auto fg(detail::color_type foreground) noexcept -> text_style { - return text_style(true, foreground); + return foreground.value_; } /// Creates a text style from the background color. FMT_CONSTEXPR inline auto bg(detail::color_type background) noexcept -> text_style { - return text_style(false, background); + return static_cast(background.value_) << 27; } FMT_CONSTEXPR inline auto operator|(emphasis lhs, emphasis rhs) noexcept @@ -334,9 +368,9 @@ template struct ansi_color_escape { const char* esc) noexcept { // If we have a terminal color, we need to output another escape code // sequence. - if (!text_color.is_rgb) { + if (text_color.is_terminal_color()) { bool is_background = esc == string_view("\x1b[48;2;"); - uint32_t value = text_color.value.term_color; + uint32_t value = text_color.value(); // Background ASCII codes are the same as the foreground ones but with // 10 more. if (is_background) value += 10u; @@ -360,7 +394,7 @@ template struct ansi_color_escape { for (int i = 0; i < 7; i++) { buffer[i] = static_cast(esc[i]); } - rgb color(text_color.value.rgb_color); + rgb color(text_color.value()); to_esc(color.r, buffer + 7, ';'); to_esc(color.g, buffer + 11, ';'); to_esc(color.b, buffer + 15, 'm'); @@ -441,32 +475,26 @@ template struct styled_arg : view { }; template -void vformat_to(buffer& buf, const text_style& ts, - basic_string_view fmt, +void vformat_to(buffer& buf, text_style ts, basic_string_view fmt, basic_format_args> args) { - bool has_style = false; if (ts.has_emphasis()) { - has_style = true; auto emphasis = make_emphasis(ts.get_emphasis()); buf.append(emphasis.begin(), emphasis.end()); } if (ts.has_foreground()) { - has_style = true; auto foreground = make_foreground_color(ts.get_foreground()); buf.append(foreground.begin(), foreground.end()); } if (ts.has_background()) { - has_style = true; auto background = make_background_color(ts.get_background()); buf.append(background.begin(), background.end()); } vformat_to(buf, fmt, args); - if (has_style) reset_color(buf); + if (ts != text_style()) reset_color(buf); } } // namespace detail -inline void vprint(FILE* f, const text_style& ts, string_view fmt, - format_args args) { +inline void vprint(FILE* f, text_style ts, string_view fmt, format_args args) { auto buf = memory_buffer(); detail::vformat_to(buf, ts, fmt, args); print(f, FMT_STRING("{}"), string_view(buf.begin(), buf.size())); @@ -482,8 +510,7 @@ inline void vprint(FILE* f, const text_style& ts, string_view fmt, * "Elapsed time: {0:.2f} seconds", 1.23); */ template -void print(FILE* f, const text_style& ts, format_string fmt, - T&&... args) { +void print(FILE* f, text_style ts, format_string fmt, T&&... args) { vprint(f, ts, fmt.str, vargs{{args...}}); } @@ -497,11 +524,11 @@ void print(FILE* f, const text_style& ts, format_string fmt, * "Elapsed time: {0:.2f} seconds", 1.23); */ template -void print(const text_style& ts, format_string fmt, T&&... args) { +void print(text_style ts, format_string fmt, T&&... args) { return print(stdout, ts, fmt, std::forward(args)...); } -inline auto vformat(const text_style& ts, string_view fmt, format_args args) +inline auto vformat(text_style ts, string_view fmt, format_args args) -> std::string { auto buf = memory_buffer(); detail::vformat_to(buf, ts, fmt, args); @@ -521,7 +548,7 @@ inline auto vformat(const text_style& ts, string_view fmt, format_args args) * ``` */ template -inline auto format(const text_style& ts, format_string fmt, T&&... args) +inline auto format(text_style ts, format_string fmt, T&&... args) -> std::string { return fmt::vformat(ts, fmt.str, vargs{{args...}}); } @@ -529,8 +556,8 @@ inline auto format(const text_style& ts, format_string fmt, T&&... args) /// Formats a string with the given text_style and writes the output to `out`. template ::value)> -auto vformat_to(OutputIt out, const text_style& ts, string_view fmt, - format_args args) -> OutputIt { +auto vformat_to(OutputIt out, text_style ts, string_view fmt, format_args args) + -> OutputIt { auto&& buf = detail::get_buffer(out); detail::vformat_to(buf, ts, fmt, args); return detail::get_iterator(buf, out); @@ -548,8 +575,8 @@ auto vformat_to(OutputIt out, const text_style& ts, string_view fmt, */ template ::value)> -inline auto format_to(OutputIt out, const text_style& ts, - format_string fmt, T&&... args) -> OutputIt { +inline auto format_to(OutputIt out, text_style ts, format_string fmt, + T&&... args) -> OutputIt { return vformat_to(out, ts, fmt.str, vargs{{args...}}); } diff --git a/include/spdlog/fmt/bundled/format-inl.h b/include/spdlog/fmt/bundled/format-inl.h index a5b79dbe..a1e01661 100644 --- a/include/spdlog/fmt/bundled/format-inl.h +++ b/include/spdlog/fmt/bundled/format-inl.h @@ -212,7 +212,7 @@ inline auto floor_log10_pow2_minus_log10_4_over_3(int e) noexcept -> int { return (e * 631305 - 261663) >> 21; } -FMT_INLINE_VARIABLE constexpr struct { +FMT_INLINE_VARIABLE constexpr struct div_small_pow10_infos_struct { uint32_t divisor; int shift_amount; } div_small_pow10_infos[] = {{10, 16}, {100, 16}}; @@ -1097,7 +1097,7 @@ template <> struct cache_accessor { return {r.high(), r.low() == 0}; } - static auto compute_delta(cache_entry_type const& cache, int beta) noexcept + static auto compute_delta(const cache_entry_type& cache, int beta) noexcept -> uint32_t { return static_cast(cache.high() >> (64 - 1 - beta)); } @@ -1526,9 +1526,8 @@ template class glibc_file : public file_base { } void init_buffer() { - if (this->file_->_IO_write_ptr) return; + if (this->file_->_IO_write_ptr < this->file_->_IO_write_end) return; // Force buffer initialization by placing and removing a char in a buffer. - assume(this->file_->_IO_write_ptr >= this->file_->_IO_write_end); putc_unlocked(0, this->file_); --this->file_->_IO_write_ptr; } diff --git a/include/spdlog/fmt/bundled/format.h b/include/spdlog/fmt/bundled/format.h index 287e7163..50e57144 100644 --- a/include/spdlog/fmt/bundled/format.h +++ b/include/spdlog/fmt/bundled/format.h @@ -117,6 +117,7 @@ # define FMT_NOINLINE #endif +// GCC 4.9 doesn't support qualified names in specializations. namespace std { template struct iterator_traits> { using iterator_category = output_iterator_tag; @@ -705,7 +706,7 @@ using is_integer = #if defined(FMT_USE_FLOAT128) // Use the provided definition. -#elif FMT_CLANG_VERSION && FMT_HAS_INCLUDE() +#elif FMT_CLANG_VERSION >= 309 && FMT_HAS_INCLUDE() # define FMT_USE_FLOAT128 1 #elif FMT_GCC_VERSION && defined(_GLIBCXX_USE_FLOAT128) && \ !defined(__STRICT_ANSI__) @@ -721,11 +722,10 @@ struct float128 {}; template using is_float128 = std::is_same; -template -using is_floating_point = - bool_constant::value || is_float128::value>; +template struct is_floating_point : std::is_floating_point {}; +template <> struct is_floating_point : std::true_type {}; -template ::value> +template ::value> struct is_fast_float : bool_constant::is_iec559 && sizeof(T) <= sizeof(double)> {}; template struct is_fast_float : std::false_type {}; @@ -1613,7 +1613,7 @@ constexpr auto convert_float(T value) -> convert_float_result { } template -FMT_NOINLINE FMT_CONSTEXPR auto fill(OutputIt it, size_t n, +FMT_CONSTEXPR FMT_NOINLINE auto fill(OutputIt it, size_t n, const basic_specs& specs) -> OutputIt { auto fill_size = specs.fill_size(); if (fill_size == 1) return detail::fill_n(it, n, specs.fill_unit()); @@ -2472,8 +2472,8 @@ template struct has_isfinite> : std::true_type {}; -template ::value&& - has_isfinite::value)> +template ::value&& has_isfinite::value)> FMT_CONSTEXPR20 auto isfinite(T value) -> bool { constexpr T inf = T(std::numeric_limits::infinity()); if (is_constant_evaluated()) diff --git a/include/spdlog/fmt/bundled/ostream.h b/include/spdlog/fmt/bundled/ostream.h index 71fd6c88..7bec4efe 100644 --- a/include/spdlog/fmt/bundled/ostream.h +++ b/include/spdlog/fmt/bundled/ostream.h @@ -158,7 +158,8 @@ void print(std::ostream& os, format_string fmt, T&&... args) { FMT_EXPORT template void println(std::ostream& os, format_string fmt, T&&... args) { - fmt::print(os, "{}\n", fmt::format(fmt, std::forward(args)...)); + fmt::print(os, FMT_STRING("{}\n"), + fmt::format(fmt, std::forward(args)...)); } FMT_END_NAMESPACE diff --git a/include/spdlog/fmt/bundled/ranges.h b/include/spdlog/fmt/bundled/ranges.h index 77d645f8..23ff7de0 100644 --- a/include/spdlog/fmt/bundled/ranges.h +++ b/include/spdlog/fmt/bundled/ranges.h @@ -774,13 +774,13 @@ struct formatter< : formatter, Char> { using all = detail::all; template - auto format(const T& t, FormatContext& ctx) const -> decltype(ctx.out()) { + auto format(const T& value, FormatContext& ctx) const -> decltype(ctx.out()) { struct getter : T { - static auto get(const T& t) -> all { - return {t.*(&getter::c)}; // Access c through the derived class. + static auto get(const T& v) -> all { + return {v.*(&getter::c)}; // Access c through the derived class. } }; - return formatter::format(getter::get(t), ctx); + return formatter::format(getter::get(value), ctx); } }; diff --git a/include/spdlog/fmt/bundled/std.h b/include/spdlog/fmt/bundled/std.h index 54eb2c2a..f43dc74d 100644 --- a/include/spdlog/fmt/bundled/std.h +++ b/include/spdlog/fmt/bundled/std.h @@ -113,7 +113,6 @@ void write_escaped_path(basic_memory_buffer& quoted, } // namespace detail -FMT_EXPORT template struct formatter { private: format_specs specs_; @@ -182,7 +181,6 @@ FMT_END_NAMESPACE #endif // FMT_CPP_LIB_FILESYSTEM FMT_BEGIN_NAMESPACE -FMT_EXPORT template struct formatter, Char> : nested_formatter, Char> { @@ -209,14 +207,12 @@ struct formatter, Char> } }; -FMT_EXPORT template struct formatter : basic_ostream_formatter {}; FMT_END_NAMESPACE #ifdef __cpp_lib_optional FMT_BEGIN_NAMESPACE -FMT_EXPORT template struct formatter, Char, std::enable_if_t::value>> { @@ -279,7 +275,6 @@ FMT_END_NAMESPACE #ifdef __cpp_lib_expected FMT_BEGIN_NAMESPACE -FMT_EXPORT template struct formatter, Char, std::enable_if_t<(std::is_void::value || @@ -311,7 +306,6 @@ FMT_END_NAMESPACE #ifdef __cpp_lib_source_location FMT_BEGIN_NAMESPACE -FMT_EXPORT template <> struct formatter { FMT_CONSTEXPR auto parse(parse_context<>& ctx) { return ctx.begin(); } @@ -367,7 +361,6 @@ template struct is_variant_formattable { detail::is_variant_formattable_::value; }; -FMT_EXPORT template struct formatter { FMT_CONSTEXPR auto parse(parse_context& ctx) -> const Char* { return ctx.begin(); @@ -380,7 +373,6 @@ template struct formatter { } }; -FMT_EXPORT template struct formatter< Variant, Char, @@ -414,11 +406,11 @@ FMT_END_NAMESPACE #endif // FMT_CPP_LIB_VARIANT FMT_BEGIN_NAMESPACE -FMT_EXPORT template <> struct formatter { private: format_specs specs_; detail::arg_ref width_ref_; + bool debug_ = false; public: FMT_CONSTEXPR auto parse(parse_context<>& ctx) -> const char* { @@ -426,11 +418,19 @@ template <> struct formatter { if (it == end) return it; it = detail::parse_align(it, end, specs_); - if (it == end) return it; char c = *it; - if ((c >= '0' && c <= '9') || c == '{') + if (it != end && ((c >= '0' && c <= '9') || c == '{')) it = detail::parse_width(it, end, specs_, width_ref_, ctx); + + if (it != end && *it == '?') { + debug_ = true; + ++it; + } + if (it != end && *it == 's') { + specs_.set_type(presentation_type::string); + ++it; + } return it; } @@ -440,12 +440,21 @@ template <> struct formatter { auto specs = specs_; detail::handle_dynamic_spec(specs.dynamic_width(), specs.width, width_ref_, ctx); - memory_buffer buf; - buf.append(string_view(ec.category().name())); - buf.push_back(':'); - detail::write(appender(buf), ec.value()); - return detail::write(ctx.out(), string_view(buf.data(), buf.size()), - specs); + auto buf = memory_buffer(); + if (specs_.type() == presentation_type::string) { + buf.append(ec.message()); + } else { + buf.append(string_view(ec.category().name())); + buf.push_back(':'); + detail::write(appender(buf), ec.value()); + } + auto quoted = memory_buffer(); + auto str = string_view(buf.data(), buf.size()); + if (debug_) { + detail::write_escaped_string(std::back_inserter(quoted), str); + str = string_view(quoted.data(), quoted.size()); + } + return detail::write(ctx.out(), str, specs); } }; @@ -520,7 +529,6 @@ auto write_demangled_name(OutputIt out, const std::type_info& ti) -> OutputIt { } // namespace detail -FMT_EXPORT template struct formatter { @@ -537,7 +545,6 @@ struct formatter struct formatter< T, Char, // DEPRECATED! Mixing code unit types. @@ -603,7 +610,6 @@ struct is_bit_reference_like> { // We can't use std::vector::reference and // std::bitset::reference because the compiler can't deduce Allocator and N // in partial specialization. -FMT_EXPORT template struct formatter::value>> @@ -623,7 +629,6 @@ template auto ptr(const std::shared_ptr& p) -> const void* { return p.get(); } -FMT_EXPORT template struct formatter, Char, enable_if_t::value>> @@ -636,7 +641,6 @@ struct formatter, Char, }; #ifdef __cpp_lib_atomic_flag_test -FMT_EXPORT template struct formatter : formatter { template @@ -647,7 +651,6 @@ struct formatter : formatter { }; #endif // __cpp_lib_atomic_flag_test -FMT_EXPORT template struct formatter, Char> { private: detail::dynamic_format_specs specs_; @@ -710,7 +713,6 @@ template struct formatter, Char> { } }; -FMT_EXPORT template struct formatter, Char, enable_if_t, Char>::value>> diff --git a/include/spdlog/fmt/bundled/xchar.h b/include/spdlog/fmt/bundled/xchar.h index 9f7f889d..90994fff 100644 --- a/include/spdlog/fmt/bundled/xchar.h +++ b/include/spdlog/fmt/bundled/xchar.h @@ -112,10 +112,6 @@ inline auto runtime(wstring_view s) -> runtime_format_string { return {{s}}; } -template <> struct is_char : std::true_type {}; -template <> struct is_char : std::true_type {}; -template <> struct is_char : std::true_type {}; - #ifdef __cpp_char8_t template <> struct is_char : bool_constant {}; #endif @@ -322,7 +318,7 @@ template void println(wformat_string fmt, T&&... args) { return print(L"{}\n", fmt::format(fmt, std::forward(args)...)); } -inline auto vformat(const text_style& ts, wstring_view fmt, wformat_args args) +inline auto vformat(text_style ts, wstring_view fmt, wformat_args args) -> std::wstring { auto buf = wmemory_buffer(); detail::vformat_to(buf, ts, fmt, args); @@ -330,19 +326,19 @@ inline auto vformat(const text_style& ts, wstring_view fmt, wformat_args args) } template -inline auto format(const text_style& ts, wformat_string fmt, T&&... args) +inline auto format(text_style ts, wformat_string fmt, T&&... args) -> std::wstring { return fmt::vformat(ts, fmt, fmt::make_wformat_args(args...)); } template -FMT_DEPRECATED void print(std::FILE* f, const text_style& ts, - wformat_string fmt, const T&... args) { +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(const text_style& ts, wformat_string fmt, +FMT_DEPRECATED void print(text_style ts, wformat_string fmt, const T&... args) { return print(stdout, ts, fmt, args...); } diff --git a/tests/test_registry.cpp b/tests/test_registry.cpp index 6397f2a3..1805ae7f 100644 --- a/tests/test_registry.cpp +++ b/tests/test_registry.cpp @@ -27,12 +27,13 @@ TEST_CASE("explicit register", "[registry]") { TEST_CASE("register_or_replace", "[registry]") { spdlog::drop_all(); - auto logger1 = std::make_shared(tested_logger_name, - std::make_shared()); + auto logger1 = std::make_shared( + tested_logger_name, std::make_shared()); spdlog::register_logger(logger1); REQUIRE(spdlog::get(tested_logger_name) == logger1); - auto logger2 = std::make_shared(tested_logger_name, std::make_shared()); + auto logger2 = std::make_shared( + tested_logger_name, std::make_shared()); spdlog::register_or_replace(logger2); REQUIRE(spdlog::get(tested_logger_name) == logger2); }