mirror of
https://github.com/fmtlib/fmt.git
synced 2025-12-07 01:06:53 +08:00
Compare commits
No commits in common. "master" and "12.1.0" have entirely different histories.
2
.github/workflows/cifuzz.yml
vendored
2
.github/workflows/cifuzz.yml
vendored
@ -25,7 +25,7 @@ jobs:
|
|||||||
language: c++
|
language: c++
|
||||||
|
|
||||||
- name: Upload crash
|
- name: Upload crash
|
||||||
uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0
|
uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0
|
||||||
if: failure() && steps.build.outcome == 'success'
|
if: failure() && steps.build.outcome == 'success'
|
||||||
with:
|
with:
|
||||||
name: artifacts
|
name: artifacts
|
||||||
|
|||||||
2
.github/workflows/doc.yml
vendored
2
.github/workflows/doc.yml
vendored
@ -10,7 +10,7 @@ jobs:
|
|||||||
runs-on: ubuntu-22.04
|
runs-on: ubuntu-22.04
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
|
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||||
|
|
||||||
- name: Add Ubuntu mirrors
|
- name: Add Ubuntu mirrors
|
||||||
run: |
|
run: |
|
||||||
|
|||||||
8
.github/workflows/lint.yml
vendored
8
.github/workflows/lint.yml
vendored
@ -13,16 +13,16 @@ jobs:
|
|||||||
format_code:
|
format_code:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
|
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||||
|
|
||||||
- name: Install clang-format
|
- name: Install clang-format
|
||||||
run: |
|
run: |
|
||||||
wget https://apt.llvm.org/llvm.sh
|
wget https://apt.llvm.org/llvm.sh
|
||||||
sudo bash ./llvm.sh 21
|
sudo bash ./llvm.sh 17
|
||||||
sudo apt install clang-format-21
|
sudo apt install clang-format-17
|
||||||
|
|
||||||
- name: Run clang-format
|
- name: Run clang-format
|
||||||
run: |
|
run: |
|
||||||
find include src -name '*.h' -o -name '*.cc' | \
|
find include src -name '*.h' -o -name '*.cc' | \
|
||||||
xargs clang-format-21 -i -style=file -fallback-style=none
|
xargs clang-format-17 -i -style=file -fallback-style=none
|
||||||
git diff --exit-code
|
git diff --exit-code
|
||||||
|
|||||||
2
.github/workflows/linux.yml
vendored
2
.github/workflows/linux.yml
vendored
@ -55,7 +55,7 @@ jobs:
|
|||||||
install: sudo apt install libc++-14-dev libc++abi-14-dev
|
install: sudo apt install libc++-14-dev libc++abi-14-dev
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
|
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||||
|
|
||||||
- name: Set timezone
|
- name: Set timezone
|
||||||
run: sudo timedatectl set-timezone 'Europe/Kyiv'
|
run: sudo timedatectl set-timezone 'Europe/Kyiv'
|
||||||
|
|||||||
9
.github/workflows/macos.yml
vendored
9
.github/workflows/macos.yml
vendored
@ -9,10 +9,13 @@ jobs:
|
|||||||
build:
|
build:
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
os: [macos-14]
|
os: [macos-13, macos-14]
|
||||||
build_type: [Debug, Release]
|
build_type: [Debug, Release]
|
||||||
std: [11, 17, 20, 23]
|
std: [11, 17, 20]
|
||||||
shared: [""]
|
shared: [""]
|
||||||
|
exclude:
|
||||||
|
- { os: macos-13, std: 11 }
|
||||||
|
- { os: macos-13, std: 17 }
|
||||||
include:
|
include:
|
||||||
- os: macos-14
|
- os: macos-14
|
||||||
std: 23
|
std: 23
|
||||||
@ -22,7 +25,7 @@ jobs:
|
|||||||
runs-on: '${{ matrix.os }}'
|
runs-on: '${{ matrix.os }}'
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
|
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||||
|
|
||||||
- name: Set timezone
|
- name: Set timezone
|
||||||
run: sudo systemsetup -settimezone 'Europe/Minsk'
|
run: sudo systemsetup -settimezone 'Europe/Minsk'
|
||||||
|
|||||||
6
.github/workflows/scorecard.yml
vendored
6
.github/workflows/scorecard.yml
vendored
@ -29,7 +29,7 @@ jobs:
|
|||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: "Checkout code"
|
- name: "Checkout code"
|
||||||
uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
|
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||||
with:
|
with:
|
||||||
persist-credentials: false
|
persist-credentials: false
|
||||||
|
|
||||||
@ -52,7 +52,7 @@ jobs:
|
|||||||
# Upload the results as artifacts (optional). Commenting out will disable uploads of run results in SARIF
|
# Upload the results as artifacts (optional). Commenting out will disable uploads of run results in SARIF
|
||||||
# format to the repository Actions tab.
|
# format to the repository Actions tab.
|
||||||
- name: "Upload artifact"
|
- name: "Upload artifact"
|
||||||
uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0
|
uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0
|
||||||
with:
|
with:
|
||||||
name: SARIF file
|
name: SARIF file
|
||||||
path: results.sarif
|
path: results.sarif
|
||||||
@ -60,6 +60,6 @@ jobs:
|
|||||||
|
|
||||||
# Upload the results to GitHub's code scanning dashboard.
|
# Upload the results to GitHub's code scanning dashboard.
|
||||||
- name: "Upload to code-scanning"
|
- name: "Upload to code-scanning"
|
||||||
uses: github/codeql-action/upload-sarif@0499de31b99561a6d14a36a5f662c2a54f91beee # v3.29.5
|
uses: github/codeql-action/upload-sarif@3599b3baa15b485a2e49ef411a7a4bb2452e7f93 # v3.29.5
|
||||||
with:
|
with:
|
||||||
sarif_file: results.sarif
|
sarif_file: results.sarif
|
||||||
|
|||||||
4
.github/workflows/windows.yml
vendored
4
.github/workflows/windows.yml
vendored
@ -32,7 +32,7 @@ jobs:
|
|||||||
standard: 20
|
standard: 20
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
|
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||||
|
|
||||||
- name: Set timezone
|
- name: Set timezone
|
||||||
run: tzutil /s "FLE Standard Time"
|
run: tzutil /s "FLE Standard Time"
|
||||||
@ -79,7 +79,7 @@ jobs:
|
|||||||
release: false
|
release: false
|
||||||
msystem: ${{matrix.sys}}
|
msystem: ${{matrix.sys}}
|
||||||
pacboy: cc:p cmake:p ninja:p lld:p
|
pacboy: cc:p cmake:p ninja:p lld:p
|
||||||
- uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
|
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||||
- name: Configure
|
- name: Configure
|
||||||
run: cmake -B ../build -DBUILD_SHARED_LIBS=ON -DCMAKE_BUILD_TYPE=Debug
|
run: cmake -B ../build -DBUILD_SHARED_LIBS=ON -DCMAKE_BUILD_TYPE=Debug
|
||||||
env: { LDFLAGS: -fuse-ld=lld }
|
env: { LDFLAGS: -fuse-ld=lld }
|
||||||
|
|||||||
38
README.md
38
README.md
@ -12,7 +12,7 @@
|
|||||||
alternative to C stdio and C++ iostreams.
|
alternative to C stdio and C++ iostreams.
|
||||||
|
|
||||||
If you like this project, please consider donating to one of the funds
|
If you like this project, please consider donating to one of the funds
|
||||||
that help victims of the war in Ukraine: <https://u24.gov.ua/>.
|
that help victims of the war in Ukraine: <https://www.stopputin.net/>.
|
||||||
|
|
||||||
[Documentation](https://fmt.dev)
|
[Documentation](https://fmt.dev)
|
||||||
|
|
||||||
@ -150,8 +150,8 @@ int main() {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
This can be [up to 9 times faster than `fprintf`](
|
This can be [5 to 9 times faster than
|
||||||
http://www.zverovich.net/2020/08/04/optimal-file-buffer-size.html).
|
fprintf](http://www.zverovich.net/2020/08/04/optimal-file-buffer-size.html).
|
||||||
|
|
||||||
**Print with colors and text styles**
|
**Print with colors and text styles**
|
||||||
|
|
||||||
@ -178,17 +178,17 @@ Output on a modern terminal with Unicode support:
|
|||||||
|
|
||||||
| Library | Method | Run Time, s |
|
| Library | Method | Run Time, s |
|
||||||
|-------------------|---------------|-------------|
|
|-------------------|---------------|-------------|
|
||||||
| libc | printf | 0.66 |
|
| libc | printf | 0.91 |
|
||||||
| libc++ | std::ostream | 1.63 |
|
| libc++ | std::ostream | 2.49 |
|
||||||
| {fmt} 12.1 | fmt::print | 0.44 |
|
| {fmt} 9.1 | fmt::print | 0.74 |
|
||||||
| Boost Format 1.88 | boost::format | 3.89 |
|
| Boost Format 1.80 | boost::format | 6.26 |
|
||||||
| Folly Format | folly::format | 1.28 |
|
| Folly Format | folly::format | 1.87 |
|
||||||
|
|
||||||
{fmt} is the fastest of the benchmarked methods, \~50% faster than
|
{fmt} is the fastest of the benchmarked methods, \~20% faster than
|
||||||
`printf`.
|
`printf`.
|
||||||
|
|
||||||
The above results were generated by building `tinyformat_test.cpp` on
|
The above results were generated by building `tinyformat_test.cpp` on
|
||||||
macOS 15.6.1 with `clang++ -O3 -DNDEBUG -DSPEED_TEST -DHAVE_FORMAT`, and
|
macOS 12.6.1 with `clang++ -O3 -DNDEBUG -DSPEED_TEST -DHAVE_FORMAT`, and
|
||||||
taking the best of three runs. In the test, the format string
|
taking the best of three runs. In the test, the format string
|
||||||
`"%0.10f:%04d:%+g:%s:%p:%c:%%\n"` or equivalent is filled 2,000,000
|
`"%0.10f:%04d:%+g:%s:%p:%c:%%\n"` or equivalent is filled 2,000,000
|
||||||
times with output sent to `/dev/null`; for further details refer to the
|
times with output sent to `/dev/null`; for further details refer to the
|
||||||
@ -217,11 +217,11 @@ in the following tables.
|
|||||||
**Optimized build (-O3)**
|
**Optimized build (-O3)**
|
||||||
|
|
||||||
| Method | Compile Time, s | Executable size, KiB | Stripped size, KiB |
|
| Method | Compile Time, s | Executable size, KiB | Stripped size, KiB |
|
||||||
|-----------------|-----------------|----------------------|--------------------|
|
|---------------|-----------------|----------------------|--------------------|
|
||||||
| printf | 1.6 | 54 | 50 |
|
| printf | 1.6 | 54 | 50 |
|
||||||
| IOStreams | 28.4 | 98 | 84 |
|
| IOStreams | 25.9 | 98 | 84 |
|
||||||
| {fmt} `1122268` | 5.0 | 54 | 50 |
|
| fmt 83652df | 4.8 | 54 | 50 |
|
||||||
| tinyformat | 32.6 | 164 | 136 |
|
| tinyformat | 29.1 | 161 | 136 |
|
||||||
| Boost Format | 55.0 | 530 | 317 |
|
| Boost Format | 55.0 | 530 | 317 |
|
||||||
|
|
||||||
{fmt} is fast to compile and is comparable to `printf` in terms of per-call
|
{fmt} is fast to compile and is comparable to `printf` in terms of per-call
|
||||||
@ -230,12 +230,12 @@ binary size (within a rounding error on this system).
|
|||||||
**Non-optimized build**
|
**Non-optimized build**
|
||||||
|
|
||||||
| Method | Compile Time, s | Executable size, KiB | Stripped size, KiB |
|
| Method | Compile Time, s | Executable size, KiB | Stripped size, KiB |
|
||||||
|-----------------|-----------------|----------------------|--------------------|
|
|---------------|-----------------|----------------------|--------------------|
|
||||||
| printf | 1.4 | 54 | 50 |
|
| printf | 1.4 | 54 | 50 |
|
||||||
| IOStreams | 27.0 | 88 | 68 |
|
| IOStreams | 23.4 | 92 | 68 |
|
||||||
| {fmt} `1122268` | 4.7 | 87 | 84 |
|
| {fmt} 83652df | 4.4 | 89 | 85 |
|
||||||
| tinyformat | 28.1 | 185 | 145 |
|
| tinyformat | 24.5 | 204 | 161 |
|
||||||
| Boost Format | 38.9 | 678 | 381 |
|
| Boost Format | 36.4 | 831 | 462 |
|
||||||
|
|
||||||
`libc`, `lib(std)c++`, and `libfmt` are all linked as shared libraries
|
`libc`, `lib(std)c++`, and `libfmt` are all linked as shared libraries
|
||||||
to compare formatting function overhead only. Boost Format is a
|
to compare formatting function overhead only. Boost Format is a
|
||||||
|
|||||||
@ -624,8 +624,6 @@ Example:
|
|||||||
|
|
||||||
::: ostream
|
::: ostream
|
||||||
|
|
||||||
::: output_file(cstring_view, T...)
|
|
||||||
|
|
||||||
::: windows_error
|
::: windows_error
|
||||||
|
|
||||||
<a id="ostream-api"></a>
|
<a id="ostream-api"></a>
|
||||||
@ -708,7 +706,7 @@ following differences:
|
|||||||
precision that provides round-trip guarantees similarly to other languages
|
precision that provides round-trip guarantees similarly to other languages
|
||||||
like Java and Python. `std::format` is currently specified in terms of
|
like Java and Python. `std::format` is currently specified in terms of
|
||||||
`std::to_chars` which tries to generate the smallest number of characters
|
`std::to_chars` which tries to generate the smallest number of characters
|
||||||
(ignoring redundant digits and sign in exponent) and may produce more
|
(ignoring redundant digits and sign in exponent) and may procude more
|
||||||
decimal digits than necessary.
|
decimal digits than necessary.
|
||||||
|
|
||||||
## Configuration Options
|
## Configuration Options
|
||||||
@ -748,7 +746,7 @@ configuring CMake.
|
|||||||
- `0` - off (default)
|
- `0` - off (default)
|
||||||
- `1` - disables locale support and applies some optimizations
|
- `1` - disables locale support and applies some optimizations
|
||||||
- `2` - disables some Unicode features, named arguments and applies more
|
- `2` - disables some Unicode features, named arguments and applies more
|
||||||
aggressive optimizations
|
aggresive optimizations
|
||||||
|
|
||||||
### Binary Size Optimization
|
### Binary Size Optimization
|
||||||
|
|
||||||
|
|||||||
@ -20,7 +20,6 @@
|
|||||||
margin-left: 1em;
|
margin-left: 1em;
|
||||||
}
|
}
|
||||||
|
|
||||||
code,
|
|
||||||
pre > code.decl {
|
pre > code.decl {
|
||||||
white-space: pre-wrap;
|
white-space: pre-wrap;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -76,7 +76,7 @@ hide:
|
|||||||
<p>
|
<p>
|
||||||
The default is <b>locale-independent</b>, but you can opt into localized
|
The default is <b>locale-independent</b>, but you can opt into localized
|
||||||
formatting and {fmt} makes it work with Unicode, addressing issues in the
|
formatting and {fmt} makes it work with Unicode, addressing issues in the
|
||||||
standard library.
|
standard libary.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@ -233,6 +233,7 @@
|
|||||||
FMT_PRAGMA_GCC(push_options)
|
FMT_PRAGMA_GCC(push_options)
|
||||||
#if !defined(__OPTIMIZE__) && !defined(__CUDACC__) && !defined(FMT_MODULE)
|
#if !defined(__OPTIMIZE__) && !defined(__CUDACC__) && !defined(FMT_MODULE)
|
||||||
FMT_PRAGMA_GCC(optimize("Og"))
|
FMT_PRAGMA_GCC(optimize("Og"))
|
||||||
|
# define FMT_GCC_OPTIMIZED
|
||||||
#endif
|
#endif
|
||||||
FMT_PRAGMA_CLANG(diagnostic push)
|
FMT_PRAGMA_CLANG(diagnostic push)
|
||||||
FMT_PRAGMA_GCC(diagnostic push)
|
FMT_PRAGMA_GCC(diagnostic push)
|
||||||
@ -245,7 +246,7 @@ FMT_PRAGMA_GCC(diagnostic push)
|
|||||||
# define FMT_ALWAYS_INLINE inline
|
# define FMT_ALWAYS_INLINE inline
|
||||||
#endif
|
#endif
|
||||||
// A version of FMT_ALWAYS_INLINE to prevent code bloat in debug mode.
|
// A version of FMT_ALWAYS_INLINE to prevent code bloat in debug mode.
|
||||||
#ifdef NDEBUG
|
#if defined(NDEBUG) || defined(FMT_GCC_OPTIMIZED)
|
||||||
# define FMT_INLINE FMT_ALWAYS_INLINE
|
# define FMT_INLINE FMT_ALWAYS_INLINE
|
||||||
#else
|
#else
|
||||||
# define FMT_INLINE inline
|
# define FMT_INLINE inline
|
||||||
@ -926,7 +927,7 @@ class locale_ref {
|
|||||||
template <typename Locale, FMT_ENABLE_IF(sizeof(Locale::collate) != 0)>
|
template <typename Locale, FMT_ENABLE_IF(sizeof(Locale::collate) != 0)>
|
||||||
locale_ref(const Locale& loc) : locale_(&loc) {
|
locale_ref(const Locale& loc) : locale_(&loc) {
|
||||||
// Check if std::isalpha is found via ADL to reduce the chance of misuse.
|
// Check if std::isalpha is found via ADL to reduce the chance of misuse.
|
||||||
detail::ignore_unused(sizeof(isalpha('x', loc)));
|
isalpha('x', loc);
|
||||||
}
|
}
|
||||||
|
|
||||||
inline explicit operator bool() const noexcept { return locale_ != nullptr; }
|
inline explicit operator bool() const noexcept { return locale_ != nullptr; }
|
||||||
@ -1850,7 +1851,8 @@ template <typename T> class buffer {
|
|||||||
#if !FMT_MSC_VERSION || FMT_MSC_VERSION >= 1940
|
#if !FMT_MSC_VERSION || FMT_MSC_VERSION >= 1940
|
||||||
FMT_CONSTEXPR20
|
FMT_CONSTEXPR20
|
||||||
#endif
|
#endif
|
||||||
void append(const U* begin, const U* end) {
|
void
|
||||||
|
append(const U* begin, const U* end) {
|
||||||
while (begin != end) {
|
while (begin != end) {
|
||||||
auto size = size_;
|
auto size = size_;
|
||||||
auto free_cap = capacity_ - size;
|
auto free_cap = capacity_ - size;
|
||||||
@ -2754,9 +2756,7 @@ template <typename... T> struct fstring {
|
|||||||
static_assert(count<(is_view<remove_cvref_t<T>>::value &&
|
static_assert(count<(is_view<remove_cvref_t<T>>::value &&
|
||||||
std::is_reference<T>::value)...>() == 0,
|
std::is_reference<T>::value)...>() == 0,
|
||||||
"passing views as lvalues is disallowed");
|
"passing views as lvalues is disallowed");
|
||||||
#if FMT_USE_CONSTEVAL
|
if (FMT_USE_CONSTEVAL) parse_format_string<char>(s, checker(s, arg_pack()));
|
||||||
parse_format_string<char>(s, checker(s, arg_pack()));
|
|
||||||
#endif
|
|
||||||
#ifdef FMT_ENFORCE_COMPILE_STRING
|
#ifdef FMT_ENFORCE_COMPILE_STRING
|
||||||
static_assert(
|
static_assert(
|
||||||
FMT_USE_CONSTEVAL && sizeof(s) != 0,
|
FMT_USE_CONSTEVAL && sizeof(s) != 0,
|
||||||
|
|||||||
@ -522,7 +522,7 @@ auto format_to_n(OutputIt out, size_t n, const S& fmt, T&&... args)
|
|||||||
-> format_to_n_result<OutputIt> {
|
-> format_to_n_result<OutputIt> {
|
||||||
using traits = detail::fixed_buffer_traits;
|
using traits = detail::fixed_buffer_traits;
|
||||||
auto buf = detail::iterator_buffer<OutputIt, char, traits>(out, n);
|
auto buf = detail::iterator_buffer<OutputIt, char, traits>(out, n);
|
||||||
fmt::format_to(appender(buf), fmt, std::forward<T>(args)...);
|
fmt::format_to(std::back_inserter(buf), fmt, std::forward<T>(args)...);
|
||||||
return {buf.out(), buf.count()};
|
return {buf.out(), buf.count()};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -559,8 +559,8 @@ template <size_t N> class static_format_result {
|
|||||||
*fmt::format_to(data, fmt, std::forward<T>(args)...) = '\0';
|
*fmt::format_to(data, fmt, std::forward<T>(args)...) = '\0';
|
||||||
}
|
}
|
||||||
|
|
||||||
FMT_CONSTEXPR auto str() const -> fmt::string_view { return {data, N - 1}; }
|
auto str() const -> fmt::string_view { return {data, N - 1}; }
|
||||||
FMT_CONSTEXPR auto c_str() const -> const char* { return data; }
|
auto c_str() const -> const char* { return data; }
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@ -30,14 +30,6 @@
|
|||||||
# define FMT_FUNC
|
# define FMT_FUNC
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if defined(FMT_USE_FULL_CACHE_DRAGONBOX)
|
|
||||||
// Use the provided definition.
|
|
||||||
#elif defined(__OPTIMIZE_SIZE__)
|
|
||||||
# define FMT_USE_FULL_CACHE_DRAGONBOX 0
|
|
||||||
#else
|
|
||||||
# define FMT_USE_FULL_CACHE_DRAGONBOX 1
|
|
||||||
#endif
|
|
||||||
|
|
||||||
FMT_BEGIN_NAMESPACE
|
FMT_BEGIN_NAMESPACE
|
||||||
|
|
||||||
#ifndef FMT_CUSTOM_ASSERT_FAIL
|
#ifndef FMT_CUSTOM_ASSERT_FAIL
|
||||||
|
|||||||
@ -493,8 +493,8 @@ template <typename OutputIt,
|
|||||||
#if FMT_CLANG_VERSION >= 307 && !FMT_ICC_VERSION
|
#if FMT_CLANG_VERSION >= 307 && !FMT_ICC_VERSION
|
||||||
__attribute__((no_sanitize("undefined")))
|
__attribute__((no_sanitize("undefined")))
|
||||||
#endif
|
#endif
|
||||||
FMT_CONSTEXPR20 inline auto reserve(OutputIt it, size_t n) ->
|
FMT_CONSTEXPR20 inline auto
|
||||||
typename OutputIt::value_type* {
|
reserve(OutputIt it, size_t n) -> typename OutputIt::value_type* {
|
||||||
auto& c = get_container(it);
|
auto& c = get_container(it);
|
||||||
size_t size = c.size();
|
size_t size = c.size();
|
||||||
c.resize(size + n);
|
c.resize(size + n);
|
||||||
@ -736,8 +736,12 @@ using fast_float_t = conditional_t<sizeof(T) == sizeof(double), double, float>;
|
|||||||
template <typename T>
|
template <typename T>
|
||||||
using is_double_double = bool_constant<std::numeric_limits<T>::digits == 106>;
|
using is_double_double = bool_constant<std::numeric_limits<T>::digits == 106>;
|
||||||
|
|
||||||
|
#ifndef FMT_USE_FULL_CACHE_DRAGONBOX
|
||||||
|
# define FMT_USE_FULL_CACHE_DRAGONBOX 0
|
||||||
|
#endif
|
||||||
|
|
||||||
// An allocator that uses malloc/free to allow removing dependency on the C++
|
// An allocator that uses malloc/free to allow removing dependency on the C++
|
||||||
// standard library runtime. std::decay is used for back_inserter to be found by
|
// standard libary runtime. std::decay is used for back_inserter to be found by
|
||||||
// ADL when applied to memory_buffer.
|
// ADL when applied to memory_buffer.
|
||||||
template <typename T> struct allocator : private std::decay<void> {
|
template <typename T> struct allocator : private std::decay<void> {
|
||||||
using value_type = T;
|
using value_type = T;
|
||||||
@ -1307,13 +1311,7 @@ class utf8_to_utf16 {
|
|||||||
inline auto str() const -> std::wstring { return {&buffer_[0], size()}; }
|
inline auto str() const -> std::wstring { return {&buffer_[0], size()}; }
|
||||||
};
|
};
|
||||||
|
|
||||||
enum class to_utf8_error_policy { abort, replace, wtf };
|
enum class to_utf8_error_policy { abort, replace };
|
||||||
|
|
||||||
inline void to_utf8_3bytes(buffer<char>& buf, uint32_t cp) {
|
|
||||||
buf.push_back(static_cast<char>(0xe0 | (cp >> 12)));
|
|
||||||
buf.push_back(static_cast<char>(0x80 | ((cp & 0xfff) >> 6)));
|
|
||||||
buf.push_back(static_cast<char>(0x80 | (cp & 0x3f)));
|
|
||||||
}
|
|
||||||
|
|
||||||
// A converter from UTF-16/UTF-32 (host endian) to UTF-8.
|
// A converter from UTF-16/UTF-32 (host endian) to UTF-8.
|
||||||
template <typename WChar, typename Buffer = memory_buffer> class to_utf8 {
|
template <typename WChar, typename Buffer = memory_buffer> class to_utf8 {
|
||||||
@ -1355,13 +1353,8 @@ template <typename WChar, typename Buffer = memory_buffer> class to_utf8 {
|
|||||||
// Handle a surrogate pair.
|
// Handle a surrogate pair.
|
||||||
++p;
|
++p;
|
||||||
if (p == s.end() || (c & 0xfc00) != 0xd800 || (*p & 0xfc00) != 0xdc00) {
|
if (p == s.end() || (c & 0xfc00) != 0xd800 || (*p & 0xfc00) != 0xdc00) {
|
||||||
switch (policy) {
|
if (policy == to_utf8_error_policy::abort) return false;
|
||||||
case to_utf8_error_policy::abort: return false;
|
|
||||||
case to_utf8_error_policy::replace:
|
|
||||||
buf.append(string_view("\xEF\xBF\xBD"));
|
buf.append(string_view("\xEF\xBF\xBD"));
|
||||||
break;
|
|
||||||
case to_utf8_error_policy::wtf: to_utf8_3bytes(buf, c); break;
|
|
||||||
}
|
|
||||||
--p;
|
--p;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@ -1373,7 +1366,9 @@ template <typename WChar, typename Buffer = memory_buffer> class to_utf8 {
|
|||||||
buf.push_back(static_cast<char>(0xc0 | (c >> 6)));
|
buf.push_back(static_cast<char>(0xc0 | (c >> 6)));
|
||||||
buf.push_back(static_cast<char>(0x80 | (c & 0x3f)));
|
buf.push_back(static_cast<char>(0x80 | (c & 0x3f)));
|
||||||
} else if ((c >= 0x800 && c <= 0xd7ff) || (c >= 0xe000 && c <= 0xffff)) {
|
} else if ((c >= 0x800 && c <= 0xd7ff) || (c >= 0xe000 && c <= 0xffff)) {
|
||||||
to_utf8_3bytes(buf, c);
|
buf.push_back(static_cast<char>(0xe0 | (c >> 12)));
|
||||||
|
buf.push_back(static_cast<char>(0x80 | ((c & 0xfff) >> 6)));
|
||||||
|
buf.push_back(static_cast<char>(0x80 | (c & 0x3f)));
|
||||||
} else if (c >= 0x10000 && c <= 0x10ffff) {
|
} else if (c >= 0x10000 && c <= 0x10ffff) {
|
||||||
buf.push_back(static_cast<char>(0xf0 | (c >> 18)));
|
buf.push_back(static_cast<char>(0xf0 | (c >> 18)));
|
||||||
buf.push_back(static_cast<char>(0x80 | ((c & 0x3ffff) >> 12)));
|
buf.push_back(static_cast<char>(0x80 | ((c & 0x3ffff) >> 12)));
|
||||||
@ -2539,7 +2534,7 @@ FMT_CONSTEXPR20 auto write_fixed(OutputIt out, const DecimalFP& f,
|
|||||||
auto grouping = Grouping(loc, specs.localized());
|
auto grouping = Grouping(loc, specs.localized());
|
||||||
size += grouping.count_separators(exp);
|
size += grouping.count_separators(exp);
|
||||||
return write_padded<Char, align::right>(
|
return write_padded<Char, align::right>(
|
||||||
out, specs, static_cast<size_t>(size), [&](iterator it) {
|
out, specs, to_unsigned(size), [&](iterator it) {
|
||||||
if (s != sign::none) *it++ = detail::getsign<Char>(s);
|
if (s != sign::none) *it++ = detail::getsign<Char>(s);
|
||||||
it = write_significand(it, f.significand, significand_size, exp,
|
it = write_significand(it, f.significand, significand_size, exp,
|
||||||
decimal_point, grouping);
|
decimal_point, grouping);
|
||||||
@ -2555,7 +2550,7 @@ FMT_CONSTEXPR20 auto write_fixed(OutputIt out, const DecimalFP& f,
|
|||||||
bool pointy = num_zeros != 0 || significand_size != 0 || specs.alt();
|
bool pointy = num_zeros != 0 || significand_size != 0 || specs.alt();
|
||||||
size += 1 + (pointy ? 1 : 0) + num_zeros;
|
size += 1 + (pointy ? 1 : 0) + num_zeros;
|
||||||
return write_padded<Char, align::right>(
|
return write_padded<Char, align::right>(
|
||||||
out, specs, static_cast<size_t>(size), [&](iterator it) {
|
out, specs, to_unsigned(size), [&](iterator it) {
|
||||||
if (s != sign::none) *it++ = detail::getsign<Char>(s);
|
if (s != sign::none) *it++ = detail::getsign<Char>(s);
|
||||||
*it++ = Char('0');
|
*it++ = Char('0');
|
||||||
if (!pointy) return it;
|
if (!pointy) return it;
|
||||||
@ -2599,7 +2594,7 @@ FMT_CONSTEXPR20 auto do_write_float(OutputIt out, const DecimalFP& f,
|
|||||||
*it++ = Char(exp_char);
|
*it++ = Char(exp_char);
|
||||||
return write_exponent<Char>(exp, it);
|
return write_exponent<Char>(exp, it);
|
||||||
};
|
};
|
||||||
size_t usize = static_cast<size_t>(size);
|
auto usize = to_unsigned(size);
|
||||||
return specs.width > 0
|
return specs.width > 0
|
||||||
? write_padded<Char, align::right>(out, specs, usize, write)
|
? write_padded<Char, align::right>(out, specs, usize, write)
|
||||||
: base_iterator(out, write(reserve(out, usize)));
|
: base_iterator(out, write(reserve(out, usize)));
|
||||||
@ -4253,11 +4248,7 @@ class format_int {
|
|||||||
* // A compile-time error because 'd' is an invalid specifier for strings.
|
* // A compile-time error because 'd' is an invalid specifier for strings.
|
||||||
* std::string s = fmt::format(FMT_STRING("{:d}"), "foo");
|
* std::string s = fmt::format(FMT_STRING("{:d}"), "foo");
|
||||||
*/
|
*/
|
||||||
#if FMT_USE_CONSTEVAL
|
|
||||||
# define FMT_STRING(s) s
|
|
||||||
#else
|
|
||||||
#define FMT_STRING(s) FMT_STRING_IMPL(s, fmt::detail::compile_string)
|
#define FMT_STRING(s) FMT_STRING_IMPL(s, fmt::detail::compile_string)
|
||||||
#endif // FMT_USE_CONSTEVAL
|
|
||||||
|
|
||||||
FMT_API auto vsystem_error(int error_code, string_view fmt, format_args args)
|
FMT_API auto vsystem_error(int error_code, string_view fmt, format_args args)
|
||||||
-> std::system_error;
|
-> std::system_error;
|
||||||
|
|||||||
@ -161,6 +161,14 @@ inline auto system_category() noexcept -> const std::error_category& {
|
|||||||
}
|
}
|
||||||
#endif // _WIN32
|
#endif // _WIN32
|
||||||
|
|
||||||
|
// std::system is not available on some platforms such as iOS (#2248).
|
||||||
|
#ifdef __OSX__
|
||||||
|
template <typename S, typename... Args, typename Char = char_t<S>>
|
||||||
|
void say(const S& fmt, Args&&... args) {
|
||||||
|
std::system(format("say \"{}\"", format(fmt, args...)).c_str());
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
// A buffered file.
|
// A buffered file.
|
||||||
class buffered_file {
|
class buffered_file {
|
||||||
private:
|
private:
|
||||||
|
|||||||
@ -84,13 +84,11 @@ namespace detail {
|
|||||||
template <typename Char, typename PathChar>
|
template <typename Char, typename PathChar>
|
||||||
auto get_path_string(const std::filesystem::path& p,
|
auto get_path_string(const std::filesystem::path& p,
|
||||||
const std::basic_string<PathChar>& native) {
|
const std::basic_string<PathChar>& native) {
|
||||||
if constexpr (std::is_same_v<Char, char> &&
|
if constexpr (std::is_same_v<Char, char> && std::is_same_v<PathChar, wchar_t>)
|
||||||
std::is_same_v<PathChar, wchar_t>) {
|
return to_utf8<wchar_t>(native, to_utf8_error_policy::replace);
|
||||||
return to_utf8<wchar_t>(native, to_utf8_error_policy::wtf);
|
else
|
||||||
} else {
|
|
||||||
return p.string<Char>();
|
return p.string<Char>();
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
template <typename Char, typename PathChar>
|
template <typename Char, typename PathChar>
|
||||||
void write_escaped_path(basic_memory_buffer<Char>& quoted,
|
void write_escaped_path(basic_memory_buffer<Char>& quoted,
|
||||||
@ -647,11 +645,6 @@ struct formatter<std::atomic_flag, Char> : formatter<bool, Char> {
|
|||||||
};
|
};
|
||||||
#endif // __cpp_lib_atomic_flag_test
|
#endif // __cpp_lib_atomic_flag_test
|
||||||
|
|
||||||
template <typename T> struct is_tuple_like;
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
struct is_tuple_like<std::complex<T>> : std::false_type {};
|
|
||||||
|
|
||||||
template <typename T, typename Char> struct formatter<std::complex<T>, Char> {
|
template <typename T, typename Char> struct formatter<std::complex<T>, Char> {
|
||||||
private:
|
private:
|
||||||
detail::dynamic_format_specs<Char> specs_;
|
detail::dynamic_format_specs<Char> specs_;
|
||||||
|
|||||||
@ -34,8 +34,8 @@ tag_map = {
|
|||||||
'emphasis': 'em',
|
'emphasis': 'em',
|
||||||
'computeroutput': 'code',
|
'computeroutput': 'code',
|
||||||
'para': 'p',
|
'para': 'p',
|
||||||
'itemizedlist': 'ul',
|
'programlisting': 'pre',
|
||||||
'listitem': 'li'
|
'verbatim': 'pre'
|
||||||
}
|
}
|
||||||
|
|
||||||
# A map from Doxygen tags to text.
|
# A map from Doxygen tags to text.
|
||||||
@ -50,37 +50,21 @@ def escape_html(s: str) -> str:
|
|||||||
return s.replace("<", "<")
|
return s.replace("<", "<")
|
||||||
|
|
||||||
|
|
||||||
# Converts a node from doxygen to HTML format.
|
|
||||||
def convert_node(node: ElementTree.Element, tag: str, attrs: dict = {}):
|
|
||||||
out = '<' + tag
|
|
||||||
for key, value in attrs.items():
|
|
||||||
out += ' ' + key + '="' + value + '"'
|
|
||||||
out += '>'
|
|
||||||
if node.text:
|
|
||||||
out += escape_html(node.text)
|
|
||||||
out += doxyxml2html(list(node))
|
|
||||||
out += '</' + tag + '>'
|
|
||||||
if node.tail:
|
|
||||||
out += node.tail
|
|
||||||
return out
|
|
||||||
|
|
||||||
|
|
||||||
def doxyxml2html(nodes: List[ElementTree.Element]):
|
def doxyxml2html(nodes: List[ElementTree.Element]):
|
||||||
out = ''
|
out = ''
|
||||||
for n in nodes:
|
for n in nodes:
|
||||||
tag = tag_map.get(n.tag)
|
tag = tag_map.get(n.tag)
|
||||||
if tag:
|
if not tag:
|
||||||
out += convert_node(n, tag)
|
|
||||||
continue
|
|
||||||
if n.tag == 'programlisting' or n.tag == 'verbatim':
|
|
||||||
out += '<pre>'
|
|
||||||
out += convert_node(n, 'code', {'class': 'language-cpp'})
|
|
||||||
out += '</pre>'
|
|
||||||
continue
|
|
||||||
if n.tag == 'ulink':
|
|
||||||
out += convert_node(n, 'a', {'href': n.attrib['url']})
|
|
||||||
continue
|
|
||||||
out += tag_text_map[n.tag]
|
out += tag_text_map[n.tag]
|
||||||
|
out += '<' + tag + '>' if tag else ''
|
||||||
|
out += '<code class="language-cpp">' if tag == 'pre' else ''
|
||||||
|
if n.text:
|
||||||
|
out += escape_html(n.text)
|
||||||
|
out += doxyxml2html(list(n))
|
||||||
|
out += '</code>' if tag == 'pre' else ''
|
||||||
|
out += '</' + tag + '>' if tag else ''
|
||||||
|
if n.tail:
|
||||||
|
out += n.tail
|
||||||
return out
|
return out
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -62,6 +62,11 @@ if (NOT (MSVC AND BUILD_SHARED_LIBS))
|
|||||||
endif ()
|
endif ()
|
||||||
add_fmt_test(ostream-test)
|
add_fmt_test(ostream-test)
|
||||||
add_fmt_test(compile-test)
|
add_fmt_test(compile-test)
|
||||||
|
add_fmt_test(compile-fp-test)
|
||||||
|
if (MSVC)
|
||||||
|
# Without this option, MSVC returns 199711L for the __cplusplus macro.
|
||||||
|
target_compile_options(compile-fp-test PRIVATE /Zc:__cplusplus)
|
||||||
|
endif()
|
||||||
add_fmt_test(printf-test)
|
add_fmt_test(printf-test)
|
||||||
add_fmt_test(ranges-test ranges-odr-test.cc)
|
add_fmt_test(ranges-test ranges-odr-test.cc)
|
||||||
add_fmt_test(no-builtin-types-test HEADER_ONLY)
|
add_fmt_test(no-builtin-types-test HEADER_ONLY)
|
||||||
|
|||||||
62
test/compile-fp-test.cc
Normal file
62
test/compile-fp-test.cc
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
// Formatting library for C++ - formatting library tests
|
||||||
|
//
|
||||||
|
// Copyright (c) 2012 - present, Victor Zverovich
|
||||||
|
// All rights reserved.
|
||||||
|
//
|
||||||
|
// For the license information refer to format.h.
|
||||||
|
|
||||||
|
#include "fmt/compile.h"
|
||||||
|
#include "gmock/gmock.h"
|
||||||
|
|
||||||
|
#if FMT_USE_CONSTEVAL
|
||||||
|
|
||||||
|
template <size_t max_string_length, typename Char = char> struct test_string {
|
||||||
|
Char buffer[max_string_length] = {};
|
||||||
|
|
||||||
|
template <typename T> constexpr bool operator==(const T& rhs) const noexcept {
|
||||||
|
return fmt::basic_string_view<Char>(rhs).compare(buffer) == 0;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <size_t max_string_length, typename Char = char, typename... Args>
|
||||||
|
consteval auto test_format(auto format, const Args&... args) {
|
||||||
|
test_string<max_string_length, Char> string{};
|
||||||
|
fmt::format_to(string.buffer, format, args...);
|
||||||
|
return string;
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(compile_time_formatting_test, floating_point) {
|
||||||
|
EXPECT_EQ("0", test_format<2>(FMT_COMPILE("{}"), 0.0f));
|
||||||
|
EXPECT_EQ("392.500000", test_format<11>(FMT_COMPILE("{0:f}"), 392.5f));
|
||||||
|
|
||||||
|
EXPECT_EQ("0", test_format<2>(FMT_COMPILE("{:}"), 0.0));
|
||||||
|
EXPECT_EQ("0.000000", test_format<9>(FMT_COMPILE("{:f}"), 0.0));
|
||||||
|
EXPECT_EQ("0", test_format<2>(FMT_COMPILE("{:g}"), 0.0));
|
||||||
|
EXPECT_EQ("392.65", test_format<7>(FMT_COMPILE("{:}"), 392.65));
|
||||||
|
EXPECT_EQ("392.65", test_format<7>(FMT_COMPILE("{:g}"), 392.65));
|
||||||
|
EXPECT_EQ("392.65", test_format<7>(FMT_COMPILE("{:G}"), 392.65));
|
||||||
|
EXPECT_EQ("4.9014e+06", test_format<11>(FMT_COMPILE("{:g}"), 4.9014e6));
|
||||||
|
EXPECT_EQ("-392.650000", test_format<12>(FMT_COMPILE("{:f}"), -392.65));
|
||||||
|
EXPECT_EQ("-392.650000", test_format<12>(FMT_COMPILE("{:F}"), -392.65));
|
||||||
|
|
||||||
|
EXPECT_EQ("3.926500e+02", test_format<13>(FMT_COMPILE("{0:e}"), 392.65));
|
||||||
|
EXPECT_EQ("3.926500E+02", test_format<13>(FMT_COMPILE("{0:E}"), 392.65));
|
||||||
|
EXPECT_EQ("+0000392.6", test_format<11>(FMT_COMPILE("{0:+010.4g}"), 392.65));
|
||||||
|
EXPECT_EQ("9223372036854775808.000000",
|
||||||
|
test_format<27>(FMT_COMPILE("{:f}"), 9223372036854775807.0));
|
||||||
|
|
||||||
|
constexpr double nan = std::numeric_limits<double>::quiet_NaN();
|
||||||
|
EXPECT_EQ("nan", test_format<4>(FMT_COMPILE("{}"), nan));
|
||||||
|
EXPECT_EQ("+nan", test_format<5>(FMT_COMPILE("{:+}"), nan));
|
||||||
|
if (std::signbit(-nan))
|
||||||
|
EXPECT_EQ("-nan", test_format<5>(FMT_COMPILE("{}"), -nan));
|
||||||
|
else
|
||||||
|
fmt::print("Warning: compiler doesn't handle negative NaN correctly");
|
||||||
|
|
||||||
|
constexpr double inf = std::numeric_limits<double>::infinity();
|
||||||
|
EXPECT_EQ("inf", test_format<4>(FMT_COMPILE("{}"), inf));
|
||||||
|
EXPECT_EQ("+inf", test_format<5>(FMT_COMPILE("{:+}"), inf));
|
||||||
|
EXPECT_EQ("-inf", test_format<5>(FMT_COMPILE("{}"), -inf));
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // FMT_USE_CONSTEVAL
|
||||||
@ -90,6 +90,9 @@ TEST(compile_test, format_escape) {
|
|||||||
EXPECT_EQ("\"abc\" ", fmt::format(FMT_COMPILE("{0:<7?}"), "abc"));
|
EXPECT_EQ("\"abc\" ", fmt::format(FMT_COMPILE("{0:<7?}"), "abc"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST(compile_test, format_wide_string) {
|
||||||
|
EXPECT_EQ(L"42", fmt::format(FMT_COMPILE(L"{}"), 42));
|
||||||
|
}
|
||||||
|
|
||||||
TEST(compile_test, format_specs) {
|
TEST(compile_test, format_specs) {
|
||||||
EXPECT_EQ("42", fmt::format(FMT_COMPILE("{:x}"), 0x42));
|
EXPECT_EQ("42", fmt::format(FMT_COMPILE("{:x}"), 0x42));
|
||||||
@ -121,6 +124,7 @@ TEST(compile_test, manual_ordering) {
|
|||||||
"true 42 42 foo 0x1234 foo",
|
"true 42 42 foo 0x1234 foo",
|
||||||
fmt::format(FMT_COMPILE("{0} {1} {2} {3} {4} {5}"), true, 42, 42.0f,
|
fmt::format(FMT_COMPILE("{0} {1} {2} {3} {4} {5}"), true, 42, 42.0f,
|
||||||
"foo", reinterpret_cast<void*>(0x1234), test_formattable()));
|
"foo", reinterpret_cast<void*>(0x1234), test_formattable()));
|
||||||
|
EXPECT_EQ(L"42", fmt::format(FMT_COMPILE(L"{0}"), 42));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(compile_test, named) {
|
TEST(compile_test, named) {
|
||||||
@ -129,6 +133,10 @@ TEST(compile_test, named) {
|
|||||||
static_assert(std::is_same_v<decltype(runtime_named_field_compiled),
|
static_assert(std::is_same_v<decltype(runtime_named_field_compiled),
|
||||||
fmt::detail::runtime_named_field<char>>);
|
fmt::detail::runtime_named_field<char>>);
|
||||||
|
|
||||||
|
EXPECT_EQ("42", fmt::format(FMT_COMPILE("{}"), fmt::arg("arg", 42)));
|
||||||
|
EXPECT_EQ("41 43", fmt::format(FMT_COMPILE("{} {}"), fmt::arg("arg", 41),
|
||||||
|
fmt::arg("arg", 43)));
|
||||||
|
|
||||||
EXPECT_EQ("foobar",
|
EXPECT_EQ("foobar",
|
||||||
fmt::format(FMT_COMPILE("{a0}{a1}"), fmt::arg("a0", "foo"),
|
fmt::format(FMT_COMPILE("{a0}{a1}"), fmt::arg("a0", "foo"),
|
||||||
fmt::arg("a1", "bar")));
|
fmt::arg("a1", "bar")));
|
||||||
@ -310,6 +318,7 @@ TEST(compile_test, compile_format_string_literal) {
|
|||||||
using namespace fmt::literals;
|
using namespace fmt::literals;
|
||||||
EXPECT_EQ("", fmt::format(""_cf));
|
EXPECT_EQ("", fmt::format(""_cf));
|
||||||
EXPECT_EQ("42", fmt::format("{}"_cf, 42));
|
EXPECT_EQ("42", fmt::format("{}"_cf, 42));
|
||||||
|
EXPECT_EQ(L"42", fmt::format(L"{}"_cf, 42));
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@ -417,40 +426,6 @@ TEST(compile_time_formatting_test, custom_type) {
|
|||||||
TEST(compile_time_formatting_test, multibyte_fill) {
|
TEST(compile_time_formatting_test, multibyte_fill) {
|
||||||
EXPECT_EQ("жж42", test_format<8>(FMT_COMPILE("{:ж>4}"), 42));
|
EXPECT_EQ("жж42", test_format<8>(FMT_COMPILE("{:ж>4}"), 42));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(compile_time_formatting_test, floating_point) {
|
|
||||||
EXPECT_EQ("0", test_format<2>(FMT_COMPILE("{}"), 0.0f));
|
|
||||||
EXPECT_EQ("392.500000", test_format<11>(FMT_COMPILE("{0:f}"), 392.5f));
|
|
||||||
|
|
||||||
EXPECT_EQ("0", test_format<2>(FMT_COMPILE("{:}"), 0.0));
|
|
||||||
EXPECT_EQ("0.000000", test_format<9>(FMT_COMPILE("{:f}"), 0.0));
|
|
||||||
EXPECT_EQ("0", test_format<2>(FMT_COMPILE("{:g}"), 0.0));
|
|
||||||
EXPECT_EQ("392.65", test_format<7>(FMT_COMPILE("{:}"), 392.65));
|
|
||||||
EXPECT_EQ("392.65", test_format<7>(FMT_COMPILE("{:g}"), 392.65));
|
|
||||||
EXPECT_EQ("392.65", test_format<7>(FMT_COMPILE("{:G}"), 392.65));
|
|
||||||
EXPECT_EQ("4.9014e+06", test_format<11>(FMT_COMPILE("{:g}"), 4.9014e6));
|
|
||||||
EXPECT_EQ("-392.650000", test_format<12>(FMT_COMPILE("{:f}"), -392.65));
|
|
||||||
EXPECT_EQ("-392.650000", test_format<12>(FMT_COMPILE("{:F}"), -392.65));
|
|
||||||
|
|
||||||
EXPECT_EQ("3.926500e+02", test_format<13>(FMT_COMPILE("{0:e}"), 392.65));
|
|
||||||
EXPECT_EQ("3.926500E+02", test_format<13>(FMT_COMPILE("{0:E}"), 392.65));
|
|
||||||
EXPECT_EQ("+0000392.6", test_format<11>(FMT_COMPILE("{0:+010.4g}"), 392.65));
|
|
||||||
EXPECT_EQ("9223372036854775808.000000",
|
|
||||||
test_format<27>(FMT_COMPILE("{:f}"), 9223372036854775807.0));
|
|
||||||
|
|
||||||
constexpr double nan = std::numeric_limits<double>::quiet_NaN();
|
|
||||||
EXPECT_EQ("nan", test_format<4>(FMT_COMPILE("{}"), nan));
|
|
||||||
EXPECT_EQ("+nan", test_format<5>(FMT_COMPILE("{:+}"), nan));
|
|
||||||
if (std::signbit(-nan))
|
|
||||||
EXPECT_EQ("-nan", test_format<5>(FMT_COMPILE("{}"), -nan));
|
|
||||||
else
|
|
||||||
fmt::print("Warning: compiler doesn't handle negative NaN correctly");
|
|
||||||
|
|
||||||
constexpr double inf = std::numeric_limits<double>::infinity();
|
|
||||||
EXPECT_EQ("inf", test_format<4>(FMT_COMPILE("{}"), inf));
|
|
||||||
EXPECT_EQ("+inf", test_format<5>(FMT_COMPILE("{:+}"), inf));
|
|
||||||
EXPECT_EQ("-inf", test_format<5>(FMT_COMPILE("{}"), -inf));
|
|
||||||
}
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if FMT_USE_CONSTEXPR_STRING
|
#if FMT_USE_CONSTEXPR_STRING
|
||||||
|
|||||||
@ -2036,6 +2036,11 @@ TEST(format_test, unpacked_args) {
|
|||||||
6, 7, 8, 9, 'a', 'b', 'c', 'd', 'e', 'f', 'g'));
|
6, 7, 8, 9, 'a', 'b', 'c', 'd', 'e', 'f', 'g'));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
constexpr char with_null[3] = {'{', '}', '\0'};
|
||||||
|
constexpr char no_null[2] = {'{', '}'};
|
||||||
|
static constexpr char static_with_null[3] = {'{', '}', '\0'};
|
||||||
|
static constexpr char static_no_null[2] = {'{', '}'};
|
||||||
|
|
||||||
TEST(format_test, compile_time_string) {
|
TEST(format_test, compile_time_string) {
|
||||||
EXPECT_EQ(fmt::format(FMT_STRING("foo")), "foo");
|
EXPECT_EQ(fmt::format(FMT_STRING("foo")), "foo");
|
||||||
EXPECT_EQ(fmt::format(FMT_STRING("{}"), 42), "42");
|
EXPECT_EQ(fmt::format(FMT_STRING("{}"), 42), "42");
|
||||||
@ -2050,12 +2055,19 @@ TEST(format_test, compile_time_string) {
|
|||||||
EXPECT_EQ(fmt::format(FMT_STRING("{} {two}"), 1, "two"_a = 2), "1 2");
|
EXPECT_EQ(fmt::format(FMT_STRING("{} {two}"), 1, "two"_a = 2), "1 2");
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
static constexpr char format_str[3] = {'{', '}', '\0'};
|
(void)static_with_null;
|
||||||
(void)format_str;
|
(void)static_no_null;
|
||||||
#ifndef _MSC_VER
|
#ifndef _MSC_VER
|
||||||
EXPECT_EQ(fmt::format(FMT_STRING(format_str), 42), "42");
|
EXPECT_EQ(fmt::format(FMT_STRING(static_with_null), 42), "42");
|
||||||
|
EXPECT_EQ(fmt::format(FMT_STRING(static_no_null), 42), "42");
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
(void)with_null;
|
||||||
|
(void)no_null;
|
||||||
|
#if FMT_CPLUSPLUS >= 201703L
|
||||||
|
EXPECT_EQ(fmt::format(FMT_STRING(with_null), 42), "42");
|
||||||
|
EXPECT_EQ(fmt::format(FMT_STRING(no_null), 42), "42");
|
||||||
|
#endif
|
||||||
#if defined(FMT_USE_STRING_VIEW) && FMT_CPLUSPLUS >= 201703L
|
#if defined(FMT_USE_STRING_VIEW) && FMT_CPLUSPLUS >= 201703L
|
||||||
EXPECT_EQ(fmt::format(FMT_STRING(std::string_view("{}")), 42), "42");
|
EXPECT_EQ(fmt::format(FMT_STRING(std::string_view("{}")), 42), "42");
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@ -12,10 +12,10 @@ void invoke_inner(fmt::string_view format_str, Rep rep) {
|
|||||||
auto value = std::chrono::duration<Rep, Period>(rep);
|
auto value = std::chrono::duration<Rep, Period>(rep);
|
||||||
try {
|
try {
|
||||||
#if FMT_FUZZ_FORMAT_TO_STRING
|
#if FMT_FUZZ_FORMAT_TO_STRING
|
||||||
std::string message = fmt::format(fmt::runtime(format_str), value);
|
std::string message = fmt::format(format_str, value);
|
||||||
#else
|
#else
|
||||||
auto buf = fmt::memory_buffer();
|
auto buf = fmt::memory_buffer();
|
||||||
fmt::format_to(std::back_inserter(buf), fmt::runtime(format_str), value);
|
fmt::format_to(std::back_inserter(buf), format_str, value);
|
||||||
#endif
|
#endif
|
||||||
} catch (std::exception&) {
|
} catch (std::exception&) {
|
||||||
}
|
}
|
||||||
|
|||||||
@ -22,7 +22,7 @@
|
|||||||
#define FMT_FUZZ_SEPARATE_ALLOCATION 1
|
#define FMT_FUZZ_SEPARATE_ALLOCATION 1
|
||||||
|
|
||||||
// The size of the largest possible type in use.
|
// The size of the largest possible type in use.
|
||||||
// To let the fuzzer mutation be efficient at cross pollinating between
|
// To let the the fuzzer mutation be efficient at cross pollinating between
|
||||||
// different types, use a fixed size format. The same bit pattern, interpreted
|
// different types, use a fixed size format. The same bit pattern, interpreted
|
||||||
// as another type, is likely interesting.
|
// as another type, is likely interesting.
|
||||||
constexpr auto fixed_size = 16;
|
constexpr auto fixed_size = 16;
|
||||||
|
|||||||
@ -265,7 +265,7 @@ template <> struct formatter<abstract> : ostream_formatter {};
|
|||||||
} // namespace fmt
|
} // namespace fmt
|
||||||
|
|
||||||
void format_abstract_compiles(const abstract& a) {
|
void format_abstract_compiles(const abstract& a) {
|
||||||
(void)fmt::format(FMT_COMPILE("{}"), a);
|
fmt::format(FMT_COMPILE("{}"), a);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(ostream_test, is_formattable) {
|
TEST(ostream_test, is_formattable) {
|
||||||
|
|||||||
@ -39,12 +39,13 @@ TEST(std_test, path) {
|
|||||||
EXPECT_EQ(fmt::format("{}", path(L"\x0428\x0447\x0443\x0447\x044B\x043D\x0448"
|
EXPECT_EQ(fmt::format("{}", path(L"\x0428\x0447\x0443\x0447\x044B\x043D\x0448"
|
||||||
L"\x0447\x044B\x043D\x0430")),
|
L"\x0447\x044B\x043D\x0430")),
|
||||||
"Шчучыншчына");
|
"Шчучыншчына");
|
||||||
EXPECT_EQ(fmt::format("{}", path(L"\xD800")), "\xED\xA0\x80");
|
EXPECT_EQ(fmt::format("{}", path(L"\xd800")), "<EFBFBD>");
|
||||||
EXPECT_EQ(fmt::format("{}", path(L"[\xD800]")), "[\xED\xA0\x80]");
|
EXPECT_EQ(fmt::format("{}", path(L"HEAD \xd800 TAIL")), "HEAD <20> TAIL");
|
||||||
EXPECT_EQ(fmt::format("{}", path(L"[\xD83D\xDE00]")), "[\xF0\x9F\x98\x80]");
|
EXPECT_EQ(fmt::format("{}", path(L"HEAD \xD83D\xDE00 TAIL")),
|
||||||
EXPECT_EQ(fmt::format("{}", path(L"[\xD83D\xD83D\xDE00]")),
|
"HEAD \xF0\x9F\x98\x80 TAIL");
|
||||||
"[\xED\xA0\xBD\xF0\x9F\x98\x80]");
|
EXPECT_EQ(fmt::format("{}", path(L"HEAD \xD83D\xD83D\xDE00 TAIL")),
|
||||||
EXPECT_EQ(fmt::format("{:?}", path(L"\xD800")), "\"\\ud800\"");
|
"HEAD <20>\xF0\x9F\x98\x80 TAIL");
|
||||||
|
EXPECT_EQ(fmt::format("{:?}", path(L"\xd800")), "\"\\ud800\"");
|
||||||
# endif
|
# endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user