mirror of
https://github.com/fmtlib/fmt.git
synced 2025-12-24 15:58:16 +01:00
Compare commits
34 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
123913715a | ||
|
|
8c1059b92e | ||
|
|
4e5aafbf43 | ||
|
|
db30fb3b81 | ||
|
|
3401ce2be2 | ||
|
|
7f7695524a | ||
|
|
251320fcb7 | ||
|
|
94ab51cb8c | ||
|
|
0ca42e836e | ||
|
|
ed27df5760 | ||
|
|
d42a068dbd | ||
|
|
f2cec917da | ||
|
|
d5b866e242 | ||
|
|
5676e408f5 | ||
|
|
71d24b564d | ||
|
|
c9267da4df | ||
|
|
373855c1b0 | ||
|
|
52eeeb52a6 | ||
|
|
9cf9f38ede | ||
|
|
4946bdb729 | ||
|
|
01a5b56f0d | ||
|
|
cb6fdf2191 | ||
|
|
f841ae61e2 | ||
|
|
a3d05d70ce | ||
|
|
41539c29f3 | ||
|
|
aabe63910c | ||
|
|
f90090be2c | ||
|
|
9ff9c695db | ||
|
|
06ad1224eb | ||
|
|
5f0572acdc | ||
|
|
898d438571 | ||
|
|
937b7c5c10 | ||
|
|
01914f0389 | ||
|
|
c43da35701 |
2
.github/workflows/cifuzz.yml
vendored
2
.github/workflows/cifuzz.yml
vendored
@@ -25,7 +25,7 @@ jobs:
|
||||
language: c++
|
||||
|
||||
- name: Upload crash
|
||||
uses: actions/upload-artifact@50769540e7f4bd5e21e526ee35c689e35e0d6874 # v4.4.0
|
||||
uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0
|
||||
if: failure() && steps.build.outcome == 'success'
|
||||
with:
|
||||
name: artifacts
|
||||
|
||||
4
.github/workflows/scorecard.yml
vendored
4
.github/workflows/scorecard.yml
vendored
@@ -52,7 +52,7 @@ jobs:
|
||||
# Upload the results as artifacts (optional). Commenting out will disable uploads of run results in SARIF
|
||||
# format to the repository Actions tab.
|
||||
- name: "Upload artifact"
|
||||
uses: actions/upload-artifact@50769540e7f4bd5e21e526ee35c689e35e0d6874 # v4.4.0
|
||||
uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0
|
||||
with:
|
||||
name: SARIF file
|
||||
path: results.sarif
|
||||
@@ -60,6 +60,6 @@ jobs:
|
||||
|
||||
# Upload the results to GitHub's code scanning dashboard.
|
||||
- name: "Upload to code-scanning"
|
||||
uses: github/codeql-action/upload-sarif@662472033e021d55d94146f66f6058822b0b39fd # v3.27.0
|
||||
uses: github/codeql-action/upload-sarif@dd746615b3b9d728a6a37ca2045b68ca76d4841a # v3.28.8
|
||||
with:
|
||||
sarif_file: results.sarif
|
||||
|
||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -3,6 +3,7 @@
|
||||
*.xcodeproj
|
||||
*~
|
||||
.vscode/
|
||||
.vs/
|
||||
/CMakeScripts
|
||||
/Testing
|
||||
/_CPack_Packages
|
||||
|
||||
@@ -27,7 +27,13 @@ endfunction()
|
||||
# DEPRECATED! Should be merged into add_module_library.
|
||||
function(enable_module target)
|
||||
if (MSVC)
|
||||
set(BMI ${CMAKE_CURRENT_BINARY_DIR}/${target}.ifc)
|
||||
if(CMAKE_GENERATOR STREQUAL "Ninja")
|
||||
# Ninja dyndep expects the .ifc output to be located in a specific relative path
|
||||
file(RELATIVE_PATH BMI_DIR "${CMAKE_BINARY_DIR}" "${CMAKE_CURRENT_BINARY_DIR}/CMakeFiles/${target}.dir")
|
||||
else()
|
||||
set(BMI_DIR "${CMAKE_CURRENT_BINARY_DIR}")
|
||||
endif()
|
||||
file(TO_NATIVE_PATH "${BMI_DIR}/${target}.ifc" BMI)
|
||||
target_compile_options(${target}
|
||||
PRIVATE /interface /ifcOutput ${BMI}
|
||||
INTERFACE /reference fmt=${BMI})
|
||||
@@ -69,8 +75,6 @@ function(add_module_library name)
|
||||
target_compile_options(${name} PUBLIC -fmodules-ts)
|
||||
endif ()
|
||||
|
||||
target_compile_definitions(${name} PRIVATE FMT_MODULE)
|
||||
|
||||
if (FMT_USE_CMAKE_MODULES)
|
||||
target_sources(${name} PUBLIC FILE_SET fmt TYPE CXX_MODULES
|
||||
FILES ${sources})
|
||||
@@ -295,6 +299,7 @@ function(add_headers VAR)
|
||||
endfunction()
|
||||
|
||||
# Define the fmt library, its includes and the needed defines.
|
||||
set(FMT_HEADERS)
|
||||
add_headers(FMT_HEADERS args.h base.h chrono.h color.h compile.h core.h format.h
|
||||
format-inl.h os.h ostream.h printf.h ranges.h std.h
|
||||
xchar.h)
|
||||
|
||||
59
ChangeLog.md
59
ChangeLog.md
@@ -1,3 +1,62 @@
|
||||
# 11.1.4 - 2025-02-26
|
||||
|
||||
- Fixed ABI compatibility with earlier 11.x versions on Windows
|
||||
(https://github.com/fmtlib/fmt/issues/4359).
|
||||
|
||||
- Improved the logic of switching between fixed and exponential format for
|
||||
`float` (https://github.com/fmtlib/fmt/issues/3649).
|
||||
|
||||
- Moved `is_compiled_string` to the public API
|
||||
(https://github.com/fmtlib/fmt/issues/4342). Thanks @SwooshyCueb.
|
||||
|
||||
- Simplified implementation of `operator""_cf`
|
||||
(https://github.com/fmtlib/fmt/pull/4349). Thanks @LocalSpook.
|
||||
|
||||
- Fixed `__builtin_strlen` detection (https://github.com/fmtlib/fmt/pull/4329).
|
||||
Thanks @LocalSpook.
|
||||
|
||||
- Fixed handling of BMI paths with the Ninja generator
|
||||
(https://github.com/fmtlib/fmt/pull/4344). Thanks @tkhyn.
|
||||
|
||||
- Fixed gcc 8.3 compile errors (https://github.com/fmtlib/fmt/issues/4331,
|
||||
https://github.com/fmtlib/fmt/pull/4336). Thanks @sergiud.
|
||||
|
||||
- Fixed a bogus MSVC warning (https://github.com/fmtlib/fmt/pull/4356).
|
||||
Thanks @dinomight.
|
||||
|
||||
# 11.1.3 - 2025-01-25
|
||||
|
||||
- Fixed compilation on GCC 9.4 (https://github.com/fmtlib/fmt/issues/4313).
|
||||
|
||||
- Worked around an internal compiler error when using C++20 modules with GCC
|
||||
14.2 and earlier (https://github.com/fmtlib/fmt/issues/4295).
|
||||
|
||||
- Worked around a bug in GCC 6 (https://github.com/fmtlib/fmt/issues/4318).
|
||||
|
||||
- Fixed an issue caused by instantiating `formatter<const T>`
|
||||
(https://github.com/fmtlib/fmt/issues/4303,
|
||||
https://github.com/fmtlib/fmt/pull/4325). Thanks @timsong-cpp.
|
||||
|
||||
- Fixed formatting into `std::ostreambuf_iterator` when using format string
|
||||
compilation (https://github.com/fmtlib/fmt/issues/4309,
|
||||
https://github.com/fmtlib/fmt/pull/4312). Thanks @phprus.
|
||||
|
||||
- Restored a constraint on the map formatter so that it correctly reports as
|
||||
unformattable when the element is (https://github.com/fmtlib/fmt/pull/4326).
|
||||
Thanks @timsong-cpp.
|
||||
|
||||
- Reduced the size of format specs (https://github.com/fmtlib/fmt/issues/4298).
|
||||
|
||||
- Readded `args()` to `fmt::format_context`
|
||||
(https://github.com/fmtlib/fmt/issues/4307,
|
||||
https://github.com/fmtlib/fmt/pull/4310). Thanks @Erroneous1.
|
||||
|
||||
- Fixed a bogus MSVC warning (https://github.com/fmtlib/fmt/issues/4314,
|
||||
https://github.com/fmtlib/fmt/pull/4322). Thanks @ZehMatt.
|
||||
|
||||
- Fixed a pedantic mode error in the CMake config
|
||||
(https://github.com/fmtlib/fmt/pull/4327). Thanks @rlalik.
|
||||
|
||||
# 11.1.2 - 2025-01-12
|
||||
|
||||
- Fixed ABI compatibility with earlier 11.x versions
|
||||
|
||||
@@ -669,5 +669,13 @@ following differences:
|
||||
|
||||
- Names are defined in the `fmt` namespace instead of `std` to avoid
|
||||
collisions with standard library implementations.
|
||||
|
||||
- Width calculation doesn't use grapheme clusterization. The latter has
|
||||
been implemented in a separate branch but hasn't been integrated yet.
|
||||
|
||||
- The default floating-point representation in {fmt} uses the smallest
|
||||
precision that provides round-trip guarantees similarly to other languages
|
||||
like Java and Python. `std::format` is currently specified in terms of
|
||||
`std::to_chars` which tries to generate the smallest number of characters
|
||||
(ignoring redundant digits and sign in exponent) and may procude more
|
||||
decimal digits than necessary.
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
#endif
|
||||
|
||||
// The fmt library version in the form major * 10000 + minor * 100 + patch.
|
||||
#define FMT_VERSION 110102
|
||||
#define FMT_VERSION 110104
|
||||
|
||||
// Detect compiler versions.
|
||||
#if defined(__clang__) && !defined(__ibmxl__)
|
||||
@@ -96,9 +96,9 @@
|
||||
// Detect C++14 relaxed constexpr.
|
||||
#ifdef FMT_USE_CONSTEXPR
|
||||
// Use the provided definition.
|
||||
#elif FMT_GCC_VERSION >= 600 && FMT_CPLUSPLUS >= 201402L
|
||||
// GCC only allows throw in constexpr since version 6:
|
||||
// https://gcc.gnu.org/bugzilla/show_bug.cgi?id=67371.
|
||||
#elif FMT_GCC_VERSION >= 702 && FMT_CPLUSPLUS >= 201402L
|
||||
// GCC only allows constexpr member functions in non-literal types since 7.2:
|
||||
// https://gcc.gnu.org/bugzilla/show_bug.cgi?id=66297.
|
||||
# define FMT_USE_CONSTEXPR 1
|
||||
#elif FMT_ICC_VERSION
|
||||
# define FMT_USE_CONSTEXPR 0 // https://github.com/fmtlib/fmt/issues/1628
|
||||
@@ -294,12 +294,12 @@
|
||||
#endif
|
||||
|
||||
#define FMT_APPLY_VARIADIC(expr) \
|
||||
using ignore = int[]; \
|
||||
(void)ignore { 0, (expr, 0)... }
|
||||
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__)
|
||||
#if !defined(__OPTIMIZE__) && !defined(__CUDACC__) && !defined(FMT_MODULE)
|
||||
FMT_PRAGMA_GCC(optimize("Og"))
|
||||
#endif
|
||||
FMT_PRAGMA_CLANG(diagnostic push)
|
||||
@@ -537,7 +537,7 @@ template <typename Char> class basic_string_view {
|
||||
FMT_ALWAYS_INLINE
|
||||
#endif
|
||||
FMT_CONSTEXPR20 basic_string_view(const Char* s) : data_(s) {
|
||||
#if FMT_HAS_BUILTIN(__buitin_strlen) || FMT_GCC_VERSION || FMT_CLANG_VERSION
|
||||
#if FMT_HAS_BUILTIN(__builtin_strlen) || FMT_GCC_VERSION || FMT_CLANG_VERSION
|
||||
if (std::is_same<Char, char>::value) {
|
||||
size_ = __builtin_strlen(detail::narrow(s));
|
||||
return;
|
||||
@@ -739,13 +739,15 @@ class basic_specs {
|
||||
max_fill_size = 4
|
||||
};
|
||||
|
||||
size_t data_ = 1 << fill_size_shift;
|
||||
unsigned data_ = 1 << fill_size_shift;
|
||||
static_assert(sizeof(basic_specs::data_) * CHAR_BIT >= 18, "");
|
||||
|
||||
// Character (code unit) type is erased to prevent template bloat.
|
||||
char fill_data_[max_fill_size] = {' '};
|
||||
|
||||
FMT_CONSTEXPR void set_fill_size(size_t size) {
|
||||
data_ = (data_ & ~fill_size_mask) | (size << fill_size_shift);
|
||||
data_ = (data_ & ~fill_size_mask) |
|
||||
(static_cast<unsigned>(size) << fill_size_shift);
|
||||
}
|
||||
|
||||
public:
|
||||
@@ -1119,7 +1121,7 @@ using use_formatter =
|
||||
bool_constant<(std::is_class<T>::value || std::is_enum<T>::value ||
|
||||
std::is_union<T>::value || std::is_array<T>::value) &&
|
||||
!has_to_string_view<T>::value && !is_named_arg<T>::value &&
|
||||
!use_format_as<T>::value && !use_format_as_member<T>::value>;
|
||||
!use_format_as<T>::value && !use_format_as_member<U>::value>;
|
||||
|
||||
template <typename Char, typename T, typename U = remove_const_t<T>>
|
||||
auto has_formatter_impl(T* p, buffered_context<Char>* ctx = nullptr)
|
||||
@@ -2261,15 +2263,15 @@ template <> struct is_output_iterator<appender, char> : std::true_type {};
|
||||
template <typename It, typename T>
|
||||
struct is_output_iterator<
|
||||
It, T,
|
||||
void_t<decltype(*std::declval<decay_t<It>&>()++ = std::declval<T>())>>
|
||||
: std::true_type {};
|
||||
enable_if_t<std::is_assignable<decltype(*std::declval<decay_t<It>&>()++),
|
||||
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 <locale> include.
|
||||
struct locale_ref {
|
||||
class locale_ref {
|
||||
#if FMT_USE_LOCALE
|
||||
private:
|
||||
const void* locale_; // A type-erased pointer to std::locale.
|
||||
@@ -2281,6 +2283,7 @@ struct locale_ref {
|
||||
inline explicit operator bool() const noexcept { return locale_ != nullptr; }
|
||||
#endif // FMT_USE_LOCALE
|
||||
|
||||
public:
|
||||
template <typename Locale> auto get() const -> Locale;
|
||||
};
|
||||
|
||||
@@ -2654,6 +2657,7 @@ class context {
|
||||
FMT_CONSTEXPR auto arg_id(string_view name) const -> int {
|
||||
return args_.get_id(name);
|
||||
}
|
||||
auto args() const -> const format_args& { return args_; }
|
||||
|
||||
// Returns an iterator to the beginning of the output range.
|
||||
FMT_CONSTEXPR auto out() const -> iterator { return out_; }
|
||||
@@ -2726,9 +2730,9 @@ template <typename... T> struct fstring {
|
||||
std::is_same<typename S::char_type, char>::value)>
|
||||
FMT_ALWAYS_INLINE fstring(const S&) : str(S()) {
|
||||
FMT_CONSTEXPR auto sv = string_view(S());
|
||||
FMT_CONSTEXPR int ignore =
|
||||
FMT_CONSTEXPR int unused =
|
||||
(parse_format_string(sv, checker(sv, arg_pack())), 0);
|
||||
detail::ignore_unused(ignore);
|
||||
detail::ignore_unused(unused);
|
||||
}
|
||||
fstring(runtime_format_string<> fmt) : str(fmt.str) {}
|
||||
|
||||
|
||||
@@ -19,11 +19,11 @@ FMT_BEGIN_NAMESPACE
|
||||
// A compile-time string which is compiled into fast formatting code.
|
||||
FMT_EXPORT class compiled_string {};
|
||||
|
||||
namespace detail {
|
||||
|
||||
template <typename S>
|
||||
struct is_compiled_string : std::is_base_of<compiled_string, S> {};
|
||||
|
||||
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,16 +41,6 @@ struct is_compiled_string : std::is_base_of<compiled_string, S> {};
|
||||
# define FMT_COMPILE(s) FMT_STRING(s)
|
||||
#endif
|
||||
|
||||
#if FMT_USE_NONTYPE_TEMPLATE_ARGS
|
||||
template <typename Char, size_t N, fmt::detail::fixed_string<Char, N> Str>
|
||||
struct udl_compiled_string : compiled_string {
|
||||
using char_type = Char;
|
||||
constexpr explicit operator basic_string_view<char_type>() const {
|
||||
return {Str.data, N - 1};
|
||||
}
|
||||
};
|
||||
#endif
|
||||
|
||||
template <typename T, typename... Tail>
|
||||
auto first(const T& value, const Tail&...) -> const T& {
|
||||
return value;
|
||||
@@ -425,7 +415,7 @@ constexpr auto compile_format_string(S fmt) {
|
||||
}
|
||||
|
||||
template <typename... Args, typename S,
|
||||
FMT_ENABLE_IF(detail::is_compiled_string<S>::value)>
|
||||
FMT_ENABLE_IF(is_compiled_string<S>::value)>
|
||||
constexpr auto compile(S fmt) {
|
||||
constexpr auto str = basic_string_view<typename S::char_type>(fmt);
|
||||
if constexpr (str.size() == 0) {
|
||||
@@ -461,7 +451,7 @@ constexpr FMT_INLINE OutputIt format_to(OutputIt out, const CompiledFormat& cf,
|
||||
}
|
||||
|
||||
template <typename S, typename... Args,
|
||||
FMT_ENABLE_IF(detail::is_compiled_string<S>::value)>
|
||||
FMT_ENABLE_IF(is_compiled_string<S>::value)>
|
||||
FMT_INLINE std::basic_string<typename S::char_type> format(const S&,
|
||||
Args&&... args) {
|
||||
if constexpr (std::is_same<typename S::char_type, char>::value) {
|
||||
@@ -488,7 +478,7 @@ FMT_INLINE std::basic_string<typename S::char_type> format(const S&,
|
||||
}
|
||||
|
||||
template <typename OutputIt, typename S, typename... Args,
|
||||
FMT_ENABLE_IF(detail::is_compiled_string<S>::value)>
|
||||
FMT_ENABLE_IF(is_compiled_string<S>::value)>
|
||||
FMT_CONSTEXPR OutputIt format_to(OutputIt out, const S&, Args&&... args) {
|
||||
constexpr auto compiled = detail::compile<Args...>(S());
|
||||
if constexpr (std::is_same<remove_cvref_t<decltype(compiled)>,
|
||||
@@ -503,7 +493,7 @@ FMT_CONSTEXPR OutputIt format_to(OutputIt out, const S&, Args&&... args) {
|
||||
#endif
|
||||
|
||||
template <typename OutputIt, typename S, typename... Args,
|
||||
FMT_ENABLE_IF(detail::is_compiled_string<S>::value)>
|
||||
FMT_ENABLE_IF(is_compiled_string<S>::value)>
|
||||
auto format_to_n(OutputIt out, size_t n, const S& fmt, Args&&... args)
|
||||
-> format_to_n_result<OutputIt> {
|
||||
using traits = detail::fixed_buffer_traits;
|
||||
@@ -513,7 +503,7 @@ auto format_to_n(OutputIt out, size_t n, const S& fmt, Args&&... args)
|
||||
}
|
||||
|
||||
template <typename S, typename... Args,
|
||||
FMT_ENABLE_IF(detail::is_compiled_string<S>::value)>
|
||||
FMT_ENABLE_IF(is_compiled_string<S>::value)>
|
||||
FMT_CONSTEXPR20 auto formatted_size(const S& fmt, const Args&... args)
|
||||
-> size_t {
|
||||
auto buf = detail::counting_buffer<>();
|
||||
@@ -522,7 +512,7 @@ FMT_CONSTEXPR20 auto formatted_size(const S& fmt, const Args&... args)
|
||||
}
|
||||
|
||||
template <typename S, typename... Args,
|
||||
FMT_ENABLE_IF(detail::is_compiled_string<S>::value)>
|
||||
FMT_ENABLE_IF(is_compiled_string<S>::value)>
|
||||
void print(std::FILE* f, const S& fmt, const Args&... args) {
|
||||
auto buf = memory_buffer();
|
||||
fmt::format_to(appender(buf), fmt, args...);
|
||||
@@ -530,7 +520,7 @@ void print(std::FILE* f, const S& fmt, const Args&... args) {
|
||||
}
|
||||
|
||||
template <typename S, typename... Args,
|
||||
FMT_ENABLE_IF(detail::is_compiled_string<S>::value)>
|
||||
FMT_ENABLE_IF(is_compiled_string<S>::value)>
|
||||
void print(const S& fmt, const Args&... args) {
|
||||
print(stdout, fmt, args...);
|
||||
}
|
||||
@@ -538,9 +528,7 @@ void print(const S& fmt, const Args&... args) {
|
||||
#if FMT_USE_NONTYPE_TEMPLATE_ARGS
|
||||
inline namespace literals {
|
||||
template <detail::fixed_string Str> constexpr auto operator""_cf() {
|
||||
using char_t = remove_cvref_t<decltype(Str.data[0])>;
|
||||
return detail::udl_compiled_string<char_t, sizeof(Str.data) / sizeof(char_t),
|
||||
Str>();
|
||||
return FMT_COMPILE(Str.data);
|
||||
}
|
||||
} // namespace literals
|
||||
#endif
|
||||
|
||||
@@ -227,7 +227,9 @@ FMT_CONSTEXPR inline void abort_fuzzing_if(bool condition) {
|
||||
#if defined(FMT_USE_STRING_VIEW)
|
||||
template <typename Char> using std_string_view = std::basic_string_view<Char>;
|
||||
#else
|
||||
template <typename T> struct std_string_view {};
|
||||
template <typename Char> struct std_string_view {
|
||||
operator basic_string_view<Char>() const;
|
||||
};
|
||||
#endif
|
||||
|
||||
template <typename Char, Char... C> struct string_literal {
|
||||
@@ -1203,7 +1205,7 @@ FMT_CONSTEXPR FMT_INLINE auto format_decimal(Char* out, UInt value,
|
||||
}
|
||||
|
||||
template <typename Char, typename UInt, typename OutputIt,
|
||||
FMT_ENABLE_IF(is_back_insert_iterator<OutputIt>::value)>
|
||||
FMT_ENABLE_IF(!std::is_pointer<remove_cvref_t<OutputIt>>::value)>
|
||||
FMT_CONSTEXPR auto format_decimal(OutputIt out, UInt value, int num_digits)
|
||||
-> OutputIt {
|
||||
if (auto ptr = to_pointer<Char>(out, to_unsigned(num_digits))) {
|
||||
@@ -2330,7 +2332,7 @@ template <typename Char, typename OutputIt, typename DecimalFP,
|
||||
typename Grouping = digit_grouping<Char>>
|
||||
FMT_CONSTEXPR20 auto do_write_float(OutputIt out, const DecimalFP& f,
|
||||
const format_specs& specs, sign s,
|
||||
locale_ref loc) -> OutputIt {
|
||||
int exp_upper, locale_ref loc) -> OutputIt {
|
||||
auto significand = f.significand;
|
||||
int significand_size = get_significand_size(f);
|
||||
const Char zero = static_cast<Char>('0');
|
||||
@@ -2346,7 +2348,7 @@ FMT_CONSTEXPR20 auto do_write_float(OutputIt out, const DecimalFP& f,
|
||||
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, exp_upper = 16;
|
||||
const int exp_lower = -4;
|
||||
return output_exp < exp_lower ||
|
||||
output_exp >= (specs.precision > 0 ? specs.precision : exp_upper);
|
||||
};
|
||||
@@ -2449,12 +2451,13 @@ template <typename Char> class fallback_digit_grouping {
|
||||
template <typename Char, typename OutputIt, typename DecimalFP>
|
||||
FMT_CONSTEXPR20 auto write_float(OutputIt out, const DecimalFP& f,
|
||||
const format_specs& specs, sign s,
|
||||
locale_ref loc) -> OutputIt {
|
||||
int exp_upper, locale_ref loc) -> OutputIt {
|
||||
if (is_constant_evaluated()) {
|
||||
return do_write_float<Char, OutputIt, DecimalFP,
|
||||
fallback_digit_grouping<Char>>(out, f, specs, s, loc);
|
||||
fallback_digit_grouping<Char>>(out, f, specs, s,
|
||||
exp_upper, loc);
|
||||
} else {
|
||||
return do_write_float<Char>(out, f, specs, s, loc);
|
||||
return do_write_float<Char>(out, f, specs, s, exp_upper, loc);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3286,6 +3289,14 @@ 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 <typename T> constexpr auto exp_upper() -> int {
|
||||
return std::numeric_limits<T>::digits10 != 0
|
||||
? min_of(16, std::numeric_limits<T>::digits10 + 1)
|
||||
: 16;
|
||||
}
|
||||
|
||||
template <typename Char, typename OutputIt, typename T>
|
||||
FMT_CONSTEXPR20 auto write_float(OutputIt out, T value, format_specs specs,
|
||||
locale_ref loc) -> OutputIt {
|
||||
@@ -3301,6 +3312,7 @@ 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<T>();
|
||||
int precision = specs.precision;
|
||||
if (precision < 0) {
|
||||
if (specs.type() != presentation_type::none) {
|
||||
@@ -3309,7 +3321,7 @@ FMT_CONSTEXPR20 auto write_float(OutputIt out, T value, format_specs specs,
|
||||
// Use Dragonbox for the shortest format.
|
||||
using floaty = conditional_t<sizeof(T) >= sizeof(double), double, float>;
|
||||
auto dec = dragonbox::to_decimal(static_cast<floaty>(value));
|
||||
return write_float<Char>(out, dec, specs, s, loc);
|
||||
return write_float<Char>(out, dec, specs, s, exp_upper, loc);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3337,7 +3349,7 @@ FMT_CONSTEXPR20 auto write_float(OutputIt out, T value, format_specs specs,
|
||||
|
||||
specs.precision = precision;
|
||||
auto f = big_decimal_fp{buffer.data(), static_cast<int>(buffer.size()), exp};
|
||||
return write_float<Char>(out, f, specs, s, loc);
|
||||
return write_float<Char>(out, f, specs, s, exp_upper, loc);
|
||||
}
|
||||
|
||||
template <typename Char, typename OutputIt, typename T,
|
||||
@@ -3364,7 +3376,7 @@ FMT_CONSTEXPR20 auto write(OutputIt out, T value) -> OutputIt {
|
||||
return write_nonfinite<Char>(out, std::isnan(value), specs, s);
|
||||
|
||||
auto dec = dragonbox::to_decimal(static_cast<floaty>(value));
|
||||
return write_float<Char>(out, dec, specs, s, {});
|
||||
return write_float<Char>(out, dec, specs, s, exp_upper<T>(), {});
|
||||
}
|
||||
|
||||
template <typename Char, typename OutputIt, typename T,
|
||||
|
||||
@@ -150,7 +150,7 @@ inline void vprint(std::ostream& os, string_view fmt, format_args args) {
|
||||
FMT_EXPORT template <typename... T>
|
||||
void print(std::ostream& os, format_string<T...> fmt, T&&... args) {
|
||||
fmt::vargs<T...> vargs = {{args...}};
|
||||
if (detail::use_utf8) return vprint(os, fmt.str, vargs);
|
||||
if (detail::const_check(detail::use_utf8)) return vprint(os, fmt.str, vargs);
|
||||
auto buffer = memory_buffer();
|
||||
detail::vformat_to(buffer, fmt.str, vargs);
|
||||
detail::write_buffer(os, buffer);
|
||||
|
||||
@@ -527,7 +527,9 @@ struct formatter<
|
||||
template <typename R, typename Char>
|
||||
struct formatter<
|
||||
R, Char,
|
||||
enable_if_t<range_format_kind<R, Char>::value == range_format::map>> {
|
||||
enable_if_t<conjunction<
|
||||
bool_constant<range_format_kind<R, Char>::value == range_format::map>,
|
||||
detail::is_formattable_delayed<R, Char>>::value>> {
|
||||
private:
|
||||
using map_type = detail::maybe_const_range<R>;
|
||||
using element_type = detail::uncvref_type<map_type>;
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
module;
|
||||
|
||||
#define FMT_MODULE
|
||||
|
||||
#ifdef _MSVC_LANG
|
||||
# define FMT_CPLUSPLUS _MSVC_LANG
|
||||
#else
|
||||
|
||||
@@ -875,3 +875,12 @@ TEST(base_test, no_repeated_format_string_conversions) {
|
||||
fmt::format_to(buf, nondeterministic_format_string());
|
||||
#endif
|
||||
}
|
||||
|
||||
TEST(base_test, format_context_accessors) {
|
||||
class copier {
|
||||
static fmt::format_context copy(fmt::appender app,
|
||||
const fmt::format_context& ctx) {
|
||||
return fmt::format_context(std::move(app), ctx.args(), ctx.locale());
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -7,6 +7,8 @@
|
||||
|
||||
#include "fmt/compile.h"
|
||||
|
||||
#include <iterator>
|
||||
#include <list>
|
||||
#include <type_traits>
|
||||
#include <vector>
|
||||
|
||||
@@ -199,6 +201,21 @@ TEST(compile_test, format_to_n) {
|
||||
EXPECT_STREQ("2a", buffer);
|
||||
}
|
||||
|
||||
TEST(compile_test, output_iterators) {
|
||||
std::list<char> out;
|
||||
fmt::format_to(std::back_inserter(out), FMT_COMPILE("{}"), 42);
|
||||
EXPECT_EQ("42", std::string(out.begin(), out.end()));
|
||||
|
||||
std::stringstream s;
|
||||
fmt::format_to(std::ostream_iterator<char>(s), FMT_COMPILE("{}"), 42);
|
||||
EXPECT_EQ("42", s.str());
|
||||
|
||||
std::stringstream s2;
|
||||
fmt::format_to(std::ostreambuf_iterator<char>(s2), FMT_COMPILE("{}.{:06d}"),
|
||||
42, 43);
|
||||
EXPECT_EQ("42.000043", s2.str());
|
||||
}
|
||||
|
||||
# if FMT_USE_CONSTEVAL && (!FMT_MSC_VERSION || FMT_MSC_VERSION >= 1940)
|
||||
TEST(compile_test, constexpr_formatted_size) {
|
||||
FMT_CONSTEXPR20 size_t size = fmt::formatted_size(FMT_COMPILE("{}"), 42);
|
||||
@@ -299,6 +316,17 @@ TEST(compile_test, compile_format_string_literal) {
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(__cpp_if_constexpr) && defined(__cpp_return_type_deduction)
|
||||
template <typename S> auto check_is_compiled_string(const S&) -> bool {
|
||||
return fmt::is_compiled_string<S>::value;
|
||||
}
|
||||
|
||||
TEST(compile_test, is_compiled_string) {
|
||||
EXPECT_TRUE(check_is_compiled_string(FMT_COMPILE("asdf")));
|
||||
EXPECT_TRUE(check_is_compiled_string(FMT_COMPILE("{}")));
|
||||
}
|
||||
#endif
|
||||
|
||||
// MSVS 2019 19.29.30145.0 - OK
|
||||
// MSVS 2022 19.32.31332.0, 19.37.32826.1 - compile-test.cc(362,3): fatal error
|
||||
// C1001: Internal compiler error.
|
||||
@@ -310,7 +338,7 @@ TEST(compile_test, compile_format_string_literal) {
|
||||
(FMT_MSC_VERSION >= 1928 && FMT_MSC_VERSION < 1930)) && \
|
||||
defined(__cpp_lib_is_constant_evaluated)
|
||||
template <size_t max_string_length, typename Char = char> struct test_string {
|
||||
template <typename T> constexpr bool operator==(const T& rhs) const noexcept {
|
||||
template <typename T> constexpr auto operator==(const T& rhs) const -> bool {
|
||||
return fmt::basic_string_view<Char>(rhs).compare(buffer) == 0;
|
||||
}
|
||||
Char buffer[max_string_length]{};
|
||||
|
||||
@@ -312,6 +312,7 @@ template <> struct numeric_limits<double_double> {
|
||||
// is_iec559 is true for double-double in libstdc++.
|
||||
static constexpr bool is_iec559 = true;
|
||||
static constexpr int digits = 106;
|
||||
static constexpr int digits10 = 33;
|
||||
};
|
||||
|
||||
template <> struct is_floating_point<slow_float> : std::true_type {};
|
||||
@@ -341,7 +342,7 @@ TEST(format_impl_test, write_dragon_even) {
|
||||
auto s = std::string();
|
||||
fmt::detail::write<char>(std::back_inserter(s), slow_float(33554450.0f), {});
|
||||
// Specializing is_floating_point is broken in MSVC.
|
||||
if (!FMT_MSC_VERSION) EXPECT_EQ(s, "33554450");
|
||||
if (!FMT_MSC_VERSION) EXPECT_EQ(s, "3.355445e+07");
|
||||
}
|
||||
|
||||
#if defined(_WIN32) && !defined(FMT_USE_WRITE_CONSOLE)
|
||||
|
||||
@@ -1068,7 +1068,8 @@ TEST(format_test, precision) {
|
||||
EXPECT_EQ(fmt::format("{:#.0f}", 123.0), "123.");
|
||||
EXPECT_EQ(fmt::format("{:.02f}", 1.234), "1.23");
|
||||
EXPECT_EQ(fmt::format("{:.1g}", 0.001), "0.001");
|
||||
EXPECT_EQ(fmt::format("{}", 1019666432.0f), "1019666400");
|
||||
EXPECT_EQ(fmt::format("{}", 123456789.0f), "1.2345679e+08");
|
||||
EXPECT_EQ(fmt::format("{}", 1019666432.0f), "1.0196664e+09");
|
||||
EXPECT_EQ(fmt::format("{:.0e}", 9.5), "1e+01");
|
||||
EXPECT_EQ(fmt::format("{:.1e}", 1e-34), "1.0e-34");
|
||||
|
||||
@@ -2102,6 +2103,10 @@ TEST(format_test, output_iterators) {
|
||||
std::stringstream s;
|
||||
fmt::format_to(std::ostream_iterator<char>(s), "{}", 42);
|
||||
EXPECT_EQ("42", s.str());
|
||||
|
||||
std::stringstream s2;
|
||||
fmt::format_to(std::ostreambuf_iterator<char>(s2), "{}.{:06d}", 42, 43);
|
||||
EXPECT_EQ("42.000043", s2.str());
|
||||
}
|
||||
|
||||
TEST(format_test, fill_via_appender) {
|
||||
|
||||
@@ -47,6 +47,8 @@ TEST(ranges_test, format_array_of_literals) {
|
||||
}
|
||||
#endif // FMT_RANGES_TEST_ENABLE_C_STYLE_ARRAY
|
||||
|
||||
struct unformattable {};
|
||||
|
||||
TEST(ranges_test, format_vector) {
|
||||
auto v = std::vector<int>{1, 2, 3, 5, 7, 11};
|
||||
EXPECT_EQ(fmt::format("{}", v), "[1, 2, 3, 5, 7, 11]");
|
||||
@@ -65,6 +67,9 @@ TEST(ranges_test, format_vector) {
|
||||
EXPECT_EQ(fmt::format("{:n}", vvc), "['a', 'b', 'c'], ['a', 'b', 'c']");
|
||||
EXPECT_EQ(fmt::format("{:n:n}", vvc), "'a', 'b', 'c', 'a', 'b', 'c'");
|
||||
EXPECT_EQ(fmt::format("{:n:n:}", vvc), "a, b, c, a, b, c");
|
||||
|
||||
EXPECT_FALSE(fmt::is_formattable<unformattable>::value);
|
||||
EXPECT_FALSE(fmt::is_formattable<std::vector<unformattable>>::value);
|
||||
}
|
||||
|
||||
TEST(ranges_test, format_nested_vector) {
|
||||
@@ -83,6 +88,8 @@ TEST(ranges_test, format_map) {
|
||||
auto m = std::map<std::string, int>{{"one", 1}, {"two", 2}};
|
||||
EXPECT_EQ(fmt::format("{}", m), "{\"one\": 1, \"two\": 2}");
|
||||
EXPECT_EQ(fmt::format("{:n}", m), "\"one\": 1, \"two\": 2");
|
||||
|
||||
EXPECT_FALSE((fmt::is_formattable<std::map<int, unformattable>>::value));
|
||||
}
|
||||
|
||||
struct test_map_value {};
|
||||
@@ -146,6 +153,7 @@ template <typename T> class flat_set {
|
||||
TEST(ranges_test, format_flat_set) {
|
||||
EXPECT_EQ(fmt::format("{}", flat_set<std::string>{"one", "two"}),
|
||||
"{\"one\", \"two\"}");
|
||||
EXPECT_FALSE(fmt::is_formattable<flat_set<unformattable>>::value);
|
||||
}
|
||||
|
||||
namespace adl {
|
||||
@@ -169,8 +177,6 @@ TEST(ranges_test, format_pair) {
|
||||
EXPECT_EQ(fmt::format("{:n}", p), "421.5");
|
||||
}
|
||||
|
||||
struct unformattable {};
|
||||
|
||||
TEST(ranges_test, format_tuple) {
|
||||
auto t =
|
||||
std::tuple<int, float, std::string, char>(42, 1.5f, "this is tuple", 'i');
|
||||
@@ -180,7 +186,6 @@ TEST(ranges_test, format_tuple) {
|
||||
EXPECT_EQ(fmt::format("{}", std::tuple<>()), "()");
|
||||
|
||||
EXPECT_TRUE((fmt::is_formattable<std::tuple<>>::value));
|
||||
EXPECT_FALSE((fmt::is_formattable<unformattable>::value));
|
||||
EXPECT_FALSE((fmt::is_formattable<std::tuple<unformattable>>::value));
|
||||
EXPECT_FALSE((fmt::is_formattable<std::tuple<unformattable, int>>::value));
|
||||
EXPECT_FALSE((fmt::is_formattable<std::tuple<int, unformattable>>::value));
|
||||
@@ -655,6 +660,8 @@ TEST(ranges_test, container_adaptor) {
|
||||
m.push(2);
|
||||
EXPECT_EQ(fmt::format("{}", m), "[1, 2]");
|
||||
}
|
||||
|
||||
EXPECT_FALSE(fmt::is_formattable<std::stack<unformattable>>::value);
|
||||
}
|
||||
|
||||
struct tieable {
|
||||
|
||||
@@ -13,13 +13,17 @@
|
||||
#include <vector>
|
||||
|
||||
#include "fmt/os.h" // fmt::system_category
|
||||
#include "fmt/ranges.h"
|
||||
#include "gtest-extra.h" // StartsWith
|
||||
|
||||
#ifdef __cpp_lib_filesystem
|
||||
TEST(std_test, path) {
|
||||
using std::filesystem::path;
|
||||
EXPECT_EQ(fmt::format("{}", path("/usr/bin")), "/usr/bin");
|
||||
|
||||
// see #4303
|
||||
const path p = "/usr/bin";
|
||||
EXPECT_EQ(fmt::format("{}", p), "/usr/bin");
|
||||
|
||||
EXPECT_EQ(fmt::format("{:?}", path("/usr/bin")), "\"/usr/bin\"");
|
||||
EXPECT_EQ(fmt::format("{:8}", path("foo")), "foo ");
|
||||
|
||||
@@ -44,6 +48,9 @@ TEST(std_test, path) {
|
||||
# endif
|
||||
}
|
||||
|
||||
// Intentionally delayed include to test #4303
|
||||
#include "fmt/ranges.h"
|
||||
|
||||
// Test ambiguity problem described in #2954.
|
||||
TEST(ranges_std_test, format_vector_path) {
|
||||
auto p = std::filesystem::path("foo/bar.txt");
|
||||
|
||||
Reference in New Issue
Block a user