mirror of
https://github.com/fmtlib/fmt.git
synced 2025-12-23 15:28:27 +01:00
Compare commits
44 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
91d1aced0a | ||
|
|
2e819a11f2 | ||
|
|
7bce22571a | ||
|
|
ec73fb7247 | ||
|
|
3269c1cea5 | ||
|
|
789aa69e0a | ||
|
|
2a50a0d808 | ||
|
|
02d6f3d9e4 | ||
|
|
19e4d5ecd9 | ||
|
|
a6e871e39b | ||
|
|
e137db699a | ||
|
|
d6712ff2c0 | ||
|
|
2d839bbc61 | ||
|
|
5bc56e24a9 | ||
|
|
2727215c11 | ||
|
|
790b9389ae | ||
|
|
a731c73fd5 | ||
|
|
3391f9e992 | ||
|
|
9f197b22ae | ||
|
|
f20b16617e | ||
|
|
14451704d5 | ||
|
|
7a0da1c68a | ||
|
|
e3d2174b3c | ||
|
|
a6fb4d3b06 | ||
|
|
706fecea30 | ||
|
|
e00fd756cc | ||
|
|
fc17e825d9 | ||
|
|
3fccfb8a80 | ||
|
|
690c9c71a0 | ||
|
|
1122268510 | ||
|
|
a4c7e17133 | ||
|
|
bfd0129b91 | ||
|
|
62f57b2496 | ||
|
|
ef7a566413 | ||
|
|
42840cb415 | ||
|
|
5ac44cd128 | ||
|
|
23c13b3060 | ||
|
|
29c46fb82d | ||
|
|
a0571f3f59 | ||
|
|
a195dd6b37 | ||
|
|
33ad559eb8 | ||
|
|
b6cd356196 | ||
|
|
c3be070b7e | ||
|
|
27bf8b47fe |
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@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0
|
||||
uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0
|
||||
if: failure() && steps.build.outcome == 'success'
|
||||
with:
|
||||
name: artifacts
|
||||
|
||||
4
.github/workflows/doc.yml
vendored
4
.github/workflows/doc.yml
vendored
@@ -10,7 +10,7 @@ jobs:
|
||||
runs-on: ubuntu-22.04
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||
- uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
|
||||
|
||||
- name: Add Ubuntu mirrors
|
||||
run: |
|
||||
@@ -24,7 +24,7 @@ jobs:
|
||||
run: |
|
||||
sudo apt update
|
||||
sudo apt install doxygen
|
||||
pip install mkdocs-material==9.5.25 mkdocstrings==0.26.1 mike==2.1.1
|
||||
pip install mkdocs-material==9.7.0 mkdocstrings==1.0.0 mike==2.1.3 typing_extensions==4.15.0
|
||||
cmake -E make_directory ${{runner.workspace}}/build
|
||||
# Workaround https://github.com/actions/checkout/issues/13:
|
||||
git config --global user.name "$(git --no-pager log --format=format:'%an' -n 1)"
|
||||
|
||||
8
.github/workflows/lint.yml
vendored
8
.github/workflows/lint.yml
vendored
@@ -13,16 +13,16 @@ jobs:
|
||||
format_code:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||
- uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
|
||||
|
||||
- name: Install clang-format
|
||||
run: |
|
||||
wget https://apt.llvm.org/llvm.sh
|
||||
sudo bash ./llvm.sh 17
|
||||
sudo apt install clang-format-17
|
||||
sudo bash ./llvm.sh 21
|
||||
sudo apt install clang-format-21
|
||||
|
||||
- name: Run clang-format
|
||||
run: |
|
||||
find include src -name '*.h' -o -name '*.cc' | \
|
||||
xargs clang-format-17 -i -style=file -fallback-style=none
|
||||
xargs clang-format-21 -i -style=file -fallback-style=none
|
||||
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
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||
- uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
|
||||
|
||||
- name: Set timezone
|
||||
run: sudo timedatectl set-timezone 'Europe/Kyiv'
|
||||
|
||||
9
.github/workflows/macos.yml
vendored
9
.github/workflows/macos.yml
vendored
@@ -9,13 +9,10 @@ jobs:
|
||||
build:
|
||||
strategy:
|
||||
matrix:
|
||||
os: [macos-13, macos-14]
|
||||
os: [macos-14]
|
||||
build_type: [Debug, Release]
|
||||
std: [11, 17, 20]
|
||||
std: [11, 17, 20, 23]
|
||||
shared: [""]
|
||||
exclude:
|
||||
- { os: macos-13, std: 11 }
|
||||
- { os: macos-13, std: 17 }
|
||||
include:
|
||||
- os: macos-14
|
||||
std: 23
|
||||
@@ -25,7 +22,7 @@ jobs:
|
||||
runs-on: '${{ matrix.os }}'
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||
- uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
|
||||
|
||||
- name: Set timezone
|
||||
run: sudo systemsetup -settimezone 'Europe/Minsk'
|
||||
|
||||
6
.github/workflows/scorecard.yml
vendored
6
.github/workflows/scorecard.yml
vendored
@@ -29,7 +29,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: "Checkout code"
|
||||
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||
uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
@@ -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@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0
|
||||
uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.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@3599b3baa15b485a2e49ef411a7a4bb2452e7f93 # v3.29.5
|
||||
uses: github/codeql-action/upload-sarif@0499de31b99561a6d14a36a5f662c2a54f91beee # v3.29.5
|
||||
with:
|
||||
sarif_file: results.sarif
|
||||
|
||||
4
.github/workflows/windows.yml
vendored
4
.github/workflows/windows.yml
vendored
@@ -32,7 +32,7 @@ jobs:
|
||||
standard: 20
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||
- uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
|
||||
|
||||
- name: Set timezone
|
||||
run: tzutil /s "FLE Standard Time"
|
||||
@@ -79,7 +79,7 @@ jobs:
|
||||
release: false
|
||||
msystem: ${{matrix.sys}}
|
||||
pacboy: cc:p cmake:p ninja:p lld:p
|
||||
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||
- uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
|
||||
- name: Configure
|
||||
run: cmake -B ../build -DBUILD_SHARED_LIBS=ON -DCMAKE_BUILD_TYPE=Debug
|
||||
env: { LDFLAGS: -fuse-ld=lld }
|
||||
|
||||
7
LICENSE
7
LICENSE
@@ -18,10 +18,3 @@ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
--- Optional exception to the license ---
|
||||
|
||||
As an exception, if, as a result of your compiling your source code, portions
|
||||
of this Software are embedded into a machine-executable object form of such
|
||||
source code, you may redistribute such embedded portions in such object form
|
||||
without including the above copyright and permission notices.
|
||||
|
||||
48
README.md
48
README.md
@@ -12,7 +12,7 @@
|
||||
alternative to C stdio and C++ iostreams.
|
||||
|
||||
If you like this project, please consider donating to one of the funds
|
||||
that help victims of the war in Ukraine: <https://www.stopputin.net/>.
|
||||
that help victims of the war in Ukraine: <https://u24.gov.ua/>.
|
||||
|
||||
[Documentation](https://fmt.dev)
|
||||
|
||||
@@ -150,8 +150,8 @@ int main() {
|
||||
}
|
||||
```
|
||||
|
||||
This can be [5 to 9 times faster than
|
||||
fprintf](http://www.zverovich.net/2020/08/04/optimal-file-buffer-size.html).
|
||||
This can be [up to 9 times faster than `fprintf`](
|
||||
http://www.zverovich.net/2020/08/04/optimal-file-buffer-size.html).
|
||||
|
||||
**Print with colors and text styles**
|
||||
|
||||
@@ -178,17 +178,17 @@ Output on a modern terminal with Unicode support:
|
||||
|
||||
| Library | Method | Run Time, s |
|
||||
|-------------------|---------------|-------------|
|
||||
| libc | printf | 0.91 |
|
||||
| libc++ | std::ostream | 2.49 |
|
||||
| {fmt} 9.1 | fmt::print | 0.74 |
|
||||
| Boost Format 1.80 | boost::format | 6.26 |
|
||||
| Folly Format | folly::format | 1.87 |
|
||||
| libc | printf | 0.66 |
|
||||
| libc++ | std::ostream | 1.63 |
|
||||
| {fmt} 12.1 | fmt::print | 0.44 |
|
||||
| Boost Format 1.88 | boost::format | 3.89 |
|
||||
| Folly Format | folly::format | 1.28 |
|
||||
|
||||
{fmt} is the fastest of the benchmarked methods, \~20% faster than
|
||||
{fmt} is the fastest of the benchmarked methods, \~50% faster than
|
||||
`printf`.
|
||||
|
||||
The above results were generated by building `tinyformat_test.cpp` on
|
||||
macOS 12.6.1 with `clang++ -O3 -DNDEBUG -DSPEED_TEST -DHAVE_FORMAT`, and
|
||||
macOS 15.6.1 with `clang++ -O3 -DNDEBUG -DSPEED_TEST -DHAVE_FORMAT`, and
|
||||
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
|
||||
times with output sent to `/dev/null`; for further details refer to the
|
||||
@@ -216,26 +216,26 @@ in the following tables.
|
||||
|
||||
**Optimized build (-O3)**
|
||||
|
||||
| Method | Compile Time, s | Executable size, KiB | Stripped size, KiB |
|
||||
|---------------|-----------------|----------------------|--------------------|
|
||||
| printf | 1.6 | 54 | 50 |
|
||||
| IOStreams | 25.9 | 98 | 84 |
|
||||
| fmt 83652df | 4.8 | 54 | 50 |
|
||||
| tinyformat | 29.1 | 161 | 136 |
|
||||
| Boost Format | 55.0 | 530 | 317 |
|
||||
| Method | Compile Time, s | Executable size, KiB | Stripped size, KiB |
|
||||
|-----------------|-----------------|----------------------|--------------------|
|
||||
| printf | 1.6 | 54 | 50 |
|
||||
| IOStreams | 28.4 | 98 | 84 |
|
||||
| {fmt} `1122268` | 5.0 | 54 | 50 |
|
||||
| tinyformat | 32.6 | 164 | 136 |
|
||||
| Boost Format | 55.0 | 530 | 317 |
|
||||
|
||||
{fmt} is fast to compile and is comparable to `printf` in terms of per-call
|
||||
binary size (within a rounding error on this system).
|
||||
|
||||
**Non-optimized build**
|
||||
|
||||
| Method | Compile Time, s | Executable size, KiB | Stripped size, KiB |
|
||||
|---------------|-----------------|----------------------|--------------------|
|
||||
| printf | 1.4 | 54 | 50 |
|
||||
| IOStreams | 23.4 | 92 | 68 |
|
||||
| {fmt} 83652df | 4.4 | 89 | 85 |
|
||||
| tinyformat | 24.5 | 204 | 161 |
|
||||
| Boost Format | 36.4 | 831 | 462 |
|
||||
| Method | Compile Time, s | Executable size, KiB | Stripped size, KiB |
|
||||
|-----------------|-----------------|----------------------|--------------------|
|
||||
| printf | 1.4 | 54 | 50 |
|
||||
| IOStreams | 27.0 | 88 | 68 |
|
||||
| {fmt} `1122268` | 4.7 | 87 | 84 |
|
||||
| tinyformat | 28.1 | 185 | 145 |
|
||||
| Boost Format | 38.9 | 678 | 381 |
|
||||
|
||||
`libc`, `lib(std)c++`, and `libfmt` are all linked as shared libraries
|
||||
to compare formatting function overhead only. Boost Format is a
|
||||
|
||||
27
doc/LICENSE-exception
Normal file
27
doc/LICENSE-exception
Normal file
@@ -0,0 +1,27 @@
|
||||
Copyright (c) 2012 - present, Victor Zverovich and {fmt} contributors
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
"Software"), to deal in the Software without restriction, including
|
||||
without limitation the rights to use, copy, modify, merge, publish,
|
||||
distribute, sublicense, and/or sell copies of the Software, and to
|
||||
permit persons to whom the Software is furnished to do so, subject to
|
||||
the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
--- Optional exception to the license ---
|
||||
|
||||
As an exception, if, as a result of your compiling your source code, portions
|
||||
of this Software are embedded into a machine-executable object form of such
|
||||
source code, you may redistribute such embedded portions in such object form
|
||||
without including the above copyright and permission notices.
|
||||
@@ -321,8 +321,6 @@ parameterized version.
|
||||
|
||||
::: arg(const Char*, const T&)
|
||||
|
||||
Named arguments are not supported in compile-time checks at the moment.
|
||||
|
||||
### Compatibility
|
||||
|
||||
::: basic_string_view
|
||||
@@ -624,6 +622,8 @@ Example:
|
||||
|
||||
::: ostream
|
||||
|
||||
::: output_file(cstring_view, T...)
|
||||
|
||||
::: windows_error
|
||||
|
||||
<a id="ostream-api"></a>
|
||||
@@ -706,7 +706,7 @@ following differences:
|
||||
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
|
||||
(ignoring redundant digits and sign in exponent) and may produce more
|
||||
decimal digits than necessary.
|
||||
|
||||
## Configuration Options
|
||||
@@ -746,7 +746,7 @@ configuring CMake.
|
||||
- `0` - off (default)
|
||||
- `1` - disables locale support and applies some optimizations
|
||||
- `2` - disables some Unicode features, named arguments and applies more
|
||||
aggresive optimizations
|
||||
aggressive optimizations
|
||||
|
||||
### Binary Size Optimization
|
||||
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
margin-left: 1em;
|
||||
}
|
||||
|
||||
code,
|
||||
pre > code.decl {
|
||||
white-space: pre-wrap;
|
||||
}
|
||||
|
||||
@@ -76,7 +76,7 @@ hide:
|
||||
<p>
|
||||
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
|
||||
standard libary.
|
||||
standard library.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
#endif
|
||||
|
||||
// The fmt library version in the form major * 10000 + minor * 100 + patch.
|
||||
#define FMT_VERSION 120100
|
||||
#define FMT_VERSION 120101
|
||||
|
||||
// Detect compiler versions.
|
||||
#if defined(__clang__) && !defined(__ibmxl__)
|
||||
@@ -233,7 +233,6 @@
|
||||
FMT_PRAGMA_GCC(push_options)
|
||||
#if !defined(__OPTIMIZE__) && !defined(__CUDACC__) && !defined(FMT_MODULE)
|
||||
FMT_PRAGMA_GCC(optimize("Og"))
|
||||
# define FMT_GCC_OPTIMIZED
|
||||
#endif
|
||||
FMT_PRAGMA_CLANG(diagnostic push)
|
||||
FMT_PRAGMA_GCC(diagnostic push)
|
||||
@@ -246,7 +245,7 @@ FMT_PRAGMA_GCC(diagnostic push)
|
||||
# define FMT_ALWAYS_INLINE inline
|
||||
#endif
|
||||
// A version of FMT_ALWAYS_INLINE to prevent code bloat in debug mode.
|
||||
#if defined(NDEBUG) || defined(FMT_GCC_OPTIMIZED)
|
||||
#ifdef NDEBUG
|
||||
# define FMT_INLINE FMT_ALWAYS_INLINE
|
||||
#else
|
||||
# define FMT_INLINE inline
|
||||
@@ -927,10 +926,13 @@ class locale_ref {
|
||||
template <typename Locale, FMT_ENABLE_IF(sizeof(Locale::collate) != 0)>
|
||||
locale_ref(const Locale& loc) : locale_(&loc) {
|
||||
// Check if std::isalpha is found via ADL to reduce the chance of misuse.
|
||||
isalpha('x', loc);
|
||||
detail::ignore_unused(sizeof(isalpha('x', loc)));
|
||||
}
|
||||
|
||||
inline explicit operator bool() const noexcept { return locale_ != nullptr; }
|
||||
#else
|
||||
public:
|
||||
inline explicit operator bool() const noexcept { return false; }
|
||||
#endif // FMT_USE_LOCALE
|
||||
|
||||
public:
|
||||
@@ -1851,8 +1853,7 @@ template <typename T> class buffer {
|
||||
#if !FMT_MSC_VERSION || FMT_MSC_VERSION >= 1940
|
||||
FMT_CONSTEXPR20
|
||||
#endif
|
||||
void
|
||||
append(const U* begin, const U* end) {
|
||||
void append(const U* begin, const U* end) {
|
||||
while (begin != end) {
|
||||
auto size = size_;
|
||||
auto free_cap = capacity_ - size;
|
||||
@@ -2260,8 +2261,11 @@ template <typename Context> class value {
|
||||
: pointer(const_cast<const void*>(x)) {}
|
||||
FMT_INLINE value(nullptr_t) : pointer(nullptr) {}
|
||||
|
||||
template <typename T, FMT_ENABLE_IF(std::is_pointer<T>::value ||
|
||||
std::is_member_pointer<T>::value)>
|
||||
template <typename T,
|
||||
FMT_ENABLE_IF(
|
||||
(std::is_pointer<T>::value ||
|
||||
std::is_member_pointer<T>::value) &&
|
||||
!std::is_void<typename std::remove_pointer<T>::type>::value)>
|
||||
value(const T&) {
|
||||
// Formatting of arbitrary pointers is disallowed. If you want to format a
|
||||
// pointer cast it to `void*` or `const void*`. In particular, this forbids
|
||||
@@ -2306,7 +2310,7 @@ template <typename Context> class value {
|
||||
template <typename T, FMT_ENABLE_IF(!has_formatter<T, char_type>())>
|
||||
FMT_CONSTEXPR value(const T&, custom_tag) {
|
||||
// Cannot format an argument; to make type T formattable provide a
|
||||
// formatter<T> specialization: https://fmt.dev/latest/api.html#udt.
|
||||
// formatter<T> specialization: https://fmt.dev/latest/api#udt.
|
||||
type_is_unformattable_for<T, char_type> _;
|
||||
}
|
||||
|
||||
@@ -2756,7 +2760,9 @@ template <typename... T> struct fstring {
|
||||
static_assert(count<(is_view<remove_cvref_t<T>>::value &&
|
||||
std::is_reference<T>::value)...>() == 0,
|
||||
"passing views as lvalues is disallowed");
|
||||
if (FMT_USE_CONSTEVAL) parse_format_string<char>(s, checker(s, arg_pack()));
|
||||
#if FMT_USE_CONSTEVAL
|
||||
parse_format_string<char>(s, checker(s, arg_pack()));
|
||||
#endif
|
||||
#ifdef FMT_ENFORCE_COMPILE_STRING
|
||||
static_assert(
|
||||
FMT_USE_CONSTEVAL && sizeof(s) != 0,
|
||||
@@ -2841,6 +2847,10 @@ using vargs =
|
||||
* **Example**:
|
||||
*
|
||||
* fmt::print("The answer is {answer}.", fmt::arg("answer", 42));
|
||||
*
|
||||
* Named arguments passed with `fmt::arg` are not supported
|
||||
* in compile-time checks, but `"answer"_a=42` are compile-time checked in
|
||||
* sufficiently new compilers. See `operator""_a()`.
|
||||
*/
|
||||
template <typename Char, typename T>
|
||||
inline auto arg(const Char* name, const T& arg) -> detail::named_arg<Char, T> {
|
||||
|
||||
@@ -155,7 +155,7 @@ enum class color : uint32_t {
|
||||
white_smoke = 0xF5F5F5, // rgb(245,245,245)
|
||||
yellow = 0xFFFF00, // rgb(255,255,0)
|
||||
yellow_green = 0x9ACD32 // rgb(154,205,50)
|
||||
}; // enum class color
|
||||
}; // enum class color
|
||||
|
||||
enum class terminal_color : uint8_t {
|
||||
black = 30,
|
||||
|
||||
@@ -522,7 +522,7 @@ auto format_to_n(OutputIt out, size_t n, const S& fmt, T&&... args)
|
||||
-> format_to_n_result<OutputIt> {
|
||||
using traits = detail::fixed_buffer_traits;
|
||||
auto buf = detail::iterator_buffer<OutputIt, char, traits>(out, n);
|
||||
fmt::format_to(std::back_inserter(buf), fmt, std::forward<T>(args)...);
|
||||
fmt::format_to(appender(buf), fmt, std::forward<T>(args)...);
|
||||
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';
|
||||
}
|
||||
|
||||
auto str() const -> fmt::string_view { return {data, N - 1}; }
|
||||
auto c_str() const -> const char* { return data; }
|
||||
FMT_CONSTEXPR auto str() const -> fmt::string_view { return {data, N - 1}; }
|
||||
FMT_CONSTEXPR auto c_str() const -> const char* { return data; }
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -493,8 +493,8 @@ template <typename OutputIt,
|
||||
#if FMT_CLANG_VERSION >= 307 && !FMT_ICC_VERSION
|
||||
__attribute__((no_sanitize("undefined")))
|
||||
#endif
|
||||
FMT_CONSTEXPR20 inline auto
|
||||
reserve(OutputIt it, size_t n) -> typename OutputIt::value_type* {
|
||||
FMT_CONSTEXPR20 inline auto reserve(OutputIt it, size_t n) ->
|
||||
typename OutputIt::value_type* {
|
||||
auto& c = get_container(it);
|
||||
size_t size = c.size();
|
||||
c.resize(size + n);
|
||||
@@ -736,12 +736,8 @@ using fast_float_t = conditional_t<sizeof(T) == sizeof(double), double, float>;
|
||||
template <typename T>
|
||||
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++
|
||||
// standard libary runtime. std::decay is used for back_inserter to be found by
|
||||
// standard library runtime. std::decay is used for back_inserter to be found by
|
||||
// ADL when applied to memory_buffer.
|
||||
template <typename T> struct allocator : private std::decay<void> {
|
||||
using value_type = T;
|
||||
@@ -1031,9 +1027,9 @@ using uint64_or_128_t = conditional_t<num_bits<T>() <= 64, uint64_t, uint128_t>;
|
||||
(factor) * 100000, (factor) * 1000000, (factor) * 10000000, \
|
||||
(factor) * 100000000, (factor) * 1000000000
|
||||
|
||||
// Converts value in the range [0, 100) to a string.
|
||||
// GCC generates slightly better code when value is pointer-size.
|
||||
inline auto digits2(size_t value) -> const char* {
|
||||
// Converts value in the range [0, 100) to a string. GCC generates a bit better
|
||||
// code when value is pointer-size (https://www.godbolt.org/z/5fEPMT1cc).
|
||||
inline auto digits2(size_t value) noexcept -> const char* {
|
||||
// Align data since unaligned access may be slower when crossing a
|
||||
// hardware-specific boundary.
|
||||
alignas(2) static const char data[] =
|
||||
@@ -1311,7 +1307,13 @@ class utf8_to_utf16 {
|
||||
inline auto str() const -> std::wstring { return {&buffer_[0], size()}; }
|
||||
};
|
||||
|
||||
enum class to_utf8_error_policy { abort, replace };
|
||||
enum class to_utf8_error_policy { abort, replace, wtf };
|
||||
|
||||
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.
|
||||
template <typename WChar, typename Buffer = memory_buffer> class to_utf8 {
|
||||
@@ -1353,8 +1355,13 @@ template <typename WChar, typename Buffer = memory_buffer> class to_utf8 {
|
||||
// Handle a surrogate pair.
|
||||
++p;
|
||||
if (p == s.end() || (c & 0xfc00) != 0xd800 || (*p & 0xfc00) != 0xdc00) {
|
||||
if (policy == to_utf8_error_policy::abort) return false;
|
||||
buf.append(string_view("\xEF\xBF\xBD"));
|
||||
switch (policy) {
|
||||
case to_utf8_error_policy::abort: return false;
|
||||
case to_utf8_error_policy::replace:
|
||||
buf.append(string_view("\xEF\xBF\xBD"));
|
||||
break;
|
||||
case to_utf8_error_policy::wtf: to_utf8_3bytes(buf, c); break;
|
||||
}
|
||||
--p;
|
||||
continue;
|
||||
}
|
||||
@@ -1366,9 +1373,7 @@ 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>(0x80 | (c & 0x3f)));
|
||||
} else if ((c >= 0x800 && c <= 0xd7ff) || (c >= 0xe000 && c <= 0xffff)) {
|
||||
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)));
|
||||
to_utf8_3bytes(buf, c);
|
||||
} else if (c >= 0x10000 && c <= 0x10ffff) {
|
||||
buf.push_back(static_cast<char>(0xf0 | (c >> 18)));
|
||||
buf.push_back(static_cast<char>(0x80 | ((c & 0x3ffff) >> 12)));
|
||||
@@ -2534,7 +2539,7 @@ FMT_CONSTEXPR20 auto write_fixed(OutputIt out, const DecimalFP& f,
|
||||
auto grouping = Grouping(loc, specs.localized());
|
||||
size += grouping.count_separators(exp);
|
||||
return write_padded<Char, align::right>(
|
||||
out, specs, to_unsigned(size), [&](iterator it) {
|
||||
out, specs, static_cast<size_t>(size), [&](iterator it) {
|
||||
if (s != sign::none) *it++ = detail::getsign<Char>(s);
|
||||
it = write_significand(it, f.significand, significand_size, exp,
|
||||
decimal_point, grouping);
|
||||
@@ -2550,7 +2555,7 @@ FMT_CONSTEXPR20 auto write_fixed(OutputIt out, const DecimalFP& f,
|
||||
bool pointy = num_zeros != 0 || significand_size != 0 || specs.alt();
|
||||
size += 1 + (pointy ? 1 : 0) + num_zeros;
|
||||
return write_padded<Char, align::right>(
|
||||
out, specs, to_unsigned(size), [&](iterator it) {
|
||||
out, specs, static_cast<size_t>(size), [&](iterator it) {
|
||||
if (s != sign::none) *it++ = detail::getsign<Char>(s);
|
||||
*it++ = Char('0');
|
||||
if (!pointy) return it;
|
||||
@@ -2594,7 +2599,7 @@ FMT_CONSTEXPR20 auto do_write_float(OutputIt out, const DecimalFP& f,
|
||||
*it++ = Char(exp_char);
|
||||
return write_exponent<Char>(exp, it);
|
||||
};
|
||||
auto usize = to_unsigned(size);
|
||||
size_t usize = static_cast<size_t>(size);
|
||||
return specs.width > 0
|
||||
? write_padded<Char, align::right>(out, specs, usize, write)
|
||||
: base_iterator(out, write(reserve(out, usize)));
|
||||
@@ -4142,6 +4147,14 @@ template <typename T, typename Char = char> struct nested_formatter {
|
||||
|
||||
inline namespace literals {
|
||||
#if FMT_USE_NONTYPE_TEMPLATE_ARGS
|
||||
/**
|
||||
* User-defined literal equivalent of `fmt::arg`, but with compile-time checks.
|
||||
*
|
||||
* **Example**:
|
||||
*
|
||||
* using namespace fmt::literals;
|
||||
* fmt::print("The answer is {answer}.", "answer"_a=42);
|
||||
*/
|
||||
template <detail::fixed_string S> constexpr auto operator""_a() {
|
||||
using char_t = remove_cvref_t<decltype(*S.data)>;
|
||||
return detail::udl_arg<char_t, sizeof(S.data) / sizeof(char_t), S>();
|
||||
@@ -4248,7 +4261,11 @@ class format_int {
|
||||
* // A compile-time error because 'd' is an invalid specifier for strings.
|
||||
* std::string s = fmt::format(FMT_STRING("{:d}"), "foo");
|
||||
*/
|
||||
#define FMT_STRING(s) FMT_STRING_IMPL(s, fmt::detail::compile_string)
|
||||
#if FMT_USE_CONSTEVAL
|
||||
# define FMT_STRING(s) s
|
||||
#else
|
||||
# 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)
|
||||
-> std::system_error;
|
||||
|
||||
@@ -161,14 +161,6 @@ inline auto system_category() noexcept -> const std::error_category& {
|
||||
}
|
||||
#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.
|
||||
class buffered_file {
|
||||
private:
|
||||
|
||||
@@ -38,7 +38,7 @@ namespace detail {
|
||||
namespace {
|
||||
struct file_access_tag {};
|
||||
} // namespace
|
||||
template <typename Tag, typename BufType, FILE* BufType::*FileMemberPtr>
|
||||
template <typename Tag, typename BufType, FILE* BufType::* FileMemberPtr>
|
||||
class file_access {
|
||||
friend auto get_file(BufType& obj) -> FILE* { return obj.*FileMemberPtr; }
|
||||
};
|
||||
|
||||
@@ -84,10 +84,12 @@ namespace detail {
|
||||
template <typename Char, typename PathChar>
|
||||
auto get_path_string(const std::filesystem::path& p,
|
||||
const std::basic_string<PathChar>& native) {
|
||||
if constexpr (std::is_same_v<Char, char> && std::is_same_v<PathChar, wchar_t>)
|
||||
return to_utf8<wchar_t>(native, to_utf8_error_policy::replace);
|
||||
else
|
||||
if constexpr (std::is_same_v<Char, char> &&
|
||||
std::is_same_v<PathChar, wchar_t>) {
|
||||
return to_utf8<wchar_t>(native, to_utf8_error_policy::wtf);
|
||||
} else {
|
||||
return p.string<Char>();
|
||||
}
|
||||
}
|
||||
|
||||
template <typename Char, typename PathChar>
|
||||
@@ -645,6 +647,11 @@ struct formatter<std::atomic_flag, Char> : formatter<bool, Char> {
|
||||
};
|
||||
#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> {
|
||||
private:
|
||||
detail::dynamic_format_specs<Char> specs_;
|
||||
|
||||
@@ -2,190 +2,274 @@
|
||||
# Copyright (c) 2012 - present, Victor Zverovich
|
||||
# https://github.com/fmtlib/fmt/blob/master/LICENSE
|
||||
|
||||
# pyright: strict
|
||||
|
||||
import os
|
||||
import xml.etree.ElementTree as ElementTree
|
||||
import xml.etree.ElementTree as ET
|
||||
from pathlib import Path
|
||||
from subprocess import PIPE, STDOUT, CalledProcessError, Popen
|
||||
from typing import Any, List, Mapping, Optional
|
||||
|
||||
from mkdocstrings.handlers.base import BaseHandler
|
||||
from markupsafe import Markup
|
||||
from mkdocstrings import BaseHandler
|
||||
from typing_extensions import TYPE_CHECKING, Any, ClassVar, final, override
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from collections.abc import Mapping, MutableMapping
|
||||
|
||||
from mkdocs.config.defaults import MkDocsConfig
|
||||
from mkdocstrings import CollectorItem, HandlerOptions
|
||||
|
||||
|
||||
@final
|
||||
class Definition:
|
||||
"""A definition extracted by Doxygen."""
|
||||
|
||||
def __init__(self, name: str, kind: Optional[str] = None,
|
||||
node: Optional[ElementTree.Element] = None,
|
||||
is_member: bool = False):
|
||||
def __init__(
|
||||
self,
|
||||
name: str,
|
||||
kind: "str | None" = None,
|
||||
node: "ET.Element | None" = None,
|
||||
is_member: bool = False,
|
||||
):
|
||||
self.name = name
|
||||
self.kind = kind if kind is not None else node.get('kind')
|
||||
self.desc = None
|
||||
self.id = name if not is_member else None
|
||||
self.members = None
|
||||
self.params = None
|
||||
self.template_params = None
|
||||
self.trailing_return_type = None
|
||||
self.type = None
|
||||
self.kind: "str | None" = None
|
||||
if kind is not None:
|
||||
self.kind = kind
|
||||
elif node is not None:
|
||||
self.kind = node.get("kind")
|
||||
self.desc: "list[ET.Element[str]] | None" = None
|
||||
self.id: "str | None" = name if not is_member else None
|
||||
self.members: "list[Definition] | None" = None
|
||||
self.params: "list[Definition] | None" = None
|
||||
self.template_params: "list[Definition] | None" = None
|
||||
self.trailing_return_type: "str | None" = None
|
||||
self.type: "str | None" = None
|
||||
|
||||
|
||||
# A map from Doxygen to HTML tags.
|
||||
tag_map = {
|
||||
'bold': 'b',
|
||||
'emphasis': 'em',
|
||||
'computeroutput': 'code',
|
||||
'para': 'p',
|
||||
'programlisting': 'pre',
|
||||
'verbatim': 'pre'
|
||||
"bold": "b",
|
||||
"emphasis": "em",
|
||||
"computeroutput": "code",
|
||||
"para": "p",
|
||||
"itemizedlist": "ul",
|
||||
"listitem": "li",
|
||||
}
|
||||
|
||||
# A map from Doxygen tags to text.
|
||||
tag_text_map = {
|
||||
'codeline': '',
|
||||
'highlight': '',
|
||||
'sp': ' '
|
||||
}
|
||||
tag_text_map = {"codeline": "", "highlight": "", "sp": " "}
|
||||
|
||||
|
||||
def escape_html(s: str) -> str:
|
||||
return s.replace("<", "<")
|
||||
|
||||
|
||||
def doxyxml2html(nodes: List[ElementTree.Element]):
|
||||
out = ''
|
||||
for n in nodes:
|
||||
tag = tag_map.get(n.tag)
|
||||
if not 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
|
||||
# Converts a node from doxygen to HTML format.
|
||||
def convert_node(
|
||||
node: ET.Element, tag: str, attrs: "Mapping[str, str] | None" = None
|
||||
) -> str:
|
||||
if attrs is None:
|
||||
attrs = {}
|
||||
|
||||
out: str = "<" + 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 convert_template_params(node: ElementTree.Element) -> Optional[List[Definition]]:
|
||||
template_param_list = node.find('templateparamlist')
|
||||
def doxyxml2html(nodes: "list[ET.Element]"):
|
||||
out = ""
|
||||
for n in nodes:
|
||||
tag = tag_map.get(n.tag)
|
||||
if 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]
|
||||
return out
|
||||
|
||||
|
||||
def convert_template_params(node: ET.Element) -> "list[Definition] | None":
|
||||
template_param_list = node.find("templateparamlist")
|
||||
if template_param_list is None:
|
||||
return None
|
||||
params = []
|
||||
for param_node in template_param_list.findall('param'):
|
||||
name = param_node.find('declname')
|
||||
param = Definition(name.text if name is not None else '', 'param')
|
||||
param.type = param_node.find('type').text
|
||||
params: "list[Definition]" = []
|
||||
for param_node in template_param_list.findall("param"):
|
||||
name = param_node.find("declname")
|
||||
if name is not None:
|
||||
name = name.text
|
||||
if name is None:
|
||||
name = ""
|
||||
param = Definition(name, "param")
|
||||
param_type = param_node.find("type")
|
||||
if param_type is not None:
|
||||
param.type = param_type.text
|
||||
params.append(param)
|
||||
return params
|
||||
|
||||
|
||||
def get_description(node: ElementTree.Element) -> List[ElementTree.Element]:
|
||||
return node.findall('briefdescription/para') + \
|
||||
node.findall('detaileddescription/para')
|
||||
def get_description(node: ET.Element) -> list[ET.Element]:
|
||||
return node.findall("briefdescription/para") + node.findall(
|
||||
"detaileddescription/para"
|
||||
)
|
||||
|
||||
|
||||
def normalize_type(type_: str) -> str:
|
||||
type_ = type_.replace('< ', '<').replace(' >', '>')
|
||||
return type_.replace(' &', '&').replace(' *', '*')
|
||||
type_ = type_.replace("< ", "<").replace(" >", ">")
|
||||
return type_.replace(" &", "&").replace(" *", "*")
|
||||
|
||||
|
||||
def convert_type(type_: ElementTree.Element) -> Optional[str]:
|
||||
def convert_type(type_: "ET.Element | None") -> "str | None":
|
||||
if type_ is None:
|
||||
return None
|
||||
result = type_.text if type_.text else ''
|
||||
result = type_.text if type_.text else ""
|
||||
for ref in type_:
|
||||
if ref.text is None:
|
||||
raise ValueError
|
||||
result += ref.text
|
||||
if ref.tail:
|
||||
result += ref.tail
|
||||
if type_.tail is None:
|
||||
raise ValueError
|
||||
result += type_.tail.strip()
|
||||
return normalize_type(result)
|
||||
|
||||
|
||||
def convert_params(func: ElementTree.Element) -> List[Definition]:
|
||||
params = []
|
||||
for p in func.findall('param'):
|
||||
d = Definition(p.find('declname').text, 'param')
|
||||
d.type = convert_type(p.find('type'))
|
||||
def convert_params(func: ET.Element) -> list[Definition]:
|
||||
params: "list[Definition]" = []
|
||||
for p in func.findall("param"):
|
||||
declname = p.find("declname")
|
||||
if declname is None or declname.text is None:
|
||||
raise ValueError
|
||||
d = Definition(declname.text, "param")
|
||||
d.type = convert_type(p.find("type"))
|
||||
params.append(d)
|
||||
return params
|
||||
|
||||
|
||||
def convert_return_type(d: Definition, node: ElementTree.Element) -> None:
|
||||
def convert_return_type(d: Definition, node: ET.Element) -> None:
|
||||
d.trailing_return_type = None
|
||||
if d.type == 'auto' or d.type == 'constexpr auto':
|
||||
parts = node.find('argsstring').text.split(' -> ')
|
||||
if d.type == "auto" or d.type == "constexpr auto":
|
||||
argsstring = node.find("argsstring")
|
||||
if argsstring is None or argsstring.text is None:
|
||||
raise ValueError
|
||||
parts = argsstring.text.split(" -> ")
|
||||
if len(parts) > 1:
|
||||
d.trailing_return_type = normalize_type(parts[1])
|
||||
|
||||
|
||||
def render_param(param: Definition) -> str:
|
||||
return param.type + (f' {param.name}' if len(param.name) > 0 else '')
|
||||
if param.type is None:
|
||||
raise ValueError
|
||||
return param.type + (f" {param.name}" if len(param.name) > 0 else "")
|
||||
|
||||
|
||||
def render_decl(d: Definition) -> str:
|
||||
text = ''
|
||||
text = ""
|
||||
if d.id is not None:
|
||||
text += f'<a id="{d.id}">\n'
|
||||
text += '<pre><code class="language-cpp decl">'
|
||||
|
||||
text += '<div>'
|
||||
text += "<div>"
|
||||
if d.template_params is not None:
|
||||
text += 'template <'
|
||||
text += ', '.join([render_param(p) for p in d.template_params])
|
||||
text += '>\n'
|
||||
text += '</div>'
|
||||
text += "template <"
|
||||
text += ", ".join([render_param(p) for p in d.template_params])
|
||||
text += ">\n"
|
||||
text += "</div>"
|
||||
|
||||
text += '<div>'
|
||||
end = ';'
|
||||
if d.kind == 'function' or d.kind == 'variable':
|
||||
text += d.type + ' ' if len(d.type) > 0 else ''
|
||||
elif d.kind == 'typedef':
|
||||
text += 'using '
|
||||
elif d.kind == 'define':
|
||||
end = ''
|
||||
text += "<div>"
|
||||
end = ";"
|
||||
if d.kind is None:
|
||||
raise ValueError
|
||||
if d.kind == "function" or d.kind == "variable":
|
||||
if d.type is None:
|
||||
raise ValueError
|
||||
text += d.type + " " if len(d.type) > 0 else ""
|
||||
elif d.kind == "typedef":
|
||||
text += "using "
|
||||
elif d.kind == "define":
|
||||
end = ""
|
||||
else:
|
||||
text += d.kind + ' '
|
||||
text += d.kind + " "
|
||||
text += d.name
|
||||
|
||||
if d.params is not None:
|
||||
params = ', '.join([
|
||||
(p.type + ' ' if p.type else '') + p.name for p in d.params])
|
||||
text += '(' + escape_html(params) + ')'
|
||||
params = ", ".join([
|
||||
(p.type + " " if p.type else "") + p.name for p in d.params
|
||||
])
|
||||
text += "(" + escape_html(params) + ")"
|
||||
if d.trailing_return_type:
|
||||
text += ' -⁠> ' + escape_html(d.trailing_return_type)
|
||||
elif d.kind == 'typedef':
|
||||
text += ' = ' + escape_html(d.type)
|
||||
text += " -⁠> " + escape_html(d.trailing_return_type)
|
||||
elif d.kind == "typedef":
|
||||
if d.type is None:
|
||||
raise ValueError
|
||||
text += " = " + escape_html(d.type)
|
||||
|
||||
text += end
|
||||
text += '</div>'
|
||||
text += '</code></pre>\n'
|
||||
text += "</div>"
|
||||
text += "</code></pre>\n"
|
||||
if d.id is not None:
|
||||
text += f'</a>\n'
|
||||
text += "</a>\n"
|
||||
return text
|
||||
|
||||
|
||||
@final
|
||||
class CxxHandler(BaseHandler):
|
||||
def __init__(self, **kwargs: Any) -> None:
|
||||
super().__init__(handler='cxx', **kwargs)
|
||||
name: ClassVar[str] = "cxx"
|
||||
|
||||
domain: ClassVar[str] = "cxx"
|
||||
|
||||
def __init__(
|
||||
self, config: "Mapping[str, Any]", base_dir: Path, **kwargs: Any
|
||||
) -> None:
|
||||
super().__init__(**kwargs)
|
||||
|
||||
self.config = config
|
||||
"""The handler configuration."""
|
||||
self.base_dir = base_dir
|
||||
"""The base directory of the project."""
|
||||
|
||||
headers = [
|
||||
'args.h', 'base.h', 'chrono.h', 'color.h', 'compile.h', 'format.h',
|
||||
'os.h', 'ostream.h', 'printf.h', 'ranges.h', 'std.h', 'xchar.h'
|
||||
"args.h",
|
||||
"base.h",
|
||||
"chrono.h",
|
||||
"color.h",
|
||||
"compile.h",
|
||||
"format.h",
|
||||
"os.h",
|
||||
"ostream.h",
|
||||
"printf.h",
|
||||
"ranges.h",
|
||||
"std.h",
|
||||
"xchar.h",
|
||||
]
|
||||
|
||||
# Run doxygen.
|
||||
cmd = ['doxygen', '-']
|
||||
cmd = ["doxygen", "-"]
|
||||
support_dir = Path(__file__).parents[3]
|
||||
top_dir = os.path.dirname(support_dir)
|
||||
include_dir = os.path.join(top_dir, 'include', 'fmt')
|
||||
self._ns2doxyxml = {}
|
||||
build_dir = os.path.join(top_dir, 'build')
|
||||
include_dir = os.path.join(top_dir, "include", "fmt")
|
||||
self._ns2doxyxml: "dict[str, ET.ElementTree[ET.Element[str]]]" = {}
|
||||
build_dir = os.path.join(top_dir, "build")
|
||||
os.makedirs(build_dir, exist_ok=True)
|
||||
self._doxyxml_dir = os.path.join(build_dir, 'doxyxml')
|
||||
self._doxyxml_dir = os.path.join(build_dir, "doxyxml")
|
||||
p = Popen(cmd, stdin=PIPE, stdout=PIPE, stderr=STDOUT)
|
||||
_, _ = p.communicate(input=r'''
|
||||
_, _ = p.communicate(
|
||||
input=r"""
|
||||
PROJECT_NAME = fmt
|
||||
GENERATE_XML = YES
|
||||
GENERATE_LATEX = NO
|
||||
@@ -205,18 +289,20 @@ class CxxHandler(BaseHandler):
|
||||
"FMT_BEGIN_NAMESPACE=namespace fmt {{" \
|
||||
"FMT_END_NAMESPACE=}}" \
|
||||
"FMT_DOC=1"
|
||||
'''.format(
|
||||
' '.join([os.path.join(include_dir, h) for h in headers]),
|
||||
self._doxyxml_dir).encode('utf-8'))
|
||||
""".format(
|
||||
" ".join([os.path.join(include_dir, h) for h in headers]),
|
||||
self._doxyxml_dir,
|
||||
).encode("utf-8")
|
||||
)
|
||||
if p.returncode != 0:
|
||||
raise CalledProcessError(p.returncode, cmd)
|
||||
|
||||
# Merge all file-level XMLs into one to simplify search.
|
||||
self._file_doxyxml = None
|
||||
self._file_doxyxml: "ET.ElementTree[ET.Element[str]] | None" = None
|
||||
for h in headers:
|
||||
filename = h.replace(".h", "_8h.xml")
|
||||
with open(os.path.join(self._doxyxml_dir, filename)) as f:
|
||||
doxyxml = ElementTree.parse(f)
|
||||
doxyxml = ET.parse(f)
|
||||
if self._file_doxyxml is None:
|
||||
self._file_doxyxml = doxyxml
|
||||
continue
|
||||
@@ -224,33 +310,43 @@ class CxxHandler(BaseHandler):
|
||||
for node in doxyxml.getroot():
|
||||
root.append(node)
|
||||
|
||||
def collect_compound(self, identifier: str,
|
||||
cls: List[ElementTree.Element]) -> Definition:
|
||||
def collect_compound(self, identifier: str, cls: "list[ET.Element]") -> Definition:
|
||||
"""Collect a compound definition such as a struct."""
|
||||
path = os.path.join(self._doxyxml_dir, cls[0].get('refid') + '.xml')
|
||||
refid = cls[0].get("refid")
|
||||
if refid is None:
|
||||
raise ValueError
|
||||
path = os.path.join(self._doxyxml_dir, refid + ".xml")
|
||||
with open(path) as f:
|
||||
xml = ElementTree.parse(f)
|
||||
node = xml.find('compounddef')
|
||||
xml = ET.parse(f)
|
||||
node = xml.find("compounddef")
|
||||
if node is None:
|
||||
raise ValueError
|
||||
d = Definition(identifier, node=node)
|
||||
d.template_params = convert_template_params(node)
|
||||
d.desc = get_description(node)
|
||||
d.members = []
|
||||
for m in \
|
||||
node.findall('sectiondef[@kind="public-attrib"]/memberdef') + \
|
||||
node.findall('sectiondef[@kind="public-func"]/memberdef'):
|
||||
name = m.find('name').text
|
||||
for m in node.findall(
|
||||
'sectiondef[@kind="public-attrib"]/memberdef'
|
||||
) + node.findall('sectiondef[@kind="public-func"]/memberdef'):
|
||||
name = m.find("name")
|
||||
if name is None or name.text is None:
|
||||
raise ValueError
|
||||
name = name.text
|
||||
# Doxygen incorrectly classifies members of private unnamed unions as
|
||||
# public members of the containing class.
|
||||
if name.endswith('_'):
|
||||
if name.endswith("_"):
|
||||
continue
|
||||
desc = get_description(m)
|
||||
if len(desc) == 0:
|
||||
continue
|
||||
kind = m.get('kind')
|
||||
member = Definition(name if name else '', kind=kind, is_member=True)
|
||||
type_text = m.find('type').text
|
||||
member.type = type_text if type_text else ''
|
||||
if kind == 'function':
|
||||
kind = m.get("kind")
|
||||
member = Definition(name if name else "", kind=kind, is_member=True)
|
||||
type_ = m.find("type")
|
||||
if type_ is None:
|
||||
raise ValueError
|
||||
type_text = type_.text
|
||||
member.type = type_text if type_text else ""
|
||||
if kind == "function":
|
||||
member.params = convert_params(m)
|
||||
convert_return_type(member, m)
|
||||
member.template_params = None
|
||||
@@ -258,48 +354,60 @@ class CxxHandler(BaseHandler):
|
||||
d.members.append(member)
|
||||
return d
|
||||
|
||||
def collect(self, identifier: str, _config: Mapping[str, Any]) -> Definition:
|
||||
qual_name = 'fmt::' + identifier
|
||||
@override
|
||||
def collect(self, identifier: str, options: "Mapping[str, Any]") -> Definition:
|
||||
qual_name = "fmt::" + identifier
|
||||
|
||||
param_str = None
|
||||
paren = qual_name.find('(')
|
||||
paren = qual_name.find("(")
|
||||
if paren > 0:
|
||||
qual_name, param_str = qual_name[:paren], qual_name[paren + 1:-1]
|
||||
qual_name, param_str = qual_name[:paren], qual_name[paren + 1 : -1]
|
||||
|
||||
colons = qual_name.rfind('::')
|
||||
namespace, name = qual_name[:colons], qual_name[colons + 2:]
|
||||
colons = qual_name.rfind("::")
|
||||
namespace, name = qual_name[:colons], qual_name[colons + 2 :]
|
||||
|
||||
# Load XML.
|
||||
doxyxml = self._ns2doxyxml.get(namespace)
|
||||
if doxyxml is None:
|
||||
path = f'namespace{namespace.replace("::", "_1_1")}.xml'
|
||||
path = f"namespace{namespace.replace('::', '_1_1')}.xml"
|
||||
with open(os.path.join(self._doxyxml_dir, path)) as f:
|
||||
doxyxml = ElementTree.parse(f)
|
||||
doxyxml = ET.parse(f)
|
||||
self._ns2doxyxml[namespace] = doxyxml
|
||||
|
||||
nodes = doxyxml.findall(
|
||||
f"compounddef/sectiondef/memberdef/name[.='{name}']/..")
|
||||
nodes = doxyxml.findall(f"compounddef/sectiondef/memberdef/name[.='{name}']/..")
|
||||
if len(nodes) == 0:
|
||||
if self._file_doxyxml is None:
|
||||
raise ValueError
|
||||
nodes = self._file_doxyxml.findall(
|
||||
f"compounddef/sectiondef/memberdef/name[.='{name}']/..")
|
||||
candidates = []
|
||||
f"compounddef/sectiondef/memberdef/name[.='{name}']/.."
|
||||
)
|
||||
candidates: "list[str]" = []
|
||||
for node in nodes:
|
||||
# Process a function or a typedef.
|
||||
params = None
|
||||
params: "list[Definition] | None" = None
|
||||
d = Definition(name, node=node)
|
||||
if d.kind == 'function':
|
||||
if d.kind == "function":
|
||||
params = convert_params(node)
|
||||
node_param_str = ', '.join([p.type for p in params])
|
||||
params_type: "list[str]" = []
|
||||
for p in params:
|
||||
if p.type is None:
|
||||
raise ValueError
|
||||
else:
|
||||
params_type.append(p.type)
|
||||
node_param_str = ", ".join(params_type)
|
||||
if param_str and param_str != node_param_str:
|
||||
candidates.append(f'{name}({node_param_str})')
|
||||
candidates.append(f"{name}({node_param_str})")
|
||||
continue
|
||||
elif d.kind == 'define':
|
||||
elif d.kind == "define":
|
||||
params = []
|
||||
for p in node.findall('param'):
|
||||
param = Definition(p.find('defname').text, kind='param')
|
||||
for p in node.findall("param"):
|
||||
defname = p.find("defname")
|
||||
if defname is None or defname.text is None:
|
||||
raise ValueError
|
||||
param = Definition(defname.text, kind="param")
|
||||
param.type = None
|
||||
params.append(param)
|
||||
d.type = convert_type(node.find('type'))
|
||||
d.type = convert_type(node.find("type"))
|
||||
d.template_params = convert_template_params(node)
|
||||
d.params = params
|
||||
convert_return_type(d, node)
|
||||
@@ -308,31 +416,42 @@ class CxxHandler(BaseHandler):
|
||||
|
||||
cls = doxyxml.findall(f"compounddef/innerclass[.='{qual_name}']")
|
||||
if not cls:
|
||||
raise Exception(f'Cannot find {identifier}. Candidates: {candidates}')
|
||||
raise Exception(f"Cannot find {identifier}. Candidates: {candidates}")
|
||||
return self.collect_compound(identifier, cls)
|
||||
|
||||
def render(self, d: Definition, config: dict) -> str:
|
||||
@override
|
||||
def render(
|
||||
self,
|
||||
data: "CollectorItem",
|
||||
options: "HandlerOptions",
|
||||
*,
|
||||
locale: "str | None" = None,
|
||||
) -> str:
|
||||
d = data
|
||||
if d.id is not None:
|
||||
self.do_heading('', 0, id=d.id)
|
||||
_ = self.do_heading(Markup(), 0, id=d.id)
|
||||
if d.desc is None:
|
||||
raise ValueError
|
||||
text = '<div class="docblock">\n'
|
||||
text += render_decl(d)
|
||||
text += '<div class="docblock-desc">\n'
|
||||
text += doxyxml2html(d.desc)
|
||||
if d.members is not None:
|
||||
for m in d.members:
|
||||
text += self.render(m, config)
|
||||
text += '</div>\n'
|
||||
text += '</div>\n'
|
||||
text += self.render(m, options, locale=locale)
|
||||
text += "</div>\n"
|
||||
text += "</div>\n"
|
||||
return text
|
||||
|
||||
|
||||
def get_handler(theme: str, custom_templates: Optional[str] = None,
|
||||
**_config: Any) -> CxxHandler:
|
||||
def get_handler(
|
||||
handler_config: "MutableMapping[str, Any]", tool_config: "MkDocsConfig", **kwargs: Any
|
||||
) -> CxxHandler:
|
||||
"""Return an instance of `CxxHandler`.
|
||||
|
||||
Arguments:
|
||||
theme: The theme to use when rendering contents.
|
||||
custom_templates: Directory containing custom templates.
|
||||
**_config: Configuration passed to the handler.
|
||||
handler_config: The handler configuration.
|
||||
tool_config: The tool (SSG) configuration.
|
||||
"""
|
||||
return CxxHandler(theme=theme, custom_templates=custom_templates)
|
||||
base_dir = Path(tool_config.config_file_path or "./mkdocs.yml").parent
|
||||
return CxxHandler(config=handler_config, base_dir=base_dir, **kwargs)
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
add_subdirectory(gtest)
|
||||
|
||||
include(CheckSymbolExists)
|
||||
|
||||
set(TEST_MAIN_SRC test-main.cc gtest-extra.cc gtest-extra.h util.cc)
|
||||
add_library(test-main STATIC ${TEST_MAIN_SRC})
|
||||
target_include_directories(test-main PUBLIC
|
||||
@@ -62,21 +60,10 @@ if (NOT (MSVC AND BUILD_SHARED_LIBS))
|
||||
endif ()
|
||||
add_fmt_test(ostream-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(ranges-test ranges-odr-test.cc)
|
||||
add_fmt_test(no-builtin-types-test HEADER_ONLY)
|
||||
|
||||
add_fmt_test(scan-test HEADER_ONLY)
|
||||
check_symbol_exists(strptime "time.h" HAVE_STRPTIME)
|
||||
if (HAVE_STRPTIME)
|
||||
target_compile_definitions(scan-test PRIVATE FMT_HAVE_STRPTIME)
|
||||
endif ()
|
||||
|
||||
add_fmt_test(std-test)
|
||||
try_compile(compile_result_unused
|
||||
${CMAKE_CURRENT_BINARY_DIR}
|
||||
|
||||
@@ -1,62 +0,0 @@
|
||||
// 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,9 +90,6 @@ TEST(compile_test, format_escape) {
|
||||
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) {
|
||||
EXPECT_EQ("42", fmt::format(FMT_COMPILE("{:x}"), 0x42));
|
||||
@@ -124,7 +121,6 @@ TEST(compile_test, manual_ordering) {
|
||||
"true 42 42 foo 0x1234 foo",
|
||||
fmt::format(FMT_COMPILE("{0} {1} {2} {3} {4} {5}"), true, 42, 42.0f,
|
||||
"foo", reinterpret_cast<void*>(0x1234), test_formattable()));
|
||||
EXPECT_EQ(L"42", fmt::format(FMT_COMPILE(L"{0}"), 42));
|
||||
}
|
||||
|
||||
TEST(compile_test, named) {
|
||||
@@ -133,10 +129,6 @@ TEST(compile_test, named) {
|
||||
static_assert(std::is_same_v<decltype(runtime_named_field_compiled),
|
||||
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",
|
||||
fmt::format(FMT_COMPILE("{a0}{a1}"), fmt::arg("a0", "foo"),
|
||||
fmt::arg("a1", "bar")));
|
||||
@@ -318,7 +310,6 @@ TEST(compile_test, compile_format_string_literal) {
|
||||
using namespace fmt::literals;
|
||||
EXPECT_EQ("", fmt::format(""_cf));
|
||||
EXPECT_EQ("42", fmt::format("{}"_cf, 42));
|
||||
EXPECT_EQ(L"42", fmt::format(L"{}"_cf, 42));
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -426,6 +417,40 @@ TEST(compile_time_formatting_test, custom_type) {
|
||||
TEST(compile_time_formatting_test, multibyte_fill) {
|
||||
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
|
||||
|
||||
#if FMT_USE_CONSTEXPR_STRING
|
||||
|
||||
@@ -2036,11 +2036,6 @@ TEST(format_test, unpacked_args) {
|
||||
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) {
|
||||
EXPECT_EQ(fmt::format(FMT_STRING("foo")), "foo");
|
||||
EXPECT_EQ(fmt::format(FMT_STRING("{}"), 42), "42");
|
||||
@@ -2055,19 +2050,12 @@ TEST(format_test, compile_time_string) {
|
||||
EXPECT_EQ(fmt::format(FMT_STRING("{} {two}"), 1, "two"_a = 2), "1 2");
|
||||
#endif
|
||||
|
||||
(void)static_with_null;
|
||||
(void)static_no_null;
|
||||
static constexpr char format_str[3] = {'{', '}', '\0'};
|
||||
(void)format_str;
|
||||
#ifndef _MSC_VER
|
||||
EXPECT_EQ(fmt::format(FMT_STRING(static_with_null), 42), "42");
|
||||
EXPECT_EQ(fmt::format(FMT_STRING(static_no_null), 42), "42");
|
||||
EXPECT_EQ(fmt::format(FMT_STRING(format_str), 42), "42");
|
||||
#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
|
||||
EXPECT_EQ(fmt::format(FMT_STRING(std::string_view("{}")), 42), "42");
|
||||
#endif
|
||||
|
||||
@@ -12,10 +12,10 @@ void invoke_inner(fmt::string_view format_str, Rep rep) {
|
||||
auto value = std::chrono::duration<Rep, Period>(rep);
|
||||
try {
|
||||
#if FMT_FUZZ_FORMAT_TO_STRING
|
||||
std::string message = fmt::format(format_str, value);
|
||||
std::string message = fmt::format(fmt::runtime(format_str), value);
|
||||
#else
|
||||
auto buf = fmt::memory_buffer();
|
||||
fmt::format_to(std::back_inserter(buf), format_str, value);
|
||||
fmt::format_to(std::back_inserter(buf), fmt::runtime(format_str), value);
|
||||
#endif
|
||||
} catch (std::exception&) {
|
||||
}
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
#define FMT_FUZZ_SEPARATE_ALLOCATION 1
|
||||
|
||||
// The size of the largest possible type in use.
|
||||
// To let the the fuzzer mutation be efficient at cross pollinating between
|
||||
// To let the fuzzer mutation be efficient at cross pollinating between
|
||||
// different types, use a fixed size format. The same bit pattern, interpreted
|
||||
// as another type, is likely interesting.
|
||||
constexpr auto fixed_size = 16;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#------------------------------------------------------------------------------
|
||||
# Build the google test library
|
||||
|
||||
# We compile Google Test ourselves instead of using pre-compiled libraries.
|
||||
# Compile Google Test ourselves instead of using pre-compiled libraries.
|
||||
# See the Google Test FAQ "Why is it not recommended to install a
|
||||
# pre-compiled copy of Google Test (for example, into /usr/local)?"
|
||||
# at http://code.google.com/p/googletest/wiki/FAQ for more details.
|
||||
@@ -19,14 +19,10 @@ else ()
|
||||
endif ()
|
||||
|
||||
if (MSVC)
|
||||
# Disable MSVC warnings of _CRT_INSECURE_DEPRECATE functions.
|
||||
# Disable MSVC warnings about _CRT_INSECURE_DEPRECATE functions.
|
||||
target_compile_definitions(gtest PRIVATE _CRT_SECURE_NO_WARNINGS)
|
||||
if (CMAKE_CXX_COMPILER_ID MATCHES "Clang")
|
||||
# Disable MSVC warnings of POSIX functions.
|
||||
# Disable MSVC warnings about POSIX functions.
|
||||
target_compile_options(gtest PUBLIC -Wno-deprecated-declarations)
|
||||
endif ()
|
||||
endif ()
|
||||
|
||||
# Silence MSVC tr1 deprecation warning in gmock.
|
||||
target_compile_definitions(gtest
|
||||
PUBLIC _SILENCE_TR1_NAMESPACE_DEPRECATION_WARNING=1)
|
||||
|
||||
@@ -7,9 +7,10 @@
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
#if !defined(__GNUC__) || __GNUC__ >= 5
|
||||
#if !defined(__GNUC__) || (__GNUC__ >= 5 || defined(__clang__))
|
||||
|
||||
# define FMT_BUILTIN_TYPES 0
|
||||
# include "fmt/format.h"
|
||||
# include "fmt/compile.h"
|
||||
|
||||
TEST(no_builtin_types_test, format) {
|
||||
EXPECT_EQ(fmt::format("{}", 42), "42");
|
||||
@@ -22,4 +23,9 @@ TEST(no_builtin_types_test, double_is_custom_type) {
|
||||
EXPECT_EQ(fmt::format_args(args).get(0).type(),
|
||||
fmt::detail::type::custom_type);
|
||||
}
|
||||
|
||||
TEST(no_builtin_types_test, format_pointer_compiled) {
|
||||
const void* p = nullptr;
|
||||
fmt::format(FMT_COMPILE("{:} {}"), 42, p);
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -265,7 +265,7 @@ template <> struct formatter<abstract> : ostream_formatter {};
|
||||
} // namespace fmt
|
||||
|
||||
void format_abstract_compiles(const abstract& a) {
|
||||
fmt::format(FMT_COMPILE("{}"), a);
|
||||
(void)fmt::format(FMT_COMPILE("{}"), a);
|
||||
}
|
||||
|
||||
TEST(ostream_test, is_formattable) {
|
||||
|
||||
@@ -39,13 +39,12 @@ TEST(std_test, path) {
|
||||
EXPECT_EQ(fmt::format("{}", path(L"\x0428\x0447\x0443\x0447\x044B\x043D\x0448"
|
||||
L"\x0447\x044B\x043D\x0430")),
|
||||
"Шчучыншчына");
|
||||
EXPECT_EQ(fmt::format("{}", path(L"\xd800")), "<EFBFBD>");
|
||||
EXPECT_EQ(fmt::format("{}", path(L"HEAD \xd800 TAIL")), "HEAD <20> TAIL");
|
||||
EXPECT_EQ(fmt::format("{}", path(L"HEAD \xD83D\xDE00 TAIL")),
|
||||
"HEAD \xF0\x9F\x98\x80 TAIL");
|
||||
EXPECT_EQ(fmt::format("{}", path(L"HEAD \xD83D\xD83D\xDE00 TAIL")),
|
||||
"HEAD <20>\xF0\x9F\x98\x80 TAIL");
|
||||
EXPECT_EQ(fmt::format("{:?}", path(L"\xd800")), "\"\\ud800\"");
|
||||
EXPECT_EQ(fmt::format("{}", path(L"\xD800")), "\xED\xA0\x80");
|
||||
EXPECT_EQ(fmt::format("{}", path(L"[\xD800]")), "[\xED\xA0\x80]");
|
||||
EXPECT_EQ(fmt::format("{}", path(L"[\xD83D\xDE00]")), "[\xF0\x9F\x98\x80]");
|
||||
EXPECT_EQ(fmt::format("{}", path(L"[\xD83D\xD83D\xDE00]")),
|
||||
"[\xED\xA0\xBD\xF0\x9F\x98\x80]");
|
||||
EXPECT_EQ(fmt::format("{:?}", path(L"\xD800")), "\"\\ud800\"");
|
||||
# endif
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user