Compare commits

..

34 Commits

Author SHA1 Message Date
Victor Zverovich
123913715a Update version 2025-02-26 10:14:28 -08:00
Victor Zverovich
8c1059b92e Update changelog 2025-02-25 17:32:17 -08:00
Victor Zverovich
4e5aafbf43 Bump version 2025-02-25 17:20:47 -08:00
Victor Zverovich
db30fb3b81 Update changelog 2025-02-25 17:20:29 -08:00
Victor Zverovich
3401ce2be2 Fix ABI compatibility 2025-02-25 13:32:24 -08:00
Dean
7f7695524a Fix conflict with std::ignore (#4356)
In situations where `using namespace std;` is used, compiler warnings
can be generated because of local variables named `ignore`. This renames
those variables to something else to address the name conflict.
2025-02-19 23:08:21 -08:00
Dean
251320fcb7 Add .vs folder to .gitignore (#4355)
Opening the fmt folder as a CMake project with Visual Studio creates
this directory. The contents can be ignored.
2025-02-19 22:44:31 -08:00
Victor Chernyakin
94ab51cb8c Simplify implementation of operator""_cf (#4349) 2025-02-14 02:02:09 -08:00
Victor Zverovich
0ca42e836e Workaround an MSVC v140 bug 2025-02-13 14:12:16 +01:00
Thomas Khyn
ed27df5760 Replace forward slashes by backslashes in BMI path for MSVC. (#4344)
* Fix slashes in BMI path for MSVC builds
* Fix BMI path for MSVC builds when building with Ninja generator
2025-02-10 00:18:33 -08:00
Victor Zverovich
d42a068dbd Apply coding conventions 2025-02-05 11:06:45 -08:00
Markus Kitsinger
f2cec917da Move is_compiled_string to public API (#4342) 2025-02-04 16:20:27 -08:00
Sergiu Deitsch
d5b866e242 fix gcc 8.3 compile errors (#4336) 2025-02-03 08:51:58 -08:00
dependabot[bot]
5676e408f5 Bump github/codeql-action from 3.27.0 to 3.28.8 (#4337)
Bumps [github/codeql-action](https://github.com/github/codeql-action) from 3.27.0 to 3.28.8.
- [Release notes](https://github.com/github/codeql-action/releases)
- [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md)
- [Commits](662472033e...dd746615b3)

---
updated-dependencies:
- dependency-name: github/codeql-action
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-02-01 12:08:34 -08:00
dependabot[bot]
71d24b564d Bump actions/upload-artifact from 4.4.0 to 4.6.0 (#4339)
Bumps [actions/upload-artifact](https://github.com/actions/upload-artifact) from 4.4.0 to 4.6.0.
- [Release notes](https://github.com/actions/upload-artifact/releases)
- [Commits](50769540e7...65c4c4a1dd)

---
updated-dependencies:
- dependency-name: actions/upload-artifact
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-02-01 11:30:13 -08:00
LocalSpook
c9267da4df Fix typo in FMT_HAS_BUILTIN check 2025-01-31 08:42:19 -08:00
Victor Zverovich
373855c1b0 Clarify difference in FP representation 2025-01-26 13:49:54 -08:00
Victor Zverovich
52eeeb52a6 Make exponent threshold depend on representation (#3649) 2025-01-26 12:10:48 -08:00
Victor Zverovich
9cf9f38ede Update version 2025-01-25 10:08:57 -08:00
Victor Zverovich
4946bdb729 Update changelog 2025-01-25 08:53:02 -08:00
Rafał Lalik
01a5b56f0d Fix error of unitialized variable FMT_HEADERS
This happens when using e.g. pedantic mode in cmake-init.
2025-01-25 08:41:49 -08:00
timsong-cpp
cb6fdf2191 Restore constraint on map formatter (#4326)
* Restore constraint on map formatter
* Remove unnecessary double parens
2025-01-25 08:31:07 -08:00
timsong-cpp
f841ae61e2 Fix #4303: avoid instantiating formatter<const T> (#4325) 2025-01-24 10:53:10 -08:00
Matt
a3d05d70ce Silence a constexpr warning when compiling with MSVC and /W4 (#4322) 2025-01-23 12:11:23 -08:00
Victor Zverovich
41539c29f3 Workaround a bug in gcc 6 (#4318) 2025-01-22 11:12:41 -08:00
Victor Zverovich
aabe63910c Tweak changelog 2025-01-20 09:33:33 -08:00
Victor Zverovich
f90090be2c Update changelog 2025-01-19 10:00:40 -08:00
Victor Zverovich
9ff9c695db Bump version 2025-01-18 10:28:46 -08:00
Victor Zverovich
06ad1224eb Update changelog 2025-01-18 10:05:40 -08:00
Victor Zverovich
5f0572acdc Workaround a compilation error on gcc 9.4 2025-01-18 09:01:00 -08:00
Vladislav Shchapov
898d438571 Fix formatting into std::ostreambuf_iterator using a compiled format (#4312)
Signed-off-by: Vladislav Shchapov <vladislav@shchapov.ru>
2025-01-18 07:16:06 -08:00
Aaron Bishop
937b7c5c10 Add args() accessor back to fmt::format_context (#4310)
Add args() accessor back to fmt::format_context
Add test that would fail to compile if you can't create a fmt::format_context from another fmt::format_context
2025-01-17 10:28:34 -08:00
Victor Zverovich
01914f0389 Reduce size of basic_specs 2025-01-12 09:18:11 -08:00
Victor Zverovich
c43da35701 Workaround an ICE when using modules with gcc 14.2 and earlier 2025-01-12 08:57:43 -08:00
18 changed files with 201 additions and 63 deletions

View File

@@ -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

View File

@@ -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
View File

@@ -3,6 +3,7 @@
*.xcodeproj
*~
.vscode/
.vs/
/CMakeScripts
/Testing
/_CPack_Packages

View File

@@ -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)

View File

@@ -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

View File

@@ -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.

View File

@@ -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) {}

View File

@@ -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

View File

@@ -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,

View File

@@ -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);

View File

@@ -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>;

View File

@@ -1,5 +1,7 @@
module;
#define FMT_MODULE
#ifdef _MSVC_LANG
# define FMT_CPLUSPLUS _MSVC_LANG
#else

View File

@@ -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());
}
};
}

View File

@@ -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]{};

View File

@@ -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)

View File

@@ -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) {

View File

@@ -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 {

View File

@@ -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");