aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndroid Build Coastguard Worker <android-build-coastguard-worker@google.com>2024-03-14 18:10:03 +0000
committerAndroid Build Coastguard Worker <android-build-coastguard-worker@google.com>2024-03-14 18:10:03 +0000
commitbed8fa9d67ef262a927d58a9ece995fd913c6bb4 (patch)
tree77eb0afe0529d93aaf8f54988182a425173c056c
parent9ceaceb7129151e8d8bc60979e0292526a59a589 (diff)
parent3b8b60f70480ead65ad864cb3311fc52ec122907 (diff)
downloadfmtlib-emu-35-1-release.tar.gz
Snap for 11574415 from 3b8b60f70480ead65ad864cb3311fc52ec122907 to emu-35-1-releaseemu-35-1-release
Change-Id: I5df10ae010eedf6307433ebdc6aa86013672adbc
-rw-r--r--.github/workflows/cifuzz.yml6
-rw-r--r--.github/workflows/lint.yml26
-rw-r--r--.github/workflows/scorecard.yml2
-rw-r--r--.gitignore39
-rw-r--r--Android.bp6
-rw-r--r--CMakeLists.txt17
-rw-r--r--ChangeLog.md252
-rw-r--r--METADATA25
-rw-r--r--README.md490
-rw-r--r--README.rst553
-rw-r--r--doc/api.rst5
-rwxr-xr-xdoc/build.py11
-rw-r--r--doc/syntax.rst15
-rw-r--r--include/fmt/args.h11
-rw-r--r--include/fmt/chrono.h267
-rw-r--r--include/fmt/color.h74
-rw-r--r--include/fmt/compile.h17
-rw-r--r--include/fmt/core.h68
-rw-r--r--include/fmt/format-inl.h116
-rw-r--r--include/fmt/format.h363
-rw-r--r--include/fmt/os.h34
-rw-r--r--include/fmt/ostream.h46
-rw-r--r--include/fmt/ranges.h25
-rw-r--r--include/fmt/std.h82
-rw-r--r--include/fmt/xchar.h17
-rw-r--r--src/fmt.cc2
-rw-r--r--src/os.cc14
-rw-r--r--support/AndroidManifest.xml2
-rwxr-xr-xsupport/manage.py62
-rw-r--r--test/chrono-test.cc464
-rw-r--r--test/compile-test.cc15
-rw-r--r--test/format-impl-test.cc7
-rw-r--r--test/format-test.cc876
-rw-r--r--test/gtest-extra-test.cc2
-rw-r--r--test/gtest-extra.cc4
-rw-r--r--test/gtest-extra.h2
-rw-r--r--test/gtest/gmock-gtest-all.cc2
-rw-r--r--test/ostream-test.cc19
-rw-r--r--test/posix-mock.h8
-rw-r--r--test/printf-test.cc4
-rw-r--r--test/ranges-test.cc34
-rw-r--r--test/scan-test.cc134
-rw-r--r--test/scan.h698
-rw-r--r--test/std-test.cc52
-rw-r--r--test/util.cc5
-rw-r--r--test/util.h26
-rw-r--r--test/xchar-test.cc20
47 files changed, 2993 insertions, 2026 deletions
diff --git a/.github/workflows/cifuzz.yml b/.github/workflows/cifuzz.yml
index f87899d0..25250e8b 100644
--- a/.github/workflows/cifuzz.yml
+++ b/.github/workflows/cifuzz.yml
@@ -10,20 +10,20 @@ jobs:
steps:
- name: Build Fuzzers
id: build
- uses: google/oss-fuzz/infra/cifuzz/actions/build_fuzzers@master
+ uses: google/oss-fuzz/infra/cifuzz/actions/build_fuzzers@061583ebb5a96653e42feb3a97ee513eedc18078 # master
with:
oss-fuzz-project-name: 'fmt'
dry-run: false
language: c++
- name: Run Fuzzers
- uses: google/oss-fuzz/infra/cifuzz/actions/run_fuzzers@master
+ uses: google/oss-fuzz/infra/cifuzz/actions/run_fuzzers@061583ebb5a96653e42feb3a97ee513eedc18078 # master
with:
oss-fuzz-project-name: 'fmt'
fuzz-seconds: 300
dry-run: false
language: c++
- name: Upload Crash
- uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce # v3.1.2
+ uses: actions/upload-artifact@c7d193f32edcb7bfad88892161225aeda64e9392 # v4.0.0
if: failure() && steps.build.outcome == 'success'
with:
name: artifacts
diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml
new file mode 100644
index 00000000..b906fac2
--- /dev/null
+++ b/.github/workflows/lint.yml
@@ -0,0 +1,26 @@
+name: lint
+
+on:
+ pull_request:
+ paths:
+ - '**.h'
+ - '**.cc'
+
+permissions:
+ contents: read
+
+jobs:
+ format_code:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v4
+
+ - name: Install clang-format
+ uses: aminya/setup-cpp@v1
+ with:
+ clangformat: 17.0.5
+
+ - name: Run clang-format
+ run: |
+ find include src -name '*.h' -o -name '*.cc' | xargs clang-format -i -style=file -fallback-style=none
+ git diff --exit-code
diff --git a/.github/workflows/scorecard.yml b/.github/workflows/scorecard.yml
index 33aecd30..c5f2ea9d 100644
--- a/.github/workflows/scorecard.yml
+++ b/.github/workflows/scorecard.yml
@@ -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@0b7f8abb1508181956e8e162db84b466c27e18ce # v3.1.2
+ uses: actions/upload-artifact@c7d193f32edcb7bfad88892161225aeda64e9392 # v4.0.0
with:
name: SARIF file
path: results.sarif
diff --git a/.gitignore b/.gitignore
index 8a37cb98..1406ac34 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,37 +1,24 @@
+*.a
+*.so*
+*.xcodeproj
+*~
.vscode/
-.vs/
-
-*.iml
-.idea/
-.externalNativeBuild/
-.gradle/
-gradle/
-gradlew*
-local.properties
-build/
-support/.cxx
-
-bin/
-/_CPack_Packages
/CMakeScripts
+/Testing
+/_CPack_Packages
/doc/doxyxml
/doc/html
/doc/node_modules
-virtualenv
-/Testing
/install_manifest.txt
-*~
-*.a
-*.so*
-*.xcodeproj
-*.zip
-cmake_install.cmake
-CPack*.cmake
-fmt-*.cmake
-CTestTestfile.cmake
CMakeCache.txt
CMakeFiles
+CPack*.cmake
+CTestTestfile.cmake
FMT.build
Makefile
-run-msbuild.bat
+bin/
+build/
+cmake_install.cmake
+fmt-*.cmake
fmt.pc
+virtualenv
diff --git a/Android.bp b/Android.bp
index 2d6c6b30..e1fb5cb5 100644
--- a/Android.bp
+++ b/Android.bp
@@ -133,7 +133,11 @@ cc_defaults {
host_supported: true,
test_suites: ["general-tests"],
// The tests require exceptions and RTTI.
- cflags: ["-fexceptions"],
+ cflags: [
+ "-fexceptions",
+ // https://github.com/fmtlib/fmt/issues/3884
+ "-Wno-non-virtual-dtor",
+ ],
rtti: true,
// The usual "gtest *and* gmock, please" dance...
gtest: false,
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 485eb86c..4b928ae3 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -1,9 +1,9 @@
cmake_minimum_required(VERSION 3.8...3.26)
# Fallback for using newer policies on CMake <3.12.
-if(${CMAKE_VERSION} VERSION_LESS 3.12)
+if (${CMAKE_VERSION} VERSION_LESS 3.12)
cmake_policy(VERSION ${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION})
-endif()
+endif ()
# Determine if fmt is built as a subproject (using add_subdirectory)
# or if it is the master project.
@@ -162,10 +162,10 @@ set(FMT_SYSTEM_HEADERS_ATTRIBUTE "")
if (FMT_SYSTEM_HEADERS)
set(FMT_SYSTEM_HEADERS_ATTRIBUTE SYSTEM)
endif ()
-if(CMAKE_SYSTEM_NAME STREQUAL "MSDOS")
+if (CMAKE_SYSTEM_NAME STREQUAL "MSDOS")
set(FMT_TEST OFF)
message(STATUS "MSDOS is incompatible with gtest")
-endif()
+endif ()
# Get version from core.h
file(READ include/fmt/core.h core_h)
@@ -283,7 +283,7 @@ if (FMT_OS)
endif ()
add_module_library(fmt src/fmt.cc FALLBACK
- ${FMT_SOURCES} ${FMT_HEADERS} README.rst ChangeLog.md
+ ${FMT_SOURCES} ${FMT_HEADERS} README.md ChangeLog.md
IF FMT_MODULE)
add_library(fmt::fmt ALIAS fmt)
if (FMT_MODULE)
@@ -334,7 +334,7 @@ if (BUILD_SHARED_LIBS)
endif ()
if (FMT_SAFE_DURATION_CAST)
target_compile_definitions(fmt PUBLIC FMT_SAFE_DURATION_CAST)
-endif()
+endif ()
add_library(fmt-header-only INTERFACE)
add_library(fmt::fmt-header-only ALIAS fmt-header-only)
@@ -342,7 +342,8 @@ add_library(fmt::fmt-header-only ALIAS fmt-header-only)
target_compile_definitions(fmt-header-only INTERFACE FMT_HEADER_ONLY=1)
target_compile_features(fmt-header-only INTERFACE cxx_std_11)
-target_include_directories(fmt-header-only ${FMT_SYSTEM_HEADERS_ATTRIBUTE} INTERFACE
+target_include_directories(fmt-header-only
+ ${FMT_SYSTEM_HEADERS_ATTRIBUTE} INTERFACE
$<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/include>
$<INSTALL_INTERFACE:${FMT_INC_DIR}>)
@@ -447,6 +448,6 @@ if (FMT_MASTER_PROJECT AND EXISTS ${gitignore})
set(CPACK_SOURCE_IGNORE_FILES ${ignored_files})
set(CPACK_SOURCE_PACKAGE_FILE_NAME fmt-${FMT_VERSION})
set(CPACK_PACKAGE_NAME fmt)
- set(CPACK_RESOURCE_FILE_README ${PROJECT_SOURCE_DIR}/README.rst)
+ set(CPACK_RESOURCE_FILE_README ${PROJECT_SOURCE_DIR}/README.md)
include(CPack)
endif ()
diff --git a/ChangeLog.md b/ChangeLog.md
index 2e51d107..8f8075ac 100644
--- a/ChangeLog.md
+++ b/ChangeLog.md
@@ -1,3 +1,248 @@
+# 10.2.0 - 2024-01-01
+
+- Added support for the `%j` specifier (the number of days) for
+ `std::chrono::duration` (https://github.com/fmtlib/fmt/issues/3643,
+ https://github.com/fmtlib/fmt/pull/3732). Thanks @intelfx.
+
+- Added support for the chrono suffix for days and changed
+ the suffix for minutes from "m" to the correct "min"
+ (https://github.com/fmtlib/fmt/issues/3662,
+ https://github.com/fmtlib/fmt/pull/3664).
+ For example ([godbolt](https://godbolt.org/z/9KhMnq9ba)):
+
+ ```c++
+ #include <fmt/chrono.h>
+
+ int main() {
+ fmt::print("{}\n", std::chrono::days(42)); // prints "42d"
+ }
+ ```
+
+ Thanks @Richardk2n.
+
+- Fixed an overflow in `std::chrono::time_point` formatting with large dates
+ (https://github.com/fmtlib/fmt/issues/3725,
+ https://github.com/fmtlib/fmt/pull/3727). Thanks @cschreib.
+
+- Added a formatter for `std::source_location`
+ (https://github.com/fmtlib/fmt/pull/3730).
+ For example ([godbolt](https://godbolt.org/z/YajfKjhhr)):
+
+ ```c++
+ #include <source_location>
+ #include <fmt/std.h>
+
+ int main() {
+ fmt::print("{}\n", std::source_location::current());
+ }
+ ```
+
+ prints
+
+ ```
+ /app/example.cpp:5:51: int main()
+ ```
+
+ Thanks @felix642.
+
+- Added a formatter for `std::bitset`
+ (https://github.com/fmtlib/fmt/pull/3660).
+ For example ([godbolt](https://godbolt.org/z/bdEaGeYxe)):
+
+ ```c++
+ #include <bitset>
+ #include <fmt/std.h>
+
+ int main() {
+ fmt::print("{}\n", std::bitset<6>(42)); // prints "101010"
+ }
+ ```
+
+ Thanks @muggenhor.
+
+- Added an experimental `nested_formatter` that provides an easy way of
+ applying a formatter to one or more subobjects while automatically handling
+ width, fill and alignment. For example:
+
+ ```c++
+ #include <fmt/format.h>
+
+ struct point {
+ double x, y;
+ };
+
+ template <>
+ struct fmt::formatter<point> : nested_formatter<double> {
+ auto format(point p, format_context& ctx) const {
+ return write_padded(ctx, [=](auto out) {
+ return format_to(out, "({}, {})", nested(p.x), nested(p.y));
+ });
+ }
+ };
+
+ int main() {
+ fmt::print("[{:>20.2f}]", point{1, 2});
+ }
+ ```
+
+ prints
+
+ ```
+ [ (1.00, 2.00)]
+ ```
+
+- Added the generic representation (`g`) to `std::filesystem::path`
+ (https://github.com/fmtlib/fmt/issues/3715,
+ https://github.com/fmtlib/fmt/pull/3729). For example:
+
+ ```c++
+ #include <filesystem>
+ #include <fmt/std.h>
+
+ int main() {
+ fmt::print("{:g}\n", std::filesystem::path("C:\\foo"));
+ }
+ ```
+
+ prints `"C:/foo"` on Windows.
+
+ Thanks @js324.
+
+- Made `format_as` work with references
+ (https://github.com/fmtlib/fmt/pull/3739). Thanks @tchaikov.
+
+- Fixed formatting of invalid UTF-8 with precision
+ (https://github.com/fmtlib/fmt/issues/3284).
+
+- Fixed an inconsistency between `fmt::to_string` and `fmt::format`
+ (https://github.com/fmtlib/fmt/issues/3684).
+
+- Disallowed unsafe uses of `fmt::styled`
+ (https://github.com/fmtlib/fmt/issues/3625):
+
+ ```c++
+ auto s = fmt::styled(std::string("dangle"), fmt::emphasis::bold);
+ fmt::print("{}\n", s); // compile error
+ ```
+
+ Pass `fmt::styled(...)` as a parameter instead.
+
+- Added a null check when formatting a C string with the `s` specifier
+ (https://github.com/fmtlib/fmt/issues/3706).
+
+- Disallowed the `c` specifier for `bool`
+ (https://github.com/fmtlib/fmt/issues/3726,
+ https://github.com/fmtlib/fmt/pull/3734). Thanks @js324.
+
+- Made the default formatting unlocalized in `fmt::ostream_formatter` for
+ consistency with the rest of the library
+ (https://github.com/fmtlib/fmt/issues/3460).
+
+- Fixed localized formatting in bases other than decimal
+ (https://github.com/fmtlib/fmt/issues/3693,
+ https://github.com/fmtlib/fmt/pull/3750). Thanks @js324.
+
+- Fixed a performance regression in experimental `fmt::ostream::print`
+ (https://github.com/fmtlib/fmt/issues/3674).
+
+- Added synchronization with the underlying output stream when writing to
+ the Windows console
+ (https://github.com/fmtlib/fmt/pull/3668,
+ https://github.com/fmtlib/fmt/issues/3688,
+ https://github.com/fmtlib/fmt/pull/3689).
+ Thanks @Roman-Koshelev and @dimztimz.
+
+- Changed to only export `format_error` when {fmt} is built as a shared
+ library (https://github.com/fmtlib/fmt/issues/3626,
+ https://github.com/fmtlib/fmt/pull/3627). Thanks @phprus.
+
+- Made `fmt::streamed` `constexpr`.
+ (https://github.com/fmtlib/fmt/pull/3650). Thanks @muggenhor.
+
+- Enabled `consteval` on older versions of MSVC
+ (https://github.com/fmtlib/fmt/pull/3757). Thanks @phprus.
+
+- Added an option to build without `wchar_t` support on Windows
+ (https://github.com/fmtlib/fmt/issues/3631,
+ https://github.com/fmtlib/fmt/pull/3636). Thanks @glebm.
+
+- Improved build and CI configuration
+ (https://github.com/fmtlib/fmt/pull/3679,
+ https://github.com/fmtlib/fmt/issues/3701,
+ https://github.com/fmtlib/fmt/pull/3702,
+ https://github.com/fmtlib/fmt/pull/3749).
+ Thanks @jcar87, @pklima and @tchaikov.
+
+- Fixed various warnings, compilation and test issues
+ (https://github.com/fmtlib/fmt/issues/3607,
+ https://github.com/fmtlib/fmt/pull/3610,
+ https://github.com/fmtlib/fmt/pull/3624,
+ https://github.com/fmtlib/fmt/pull/3630,
+ https://github.com/fmtlib/fmt/pull/3634,
+ https://github.com/fmtlib/fmt/pull/3638,
+ https://github.com/fmtlib/fmt/issues/3645,
+ https://github.com/fmtlib/fmt/issues/3646,
+ https://github.com/fmtlib/fmt/pull/3647,
+ https://github.com/fmtlib/fmt/pull/3652,
+ https://github.com/fmtlib/fmt/issues/3654,
+ https://github.com/fmtlib/fmt/pull/3663,
+ https://github.com/fmtlib/fmt/issues/3670,
+ https://github.com/fmtlib/fmt/pull/3680,
+ https://github.com/fmtlib/fmt/issues/3694,
+ https://github.com/fmtlib/fmt/pull/3695,
+ https://github.com/fmtlib/fmt/pull/3699,
+ https://github.com/fmtlib/fmt/issues/3705,
+ https://github.com/fmtlib/fmt/issues/3710,
+ https://github.com/fmtlib/fmt/issues/3712,
+ https://github.com/fmtlib/fmt/pull/3713,
+ https://github.com/fmtlib/fmt/issues/3714,
+ https://github.com/fmtlib/fmt/pull/3716,
+ https://github.com/fmtlib/fmt/pull/3723,
+ https://github.com/fmtlib/fmt/issues/3738,
+ https://github.com/fmtlib/fmt/issues/3740,
+ https://github.com/fmtlib/fmt/pull/3741,
+ https://github.com/fmtlib/fmt/pull/3743,
+ https://github.com/fmtlib/fmt/issues/3745,
+ https://github.com/fmtlib/fmt/pull/3747,
+ https://github.com/fmtlib/fmt/pull/3748,
+ https://github.com/fmtlib/fmt/pull/3751,
+ https://github.com/fmtlib/fmt/pull/3754,
+ https://github.com/fmtlib/fmt/pull/3755,
+ https://github.com/fmtlib/fmt/issues/3760,
+ https://github.com/fmtlib/fmt/pull/3762,
+ https://github.com/fmtlib/fmt/issues/3763,
+ https://github.com/fmtlib/fmt/pull/3764,
+ https://github.com/fmtlib/fmt/issues/3774,
+ https://github.com/fmtlib/fmt/pull/3779).
+ Thanks @danakj, @vinayyadav3016, @cyyever, @phprus, @qimiko, @saschasc,
+ @gsjaardema, @lazka, @Zhaojun-Liu, @carlsmedstad, @hotwatermorning,
+ @cptFracassa, @kuguma, @PeterJohnson, @H1X4Dev, @asantoni, @eltociear,
+ @msimberg, @tchaikov, @waywardmonkeys.
+
+- Improved documentation and README
+ (https://github.com/fmtlib/fmt/issues/2086,
+ https://github.com/fmtlib/fmt/issues/3637,
+ https://github.com/fmtlib/fmt/pull/3642,
+ https://github.com/fmtlib/fmt/pull/3653,
+ https://github.com/fmtlib/fmt/pull/3655,
+ https://github.com/fmtlib/fmt/pull/3661,
+ https://github.com/fmtlib/fmt/issues/3673,
+ https://github.com/fmtlib/fmt/pull/3677,
+ https://github.com/fmtlib/fmt/pull/3737,
+ https://github.com/fmtlib/fmt/issues/3742,
+ https://github.com/fmtlib/fmt/pull/3744).
+ Thanks @idzm, @perlun, @joycebrum, @fennewald, @reinhardt1053, @GeorgeLS.
+
+- Updated CI dependencies
+ (https://github.com/fmtlib/fmt/pull/3615,
+ https://github.com/fmtlib/fmt/pull/3622,
+ https://github.com/fmtlib/fmt/pull/3623,
+ https://github.com/fmtlib/fmt/pull/3666,
+ https://github.com/fmtlib/fmt/pull/3696,
+ https://github.com/fmtlib/fmt/pull/3697,
+ https://github.com/fmtlib/fmt/pull/3759,
+ https://github.com/fmtlib/fmt/pull/3782).
+
# 10.1.1 - 2023-08-28
- Added formatters for `std::atomic` and `atomic_flag`
@@ -13,8 +258,7 @@
https://github.com/fmtlib/fmt/pull/3605).
Thanks @MathewBensonCode.
- Made `fmt::to_string` work with types that have `format_as`
- overloads (https://github.com/fmtlib/fmt/pull/3575).
- Thanks @phprus.
+ overloads (https://github.com/fmtlib/fmt/pull/3575). Thanks @phprus.
- Made `formatted_size` work with integral format specifiers at
compile time (https://github.com/fmtlib/fmt/pull/3591).
Thanks @elbeno.
@@ -388,7 +632,9 @@
Thanks @ShawnZhong.
-- Added a formatter for `std::optional` to `fmt/std.h`.
+- Added a formatter for `std::optional` to `fmt/std.h`
+ (https://github.com/fmtlib/fmt/issues/1367,
+ https://github.com/fmtlib/fmt/pull/3303).
Thanks @tom-huntington.
- Fixed formatting of valueless by exception variants
diff --git a/METADATA b/METADATA
index b4f11829..b4da998f 100644
--- a/METADATA
+++ b/METADATA
@@ -1,19 +1,20 @@
+# This project was upgraded with external_updater.
+# Usage: tools/external_updater/updater.sh update external/fmtlib
+# For more info, check https://cs.android.com/android/platform/superproject/+/main:tools/external_updater/README.md
+
name: "fmtlib"
description: "{fmt} is an open-source formatting library providing a fast and safe alternative to C stdio and C++ iostreams."
third_party {
- url {
- type: HOMEPAGE
- value: "https://github.com/fmtlib/fmt"
- }
- url {
- type: GIT
- value: "https://github.com/fmtlib/fmt.git"
- }
- version: "10.1.1"
license_type: NOTICE
last_upgrade_date {
- year: 2020
- month: 12
- day: 7
+ year: 2024
+ month: 3
+ day: 5
+ }
+ homepage: "https://github.com/fmtlib/fmt"
+ identifier {
+ type: "Git"
+ value: "https://github.com/fmtlib/fmt.git"
+ version: "10.2.0"
}
}
diff --git a/README.md b/README.md
new file mode 100644
index 00000000..dcfb16ec
--- /dev/null
+++ b/README.md
@@ -0,0 +1,490 @@
+<img src="https://user-images.githubusercontent.com/576385/156254208-f5b743a9-88cf-439d-b0c0-923d53e8d551.png" alt="{fmt}" width="25%"/>
+
+[![image](https://github.com/fmtlib/fmt/workflows/linux/badge.svg)](https://github.com/fmtlib/fmt/actions?query=workflow%3Alinux)
+[![image](https://github.com/fmtlib/fmt/workflows/macos/badge.svg)](https://github.com/fmtlib/fmt/actions?query=workflow%3Amacos)
+[![image](https://github.com/fmtlib/fmt/workflows/windows/badge.svg)](https://github.com/fmtlib/fmt/actions?query=workflow%3Awindows)
+[![fmt is continuously fuzzed at oss-fuzz](https://oss-fuzz-build-logs.storage.googleapis.com/badges/fmt.svg)](https://bugs.chromium.org/p/oss-fuzz/issues/list?\%0Acolspec=ID%20Type%20Component%20Status%20Proj%20Reported%20Owner%20\%0ASummary&q=proj%3Dfmt&can=1)
+[![Ask questions at StackOverflow with the tag fmt](https://img.shields.io/badge/stackoverflow-fmt-blue.svg)](https://stackoverflow.com/questions/tagged/fmt)
+[![image](https://api.securityscorecards.dev/projects/github.com/fmtlib/fmt/badge)](https://securityscorecards.dev/viewer/?uri=github.com/fmtlib/fmt)
+
+**{fmt}** is an open-source formatting library providing a fast and safe
+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/>.
+
+[Documentation](https://fmt.dev)
+
+[Cheat Sheets](https://hackingcpp.com/cpp/libs/fmt.html)
+
+Q&A: ask questions on [StackOverflow with the tag
+fmt](https://stackoverflow.com/questions/tagged/fmt).
+
+Try {fmt} in [Compiler Explorer](https://godbolt.org/z/Eq5763).
+
+# Features
+
+- Simple [format API](https://fmt.dev/latest/api.html) with positional
+ arguments for localization
+- Implementation of [C++20
+ std::format](https://en.cppreference.com/w/cpp/utility/format) and
+ [C++23 std::print](https://en.cppreference.com/w/cpp/io/print)
+- [Format string syntax](https://fmt.dev/latest/syntax.html) similar
+ to Python\'s
+ [format](https://docs.python.org/3/library/stdtypes.html#str.format)
+- Fast IEEE 754 floating-point formatter with correct rounding,
+ shortness and round-trip guarantees using the
+ [Dragonbox](https://github.com/jk-jeon/dragonbox) algorithm
+- Portable Unicode support
+- Safe [printf
+ implementation](https://fmt.dev/latest/api.html#printf-formatting)
+ including the POSIX extension for positional arguments
+- Extensibility: [support for user-defined
+ types](https://fmt.dev/latest/api.html#formatting-user-defined-types)
+- High performance: faster than common standard library
+ implementations of `(s)printf`, iostreams, `to_string` and
+ `to_chars`, see [Speed tests](#speed-tests) and [Converting a
+ hundred million integers to strings per
+ second](http://www.zverovich.net/2020/06/13/fast-int-to-string-revisited.html)
+- Small code size both in terms of source code with the minimum
+ configuration consisting of just three files, `core.h`, `format.h`
+ and `format-inl.h`, and compiled code; see [Compile time and code
+ bloat](#compile-time-and-code-bloat)
+- Reliability: the library has an extensive set of
+ [tests](https://github.com/fmtlib/fmt/tree/master/test) and is
+ [continuously fuzzed](https://bugs.chromium.org/p/oss-fuzz/issues/list?colspec=ID%20Type%20Component%20Status%20Proj%20Reported%20Owner%20Summary&q=proj%3Dfmt&can=1)
+- Safety: the library is fully type-safe, errors in format strings can
+ be reported at compile time, automatic memory management prevents
+ buffer overflow errors
+- Ease of use: small self-contained code base, no external
+ dependencies, permissive MIT
+ [license](https://github.com/fmtlib/fmt/blob/master/LICENSE.rst)
+- [Portability](https://fmt.dev/latest/index.html#portability) with
+ consistent output across platforms and support for older compilers
+- Clean warning-free codebase even on high warning levels such as
+ `-Wall -Wextra -pedantic`
+- Locale independence by default
+- Optional header-only configuration enabled with the
+ `FMT_HEADER_ONLY` macro
+
+See the [documentation](https://fmt.dev) for more details.
+
+# Examples
+
+**Print to stdout** ([run](https://godbolt.org/z/Tevcjh))
+
+``` c++
+#include <fmt/core.h>
+
+int main() {
+ fmt::print("Hello, world!\n");
+}
+```
+
+**Format a string** ([run](https://godbolt.org/z/oK8h33))
+
+``` c++
+std::string s = fmt::format("The answer is {}.", 42);
+// s == "The answer is 42."
+```
+
+**Format a string using positional arguments**
+([run](https://godbolt.org/z/Yn7Txe))
+
+``` c++
+std::string s = fmt::format("I'd rather be {1} than {0}.", "right", "happy");
+// s == "I'd rather be happy than right."
+```
+
+**Print dates and times** ([run](https://godbolt.org/z/c31ExdY3W))
+
+``` c++
+#include <fmt/chrono.h>
+
+int main() {
+ auto now = std::chrono::system_clock::now();
+ fmt::print("Date and time: {}\n", now);
+ fmt::print("Time: {:%H:%M}\n", now);
+}
+```
+
+Output:
+
+ Date and time: 2023-12-26 19:10:31.557195597
+ Time: 19:10
+
+**Print a container** ([run](https://godbolt.org/z/MxM1YqjE7))
+
+``` c++
+#include <vector>
+#include <fmt/ranges.h>
+
+int main() {
+ std::vector<int> v = {1, 2, 3};
+ fmt::print("{}\n", v);
+}
+```
+
+Output:
+
+ [1, 2, 3]
+
+**Check a format string at compile time**
+
+``` c++
+std::string s = fmt::format("{:d}", "I am not a number");
+```
+
+This gives a compile-time error in C++20 because `d` is an invalid
+format specifier for a string.
+
+**Write a file from a single thread**
+
+``` c++
+#include <fmt/os.h>
+
+int main() {
+ auto out = fmt::output_file("guide.txt");
+ out.print("Don't {}", "Panic");
+}
+```
+
+This can be [5 to 9 times faster than
+fprintf](http://www.zverovich.net/2020/08/04/optimal-file-buffer-size.html).
+
+**Print with colors and text styles**
+
+``` c++
+#include <fmt/color.h>
+
+int main() {
+ fmt::print(fg(fmt::color::crimson) | fmt::emphasis::bold,
+ "Hello, {}!\n", "world");
+ fmt::print(fg(fmt::color::floral_white) | bg(fmt::color::slate_gray) |
+ fmt::emphasis::underline, "Olá, {}!\n", "Mundo");
+ fmt::print(fg(fmt::color::steel_blue) | fmt::emphasis::italic,
+ "你好{}!\n", "世界");
+}
+```
+
+Output on a modern terminal with Unicode support:
+
+![image](https://github.com/fmtlib/fmt/assets/%0A576385/2a93c904-d6fa-4aa6-b453-2618e1c327d7)
+
+# Benchmarks
+
+## Speed tests
+
+| 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 |
+
+{fmt} is the fastest of the benchmarked methods, \~20% 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
+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
+[source](https://github.com/fmtlib/format-benchmark/blob/master/src/tinyformat-test.cc).
+
+{fmt} is up to 20-30x faster than `std::ostringstream` and `sprintf` on
+IEEE754 `float` and `double` formatting
+([dtoa-benchmark](https://github.com/fmtlib/dtoa-benchmark)) and faster
+than [double-conversion](https://github.com/google/double-conversion)
+and [ryu](https://github.com/ulfjack/ryu):
+
+[![image](https://user-images.githubusercontent.com/576385/95684665-11719600-0ba8-11eb-8e5b-972ff4e49428.png)](https://fmt.dev/unknown_mac64_clang12.0.html)
+
+## Compile time and code bloat
+
+The script
+[bloat-test.py](https://github.com/fmtlib/format-benchmark/blob/master/bloat-test.py)
+from [format-benchmark](https://github.com/fmtlib/format-benchmark)
+tests compile time and code bloat for nontrivial projects. It generates
+100 translation units and uses `printf()` or its alternative five times
+in each to simulate a medium-sized project. The resulting executable
+size and compile time (Apple LLVM version 8.1.0 (clang-802.0.42), macOS
+Sierra, best of three) is shown in the following tables.
+
+**Optimized build (-O3)**
+
+| Method | Compile Time, s | Executable size, KiB | Stripped size, KiB |
+|---------------|-----------------|----------------------|--------------------|
+| printf | 2.6 | 29 | 26 |
+| printf+string | 16.4 | 29 | 26 |
+| iostreams | 31.1 | 59 | 55 |
+| {fmt} | 19.0 | 37 | 34 |
+| Boost Format | 91.9 | 226 | 203 |
+| Folly Format | 115.7 | 101 | 88 |
+
+As you can see, {fmt} has 60% less overhead in terms of resulting binary
+code size compared to iostreams and comes pretty close to `printf`.
+Boost Format and Folly Format have the largest overheads.
+
+`printf+string` is the same as `printf` but with an extra `<string>`
+include to measure the overhead of the latter.
+
+**Non-optimized build**
+
+| Method | Compile Time, s | Executable size, KiB | Stripped size, KiB |
+|---------------|-----------------|----------------------|--------------------|
+| printf | 2.2 | 33 | 30 |
+| printf+string | 16.0 | 33 | 30 |
+| iostreams | 28.3 | 56 | 52 |
+| {fmt} | 18.2 | 59 | 50 |
+| Boost Format | 54.1 | 365 | 303 |
+| Folly Format | 79.9 | 445 | 430 |
+
+`libc`, `lib(std)c++`, and `libfmt` are all linked as shared libraries
+to compare formatting function overhead only. Boost Format is a
+header-only library so it doesn\'t provide any linkage options.
+
+## Running the tests
+
+Please refer to [Building the
+library](https://fmt.dev/latest/usage.html#building-the-library) for
+instructions on how to build the library and run the unit tests.
+
+Benchmarks reside in a separate repository,
+[format-benchmarks](https://github.com/fmtlib/format-benchmark), so to
+run the benchmarks you first need to clone this repository and generate
+Makefiles with CMake:
+
+ $ git clone --recursive https://github.com/fmtlib/format-benchmark.git
+ $ cd format-benchmark
+ $ cmake .
+
+Then you can run the speed test:
+
+ $ make speed-test
+
+or the bloat test:
+
+ $ make bloat-test
+
+# Migrating code
+
+[clang-tidy](https://clang.llvm.org/extra/clang-tidy/) v17 (not yet
+released) provides the
+[modernize-use-std-print](https://clang.llvm.org/extra/clang-tidy/checks/modernize/use-std-print.html)
+check that is capable of converting occurrences of `printf` and
+`fprintf` to `fmt::print` if configured to do so. (By default it
+converts to `std::print`.)
+
+# Notable projects using this library
+
+- [0 A.D.](https://play0ad.com/): a free, open-source, cross-platform
+ real-time strategy game
+- [AMPL/MP](https://github.com/ampl/mp): an open-source library for
+ mathematical programming
+- [Apple's FoundationDB](https://github.com/apple/foundationdb): an open-source,
+ distributed, transactional key-value store
+- [Aseprite](https://github.com/aseprite/aseprite): animated sprite
+ editor & pixel art tool
+- [AvioBook](https://www.aviobook.aero/en): a comprehensive aircraft
+ operations suite
+- [Blizzard Battle.net](https://battle.net/): an online gaming
+ platform
+- [Celestia](https://celestia.space/): real-time 3D visualization of
+ space
+- [Ceph](https://ceph.com/): a scalable distributed storage system
+- [ccache](https://ccache.dev/): a compiler cache
+- [ClickHouse](https://github.com/ClickHouse/ClickHouse): an
+ analytical database management system
+- [Contour](https://github.com/contour-terminal/contour/): a modern
+ terminal emulator
+- [CUAUV](https://cuauv.org/): Cornell University\'s autonomous
+ underwater vehicle
+- [Drake](https://drake.mit.edu/): a planning, control, and analysis
+ toolbox for nonlinear dynamical systems (MIT)
+- [Envoy](https://lyft.github.io/envoy/): C++ L7 proxy and
+ communication bus (Lyft)
+- [FiveM](https://fivem.net/): a modification framework for GTA V
+- [fmtlog](https://github.com/MengRao/fmtlog): a performant
+ fmtlib-style logging library with latency in nanoseconds
+- [Folly](https://github.com/facebook/folly): Facebook open-source
+ library
+- [GemRB](https://gemrb.org/): a portable open-source implementation
+ of Bioware's Infinity Engine
+- [Grand Mountain
+ Adventure](https://store.steampowered.com/app/1247360/Grand_Mountain_Adventure/):
+ a beautiful open-world ski & snowboarding game
+- [HarpyWar/pvpgn](https://github.com/pvpgn/pvpgn-server): Player vs
+ Player Gaming Network with tweaks
+- [KBEngine](https://github.com/kbengine/kbengine): an open-source
+ MMOG server engine
+- [Keypirinha](https://keypirinha.com/): a semantic launcher for
+ Windows
+- [Kodi](https://kodi.tv/) (formerly xbmc): home theater software
+- [Knuth](https://kth.cash/): high-performance Bitcoin full-node
+- [libunicode](https://github.com/contour-terminal/libunicode/): a
+ modern C++17 Unicode library
+- [MariaDB](https://mariadb.org/): relational database management
+ system
+- [Microsoft Verona](https://github.com/microsoft/verona): research
+ programming language for concurrent ownership
+- [MongoDB](https://mongodb.com/): distributed document database
+- [MongoDB Smasher](https://github.com/duckie/mongo_smasher): a small
+ tool to generate randomized datasets
+- [OpenSpace](https://openspaceproject.com/): an open-source
+ astrovisualization framework
+- [PenUltima Online (POL)](https://www.polserver.com/): an MMO server,
+ compatible with most Ultima Online clients
+- [PyTorch](https://github.com/pytorch/pytorch): an open-source
+ machine learning library
+- [quasardb](https://www.quasardb.net/): a distributed,
+ high-performance, associative database
+- [Quill](https://github.com/odygrd/quill): asynchronous low-latency
+ logging library
+- [QKW](https://github.com/ravijanjam/qkw): generalizing aliasing to
+ simplify navigation, and executing complex multi-line terminal
+ command sequences
+- [redis-cerberus](https://github.com/HunanTV/redis-cerberus): a Redis
+ cluster proxy
+- [redpanda](https://vectorized.io/redpanda): a 10x faster Kafka®
+ replacement for mission-critical systems written in C++
+- [rpclib](http://rpclib.net/): a modern C++ msgpack-RPC server and
+ client library
+- [Salesforce Analytics
+ Cloud](https://www.salesforce.com/analytics-cloud/overview/):
+ business intelligence software
+- [Scylla](https://www.scylladb.com/): a Cassandra-compatible NoSQL
+ data store that can handle 1 million transactions per second on a
+ single server
+- [Seastar](http://www.seastar-project.org/): an advanced, open-source
+ C++ framework for high-performance server applications on modern
+ hardware
+- [spdlog](https://github.com/gabime/spdlog): super fast C++ logging
+ library
+- [Stellar](https://www.stellar.org/): financial platform
+- [Touch Surgery](https://www.touchsurgery.com/): surgery simulator
+- [TrinityCore](https://github.com/TrinityCore/TrinityCore):
+ open-source MMORPG framework
+- [🐙 userver framework](https://userver.tech/): open-source
+ asynchronous framework with a rich set of abstractions and database
+ drivers
+- [Windows Terminal](https://github.com/microsoft/terminal): the new
+ Windows terminal
+
+[More\...](https://github.com/search?q=fmtlib&type=Code)
+
+If you are aware of other projects using this library, please let me
+know by [email](mailto:victor.zverovich@gmail.com) or by submitting an
+[issue](https://github.com/fmtlib/fmt/issues).
+
+# Motivation
+
+So why yet another formatting library?
+
+There are plenty of methods for doing this task, from standard ones like
+the printf family of function and iostreams to Boost Format and
+FastFormat libraries. The reason for creating a new library is that
+every existing solution that I found either had serious issues or
+didn\'t provide all the features I needed.
+
+## printf
+
+The good thing about `printf` is that it is pretty fast and readily
+available being a part of the C standard library. The main drawback is
+that it doesn\'t support user-defined types. `printf` also has safety
+issues although they are somewhat mitigated with [\_\_attribute\_\_
+((format (printf,
+\...))](https://gcc.gnu.org/onlinedocs/gcc/Function-Attributes.html) in
+GCC. There is a POSIX extension that adds positional arguments required
+for
+[i18n](https://en.wikipedia.org/wiki/Internationalization_and_localization)
+to `printf` but it is not a part of C99 and may not be available on some
+platforms.
+
+## iostreams
+
+The main issue with iostreams is best illustrated with an example:
+
+``` c++
+std::cout << std::setprecision(2) << std::fixed << 1.23456 << "\n";
+```
+
+which is a lot of typing compared to printf:
+
+``` c++
+printf("%.2f\n", 1.23456);
+```
+
+Matthew Wilson, the author of FastFormat, called this \"chevron hell\".
+iostreams don\'t support positional arguments by design.
+
+The good part is that iostreams support user-defined types and are safe
+although error handling is awkward.
+
+## Boost Format
+
+This is a very powerful library that supports both `printf`-like format
+strings and positional arguments. Its main drawback is performance.
+According to various benchmarks, it is much slower than other methods
+considered here. Boost Format also has excessive build times and severe
+code bloat issues (see [Benchmarks](#benchmarks)).
+
+## FastFormat
+
+This is an interesting library that is fast, safe, and has positional
+arguments. However, it has significant limitations, citing its author:
+
+> Three features that have no hope of being accommodated within the
+> current design are:
+>
+> - Leading zeros (or any other non-space padding)
+> - Octal/hexadecimal encoding
+> - Runtime width/alignment specification
+
+It is also quite big and has a heavy dependency, STLSoft, which might be
+too restrictive for using it in some projects.
+
+## Boost Spirit.Karma
+
+This is not a formatting library but I decided to include it here for
+completeness. As iostreams, it suffers from the problem of mixing
+verbatim text with arguments. The library is pretty fast, but slower on
+integer formatting than `fmt::format_to` with format string compilation
+on Karma\'s own benchmark, see [Converting a hundred million integers to
+strings per
+second](http://www.zverovich.net/2020/06/13/fast-int-to-string-revisited.html).
+
+# License
+
+{fmt} is distributed under the MIT
+[license](https://github.com/fmtlib/fmt/blob/master/LICENSE).
+
+# Documentation License
+
+The [Format String Syntax](https://fmt.dev/latest/syntax.html) section
+in the documentation is based on the one from Python [string module
+documentation](https://docs.python.org/3/library/string.html#module-string).
+For this reason, the documentation is distributed under the Python
+Software Foundation license available in
+[doc/python-license.txt](https://raw.github.com/fmtlib/fmt/master/doc/python-license.txt).
+It only applies if you distribute the documentation of {fmt}.
+
+# Maintainers
+
+The {fmt} library is maintained by Victor Zverovich
+([vitaut](https://github.com/vitaut)) with contributions from many other
+people. See
+[Contributors](https://github.com/fmtlib/fmt/graphs/contributors) and
+[Releases](https://github.com/fmtlib/fmt/releases) for some of the
+names. Let us know if your contribution is not listed or mentioned
+incorrectly and we\'ll make it right.
+
+# Security Policy
+
+To report a security issue, please disclose it at [security
+advisory](https://github.com/fmtlib/fmt/security/advisories/new).
+
+This project is maintained by a team of volunteers on a
+reasonable-effort basis. As such, please give us at least 90 days to
+work on a fix before public exposure.
diff --git a/README.rst b/README.rst
deleted file mode 100644
index 81948bd6..00000000
--- a/README.rst
+++ /dev/null
@@ -1,553 +0,0 @@
-.. image:: https://user-images.githubusercontent.com/
- 576385/156254208-f5b743a9-88cf-439d-b0c0-923d53e8d551.png
- :width: 25%
- :alt: {fmt}
-
-.. image:: https://github.com/fmtlib/fmt/workflows/linux/badge.svg
- :target: https://github.com/fmtlib/fmt/actions?query=workflow%3Alinux
-
-.. image:: https://github.com/fmtlib/fmt/workflows/macos/badge.svg
- :target: https://github.com/fmtlib/fmt/actions?query=workflow%3Amacos
-
-.. image:: https://github.com/fmtlib/fmt/workflows/windows/badge.svg
- :target: https://github.com/fmtlib/fmt/actions?query=workflow%3Awindows
-
-.. image:: https://oss-fuzz-build-logs.storage.googleapis.com/badges/fmt.svg
- :alt: fmt is continuously fuzzed at oss-fuzz
- :target: https://bugs.chromium.org/p/oss-fuzz/issues/list?\
- colspec=ID%20Type%20Component%20Status%20Proj%20Reported%20Owner%20\
- Summary&q=proj%3Dfmt&can=1
-
-.. image:: https://img.shields.io/badge/stackoverflow-fmt-blue.svg
- :alt: Ask questions at StackOverflow with the tag fmt
- :target: https://stackoverflow.com/questions/tagged/fmt
-
-.. image:: https://api.securityscorecards.dev/projects/github.com/fmtlib/fmt/badge
- :target: https://securityscorecards.dev/viewer/?uri=github.com/fmtlib/fmt
-
-**{fmt}** is an open-source formatting library providing a fast and safe
-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/.
-
-`Documentation <https://fmt.dev>`__
-
-`Cheat Sheets <https://hackingcpp.com/cpp/libs/fmt.html>`__
-
-Q&A: ask questions on `StackOverflow with the tag fmt
-<https://stackoverflow.com/questions/tagged/fmt>`_.
-
-Try {fmt} in `Compiler Explorer <https://godbolt.org/z/Eq5763>`_.
-
-Features
---------
-
-* Simple `format API <https://fmt.dev/latest/api.html>`_ with positional arguments
- for localization
-* Implementation of `C++20 std::format
- <https://en.cppreference.com/w/cpp/utility/format>`__ and `C++23 std::print
- <https://en.cppreference.com/w/cpp/io/print>`__
-* `Format string syntax <https://fmt.dev/latest/syntax.html>`_ similar to Python's
- `format <https://docs.python.org/3/library/stdtypes.html#str.format>`_
-* Fast IEEE 754 floating-point formatter with correct rounding, shortness and
- round-trip guarantees using the `Dragonbox <https://github.com/jk-jeon/dragonbox>`_
- algorithm
-* Portable Unicode support
-* Safe `printf implementation
- <https://fmt.dev/latest/api.html#printf-formatting>`_ including the POSIX
- extension for positional arguments
-* Extensibility: `support for user-defined types
- <https://fmt.dev/latest/api.html#formatting-user-defined-types>`_
-* High performance: faster than common standard library implementations of
- ``(s)printf``, iostreams, ``to_string`` and ``to_chars``, see `Speed tests`_
- and `Converting a hundred million integers to strings per second
- <http://www.zverovich.net/2020/06/13/fast-int-to-string-revisited.html>`_
-* Small code size both in terms of source code with the minimum configuration
- consisting of just three files, ``core.h``, ``format.h`` and ``format-inl.h``,
- and compiled code; see `Compile time and code bloat`_
-* Reliability: the library has an extensive set of `tests
- <https://github.com/fmtlib/fmt/tree/master/test>`_ and is `continuously fuzzed
- <https://bugs.chromium.org/p/oss-fuzz/issues/list?colspec=ID%20Type%20
- Component%20Status%20Proj%20Reported%20Owner%20Summary&q=proj%3Dfmt&can=1>`_
-* Safety: the library is fully type-safe, errors in format strings can be
- reported at compile time, automatic memory management prevents buffer overflow
- errors
-* Ease of use: small self-contained code base, no external dependencies,
- permissive MIT `license
- <https://github.com/fmtlib/fmt/blob/master/LICENSE.rst>`_
-* `Portability <https://fmt.dev/latest/index.html#portability>`_ with
- consistent output across platforms and support for older compilers
-* Clean warning-free codebase even on high warning levels such as
- ``-Wall -Wextra -pedantic``
-* Locale independence by default
-* Optional header-only configuration enabled with the ``FMT_HEADER_ONLY`` macro
-
-See the `documentation <https://fmt.dev>`_ for more details.
-
-Examples
---------
-
-**Print to stdout** (`run <https://godbolt.org/z/Tevcjh>`_)
-
-.. code:: c++
-
- #include <fmt/core.h>
-
- int main() {
- fmt::print("Hello, world!\n");
- }
-
-**Format a string** (`run <https://godbolt.org/z/oK8h33>`_)
-
-.. code:: c++
-
- std::string s = fmt::format("The answer is {}.", 42);
- // s == "The answer is 42."
-
-**Format a string using positional arguments** (`run <https://godbolt.org/z/Yn7Txe>`_)
-
-.. code:: c++
-
- std::string s = fmt::format("I'd rather be {1} than {0}.", "right", "happy");
- // s == "I'd rather be happy than right."
-
-**Print chrono durations** (`run <https://godbolt.org/z/K8s4Mc>`_)
-
-.. code:: c++
-
- #include <fmt/chrono.h>
-
- int main() {
- using namespace std::literals::chrono_literals;
- fmt::print("Default format: {} {}\n", 42s, 100ms);
- fmt::print("strftime-like format: {:%H:%M:%S}\n", 3h + 15min + 30s);
- }
-
-Output::
-
- Default format: 42s 100ms
- strftime-like format: 03:15:30
-
-**Print a container** (`run <https://godbolt.org/z/MxM1YqjE7>`_)
-
-.. code:: c++
-
- #include <vector>
- #include <fmt/ranges.h>
-
- int main() {
- std::vector<int> v = {1, 2, 3};
- fmt::print("{}\n", v);
- }
-
-Output::
-
- [1, 2, 3]
-
-**Check a format string at compile time**
-
-.. code:: c++
-
- std::string s = fmt::format("{:d}", "I am not a number");
-
-This gives a compile-time error in C++20 because ``d`` is an invalid format
-specifier for a string.
-
-**Write a file from a single thread**
-
-.. code:: c++
-
- #include <fmt/os.h>
-
- int main() {
- auto out = fmt::output_file("guide.txt");
- out.print("Don't {}", "Panic");
- }
-
-This can be `5 to 9 times faster than fprintf
-<http://www.zverovich.net/2020/08/04/optimal-file-buffer-size.html>`_.
-
-**Print with colors and text styles**
-
-.. code:: c++
-
- #include <fmt/color.h>
-
- int main() {
- fmt::print(fg(fmt::color::crimson) | fmt::emphasis::bold,
- "Hello, {}!\n", "world");
- fmt::print(fg(fmt::color::floral_white) | bg(fmt::color::slate_gray) |
- fmt::emphasis::underline, "Olá, {}!\n", "Mundo");
- fmt::print(fg(fmt::color::steel_blue) | fmt::emphasis::italic,
- "你好{}!\n", "世界");
- }
-
-Output on a modern terminal with Unicode support:
-
-.. image:: https://github.com/fmtlib/fmt/assets/
- 576385/2a93c904-d6fa-4aa6-b453-2618e1c327d7
-
-Benchmarks
-----------
-
-Speed tests
-~~~~~~~~~~~
-
-================= ============= ===========
-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
-================= ============= ===========
-
-{fmt} is the fastest of the benchmarked methods, ~20% 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 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 `source
-<https://github.com/fmtlib/format-benchmark/blob/master/src/tinyformat-test.cc>`_.
-
-{fmt} is up to 20-30x faster than ``std::ostringstream`` and ``sprintf`` on
-IEEE754 ``float`` and ``double`` formatting (`dtoa-benchmark <https://github.com/fmtlib/dtoa-benchmark>`_)
-and faster than `double-conversion <https://github.com/google/double-conversion>`_ and
-`ryu <https://github.com/ulfjack/ryu>`_:
-
-.. image:: https://user-images.githubusercontent.com/576385/
- 95684665-11719600-0ba8-11eb-8e5b-972ff4e49428.png
- :target: https://fmt.dev/unknown_mac64_clang12.0.html
-
-Compile time and code bloat
-~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-The script `bloat-test.py
-<https://github.com/fmtlib/format-benchmark/blob/master/bloat-test.py>`_
-from `format-benchmark <https://github.com/fmtlib/format-benchmark>`_
-tests compile time and code bloat for nontrivial projects.
-It generates 100 translation units and uses ``printf()`` or its alternative
-five times in each to simulate a medium-sized project. The resulting
-executable size and compile time (Apple LLVM version 8.1.0 (clang-802.0.42),
-macOS Sierra, best of three) is shown in the following tables.
-
-**Optimized build (-O3)**
-
-============= =============== ==================== ==================
-Method Compile Time, s Executable size, KiB Stripped size, KiB
-============= =============== ==================== ==================
-printf 2.6 29 26
-printf+string 16.4 29 26
-iostreams 31.1 59 55
-{fmt} 19.0 37 34
-Boost Format 91.9 226 203
-Folly Format 115.7 101 88
-============= =============== ==================== ==================
-
-As you can see, {fmt} has 60% less overhead in terms of resulting binary code
-size compared to iostreams and comes pretty close to ``printf``. Boost Format
-and Folly Format have the largest overheads.
-
-``printf+string`` is the same as ``printf`` but with an extra ``<string>``
-include to measure the overhead of the latter.
-
-**Non-optimized build**
-
-============= =============== ==================== ==================
-Method Compile Time, s Executable size, KiB Stripped size, KiB
-============= =============== ==================== ==================
-printf 2.2 33 30
-printf+string 16.0 33 30
-iostreams 28.3 56 52
-{fmt} 18.2 59 50
-Boost Format 54.1 365 303
-Folly Format 79.9 445 430
-============= =============== ==================== ==================
-
-``libc``, ``lib(std)c++``, and ``libfmt`` are all linked as shared libraries to
-compare formatting function overhead only. Boost Format is a
-header-only library so it doesn't provide any linkage options.
-
-Running the tests
-~~~~~~~~~~~~~~~~~
-
-Please refer to `Building the library`__ for instructions on how to build
-the library and run the unit tests.
-
-__ https://fmt.dev/latest/usage.html#building-the-library
-
-Benchmarks reside in a separate repository,
-`format-benchmarks <https://github.com/fmtlib/format-benchmark>`_,
-so to run the benchmarks you first need to clone this repository and
-generate Makefiles with CMake::
-
- $ git clone --recursive https://github.com/fmtlib/format-benchmark.git
- $ cd format-benchmark
- $ cmake .
-
-Then you can run the speed test::
-
- $ make speed-test
-
-or the bloat test::
-
- $ make bloat-test
-
-Migrating code
---------------
-
-`clang-tidy <https://clang.llvm.org/extra/clang-tidy/>`_ v17 (not yet
-released) provides the `modernize-use-std-print
-<https://clang.llvm.org/extra/clang-tidy/checks/modernize/use-std-print.html>`_
-check that is capable of converting occurrences of ``printf`` and
-``fprintf`` to ``fmt::print`` if configured to do so. (By default it
-converts to ``std::print``.)
-
-Projects using this library
----------------------------
-
-* `0 A.D. <https://play0ad.com/>`_: a free, open-source, cross-platform
- real-time strategy game
-
-* `AMPL/MP <https://github.com/ampl/mp>`_:
- an open-source library for mathematical programming
-
-* `Aseprite <https://github.com/aseprite/aseprite>`_:
- animated sprite editor & pixel art tool
-
-* `AvioBook <https://www.aviobook.aero/en>`_: a comprehensive aircraft
- operations suite
-
-* `Blizzard Battle.net <https://battle.net/>`_: an online gaming platform
-
-* `Celestia <https://celestia.space/>`_: real-time 3D visualization of space
-
-* `Ceph <https://ceph.com/>`_: a scalable distributed storage system
-
-* `ccache <https://ccache.dev/>`_: a compiler cache
-
-* `ClickHouse <https://github.com/ClickHouse/ClickHouse>`_: an analytical database
- management system
-
-* `Contour <https://github.com/contour-terminal/contour/>`_: a modern terminal emulator
-
-* `CUAUV <https://cuauv.org/>`_: Cornell University's autonomous underwater
- vehicle
-
-* `Drake <https://drake.mit.edu/>`_: a planning, control, and analysis toolbox
- for nonlinear dynamical systems (MIT)
-
-* `Envoy <https://lyft.github.io/envoy/>`_: C++ L7 proxy and communication bus
- (Lyft)
-
-* `FiveM <https://fivem.net/>`_: a modification framework for GTA V
-
-* `fmtlog <https://github.com/MengRao/fmtlog>`_: a performant fmtlib-style
- logging library with latency in nanoseconds
-
-* `Folly <https://github.com/facebook/folly>`_: Facebook open-source library
-
-* `GemRB <https://gemrb.org/>`_: a portable open-source implementation of
- Bioware’s Infinity Engine
-
-* `Grand Mountain Adventure
- <https://store.steampowered.com/app/1247360/Grand_Mountain_Adventure/>`_:
- a beautiful open-world ski & snowboarding game
-
-* `HarpyWar/pvpgn <https://github.com/pvpgn/pvpgn-server>`_:
- Player vs Player Gaming Network with tweaks
-
-* `KBEngine <https://github.com/kbengine/kbengine>`_: an open-source MMOG server
- engine
-
-* `Keypirinha <https://keypirinha.com/>`_: a semantic launcher for Windows
-
-* `Kodi <https://kodi.tv/>`_ (formerly xbmc): home theater software
-
-* `Knuth <https://kth.cash/>`_: high-performance Bitcoin full-node
-
-* `libunicode <https://github.com/contour-terminal/libunicode/>`_: a modern C++17 Unicode library
-
-* `MariaDB <https://mariadb.org/>`_: relational database management system
-
-* `Microsoft Verona <https://github.com/microsoft/verona>`_:
- research programming language for concurrent ownership
-
-* `MongoDB <https://mongodb.com/>`_: distributed document database
-
-* `MongoDB Smasher <https://github.com/duckie/mongo_smasher>`_: a small tool to
- generate randomized datasets
-
-* `OpenSpace <https://openspaceproject.com/>`_: an open-source
- astrovisualization framework
-
-* `PenUltima Online (POL) <https://www.polserver.com/>`_:
- an MMO server, compatible with most Ultima Online clients
-
-* `PyTorch <https://github.com/pytorch/pytorch>`_: an open-source machine
- learning library
-
-* `quasardb <https://www.quasardb.net/>`_: a distributed, high-performance,
- associative database
-
-* `Quill <https://github.com/odygrd/quill>`_: asynchronous low-latency logging library
-
-* `QKW <https://github.com/ravijanjam/qkw>`_: generalizing aliasing to simplify
- navigation, and executing complex multi-line terminal command sequences
-
-* `redis-cerberus <https://github.com/HunanTV/redis-cerberus>`_: a Redis cluster
- proxy
-
-* `redpanda <https://vectorized.io/redpanda>`_: a 10x faster Kafka® replacement
- for mission-critical systems written in C++
-
-* `rpclib <http://rpclib.net/>`_: a modern C++ msgpack-RPC server and client
- library
-
-* `Salesforce Analytics Cloud
- <https://www.salesforce.com/analytics-cloud/overview/>`_:
- business intelligence software
-
-* `Scylla <https://www.scylladb.com/>`_: a Cassandra-compatible NoSQL data store
- that can handle 1 million transactions per second on a single server
-
-* `Seastar <http://www.seastar-project.org/>`_: an advanced, open-source C++
- framework for high-performance server applications on modern hardware
-
-* `spdlog <https://github.com/gabime/spdlog>`_: super fast C++ logging library
-
-* `Stellar <https://www.stellar.org/>`_: financial platform
-
-* `Touch Surgery <https://www.touchsurgery.com/>`_: surgery simulator
-
-* `TrinityCore <https://github.com/TrinityCore/TrinityCore>`_: open-source
- MMORPG framework
-
-* `🐙 userver framework <https://userver.tech/>`_: open-source asynchronous
- framework with a rich set of abstractions and database drivers
-
-* `Windows Terminal <https://github.com/microsoft/terminal>`_: the new Windows
- terminal
-
-`More... <https://github.com/search?q=fmtlib&type=Code>`_
-
-If you are aware of other projects using this library, please let me know
-by `email <mailto:victor.zverovich@gmail.com>`_ or by submitting an
-`issue <https://github.com/fmtlib/fmt/issues>`_.
-
-Motivation
-----------
-
-So why yet another formatting library?
-
-There are plenty of methods for doing this task, from standard ones like
-the printf family of function and iostreams to Boost Format and FastFormat
-libraries. The reason for creating a new library is that every existing
-solution that I found either had serious issues or didn't provide
-all the features I needed.
-
-printf
-~~~~~~
-
-The good thing about ``printf`` is that it is pretty fast and readily available
-being a part of the C standard library. The main drawback is that it
-doesn't support user-defined types. ``printf`` also has safety issues although
-they are somewhat mitigated with `__attribute__ ((format (printf, ...))
-<https://gcc.gnu.org/onlinedocs/gcc/Function-Attributes.html>`_ in GCC.
-There is a POSIX extension that adds positional arguments required for
-`i18n <https://en.wikipedia.org/wiki/Internationalization_and_localization>`_
-to ``printf`` but it is not a part of C99 and may not be available on some
-platforms.
-
-iostreams
-~~~~~~~~~
-
-The main issue with iostreams is best illustrated with an example:
-
-.. code:: c++
-
- std::cout << std::setprecision(2) << std::fixed << 1.23456 << "\n";
-
-which is a lot of typing compared to printf:
-
-.. code:: c++
-
- printf("%.2f\n", 1.23456);
-
-Matthew Wilson, the author of FastFormat, called this "chevron hell". iostreams
-don't support positional arguments by design.
-
-The good part is that iostreams support user-defined types and are safe although
-error handling is awkward.
-
-Boost Format
-~~~~~~~~~~~~
-
-This is a very powerful library that supports both ``printf``-like format
-strings and positional arguments. Its main drawback is performance. According to
-various benchmarks, it is much slower than other methods considered here. Boost
-Format also has excessive build times and severe code bloat issues (see
-`Benchmarks`_).
-
-FastFormat
-~~~~~~~~~~
-
-This is an interesting library that is fast, safe, and has positional arguments.
-However, it has significant limitations, citing its author:
-
- Three features that have no hope of being accommodated within the
- current design are:
-
- * Leading zeros (or any other non-space padding)
- * Octal/hexadecimal encoding
- * Runtime width/alignment specification
-
-It is also quite big and has a heavy dependency, STLSoft, which might be too
-restrictive for using it in some projects.
-
-Boost Spirit.Karma
-~~~~~~~~~~~~~~~~~~
-
-This is not a formatting library but I decided to include it here for
-completeness. As iostreams, it suffers from the problem of mixing verbatim text
-with arguments. The library is pretty fast, but slower on integer formatting
-than ``fmt::format_to`` with format string compilation on Karma's own benchmark,
-see `Converting a hundred million integers to strings per second
-<http://www.zverovich.net/2020/06/13/fast-int-to-string-revisited.html>`_.
-
-License
--------
-
-{fmt} is distributed under the MIT `license
-<https://github.com/fmtlib/fmt/blob/master/LICENSE>`_.
-
-Documentation License
----------------------
-
-The `Format String Syntax <https://fmt.dev/latest/syntax.html>`_
-section in the documentation is based on the one from Python `string module
-documentation <https://docs.python.org/3/library/string.html#module-string>`_.
-For this reason, the documentation is distributed under the Python Software
-Foundation license available in `doc/python-license.txt
-<https://raw.github.com/fmtlib/fmt/master/doc/python-license.txt>`_.
-It only applies if you distribute the documentation of {fmt}.
-
-Maintainers
------------
-
-The {fmt} library is maintained by Victor Zverovich (`vitaut
-<https://github.com/vitaut>`_) with contributions from many other people.
-See `Contributors <https://github.com/fmtlib/fmt/graphs/contributors>`_ and
-`Releases <https://github.com/fmtlib/fmt/releases>`_ for some of the names.
-Let us know if your contribution is not listed or mentioned incorrectly and
-we'll make it right.
-
-Security Policy
----------------
-
-To report a security issue, please disclose it at `security advisory <https://github.com/fmtlib/fmt/security/advisories/new>`_.
-
-This project is maintained by a team of volunteers on a reasonable-effort basis. As such, please give us at least 90 days to work on a fix before public exposure.
diff --git a/doc/api.rst b/doc/api.rst
index 8b8b19c8..31b0328d 100644
--- a/doc/api.rst
+++ b/doc/api.rst
@@ -167,8 +167,8 @@ for example
will return ``" blue"``.
-The experimental ``nested_formatter`` provides an easy way applying a formatter
-to one or more subobjects.
+The experimental ``nested_formatter`` provides an easy way of applying a
+formatter to one or more subobjects.
For example::
@@ -600,7 +600,6 @@ System APIs
:members:
.. doxygenfunction:: fmt::windows_error
- :members:
.. _ostream-api:
diff --git a/doc/build.py b/doc/build.py
index bd10aa84..a6ae8f85 100755
--- a/doc/build.py
+++ b/doc/build.py
@@ -4,7 +4,12 @@
import errno, os, re, sys
from subprocess import check_call, CalledProcessError, Popen, PIPE, STDOUT
-versions = ['1.0.0', '1.1.0', '2.0.0', '3.0.2', '4.0.0', '4.1.0', '5.0.0', '5.1.0', '5.2.0', '5.2.1', '5.3.0', '6.0.0', '6.1.0', '6.1.1', '6.1.2', '6.2.0', '6.2.1', '7.0.0', '7.0.1', '7.0.2', '7.0.3', '7.1.0', '7.1.1', '7.1.2', '7.1.3', '8.0.0', '8.0.1', '8.1.0', '8.1.1', '9.0.0', '9.1.0', '10.0.0', '10.1.0', '10.1.1', '10.1.1']
+versions = [
+ '1.0.0', '1.1.0', '2.0.0', '3.0.2', '4.0.0', '4.1.0', '5.0.0', '5.1.0',
+ '5.2.0', '5.2.1', '5.3.0', '6.0.0', '6.1.0', '6.1.1', '6.1.2', '6.2.0',
+ '6.2.1', '7.0.0', '7.0.1', '7.0.2', '7.0.3', '7.1.0', '7.1.1', '7.1.2',
+ '7.1.3', '8.0.0', '8.0.1', '8.1.0', '8.1.1', '9.0.0', '9.1.0']
+versions += ['10.0.0', '10.1.0', '10.1.1', '10.1.1', '10.2.0']
class Pip:
def __init__(self, venv_dir):
@@ -31,7 +36,7 @@ def create_build_env(venv_dir='virtualenv'):
# Jinja2 >= 3.1 incompatible with sphinx 3.3.0
# See: https://github.com/sphinx-doc/sphinx/issues/10291
pip.install('Jinja2<3.1')
- pip.install('sphinx-doc/sphinx', 'v3.3.0')
+ pip.install('sphinx==3.3.0')
pip.install('michaeljones/breathe', 'v4.25.0')
def build_docs(version='dev', **kwargs):
@@ -50,7 +55,7 @@ def build_docs(version='dev', **kwargs):
GENERATE_RTF = NO
CASE_SENSE_NAMES = NO
INPUT = {0}/args.h {0}/chrono.h {0}/color.h {0}/core.h \
- {0}/compile.h {0}/format.h {0}/os.h {0}/ostream.h \
+ {0}/compile.h {0}/format.h {0}/os.h {0}/ostream.h \
{0}/printf.h {0}/xchar.h
QUIET = YES
JAVADOC_AUTOBRIEF = YES
diff --git a/doc/syntax.rst b/doc/syntax.rst
index 74b64c5a..3c21902c 100644
--- a/doc/syntax.rst
+++ b/doc/syntax.rst
@@ -82,7 +82,7 @@ The general form of a *standard format specifier* is:
width: `integer` | "{" [`arg_id`] "}"
precision: `integer` | "{" [`arg_id`] "}"
type: "a" | "A" | "b" | "B" | "c" | "d" | "e" | "E" | "f" | "F" | "g" | "G" |
- : "o" | "p" | "s" | "x" | "X"
+ : "o" | "p" | "s" | "x" | "X" | "?"
The *fill* character can be any Unicode code point other than ``'{'`` or
``'}'``. The presence of a fill character is signaled by the character following
@@ -177,6 +177,9 @@ The available string presentation types are:
| ``'s'`` | String format. This is the default type for strings and |
| | may be omitted. |
+---------+----------------------------------------------------------+
+| ``'?'`` | Debug format. The string is quoted and special |
+| | characters escaped. |
++---------+----------------------------------------------------------+
| none | The same as ``'s'``. |
+---------+----------------------------------------------------------+
@@ -188,6 +191,9 @@ The available character presentation types are:
| ``'c'`` | Character format. This is the default type for |
| | characters and may be omitted. |
+---------+----------------------------------------------------------+
+| ``'?'`` | Debug format. The character is quoted and special |
+| | characters escaped. |
++---------+----------------------------------------------------------+
| none | The same as ``'c'``. |
+---------+----------------------------------------------------------+
@@ -223,9 +229,10 @@ The available integer presentation types are:
| none | The same as ``'d'``. |
+---------+----------------------------------------------------------+
-Integer presentation types can also be used with character and Boolean values.
-Boolean values are formatted using textual representation, either ``true`` or
-``false``, if the presentation type is not specified.
+Integer presentation types can also be used with character and Boolean values
+with the only exception that ``'c'`` cannot be used with `bool`. Boolean values
+are formatted using textual representation, either ``true`` or ``false``, if the
+presentation type is not specified.
The available presentation types for floating-point values are:
diff --git a/include/fmt/args.h b/include/fmt/args.h
index 2d684e7c..ad1654bb 100644
--- a/include/fmt/args.h
+++ b/include/fmt/args.h
@@ -22,8 +22,9 @@ template <typename T> struct is_reference_wrapper : std::false_type {};
template <typename T>
struct is_reference_wrapper<std::reference_wrapper<T>> : std::true_type {};
-template <typename T> const T& unwrap(const T& v) { return v; }
-template <typename T> const T& unwrap(const std::reference_wrapper<T>& v) {
+template <typename T> auto unwrap(const T& v) -> const T& { return v; }
+template <typename T>
+auto unwrap(const std::reference_wrapper<T>& v) -> const T& {
return static_cast<const T&>(v);
}
@@ -50,7 +51,7 @@ class dynamic_arg_list {
std::unique_ptr<node<>> head_;
public:
- template <typename T, typename Arg> const T& push(const Arg& arg) {
+ template <typename T, typename Arg> auto push(const Arg& arg) -> const T& {
auto new_node = std::unique_ptr<typed_node<T>>(new typed_node<T>(arg));
auto& value = new_node->value;
new_node->next = std::move(head_);
@@ -110,14 +111,14 @@ class dynamic_format_arg_store
friend class basic_format_args<Context>;
- unsigned long long get_types() const {
+ auto get_types() const -> unsigned long long {
return detail::is_unpacked_bit | data_.size() |
(named_info_.empty()
? 0ULL
: static_cast<unsigned long long>(detail::has_named_args_bit));
}
- const basic_format_arg<Context>* data() const {
+ auto data() const -> const basic_format_arg<Context>* {
return named_info_.empty() ? data_.data() : data_.data() + 1;
}
diff --git a/include/fmt/chrono.h b/include/fmt/chrono.h
index 7bd4206d..9d54574e 100644
--- a/include/fmt/chrono.h
+++ b/include/fmt/chrono.h
@@ -18,7 +18,7 @@
#include <ostream>
#include <type_traits>
-#include "format.h"
+#include "ostream.h" // formatbuf
FMT_BEGIN_NAMESPACE
@@ -72,7 +72,8 @@ template <typename To, typename From,
FMT_ENABLE_IF(!std::is_same<From, To>::value &&
std::numeric_limits<From>::is_signed ==
std::numeric_limits<To>::is_signed)>
-FMT_CONSTEXPR To lossless_integral_conversion(const From from, int& ec) {
+FMT_CONSTEXPR auto lossless_integral_conversion(const From from, int& ec)
+ -> To {
ec = 0;
using F = std::numeric_limits<From>;
using T = std::numeric_limits<To>;
@@ -101,7 +102,8 @@ template <typename To, typename From,
FMT_ENABLE_IF(!std::is_same<From, To>::value &&
std::numeric_limits<From>::is_signed !=
std::numeric_limits<To>::is_signed)>
-FMT_CONSTEXPR To lossless_integral_conversion(const From from, int& ec) {
+FMT_CONSTEXPR auto lossless_integral_conversion(const From from, int& ec)
+ -> To {
ec = 0;
using F = std::numeric_limits<From>;
using T = std::numeric_limits<To>;
@@ -133,7 +135,8 @@ FMT_CONSTEXPR To lossless_integral_conversion(const From from, int& ec) {
template <typename To, typename From,
FMT_ENABLE_IF(std::is_same<From, To>::value)>
-FMT_CONSTEXPR To lossless_integral_conversion(const From from, int& ec) {
+FMT_CONSTEXPR auto lossless_integral_conversion(const From from, int& ec)
+ -> To {
ec = 0;
return from;
} // function
@@ -154,7 +157,7 @@ FMT_CONSTEXPR To lossless_integral_conversion(const From from, int& ec) {
// clang-format on
template <typename To, typename From,
FMT_ENABLE_IF(!std::is_same<From, To>::value)>
-FMT_CONSTEXPR To safe_float_conversion(const From from, int& ec) {
+FMT_CONSTEXPR auto safe_float_conversion(const From from, int& ec) -> To {
ec = 0;
using T = std::numeric_limits<To>;
static_assert(std::is_floating_point<From>::value, "From must be floating");
@@ -176,7 +179,7 @@ FMT_CONSTEXPR To safe_float_conversion(const From from, int& ec) {
template <typename To, typename From,
FMT_ENABLE_IF(std::is_same<From, To>::value)>
-FMT_CONSTEXPR To safe_float_conversion(const From from, int& ec) {
+FMT_CONSTEXPR auto safe_float_conversion(const From from, int& ec) -> To {
ec = 0;
static_assert(std::is_floating_point<From>::value, "From must be floating");
return from;
@@ -188,8 +191,8 @@ FMT_CONSTEXPR To safe_float_conversion(const From from, int& ec) {
template <typename To, typename FromRep, typename FromPeriod,
FMT_ENABLE_IF(std::is_integral<FromRep>::value),
FMT_ENABLE_IF(std::is_integral<typename To::rep>::value)>
-To safe_duration_cast(std::chrono::duration<FromRep, FromPeriod> from,
- int& ec) {
+auto safe_duration_cast(std::chrono::duration<FromRep, FromPeriod> from,
+ int& ec) -> To {
using From = std::chrono::duration<FromRep, FromPeriod>;
ec = 0;
// the basic idea is that we need to convert from count() in the from type
@@ -240,8 +243,8 @@ To safe_duration_cast(std::chrono::duration<FromRep, FromPeriod> from,
template <typename To, typename FromRep, typename FromPeriod,
FMT_ENABLE_IF(std::is_floating_point<FromRep>::value),
FMT_ENABLE_IF(std::is_floating_point<typename To::rep>::value)>
-To safe_duration_cast(std::chrono::duration<FromRep, FromPeriod> from,
- int& ec) {
+auto safe_duration_cast(std::chrono::duration<FromRep, FromPeriod> from,
+ int& ec) -> To {
using From = std::chrono::duration<FromRep, FromPeriod>;
ec = 0;
if (std::isnan(from.count())) {
@@ -321,12 +324,12 @@ To safe_duration_cast(std::chrono::duration<FromRep, FromPeriod> from,
namespace detail {
template <typename T = void> struct null {};
-inline null<> localtime_r FMT_NOMACRO(...) { return null<>(); }
-inline null<> localtime_s(...) { return null<>(); }
-inline null<> gmtime_r(...) { return null<>(); }
-inline null<> gmtime_s(...) { return null<>(); }
+inline auto localtime_r FMT_NOMACRO(...) -> null<> { return null<>(); }
+inline auto localtime_s(...) -> null<> { return null<>(); }
+inline auto gmtime_r(...) -> null<> { return null<>(); }
+inline auto gmtime_s(...) -> null<> { return null<>(); }
-inline const std::locale& get_classic_locale() {
+inline auto get_classic_locale() -> const std::locale& {
static const auto& locale = std::locale::classic();
return locale;
}
@@ -406,8 +409,7 @@ inline void do_write(buffer<Char>& buf, const std::tm& time,
auto&& format_buf = formatbuf<std::basic_streambuf<Char>>(buf);
auto&& os = std::basic_ostream<Char>(&format_buf);
os.imbue(loc);
- using iterator = std::ostreambuf_iterator<Char>;
- const auto& facet = std::use_facet<std::time_put<Char, iterator>>(loc);
+ const auto& facet = std::use_facet<std::time_put<Char>>(loc);
auto end = facet.put(os, os, Char(' '), &time, format, modifier);
if (end.failed()) FMT_THROW(format_error("failed to format time"));
}
@@ -430,6 +432,51 @@ auto write(OutputIt out, const std::tm& time, const std::locale& loc,
return write_encoded_tm_str(out, string_view(buf.data(), buf.size()), loc);
}
+template <typename Rep1, typename Rep2>
+struct is_same_arithmetic_type
+ : public std::integral_constant<bool,
+ (std::is_integral<Rep1>::value &&
+ std::is_integral<Rep2>::value) ||
+ (std::is_floating_point<Rep1>::value &&
+ std::is_floating_point<Rep2>::value)> {
+};
+
+template <
+ typename To, typename FromRep, typename FromPeriod,
+ FMT_ENABLE_IF(is_same_arithmetic_type<FromRep, typename To::rep>::value)>
+auto fmt_duration_cast(std::chrono::duration<FromRep, FromPeriod> from) -> To {
+#if FMT_SAFE_DURATION_CAST
+ // Throwing version of safe_duration_cast is only available for
+ // integer to integer or float to float casts.
+ int ec;
+ To to = safe_duration_cast::safe_duration_cast<To>(from, ec);
+ if (ec) FMT_THROW(format_error("cannot format duration"));
+ return to;
+#else
+ // Standard duration cast, may overflow.
+ return std::chrono::duration_cast<To>(from);
+#endif
+}
+
+template <
+ typename To, typename FromRep, typename FromPeriod,
+ FMT_ENABLE_IF(!is_same_arithmetic_type<FromRep, typename To::rep>::value)>
+auto fmt_duration_cast(std::chrono::duration<FromRep, FromPeriod> from) -> To {
+ // Mixed integer <-> float cast is not supported by safe_duration_cast.
+ return std::chrono::duration_cast<To>(from);
+}
+
+template <typename Duration>
+auto to_time_t(
+ std::chrono::time_point<std::chrono::system_clock, Duration> time_point)
+ -> std::time_t {
+ // Cannot use std::chrono::system_clock::to_time_t since this would first
+ // require a cast to std::chrono::system_clock::time_point, which could
+ // overflow.
+ return fmt_duration_cast<std::chrono::duration<std::time_t>>(
+ time_point.time_since_epoch())
+ .count();
+}
} // namespace detail
FMT_BEGIN_EXPORT
@@ -439,29 +486,29 @@ FMT_BEGIN_EXPORT
expressed in local time. Unlike ``std::localtime``, this function is
thread-safe on most platforms.
*/
-inline std::tm localtime(std::time_t time) {
+inline auto localtime(std::time_t time) -> std::tm {
struct dispatcher {
std::time_t time_;
std::tm tm_;
dispatcher(std::time_t t) : time_(t) {}
- bool run() {
+ auto run() -> bool {
using namespace fmt::detail;
return handle(localtime_r(&time_, &tm_));
}
- bool handle(std::tm* tm) { return tm != nullptr; }
+ auto handle(std::tm* tm) -> bool { return tm != nullptr; }
- bool handle(detail::null<>) {
+ auto handle(detail::null<>) -> bool {
using namespace fmt::detail;
return fallback(localtime_s(&tm_, &time_));
}
- bool fallback(int res) { return res == 0; }
+ auto fallback(int res) -> bool { return res == 0; }
#if !FMT_MSC_VERSION
- bool fallback(detail::null<>) {
+ auto fallback(detail::null<>) -> bool {
using namespace fmt::detail;
std::tm* tm = std::localtime(&time_);
if (tm) tm_ = *tm;
@@ -478,8 +525,8 @@ inline std::tm localtime(std::time_t time) {
#if FMT_USE_LOCAL_TIME
template <typename Duration>
inline auto localtime(std::chrono::local_time<Duration> time) -> std::tm {
- return localtime(std::chrono::system_clock::to_time_t(
- std::chrono::current_zone()->to_sys(time)));
+ return localtime(
+ detail::to_time_t(std::chrono::current_zone()->to_sys(time)));
}
#endif
@@ -488,29 +535,29 @@ inline auto localtime(std::chrono::local_time<Duration> time) -> std::tm {
expressed in Coordinated Universal Time (UTC). Unlike ``std::gmtime``, this
function is thread-safe on most platforms.
*/
-inline std::tm gmtime(std::time_t time) {
+inline auto gmtime(std::time_t time) -> std::tm {
struct dispatcher {
std::time_t time_;
std::tm tm_;
dispatcher(std::time_t t) : time_(t) {}
- bool run() {
+ auto run() -> bool {
using namespace fmt::detail;
return handle(gmtime_r(&time_, &tm_));
}
- bool handle(std::tm* tm) { return tm != nullptr; }
+ auto handle(std::tm* tm) -> bool { return tm != nullptr; }
- bool handle(detail::null<>) {
+ auto handle(detail::null<>) -> bool {
using namespace fmt::detail;
return fallback(gmtime_s(&tm_, &time_));
}
- bool fallback(int res) { return res == 0; }
+ auto fallback(int res) -> bool { return res == 0; }
#if !FMT_MSC_VERSION
- bool fallback(detail::null<>) {
+ auto fallback(detail::null<>) -> bool {
std::tm* tm = std::gmtime(&time_);
if (tm) tm_ = *tm;
return tm != nullptr;
@@ -523,9 +570,11 @@ inline std::tm gmtime(std::time_t time) {
return gt.tm_;
}
-inline std::tm gmtime(
- std::chrono::time_point<std::chrono::system_clock> time_point) {
- return gmtime(std::chrono::system_clock::to_time_t(time_point));
+template <typename Duration>
+inline auto gmtime(
+ std::chrono::time_point<std::chrono::system_clock, Duration> time_point)
+ -> std::tm {
+ return gmtime(detail::to_time_t(time_point));
}
namespace detail {
@@ -564,7 +613,8 @@ inline void write_digit2_separated(char* buf, unsigned a, unsigned b,
}
}
-template <typename Period> FMT_CONSTEXPR inline const char* get_units() {
+template <typename Period>
+FMT_CONSTEXPR inline auto get_units() -> const char* {
if (std::is_same<Period, std::atto>::value) return "as";
if (std::is_same<Period, std::femto>::value) return "fs";
if (std::is_same<Period, std::pico>::value) return "ps";
@@ -620,9 +670,8 @@ auto write_padding(OutputIt out, pad_type pad) -> OutputIt {
// Parses a put_time-like format string and invokes handler actions.
template <typename Char, typename Handler>
-FMT_CONSTEXPR const Char* parse_chrono_format(const Char* begin,
- const Char* end,
- Handler&& handler) {
+FMT_CONSTEXPR auto parse_chrono_format(const Char* begin, const Char* end,
+ Handler&& handler) -> const Char* {
if (begin == end || *begin == '}') return begin;
if (*begin != '%') FMT_THROW(format_error("invalid format"));
auto ptr = begin;
@@ -953,25 +1002,25 @@ struct tm_format_checker : null_chrono_spec_handler<tm_format_checker> {
FMT_CONSTEXPR void on_tz_name() {}
};
-inline const char* tm_wday_full_name(int wday) {
+inline auto tm_wday_full_name(int wday) -> const char* {
static constexpr const char* full_name_list[] = {
"Sunday", "Monday", "Tuesday", "Wednesday",
"Thursday", "Friday", "Saturday"};
return wday >= 0 && wday <= 6 ? full_name_list[wday] : "?";
}
-inline const char* tm_wday_short_name(int wday) {
+inline auto tm_wday_short_name(int wday) -> const char* {
static constexpr const char* short_name_list[] = {"Sun", "Mon", "Tue", "Wed",
"Thu", "Fri", "Sat"};
return wday >= 0 && wday <= 6 ? short_name_list[wday] : "???";
}
-inline const char* tm_mon_full_name(int mon) {
+inline auto tm_mon_full_name(int mon) -> const char* {
static constexpr const char* full_name_list[] = {
"January", "February", "March", "April", "May", "June",
"July", "August", "September", "October", "November", "December"};
return mon >= 0 && mon <= 11 ? full_name_list[mon] : "?";
}
-inline const char* tm_mon_short_name(int mon) {
+inline auto tm_mon_short_name(int mon) -> const char* {
static constexpr const char* short_name_list[] = {
"Jan", "Feb", "Mar", "Apr", "May", "Jun",
"Jul", "Aug", "Sep", "Oct", "Nov", "Dec",
@@ -1003,21 +1052,21 @@ inline void tzset_once() {
// Converts value to Int and checks that it's in the range [0, upper).
template <typename T, typename Int, FMT_ENABLE_IF(std::is_integral<T>::value)>
-inline Int to_nonnegative_int(T value, Int upper) {
- FMT_ASSERT(std::is_unsigned<Int>::value ||
- (value >= 0 && to_unsigned(value) <= to_unsigned(upper)),
- "invalid value");
- (void)upper;
+inline auto to_nonnegative_int(T value, Int upper) -> Int {
+ if (!std::is_unsigned<Int>::value &&
+ (value < 0 || to_unsigned(value) > to_unsigned(upper))) {
+ FMT_THROW(fmt::format_error("chrono value is out of range"));
+ }
return static_cast<Int>(value);
}
template <typename T, typename Int, FMT_ENABLE_IF(!std::is_integral<T>::value)>
-inline Int to_nonnegative_int(T value, Int upper) {
+inline auto to_nonnegative_int(T value, Int upper) -> Int {
if (value < 0 || value > static_cast<T>(upper))
FMT_THROW(format_error("invalid value"));
return static_cast<Int>(value);
}
-constexpr long long pow10(std::uint32_t n) {
+constexpr auto pow10(std::uint32_t n) -> long long {
return n == 0 ? 1 : 10 * pow10(n - 1);
}
@@ -1051,13 +1100,12 @@ void write_fractional_seconds(OutputIt& out, Duration d, int precision = -1) {
std::chrono::seconds::rep>::type,
std::ratio<1, detail::pow10(num_fractional_digits)>>;
- const auto fractional =
- d - std::chrono::duration_cast<std::chrono::seconds>(d);
+ const auto fractional = d - fmt_duration_cast<std::chrono::seconds>(d);
const auto subseconds =
std::chrono::treat_as_floating_point<
typename subsecond_precision::rep>::value
? fractional.count()
- : std::chrono::duration_cast<subsecond_precision>(fractional).count();
+ : fmt_duration_cast<subsecond_precision>(fractional).count();
auto n = static_cast<uint32_or_64_or_128_t<long long>>(subseconds);
const int num_digits = detail::count_digits(n);
@@ -1108,11 +1156,11 @@ void write_floating_seconds(memory_buffer& buf, Duration duration,
num_fractional_digits = 6;
}
- format_to(std::back_inserter(buf), FMT_STRING("{:.{}f}"),
- std::fmod(val * static_cast<rep>(Duration::period::num) /
- static_cast<rep>(Duration::period::den),
- static_cast<rep>(60)),
- num_fractional_digits);
+ fmt::format_to(std::back_inserter(buf), FMT_STRING("{:.{}f}"),
+ std::fmod(val * static_cast<rep>(Duration::period::num) /
+ static_cast<rep>(Duration::period::den),
+ static_cast<rep>(60)),
+ num_fractional_digits);
}
template <typename OutputIt, typename Char,
@@ -1173,8 +1221,7 @@ class tm_writer {
return static_cast<int>(l);
}
- // Algorithm:
- // https://en.wikipedia.org/wiki/ISO_week_date#Calculating_the_week_number_from_a_month_and_day_of_the_month_or_ordinal_date
+ // Algorithm: https://en.wikipedia.org/wiki/ISO_week_date.
auto iso_year_weeks(long long curr_year) const noexcept -> int {
const auto prev_year = curr_year - 1;
const auto curr_p =
@@ -1314,7 +1361,7 @@ class tm_writer {
subsecs_(subsecs),
tm_(tm) {}
- OutputIt out() const { return out_; }
+ auto out() const -> OutputIt { return out_; }
FMT_CONSTEXPR void on_text(const Char* begin, const Char* end) {
out_ = copy_str<Char>(begin, end, out_);
@@ -1578,6 +1625,7 @@ struct chrono_format_checker : null_chrono_spec_handler<chrono_format_checker> {
template <typename Char>
FMT_CONSTEXPR void on_text(const Char*, const Char*) {}
+ FMT_CONSTEXPR void on_day_of_year() {}
FMT_CONSTEXPR void on_24_hour(numeric_system, pad_type) {}
FMT_CONSTEXPR void on_12_hour(numeric_system, pad_type) {}
FMT_CONSTEXPR void on_minute(numeric_system, pad_type) {}
@@ -1596,16 +1644,16 @@ struct chrono_format_checker : null_chrono_spec_handler<chrono_format_checker> {
template <typename T,
FMT_ENABLE_IF(std::is_integral<T>::value&& has_isfinite<T>::value)>
-inline bool isfinite(T) {
+inline auto isfinite(T) -> bool {
return true;
}
template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)>
-inline T mod(T x, int y) {
+inline auto mod(T x, int y) -> T {
return x % static_cast<T>(y);
}
template <typename T, FMT_ENABLE_IF(std::is_floating_point<T>::value)>
-inline T mod(T x, int y) {
+inline auto mod(T x, int y) -> T {
return std::fmod(x, static_cast<T>(y));
}
@@ -1620,49 +1668,38 @@ template <typename T> struct make_unsigned_or_unchanged<T, true> {
using type = typename std::make_unsigned<T>::type;
};
-#if FMT_SAFE_DURATION_CAST
-// throwing version of safe_duration_cast
-template <typename To, typename FromRep, typename FromPeriod>
-To fmt_safe_duration_cast(std::chrono::duration<FromRep, FromPeriod> from) {
- int ec;
- To to = safe_duration_cast::safe_duration_cast<To>(from, ec);
- if (ec) FMT_THROW(format_error("cannot format duration"));
- return to;
-}
-#endif
-
template <typename Rep, typename Period,
FMT_ENABLE_IF(std::is_integral<Rep>::value)>
-inline std::chrono::duration<Rep, std::milli> get_milliseconds(
- std::chrono::duration<Rep, Period> d) {
+inline auto get_milliseconds(std::chrono::duration<Rep, Period> d)
+ -> std::chrono::duration<Rep, std::milli> {
// this may overflow and/or the result may not fit in the
// target type.
#if FMT_SAFE_DURATION_CAST
using CommonSecondsType =
typename std::common_type<decltype(d), std::chrono::seconds>::type;
- const auto d_as_common = fmt_safe_duration_cast<CommonSecondsType>(d);
+ const auto d_as_common = fmt_duration_cast<CommonSecondsType>(d);
const auto d_as_whole_seconds =
- fmt_safe_duration_cast<std::chrono::seconds>(d_as_common);
+ fmt_duration_cast<std::chrono::seconds>(d_as_common);
// this conversion should be nonproblematic
const auto diff = d_as_common - d_as_whole_seconds;
const auto ms =
- fmt_safe_duration_cast<std::chrono::duration<Rep, std::milli>>(diff);
+ fmt_duration_cast<std::chrono::duration<Rep, std::milli>>(diff);
return ms;
#else
- auto s = std::chrono::duration_cast<std::chrono::seconds>(d);
- return std::chrono::duration_cast<std::chrono::milliseconds>(d - s);
+ auto s = fmt_duration_cast<std::chrono::seconds>(d);
+ return fmt_duration_cast<std::chrono::milliseconds>(d - s);
#endif
}
template <typename Char, typename Rep, typename OutputIt,
FMT_ENABLE_IF(std::is_integral<Rep>::value)>
-OutputIt format_duration_value(OutputIt out, Rep val, int) {
+auto format_duration_value(OutputIt out, Rep val, int) -> OutputIt {
return write<Char>(out, val);
}
template <typename Char, typename Rep, typename OutputIt,
FMT_ENABLE_IF(std::is_floating_point<Rep>::value)>
-OutputIt format_duration_value(OutputIt out, Rep val, int precision) {
+auto format_duration_value(OutputIt out, Rep val, int precision) -> OutputIt {
auto specs = format_specs<Char>();
specs.precision = precision;
specs.type = precision >= 0 ? presentation_type::fixed_lower
@@ -1671,12 +1708,12 @@ OutputIt format_duration_value(OutputIt out, Rep val, int precision) {
}
template <typename Char, typename OutputIt>
-OutputIt copy_unit(string_view unit, OutputIt out, Char) {
+auto copy_unit(string_view unit, OutputIt out, Char) -> OutputIt {
return std::copy(unit.begin(), unit.end(), out);
}
template <typename OutputIt>
-OutputIt copy_unit(string_view unit, OutputIt out, wchar_t) {
+auto copy_unit(string_view unit, OutputIt out, wchar_t) -> OutputIt {
// This works when wchar_t is UTF-32 because units only contain characters
// that have the same representation in UTF-16 and UTF-32.
utf8_to_utf16 u(unit);
@@ -1684,7 +1721,7 @@ OutputIt copy_unit(string_view unit, OutputIt out, wchar_t) {
}
template <typename Char, typename Period, typename OutputIt>
-OutputIt format_duration_unit(OutputIt out) {
+auto format_duration_unit(OutputIt out) -> OutputIt {
if (const char* unit = get_units<Period>())
return copy_unit(string_view(unit), out, Char());
*out++ = '[';
@@ -1751,18 +1788,12 @@ struct chrono_formatter {
// this may overflow and/or the result may not fit in the
// target type.
-#if FMT_SAFE_DURATION_CAST
// might need checked conversion (rep!=Rep)
- auto tmpval = std::chrono::duration<rep, Period>(val);
- s = fmt_safe_duration_cast<seconds>(tmpval);
-#else
- s = std::chrono::duration_cast<seconds>(
- std::chrono::duration<rep, Period>(val));
-#endif
+ s = fmt_duration_cast<seconds>(std::chrono::duration<rep, Period>(val));
}
// returns true if nan or inf, writes to out.
- bool handle_nan_inf() {
+ auto handle_nan_inf() -> bool {
if (isfinite(val)) {
return false;
}
@@ -1779,17 +1810,22 @@ struct chrono_formatter {
return true;
}
- Rep hour() const { return static_cast<Rep>(mod((s.count() / 3600), 24)); }
+ auto days() const -> Rep { return static_cast<Rep>(s.count() / 86400); }
+ auto hour() const -> Rep {
+ return static_cast<Rep>(mod((s.count() / 3600), 24));
+ }
- Rep hour12() const {
+ auto hour12() const -> Rep {
Rep hour = static_cast<Rep>(mod((s.count() / 3600), 12));
return hour <= 0 ? 12 : hour;
}
- Rep minute() const { return static_cast<Rep>(mod((s.count() / 60), 60)); }
- Rep second() const { return static_cast<Rep>(mod(s.count(), 60)); }
+ auto minute() const -> Rep {
+ return static_cast<Rep>(mod((s.count() / 60), 60));
+ }
+ auto second() const -> Rep { return static_cast<Rep>(mod(s.count(), 60)); }
- std::tm time() const {
+ auto time() const -> std::tm {
auto time = std::tm();
time.tm_hour = to_nonnegative_int(hour(), 24);
time.tm_min = to_nonnegative_int(minute(), 60);
@@ -1857,10 +1893,14 @@ struct chrono_formatter {
void on_dec0_week_of_year(numeric_system) {}
void on_dec1_week_of_year(numeric_system) {}
void on_iso_week_of_year(numeric_system) {}
- void on_day_of_year() {}
void on_day_of_month(numeric_system) {}
void on_day_of_month_space(numeric_system) {}
+ void on_day_of_year() {
+ if (handle_nan_inf()) return;
+ write(days(), 0);
+ }
+
void on_24_hour(numeric_system ns, pad_type pad) {
if (handle_nan_inf()) return;
@@ -1967,7 +2007,7 @@ class weekday {
weekday() = default;
explicit constexpr weekday(unsigned wd) noexcept
: value(static_cast<unsigned char>(wd != 7 ? wd : 0)) {}
- constexpr unsigned c_encoding() const noexcept { return value; }
+ constexpr auto c_encoding() const noexcept -> unsigned { return value; }
};
class year_month_day {};
@@ -2082,25 +2122,22 @@ struct formatter<std::chrono::time_point<std::chrono::system_clock, Duration>,
period::num != 1 || period::den != 1 ||
std::is_floating_point<typename Duration::rep>::value)) {
const auto epoch = val.time_since_epoch();
- auto subsecs = std::chrono::duration_cast<Duration>(
- epoch - std::chrono::duration_cast<std::chrono::seconds>(epoch));
+ auto subsecs = detail::fmt_duration_cast<Duration>(
+ epoch - detail::fmt_duration_cast<std::chrono::seconds>(epoch));
if (subsecs.count() < 0) {
auto second =
- std::chrono::duration_cast<Duration>(std::chrono::seconds(1));
+ detail::fmt_duration_cast<Duration>(std::chrono::seconds(1));
if (epoch.count() < ((Duration::min)() + second).count())
FMT_THROW(format_error("duration is too small"));
subsecs += second;
val -= second;
}
- return formatter<std::tm, Char>::do_format(
- gmtime(std::chrono::time_point_cast<std::chrono::seconds>(val)), ctx,
- &subsecs);
+ return formatter<std::tm, Char>::do_format(gmtime(val), ctx, &subsecs);
}
- return formatter<std::tm, Char>::format(
- gmtime(std::chrono::time_point_cast<std::chrono::seconds>(val)), ctx);
+ return formatter<std::tm, Char>::format(gmtime(val), ctx);
}
};
@@ -2119,17 +2156,13 @@ struct formatter<std::chrono::local_time<Duration>, Char>
if (period::num != 1 || period::den != 1 ||
std::is_floating_point<typename Duration::rep>::value) {
const auto epoch = val.time_since_epoch();
- const auto subsecs = std::chrono::duration_cast<Duration>(
- epoch - std::chrono::duration_cast<std::chrono::seconds>(epoch));
+ const auto subsecs = detail::fmt_duration_cast<Duration>(
+ epoch - detail::fmt_duration_cast<std::chrono::seconds>(epoch));
- return formatter<std::tm, Char>::do_format(
- localtime(std::chrono::time_point_cast<std::chrono::seconds>(val)),
- ctx, &subsecs);
+ return formatter<std::tm, Char>::do_format(localtime(val), ctx, &subsecs);
}
- return formatter<std::tm, Char>::format(
- localtime(std::chrono::time_point_cast<std::chrono::seconds>(val)),
- ctx);
+ return formatter<std::tm, Char>::format(localtime(val), ctx);
}
};
#endif
diff --git a/include/fmt/color.h b/include/fmt/color.h
index 57b7f578..367849a8 100644
--- a/include/fmt/color.h
+++ b/include/fmt/color.h
@@ -233,7 +233,7 @@ class text_style {
FMT_CONSTEXPR text_style(emphasis em = emphasis()) noexcept
: set_foreground_color(), set_background_color(), ems(em) {}
- FMT_CONSTEXPR text_style& operator|=(const text_style& rhs) {
+ FMT_CONSTEXPR auto operator|=(const text_style& rhs) -> text_style& {
if (!set_foreground_color) {
set_foreground_color = rhs.set_foreground_color;
foreground_color = rhs.foreground_color;
@@ -257,29 +257,29 @@ class text_style {
return *this;
}
- friend FMT_CONSTEXPR text_style operator|(text_style lhs,
- const text_style& rhs) {
+ friend FMT_CONSTEXPR auto operator|(text_style lhs, const text_style& rhs)
+ -> text_style {
return lhs |= rhs;
}
- FMT_CONSTEXPR bool has_foreground() const noexcept {
+ FMT_CONSTEXPR auto has_foreground() const noexcept -> bool {
return set_foreground_color;
}
- FMT_CONSTEXPR bool has_background() const noexcept {
+ FMT_CONSTEXPR auto has_background() const noexcept -> bool {
return set_background_color;
}
- FMT_CONSTEXPR bool has_emphasis() const noexcept {
+ FMT_CONSTEXPR auto has_emphasis() const noexcept -> bool {
return static_cast<uint8_t>(ems) != 0;
}
- FMT_CONSTEXPR detail::color_type get_foreground() const noexcept {
+ FMT_CONSTEXPR auto get_foreground() const noexcept -> detail::color_type {
FMT_ASSERT(has_foreground(), "no foreground specified for this style");
return foreground_color;
}
- FMT_CONSTEXPR detail::color_type get_background() const noexcept {
+ FMT_CONSTEXPR auto get_background() const noexcept -> detail::color_type {
FMT_ASSERT(has_background(), "no background specified for this style");
return background_color;
}
- FMT_CONSTEXPR emphasis get_emphasis() const noexcept {
+ FMT_CONSTEXPR auto get_emphasis() const noexcept -> emphasis {
FMT_ASSERT(has_emphasis(), "no emphasis specified for this style");
return ems;
}
@@ -297,9 +297,11 @@ class text_style {
}
}
- friend FMT_CONSTEXPR text_style fg(detail::color_type foreground) noexcept;
+ friend FMT_CONSTEXPR auto fg(detail::color_type foreground) noexcept
+ -> text_style;
- friend FMT_CONSTEXPR text_style bg(detail::color_type background) noexcept;
+ friend FMT_CONSTEXPR auto bg(detail::color_type background) noexcept
+ -> text_style;
detail::color_type foreground_color;
detail::color_type background_color;
@@ -309,16 +311,19 @@ class text_style {
};
/** Creates a text style from the foreground (text) color. */
-FMT_CONSTEXPR inline text_style fg(detail::color_type foreground) noexcept {
+FMT_CONSTEXPR inline auto fg(detail::color_type foreground) noexcept
+ -> text_style {
return text_style(true, foreground);
}
/** Creates a text style from the background color. */
-FMT_CONSTEXPR inline text_style bg(detail::color_type background) noexcept {
+FMT_CONSTEXPR inline auto bg(detail::color_type background) noexcept
+ -> text_style {
return text_style(false, background);
}
-FMT_CONSTEXPR inline text_style operator|(emphasis lhs, emphasis rhs) noexcept {
+FMT_CONSTEXPR inline auto operator|(emphasis lhs, emphasis rhs) noexcept
+ -> text_style {
return text_style(lhs) | rhs;
}
@@ -384,8 +389,8 @@ template <typename Char> struct ansi_color_escape {
}
FMT_CONSTEXPR operator const Char*() const noexcept { return buffer; }
- FMT_CONSTEXPR const Char* begin() const noexcept { return buffer; }
- FMT_CONSTEXPR_CHAR_TRAITS const Char* end() const noexcept {
+ FMT_CONSTEXPR auto begin() const noexcept -> const Char* { return buffer; }
+ FMT_CONSTEXPR_CHAR_TRAITS auto end() const noexcept -> const Char* {
return buffer + std::char_traits<Char>::length(buffer);
}
@@ -400,25 +405,27 @@ template <typename Char> struct ansi_color_escape {
out[2] = static_cast<Char>('0' + c % 10);
out[3] = static_cast<Char>(delimiter);
}
- static FMT_CONSTEXPR bool has_emphasis(emphasis em, emphasis mask) noexcept {
+ static FMT_CONSTEXPR auto has_emphasis(emphasis em, emphasis mask) noexcept
+ -> bool {
return static_cast<uint8_t>(em) & static_cast<uint8_t>(mask);
}
};
template <typename Char>
-FMT_CONSTEXPR ansi_color_escape<Char> make_foreground_color(
- detail::color_type foreground) noexcept {
+FMT_CONSTEXPR auto make_foreground_color(detail::color_type foreground) noexcept
+ -> ansi_color_escape<Char> {
return ansi_color_escape<Char>(foreground, "\x1b[38;2;");
}
template <typename Char>
-FMT_CONSTEXPR ansi_color_escape<Char> make_background_color(
- detail::color_type background) noexcept {
+FMT_CONSTEXPR auto make_background_color(detail::color_type background) noexcept
+ -> ansi_color_escape<Char> {
return ansi_color_escape<Char>(background, "\x1b[48;2;");
}
template <typename Char>
-FMT_CONSTEXPR ansi_color_escape<Char> make_emphasis(emphasis em) noexcept {
+FMT_CONSTEXPR auto make_emphasis(emphasis em) noexcept
+ -> ansi_color_escape<Char> {
return ansi_color_escape<Char>(em);
}
@@ -511,9 +518,10 @@ void print(const text_style& ts, const S& format_str, const Args&... args) {
}
template <typename S, typename Char = char_t<S>>
-inline std::basic_string<Char> vformat(
+inline auto vformat(
const text_style& ts, const S& format_str,
- basic_format_args<buffer_context<type_identity_t<Char>>> args) {
+ basic_format_args<buffer_context<type_identity_t<Char>>> args)
+ -> std::basic_string<Char> {
basic_memory_buffer<Char> buf;
detail::vformat_to(buf, ts, detail::to_string_view(format_str), args);
return fmt::to_string(buf);
@@ -532,8 +540,8 @@ inline std::basic_string<Char> vformat(
\endrst
*/
template <typename S, typename... Args, typename Char = char_t<S>>
-inline std::basic_string<Char> format(const text_style& ts, const S& format_str,
- const Args&... args) {
+inline auto format(const text_style& ts, const S& format_str,
+ const Args&... args) -> std::basic_string<Char> {
return fmt::vformat(ts, detail::to_string_view(format_str),
fmt::make_format_args<buffer_context<Char>>(args...));
}
@@ -543,9 +551,10 @@ inline std::basic_string<Char> format(const text_style& ts, const S& format_str,
*/
template <typename OutputIt, typename Char,
FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, Char>::value)>
-OutputIt vformat_to(
- OutputIt out, const text_style& ts, basic_string_view<Char> format_str,
- basic_format_args<buffer_context<type_identity_t<Char>>> args) {
+auto vformat_to(OutputIt out, const text_style& ts,
+ basic_string_view<Char> format_str,
+ basic_format_args<buffer_context<type_identity_t<Char>>> args)
+ -> OutputIt {
auto&& buf = detail::get_buffer<Char>(out);
detail::vformat_to(buf, ts, format_str, args);
return detail::get_iterator(buf, out);
@@ -563,9 +572,10 @@ OutputIt vformat_to(
fmt::emphasis::bold | fg(fmt::color::red), "{}", 42);
\endrst
*/
-template <typename OutputIt, typename S, typename... Args,
- bool enable = detail::is_output_iterator<OutputIt, char_t<S>>::value&&
- detail::is_string<S>::value>
+template <
+ typename OutputIt, typename S, typename... Args,
+ bool enable = detail::is_output_iterator<OutputIt, char_t<S>>::value &&
+ detail::is_string<S>::value>
inline auto format_to(OutputIt out, const text_style& ts, const S& format_str,
Args&&... args) ->
typename std::enable_if<enable, OutputIt>::type {
diff --git a/include/fmt/compile.h b/include/fmt/compile.h
index a4c7e495..3b3f166e 100644
--- a/include/fmt/compile.h
+++ b/include/fmt/compile.h
@@ -14,8 +14,8 @@ FMT_BEGIN_NAMESPACE
namespace detail {
template <typename Char, typename InputIt>
-FMT_CONSTEXPR inline counting_iterator copy_str(InputIt begin, InputIt end,
- counting_iterator it) {
+FMT_CONSTEXPR inline auto copy_str(InputIt begin, InputIt end,
+ counting_iterator it) -> counting_iterator {
return it + (end - begin);
}
@@ -57,7 +57,7 @@ struct udl_compiled_string : compiled_string {
#endif
template <typename T, typename... Tail>
-const T& first(const T& value, const Tail&...) {
+auto first(const T& value, const Tail&...) -> const T& {
return value;
}
@@ -488,18 +488,19 @@ FMT_CONSTEXPR OutputIt format_to(OutputIt out, const S&, Args&&... args) {
template <typename OutputIt, typename S, typename... Args,
FMT_ENABLE_IF(detail::is_compiled_string<S>::value)>
-format_to_n_result<OutputIt> format_to_n(OutputIt out, size_t n,
- const S& format_str, Args&&... args) {
+auto format_to_n(OutputIt out, size_t n, const S& format_str, Args&&... args)
+ -> format_to_n_result<OutputIt> {
using traits = detail::fixed_buffer_traits;
auto buf = detail::iterator_buffer<OutputIt, char, traits>(out, n);
- format_to(std::back_inserter(buf), format_str, std::forward<Args>(args)...);
+ fmt::format_to(std::back_inserter(buf), format_str,
+ std::forward<Args>(args)...);
return {buf.out(), buf.count()};
}
template <typename S, typename... Args,
FMT_ENABLE_IF(detail::is_compiled_string<S>::value)>
-FMT_CONSTEXPR20 size_t formatted_size(const S& format_str,
- const Args&... args) {
+FMT_CONSTEXPR20 auto formatted_size(const S& format_str, const Args&... args)
+ -> size_t {
return fmt::format_to(detail::counting_iterator(), format_str, args...)
.count();
}
diff --git a/include/fmt/core.h b/include/fmt/core.h
index 18ebada2..94f3f22b 100644
--- a/include/fmt/core.h
+++ b/include/fmt/core.h
@@ -18,7 +18,7 @@
#include <type_traits>
// The fmt library version in the form major * 10000 + minor * 100 + patch.
-#define FMT_VERSION 100101
+#define FMT_VERSION 100200
#if defined(__clang__) && !defined(__ibmxl__)
# define FMT_CLANG_VERSION (__clang_major__ * 100 + __clang_minor__)
@@ -105,9 +105,12 @@
# define FMT_CONSTEXPR
#endif
-#if ((FMT_CPLUSPLUS >= 202002L) && \
- (!defined(_GLIBCXX_RELEASE) || _GLIBCXX_RELEASE > 9)) || \
- (FMT_CPLUSPLUS >= 201709L && FMT_GCC_VERSION >= 1002)
+#if (FMT_CPLUSPLUS >= 202002L || \
+ (FMT_CPLUSPLUS >= 201709L && FMT_GCC_VERSION >= 1002)) && \
+ ((!defined(_GLIBCXX_RELEASE) || _GLIBCXX_RELEASE >= 10) && \
+ (!defined(_LIBCPP_VERSION) || _LIBCPP_VERSION >= 10000) && \
+ (!FMT_MSC_VERSION || FMT_MSC_VERSION >= 1928)) && \
+ defined(__cpp_lib_is_constant_evaluated)
# define FMT_CONSTEXPR20 constexpr
#else
# define FMT_CONSTEXPR20
@@ -224,8 +227,9 @@
__apple_build_version__ >= 14000029L) && \
FMT_CPLUSPLUS >= 202002L) || \
(defined(__cpp_consteval) && \
- (!FMT_MSC_VERSION || _MSC_FULL_VER >= 193030704))
-// consteval is broken in MSVC before VS2022 and Apple clang before 14.
+ (!FMT_MSC_VERSION || FMT_MSC_VERSION >= 1929))
+// consteval is broken in MSVC before VS2019 version 16.10 and Apple clang
+// before 14.
# define FMT_CONSTEVAL consteval
# define FMT_HAS_CONSTEVAL
# else
@@ -244,6 +248,15 @@
# endif
#endif
+// GCC < 5 requires this-> in decltype
+#ifndef FMT_DECLTYPE_THIS
+# if FMT_GCC_VERSION && FMT_GCC_VERSION < 500
+# define FMT_DECLTYPE_THIS this->
+# else
+# define FMT_DECLTYPE_THIS
+# endif
+#endif
+
// Enable minimal optimizations for more compact code in debug mode.
FMT_GCC_PRAGMA("GCC push_options")
#if !defined(__OPTIMIZE__) && !defined(__NVCOMPILER) && !defined(__LCC__) && \
@@ -458,15 +471,15 @@ template <typename Char> class basic_string_view {
size_ -= n;
}
- FMT_CONSTEXPR_CHAR_TRAITS bool starts_with(
- basic_string_view<Char> sv) const noexcept {
+ FMT_CONSTEXPR_CHAR_TRAITS auto starts_with(
+ basic_string_view<Char> sv) const noexcept -> bool {
return size_ >= sv.size_ &&
std::char_traits<Char>::compare(data_, sv.data_, sv.size_) == 0;
}
- FMT_CONSTEXPR_CHAR_TRAITS bool starts_with(Char c) const noexcept {
+ FMT_CONSTEXPR_CHAR_TRAITS auto starts_with(Char c) const noexcept -> bool {
return size_ >= 1 && std::char_traits<Char>::eq(*data_, c);
}
- FMT_CONSTEXPR_CHAR_TRAITS bool starts_with(const Char* s) const {
+ FMT_CONSTEXPR_CHAR_TRAITS auto starts_with(const Char* s) const -> bool {
return starts_with(basic_string_view<Char>(s));
}
@@ -604,10 +617,10 @@ FMT_TYPE_CONSTANT(const Char*, cstring_type);
FMT_TYPE_CONSTANT(basic_string_view<Char>, string_type);
FMT_TYPE_CONSTANT(const void*, pointer_type);
-constexpr bool is_integral_type(type t) {
+constexpr auto is_integral_type(type t) -> bool {
return t > type::none_type && t <= type::last_integer_type;
}
-constexpr bool is_arithmetic_type(type t) {
+constexpr auto is_arithmetic_type(type t) -> bool {
return t > type::none_type && t <= type::last_numeric_type;
}
@@ -631,6 +644,7 @@ enum {
pointer_set = set(type::pointer_type)
};
+// DEPRECATED!
FMT_NORETURN FMT_API void throw_format_error(const char* message);
struct error_handler {
@@ -808,7 +822,7 @@ template <typename T> class buffer {
protected:
// Don't initialize ptr_ since it is not accessed to save a few cycles.
FMT_MSC_WARNING(suppress : 26495)
- buffer(size_t sz) noexcept : size_(sz), capacity_(sz) {}
+ FMT_CONSTEXPR buffer(size_t sz) noexcept : size_(sz), capacity_(sz) {}
FMT_CONSTEXPR20 buffer(T* p = nullptr, size_t sz = 0, size_t cap = 0) noexcept
: ptr_(p), size_(sz), capacity_(cap) {}
@@ -823,6 +837,7 @@ template <typename T> class buffer {
}
/** Increases the buffer capacity to hold at least *capacity* elements. */
+ // DEPRECATED!
virtual FMT_CONSTEXPR20 void grow(size_t capacity) = 0;
public:
@@ -1309,6 +1324,7 @@ template <typename Context> class value {
parse_ctx.advance_to(f.parse(parse_ctx));
using qualified_type =
conditional_t<has_const_formatter<T, Context>(), const T, T>;
+ // Calling format through a mutable reference is deprecated.
ctx.advance_to(f.format(*static_cast<qualified_type*>(arg), ctx));
}
};
@@ -1322,7 +1338,7 @@ using ulong_type = conditional_t<long_short, unsigned, unsigned long long>;
template <typename T> struct format_as_result {
template <typename U,
FMT_ENABLE_IF(std::is_enum<U>::value || std::is_class<U>::value)>
- static auto map(U*) -> decltype(format_as(std::declval<U>()));
+ static auto map(U*) -> remove_cvref_t<decltype(format_as(std::declval<U>()))>;
static auto map(...) -> void;
using type = decltype(map(static_cast<T*>(nullptr)));
@@ -1439,7 +1455,8 @@ template <typename Context> struct arg_mapper {
// Only map owning types because mapping views can be unsafe.
template <typename T, typename U = format_as_t<T>,
FMT_ENABLE_IF(std::is_arithmetic<U>::value)>
- FMT_CONSTEXPR FMT_INLINE auto map(const T& val) -> decltype(this->map(U())) {
+ FMT_CONSTEXPR FMT_INLINE auto map(const T& val)
+ -> decltype(FMT_DECLTYPE_THIS map(U())) {
return map(format_as(val));
}
@@ -1463,13 +1480,14 @@ template <typename Context> struct arg_mapper {
!is_string<U>::value && !is_char<U>::value &&
!is_named_arg<U>::value &&
!std::is_arithmetic<format_as_t<U>>::value)>
- FMT_CONSTEXPR FMT_INLINE auto map(T& val) -> decltype(this->do_map(val)) {
+ FMT_CONSTEXPR FMT_INLINE auto map(T& val)
+ -> decltype(FMT_DECLTYPE_THIS do_map(val)) {
return do_map(val);
}
template <typename T, FMT_ENABLE_IF(is_named_arg<T>::value)>
FMT_CONSTEXPR FMT_INLINE auto map(const T& named_arg)
- -> decltype(this->map(named_arg.value)) {
+ -> decltype(FMT_DECLTYPE_THIS map(named_arg.value)) {
return map(named_arg.value);
}
@@ -1604,8 +1622,8 @@ FMT_CONSTEXPR inline auto make_arg(T& val) -> basic_format_arg<Context> {
} // namespace detail
FMT_BEGIN_EXPORT
-// A formatting argument. It is a trivially copyable/constructible type to
-// allow storage in basic_memory_buffer.
+// A formatting argument. Context is a template parameter for the compiled API
+// where output can be unbuffered.
template <typename Context> class basic_format_arg {
private:
detail::value<Context> value_;
@@ -1657,6 +1675,15 @@ template <typename Context> class basic_format_arg {
auto is_arithmetic() const -> bool {
return detail::is_arithmetic_type(type_);
}
+
+ FMT_INLINE auto format_custom(const char_type* parse_begin,
+ typename Context::parse_context_type& parse_ctx,
+ Context& ctx) -> bool {
+ if (type_ != detail::type::custom_type) return false;
+ parse_ctx.advance_to(parse_begin);
+ value_.custom.format(value_.custom.value, parse_ctx, ctx);
+ return true;
+ }
};
/**
@@ -1745,6 +1772,7 @@ template <typename OutputIt, typename Char> class basic_format_context {
}
auto args() const -> const format_args& { return args_; }
+ // DEPRECATED!
FMT_CONSTEXPR auto error_handler() -> detail::error_handler { return {}; }
void on_error(const char* message) { error_handler().on_error(message); }
@@ -2417,6 +2445,8 @@ FMT_CONSTEXPR FMT_INLINE auto parse_format_specs(
case 'G':
return parse_presentation_type(pres::general_upper, float_set);
case 'c':
+ if (arg_type == type::bool_type)
+ throw_format_error("invalid format specifier");
return parse_presentation_type(pres::chr, integral_set);
case 's':
return parse_presentation_type(pres::string,
diff --git a/include/fmt/format-inl.h b/include/fmt/format-inl.h
index 5f8c83a2..e9a4ca45 100644
--- a/include/fmt/format-inl.h
+++ b/include/fmt/format-inl.h
@@ -58,8 +58,8 @@ FMT_FUNC void format_error_code(detail::buffer<char>& out, int error_code,
error_code_size += detail::to_unsigned(detail::count_digits(abs_value));
auto it = buffer_appender<char>(out);
if (message.size() <= inline_buffer_size - error_code_size)
- format_to(it, FMT_STRING("{}{}"), message, SEP);
- format_to(it, FMT_STRING("{}{}"), ERROR_STR, error_code);
+ fmt::format_to(it, FMT_STRING("{}{}"), message, SEP);
+ fmt::format_to(it, FMT_STRING("{}{}"), ERROR_STR, error_code);
FMT_ASSERT(out.size() <= inline_buffer_size, "");
}
@@ -85,7 +85,7 @@ locale_ref::locale_ref(const Locale& loc) : locale_(&loc) {
static_assert(std::is_same<Locale, std::locale>::value, "");
}
-template <typename Locale> Locale locale_ref::get() const {
+template <typename Locale> auto locale_ref::get() const -> Locale {
static_assert(std::is_same<Locale, std::locale>::value, "");
return locale_ ? *static_cast<const std::locale*>(locale_) : std::locale();
}
@@ -97,7 +97,8 @@ FMT_FUNC auto thousands_sep_impl(locale_ref loc) -> thousands_sep_result<Char> {
auto thousands_sep = grouping.empty() ? Char() : facet.thousands_sep();
return {std::move(grouping), thousands_sep};
}
-template <typename Char> FMT_FUNC Char decimal_point_impl(locale_ref loc) {
+template <typename Char>
+FMT_FUNC auto decimal_point_impl(locale_ref loc) -> Char {
return std::use_facet<std::numpunct<Char>>(loc.get<std::locale>())
.decimal_point();
}
@@ -143,24 +144,25 @@ FMT_API FMT_FUNC auto format_facet<std::locale>::do_put(
}
#endif
-FMT_FUNC std::system_error vsystem_error(int error_code, string_view fmt,
- format_args args) {
+FMT_FUNC auto vsystem_error(int error_code, string_view fmt, format_args args)
+ -> std::system_error {
auto ec = std::error_code(error_code, std::generic_category());
return std::system_error(ec, vformat(fmt, args));
}
namespace detail {
-template <typename F> inline bool operator==(basic_fp<F> x, basic_fp<F> y) {
+template <typename F>
+inline auto operator==(basic_fp<F> x, basic_fp<F> y) -> bool {
return x.f == y.f && x.e == y.e;
}
// Compilers should be able to optimize this into the ror instruction.
-FMT_CONSTEXPR inline uint32_t rotr(uint32_t n, uint32_t r) noexcept {
+FMT_CONSTEXPR inline auto rotr(uint32_t n, uint32_t r) noexcept -> uint32_t {
r &= 31;
return (n >> r) | (n << (32 - r));
}
-FMT_CONSTEXPR inline uint64_t rotr(uint64_t n, uint32_t r) noexcept {
+FMT_CONSTEXPR inline auto rotr(uint64_t n, uint32_t r) noexcept -> uint64_t {
r &= 63;
return (n >> r) | (n << (64 - r));
}
@@ -169,14 +171,14 @@ FMT_CONSTEXPR inline uint64_t rotr(uint64_t n, uint32_t r) noexcept {
namespace dragonbox {
// Computes upper 64 bits of multiplication of a 32-bit unsigned integer and a
// 64-bit unsigned integer.
-inline uint64_t umul96_upper64(uint32_t x, uint64_t y) noexcept {
+inline auto umul96_upper64(uint32_t x, uint64_t y) noexcept -> uint64_t {
return umul128_upper64(static_cast<uint64_t>(x) << 32, y);
}
// Computes lower 128 bits of multiplication of a 64-bit unsigned integer and a
// 128-bit unsigned integer.
-inline uint128_fallback umul192_lower128(uint64_t x,
- uint128_fallback y) noexcept {
+inline auto umul192_lower128(uint64_t x, uint128_fallback y) noexcept
+ -> uint128_fallback {
uint64_t high = x * y.high();
uint128_fallback high_low = umul128(x, y.low());
return {high + high_low.high(), high_low.low()};
@@ -184,12 +186,12 @@ inline uint128_fallback umul192_lower128(uint64_t x,
// Computes lower 64 bits of multiplication of a 32-bit unsigned integer and a
// 64-bit unsigned integer.
-inline uint64_t umul96_lower64(uint32_t x, uint64_t y) noexcept {
+inline auto umul96_lower64(uint32_t x, uint64_t y) noexcept -> uint64_t {
return x * y;
}
// Various fast log computations.
-inline int floor_log10_pow2_minus_log10_4_over_3(int e) noexcept {
+inline auto floor_log10_pow2_minus_log10_4_over_3(int e) noexcept -> int {
FMT_ASSERT(e <= 2936 && e >= -2985, "too large exponent");
return (e * 631305 - 261663) >> 21;
}
@@ -203,7 +205,7 @@ FMT_INLINE_VARIABLE constexpr struct {
// divisible by pow(10, N).
// Precondition: n <= pow(10, N + 1).
template <int N>
-bool check_divisibility_and_divide_by_pow10(uint32_t& n) noexcept {
+auto check_divisibility_and_divide_by_pow10(uint32_t& n) noexcept -> bool {
// The numbers below are chosen such that:
// 1. floor(n/d) = floor(nm / 2^k) where d=10 or d=100,
// 2. nm mod 2^k < m if and only if n is divisible by d,
@@ -228,7 +230,7 @@ bool check_divisibility_and_divide_by_pow10(uint32_t& n) noexcept {
// Computes floor(n / pow(10, N)) for small n and N.
// Precondition: n <= pow(10, N + 1).
-template <int N> uint32_t small_division_by_pow10(uint32_t n) noexcept {
+template <int N> auto small_division_by_pow10(uint32_t n) noexcept -> uint32_t {
constexpr auto info = div_small_pow10_infos[N - 1];
FMT_ASSERT(n <= info.divisor * 10, "n is too large");
constexpr uint32_t magic_number =
@@ -237,12 +239,12 @@ template <int N> uint32_t small_division_by_pow10(uint32_t n) noexcept {
}
// Computes floor(n / 10^(kappa + 1)) (float)
-inline uint32_t divide_by_10_to_kappa_plus_1(uint32_t n) noexcept {
+inline auto divide_by_10_to_kappa_plus_1(uint32_t n) noexcept -> uint32_t {
// 1374389535 = ceil(2^37/100)
return static_cast<uint32_t>((static_cast<uint64_t>(n) * 1374389535) >> 37);
}
// Computes floor(n / 10^(kappa + 1)) (double)
-inline uint64_t divide_by_10_to_kappa_plus_1(uint64_t n) noexcept {
+inline auto divide_by_10_to_kappa_plus_1(uint64_t n) noexcept -> uint64_t {
// 2361183241434822607 = ceil(2^(64+7)/1000)
return umul128_upper64(n, 2361183241434822607ull) >> 7;
}
@@ -254,7 +256,7 @@ template <> struct cache_accessor<float> {
using carrier_uint = float_info<float>::carrier_uint;
using cache_entry_type = uint64_t;
- static uint64_t get_cached_power(int k) noexcept {
+ static auto get_cached_power(int k) noexcept -> uint64_t {
FMT_ASSERT(k >= float_info<float>::min_k && k <= float_info<float>::max_k,
"k is out of range");
static constexpr const uint64_t pow10_significands[] = {
@@ -296,20 +298,23 @@ template <> struct cache_accessor<float> {
bool is_integer;
};
- static compute_mul_result compute_mul(
- carrier_uint u, const cache_entry_type& cache) noexcept {
+ static auto compute_mul(carrier_uint u,
+ const cache_entry_type& cache) noexcept
+ -> compute_mul_result {
auto r = umul96_upper64(u, cache);
return {static_cast<carrier_uint>(r >> 32),
static_cast<carrier_uint>(r) == 0};
}
- static uint32_t compute_delta(const cache_entry_type& cache,
- int beta) noexcept {
+ static auto compute_delta(const cache_entry_type& cache, int beta) noexcept
+ -> uint32_t {
return static_cast<uint32_t>(cache >> (64 - 1 - beta));
}
- static compute_mul_parity_result compute_mul_parity(
- carrier_uint two_f, const cache_entry_type& cache, int beta) noexcept {
+ static auto compute_mul_parity(carrier_uint two_f,
+ const cache_entry_type& cache,
+ int beta) noexcept
+ -> compute_mul_parity_result {
FMT_ASSERT(beta >= 1, "");
FMT_ASSERT(beta < 64, "");
@@ -318,22 +323,22 @@ template <> struct cache_accessor<float> {
static_cast<uint32_t>(r >> (32 - beta)) == 0};
}
- static carrier_uint compute_left_endpoint_for_shorter_interval_case(
- const cache_entry_type& cache, int beta) noexcept {
+ static auto compute_left_endpoint_for_shorter_interval_case(
+ const cache_entry_type& cache, int beta) noexcept -> carrier_uint {
return static_cast<carrier_uint>(
(cache - (cache >> (num_significand_bits<float>() + 2))) >>
(64 - num_significand_bits<float>() - 1 - beta));
}
- static carrier_uint compute_right_endpoint_for_shorter_interval_case(
- const cache_entry_type& cache, int beta) noexcept {
+ static auto compute_right_endpoint_for_shorter_interval_case(
+ const cache_entry_type& cache, int beta) noexcept -> carrier_uint {
return static_cast<carrier_uint>(
(cache + (cache >> (num_significand_bits<float>() + 1))) >>
(64 - num_significand_bits<float>() - 1 - beta));
}
- static carrier_uint compute_round_up_for_shorter_interval_case(
- const cache_entry_type& cache, int beta) noexcept {
+ static auto compute_round_up_for_shorter_interval_case(
+ const cache_entry_type& cache, int beta) noexcept -> carrier_uint {
return (static_cast<carrier_uint>(
cache >> (64 - num_significand_bits<float>() - 2 - beta)) +
1) /
@@ -345,7 +350,7 @@ template <> struct cache_accessor<double> {
using carrier_uint = float_info<double>::carrier_uint;
using cache_entry_type = uint128_fallback;
- static uint128_fallback get_cached_power(int k) noexcept {
+ static auto get_cached_power(int k) noexcept -> uint128_fallback {
FMT_ASSERT(k >= float_info<double>::min_k && k <= float_info<double>::max_k,
"k is out of range");
@@ -984,7 +989,7 @@ template <> struct cache_accessor<double> {
{0xe0accfa875af45a7, 0x93eb1b80a33b8606},
{0x8c6c01c9498d8b88, 0xbc72f130660533c4},
{0xaf87023b9bf0ee6a, 0xeb8fad7c7f8680b5},
- {0xdb68c2ca82ed2a05, 0xa67398db9f6820e2}
+ {0xdb68c2ca82ed2a05, 0xa67398db9f6820e2},
#else
{0xff77b1fcbebcdc4f, 0x25e8e89c13bb0f7b},
{0xce5d73ff402d98e3, 0xfb0a3d212dc81290},
@@ -1069,19 +1074,22 @@ template <> struct cache_accessor<double> {
bool is_integer;
};
- static compute_mul_result compute_mul(
- carrier_uint u, const cache_entry_type& cache) noexcept {
+ static auto compute_mul(carrier_uint u,
+ const cache_entry_type& cache) noexcept
+ -> compute_mul_result {
auto r = umul192_upper128(u, cache);
return {r.high(), r.low() == 0};
}
- static uint32_t compute_delta(cache_entry_type const& cache,
- int beta) noexcept {
+ static auto compute_delta(cache_entry_type const& cache, int beta) noexcept
+ -> uint32_t {
return static_cast<uint32_t>(cache.high() >> (64 - 1 - beta));
}
- static compute_mul_parity_result compute_mul_parity(
- carrier_uint two_f, const cache_entry_type& cache, int beta) noexcept {
+ static auto compute_mul_parity(carrier_uint two_f,
+ const cache_entry_type& cache,
+ int beta) noexcept
+ -> compute_mul_parity_result {
FMT_ASSERT(beta >= 1, "");
FMT_ASSERT(beta < 64, "");
@@ -1090,35 +1098,35 @@ template <> struct cache_accessor<double> {
((r.high() << beta) | (r.low() >> (64 - beta))) == 0};
}
- static carrier_uint compute_left_endpoint_for_shorter_interval_case(
- const cache_entry_type& cache, int beta) noexcept {
+ static auto compute_left_endpoint_for_shorter_interval_case(
+ const cache_entry_type& cache, int beta) noexcept -> carrier_uint {
return (cache.high() -
(cache.high() >> (num_significand_bits<double>() + 2))) >>
(64 - num_significand_bits<double>() - 1 - beta);
}
- static carrier_uint compute_right_endpoint_for_shorter_interval_case(
- const cache_entry_type& cache, int beta) noexcept {
+ static auto compute_right_endpoint_for_shorter_interval_case(
+ const cache_entry_type& cache, int beta) noexcept -> carrier_uint {
return (cache.high() +
(cache.high() >> (num_significand_bits<double>() + 1))) >>
(64 - num_significand_bits<double>() - 1 - beta);
}
- static carrier_uint compute_round_up_for_shorter_interval_case(
- const cache_entry_type& cache, int beta) noexcept {
+ static auto compute_round_up_for_shorter_interval_case(
+ const cache_entry_type& cache, int beta) noexcept -> carrier_uint {
return ((cache.high() >> (64 - num_significand_bits<double>() - 2 - beta)) +
1) /
2;
}
};
-FMT_FUNC uint128_fallback get_cached_power(int k) noexcept {
+FMT_FUNC auto get_cached_power(int k) noexcept -> uint128_fallback {
return cache_accessor<double>::get_cached_power(k);
}
// Various integer checks
template <typename T>
-bool is_left_endpoint_integer_shorter_interval(int exponent) noexcept {
+auto is_left_endpoint_integer_shorter_interval(int exponent) noexcept -> bool {
const int case_shorter_interval_left_endpoint_lower_threshold = 2;
const int case_shorter_interval_left_endpoint_upper_threshold = 3;
return exponent >= case_shorter_interval_left_endpoint_lower_threshold &&
@@ -1232,7 +1240,7 @@ FMT_INLINE decimal_fp<T> shorter_interval_case(int exponent) noexcept {
return ret_value;
}
-template <typename T> decimal_fp<T> to_decimal(T x) noexcept {
+template <typename T> auto to_decimal(T x) noexcept -> decimal_fp<T> {
// Step 1: integer promotion & Schubfach multiplier calculation.
using carrier_uint = typename float_info<T>::carrier_uint;
@@ -1371,15 +1379,15 @@ template <> struct formatter<detail::bigint> {
for (auto i = n.bigits_.size(); i > 0; --i) {
auto value = n.bigits_[i - 1u];
if (first) {
- out = format_to(out, FMT_STRING("{:x}"), value);
+ out = fmt::format_to(out, FMT_STRING("{:x}"), value);
first = false;
continue;
}
- out = format_to(out, FMT_STRING("{:08x}"), value);
+ out = fmt::format_to(out, FMT_STRING("{:08x}"), value);
}
if (n.exp_ > 0)
- out = format_to(out, FMT_STRING("p{}"),
- n.exp_ * detail::bigint::bigit_bits);
+ out = fmt::format_to(out, FMT_STRING("p{}"),
+ n.exp_ * detail::bigint::bigit_bits);
return out;
}
};
@@ -1415,7 +1423,7 @@ FMT_FUNC void report_system_error(int error_code,
report_error(format_system_error, error_code, message);
}
-FMT_FUNC std::string vformat(string_view fmt, format_args args) {
+FMT_FUNC auto vformat(string_view fmt, format_args args) -> std::string {
// Don't optimize the "{}" case to keep the binary size small and because it
// can be better optimized in fmt::format anyway.
auto buffer = memory_buffer();
@@ -1425,7 +1433,7 @@ FMT_FUNC std::string vformat(string_view fmt, format_args args) {
namespace detail {
#if !defined(_WIN32) || defined(FMT_WINDOWS_NO_WCHAR)
-FMT_FUNC bool write_console(int, string_view) { return false; }
+FMT_FUNC auto write_console(int, string_view) -> bool { return false; }
#else
using dword = conditional_t<sizeof(long) == 4, unsigned long, unsigned>;
extern "C" __declspec(dllimport) int __stdcall WriteConsoleW( //
diff --git a/include/fmt/format.h b/include/fmt/format.h
index c8e1c46d..f0ca55cf 100644
--- a/include/fmt/format.h
+++ b/include/fmt/format.h
@@ -43,7 +43,7 @@
#include <system_error> // std::system_error
#ifdef __cpp_lib_bit_cast
-# include <bit> // std::bitcast
+# include <bit> // std::bit_cast
#endif
#include "core.h"
@@ -277,19 +277,6 @@ FMT_END_NAMESPACE
#endif
FMT_BEGIN_NAMESPACE
-
-template <typename...> struct disjunction : std::false_type {};
-template <typename P> struct disjunction<P> : P {};
-template <typename P1, typename... Pn>
-struct disjunction<P1, Pn...>
- : conditional_t<bool(P1::value), P1, disjunction<Pn...>> {};
-
-template <typename...> struct conjunction : std::true_type {};
-template <typename P> struct conjunction<P> : P {};
-template <typename P1, typename... Pn>
-struct conjunction<P1, Pn...>
- : conditional_t<bool(P1::value), conjunction<Pn...>, P1> {};
-
namespace detail {
FMT_CONSTEXPR inline void abort_fuzzing_if(bool condition) {
@@ -311,37 +298,6 @@ template <typename CharT, CharT... C>
constexpr CharT string_literal<CharT, C...>::value[sizeof...(C)];
#endif
-template <typename Streambuf> class formatbuf : public Streambuf {
- private:
- using char_type = typename Streambuf::char_type;
- using streamsize = decltype(std::declval<Streambuf>().sputn(nullptr, 0));
- using int_type = typename Streambuf::int_type;
- using traits_type = typename Streambuf::traits_type;
-
- buffer<char_type>& buffer_;
-
- public:
- explicit formatbuf(buffer<char_type>& buf) : buffer_(buf) {}
-
- protected:
- // The put area is always empty. This makes the implementation simpler and has
- // the advantage that the streambuf and the buffer are always in sync and
- // sputc never writes into uninitialized memory. A disadvantage is that each
- // call to sputc always results in a (virtual) call to overflow. There is no
- // disadvantage here for sputn since this always results in a call to xsputn.
-
- auto overflow(int_type ch) -> int_type override {
- if (!traits_type::eq_int_type(ch, traits_type::eof()))
- buffer_.push_back(static_cast<char_type>(ch));
- return ch;
- }
-
- auto xsputn(const char_type* s, streamsize count) -> streamsize override {
- buffer_.append(s, s + count);
- return count;
- }
-};
-
// Implementation of std::bit_cast for pre-C++20.
template <typename To, typename From, FMT_ENABLE_IF(sizeof(To) == sizeof(From))>
FMT_CONSTEXPR20 auto bit_cast(const From& from) -> To {
@@ -377,8 +333,8 @@ class uint128_fallback {
constexpr uint128_fallback(uint64_t hi, uint64_t lo) : lo_(lo), hi_(hi) {}
constexpr uint128_fallback(uint64_t value = 0) : lo_(value), hi_(0) {}
- constexpr uint64_t high() const noexcept { return hi_; }
- constexpr uint64_t low() const noexcept { return lo_; }
+ constexpr auto high() const noexcept -> uint64_t { return hi_; }
+ constexpr auto low() const noexcept -> uint64_t { return lo_; }
template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)>
constexpr explicit operator T() const {
@@ -454,7 +410,7 @@ class uint128_fallback {
hi_ &= n.hi_;
}
- FMT_CONSTEXPR20 uint128_fallback& operator+=(uint64_t n) noexcept {
+ FMT_CONSTEXPR20 auto operator+=(uint64_t n) noexcept -> uint128_fallback& {
if (is_constant_evaluated()) {
lo_ += n;
hi_ += (lo_ < n ? 1 : 0);
@@ -744,7 +700,7 @@ inline auto compute_width(basic_string_view<Char> s) -> size_t {
}
// Computes approximate display width of a UTF-8 string.
-FMT_CONSTEXPR inline size_t compute_width(string_view s) {
+FMT_CONSTEXPR inline auto compute_width(string_view s) -> size_t {
size_t num_code_points = 0;
// It is not a lambda for compatibility with C++14.
struct count_code_points {
@@ -791,12 +747,17 @@ inline auto code_point_index(basic_string_view<Char> s, size_t n) -> size_t {
// Calculates the index of the nth code point in a UTF-8 string.
inline auto code_point_index(string_view s, size_t n) -> size_t {
- const char* data = s.data();
- size_t num_code_points = 0;
- for (size_t i = 0, size = s.size(); i != size; ++i) {
- if ((data[i] & 0xc0) != 0x80 && ++num_code_points > n) return i;
- }
- return s.size();
+ size_t result = s.size();
+ const char* begin = s.begin();
+ for_each_codepoint(s, [begin, &n, &result](uint32_t, string_view sv) {
+ if (n != 0) {
+ --n;
+ return true;
+ }
+ result = to_unsigned(sv.begin() - begin);
+ return false;
+ });
+ return result;
}
inline auto code_point_index(basic_string_view<char8_type> s, size_t n)
@@ -906,7 +867,7 @@ enum { inline_buffer_size = 500 };
**Example**::
auto out = fmt::memory_buffer();
- format_to(std::back_inserter(out), "The answer is {}.", 42);
+ fmt::format_to(std::back_inserter(out), "The answer is {}.", 42);
This will append the following output to the ``out`` object:
@@ -1022,7 +983,6 @@ class basic_memory_buffer final : public detail::buffer<T> {
/** Increases the buffer capacity to *new_capacity*. */
void reserve(size_t new_capacity) { this->try_reserve(new_capacity); }
- // Directly append data into the buffer
using detail::buffer<T>::append;
template <typename ContiguousRange>
void append(const ContiguousRange& range) {
@@ -1038,7 +998,7 @@ struct is_contiguous<basic_memory_buffer<T, SIZE, Allocator>> : std::true_type {
FMT_END_EXPORT
namespace detail {
-FMT_API bool write_console(int fd, string_view text);
+FMT_API auto write_console(int fd, string_view text) -> bool;
FMT_API void print(std::FILE*, string_view);
} // namespace detail
@@ -1157,13 +1117,13 @@ using uint32_or_64_or_128_t =
template <typename T>
using uint64_or_128_t = conditional_t<num_bits<T>() <= 64, uint64_t, uint128_t>;
-#define FMT_POWERS_OF_10(factor) \
- factor * 10, (factor)*100, (factor)*1000, (factor)*10000, (factor)*100000, \
- (factor)*1000000, (factor)*10000000, (factor)*100000000, \
- (factor)*1000000000
+#define FMT_POWERS_OF_10(factor) \
+ factor * 10, (factor) * 100, (factor) * 1000, (factor) * 10000, \
+ (factor) * 100000, (factor) * 1000000, (factor) * 10000000, \
+ (factor) * 100000000, (factor) * 1000000000
// Converts value in the range [0, 100) to a string.
-constexpr const char* digits2(size_t value) {
+constexpr auto digits2(size_t value) -> const char* {
// GCC generates slightly better code when value is pointer-size.
return &"0001020304050607080910111213141516171819"
"2021222324252627282930313233343536373839"
@@ -1173,7 +1133,7 @@ constexpr const char* digits2(size_t value) {
}
// Sign is a template parameter to workaround a bug in gcc 4.8.
-template <typename Char, typename Sign> constexpr Char sign(Sign s) {
+template <typename Char, typename Sign> constexpr auto sign(Sign s) -> Char {
#if !FMT_GCC_VERSION || FMT_GCC_VERSION >= 604
static_assert(std::is_same<Sign, sign_t>::value, "");
#endif
@@ -1434,22 +1394,23 @@ template <typename WChar, typename Buffer = memory_buffer> class to_utf8 {
: "invalid utf32"));
}
operator string_view() const { return string_view(&buffer_[0], size()); }
- size_t size() const { return buffer_.size() - 1; }
- const char* c_str() const { return &buffer_[0]; }
- std::string str() const { return std::string(&buffer_[0], size()); }
+ auto size() const -> size_t { return buffer_.size() - 1; }
+ auto c_str() const -> const char* { return &buffer_[0]; }
+ auto str() const -> std::string { return std::string(&buffer_[0], size()); }
// Performs conversion returning a bool instead of throwing exception on
// conversion error. This method may still throw in case of memory allocation
// error.
- bool convert(basic_string_view<WChar> s,
- to_utf8_error_policy policy = to_utf8_error_policy::abort) {
+ auto convert(basic_string_view<WChar> s,
+ to_utf8_error_policy policy = to_utf8_error_policy::abort)
+ -> bool {
if (!convert(buffer_, s, policy)) return false;
buffer_.push_back(0);
return true;
}
- static bool convert(
- Buffer& buf, basic_string_view<WChar> s,
- to_utf8_error_policy policy = to_utf8_error_policy::abort) {
+ static auto convert(Buffer& buf, basic_string_view<WChar> s,
+ to_utf8_error_policy policy = to_utf8_error_policy::abort)
+ -> bool {
for (auto p = s.begin(); p != s.end(); ++p) {
uint32_t c = static_cast<uint32_t>(*p);
if (sizeof(WChar) == 2 && c >= 0xd800 && c <= 0xdfff) {
@@ -1485,7 +1446,7 @@ template <typename WChar, typename Buffer = memory_buffer> class to_utf8 {
};
// Computes 128-bit result of multiplication of two 64-bit unsigned integers.
-inline uint128_fallback umul128(uint64_t x, uint64_t y) noexcept {
+inline auto umul128(uint64_t x, uint64_t y) noexcept -> uint128_fallback {
#if FMT_USE_INT128
auto p = static_cast<uint128_opt>(x) * static_cast<uint128_opt>(y);
return {static_cast<uint64_t>(p >> 64), static_cast<uint64_t>(p)};
@@ -1516,19 +1477,19 @@ inline uint128_fallback umul128(uint64_t x, uint64_t y) noexcept {
namespace dragonbox {
// Computes floor(log10(pow(2, e))) for e in [-2620, 2620] using the method from
// https://fmt.dev/papers/Dragonbox.pdf#page=28, section 6.1.
-inline int floor_log10_pow2(int e) noexcept {
+inline auto floor_log10_pow2(int e) noexcept -> int {
FMT_ASSERT(e <= 2620 && e >= -2620, "too large exponent");
static_assert((-1 >> 1) == -1, "right shift is not arithmetic");
return (e * 315653) >> 20;
}
-inline int floor_log2_pow10(int e) noexcept {
+inline auto floor_log2_pow10(int e) noexcept -> int {
FMT_ASSERT(e <= 1233 && e >= -1233, "too large exponent");
return (e * 1741647) >> 19;
}
// Computes upper 64 bits of multiplication of two 64-bit unsigned integers.
-inline uint64_t umul128_upper64(uint64_t x, uint64_t y) noexcept {
+inline auto umul128_upper64(uint64_t x, uint64_t y) noexcept -> uint64_t {
#if FMT_USE_INT128
auto p = static_cast<uint128_opt>(x) * static_cast<uint128_opt>(y);
return static_cast<uint64_t>(p >> 64);
@@ -1541,14 +1502,14 @@ inline uint64_t umul128_upper64(uint64_t x, uint64_t y) noexcept {
// Computes upper 128 bits of multiplication of a 64-bit unsigned integer and a
// 128-bit unsigned integer.
-inline uint128_fallback umul192_upper128(uint64_t x,
- uint128_fallback y) noexcept {
+inline auto umul192_upper128(uint64_t x, uint128_fallback y) noexcept
+ -> uint128_fallback {
uint128_fallback r = umul128(x, y.high());
r += umul128_upper64(x, y.low());
return r;
}
-FMT_API uint128_fallback get_cached_power(int k) noexcept;
+FMT_API auto get_cached_power(int k) noexcept -> uint128_fallback;
// Type-specific information that Dragonbox uses.
template <typename T, typename Enable = void> struct float_info;
@@ -1602,14 +1563,14 @@ template <typename T> FMT_API auto to_decimal(T x) noexcept -> decimal_fp<T>;
} // namespace dragonbox
// Returns true iff Float has the implicit bit which is not stored.
-template <typename Float> constexpr bool has_implicit_bit() {
+template <typename Float> constexpr auto has_implicit_bit() -> bool {
// An 80-bit FP number has a 64-bit significand an no implicit bit.
return std::numeric_limits<Float>::digits != 64;
}
// Returns the number of significand bits stored in Float. The implicit bit is
// not counted since it is not stored.
-template <typename Float> constexpr int num_significand_bits() {
+template <typename Float> constexpr auto num_significand_bits() -> int {
// std::numeric_limits may not support __float128.
return is_float128<Float>() ? 112
: (std::numeric_limits<Float>::digits -
@@ -1702,7 +1663,7 @@ using fp = basic_fp<unsigned long long>;
// Normalizes the value converted from double and multiplied by (1 << SHIFT).
template <int SHIFT = 0, typename F>
-FMT_CONSTEXPR basic_fp<F> normalize(basic_fp<F> value) {
+FMT_CONSTEXPR auto normalize(basic_fp<F> value) -> basic_fp<F> {
// Handle subnormals.
const auto implicit_bit = F(1) << num_significand_bits<double>();
const auto shifted_implicit_bit = implicit_bit << SHIFT;
@@ -1719,7 +1680,7 @@ FMT_CONSTEXPR basic_fp<F> normalize(basic_fp<F> value) {
}
// Computes lhs * rhs / pow(2, 64) rounded to nearest with half-up tie breaking.
-FMT_CONSTEXPR inline uint64_t multiply(uint64_t lhs, uint64_t rhs) {
+FMT_CONSTEXPR inline auto multiply(uint64_t lhs, uint64_t rhs) -> uint64_t {
#if FMT_USE_INT128
auto product = static_cast<__uint128_t>(lhs) * rhs;
auto f = static_cast<uint64_t>(product >> 64);
@@ -1736,7 +1697,7 @@ FMT_CONSTEXPR inline uint64_t multiply(uint64_t lhs, uint64_t rhs) {
#endif
}
-FMT_CONSTEXPR inline fp operator*(fp x, fp y) {
+FMT_CONSTEXPR inline auto operator*(fp x, fp y) -> fp {
return {multiply(x.f, y.f), x.e + y.e + 64};
}
@@ -1962,8 +1923,9 @@ auto write_escaped_char(OutputIt out, Char v) -> OutputIt {
*out++ = static_cast<Char>('\'');
if ((needs_escape(static_cast<uint32_t>(v)) && v != static_cast<Char>('"')) ||
v == static_cast<Char>('\'')) {
- out = write_escaped_cp(
- out, find_escape_result<Char>{v_array, v_array + 1, static_cast<uint32_t>(v)});
+ out = write_escaped_cp(out,
+ find_escape_result<Char>{v_array, v_array + 1,
+ static_cast<uint32_t>(v)});
} else {
*out++ = v;
}
@@ -2052,10 +2014,10 @@ template <typename Char> class digit_grouping {
std::string::const_iterator group;
int pos;
};
- next_state initial_state() const { return {grouping_.begin(), 0}; }
+ auto initial_state() const -> next_state { return {grouping_.begin(), 0}; }
// Returns the next digit group separator position.
- int next(next_state& state) const {
+ auto next(next_state& state) const -> int {
if (thousands_sep_.empty()) return max_value<int>();
if (state.group == grouping_.end()) return state.pos += grouping_.back();
if (*state.group <= 0 || *state.group == max_value<char>())
@@ -2074,9 +2036,9 @@ template <typename Char> class digit_grouping {
digit_grouping(std::string grouping, std::basic_string<Char> sep)
: grouping_(std::move(grouping)), thousands_sep_(std::move(sep)) {}
- bool has_separator() const { return !thousands_sep_.empty(); }
+ auto has_separator() const -> bool { return !thousands_sep_.empty(); }
- int count_separators(int num_digits) const {
+ auto count_separators(int num_digits) const -> int {
int count = 0;
auto state = initial_state();
while (num_digits > next(state)) ++count;
@@ -2085,7 +2047,7 @@ template <typename Char> class digit_grouping {
// Applies grouping to digits and write the output to out.
template <typename Out, typename C>
- Out apply(Out out, basic_string_view<C> digits) const {
+ auto apply(Out out, basic_string_view<C> digits) const -> Out {
auto num_digits = static_cast<int>(digits.size());
auto separators = basic_memory_buffer<int>();
separators.push_back(0);
@@ -2108,24 +2070,66 @@ template <typename Char> class digit_grouping {
}
};
+FMT_CONSTEXPR inline void prefix_append(unsigned& prefix, unsigned value) {
+ prefix |= prefix != 0 ? value << 8 : value;
+ prefix += (1u + (value > 0xff ? 1 : 0)) << 24;
+}
+
// Writes a decimal integer with digit grouping.
template <typename OutputIt, typename UInt, typename Char>
auto write_int(OutputIt out, UInt value, unsigned prefix,
const format_specs<Char>& specs,
const digit_grouping<Char>& grouping) -> OutputIt {
static_assert(std::is_same<uint64_or_128_t<UInt>, UInt>::value, "");
- int num_digits = count_digits(value);
- char digits[40];
- format_decimal(digits, value, num_digits);
- unsigned size = to_unsigned((prefix != 0 ? 1 : 0) + num_digits +
- grouping.count_separators(num_digits));
+ int num_digits = 0;
+ auto buffer = memory_buffer();
+ switch (specs.type) {
+ case presentation_type::none:
+ case presentation_type::dec: {
+ num_digits = count_digits(value);
+ format_decimal<char>(appender(buffer), value, num_digits);
+ break;
+ }
+ case presentation_type::hex_lower:
+ case presentation_type::hex_upper: {
+ bool upper = specs.type == presentation_type::hex_upper;
+ if (specs.alt)
+ prefix_append(prefix, unsigned(upper ? 'X' : 'x') << 8 | '0');
+ num_digits = count_digits<4>(value);
+ format_uint<4, char>(appender(buffer), value, num_digits, upper);
+ break;
+ }
+ case presentation_type::bin_lower:
+ case presentation_type::bin_upper: {
+ bool upper = specs.type == presentation_type::bin_upper;
+ if (specs.alt)
+ prefix_append(prefix, unsigned(upper ? 'B' : 'b') << 8 | '0');
+ num_digits = count_digits<1>(value);
+ format_uint<1, char>(appender(buffer), value, num_digits);
+ break;
+ }
+ case presentation_type::oct: {
+ num_digits = count_digits<3>(value);
+ // Octal prefix '0' is counted as a digit, so only add it if precision
+ // is not greater than the number of digits.
+ if (specs.alt && specs.precision <= num_digits && value != 0)
+ prefix_append(prefix, '0');
+ format_uint<3, char>(appender(buffer), value, num_digits);
+ break;
+ }
+ case presentation_type::chr:
+ return write_char(out, static_cast<Char>(value), specs);
+ default:
+ throw_format_error("invalid format specifier");
+ }
+
+ unsigned size = (prefix != 0 ? prefix >> 24 : 0) + to_unsigned(num_digits) +
+ to_unsigned(grouping.count_separators(num_digits));
return write_padded<align::right>(
out, specs, size, size, [&](reserve_iterator<OutputIt> it) {
- if (prefix != 0) {
- char sign = static_cast<char>(prefix);
- *it++ = static_cast<Char>(sign);
- }
- return grouping.apply(it, string_view(digits, to_unsigned(num_digits)));
+ for (unsigned p = prefix & 0xffffff; p != 0; p >>= 8)
+ *it++ = static_cast<Char>(p & 0xff);
+ return grouping.apply(it, string_view(buffer.data(), buffer.size()));
});
}
@@ -2138,11 +2142,6 @@ inline auto write_loc(OutputIt, loc_value, const format_specs<Char>&,
return false;
}
-FMT_CONSTEXPR inline void prefix_append(unsigned& prefix, unsigned value) {
- prefix |= prefix != 0 ? value << 8 : value;
- prefix += (1u + (value > 0xff ? 1 : 0)) << 24;
-}
-
template <typename UInt> struct write_int_arg {
UInt abs_value;
unsigned prefix;
@@ -2289,25 +2288,25 @@ class counting_iterator {
FMT_CONSTEXPR counting_iterator() : count_(0) {}
- FMT_CONSTEXPR size_t count() const { return count_; }
+ FMT_CONSTEXPR auto count() const -> size_t { return count_; }
- FMT_CONSTEXPR counting_iterator& operator++() {
+ FMT_CONSTEXPR auto operator++() -> counting_iterator& {
++count_;
return *this;
}
- FMT_CONSTEXPR counting_iterator operator++(int) {
+ FMT_CONSTEXPR auto operator++(int) -> counting_iterator {
auto it = *this;
++*this;
return it;
}
- FMT_CONSTEXPR friend counting_iterator operator+(counting_iterator it,
- difference_type n) {
+ FMT_CONSTEXPR friend auto operator+(counting_iterator it, difference_type n)
+ -> counting_iterator {
it.count_ += static_cast<size_t>(n);
return it;
}
- FMT_CONSTEXPR value_type operator*() const { return {}; }
+ FMT_CONSTEXPR auto operator*() const -> value_type { return {}; }
};
template <typename Char, typename OutputIt>
@@ -2342,9 +2341,10 @@ template <typename Char, typename OutputIt>
FMT_CONSTEXPR auto write(OutputIt out, const Char* s,
const format_specs<Char>& specs, locale_ref)
-> OutputIt {
- return specs.type != presentation_type::pointer
- ? write(out, basic_string_view<Char>(s), specs, {})
- : write_ptr<Char>(out, bit_cast<uintptr_t>(s), &specs);
+ if (specs.type == presentation_type::pointer)
+ return write_ptr<Char>(out, bit_cast<uintptr_t>(s), &specs);
+ if (!s) throw_format_error("string pointer is null");
+ return write(out, basic_string_view<Char>(s), specs, {});
}
template <typename Char, typename OutputIt, typename T,
@@ -2430,9 +2430,8 @@ struct float_specs {
bool showpoint : 1;
};
-template <typename ErrorHandler = error_handler, typename Char>
-FMT_CONSTEXPR auto parse_float_type_spec(const format_specs<Char>& specs,
- ErrorHandler&& eh = {})
+template <typename Char>
+FMT_CONSTEXPR auto parse_float_type_spec(const format_specs<Char>& specs)
-> float_specs {
auto result = float_specs();
result.showpoint = specs.alt;
@@ -2468,7 +2467,7 @@ FMT_CONSTEXPR auto parse_float_type_spec(const format_specs<Char>& specs,
result.format = float_format::hex;
break;
default:
- eh.on_error("invalid format specifier");
+ throw_format_error("invalid format specifier");
break;
}
return result;
@@ -2707,12 +2706,12 @@ template <typename Char> class fallback_digit_grouping {
public:
constexpr fallback_digit_grouping(locale_ref, bool) {}
- constexpr bool has_separator() const { return false; }
+ constexpr auto has_separator() const -> bool { return false; }
- constexpr int count_separators(int) const { return 0; }
+ constexpr auto count_separators(int) const -> int { return 0; }
template <typename Out, typename C>
- constexpr Out apply(Out out, basic_string_view<C>) const {
+ constexpr auto apply(Out out, basic_string_view<C>) const -> Out {
return out;
}
};
@@ -2731,7 +2730,7 @@ FMT_CONSTEXPR20 auto write_float(OutputIt out, const DecimalFP& f,
}
}
-template <typename T> constexpr bool isnan(T value) {
+template <typename T> constexpr auto isnan(T value) -> bool {
return !(value >= value); // std::isnan doesn't support __float128.
}
@@ -2744,14 +2743,14 @@ struct has_isfinite<T, enable_if_t<sizeof(std::isfinite(T())) != 0>>
template <typename T, FMT_ENABLE_IF(std::is_floating_point<T>::value&&
has_isfinite<T>::value)>
-FMT_CONSTEXPR20 bool isfinite(T value) {
+FMT_CONSTEXPR20 auto isfinite(T value) -> bool {
constexpr T inf = T(std::numeric_limits<double>::infinity());
if (is_constant_evaluated(true))
return !detail::isnan(value) && value < inf && value > -inf;
return std::isfinite(value);
}
template <typename T, FMT_ENABLE_IF(!has_isfinite<T>::value)>
-FMT_CONSTEXPR bool isfinite(T value) {
+FMT_CONSTEXPR auto isfinite(T value) -> bool {
T inf = T(std::numeric_limits<double>::infinity());
// std::isfinite doesn't support __float128.
return !detail::isnan(value) && value < inf && value > -inf;
@@ -2788,10 +2787,10 @@ class bigint {
basic_memory_buffer<bigit, bigits_capacity> bigits_;
int exp_;
- FMT_CONSTEXPR20 bigit operator[](int index) const {
+ FMT_CONSTEXPR20 auto operator[](int index) const -> bigit {
return bigits_[to_unsigned(index)];
}
- FMT_CONSTEXPR20 bigit& operator[](int index) {
+ FMT_CONSTEXPR20 auto operator[](int index) -> bigit& {
return bigits_[to_unsigned(index)];
}
@@ -2887,11 +2886,11 @@ class bigint {
assign(uint64_or_128_t<Int>(n));
}
- FMT_CONSTEXPR20 int num_bigits() const {
+ FMT_CONSTEXPR20 auto num_bigits() const -> int {
return static_cast<int>(bigits_.size()) + exp_;
}
- FMT_NOINLINE FMT_CONSTEXPR20 bigint& operator<<=(int shift) {
+ FMT_NOINLINE FMT_CONSTEXPR20 auto operator<<=(int shift) -> bigint& {
FMT_ASSERT(shift >= 0, "");
exp_ += shift / bigit_bits;
shift %= bigit_bits;
@@ -2906,13 +2905,15 @@ class bigint {
return *this;
}
- template <typename Int> FMT_CONSTEXPR20 bigint& operator*=(Int value) {
+ template <typename Int>
+ FMT_CONSTEXPR20 auto operator*=(Int value) -> bigint& {
FMT_ASSERT(value > 0, "");
multiply(uint32_or_64_or_128_t<Int>(value));
return *this;
}
- friend FMT_CONSTEXPR20 int compare(const bigint& lhs, const bigint& rhs) {
+ friend FMT_CONSTEXPR20 auto compare(const bigint& lhs, const bigint& rhs)
+ -> int {
int num_lhs_bigits = lhs.num_bigits(), num_rhs_bigits = rhs.num_bigits();
if (num_lhs_bigits != num_rhs_bigits)
return num_lhs_bigits > num_rhs_bigits ? 1 : -1;
@@ -2929,8 +2930,9 @@ class bigint {
}
// Returns compare(lhs1 + lhs2, rhs).
- friend FMT_CONSTEXPR20 int add_compare(const bigint& lhs1, const bigint& lhs2,
- const bigint& rhs) {
+ friend FMT_CONSTEXPR20 auto add_compare(const bigint& lhs1,
+ const bigint& lhs2, const bigint& rhs)
+ -> int {
auto minimum = [](int a, int b) { return a < b ? a : b; };
auto maximum = [](int a, int b) { return a > b ? a : b; };
int max_lhs_bigits = maximum(lhs1.num_bigits(), lhs2.num_bigits());
@@ -3017,7 +3019,7 @@ class bigint {
// Divides this bignum by divisor, assigning the remainder to this and
// returning the quotient.
- FMT_CONSTEXPR20 int divmod_assign(const bigint& divisor) {
+ FMT_CONSTEXPR20 auto divmod_assign(const bigint& divisor) -> int {
FMT_ASSERT(this != &divisor, "");
if (compare(*this, divisor) < 0) return 0;
FMT_ASSERT(divisor.bigits_[divisor.bigits_.size() - 1u] != 0, "");
@@ -3260,7 +3262,7 @@ FMT_CONSTEXPR20 void format_hexfloat(Float value, int precision,
format_hexfloat(static_cast<double>(value), precision, specs, buf);
}
-constexpr uint32_t fractional_part_rounding_thresholds(int index) {
+constexpr auto fractional_part_rounding_thresholds(int index) -> uint32_t {
// For checking rounding thresholds.
// The kth entry is chosen to be the smallest integer such that the
// upper 32-bits of 10^(k+1) times it is strictly bigger than 5 * 10^k.
@@ -3799,62 +3801,39 @@ template <typename Char> struct arg_formatter {
}
};
-template <typename Char> struct custom_formatter {
- basic_format_parse_context<Char>& parse_ctx;
- buffer_context<Char>& ctx;
-
- void operator()(
- typename basic_format_arg<buffer_context<Char>>::handle h) const {
- h.format(parse_ctx, ctx);
- }
- template <typename T> void operator()(T) const {}
-};
-
-template <typename ErrorHandler> class width_checker {
- public:
- explicit FMT_CONSTEXPR width_checker(ErrorHandler& eh) : handler_(eh) {}
-
+struct width_checker {
template <typename T, FMT_ENABLE_IF(is_integer<T>::value)>
FMT_CONSTEXPR auto operator()(T value) -> unsigned long long {
- if (is_negative(value)) handler_.on_error("negative width");
+ if (is_negative(value)) throw_format_error("negative width");
return static_cast<unsigned long long>(value);
}
template <typename T, FMT_ENABLE_IF(!is_integer<T>::value)>
FMT_CONSTEXPR auto operator()(T) -> unsigned long long {
- handler_.on_error("width is not integer");
+ throw_format_error("width is not integer");
return 0;
}
-
- private:
- ErrorHandler& handler_;
};
-template <typename ErrorHandler> class precision_checker {
- public:
- explicit FMT_CONSTEXPR precision_checker(ErrorHandler& eh) : handler_(eh) {}
-
+struct precision_checker {
template <typename T, FMT_ENABLE_IF(is_integer<T>::value)>
FMT_CONSTEXPR auto operator()(T value) -> unsigned long long {
- if (is_negative(value)) handler_.on_error("negative precision");
+ if (is_negative(value)) throw_format_error("negative precision");
return static_cast<unsigned long long>(value);
}
template <typename T, FMT_ENABLE_IF(!is_integer<T>::value)>
FMT_CONSTEXPR auto operator()(T) -> unsigned long long {
- handler_.on_error("precision is not integer");
+ throw_format_error("precision is not integer");
return 0;
}
-
- private:
- ErrorHandler& handler_;
};
-template <template <typename> class Handler, typename FormatArg,
- typename ErrorHandler>
-FMT_CONSTEXPR auto get_dynamic_spec(FormatArg arg, ErrorHandler eh) -> int {
- unsigned long long value = visit_format_arg(Handler<ErrorHandler>(eh), arg);
- if (value > to_unsigned(max_value<int>())) eh.on_error("number is too big");
+template <typename Handler, typename FormatArg>
+FMT_CONSTEXPR auto get_dynamic_spec(FormatArg arg) -> int {
+ unsigned long long value = visit_format_arg(Handler(), arg);
+ if (value > to_unsigned(max_value<int>()))
+ throw_format_error("number is too big");
return static_cast<int>(value);
}
@@ -3865,7 +3844,7 @@ FMT_CONSTEXPR auto get_arg(Context& ctx, ID id) -> decltype(ctx.arg(id)) {
return arg;
}
-template <template <typename> class Handler, typename Context>
+template <typename Handler, typename Context>
FMT_CONSTEXPR void handle_dynamic_spec(int& value,
arg_ref<typename Context::char_type> ref,
Context& ctx) {
@@ -3873,12 +3852,10 @@ FMT_CONSTEXPR void handle_dynamic_spec(int& value,
case arg_id_kind::none:
break;
case arg_id_kind::index:
- value = detail::get_dynamic_spec<Handler>(get_arg(ctx, ref.val.index),
- ctx.error_handler());
+ value = detail::get_dynamic_spec<Handler>(get_arg(ctx, ref.val.index));
break;
case arg_id_kind::name:
- value = detail::get_dynamic_spec<Handler>(get_arg(ctx, ref.val.name),
- ctx.error_handler());
+ value = detail::get_dynamic_spec<Handler>(get_arg(ctx, ref.val.name));
break;
}
}
@@ -4050,12 +4027,10 @@ class format_int {
template <typename T, typename Char>
struct formatter<T, Char, enable_if_t<detail::has_format_as<T>::value>>
- : private formatter<detail::format_as_t<T>, Char> {
- using base = formatter<detail::format_as_t<T>, Char>;
- using base::parse;
-
+ : formatter<detail::format_as_t<T>, Char> {
template <typename FormatContext>
auto format(const T& value, FormatContext& ctx) const -> decltype(ctx.out()) {
+ using base = formatter<detail::format_as_t<T>, Char>;
return base::format(format_as(value), ctx);
}
};
@@ -4380,7 +4355,7 @@ void vformat_to(buffer<Char>& buf, basic_string_view<Char> fmt,
auto out = buffer_appender<Char>(buf);
if (fmt.size() == 2 && equal2(fmt.data(), "{}")) {
auto arg = args.get(0);
- if (!arg) error_handler().on_error("argument not found");
+ if (!arg) throw_format_error("argument not found");
visit_format_arg(default_arg_formatter<Char>{out, args, loc}, arg);
return;
}
@@ -4407,7 +4382,7 @@ void vformat_to(buffer<Char>& buf, basic_string_view<Char> fmt,
}
FMT_CONSTEXPR auto on_arg_id(basic_string_view<Char> id) -> int {
int arg_id = context.arg_id(id);
- if (arg_id < 0) on_error("argument not found");
+ if (arg_id < 0) throw_format_error("argument not found");
return arg_id;
}
@@ -4422,11 +4397,9 @@ void vformat_to(buffer<Char>& buf, basic_string_view<Char> fmt,
auto on_format_specs(int id, const Char* begin, const Char* end)
-> const Char* {
auto arg = get_arg(context, id);
- if (arg.type() == type::custom_type) {
- parse_context.advance_to(begin);
- visit_format_arg(custom_formatter<Char>{parse_context, context}, arg);
+ // Not using a visitor for custom types gives better codegen.
+ if (arg.format_custom(begin, parse_context, context))
return parse_context.begin();
- }
auto specs = detail::dynamic_format_specs<Char>();
begin = parse_format_specs(begin, end, specs, parse_context, arg.type());
detail::handle_dynamic_spec<detail::width_checker>(
@@ -4434,7 +4407,7 @@ void vformat_to(buffer<Char>& buf, basic_string_view<Char> fmt,
detail::handle_dynamic_spec<detail::precision_checker>(
specs.precision, specs.precision_ref, context);
if (begin == end || *begin != '}')
- on_error("missing '}' in format string");
+ throw_format_error("missing '}' in format string");
auto f = arg_formatter<Char>{context.out(), specs, context.locale()};
context.advance_to(visit_format_arg(f, arg));
return begin;
@@ -4537,16 +4510,16 @@ formatter<T, Char,
detail::type::custom_type>>::format(const T& val,
FormatContext& ctx)
const -> decltype(ctx.out()) {
- if (specs_.width_ref.kind != detail::arg_id_kind::none ||
- specs_.precision_ref.kind != detail::arg_id_kind::none) {
- auto specs = specs_;
- detail::handle_dynamic_spec<detail::width_checker>(specs.width,
- specs.width_ref, ctx);
- detail::handle_dynamic_spec<detail::precision_checker>(
- specs.precision, specs.precision_ref, ctx);
- return detail::write<Char>(ctx.out(), val, specs, ctx.locale());
- }
- return detail::write<Char>(ctx.out(), val, specs_, ctx.locale());
+ if (specs_.width_ref.kind == detail::arg_id_kind::none &&
+ specs_.precision_ref.kind == detail::arg_id_kind::none) {
+ return detail::write<Char>(ctx.out(), val, specs_, ctx.locale());
+ }
+ auto specs = specs_;
+ detail::handle_dynamic_spec<detail::width_checker>(specs.width,
+ specs.width_ref, ctx);
+ detail::handle_dynamic_spec<detail::precision_checker>(
+ specs.precision, specs.precision_ref, ctx);
+ return detail::write<Char>(ctx.out(), val, specs, ctx.locale());
}
FMT_END_NAMESPACE
diff --git a/include/fmt/os.h b/include/fmt/os.h
index 2b517cd4..3c7b3ccb 100644
--- a/include/fmt/os.h
+++ b/include/fmt/os.h
@@ -48,6 +48,7 @@
// Calls to system functions are wrapped in FMT_SYSTEM for testability.
#ifdef FMT_SYSTEM
+# define FMT_HAS_SYSTEM
# define FMT_POSIX_CALL(call) FMT_SYSTEM(call)
#else
# define FMT_SYSTEM(call) ::call
@@ -116,7 +117,7 @@ template <typename Char> class basic_cstring_view {
basic_cstring_view(const std::basic_string<Char>& s) : data_(s.c_str()) {}
/** Returns the pointer to a C string. */
- const Char* c_str() const { return data_; }
+ auto c_str() const -> const Char* { return data_; }
};
using cstring_view = basic_cstring_view<char>;
@@ -171,7 +172,7 @@ std::system_error windows_error(int error_code, string_view message,
// Can be used to report errors from destructors.
FMT_API void report_windows_error(int error_code, const char* message) noexcept;
#else
-inline const std::error_category& system_category() noexcept {
+inline auto system_category() noexcept -> const std::error_category& {
return std::system_category();
}
#endif // _WIN32
@@ -208,7 +209,7 @@ class buffered_file {
other.file_ = nullptr;
}
- buffered_file& operator=(buffered_file&& other) {
+ auto operator=(buffered_file&& other) -> buffered_file& {
close();
file_ = other.file_;
other.file_ = nullptr;
@@ -222,9 +223,9 @@ class buffered_file {
FMT_API void close();
// Returns the pointer to a FILE object representing this file.
- FILE* get() const noexcept { return file_; }
+ auto get() const noexcept -> FILE* { return file_; }
- FMT_API int descriptor() const;
+ FMT_API auto descriptor() const -> int;
void vprint(string_view format_str, format_args args) {
fmt::vprint(file_, format_str, args);
@@ -274,7 +275,7 @@ class FMT_API file {
file(file&& other) noexcept : fd_(other.fd_) { other.fd_ = -1; }
// Move assignment is not noexcept because close may throw.
- file& operator=(file&& other) {
+ auto operator=(file&& other) -> file& {
close();
fd_ = other.fd_;
other.fd_ = -1;
@@ -285,24 +286,24 @@ class FMT_API file {
~file() noexcept;
// Returns the file descriptor.
- int descriptor() const noexcept { return fd_; }
+ auto descriptor() const noexcept -> int { return fd_; }
// Closes the file.
void close();
// Returns the file size. The size has signed type for consistency with
// stat::st_size.
- long long size() const;
+ auto size() const -> long long;
// Attempts to read count bytes from the file into the specified buffer.
- size_t read(void* buffer, size_t count);
+ auto read(void* buffer, size_t count) -> size_t;
// Attempts to write count bytes from the specified buffer to the file.
- size_t write(const void* buffer, size_t count);
+ auto write(const void* buffer, size_t count) -> size_t;
// Duplicates a file descriptor with the dup function and returns
// the duplicate as a file object.
- static file dup(int fd);
+ static auto dup(int fd) -> file;
// Makes fd be the copy of this file descriptor, closing fd first if
// necessary.
@@ -314,11 +315,12 @@ class FMT_API file {
// Creates a pipe setting up read_end and write_end file objects for reading
// and writing respectively.
+ // DEPRECATED! Taking files as out parameters is deprecated.
static void pipe(file& read_end, file& write_end);
// Creates a buffered_file object associated with this file and detaches
// this file object from the file.
- buffered_file fdopen(const char* mode);
+ auto fdopen(const char* mode) -> buffered_file;
# if defined(_WIN32) && !defined(__MINGW32__)
// Opens a file and constructs a file object representing this file by
@@ -328,14 +330,14 @@ class FMT_API file {
};
// Returns the memory page size.
-long getpagesize();
+auto getpagesize() -> long;
namespace detail {
struct buffer_size {
buffer_size() = default;
size_t value = 0;
- buffer_size operator=(size_t val) const {
+ auto operator=(size_t val) const -> buffer_size {
auto bs = buffer_size();
bs.value = val;
return bs;
@@ -412,7 +414,7 @@ class FMT_API ostream {
void flush() { buffer_.flush(); }
template <typename... T>
- friend ostream output_file(cstring_view path, T... params);
+ friend auto output_file(cstring_view path, T... params) -> ostream;
void close() { buffer_.close(); }
@@ -442,7 +444,7 @@ class FMT_API ostream {
\endrst
*/
template <typename... T>
-inline ostream output_file(cstring_view path, T... params) {
+inline auto output_file(cstring_view path, T... params) -> ostream {
return {path, detail::ostream_params(params...)};
}
#endif // FMT_USE_FCNTL
diff --git a/include/fmt/ostream.h b/include/fmt/ostream.h
index 782ace5c..26fb3b5a 100644
--- a/include/fmt/ostream.h
+++ b/include/fmt/ostream.h
@@ -21,9 +21,39 @@
#include "format.h"
FMT_BEGIN_NAMESPACE
-
namespace detail {
+template <typename Streambuf> class formatbuf : public Streambuf {
+ private:
+ using char_type = typename Streambuf::char_type;
+ using streamsize = decltype(std::declval<Streambuf>().sputn(nullptr, 0));
+ using int_type = typename Streambuf::int_type;
+ using traits_type = typename Streambuf::traits_type;
+
+ buffer<char_type>& buffer_;
+
+ public:
+ explicit formatbuf(buffer<char_type>& buf) : buffer_(buf) {}
+
+ protected:
+ // The put area is always empty. This makes the implementation simpler and has
+ // the advantage that the streambuf and the buffer are always in sync and
+ // sputc never writes into uninitialized memory. A disadvantage is that each
+ // call to sputc always results in a (virtual) call to overflow. There is no
+ // disadvantage here for sputn since this always results in a call to xsputn.
+
+ auto overflow(int_type ch) -> int_type override {
+ if (!traits_type::eq_int_type(ch, traits_type::eof()))
+ buffer_.push_back(static_cast<char_type>(ch));
+ return ch;
+ }
+
+ auto xsputn(const char_type* s, streamsize count) -> streamsize override {
+ buffer_.append(s, s + count);
+ return count;
+ }
+};
+
// Generate a unique explicit instantion in every translation unit using a tag
// type in an anonymous namespace.
namespace {
@@ -40,7 +70,8 @@ template class file_access<file_access_tag, std::filebuf,
auto get_file(std::filebuf&) -> FILE*;
#endif
-inline bool write_ostream_unicode(std::ostream& os, fmt::string_view data) {
+inline auto write_ostream_unicode(std::ostream& os, fmt::string_view data)
+ -> bool {
FILE* f = nullptr;
#if FMT_MSC_VERSION
if (auto* buf = dynamic_cast<std::filebuf*>(os.rdbuf()))
@@ -69,8 +100,8 @@ inline bool write_ostream_unicode(std::ostream& os, fmt::string_view data) {
#endif
return false;
}
-inline bool write_ostream_unicode(std::wostream&,
- fmt::basic_string_view<wchar_t>) {
+inline auto write_ostream_unicode(std::wostream&,
+ fmt::basic_string_view<wchar_t>) -> bool {
return false;
}
@@ -91,12 +122,11 @@ void write_buffer(std::basic_ostream<Char>& os, buffer<Char>& buf) {
}
template <typename Char, typename T>
-void format_value(buffer<Char>& buf, const T& value,
- locale_ref loc = locale_ref()) {
+void format_value(buffer<Char>& buf, const T& value) {
auto&& format_buf = formatbuf<std::basic_streambuf<Char>>(buf);
auto&& output = std::basic_ostream<Char>(&format_buf);
#if !defined(FMT_STATIC_THOUSANDS_SEPARATOR)
- if (loc) output.imbue(loc.get<std::locale>());
+ output.imbue(std::locale::classic()); // The default is always unlocalized.
#endif
output << value;
output.exceptions(std::ios_base::failbit | std::ios_base::badbit);
@@ -117,7 +147,7 @@ struct basic_ostream_formatter : formatter<basic_string_view<Char>, Char> {
auto format(const T& value, basic_format_context<OutputIt, Char>& ctx) const
-> OutputIt {
auto buffer = basic_memory_buffer<Char>();
- detail::format_value(buffer, value, ctx.locale());
+ detail::format_value(buffer, value);
return formatter<basic_string_view<Char>, Char>::format(
{buffer.data(), buffer.size()}, ctx);
}
diff --git a/include/fmt/ranges.h b/include/fmt/ranges.h
index c0b51aee..3638fffb 100644
--- a/include/fmt/ranges.h
+++ b/include/fmt/ranges.h
@@ -183,7 +183,7 @@ template <size_t N> using make_index_sequence = std::make_index_sequence<N>;
template <typename T, T... N> struct integer_sequence {
using value_type = T;
- static FMT_CONSTEXPR size_t size() { return sizeof...(N); }
+ static FMT_CONSTEXPR auto size() -> size_t { return sizeof...(N); }
};
template <size_t... N> using index_sequence = integer_sequence<size_t, N...>;
@@ -207,15 +207,15 @@ class is_tuple_formattable_ {
};
template <typename T, typename C> class is_tuple_formattable_<T, C, true> {
template <std::size_t... Is>
- static std::true_type check2(index_sequence<Is...>,
- integer_sequence<bool, (Is == Is)...>);
- static std::false_type check2(...);
+ static auto check2(index_sequence<Is...>,
+ integer_sequence<bool, (Is == Is)...>) -> std::true_type;
+ static auto check2(...) -> std::false_type;
template <std::size_t... Is>
- static decltype(check2(
+ static auto check(index_sequence<Is...>) -> decltype(check2(
index_sequence<Is...>{},
- integer_sequence<
- bool, (is_formattable<typename std::tuple_element<Is, T>::type,
- C>::value)...>{})) check(index_sequence<Is...>);
+ integer_sequence<bool,
+ (is_formattable<typename std::tuple_element<Is, T>::type,
+ C>::value)...>{}));
public:
static constexpr const bool value =
@@ -417,6 +417,12 @@ struct is_formattable_delayed
#endif
} // namespace detail
+template <typename...> struct conjunction : std::true_type {};
+template <typename P> struct conjunction<P> : P {};
+template <typename P1, typename... Pn>
+struct conjunction<P1, Pn...>
+ : conditional_t<bool(P1::value), conjunction<Pn...>, P1> {};
+
template <typename T, typename Char, typename Enable = void>
struct range_formatter;
@@ -482,7 +488,8 @@ struct range_formatter<
for (; it != end; ++it) {
if (i > 0) out = detail::copy_str<Char>(separator_, out);
ctx.advance_to(out);
- out = underlying_.format(mapper.map(*it), ctx);
+ auto&& item = *it;
+ out = underlying_.format(mapper.map(item), ctx);
++i;
}
out = detail::copy_str<Char>(closing_bracket_, out);
diff --git a/include/fmt/std.h b/include/fmt/std.h
index 4d1f97d2..7cff1159 100644
--- a/include/fmt/std.h
+++ b/include/fmt/std.h
@@ -38,6 +38,10 @@
# endif
#endif
+#if FMT_CPLUSPLUS > 201703L && FMT_HAS_INCLUDE(<source_location>)
+# include <source_location>
+#endif
+
// GCC 4 does not support FMT_HAS_INCLUDE.
#if FMT_HAS_INCLUDE(<cxxabi.h>) || defined(__GLIBCXX__)
# include <cxxabi.h>
@@ -59,13 +63,31 @@
# endif
#endif
-#ifdef __cpp_lib_filesystem
+// For older Xcode versions, __cpp_lib_xxx flags are inaccurately defined.
+#ifndef FMT_CPP_LIB_FILESYSTEM
+# ifdef __cpp_lib_filesystem
+# define FMT_CPP_LIB_FILESYSTEM __cpp_lib_filesystem
+# else
+# define FMT_CPP_LIB_FILESYSTEM 0
+# endif
+#endif
+
+#ifndef FMT_CPP_LIB_VARIANT
+# ifdef __cpp_lib_variant
+# define FMT_CPP_LIB_VARIANT __cpp_lib_variant
+# else
+# define FMT_CPP_LIB_VARIANT 0
+# endif
+#endif
+
+#if FMT_CPP_LIB_FILESYSTEM
FMT_BEGIN_NAMESPACE
namespace detail {
-template <typename Char, typename PathChar> auto get_path_string(
- const std::filesystem::path& p, const std::basic_string<PathChar>& native) {
+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
@@ -76,7 +98,8 @@ template <typename Char, typename PathChar>
void write_escaped_path(basic_memory_buffer<Char>& quoted,
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>) {
+ if constexpr (std::is_same_v<Char, char> &&
+ std::is_same_v<PathChar, wchar_t>) {
auto buf = basic_memory_buffer<wchar_t>();
write_escaped_string<wchar_t>(std::back_inserter(buf), native);
bool valid = to_utf8<wchar_t>::convert(quoted, {buf.data(), buf.size()});
@@ -97,6 +120,7 @@ template <typename Char> struct formatter<std::filesystem::path, Char> {
format_specs<Char> specs_;
detail::arg_ref<Char> width_ref_;
bool debug_ = false;
+ char path_type_ = 0;
public:
FMT_CONSTEXPR void set_debug_format(bool set = true) { debug_ = set; }
@@ -113,27 +137,34 @@ template <typename Char> struct formatter<std::filesystem::path, Char> {
debug_ = true;
++it;
}
+ if (it != end && (*it == 'g')) path_type_ = *it++;
return it;
}
template <typename FormatContext>
auto format(const std::filesystem::path& p, FormatContext& ctx) const {
auto specs = specs_;
+# ifdef _WIN32
+ auto path_string = !path_type_ ? p.native() : p.generic_wstring();
+# else
+ auto path_string = !path_type_ ? p.native() : p.generic_string();
+# endif
+
detail::handle_dynamic_spec<detail::width_checker>(specs.width, width_ref_,
ctx);
if (!debug_) {
- auto s = detail::get_path_string<Char>(p, p.native());
+ auto s = detail::get_path_string<Char>(p, path_string);
return detail::write(ctx.out(), basic_string_view<Char>(s), specs);
}
auto quoted = basic_memory_buffer<Char>();
- detail::write_escaped_path(quoted, p, p.native());
+ detail::write_escaped_path(quoted, p, path_string);
return detail::write(ctx.out(),
basic_string_view<Char>(quoted.data(), quoted.size()),
specs);
}
};
FMT_END_NAMESPACE
-#endif
+#endif // FMT_CPP_LIB_FILESYSTEM
FMT_BEGIN_NAMESPACE
FMT_EXPORT
@@ -145,7 +176,7 @@ struct formatter<std::bitset<N>, Char> : nested_formatter<string_view> {
const std::bitset<N>& bs;
template <typename OutputIt>
- FMT_CONSTEXPR OutputIt operator()(OutputIt out) {
+ FMT_CONSTEXPR auto operator()(OutputIt out) -> OutputIt {
for (auto pos = N; pos > 0; --pos) {
out = detail::write<Char>(out, bs[pos - 1] ? Char('1') : Char('0'));
}
@@ -197,7 +228,7 @@ struct formatter<std::optional<T>, Char,
}
template <typename FormatContext>
- auto format(std::optional<T> const& opt, FormatContext& ctx) const
+ auto format(const std::optional<T>& opt, FormatContext& ctx) const
-> decltype(ctx.out()) {
if (!opt) return detail::write<Char>(ctx.out(), none);
@@ -211,7 +242,32 @@ struct formatter<std::optional<T>, Char,
FMT_END_NAMESPACE
#endif // __cpp_lib_optional
-#ifdef __cpp_lib_variant
+#ifdef __cpp_lib_source_location
+FMT_BEGIN_NAMESPACE
+FMT_EXPORT
+template <> struct formatter<std::source_location> {
+ template <typename ParseContext> FMT_CONSTEXPR auto parse(ParseContext& ctx) {
+ return ctx.begin();
+ }
+
+ template <typename FormatContext>
+ auto format(const std::source_location& loc, FormatContext& ctx) const
+ -> decltype(ctx.out()) {
+ auto out = ctx.out();
+ out = detail::write(out, loc.file_name());
+ out = detail::write(out, ':');
+ out = detail::write<char>(out, loc.line());
+ out = detail::write(out, ':');
+ out = detail::write<char>(out, loc.column());
+ out = detail::write(out, ": ");
+ out = detail::write(out, loc.function_name());
+ return out;
+ }
+};
+FMT_END_NAMESPACE
+#endif
+
+#if FMT_CPP_LIB_VARIANT
FMT_BEGIN_NAMESPACE
namespace detail {
@@ -302,7 +358,7 @@ struct formatter<
}
};
FMT_END_NAMESPACE
-#endif // __cpp_lib_variant
+#endif // FMT_CPP_LIB_VARIANT
FMT_BEGIN_NAMESPACE
FMT_EXPORT
@@ -326,7 +382,7 @@ template <typename Char> struct formatter<std::error_code, Char> {
FMT_EXPORT
template <typename T, typename Char>
struct formatter<
- T, Char,
+ T, Char, // DEPRECATED! Mixing code unit types.
typename std::enable_if<std::is_base_of<std::exception, T>::value>::type> {
private:
bool with_typename_ = false;
@@ -357,7 +413,7 @@ struct formatter<
# ifdef FMT_HAS_ABI_CXA_DEMANGLE
int status = 0;
std::size_t size = 0;
- std::unique_ptr<char, decltype(&std::free)> demangled_name_ptr(
+ std::unique_ptr<char, void (*)(void*)> demangled_name_ptr(
abi::__cxa_demangle(ti.name(), nullptr, &size, &status), &std::free);
string_view demangled_name_view;
diff --git a/include/fmt/xchar.h b/include/fmt/xchar.h
index 625ec369..f609c5c4 100644
--- a/include/fmt/xchar.h
+++ b/include/fmt/xchar.h
@@ -63,14 +63,15 @@ template <> struct is_char<char16_t> : std::true_type {};
template <> struct is_char<char32_t> : std::true_type {};
template <typename... T>
-constexpr format_arg_store<wformat_context, T...> make_wformat_args(
- const T&... args) {
+constexpr auto make_wformat_args(const T&... args)
+ -> format_arg_store<wformat_context, T...> {
return {args...};
}
inline namespace literals {
#if FMT_USE_USER_DEFINED_LITERALS && !FMT_USE_NONTYPE_TEMPLATE_ARGS
-constexpr detail::udl_arg<wchar_t> operator"" _a(const wchar_t* s, size_t) {
+constexpr auto operator""_a(const wchar_t* s, size_t)
+ -> detail::udl_arg<wchar_t> {
return {s};
}
#endif
@@ -172,11 +173,11 @@ inline auto vformat_to(
return detail::get_iterator(buf, out);
}
-template <
- typename OutputIt, typename Locale, typename S, typename... T,
- typename Char = char_t<S>,
- bool enable = detail::is_output_iterator<OutputIt, Char>::value&&
- detail::is_locale<Locale>::value&& detail::is_exotic_char<Char>::value>
+template <typename OutputIt, typename Locale, typename S, typename... T,
+ typename Char = char_t<S>,
+ bool enable = detail::is_output_iterator<OutputIt, Char>::value &&
+ detail::is_locale<Locale>::value &&
+ detail::is_exotic_char<Char>::value>
inline auto format_to(OutputIt out, const Locale& loc, const S& format_str,
T&&... args) ->
typename std::enable_if<enable, OutputIt>::type {
diff --git a/src/fmt.cc b/src/fmt.cc
index 6638bb4a..5330463a 100644
--- a/src/fmt.cc
+++ b/src/fmt.cc
@@ -101,7 +101,7 @@ extern "C++" {
// gcc doesn't yet implement private module fragments
#if !FMT_GCC_VERSION
-module : private;
+module :private;
#endif
#include "format.cc"
diff --git a/src/os.cc b/src/os.cc
index bca410e9..a639e78c 100644
--- a/src/os.cc
+++ b/src/os.cc
@@ -18,8 +18,8 @@
# include <sys/stat.h>
# include <sys/types.h>
-# ifdef _WRS_KERNEL // VxWorks7 kernel
-# include <ioLib.h> // getpagesize
+# ifdef _WRS_KERNEL // VxWorks7 kernel
+# include <ioLib.h> // getpagesize
# endif
# ifndef _WIN32
@@ -182,10 +182,14 @@ void buffered_file::close() {
}
int buffered_file::descriptor() const {
-#ifdef fileno // fileno is a macro on OpenBSD so we cannot use FMT_POSIX_CALL.
- int fd = fileno(file_);
-#else
+#if !defined(fileno)
int fd = FMT_POSIX_CALL(fileno(file_));
+#elif defined(FMT_HAS_SYSTEM)
+ // fileno is a macro on OpenBSD so we cannot use FMT_POSIX_CALL.
+# define FMT_DISABLE_MACRO
+ int fd = FMT_SYSTEM(fileno FMT_DISABLE_MACRO(file_));
+#else
+ int fd = fileno(file_);
#endif
if (fd == -1)
FMT_THROW(system_error(errno, FMT_STRING("cannot get file descriptor")));
diff --git a/support/AndroidManifest.xml b/support/AndroidManifest.xml
index b5281fee..c282ef5a 100644
--- a/support/AndroidManifest.xml
+++ b/support/AndroidManifest.xml
@@ -1 +1 @@
-<manifest package="net.fmtlib" />
+<manifest package="dev.fmt" />
diff --git a/support/manage.py b/support/manage.py
index 36f61d9b..cfb49797 100755
--- a/support/manage.py
+++ b/support/manage.py
@@ -12,7 +12,7 @@ obtained from https://github.com/settings/tokens.
from __future__ import print_function
import datetime, docopt, errno, fileinput, json, os
-import re, requests, shutil, sys, tempfile
+import re, requests, shutil, sys
from contextlib import contextmanager
from distutils.version import LooseVersion
from subprocess import check_call
@@ -229,12 +229,50 @@ def release(args):
if not fmt_repo.update('-b', branch, fmt_repo_url):
clean_checkout(fmt_repo, branch)
- # Convert changelog from RST to GitHub-flavored Markdown and get the
- # version.
+ # Update the date in the changelog and extract the version and the first
+ # section content.
changelog = 'ChangeLog.md'
changelog_path = os.path.join(fmt_repo.dir, changelog)
- import rst2md
- changes, version = rst2md.convert(changelog_path)
+ is_first_section = True
+ first_section = []
+ for i, line in enumerate(fileinput.input(changelog_path, inplace=True)):
+ if i == 0:
+ version = re.match(r'# (.*) - TBD', line).group(1)
+ line = '# {} - {}\n'.format(
+ version, datetime.date.today().isoformat())
+ elif not is_first_section:
+ pass
+ elif line.startswith('#'):
+ is_first_section = False
+ else:
+ first_section.append(line)
+ sys.stdout.write(line)
+ if first_section[0] == '\n':
+ first_section.pop(0)
+
+ changes = ''
+ code_block = False
+ stripped = False
+ for line in first_section:
+ if re.match(r'^\s*```', line):
+ code_block = not code_block
+ changes += line
+ stripped = False
+ continue
+ if code_block:
+ changes += line
+ continue
+ if line == '\n':
+ changes += line
+ if stripped:
+ changes += line
+ stripped = False
+ continue
+ if stripped:
+ line = ' ' + line.lstrip()
+ changes += line.rstrip()
+ stripped = True
+
cmakelists = 'CMakeLists.txt'
for line in fileinput.input(os.path.join(fmt_repo.dir, cmakelists),
inplace=True):
@@ -243,23 +281,11 @@ def release(args):
line = prefix + version + ')\n'
sys.stdout.write(line)
- # Update the version in the changelog.
- title_len = 0
- for line in fileinput.input(changelog_path, inplace=True):
- if line.startswith(version + ' - TBD'):
- line = version + ' - ' + datetime.date.today().isoformat()
- title_len = len(line)
- line += '\n'
- elif title_len:
- line = '-' * title_len + '\n'
- title_len = 0
- sys.stdout.write(line)
-
# Add the version to the build script.
script = os.path.join('doc', 'build.py')
script_path = os.path.join(fmt_repo.dir, script)
for line in fileinput.input(script_path, inplace=True):
- m = re.match(r'( *versions = )\[(.+)\]', line)
+ m = re.match(r'( *versions \+= )\[(.+)\]', line)
if m:
line = '{}[{}, \'{}\']\n'.format(m.group(1), m.group(2), version)
sys.stdout.write(line)
diff --git a/test/chrono-test.cc b/test/chrono-test.cc
index 07760688..b2d03f97 100644
--- a/test/chrono-test.cc
+++ b/test/chrono-test.cc
@@ -17,6 +17,9 @@
using fmt::runtime;
using testing::Contains;
+template <typename Duration>
+using sys_time = std::chrono::time_point<std::chrono::system_clock, Duration>;
+
#if defined(__MINGW32__) && !defined(_UCRT)
// Only C89 conversion specifiers when using MSVCRT instead of UCRT
# define FMT_HAS_C99_STRFTIME 0
@@ -24,6 +27,12 @@ using testing::Contains;
# define FMT_HAS_C99_STRFTIME 1
#endif
+#if defined(__cpp_lib_chrono) && __cpp_lib_chrono >= 201907L
+using days = std::chrono::days;
+#else
+using days = std::chrono::duration<std::chrono::hours::rep, std::ratio<86400>>;
+#endif
+
auto make_tm() -> std::tm {
auto time = std::tm();
time.tm_mday = 1;
@@ -260,9 +269,8 @@ TEST(chrono_test, system_clock_time_point) {
EXPECT_EQ(strftime_full_utc(t1), fmt::format("{:%Y-%m-%d %H:%M:%S}", t1));
EXPECT_EQ(strftime_full_utc(t1), fmt::format("{}", t1));
EXPECT_EQ(strftime_full_utc(t1), fmt::format("{:}", t1));
- using time_point =
- std::chrono::time_point<std::chrono::system_clock, std::chrono::seconds>;
- auto t2 = time_point(std::chrono::seconds(42));
+
+ auto t2 = sys_time<std::chrono::seconds>(std::chrono::seconds(42));
EXPECT_EQ(strftime_full_utc(t2), fmt::format("{:%Y-%m-%d %H:%M:%S}", t2));
std::vector<std::string> spec_list = {
@@ -331,14 +339,14 @@ TEST(chrono_test, system_clock_time_point) {
auto t = std::chrono::system_clock::to_time_t(t1);
auto tm = *std::gmtime(&t);
- EXPECT_EQ("+0000", fmt::format("{:%z}", t1));
- EXPECT_EQ("+0000", fmt::format("{:%z}", tm));
+ EXPECT_EQ(fmt::format("{:%z}", t1), "+0000");
+ EXPECT_EQ(fmt::format("{:%z}", tm), "+0000");
- EXPECT_EQ("+00:00", fmt::format("{:%Ez}", t1));
- EXPECT_EQ("+00:00", fmt::format("{:%Ez}", tm));
+ EXPECT_EQ(fmt::format("{:%Ez}", t1), "+00:00");
+ EXPECT_EQ(fmt::format("{:%Ez}", tm), "+00:00");
- EXPECT_EQ("+00:00", fmt::format("{:%Oz}", t1));
- EXPECT_EQ("+00:00", fmt::format("{:%Oz}", tm));
+ EXPECT_EQ(fmt::format("{:%Oz}", t1), "+00:00");
+ EXPECT_EQ(fmt::format("{:%Oz}", tm), "+00:00");
}
}
@@ -423,122 +431,122 @@ TEST(chrono_test, local_system_clock_time_point) {
#ifndef FMT_STATIC_THOUSANDS_SEPARATOR
TEST(chrono_test, format_default) {
- EXPECT_EQ("42s", fmt::format("{}", std::chrono::seconds(42)));
- EXPECT_EQ("42as",
- fmt::format("{}", std::chrono::duration<int, std::atto>(42)));
- EXPECT_EQ("42fs",
- fmt::format("{}", std::chrono::duration<int, std::femto>(42)));
- EXPECT_EQ("42ps",
- fmt::format("{}", std::chrono::duration<int, std::pico>(42)));
- EXPECT_EQ("42ns", fmt::format("{}", std::chrono::nanoseconds(42)));
- EXPECT_EQ("42µs", fmt::format("{}", std::chrono::microseconds(42)));
- EXPECT_EQ("42ms", fmt::format("{}", std::chrono::milliseconds(42)));
- EXPECT_EQ("42cs",
- fmt::format("{}", std::chrono::duration<int, std::centi>(42)));
- EXPECT_EQ("42ds",
- fmt::format("{}", std::chrono::duration<int, std::deci>(42)));
- EXPECT_EQ("42s", fmt::format("{}", std::chrono::seconds(42)));
- EXPECT_EQ("42das",
- fmt::format("{}", std::chrono::duration<int, std::deca>(42)));
- EXPECT_EQ("42hs",
- fmt::format("{}", std::chrono::duration<int, std::hecto>(42)));
- EXPECT_EQ("42ks",
- fmt::format("{}", std::chrono::duration<int, std::kilo>(42)));
- EXPECT_EQ("42Ms",
- fmt::format("{}", std::chrono::duration<int, std::mega>(42)));
- EXPECT_EQ("42Gs",
- fmt::format("{}", std::chrono::duration<int, std::giga>(42)));
- EXPECT_EQ("42Ts",
- fmt::format("{}", std::chrono::duration<int, std::tera>(42)));
- EXPECT_EQ("42Ps",
- fmt::format("{}", std::chrono::duration<int, std::peta>(42)));
- EXPECT_EQ("42Es",
- fmt::format("{}", std::chrono::duration<int, std::exa>(42)));
- EXPECT_EQ("42min", fmt::format("{}", std::chrono::minutes(42)));
- EXPECT_EQ("42h", fmt::format("{}", std::chrono::hours(42)));
-# if defined(__cpp_lib_chrono) && __cpp_lib_chrono >= 201907L
- EXPECT_EQ("42d", fmt::format("{}", std::chrono::days(42)));
-# endif
+ EXPECT_EQ(fmt::format("{}", std::chrono::seconds(42)), "42s");
+ EXPECT_EQ(fmt::format("{}", std::chrono::duration<int, std::atto>(42)),
+ "42as");
+ EXPECT_EQ(fmt::format("{}", std::chrono::duration<int, std::femto>(42)),
+ "42fs");
+ EXPECT_EQ(fmt::format("{}", std::chrono::duration<int, std::pico>(42)),
+ "42ps");
+ EXPECT_EQ(fmt::format("{}", std::chrono::nanoseconds(42)), "42ns");
+ EXPECT_EQ(fmt::format("{}", std::chrono::microseconds(42)), "42µs");
+ EXPECT_EQ(fmt::format("{}", std::chrono::milliseconds(42)), "42ms");
+ EXPECT_EQ(fmt::format("{}", std::chrono::duration<int, std::centi>(42)),
+ "42cs");
+ EXPECT_EQ(fmt::format("{}", std::chrono::duration<int, std::deci>(42)),
+ "42ds");
+ EXPECT_EQ(fmt::format("{}", std::chrono::seconds(42)), "42s");
+ EXPECT_EQ(fmt::format("{}", std::chrono::duration<int, std::deca>(42)),
+ "42das");
+ EXPECT_EQ(fmt::format("{}", std::chrono::duration<int, std::hecto>(42)),
+ "42hs");
+ EXPECT_EQ(fmt::format("{}", std::chrono::duration<int, std::kilo>(42)),
+ "42ks");
+ EXPECT_EQ(fmt::format("{}", std::chrono::duration<int, std::mega>(42)),
+ "42Ms");
+ EXPECT_EQ(fmt::format("{}", std::chrono::duration<int, std::giga>(42)),
+ "42Gs");
+ EXPECT_EQ(fmt::format("{}", std::chrono::duration<int, std::tera>(42)),
+ "42Ts");
+ EXPECT_EQ(fmt::format("{}", std::chrono::duration<int, std::peta>(42)),
+ "42Ps");
+ EXPECT_EQ(fmt::format("{}", std::chrono::duration<int, std::exa>(42)),
+ "42Es");
+ EXPECT_EQ(fmt::format("{}", std::chrono::minutes(42)), "42min");
+ EXPECT_EQ(fmt::format("{}", std::chrono::hours(42)), "42h");
+ EXPECT_EQ(fmt::format("{}", days(42)), "42d");
EXPECT_EQ(
- "42[15]s",
- fmt::format("{}", std::chrono::duration<int, std::ratio<15, 1>>(42)));
+ fmt::format("{}", std::chrono::duration<int, std::ratio<15, 1>>(42)),
+ "42[15]s");
EXPECT_EQ(
- "42[15/4]s",
- fmt::format("{}", std::chrono::duration<int, std::ratio<15, 4>>(42)));
+ fmt::format("{}", std::chrono::duration<int, std::ratio<15, 4>>(42)),
+ "42[15/4]s");
}
TEST(chrono_test, duration_align) {
auto s = std::chrono::seconds(42);
- EXPECT_EQ("42s ", fmt::format("{:5}", s));
- EXPECT_EQ("42s ", fmt::format("{:{}}", s, 5));
- EXPECT_EQ(" 42s", fmt::format("{:>5}", s));
- EXPECT_EQ("**42s**", fmt::format("{:*^7}", s));
- EXPECT_EQ("03:25:45 ",
- fmt::format("{:12%H:%M:%S}", std::chrono::seconds(12345)));
- EXPECT_EQ(" 03:25:45",
- fmt::format("{:>12%H:%M:%S}", std::chrono::seconds(12345)));
- EXPECT_EQ("~~03:25:45~~",
- fmt::format("{:~^12%H:%M:%S}", std::chrono::seconds(12345)));
- EXPECT_EQ("03:25:45 ",
- fmt::format("{:{}%H:%M:%S}", std::chrono::seconds(12345), 12));
+ EXPECT_EQ(fmt::format("{:5}", s), "42s ");
+ EXPECT_EQ(fmt::format("{:{}}", s, 5), "42s ");
+ EXPECT_EQ(fmt::format("{:>5}", s), " 42s");
+ EXPECT_EQ(fmt::format("{:*^7}", s), "**42s**");
+ EXPECT_EQ(fmt::format("{:12%H:%M:%S}", std::chrono::seconds(12345)),
+ "03:25:45 ");
+ EXPECT_EQ(fmt::format("{:>12%H:%M:%S}", std::chrono::seconds(12345)),
+ " 03:25:45");
+ EXPECT_EQ(fmt::format("{:~^12%H:%M:%S}", std::chrono::seconds(12345)),
+ "~~03:25:45~~");
+ EXPECT_EQ(fmt::format("{:{}%H:%M:%S}", std::chrono::seconds(12345), 12),
+ "03:25:45 ");
}
TEST(chrono_test, tm_align) {
auto t = make_tm(1975, 12, 29, 12, 14, 16);
- EXPECT_EQ("1975-12-29 12:14:16", fmt::format("{:%F %T}", t));
- EXPECT_EQ("1975-12-29 12:14:16 ", fmt::format("{:30%F %T}", t));
- EXPECT_EQ("1975-12-29 12:14:16 ", fmt::format("{:{}%F %T}", t, 30));
- EXPECT_EQ("1975-12-29 12:14:16 ", fmt::format("{:<30%F %T}", t));
- EXPECT_EQ(" 1975-12-29 12:14:16 ", fmt::format("{:^30%F %T}", t));
- EXPECT_EQ(" 1975-12-29 12:14:16", fmt::format("{:>30%F %T}", t));
+ EXPECT_EQ(fmt::format("{:%F %T}", t), "1975-12-29 12:14:16");
+ EXPECT_EQ(fmt::format("{:30%F %T}", t), "1975-12-29 12:14:16 ");
+ EXPECT_EQ(fmt::format("{:{}%F %T}", t, 30), "1975-12-29 12:14:16 ");
+ EXPECT_EQ(fmt::format("{:<30%F %T}", t), "1975-12-29 12:14:16 ");
+ EXPECT_EQ(fmt::format("{:^30%F %T}", t), " 1975-12-29 12:14:16 ");
+ EXPECT_EQ(fmt::format("{:>30%F %T}", t), " 1975-12-29 12:14:16");
- EXPECT_EQ("1975-12-29 12:14:16***********", fmt::format("{:*<30%F %T}", t));
- EXPECT_EQ("*****1975-12-29 12:14:16******", fmt::format("{:*^30%F %T}", t));
- EXPECT_EQ("***********1975-12-29 12:14:16", fmt::format("{:*>30%F %T}", t));
+ EXPECT_EQ(fmt::format("{:*<30%F %T}", t), "1975-12-29 12:14:16***********");
+ EXPECT_EQ(fmt::format("{:*^30%F %T}", t), "*****1975-12-29 12:14:16******");
+ EXPECT_EQ(fmt::format("{:*>30%F %T}", t), "***********1975-12-29 12:14:16");
}
TEST(chrono_test, tp_align) {
auto tp = std::chrono::time_point_cast<std::chrono::microseconds>(
std::chrono::system_clock::from_time_t(0));
- EXPECT_EQ("00:00.000000", fmt::format("{:%M:%S}", tp));
- EXPECT_EQ("00:00.000000 ", fmt::format("{:15%M:%S}", tp));
- EXPECT_EQ("00:00.000000 ", fmt::format("{:{}%M:%S}", tp, 15));
- EXPECT_EQ("00:00.000000 ", fmt::format("{:<15%M:%S}", tp));
- EXPECT_EQ(" 00:00.000000 ", fmt::format("{:^15%M:%S}", tp));
- EXPECT_EQ(" 00:00.000000", fmt::format("{:>15%M:%S}", tp));
+ EXPECT_EQ(fmt::format("{:%M:%S}", tp), "00:00.000000");
+ EXPECT_EQ(fmt::format("{:15%M:%S}", tp), "00:00.000000 ");
+ EXPECT_EQ(fmt::format("{:{}%M:%S}", tp, 15), "00:00.000000 ");
+ EXPECT_EQ(fmt::format("{:<15%M:%S}", tp), "00:00.000000 ");
+ EXPECT_EQ(fmt::format("{:^15%M:%S}", tp), " 00:00.000000 ");
+ EXPECT_EQ(fmt::format("{:>15%M:%S}", tp), " 00:00.000000");
- EXPECT_EQ("00:00.000000***", fmt::format("{:*<15%M:%S}", tp));
- EXPECT_EQ("*00:00.000000**", fmt::format("{:*^15%M:%S}", tp));
- EXPECT_EQ("***00:00.000000", fmt::format("{:*>15%M:%S}", tp));
+ EXPECT_EQ(fmt::format("{:*<15%M:%S}", tp), "00:00.000000***");
+ EXPECT_EQ(fmt::format("{:*^15%M:%S}", tp), "*00:00.000000**");
+ EXPECT_EQ(fmt::format("{:*>15%M:%S}", tp), "***00:00.000000");
}
TEST(chrono_test, format_specs) {
- EXPECT_EQ("%", fmt::format("{:%%}", std::chrono::seconds(0)));
- EXPECT_EQ("\n", fmt::format("{:%n}", std::chrono::seconds(0)));
- EXPECT_EQ("\t", fmt::format("{:%t}", std::chrono::seconds(0)));
- EXPECT_EQ("00", fmt::format("{:%S}", std::chrono::seconds(0)));
- EXPECT_EQ("00", fmt::format("{:%S}", std::chrono::seconds(60)));
- EXPECT_EQ("42", fmt::format("{:%S}", std::chrono::seconds(42)));
- EXPECT_EQ("01.234", fmt::format("{:%S}", std::chrono::milliseconds(1234)));
- EXPECT_EQ("00", fmt::format("{:%M}", std::chrono::minutes(0)));
- EXPECT_EQ("00", fmt::format("{:%M}", std::chrono::minutes(60)));
- EXPECT_EQ("42", fmt::format("{:%M}", std::chrono::minutes(42)));
- EXPECT_EQ("01", fmt::format("{:%M}", std::chrono::seconds(61)));
- EXPECT_EQ("00", fmt::format("{:%H}", std::chrono::hours(0)));
- EXPECT_EQ("00", fmt::format("{:%H}", std::chrono::hours(24)));
- EXPECT_EQ("14", fmt::format("{:%H}", std::chrono::hours(14)));
- EXPECT_EQ("01", fmt::format("{:%H}", std::chrono::minutes(61)));
- EXPECT_EQ("12", fmt::format("{:%I}", std::chrono::hours(0)));
- EXPECT_EQ("12", fmt::format("{:%I}", std::chrono::hours(12)));
- EXPECT_EQ("12", fmt::format("{:%I}", std::chrono::hours(24)));
- EXPECT_EQ("04", fmt::format("{:%I}", std::chrono::hours(4)));
- EXPECT_EQ("02", fmt::format("{:%I}", std::chrono::hours(14)));
- EXPECT_EQ("03:25:45",
- fmt::format("{:%H:%M:%S}", std::chrono::seconds(12345)));
- EXPECT_EQ("03:25", fmt::format("{:%R}", std::chrono::seconds(12345)));
- EXPECT_EQ("03:25:45", fmt::format("{:%T}", std::chrono::seconds(12345)));
- EXPECT_EQ("12345", fmt::format("{:%Q}", std::chrono::seconds(12345)));
- EXPECT_EQ("s", fmt::format("{:%q}", std::chrono::seconds(12345)));
+ EXPECT_EQ(fmt::format("{:%%}", std::chrono::seconds(0)), "%");
+ EXPECT_EQ(fmt::format("{:%n}", std::chrono::seconds(0)), "\n");
+ EXPECT_EQ(fmt::format("{:%t}", std::chrono::seconds(0)), "\t");
+ EXPECT_EQ(fmt::format("{:%S}", std::chrono::seconds(0)), "00");
+ EXPECT_EQ(fmt::format("{:%S}", std::chrono::seconds(60)), "00");
+ EXPECT_EQ(fmt::format("{:%S}", std::chrono::seconds(42)), "42");
+ EXPECT_EQ(fmt::format("{:%S}", std::chrono::milliseconds(1234)), "01.234");
+ EXPECT_EQ(fmt::format("{:%M}", std::chrono::minutes(0)), "00");
+ EXPECT_EQ(fmt::format("{:%M}", std::chrono::minutes(60)), "00");
+ EXPECT_EQ(fmt::format("{:%M}", std::chrono::minutes(42)), "42");
+ EXPECT_EQ(fmt::format("{:%M}", std::chrono::seconds(61)), "01");
+ EXPECT_EQ(fmt::format("{:%H}", std::chrono::hours(0)), "00");
+ EXPECT_EQ(fmt::format("{:%H}", std::chrono::hours(24)), "00");
+ EXPECT_EQ(fmt::format("{:%H}", std::chrono::hours(14)), "14");
+ EXPECT_EQ(fmt::format("{:%H}", std::chrono::minutes(61)), "01");
+ EXPECT_EQ(fmt::format("{:%I}", std::chrono::hours(0)), "12");
+ EXPECT_EQ(fmt::format("{:%I}", std::chrono::hours(12)), "12");
+ EXPECT_EQ(fmt::format("{:%I}", std::chrono::hours(24)), "12");
+ EXPECT_EQ(fmt::format("{:%I}", std::chrono::hours(4)), "04");
+ EXPECT_EQ(fmt::format("{:%I}", std::chrono::hours(14)), "02");
+ EXPECT_EQ(fmt::format("{:%j}", days(12345)), "12345");
+ EXPECT_EQ(fmt::format("{:%j}", std::chrono::hours(12345 * 24 + 12)), "12345");
+ EXPECT_EQ(fmt::format("{:%H:%M:%S}", std::chrono::seconds(12345)),
+ "03:25:45");
+ EXPECT_EQ(fmt::format("{:%R}", std::chrono::seconds(12345)), "03:25");
+ EXPECT_EQ(fmt::format("{:%T}", std::chrono::seconds(12345)), "03:25:45");
+ EXPECT_EQ(fmt::format("{:%Q}", std::chrono::seconds(12345)), "12345");
+ EXPECT_EQ(fmt::format("{:%q}", std::chrono::seconds(12345)), "s");
}
TEST(chrono_test, invalid_specs) {
@@ -619,78 +627,77 @@ TEST(chrono_test, locale) {
using dms = std::chrono::duration<double, std::milli>;
TEST(chrono_test, format_default_fp) {
- typedef std::chrono::duration<float> fs;
- EXPECT_EQ("1.234s", fmt::format("{}", fs(1.234)));
- typedef std::chrono::duration<float, std::milli> fms;
- EXPECT_EQ("1.234ms", fmt::format("{}", fms(1.234)));
- typedef std::chrono::duration<double> ds;
- EXPECT_EQ("1.234s", fmt::format("{}", ds(1.234)));
- EXPECT_EQ("1.234ms", fmt::format("{}", dms(1.234)));
+ EXPECT_EQ(fmt::format("{}", std::chrono::duration<float>(1.234)), "1.234s");
+ EXPECT_EQ(fmt::format("{}", std::chrono::duration<float, std::milli>(1.234)),
+ "1.234ms");
+ EXPECT_EQ(fmt::format("{}", std::chrono::duration<double>(1.234)), "1.234s");
+ EXPECT_EQ(fmt::format("{}", dms(1.234)), "1.234ms");
}
TEST(chrono_test, format_precision) {
EXPECT_THROW_MSG(
(void)fmt::format(runtime("{:.2%Q}"), std::chrono::seconds(42)),
fmt::format_error, "precision not allowed for this argument type");
- EXPECT_EQ("1ms", fmt::format("{:.0}", dms(1.234)));
- EXPECT_EQ("1.2ms", fmt::format("{:.1}", dms(1.234)));
- EXPECT_EQ("1.23ms", fmt::format("{:.{}}", dms(1.234), 2));
+ EXPECT_EQ(fmt::format("{:.0}", dms(1.234)), "1ms");
+ EXPECT_EQ(fmt::format("{:.1}", dms(1.234)), "1.2ms");
+ EXPECT_EQ(fmt::format("{:.{}}", dms(1.234), 2), "1.23ms");
- EXPECT_EQ("13ms", fmt::format("{:.0}", dms(12.56)));
- EXPECT_EQ("12.6ms", fmt::format("{:.1}", dms(12.56)));
- EXPECT_EQ("12.56ms", fmt::format("{:.2}", dms(12.56)));
+ EXPECT_EQ(fmt::format("{:.0}", dms(12.56)), "13ms");
+ EXPECT_EQ(fmt::format("{:.1}", dms(12.56)), "12.6ms");
+ EXPECT_EQ(fmt::format("{:.2}", dms(12.56)), "12.56ms");
}
TEST(chrono_test, format_full_specs) {
- EXPECT_EQ("1ms ", fmt::format("{:6.0}", dms(1.234)));
- EXPECT_EQ("1.2ms ", fmt::format("{:6.1}", dms(1.234)));
- EXPECT_EQ(" 1.23ms", fmt::format("{:>8.{}}", dms(1.234), 2));
- EXPECT_EQ(" 1.2ms ", fmt::format("{:^{}.{}}", dms(1.234), 7, 1));
- EXPECT_EQ(" 1.23ms ", fmt::format("{0:^{2}.{1}}", dms(1.234), 2, 8));
- EXPECT_EQ("=1.234ms=", fmt::format("{:=^{}.{}}", dms(1.234), 9, 3));
- EXPECT_EQ("*1.2340ms*", fmt::format("{:*^10.4}", dms(1.234)));
-
- EXPECT_EQ("13ms ", fmt::format("{:6.0}", dms(12.56)));
- EXPECT_EQ(" 13ms", fmt::format("{:>8.{}}", dms(12.56), 0));
- EXPECT_EQ(" 13ms ", fmt::format("{:^{}.{}}", dms(12.56), 6, 0));
- EXPECT_EQ(" 13ms ", fmt::format("{0:^{2}.{1}}", dms(12.56), 0, 8));
- EXPECT_EQ("==13ms===", fmt::format("{:=^{}.{}}", dms(12.56), 9, 0));
- EXPECT_EQ("***13ms***", fmt::format("{:*^10.0}", dms(12.56)));
+ EXPECT_EQ(fmt::format("{:6.0}", dms(1.234)), "1ms ");
+ EXPECT_EQ(fmt::format("{:6.1}", dms(1.234)), "1.2ms ");
+ EXPECT_EQ(fmt::format("{:>8.{}}", dms(1.234), 2), " 1.23ms");
+ EXPECT_EQ(fmt::format("{:^{}.{}}", dms(1.234), 7, 1), " 1.2ms ");
+ EXPECT_EQ(fmt::format("{0:^{2}.{1}}", dms(1.234), 2, 8), " 1.23ms ");
+ EXPECT_EQ(fmt::format("{:=^{}.{}}", dms(1.234), 9, 3), "=1.234ms=");
+ EXPECT_EQ(fmt::format("{:*^10.4}", dms(1.234)), "*1.2340ms*");
+
+ EXPECT_EQ(fmt::format("{:6.0}", dms(12.56)), "13ms ");
+ EXPECT_EQ(fmt::format("{:>8.{}}", dms(12.56), 0), " 13ms");
+ EXPECT_EQ(fmt::format("{:^{}.{}}", dms(12.56), 6, 0), " 13ms ");
+ EXPECT_EQ(fmt::format("{0:^{2}.{1}}", dms(12.56), 0, 8), " 13ms ");
+ EXPECT_EQ(fmt::format("{:=^{}.{}}", dms(12.56), 9, 0), "==13ms===");
+ EXPECT_EQ(fmt::format("{:*^10.0}", dms(12.56)), "***13ms***");
}
TEST(chrono_test, format_simple_q) {
- typedef std::chrono::duration<float> fs;
- EXPECT_EQ("1.234 s", fmt::format("{:%Q %q}", fs(1.234)));
- typedef std::chrono::duration<float, std::milli> fms;
- EXPECT_EQ("1.234 ms", fmt::format("{:%Q %q}", fms(1.234)));
- typedef std::chrono::duration<double> ds;
- EXPECT_EQ("1.234 s", fmt::format("{:%Q %q}", ds(1.234)));
- EXPECT_EQ("1.234 ms", fmt::format("{:%Q %q}", dms(1.234)));
+ EXPECT_EQ(fmt::format("{:%Q %q}", std::chrono::duration<float>(1.234)),
+ "1.234 s");
+ EXPECT_EQ(
+ fmt::format("{:%Q %q}", std::chrono::duration<float, std::milli>(1.234)),
+ "1.234 ms");
+ EXPECT_EQ(fmt::format("{:%Q %q}", std::chrono::duration<double>(1.234)),
+ "1.234 s");
+ EXPECT_EQ(fmt::format("{:%Q %q}", dms(1.234)), "1.234 ms");
}
TEST(chrono_test, format_precision_q) {
EXPECT_THROW_MSG(
(void)fmt::format(runtime("{:.2%Q %q}"), std::chrono::seconds(42)),
fmt::format_error, "precision not allowed for this argument type");
- EXPECT_EQ("1.2 ms", fmt::format("{:.1%Q %q}", dms(1.234)));
- EXPECT_EQ("1.23 ms", fmt::format("{:.{}%Q %q}", dms(1.234), 2));
+ EXPECT_EQ(fmt::format("{:.1%Q %q}", dms(1.234)), "1.2 ms");
+ EXPECT_EQ(fmt::format("{:.{}%Q %q}", dms(1.234), 2), "1.23 ms");
}
TEST(chrono_test, format_full_specs_q) {
- EXPECT_EQ("1 ms ", fmt::format("{:7.0%Q %q}", dms(1.234)));
- EXPECT_EQ("1.2 ms ", fmt::format("{:7.1%Q %q}", dms(1.234)));
- EXPECT_EQ(" 1.23 ms", fmt::format("{:>8.{}%Q %q}", dms(1.234), 2));
- EXPECT_EQ(" 1.2 ms ", fmt::format("{:^{}.{}%Q %q}", dms(1.234), 8, 1));
- EXPECT_EQ(" 1.23 ms ", fmt::format("{0:^{2}.{1}%Q %q}", dms(1.234), 2, 9));
- EXPECT_EQ("=1.234 ms=", fmt::format("{:=^{}.{}%Q %q}", dms(1.234), 10, 3));
- EXPECT_EQ("*1.2340 ms*", fmt::format("{:*^11.4%Q %q}", dms(1.234)));
-
- EXPECT_EQ("13 ms ", fmt::format("{:7.0%Q %q}", dms(12.56)));
- EXPECT_EQ(" 13 ms", fmt::format("{:>8.{}%Q %q}", dms(12.56), 0));
- EXPECT_EQ(" 13 ms ", fmt::format("{:^{}.{}%Q %q}", dms(12.56), 8, 0));
- EXPECT_EQ(" 13 ms ", fmt::format("{0:^{2}.{1}%Q %q}", dms(12.56), 0, 9));
- EXPECT_EQ("==13 ms==", fmt::format("{:=^{}.{}%Q %q}", dms(12.56), 9, 0));
- EXPECT_EQ("***13 ms***", fmt::format("{:*^11.0%Q %q}", dms(12.56)));
+ EXPECT_EQ(fmt::format("{:7.0%Q %q}", dms(1.234)), "1 ms ");
+ EXPECT_EQ(fmt::format("{:7.1%Q %q}", dms(1.234)), "1.2 ms ");
+ EXPECT_EQ(fmt::format("{:>8.{}%Q %q}", dms(1.234), 2), " 1.23 ms");
+ EXPECT_EQ(fmt::format("{:^{}.{}%Q %q}", dms(1.234), 8, 1), " 1.2 ms ");
+ EXPECT_EQ(fmt::format("{0:^{2}.{1}%Q %q}", dms(1.234), 2, 9), " 1.23 ms ");
+ EXPECT_EQ(fmt::format("{:=^{}.{}%Q %q}", dms(1.234), 10, 3), "=1.234 ms=");
+ EXPECT_EQ(fmt::format("{:*^11.4%Q %q}", dms(1.234)), "*1.2340 ms*");
+
+ EXPECT_EQ(fmt::format("{:7.0%Q %q}", dms(12.56)), "13 ms ");
+ EXPECT_EQ(fmt::format("{:>8.{}%Q %q}", dms(12.56), 0), " 13 ms");
+ EXPECT_EQ(fmt::format("{:^{}.{}%Q %q}", dms(12.56), 8, 0), " 13 ms ");
+ EXPECT_EQ(fmt::format("{0:^{2}.{1}%Q %q}", dms(12.56), 0, 9), " 13 ms ");
+ EXPECT_EQ(fmt::format("{:=^{}.{}%Q %q}", dms(12.56), 9, 0), "==13 ms==");
+ EXPECT_EQ(fmt::format("{:*^11.0%Q %q}", dms(12.56)), "***13 ms***");
}
TEST(chrono_test, invalid_width_id) {
@@ -704,27 +711,26 @@ TEST(chrono_test, invalid_colons) {
}
TEST(chrono_test, negative_durations) {
- EXPECT_EQ("-12345", fmt::format("{:%Q}", std::chrono::seconds(-12345)));
- EXPECT_EQ("-03:25:45",
- fmt::format("{:%H:%M:%S}", std::chrono::seconds(-12345)));
- EXPECT_EQ("-00:01",
- fmt::format("{:%M:%S}", std::chrono::duration<double>(-1)));
- EXPECT_EQ("s", fmt::format("{:%q}", std::chrono::seconds(-12345)));
- EXPECT_EQ("-00.127",
- fmt::format("{:%S}",
- std::chrono::duration<signed char, std::milli>{-127}));
+ EXPECT_EQ(fmt::format("{:%Q}", std::chrono::seconds(-12345)), "-12345");
+ EXPECT_EQ(fmt::format("{:%H:%M:%S}", std::chrono::seconds(-12345)),
+ "-03:25:45");
+ EXPECT_EQ(fmt::format("{:%M:%S}", std::chrono::duration<double>(-1)),
+ "-00:01");
+ EXPECT_EQ(fmt::format("{:%q}", std::chrono::seconds(-12345)), "s");
+ EXPECT_EQ(fmt::format("{:%S}",
+ std::chrono::duration<signed char, std::milli>(-127)),
+ "-00.127");
auto min = std::numeric_limits<int>::min();
EXPECT_EQ(fmt::format("{}", min),
fmt::format("{:%Q}", std::chrono::duration<int>(min)));
}
TEST(chrono_test, special_durations) {
- auto value = fmt::format("{:%S}", std::chrono::duration<double>(1e20));
- EXPECT_EQ(value, "40");
+ EXPECT_EQ(fmt::format("{:%S}", std::chrono::duration<double>(1e20)), "40");
auto nan = std::numeric_limits<double>::quiet_NaN();
EXPECT_EQ(
- "nan nan nan nan nan:nan nan",
- fmt::format("{:%I %H %M %S %R %r}", std::chrono::duration<double>(nan)));
+ fmt::format("{:%I %H %M %S %R %r}", std::chrono::duration<double>(nan)),
+ "nan nan nan nan nan:nan nan");
EXPECT_EQ(fmt::format("{}", std::chrono::duration<float, std::exa>(1)),
"1Es");
EXPECT_EQ(fmt::format("{}", std::chrono::duration<float, std::atto>(1)),
@@ -733,13 +739,13 @@ TEST(chrono_test, special_durations) {
"03:33");
EXPECT_EQ(fmt::format("{:%T}", std::chrono::duration<char, std::mega>{2}),
"03:33:20");
- EXPECT_EQ("01.234",
- fmt::format("{:.3%S}", std::chrono::duration<float, std::pico>(
- 1.234e12)));
+ EXPECT_EQ(
+ fmt::format("{:.3%S}", std::chrono::duration<float, std::pico>(1.234e12)),
+ "01.234");
}
TEST(chrono_test, unsigned_duration) {
- EXPECT_EQ("42s", fmt::format("{}", std::chrono::duration<unsigned>(42)));
+ EXPECT_EQ(fmt::format("{}", std::chrono::duration<unsigned>(42)), "42s");
}
TEST(chrono_test, weekday) {
@@ -852,103 +858,86 @@ TEST(chrono_test, utc_clock) {
}
#endif
-TEST(chrono_test, timestamps_ratios) {
- std::chrono::time_point<std::chrono::system_clock, std::chrono::milliseconds>
- t1(std::chrono::milliseconds(67890));
-
+TEST(chrono_test, timestamp_ratios) {
+ auto t1 =
+ sys_time<std::chrono::milliseconds>(std::chrono::milliseconds(67890));
EXPECT_EQ(fmt::format("{:%M:%S}", t1), "01:07.890");
- std::chrono::time_point<std::chrono::system_clock, std::chrono::minutes>
- t2(std::chrono::minutes(7));
-
+ auto t2 = sys_time<std::chrono::minutes>(std::chrono::minutes(7));
EXPECT_EQ(fmt::format("{:%M:%S}", t2), "07:00");
- std::chrono::time_point<std::chrono::system_clock,
- std::chrono::duration<int, std::ratio<9>>>
- t3(std::chrono::duration<int, std::ratio<9>>(7));
-
+ auto t3 = sys_time<std::chrono::duration<int, std::ratio<9>>>(
+ std::chrono::duration<int, std::ratio<9>>(7));
EXPECT_EQ(fmt::format("{:%M:%S}", t3), "01:03");
- std::chrono::time_point<std::chrono::system_clock,
- std::chrono::duration<int, std::ratio<63>>>
- t4(std::chrono::duration<int, std::ratio<63>>(1));
-
+ auto t4 = sys_time<std::chrono::duration<int, std::ratio<63>>>(
+ std::chrono::duration<int, std::ratio<63>>(1));
EXPECT_EQ(fmt::format("{:%M:%S}", t4), "01:03");
-}
-TEST(chrono_test, timestamps_sub_seconds) {
- std::chrono::time_point<std::chrono::system_clock,
- std::chrono::duration<long long, std::ratio<1, 3>>>
- t1(std::chrono::duration<long long, std::ratio<1, 3>>(4));
+ if (sizeof(time_t) > 4) {
+ auto tp =
+ sys_time<std::chrono::milliseconds>(std::chrono::seconds(32503680000));
+ EXPECT_EQ(fmt::format("{:%Y-%m-%d}", tp), "3000-01-01");
+ }
+
+ if (FMT_SAFE_DURATION_CAST) {
+ using years = std::chrono::duration<std::int64_t, std::ratio<31556952>>;
+ auto tp = sys_time<years>(years(std::numeric_limits<std::int64_t>::max()));
+ EXPECT_THROW_MSG((void)fmt::format("{:%Y-%m-%d}", tp), fmt::format_error,
+ "cannot format duration");
+ }
+}
+TEST(chrono_test, timestamp_sub_seconds) {
+ auto t1 = sys_time<std::chrono::duration<long long, std::ratio<1, 3>>>(
+ std::chrono::duration<long long, std::ratio<1, 3>>(4));
EXPECT_EQ(fmt::format("{:%S}", t1), "01.333333");
- std::chrono::time_point<std::chrono::system_clock,
- std::chrono::duration<double, std::ratio<1, 3>>>
- t2(std::chrono::duration<double, std::ratio<1, 3>>(4));
-
+ auto t2 = sys_time<std::chrono::duration<double, std::ratio<1, 3>>>(
+ std::chrono::duration<double, std::ratio<1, 3>>(4));
EXPECT_EQ(fmt::format("{:%S}", t2), "01.333333");
- const std::chrono::time_point<std::chrono::system_clock, std::chrono::seconds>
- t3(std::chrono::seconds(2));
-
+ auto t3 = sys_time<std::chrono::seconds>(std::chrono::seconds(2));
EXPECT_EQ(fmt::format("{:%S}", t3), "02");
- const std::chrono::time_point<std::chrono::system_clock,
- std::chrono::duration<double>>
- t4(std::chrono::duration<double, std::ratio<1, 1>>(9.5));
-
+ auto t4 = sys_time<std::chrono::duration<double>>(
+ std::chrono::duration<double, std::ratio<1, 1>>(9.5));
EXPECT_EQ(fmt::format("{:%S}", t4), "09.500000");
- const std::chrono::time_point<std::chrono::system_clock,
- std::chrono::duration<double>>
- t5(std::chrono::duration<double, std::ratio<1, 1>>(9));
-
+ auto t5 = sys_time<std::chrono::duration<double>>(
+ std::chrono::duration<double, std::ratio<1, 1>>(9));
EXPECT_EQ(fmt::format("{:%S}", t5), "09");
- const std::chrono::time_point<std::chrono::system_clock,
- std::chrono::milliseconds>
- t6(std::chrono::seconds(1) + std::chrono::milliseconds(120));
-
+ auto t6 = sys_time<std::chrono::milliseconds>(std::chrono::seconds(1) +
+ std::chrono::milliseconds(120));
EXPECT_EQ(fmt::format("{:%S}", t6), "01.120");
- const std::chrono::time_point<std::chrono::system_clock,
- std::chrono::microseconds>
- t7(std::chrono::microseconds(1234567));
-
+ auto t7 =
+ sys_time<std::chrono::microseconds>(std::chrono::microseconds(1234567));
EXPECT_EQ(fmt::format("{:%S}", t7), "01.234567");
- const std::chrono::time_point<std::chrono::system_clock,
- std::chrono::nanoseconds>
- t8(std::chrono::nanoseconds(123456789));
-
+ auto t8 =
+ sys_time<std::chrono::nanoseconds>(std::chrono::nanoseconds(123456789));
EXPECT_EQ(fmt::format("{:%S}", t8), "00.123456789");
- const auto t9 = std::chrono::time_point_cast<std::chrono::nanoseconds>(
+ auto t9 = std::chrono::time_point_cast<std::chrono::nanoseconds>(
std::chrono::system_clock::now());
- const auto t9_sec = std::chrono::time_point_cast<std::chrono::seconds>(t9);
+ auto t9_sec = std::chrono::time_point_cast<std::chrono::seconds>(t9);
auto t9_sub_sec_part = fmt::format("{0:09}", (t9 - t9_sec).count());
-
EXPECT_EQ(fmt::format("{}.{}", strftime_full_utc(t9_sec), t9_sub_sec_part),
fmt::format("{:%Y-%m-%d %H:%M:%S}", t9));
EXPECT_EQ(fmt::format("{}.{}", strftime_full_utc(t9_sec), t9_sub_sec_part),
fmt::format("{:%Y-%m-%d %T}", t9));
- const std::chrono::time_point<std::chrono::system_clock,
- std::chrono::milliseconds>
- t10(std::chrono::milliseconds(2000));
-
+ auto t10 =
+ sys_time<std::chrono::milliseconds>(std::chrono::milliseconds(2000));
EXPECT_EQ(fmt::format("{:%S}", t10), "02.000");
- {
- const auto epoch = std::chrono::time_point<std::chrono::system_clock,
- std::chrono::milliseconds>();
- const auto d = std::chrono::milliseconds(250);
-
- EXPECT_EQ("59.750", fmt::format("{:%S}", epoch - d));
- EXPECT_EQ("00.000", fmt::format("{:%S}", epoch));
- EXPECT_EQ("00.250", fmt::format("{:%S}", epoch + d));
- }
+ auto epoch = sys_time<std::chrono::milliseconds>();
+ auto d = std::chrono::milliseconds(250);
+ EXPECT_EQ(fmt::format("{:%S}", epoch - d), "59.750");
+ EXPECT_EQ(fmt::format("{:%S}", epoch), "00.000");
+ EXPECT_EQ(fmt::format("{:%S}", epoch + d), "00.250");
}
TEST(chrono_test, glibc_extensions) {
@@ -1003,3 +992,8 @@ TEST(chrono_test, glibc_extensions) {
EXPECT_EQ(fmt::format("{:%-S}", d), "3.140000");
}
}
+
+TEST(chrono_test, out_of_range) {
+ auto d = std::chrono::duration<unsigned long, std::giga>(538976288);
+ EXPECT_THROW((void)fmt::format("{:%j}", d), fmt::format_error);
+} \ No newline at end of file
diff --git a/test/compile-test.cc b/test/compile-test.cc
index d6c7c643..8551303e 100644
--- a/test/compile-test.cc
+++ b/test/compile-test.cc
@@ -280,15 +280,18 @@ TEST(compile_test, compile_format_string_literal) {
#endif
// MSVS 2019 19.29.30145.0 - Support C++20 and OK.
-// MSVS 2022 19.32.31332.0 - compile-test.cc(362,3): fatal error C1001: Internal
-// compiler error.
+// MSVS 2022 19.32.31332.0, 19.37.32826.1 - compile-test.cc(362,3): fatal error
+// C1001: Internal compiler error.
// (compiler file
// 'D:\a\_work\1\s\src\vctools\Compiler\CxxFE\sl\p1\c\constexpr\constexpr.cpp',
// line 8635)
-#if ((FMT_CPLUSPLUS >= 202002L) && \
- (!defined(_GLIBCXX_RELEASE) || _GLIBCXX_RELEASE > 9) && \
- (!FMT_MSC_VERSION || FMT_MSC_VERSION < 1930)) || \
- (FMT_CPLUSPLUS >= 201709L && FMT_GCC_VERSION >= 1002)
+#if (FMT_CPLUSPLUS >= 202002L || \
+ (FMT_CPLUSPLUS >= 201709L && FMT_GCC_VERSION >= 1002)) && \
+ ((!defined(_GLIBCXX_RELEASE) || _GLIBCXX_RELEASE >= 10) && \
+ (!defined(_LIBCPP_VERSION) || _LIBCPP_VERSION >= 10000) && \
+ (!FMT_MSC_VERSION || \
+ (FMT_MSC_VERSION >= 1928 && FMT_MSC_VERSION < 1930))) && \
+ defined(__cpp_lib_is_constant_evaluated)
template <size_t max_string_length, typename Char = char> struct test_string {
template <typename T> constexpr bool operator==(const T& rhs) const noexcept {
return fmt::basic_string_view<Char>(rhs).compare(buffer) == 0;
diff --git a/test/format-impl-test.cc b/test/format-impl-test.cc
index 4d6198b6..eda1f239 100644
--- a/test/format-impl-test.cc
+++ b/test/format-impl-test.cc
@@ -246,13 +246,6 @@ TEST(format_impl_test, format_error_code) {
}
}
-TEST(format_impl_test, compute_width) {
- EXPECT_EQ(4,
- fmt::detail::compute_width(
- fmt::basic_string_view<fmt::detail::char8_type>(
- reinterpret_cast<const fmt::detail::char8_type*>("ёжик"))));
-}
-
// Tests fmt::detail::count_digits for integer type Int.
template <typename Int> void test_count_digits() {
for (Int i = 0; i < 10; ++i) EXPECT_EQ(1u, fmt::detail::count_digits(i));
diff --git a/test/format-test.cc b/test/format-test.cc
index 9788c246..325bef99 100644
--- a/test/format-test.cc
+++ b/test/format-test.cc
@@ -173,6 +173,10 @@ TEST(util_test, parse_nonnegative_int) {
EXPECT_EQ(fmt::detail::parse_nonnegative_int(begin, end, -1), -1);
}
+TEST(format_impl_test, compute_width) {
+ EXPECT_EQ(fmt::detail::compute_width("вожык"), 5);
+}
+
TEST(util_test, utf8_to_utf16) {
auto u = fmt::detail::utf8_to_utf16("лошадка");
EXPECT_EQ(L"\x043B\x043E\x0448\x0430\x0434\x043A\x0430", u.str());
@@ -349,9 +353,9 @@ TEST(memory_buffer_test, move_assignment) {
}
TEST(memory_buffer_test, grow) {
- typedef allocator_ref<mock_allocator<int>> Allocator;
+ using allocator = allocator_ref<mock_allocator<int>>;
mock_allocator<int> alloc;
- basic_memory_buffer<int, 10, Allocator> buffer((Allocator(&alloc)));
+ basic_memory_buffer<int, 10, allocator> buffer((allocator(&alloc)));
buffer.resize(7);
using fmt::detail::to_unsigned;
for (int i = 0; i < 7; ++i) buffer[to_unsigned(i)] = i * i;
@@ -448,18 +452,18 @@ TEST(format_test, exception_from_lib) {
}
TEST(format_test, escape) {
- EXPECT_EQ("{", fmt::format("{{"));
- EXPECT_EQ("before {", fmt::format("before {{"));
- EXPECT_EQ("{ after", fmt::format("{{ after"));
- EXPECT_EQ("before { after", fmt::format("before {{ after"));
+ EXPECT_EQ(fmt::format("{{"), "{");
+ EXPECT_EQ(fmt::format("before {{"), "before {");
+ EXPECT_EQ(fmt::format("{{ after"), "{ after");
+ EXPECT_EQ(fmt::format("before {{ after"), "before { after");
- EXPECT_EQ("}", fmt::format("}}"));
- EXPECT_EQ("before }", fmt::format("before }}"));
- EXPECT_EQ("} after", fmt::format("}} after"));
- EXPECT_EQ("before } after", fmt::format("before }} after"));
+ EXPECT_EQ(fmt::format("}}"), "}");
+ EXPECT_EQ(fmt::format("before }}"), "before }");
+ EXPECT_EQ(fmt::format("}} after"), "} after");
+ EXPECT_EQ(fmt::format("before }} after"), "before } after");
- EXPECT_EQ("{}", fmt::format("{{}}"));
- EXPECT_EQ("{42}", fmt::format("{{{0}}}", 42));
+ EXPECT_EQ(fmt::format("{{}}"), "{}");
+ EXPECT_EQ(fmt::format("{{{0}}}", 42), "{42}");
}
TEST(format_test, unmatched_braces) {
@@ -471,16 +475,16 @@ TEST(format_test, unmatched_braces) {
"invalid format string");
}
-TEST(format_test, no_args) { EXPECT_EQ("test", fmt::format("test")); }
+TEST(format_test, no_args) { EXPECT_EQ(fmt::format("test"), "test"); }
TEST(format_test, args_in_different_positions) {
- EXPECT_EQ("42", fmt::format("{0}", 42));
- EXPECT_EQ("before 42", fmt::format("before {0}", 42));
- EXPECT_EQ("42 after", fmt::format("{0} after", 42));
- EXPECT_EQ("before 42 after", fmt::format("before {0} after", 42));
- EXPECT_EQ("answer = 42", fmt::format("{0} = {1}", "answer", 42));
- EXPECT_EQ("42 is the answer", fmt::format("{1} is the {0}", "answer", 42));
- EXPECT_EQ("abracadabra", fmt::format("{0}{1}{0}", "abra", "cad"));
+ EXPECT_EQ(fmt::format("{0}", 42), "42");
+ EXPECT_EQ(fmt::format("before {0}", 42), "before 42");
+ EXPECT_EQ(fmt::format("{0} after", 42), "42 after");
+ EXPECT_EQ(fmt::format("before {0} after", 42), "before 42 after");
+ EXPECT_EQ(fmt::format("{0} = {1}", "answer", 42), "answer = 42");
+ EXPECT_EQ(fmt::format("{1} is the {0}", "answer", 42), "42 is the answer");
+ EXPECT_EQ(fmt::format("{0}{1}{0}", "abra", "cad"), "abracadabra");
}
TEST(format_test, arg_errors) {
@@ -495,32 +499,29 @@ TEST(format_test, arg_errors) {
EXPECT_THROW_MSG((void)fmt::format(runtime("{00}"), 42), format_error,
"invalid format string");
- char format_str[buffer_size];
- safe_sprintf(format_str, "{%u", INT_MAX);
- EXPECT_THROW_MSG((void)fmt::format(runtime(format_str)), format_error,
+ auto int_max = std::to_string(INT_MAX);
+ EXPECT_THROW_MSG((void)fmt::format(runtime("{" + int_max)), format_error,
"invalid format string");
- safe_sprintf(format_str, "{%u}", INT_MAX);
- EXPECT_THROW_MSG((void)fmt::format(runtime(format_str)), format_error,
- "argument not found");
+ EXPECT_THROW_MSG((void)fmt::format(runtime("{" + int_max + "}")),
+ format_error, "argument not found");
- safe_sprintf(format_str, "{%u", INT_MAX + 1u);
- EXPECT_THROW_MSG((void)fmt::format(runtime(format_str)), format_error,
+ auto int_maxer = std::to_string(INT_MAX + 1u);
+ EXPECT_THROW_MSG((void)fmt::format(runtime("{" + int_maxer)), format_error,
"invalid format string");
- safe_sprintf(format_str, "{%u}", INT_MAX + 1u);
- EXPECT_THROW_MSG((void)fmt::format(runtime(format_str)), format_error,
- "argument not found");
+ EXPECT_THROW_MSG((void)fmt::format(runtime("{" + int_maxer + "}")),
+ format_error, "argument not found");
}
template <int N> struct test_format {
template <typename... T>
- static std::string format(fmt::string_view fmt, const T&... args) {
+ static auto format(fmt::string_view fmt, const T&... args) -> std::string {
return test_format<N - 1>::format(fmt, N - 1, args...);
}
};
template <> struct test_format<0> {
template <typename... T>
- static std::string format(fmt::string_view fmt, const T&... args) {
+ static auto format(fmt::string_view fmt, const T&... args) -> std::string {
return fmt::format(runtime(fmt), args...);
}
};
@@ -540,10 +541,10 @@ TEST(format_test, many_args) {
TEST(format_test, named_arg) {
EXPECT_EQ("1/a/A", fmt::format("{_1}/{a_}/{A_}", fmt::arg("a_", 'a'),
fmt::arg("A_", "A"), fmt::arg("_1", 1)));
- EXPECT_EQ(" -42", fmt::format("{0:{width}}", -42, fmt::arg("width", 4)));
+ EXPECT_EQ(fmt::format("{0:{width}}", -42, fmt::arg("width", 4)), " -42");
EXPECT_EQ("st",
fmt::format("{0:.{precision}}", "str", fmt::arg("precision", 2)));
- EXPECT_EQ("1 2", fmt::format("{} {two}", 1, fmt::arg("two", 2)));
+ EXPECT_EQ(fmt::format("{} {two}", 1, fmt::arg("two", 2)), "1 2");
EXPECT_EQ("42",
fmt::format("{c}", fmt::arg("a", 0), fmt::arg("b", 0),
fmt::arg("c", 42), fmt::arg("d", 0), fmt::arg("e", 0),
@@ -558,12 +559,12 @@ TEST(format_test, named_arg) {
}
TEST(format_test, auto_arg_index) {
- EXPECT_EQ("abc", fmt::format("{}{}{}", 'a', 'b', 'c'));
+ EXPECT_EQ(fmt::format("{}{}{}", 'a', 'b', 'c'), "abc");
EXPECT_THROW_MSG((void)fmt::format(runtime("{0}{}"), 'a', 'b'), format_error,
"cannot switch from manual to automatic argument indexing");
EXPECT_THROW_MSG((void)fmt::format(runtime("{}{0}"), 'a', 'b'), format_error,
"cannot switch from automatic to manual argument indexing");
- EXPECT_EQ("1.2", fmt::format("{:.{}}", 1.2345, 2));
+ EXPECT_EQ(fmt::format("{:.{}}", 1.2345, 2), "1.2");
EXPECT_THROW_MSG((void)fmt::format(runtime("{0}:.{}"), 1.2345, 2),
format_error,
"cannot switch from manual to automatic argument indexing");
@@ -574,57 +575,57 @@ TEST(format_test, auto_arg_index) {
"argument not found");
}
-TEST(format_test, empty_specs) { EXPECT_EQ("42", fmt::format("{0:}", 42)); }
+TEST(format_test, empty_specs) { EXPECT_EQ(fmt::format("{0:}", 42), "42"); }
TEST(format_test, left_align) {
- EXPECT_EQ("42 ", fmt::format("{0:<4}", 42));
- EXPECT_EQ("42 ", fmt::format("{0:<4o}", 042));
- EXPECT_EQ("42 ", fmt::format("{0:<4x}", 0x42));
- EXPECT_EQ("-42 ", fmt::format("{0:<5}", -42));
- EXPECT_EQ("42 ", fmt::format("{0:<5}", 42u));
- EXPECT_EQ("-42 ", fmt::format("{0:<5}", -42l));
- EXPECT_EQ("42 ", fmt::format("{0:<5}", 42ul));
- EXPECT_EQ("-42 ", fmt::format("{0:<5}", -42ll));
- EXPECT_EQ("42 ", fmt::format("{0:<5}", 42ull));
- EXPECT_EQ("-42 ", fmt::format("{0:<5}", -42.0));
- EXPECT_EQ("-42 ", fmt::format("{0:<5}", -42.0l));
- EXPECT_EQ("c ", fmt::format("{0:<5}", 'c'));
- EXPECT_EQ("abc ", fmt::format("{0:<5}", "abc"));
- EXPECT_EQ("0xface ", fmt::format("{0:<8}", reinterpret_cast<void*>(0xface)));
+ EXPECT_EQ(fmt::format("{0:<4}", 42), "42 ");
+ EXPECT_EQ(fmt::format("{0:<4o}", 042), "42 ");
+ EXPECT_EQ(fmt::format("{0:<4x}", 0x42), "42 ");
+ EXPECT_EQ(fmt::format("{0:<5}", -42), "-42 ");
+ EXPECT_EQ(fmt::format("{0:<5}", 42u), "42 ");
+ EXPECT_EQ(fmt::format("{0:<5}", -42l), "-42 ");
+ EXPECT_EQ(fmt::format("{0:<5}", 42ul), "42 ");
+ EXPECT_EQ(fmt::format("{0:<5}", -42ll), "-42 ");
+ EXPECT_EQ(fmt::format("{0:<5}", 42ull), "42 ");
+ EXPECT_EQ(fmt::format("{0:<5}", -42.0), "-42 ");
+ EXPECT_EQ(fmt::format("{0:<5}", -42.0l), "-42 ");
+ EXPECT_EQ(fmt::format("{0:<5}", 'c'), "c ");
+ EXPECT_EQ(fmt::format("{0:<5}", "abc"), "abc ");
+ EXPECT_EQ(fmt::format("{0:<8}", reinterpret_cast<void*>(0xface)), "0xface ");
}
TEST(format_test, right_align) {
- EXPECT_EQ(" 42", fmt::format("{0:>4}", 42));
- EXPECT_EQ(" 42", fmt::format("{0:>4o}", 042));
- EXPECT_EQ(" 42", fmt::format("{0:>4x}", 0x42));
- EXPECT_EQ(" -42", fmt::format("{0:>5}", -42));
- EXPECT_EQ(" 42", fmt::format("{0:>5}", 42u));
- EXPECT_EQ(" -42", fmt::format("{0:>5}", -42l));
- EXPECT_EQ(" 42", fmt::format("{0:>5}", 42ul));
- EXPECT_EQ(" -42", fmt::format("{0:>5}", -42ll));
- EXPECT_EQ(" 42", fmt::format("{0:>5}", 42ull));
- EXPECT_EQ(" -42", fmt::format("{0:>5}", -42.0));
- EXPECT_EQ(" -42", fmt::format("{0:>5}", -42.0l));
- EXPECT_EQ(" c", fmt::format("{0:>5}", 'c'));
- EXPECT_EQ(" abc", fmt::format("{0:>5}", "abc"));
- EXPECT_EQ(" 0xface", fmt::format("{0:>8}", reinterpret_cast<void*>(0xface)));
+ EXPECT_EQ(fmt::format("{0:>4}", 42), " 42");
+ EXPECT_EQ(fmt::format("{0:>4o}", 042), " 42");
+ EXPECT_EQ(fmt::format("{0:>4x}", 0x42), " 42");
+ EXPECT_EQ(fmt::format("{0:>5}", -42), " -42");
+ EXPECT_EQ(fmt::format("{0:>5}", 42u), " 42");
+ EXPECT_EQ(fmt::format("{0:>5}", -42l), " -42");
+ EXPECT_EQ(fmt::format("{0:>5}", 42ul), " 42");
+ EXPECT_EQ(fmt::format("{0:>5}", -42ll), " -42");
+ EXPECT_EQ(fmt::format("{0:>5}", 42ull), " 42");
+ EXPECT_EQ(fmt::format("{0:>5}", -42.0), " -42");
+ EXPECT_EQ(fmt::format("{0:>5}", -42.0l), " -42");
+ EXPECT_EQ(fmt::format("{0:>5}", 'c'), " c");
+ EXPECT_EQ(fmt::format("{0:>5}", "abc"), " abc");
+ EXPECT_EQ(fmt::format("{0:>8}", reinterpret_cast<void*>(0xface)), " 0xface");
}
TEST(format_test, center_align) {
- EXPECT_EQ(" 42 ", fmt::format("{0:^5}", 42));
- EXPECT_EQ(" 42 ", fmt::format("{0:^5o}", 042));
- EXPECT_EQ(" 42 ", fmt::format("{0:^5x}", 0x42));
- EXPECT_EQ(" -42 ", fmt::format("{0:^5}", -42));
- EXPECT_EQ(" 42 ", fmt::format("{0:^5}", 42u));
- EXPECT_EQ(" -42 ", fmt::format("{0:^5}", -42l));
- EXPECT_EQ(" 42 ", fmt::format("{0:^5}", 42ul));
- EXPECT_EQ(" -42 ", fmt::format("{0:^5}", -42ll));
- EXPECT_EQ(" 42 ", fmt::format("{0:^5}", 42ull));
- EXPECT_EQ(" -42 ", fmt::format("{0:^5}", -42.0));
- EXPECT_EQ(" -42 ", fmt::format("{0:^5}", -42.0l));
- EXPECT_EQ(" c ", fmt::format("{0:^5}", 'c'));
- EXPECT_EQ(" abc ", fmt::format("{0:^6}", "abc"));
- EXPECT_EQ(" 0xface ", fmt::format("{0:^8}", reinterpret_cast<void*>(0xface)));
+ EXPECT_EQ(fmt::format("{0:^5}", 42), " 42 ");
+ EXPECT_EQ(fmt::format("{0:^5o}", 042), " 42 ");
+ EXPECT_EQ(fmt::format("{0:^5x}", 0x42), " 42 ");
+ EXPECT_EQ(fmt::format("{0:^5}", -42), " -42 ");
+ EXPECT_EQ(fmt::format("{0:^5}", 42u), " 42 ");
+ EXPECT_EQ(fmt::format("{0:^5}", -42l), " -42 ");
+ EXPECT_EQ(fmt::format("{0:^5}", 42ul), " 42 ");
+ EXPECT_EQ(fmt::format("{0:^5}", -42ll), " -42 ");
+ EXPECT_EQ(fmt::format("{0:^5}", 42ull), " 42 ");
+ EXPECT_EQ(fmt::format("{0:^5}", -42.0), " -42 ");
+ EXPECT_EQ(fmt::format("{0:^5}", -42.0l), " -42 ");
+ EXPECT_EQ(fmt::format("{0:^5}", 'c'), " c ");
+ EXPECT_EQ(fmt::format("{0:^6}", "abc"), " abc ");
+ EXPECT_EQ(fmt::format("{0:^8}", reinterpret_cast<void*>(0xface)), " 0xface ");
}
TEST(format_test, fill) {
@@ -632,44 +633,44 @@ TEST(format_test, fill) {
"invalid fill character '{'");
EXPECT_THROW_MSG((void)fmt::format(runtime("{0:{<5}}"), 'c'), format_error,
"invalid fill character '{'");
- EXPECT_EQ("**42", fmt::format("{0:*>4}", 42));
- EXPECT_EQ("**-42", fmt::format("{0:*>5}", -42));
- EXPECT_EQ("***42", fmt::format("{0:*>5}", 42u));
- EXPECT_EQ("**-42", fmt::format("{0:*>5}", -42l));
- EXPECT_EQ("***42", fmt::format("{0:*>5}", 42ul));
- EXPECT_EQ("**-42", fmt::format("{0:*>5}", -42ll));
- EXPECT_EQ("***42", fmt::format("{0:*>5}", 42ull));
- EXPECT_EQ("**-42", fmt::format("{0:*>5}", -42.0));
- EXPECT_EQ("**-42", fmt::format("{0:*>5}", -42.0l));
- EXPECT_EQ("c****", fmt::format("{0:*<5}", 'c'));
- EXPECT_EQ("abc**", fmt::format("{0:*<5}", "abc"));
+ EXPECT_EQ(fmt::format("{0:*>4}", 42), "**42");
+ EXPECT_EQ(fmt::format("{0:*>5}", -42), "**-42");
+ EXPECT_EQ(fmt::format("{0:*>5}", 42u), "***42");
+ EXPECT_EQ(fmt::format("{0:*>5}", -42l), "**-42");
+ EXPECT_EQ(fmt::format("{0:*>5}", 42ul), "***42");
+ EXPECT_EQ(fmt::format("{0:*>5}", -42ll), "**-42");
+ EXPECT_EQ(fmt::format("{0:*>5}", 42ull), "***42");
+ EXPECT_EQ(fmt::format("{0:*>5}", -42.0), "**-42");
+ EXPECT_EQ(fmt::format("{0:*>5}", -42.0l), "**-42");
+ EXPECT_EQ(fmt::format("{0:*<5}", 'c'), "c****");
+ EXPECT_EQ(fmt::format("{0:*<5}", "abc"), "abc**");
EXPECT_EQ("**0xface",
fmt::format("{0:*>8}", reinterpret_cast<void*>(0xface)));
- EXPECT_EQ("foo=", fmt::format("{:}=", "foo"));
+ EXPECT_EQ(fmt::format("{:}=", "foo"), "foo=");
EXPECT_EQ(std::string("\0\0\0*", 4),
fmt::format(string_view("{:\0>4}", 6), '*'));
- EXPECT_EQ("жж42", fmt::format("{0:ж>4}", 42));
+ EXPECT_EQ(fmt::format("{0:ж>4}", 42), "жж42");
EXPECT_THROW_MSG((void)fmt::format(runtime("{:\x80\x80\x80\x80\x80>}"), 0),
format_error, "invalid format specifier");
}
TEST(format_test, plus_sign) {
- EXPECT_EQ("+42", fmt::format("{0:+}", 42));
- EXPECT_EQ("-42", fmt::format("{0:+}", -42));
- EXPECT_EQ("+42", fmt::format("{0:+}", 42));
+ EXPECT_EQ(fmt::format("{0:+}", 42), "+42");
+ EXPECT_EQ(fmt::format("{0:+}", -42), "-42");
+ EXPECT_EQ(fmt::format("{0:+}", 42), "+42");
EXPECT_THROW_MSG((void)fmt::format(runtime("{0:+}"), 42u), format_error,
"invalid format specifier");
- EXPECT_EQ("+42", fmt::format("{0:+}", 42l));
+ EXPECT_EQ(fmt::format("{0:+}", 42l), "+42");
EXPECT_THROW_MSG((void)fmt::format(runtime("{0:+}"), 42ul), format_error,
"invalid format specifier");
- EXPECT_EQ("+42", fmt::format("{0:+}", 42ll));
+ EXPECT_EQ(fmt::format("{0:+}", 42ll), "+42");
#if FMT_USE_INT128
- EXPECT_EQ("+42", fmt::format("{0:+}", __int128_t(42)));
+ EXPECT_EQ(fmt::format("{0:+}", __int128_t(42)), "+42");
#endif
EXPECT_THROW_MSG((void)fmt::format(runtime("{0:+}"), 42ull), format_error,
"invalid format specifier");
- EXPECT_EQ("+42", fmt::format("{0:+}", 42.0));
- EXPECT_EQ("+42", fmt::format("{0:+}", 42.0l));
+ EXPECT_EQ(fmt::format("{0:+}", 42.0), "+42");
+ EXPECT_EQ(fmt::format("{0:+}", 42.0l), "+42");
EXPECT_THROW_MSG((void)fmt::format(runtime("{0:+}"), 'c'), format_error,
"invalid format specifier");
EXPECT_THROW_MSG((void)fmt::format(runtime("{0:+}"), "abc"), format_error,
@@ -680,19 +681,19 @@ TEST(format_test, plus_sign) {
}
TEST(format_test, minus_sign) {
- EXPECT_EQ("42", fmt::format("{0:-}", 42));
- EXPECT_EQ("-42", fmt::format("{0:-}", -42));
- EXPECT_EQ("42", fmt::format("{0:-}", 42));
+ EXPECT_EQ(fmt::format("{0:-}", 42), "42");
+ EXPECT_EQ(fmt::format("{0:-}", -42), "-42");
+ EXPECT_EQ(fmt::format("{0:-}", 42), "42");
EXPECT_THROW_MSG((void)fmt::format(runtime("{0:-}"), 42u), format_error,
"invalid format specifier");
- EXPECT_EQ("42", fmt::format("{0:-}", 42l));
+ EXPECT_EQ(fmt::format("{0:-}", 42l), "42");
EXPECT_THROW_MSG((void)fmt::format(runtime("{0:-}"), 42ul), format_error,
"invalid format specifier");
- EXPECT_EQ("42", fmt::format("{0:-}", 42ll));
+ EXPECT_EQ(fmt::format("{0:-}", 42ll), "42");
EXPECT_THROW_MSG((void)fmt::format(runtime("{0:-}"), 42ull), format_error,
"invalid format specifier");
- EXPECT_EQ("42", fmt::format("{0:-}", 42.0));
- EXPECT_EQ("42", fmt::format("{0:-}", 42.0l));
+ EXPECT_EQ(fmt::format("{0:-}", 42.0), "42");
+ EXPECT_EQ(fmt::format("{0:-}", 42.0l), "42");
EXPECT_THROW_MSG((void)fmt::format(runtime("{0:-}"), 'c'), format_error,
"invalid format specifier");
EXPECT_THROW_MSG((void)fmt::format(runtime("{0:-}"), "abc"), format_error,
@@ -703,19 +704,19 @@ TEST(format_test, minus_sign) {
}
TEST(format_test, space_sign) {
- EXPECT_EQ(" 42", fmt::format("{0: }", 42));
- EXPECT_EQ("-42", fmt::format("{0: }", -42));
- EXPECT_EQ(" 42", fmt::format("{0: }", 42));
+ EXPECT_EQ(fmt::format("{0: }", 42), " 42");
+ EXPECT_EQ(fmt::format("{0: }", -42), "-42");
+ EXPECT_EQ(fmt::format("{0: }", 42), " 42");
EXPECT_THROW_MSG((void)fmt::format(runtime("{0: }"), 42u), format_error,
"invalid format specifier");
- EXPECT_EQ(" 42", fmt::format("{0: }", 42l));
+ EXPECT_EQ(fmt::format("{0: }", 42l), " 42");
EXPECT_THROW_MSG((void)fmt::format(runtime("{0: }"), 42ul), format_error,
"invalid format specifier");
- EXPECT_EQ(" 42", fmt::format("{0: }", 42ll));
+ EXPECT_EQ(fmt::format("{0: }", 42ll), " 42");
EXPECT_THROW_MSG((void)fmt::format(runtime("{0: }"), 42ull), format_error,
"invalid format specifier");
- EXPECT_EQ(" 42", fmt::format("{0: }", 42.0));
- EXPECT_EQ(" 42", fmt::format("{0: }", 42.0l));
+ EXPECT_EQ(fmt::format("{0: }", 42.0), " 42");
+ EXPECT_EQ(fmt::format("{0: }", 42.0l), " 42");
EXPECT_THROW_MSG((void)fmt::format(runtime("{0: }"), 'c'), format_error,
"invalid format specifier");
EXPECT_THROW_MSG((void)fmt::format(runtime("{0: }"), "abc"), format_error,
@@ -726,45 +727,45 @@ TEST(format_test, space_sign) {
}
TEST(format_test, hash_flag) {
- EXPECT_EQ("42", fmt::format("{0:#}", 42));
- EXPECT_EQ("-42", fmt::format("{0:#}", -42));
- EXPECT_EQ("0b101010", fmt::format("{0:#b}", 42));
- EXPECT_EQ("0B101010", fmt::format("{0:#B}", 42));
- EXPECT_EQ("-0b101010", fmt::format("{0:#b}", -42));
- EXPECT_EQ("0x42", fmt::format("{0:#x}", 0x42));
- EXPECT_EQ("0X42", fmt::format("{0:#X}", 0x42));
- EXPECT_EQ("-0x42", fmt::format("{0:#x}", -0x42));
- EXPECT_EQ("0", fmt::format("{0:#o}", 0));
- EXPECT_EQ("042", fmt::format("{0:#o}", 042));
- EXPECT_EQ("-042", fmt::format("{0:#o}", -042));
- EXPECT_EQ("42", fmt::format("{0:#}", 42u));
- EXPECT_EQ("0x42", fmt::format("{0:#x}", 0x42u));
- EXPECT_EQ("042", fmt::format("{0:#o}", 042u));
-
- EXPECT_EQ("-42", fmt::format("{0:#}", -42l));
- EXPECT_EQ("0x42", fmt::format("{0:#x}", 0x42l));
- EXPECT_EQ("-0x42", fmt::format("{0:#x}", -0x42l));
- EXPECT_EQ("042", fmt::format("{0:#o}", 042l));
- EXPECT_EQ("-042", fmt::format("{0:#o}", -042l));
- EXPECT_EQ("42", fmt::format("{0:#}", 42ul));
- EXPECT_EQ("0x42", fmt::format("{0:#x}", 0x42ul));
- EXPECT_EQ("042", fmt::format("{0:#o}", 042ul));
-
- EXPECT_EQ("-42", fmt::format("{0:#}", -42ll));
- EXPECT_EQ("0x42", fmt::format("{0:#x}", 0x42ll));
- EXPECT_EQ("-0x42", fmt::format("{0:#x}", -0x42ll));
- EXPECT_EQ("042", fmt::format("{0:#o}", 042ll));
- EXPECT_EQ("-042", fmt::format("{0:#o}", -042ll));
- EXPECT_EQ("42", fmt::format("{0:#}", 42ull));
- EXPECT_EQ("0x42", fmt::format("{0:#x}", 0x42ull));
- EXPECT_EQ("042", fmt::format("{0:#o}", 042ull));
-
- EXPECT_EQ("-42.", fmt::format("{0:#}", -42.0));
- EXPECT_EQ("-42.", fmt::format("{0:#}", -42.0l));
- EXPECT_EQ("4.e+01", fmt::format("{:#.0e}", 42.0));
- EXPECT_EQ("0.", fmt::format("{:#.0f}", 0.01));
- EXPECT_EQ("0.50", fmt::format("{:#.2g}", 0.5));
- EXPECT_EQ("0.", fmt::format("{:#.0f}", 0.5));
+ EXPECT_EQ(fmt::format("{0:#}", 42), "42");
+ EXPECT_EQ(fmt::format("{0:#}", -42), "-42");
+ EXPECT_EQ(fmt::format("{0:#b}", 42), "0b101010");
+ EXPECT_EQ(fmt::format("{0:#B}", 42), "0B101010");
+ EXPECT_EQ(fmt::format("{0:#b}", -42), "-0b101010");
+ EXPECT_EQ(fmt::format("{0:#x}", 0x42), "0x42");
+ EXPECT_EQ(fmt::format("{0:#X}", 0x42), "0X42");
+ EXPECT_EQ(fmt::format("{0:#x}", -0x42), "-0x42");
+ EXPECT_EQ(fmt::format("{0:#o}", 0), "0");
+ EXPECT_EQ(fmt::format("{0:#o}", 042), "042");
+ EXPECT_EQ(fmt::format("{0:#o}", -042), "-042");
+ EXPECT_EQ(fmt::format("{0:#}", 42u), "42");
+ EXPECT_EQ(fmt::format("{0:#x}", 0x42u), "0x42");
+ EXPECT_EQ(fmt::format("{0:#o}", 042u), "042");
+
+ EXPECT_EQ(fmt::format("{0:#}", -42l), "-42");
+ EXPECT_EQ(fmt::format("{0:#x}", 0x42l), "0x42");
+ EXPECT_EQ(fmt::format("{0:#x}", -0x42l), "-0x42");
+ EXPECT_EQ(fmt::format("{0:#o}", 042l), "042");
+ EXPECT_EQ(fmt::format("{0:#o}", -042l), "-042");
+ EXPECT_EQ(fmt::format("{0:#}", 42ul), "42");
+ EXPECT_EQ(fmt::format("{0:#x}", 0x42ul), "0x42");
+ EXPECT_EQ(fmt::format("{0:#o}", 042ul), "042");
+
+ EXPECT_EQ(fmt::format("{0:#}", -42ll), "-42");
+ EXPECT_EQ(fmt::format("{0:#x}", 0x42ll), "0x42");
+ EXPECT_EQ(fmt::format("{0:#x}", -0x42ll), "-0x42");
+ EXPECT_EQ(fmt::format("{0:#o}", 042ll), "042");
+ EXPECT_EQ(fmt::format("{0:#o}", -042ll), "-042");
+ EXPECT_EQ(fmt::format("{0:#}", 42ull), "42");
+ EXPECT_EQ(fmt::format("{0:#x}", 0x42ull), "0x42");
+ EXPECT_EQ(fmt::format("{0:#o}", 042ull), "042");
+
+ EXPECT_EQ(fmt::format("{0:#}", -42.0), "-42.");
+ EXPECT_EQ(fmt::format("{0:#}", -42.0l), "-42.");
+ EXPECT_EQ(fmt::format("{:#.0e}", 42.0), "4.e+01");
+ EXPECT_EQ(fmt::format("{:#.0f}", 0.01), "0.");
+ EXPECT_EQ(fmt::format("{:#.2g}", 0.5), "0.50");
+ EXPECT_EQ(fmt::format("{:#.0f}", 0.5), "0.");
EXPECT_THROW_MSG((void)fmt::format(runtime("{0:#"), 'c'), format_error,
"missing '}' in format string");
EXPECT_THROW_MSG((void)fmt::format(runtime("{0:#}"), 'c'), format_error,
@@ -777,15 +778,15 @@ TEST(format_test, hash_flag) {
}
TEST(format_test, zero_flag) {
- EXPECT_EQ("42", fmt::format("{0:0}", 42));
- EXPECT_EQ("-0042", fmt::format("{0:05}", -42));
- EXPECT_EQ("00042", fmt::format("{0:05}", 42u));
- EXPECT_EQ("-0042", fmt::format("{0:05}", -42l));
- EXPECT_EQ("00042", fmt::format("{0:05}", 42ul));
- EXPECT_EQ("-0042", fmt::format("{0:05}", -42ll));
- EXPECT_EQ("00042", fmt::format("{0:05}", 42ull));
- EXPECT_EQ("-000042", fmt::format("{0:07}", -42.0));
- EXPECT_EQ("-000042", fmt::format("{0:07}", -42.0l));
+ EXPECT_EQ(fmt::format("{0:0}", 42), "42");
+ EXPECT_EQ(fmt::format("{0:05}", -42), "-0042");
+ EXPECT_EQ(fmt::format("{0:05}", 42u), "00042");
+ EXPECT_EQ(fmt::format("{0:05}", -42l), "-0042");
+ EXPECT_EQ(fmt::format("{0:05}", 42ul), "00042");
+ EXPECT_EQ(fmt::format("{0:05}", -42ll), "-0042");
+ EXPECT_EQ(fmt::format("{0:05}", 42ull), "00042");
+ EXPECT_EQ(fmt::format("{0:07}", -42.0), "-000042");
+ EXPECT_EQ(fmt::format("{0:07}", -42.0l), "-000042");
EXPECT_THROW_MSG((void)fmt::format(runtime("{0:0"), 'c'), format_error,
"missing '}' in format string");
EXPECT_THROW_MSG((void)fmt::format(runtime("{0:05}"), 'c'), format_error,
@@ -800,44 +801,33 @@ TEST(format_test, zero_flag) {
TEST(format_test, zero_flag_and_align) {
// If the 0 character and an align option both appear, the 0 character is
// ignored.
- EXPECT_EQ("42 ", fmt::format("{0:<05}", 42));
- EXPECT_EQ("-42 ", fmt::format("{0:<05}", -42));
- EXPECT_EQ(" 42 ", fmt::format("{0:^05}", 42));
- EXPECT_EQ(" -42 ", fmt::format("{0:^05}", -42));
- EXPECT_EQ(" 42", fmt::format("{0:>05}", 42));
- EXPECT_EQ(" -42", fmt::format("{0:>05}", -42));
+ EXPECT_EQ(fmt::format("{:<05}", 42), "42 ");
+ EXPECT_EQ(fmt::format("{:<05}", -42), "-42 ");
+ EXPECT_EQ(fmt::format("{:^05}", 42), " 42 ");
+ EXPECT_EQ(fmt::format("{:^05}", -42), " -42 ");
+ EXPECT_EQ(fmt::format("{:>05}", 42), " 42");
+ EXPECT_EQ(fmt::format("{:>05}", -42), " -42");
}
TEST(format_test, width) {
- char format_str[buffer_size];
- safe_sprintf(format_str, "{0:%u", UINT_MAX);
- increment(format_str + 3);
- EXPECT_THROW_MSG((void)fmt::format(runtime(format_str), 0), format_error,
- "number is too big");
- size_t size = std::strlen(format_str);
- format_str[size] = '}';
- format_str[size + 1] = 0;
- EXPECT_THROW_MSG((void)fmt::format(runtime(format_str), 0), format_error,
- "number is too big");
+ auto int_maxer = std::to_string(INT_MAX + 1u);
+ EXPECT_THROW_MSG((void)fmt::format(runtime("{:" + int_maxer), 0),
+ format_error, "number is too big");
+ EXPECT_THROW_MSG((void)fmt::format(runtime("{:" + int_maxer + "}"), 0),
+ format_error, "number is too big");
- safe_sprintf(format_str, "{0:%u", INT_MAX + 1u);
- EXPECT_THROW_MSG((void)fmt::format(runtime(format_str), 0), format_error,
- "number is too big");
- safe_sprintf(format_str, "{0:%u}", INT_MAX + 1u);
- EXPECT_THROW_MSG((void)fmt::format(runtime(format_str), 0), format_error,
- "number is too big");
- EXPECT_EQ(" -42", fmt::format("{0:4}", -42));
- EXPECT_EQ(" 42", fmt::format("{0:5}", 42u));
- EXPECT_EQ(" -42", fmt::format("{0:6}", -42l));
- EXPECT_EQ(" 42", fmt::format("{0:7}", 42ul));
- EXPECT_EQ(" -42", fmt::format("{0:6}", -42ll));
- EXPECT_EQ(" 42", fmt::format("{0:7}", 42ull));
- EXPECT_EQ(" -1.23", fmt::format("{0:8}", -1.23));
- EXPECT_EQ(" -1.23", fmt::format("{0:9}", -1.23l));
- EXPECT_EQ(" 0xcafe",
- fmt::format("{0:10}", reinterpret_cast<void*>(0xcafe)));
- EXPECT_EQ("x ", fmt::format("{0:11}", 'x'));
- EXPECT_EQ("str ", fmt::format("{0:12}", "str"));
+ EXPECT_EQ(fmt::format("{:4}", -42), " -42");
+ EXPECT_EQ(fmt::format("{:5}", 42u), " 42");
+ EXPECT_EQ(fmt::format("{:6}", -42l), " -42");
+ EXPECT_EQ(fmt::format("{:7}", 42ul), " 42");
+ EXPECT_EQ(fmt::format("{:6}", -42ll), " -42");
+ EXPECT_EQ(fmt::format("{:7}", 42ull), " 42");
+ EXPECT_EQ(fmt::format("{:8}", -1.23), " -1.23");
+ EXPECT_EQ(fmt::format("{:9}", -1.23l), " -1.23");
+ EXPECT_EQ(fmt::format("{:10}", reinterpret_cast<void*>(0xcafe)),
+ " 0xcafe");
+ EXPECT_EQ(fmt::format("{:11}", 'x'), "x ");
+ EXPECT_EQ(fmt::format("{:12}", "str"), "str ");
EXPECT_EQ(fmt::format("{:*^6}", "🤡"), "**🤡**");
EXPECT_EQ(fmt::format("{:*^8}", "你好"), "**你好**");
EXPECT_EQ(fmt::format("{:#6}", 42.0), " 42.");
@@ -846,20 +836,13 @@ TEST(format_test, width) {
}
TEST(format_test, runtime_width) {
- char format_str[buffer_size];
- safe_sprintf(format_str, "{0:{%u", UINT_MAX);
- increment(format_str + 4);
- EXPECT_THROW_MSG((void)fmt::format(runtime(format_str), 0), format_error,
- "invalid format string");
- size_t size = std::strlen(format_str);
- format_str[size] = '}';
- format_str[size + 1] = 0;
- EXPECT_THROW_MSG((void)fmt::format(runtime(format_str), 0), format_error,
- "argument not found");
- format_str[size + 1] = '}';
- format_str[size + 2] = 0;
- EXPECT_THROW_MSG((void)fmt::format(runtime(format_str), 0), format_error,
- "argument not found");
+ auto int_maxer = std::to_string(INT_MAX + 1u);
+ EXPECT_THROW_MSG((void)fmt::format(runtime("{0:{" + int_maxer), 0),
+ format_error, "invalid format string");
+ EXPECT_THROW_MSG((void)fmt::format(runtime("{0:{" + int_maxer + "}"), 0),
+ format_error, "argument not found");
+ EXPECT_THROW_MSG((void)fmt::format(runtime("{0:{" + int_maxer + "}}"), 0),
+ format_error, "argument not found");
EXPECT_THROW_MSG((void)fmt::format(runtime("{0:{"), 0), format_error,
"invalid format string");
@@ -892,18 +875,18 @@ TEST(format_test, runtime_width) {
EXPECT_THROW_MSG((void)fmt::format(runtime("{0:{1}}"), 0, 0.0), format_error,
"width is not integer");
- EXPECT_EQ(" -42", fmt::format("{0:{1}}", -42, 4));
- EXPECT_EQ(" 42", fmt::format("{0:{1}}", 42u, 5));
- EXPECT_EQ(" -42", fmt::format("{0:{1}}", -42l, 6));
- EXPECT_EQ(" 42", fmt::format("{0:{1}}", 42ul, 7));
- EXPECT_EQ(" -42", fmt::format("{0:{1}}", -42ll, 6));
- EXPECT_EQ(" 42", fmt::format("{0:{1}}", 42ull, 7));
- EXPECT_EQ(" -1.23", fmt::format("{0:{1}}", -1.23, 8));
- EXPECT_EQ(" -1.23", fmt::format("{0:{1}}", -1.23l, 9));
+ EXPECT_EQ(fmt::format("{0:{1}}", -42, 4), " -42");
+ EXPECT_EQ(fmt::format("{0:{1}}", 42u, 5), " 42");
+ EXPECT_EQ(fmt::format("{0:{1}}", -42l, 6), " -42");
+ EXPECT_EQ(fmt::format("{0:{1}}", 42ul, 7), " 42");
+ EXPECT_EQ(fmt::format("{0:{1}}", -42ll, 6), " -42");
+ EXPECT_EQ(fmt::format("{0:{1}}", 42ull, 7), " 42");
+ EXPECT_EQ(fmt::format("{0:{1}}", -1.23, 8), " -1.23");
+ EXPECT_EQ(fmt::format("{0:{1}}", -1.23l, 9), " -1.23");
EXPECT_EQ(" 0xcafe",
fmt::format("{0:{1}}", reinterpret_cast<void*>(0xcafe), 10));
- EXPECT_EQ("x ", fmt::format("{0:{1}}", 'x', 11));
- EXPECT_EQ("str ", fmt::format("{0:{1}}", "str", 12));
+ EXPECT_EQ(fmt::format("{0:{1}}", 'x', 11), "x ");
+ EXPECT_EQ(fmt::format("{0:{1}}", "str", 12), "str ");
EXPECT_EQ(fmt::format("{:{}}", 42, short(4)), " 42");
}
@@ -959,12 +942,12 @@ TEST(format_test, precision) {
"invalid format specifier");
EXPECT_THROW_MSG((void)fmt::format(runtime("{0:3.0}"), 'x'), format_error,
"invalid format specifier");
- EXPECT_EQ("1.2", fmt::format("{0:.2}", 1.2345));
- EXPECT_EQ("1.2", fmt::format("{0:.2}", 1.2345l));
- EXPECT_EQ("1.2e+56", fmt::format("{:.2}", 1.234e56));
- EXPECT_EQ("1.1", fmt::format("{0:.3}", 1.1));
- EXPECT_EQ("1e+00", fmt::format("{:.0e}", 1.0L));
- EXPECT_EQ(" 0.0e+00", fmt::format("{:9.1e}", 0.0));
+ EXPECT_EQ(fmt::format("{0:.2}", 1.2345), "1.2");
+ EXPECT_EQ(fmt::format("{0:.2}", 1.2345l), "1.2");
+ EXPECT_EQ(fmt::format("{:.2}", 1.234e56), "1.2e+56");
+ EXPECT_EQ(fmt::format("{0:.3}", 1.1), "1.1");
+ EXPECT_EQ(fmt::format("{:.0e}", 1.0L), "1e+00");
+ EXPECT_EQ(fmt::format("{:9.1e}", 0.0), " 0.0e+00");
EXPECT_EQ(
fmt::format("{:.494}", 4.9406564584124654E-324),
"4.9406564584124654417656879286822137236505980261432476442558568250067550"
@@ -1028,12 +1011,12 @@ TEST(format_test, precision) {
"3.6452e-4951");
}
- EXPECT_EQ("123.", fmt::format("{:#.0f}", 123.0));
- EXPECT_EQ("1.23", fmt::format("{:.02f}", 1.234));
- EXPECT_EQ("0.001", fmt::format("{:.1g}", 0.001));
- EXPECT_EQ("1019666400", fmt::format("{}", 1019666432.0f));
- EXPECT_EQ("1e+01", fmt::format("{:.0e}", 9.5));
- EXPECT_EQ("1.0e-34", fmt::format("{:.1e}", 1e-34));
+ EXPECT_EQ(fmt::format("{:#.0f}", 123.0), "123.");
+ EXPECT_EQ(fmt::format("{:.02f}", 1.234), "1.23");
+ EXPECT_EQ(fmt::format("{:.1g}", 0.001), "0.001");
+ EXPECT_EQ(fmt::format("{}", 1019666432.0f), "1019666400");
+ EXPECT_EQ(fmt::format("{:.0e}", 9.5), "1e+01");
+ EXPECT_EQ(fmt::format("{:.1e}", 1e-34), "1.0e-34");
EXPECT_THROW_MSG(
(void)fmt::format(runtime("{0:.2}"), reinterpret_cast<void*>(0xcafe)),
@@ -1048,8 +1031,15 @@ TEST(format_test, precision) {
(void)fmt::format("{:.2147483646f}", -2.2121295195081227E+304),
format_error, "number is too big");
- EXPECT_EQ("st", fmt::format("{0:.2}", "str"));
- EXPECT_EQ("вожык", fmt::format("{0:.5}", "вожыкі"));
+ EXPECT_EQ(fmt::format("{0:.2}", "str"), "st");
+ EXPECT_EQ(fmt::format("{0:.5}", "вожыкі"), "вожык");
+ EXPECT_EQ(fmt::format("{0:.6}", "123456\xad"), "123456");
+}
+
+TEST(xchar_test, utf8_precision) {
+ auto result = fmt::format("{:.4}", "caf\u00e9s"); // cafés
+ EXPECT_EQ(fmt::detail::compute_width(result), 4);
+ EXPECT_EQ(result, "caf\u00e9");
}
TEST(format_test, runtime_precision) {
@@ -1127,8 +1117,8 @@ TEST(format_test, runtime_precision) {
format_error, "invalid format specifier");
EXPECT_THROW_MSG((void)fmt::format(runtime("{0:3.{1}}"), 'x', 0),
format_error, "invalid format specifier");
- EXPECT_EQ("1.2", fmt::format("{0:.{1}}", 1.2345, 2));
- EXPECT_EQ("1.2", fmt::format("{1:.{0}}", 2, 1.2345l));
+ EXPECT_EQ(fmt::format("{0:.{1}}", 1.2345, 2), "1.2");
+ EXPECT_EQ(fmt::format("{1:.{0}}", 2, 1.2345l), "1.2");
EXPECT_THROW_MSG((void)fmt::format(runtime("{0:.{1}}"),
reinterpret_cast<void*>(0xcafe), 2),
@@ -1137,24 +1127,26 @@ TEST(format_test, runtime_precision) {
reinterpret_cast<void*>(0xcafe), 2),
format_error, "invalid format specifier");
- EXPECT_EQ("st", fmt::format("{0:.{1}}", "str", 2));
+ EXPECT_EQ(fmt::format("{0:.{1}}", "str", 2), "st");
}
TEST(format_test, format_bool) {
- EXPECT_EQ("true", fmt::format("{}", true));
- EXPECT_EQ("false", fmt::format("{}", false));
- EXPECT_EQ("1", fmt::format("{:d}", true));
- EXPECT_EQ("true ", fmt::format("{:5}", true));
- EXPECT_EQ("true", fmt::format("{:s}", true));
- EXPECT_EQ("false", fmt::format("{:s}", false));
- EXPECT_EQ("false ", fmt::format("{:6s}", false));
+ EXPECT_EQ(fmt::format("{}", true), "true");
+ EXPECT_EQ(fmt::format("{}", false), "false");
+ EXPECT_EQ(fmt::format("{:d}", true), "1");
+ EXPECT_EQ(fmt::format("{:5}", true), "true ");
+ EXPECT_EQ(fmt::format("{:s}", true), "true");
+ EXPECT_EQ(fmt::format("{:s}", false), "false");
+ EXPECT_EQ(fmt::format("{:6s}", false), "false ");
+ EXPECT_THROW_MSG((void)fmt::format(runtime("{:c}"), false), format_error,
+ "invalid format specifier");
}
TEST(format_test, format_short) {
short s = 42;
- EXPECT_EQ("42", fmt::format("{0:d}", s));
+ EXPECT_EQ(fmt::format("{0:d}", s), "42");
unsigned short us = 42;
- EXPECT_EQ("42", fmt::format("{0:d}", us));
+ EXPECT_EQ(fmt::format("{0:d}", us), "42");
}
template <typename T>
@@ -1176,16 +1168,16 @@ TEST(format_test, format_int) {
EXPECT_THROW_MSG((void)fmt::format(runtime("{0:v"), 42), format_error,
"invalid format specifier");
check_unknown_types(42, "bBdoxXnLc", "integer");
- EXPECT_EQ("x", fmt::format("{:c}", static_cast<int>('x')));
+ EXPECT_EQ(fmt::format("{:c}", static_cast<int>('x')), "x");
}
TEST(format_test, format_bin) {
- EXPECT_EQ("0", fmt::format("{0:b}", 0));
- EXPECT_EQ("101010", fmt::format("{0:b}", 42));
- EXPECT_EQ("101010", fmt::format("{0:b}", 42u));
- EXPECT_EQ("-101010", fmt::format("{0:b}", -42));
- EXPECT_EQ("11000000111001", fmt::format("{0:b}", 12345));
- EXPECT_EQ("10010001101000101011001111000", fmt::format("{0:b}", 0x12345678));
+ EXPECT_EQ(fmt::format("{0:b}", 0), "0");
+ EXPECT_EQ(fmt::format("{0:b}", 42), "101010");
+ EXPECT_EQ(fmt::format("{0:b}", 42u), "101010");
+ EXPECT_EQ(fmt::format("{0:b}", -42), "-101010");
+ EXPECT_EQ(fmt::format("{0:b}", 12345), "11000000111001");
+ EXPECT_EQ(fmt::format("{0:b}", 0x12345678), "10010001101000101011001111000");
EXPECT_EQ("10010000101010111100110111101111",
fmt::format("{0:b}", 0x90ABCDEF));
EXPECT_EQ("11111111111111111111111111111111",
@@ -1201,17 +1193,17 @@ constexpr auto uint128_max = ~static_cast<__uint128_t>(0);
#endif
TEST(format_test, format_dec) {
- EXPECT_EQ("0", fmt::format("{0}", 0));
- EXPECT_EQ("42", fmt::format("{0}", 42));
- EXPECT_EQ("42>", fmt::format("{:}>", 42));
- EXPECT_EQ("42", fmt::format("{0:d}", 42));
- EXPECT_EQ("42", fmt::format("{0}", 42u));
- EXPECT_EQ("-42", fmt::format("{0}", -42));
- EXPECT_EQ("12345", fmt::format("{0}", 12345));
- EXPECT_EQ("67890", fmt::format("{0}", 67890));
+ EXPECT_EQ(fmt::format("{0}", 0), "0");
+ EXPECT_EQ(fmt::format("{0}", 42), "42");
+ EXPECT_EQ(fmt::format("{:}>", 42), "42>");
+ EXPECT_EQ(fmt::format("{0:d}", 42), "42");
+ EXPECT_EQ(fmt::format("{0}", 42u), "42");
+ EXPECT_EQ(fmt::format("{0}", -42), "-42");
+ EXPECT_EQ(fmt::format("{0}", 12345), "12345");
+ EXPECT_EQ(fmt::format("{0}", 67890), "67890");
#if FMT_USE_INT128
- EXPECT_EQ("0", fmt::format("{0}", static_cast<__int128_t>(0)));
- EXPECT_EQ("0", fmt::format("{0}", static_cast<__uint128_t>(0)));
+ EXPECT_EQ(fmt::format("{0}", static_cast<__int128_t>(0)), "0");
+ EXPECT_EQ(fmt::format("{0}", static_cast<__uint128_t>(0)), "0");
EXPECT_EQ("9223372036854775808",
fmt::format("{0}", static_cast<__int128_t>(INT64_MAX) + 1));
EXPECT_EQ("-9223372036854775809",
@@ -1242,17 +1234,17 @@ TEST(format_test, format_dec) {
}
TEST(format_test, format_hex) {
- EXPECT_EQ("0", fmt::format("{0:x}", 0));
- EXPECT_EQ("42", fmt::format("{0:x}", 0x42));
- EXPECT_EQ("42", fmt::format("{0:x}", 0x42u));
- EXPECT_EQ("-42", fmt::format("{0:x}", -0x42));
- EXPECT_EQ("12345678", fmt::format("{0:x}", 0x12345678));
- EXPECT_EQ("90abcdef", fmt::format("{0:x}", 0x90abcdef));
- EXPECT_EQ("12345678", fmt::format("{0:X}", 0x12345678));
- EXPECT_EQ("90ABCDEF", fmt::format("{0:X}", 0x90ABCDEF));
+ EXPECT_EQ(fmt::format("{0:x}", 0), "0");
+ EXPECT_EQ(fmt::format("{0:x}", 0x42), "42");
+ EXPECT_EQ(fmt::format("{0:x}", 0x42u), "42");
+ EXPECT_EQ(fmt::format("{0:x}", -0x42), "-42");
+ EXPECT_EQ(fmt::format("{0:x}", 0x12345678), "12345678");
+ EXPECT_EQ(fmt::format("{0:x}", 0x90abcdef), "90abcdef");
+ EXPECT_EQ(fmt::format("{0:X}", 0x12345678), "12345678");
+ EXPECT_EQ(fmt::format("{0:X}", 0x90ABCDEF), "90ABCDEF");
#if FMT_USE_INT128
- EXPECT_EQ("0", fmt::format("{0:x}", static_cast<__int128_t>(0)));
- EXPECT_EQ("0", fmt::format("{0:x}", static_cast<__uint128_t>(0)));
+ EXPECT_EQ(fmt::format("{0:x}", static_cast<__int128_t>(0)), "0");
+ EXPECT_EQ(fmt::format("{0:x}", static_cast<__uint128_t>(0)), "0");
EXPECT_EQ("8000000000000000",
fmt::format("{0:x}", static_cast<__int128_t>(INT64_MAX) + 1));
EXPECT_EQ("-8000000000000001",
@@ -1283,14 +1275,14 @@ TEST(format_test, format_hex) {
}
TEST(format_test, format_oct) {
- EXPECT_EQ("0", fmt::format("{0:o}", 0));
- EXPECT_EQ("42", fmt::format("{0:o}", 042));
- EXPECT_EQ("42", fmt::format("{0:o}", 042u));
- EXPECT_EQ("-42", fmt::format("{0:o}", -042));
- EXPECT_EQ("12345670", fmt::format("{0:o}", 012345670));
+ EXPECT_EQ(fmt::format("{0:o}", 0), "0");
+ EXPECT_EQ(fmt::format("{0:o}", 042), "42");
+ EXPECT_EQ(fmt::format("{0:o}", 042u), "42");
+ EXPECT_EQ(fmt::format("{0:o}", -042), "-42");
+ EXPECT_EQ(fmt::format("{0:o}", 012345670), "12345670");
#if FMT_USE_INT128
- EXPECT_EQ("0", fmt::format("{0:o}", static_cast<__int128_t>(0)));
- EXPECT_EQ("0", fmt::format("{0:o}", static_cast<__uint128_t>(0)));
+ EXPECT_EQ(fmt::format("{0:o}", static_cast<__int128_t>(0)), "0");
+ EXPECT_EQ(fmt::format("{0:o}", static_cast<__uint128_t>(0)), "0");
EXPECT_EQ("1000000000000000000000",
fmt::format("{0:o}", static_cast<__int128_t>(INT64_MAX) + 1));
EXPECT_EQ("-1000000000000000000001",
@@ -1321,12 +1313,12 @@ TEST(format_test, format_oct) {
}
TEST(format_test, format_int_locale) {
- EXPECT_EQ("1234", fmt::format("{:L}", 1234));
+ EXPECT_EQ(fmt::format("{:L}", 1234), "1234");
}
TEST(format_test, format_float) {
- EXPECT_EQ("0", fmt::format("{}", 0.0f));
- EXPECT_EQ("392.500000", fmt::format("{0:f}", 392.5f));
+ EXPECT_EQ(fmt::format("{}", 0.0f), "0");
+ EXPECT_EQ(fmt::format("{0:f}", 392.5f), "392.500000");
}
TEST(format_test, format_double) {
@@ -1348,14 +1340,11 @@ TEST(format_test, format_double) {
EXPECT_EQ(fmt::format("{0:e}", 392.65), "3.926500e+02");
EXPECT_EQ(fmt::format("{0:E}", 392.65), "3.926500E+02");
EXPECT_EQ(fmt::format("{0:+010.4g}", 392.65), "+0000392.6");
- char buffer[buffer_size];
#if FMT_CPLUSPLUS >= 201703L
double xd = 0x1.ffffffffffp+2;
- safe_sprintf(buffer, "%.*a", 10, xd);
- EXPECT_EQ(fmt::format("{:.10a}", xd), buffer);
- safe_sprintf(buffer, "%.*a", 9, xd);
- EXPECT_EQ(fmt::format("{:.9a}", xd), buffer);
+ EXPECT_EQ(fmt::format("{:.10a}", xd), "0x1.ffffffffffp+2");
+ EXPECT_EQ(fmt::format("{:.9a}", xd), "0x2.000000000p+2");
if (std::numeric_limits<long double>::digits == 64) {
auto ld = 0xf.ffffffffffp-3l;
@@ -1371,8 +1360,7 @@ TEST(format_test, format_double) {
EXPECT_EQ(fmt::format("{:#a}", d), "0x1.p-1022");
d = (std::numeric_limits<double>::max)();
- safe_sprintf(buffer, "%a", d);
- EXPECT_EQ(fmt::format("{:a}", d), buffer);
+ EXPECT_EQ(fmt::format("{:a}", d), "0x1.fffffffffffffp+1023");
d = std::numeric_limits<double>::denorm_min();
EXPECT_EQ(fmt::format("{:a}", d), "0x0.0000000000001p-1022");
@@ -1389,8 +1377,7 @@ TEST(format_test, format_double) {
EXPECT_EQ(fmt::format("{:a}", ld), "0x0.000000000000001p-16382");
}
- safe_sprintf(buffer, "%.*a", 10, 4.2);
- EXPECT_EQ(fmt::format("{:.10a}", 4.2), buffer);
+ EXPECT_EQ(fmt::format("{:.10a}", 4.2), "0x1.0ccccccccdp+2");
EXPECT_EQ(fmt::format("{:a}", -42.0), "-0x1.5p+5");
EXPECT_EQ(fmt::format("{:A}", -42.0), "-0X1.5P+5");
@@ -1400,92 +1387,92 @@ TEST(format_test, format_double) {
}
TEST(format_test, precision_rounding) {
- EXPECT_EQ("0", fmt::format("{:.0f}", 0.0));
- EXPECT_EQ("0", fmt::format("{:.0f}", 0.01));
- EXPECT_EQ("0", fmt::format("{:.0f}", 0.1));
- EXPECT_EQ("0.000", fmt::format("{:.3f}", 0.00049));
- EXPECT_EQ("0.001", fmt::format("{:.3f}", 0.0005));
- EXPECT_EQ("0.001", fmt::format("{:.3f}", 0.00149));
- EXPECT_EQ("0.002", fmt::format("{:.3f}", 0.0015));
- EXPECT_EQ("1.000", fmt::format("{:.3f}", 0.9999));
- EXPECT_EQ("0.00123", fmt::format("{:.3}", 0.00123));
- EXPECT_EQ("0.1", fmt::format("{:.16g}", 0.1));
- EXPECT_EQ("1", fmt::format("{:.0}", 1.0));
+ EXPECT_EQ(fmt::format("{:.0f}", 0.0), "0");
+ EXPECT_EQ(fmt::format("{:.0f}", 0.01), "0");
+ EXPECT_EQ(fmt::format("{:.0f}", 0.1), "0");
+ EXPECT_EQ(fmt::format("{:.3f}", 0.00049), "0.000");
+ EXPECT_EQ(fmt::format("{:.3f}", 0.0005), "0.001");
+ EXPECT_EQ(fmt::format("{:.3f}", 0.00149), "0.001");
+ EXPECT_EQ(fmt::format("{:.3f}", 0.0015), "0.002");
+ EXPECT_EQ(fmt::format("{:.3f}", 0.9999), "1.000");
+ EXPECT_EQ(fmt::format("{:.3}", 0.00123), "0.00123");
+ EXPECT_EQ(fmt::format("{:.16g}", 0.1), "0.1");
+ EXPECT_EQ(fmt::format("{:.0}", 1.0), "1");
EXPECT_EQ("225.51575035152063720",
fmt::format("{:.17f}", 225.51575035152064));
- EXPECT_EQ("-761519619559038.2", fmt::format("{:.1f}", -761519619559038.2));
+ EXPECT_EQ(fmt::format("{:.1f}", -761519619559038.2), "-761519619559038.2");
EXPECT_EQ("1.9156918820264798e-56",
fmt::format("{}", 1.9156918820264798e-56));
- EXPECT_EQ("0.0000", fmt::format("{:.4f}", 7.2809479766055470e-15));
+ EXPECT_EQ(fmt::format("{:.4f}", 7.2809479766055470e-15), "0.0000");
}
TEST(format_test, prettify_float) {
- EXPECT_EQ("0.0001", fmt::format("{}", 1e-4));
- EXPECT_EQ("1e-05", fmt::format("{}", 1e-5));
- EXPECT_EQ("1000000000000000", fmt::format("{}", 1e15));
- EXPECT_EQ("1e+16", fmt::format("{}", 1e16));
- EXPECT_EQ("9.999e-05", fmt::format("{}", 9.999e-5));
- EXPECT_EQ("10000000000", fmt::format("{}", 1e10));
- EXPECT_EQ("100000000000", fmt::format("{}", 1e11));
- EXPECT_EQ("12340000000", fmt::format("{}", 1234e7));
- EXPECT_EQ("12.34", fmt::format("{}", 1234e-2));
- EXPECT_EQ("0.001234", fmt::format("{}", 1234e-6));
- EXPECT_EQ("0.1", fmt::format("{}", 0.1f));
- EXPECT_EQ("1.3563156e-19", fmt::format("{}", 1.35631564e-19f));
+ EXPECT_EQ(fmt::format("{}", 1e-4), "0.0001");
+ EXPECT_EQ(fmt::format("{}", 1e-5), "1e-05");
+ EXPECT_EQ(fmt::format("{}", 1e15), "1000000000000000");
+ EXPECT_EQ(fmt::format("{}", 1e16), "1e+16");
+ EXPECT_EQ(fmt::format("{}", 9.999e-5), "9.999e-05");
+ EXPECT_EQ(fmt::format("{}", 1e10), "10000000000");
+ EXPECT_EQ(fmt::format("{}", 1e11), "100000000000");
+ EXPECT_EQ(fmt::format("{}", 1234e7), "12340000000");
+ EXPECT_EQ(fmt::format("{}", 1234e-2), "12.34");
+ EXPECT_EQ(fmt::format("{}", 1234e-6), "0.001234");
+ EXPECT_EQ(fmt::format("{}", 0.1f), "0.1");
+ EXPECT_EQ(fmt::format("{}", 1.35631564e-19f), "1.3563156e-19");
}
TEST(format_test, format_nan) {
double nan = std::numeric_limits<double>::quiet_NaN();
- EXPECT_EQ("nan", fmt::format("{}", nan));
- EXPECT_EQ("+nan", fmt::format("{:+}", nan));
- EXPECT_EQ(" +nan", fmt::format("{:+06}", nan));
- EXPECT_EQ("+nan ", fmt::format("{:<+06}", nan));
- EXPECT_EQ(" +nan ", fmt::format("{:^+06}", nan));
- EXPECT_EQ(" +nan", fmt::format("{:>+06}", nan));
+ EXPECT_EQ(fmt::format("{}", nan), "nan");
+ EXPECT_EQ(fmt::format("{:+}", nan), "+nan");
+ EXPECT_EQ(fmt::format("{:+06}", nan), " +nan");
+ EXPECT_EQ(fmt::format("{:<+06}", nan), "+nan ");
+ EXPECT_EQ(fmt::format("{:^+06}", nan), " +nan ");
+ EXPECT_EQ(fmt::format("{:>+06}", nan), " +nan");
if (std::signbit(-nan)) {
- EXPECT_EQ("-nan", fmt::format("{}", -nan));
- EXPECT_EQ(" -nan", fmt::format("{:+06}", -nan));
+ EXPECT_EQ(fmt::format("{}", -nan), "-nan");
+ EXPECT_EQ(fmt::format("{:+06}", -nan), " -nan");
} else {
fmt::print("Warning: compiler doesn't handle negative NaN correctly");
}
- EXPECT_EQ(" nan", fmt::format("{: }", nan));
- EXPECT_EQ("NAN", fmt::format("{:F}", nan));
- EXPECT_EQ("nan ", fmt::format("{:<7}", nan));
- EXPECT_EQ(" nan ", fmt::format("{:^7}", nan));
- EXPECT_EQ(" nan", fmt::format("{:>7}", nan));
+ EXPECT_EQ(fmt::format("{: }", nan), " nan");
+ EXPECT_EQ(fmt::format("{:F}", nan), "NAN");
+ EXPECT_EQ(fmt::format("{:<7}", nan), "nan ");
+ EXPECT_EQ(fmt::format("{:^7}", nan), " nan ");
+ EXPECT_EQ(fmt::format("{:>7}", nan), " nan");
}
TEST(format_test, format_infinity) {
double inf = std::numeric_limits<double>::infinity();
- EXPECT_EQ("inf", fmt::format("{}", inf));
- EXPECT_EQ("+inf", fmt::format("{:+}", inf));
- EXPECT_EQ("-inf", fmt::format("{}", -inf));
- EXPECT_EQ(" +inf", fmt::format("{:+06}", inf));
- EXPECT_EQ(" -inf", fmt::format("{:+06}", -inf));
- EXPECT_EQ("+inf ", fmt::format("{:<+06}", inf));
- EXPECT_EQ(" +inf ", fmt::format("{:^+06}", inf));
- EXPECT_EQ(" +inf", fmt::format("{:>+06}", inf));
- EXPECT_EQ(" inf", fmt::format("{: }", inf));
- EXPECT_EQ("INF", fmt::format("{:F}", inf));
- EXPECT_EQ("inf ", fmt::format("{:<7}", inf));
- EXPECT_EQ(" inf ", fmt::format("{:^7}", inf));
- EXPECT_EQ(" inf", fmt::format("{:>7}", inf));
+ EXPECT_EQ(fmt::format("{}", inf), "inf");
+ EXPECT_EQ(fmt::format("{:+}", inf), "+inf");
+ EXPECT_EQ(fmt::format("{}", -inf), "-inf");
+ EXPECT_EQ(fmt::format("{:+06}", inf), " +inf");
+ EXPECT_EQ(fmt::format("{:+06}", -inf), " -inf");
+ EXPECT_EQ(fmt::format("{:<+06}", inf), "+inf ");
+ EXPECT_EQ(fmt::format("{:^+06}", inf), " +inf ");
+ EXPECT_EQ(fmt::format("{:>+06}", inf), " +inf");
+ EXPECT_EQ(fmt::format("{: }", inf), " inf");
+ EXPECT_EQ(fmt::format("{:F}", inf), "INF");
+ EXPECT_EQ(fmt::format("{:<7}", inf), "inf ");
+ EXPECT_EQ(fmt::format("{:^7}", inf), " inf ");
+ EXPECT_EQ(fmt::format("{:>7}", inf), " inf");
}
TEST(format_test, format_long_double) {
- EXPECT_EQ("0", fmt::format("{0:}", 0.0l));
- EXPECT_EQ("0.000000", fmt::format("{0:f}", 0.0l));
- EXPECT_EQ("0.0", fmt::format("{:.1f}", 0.000000001l));
- EXPECT_EQ("0.10", fmt::format("{:.2f}", 0.099l));
- EXPECT_EQ("392.65", fmt::format("{0:}", 392.65l));
- EXPECT_EQ("392.65", fmt::format("{0:g}", 392.65l));
- EXPECT_EQ("392.65", fmt::format("{0:G}", 392.65l));
- EXPECT_EQ("392.650000", fmt::format("{0:f}", 392.65l));
- EXPECT_EQ("392.650000", fmt::format("{0:F}", 392.65l));
+ EXPECT_EQ(fmt::format("{0:}", 0.0l), "0");
+ EXPECT_EQ(fmt::format("{0:f}", 0.0l), "0.000000");
+ EXPECT_EQ(fmt::format("{:.1f}", 0.000000001l), "0.0");
+ EXPECT_EQ(fmt::format("{:.2f}", 0.099l), "0.10");
+ EXPECT_EQ(fmt::format("{0:}", 392.65l), "392.65");
+ EXPECT_EQ(fmt::format("{0:g}", 392.65l), "392.65");
+ EXPECT_EQ(fmt::format("{0:G}", 392.65l), "392.65");
+ EXPECT_EQ(fmt::format("{0:f}", 392.65l), "392.650000");
+ EXPECT_EQ(fmt::format("{0:F}", 392.65l), "392.650000");
char buffer[buffer_size];
safe_sprintf(buffer, "%Le", 392.65l);
EXPECT_EQ(buffer, fmt::format("{0:e}", 392.65l));
- EXPECT_EQ("+0000392.6", fmt::format("{0:+010.4g}", 392.64l));
+ EXPECT_EQ(fmt::format("{0:+010.4g}", 392.64l), "+0000392.6");
auto ld = 3.31l;
if (fmt::detail::is_double_double<decltype(ld)>::value) {
@@ -1499,8 +1486,8 @@ TEST(format_test, format_long_double) {
TEST(format_test, format_char) {
const char types[] = "cbBdoxX";
check_unknown_types('a', types, "char");
- EXPECT_EQ("a", fmt::format("{0}", 'a'));
- EXPECT_EQ("z", fmt::format("{0:c}", 'z'));
+ EXPECT_EQ(fmt::format("{0}", 'a'), "a");
+ EXPECT_EQ(fmt::format("{0:c}", 'z'), "z");
int n = 'x';
for (const char* type = types + 1; *type; ++type) {
std::string format_str = fmt::format("{{:{}}}", *type);
@@ -1510,39 +1497,41 @@ TEST(format_test, format_char) {
}
EXPECT_EQ(fmt::format("{:02X}", n), fmt::format("{:02X}", 'x'));
- EXPECT_EQ("\n", fmt::format("{}", '\n'));
- EXPECT_EQ("'\\n'", fmt::format("{:?}", '\n'));
- EXPECT_EQ("ff", fmt::format("{:x}", '\xff'));
+ EXPECT_EQ(fmt::format("{}", '\n'), "\n");
+ EXPECT_EQ(fmt::format("{:?}", '\n'), "'\\n'");
+ EXPECT_EQ(fmt::format("{:x}", '\xff'), "ff");
}
TEST(format_test, format_volatile_char) {
volatile char c = 'x';
- EXPECT_EQ("x", fmt::format("{}", c));
+ EXPECT_EQ(fmt::format("{}", c), "x");
}
TEST(format_test, format_unsigned_char) {
- EXPECT_EQ("42", fmt::format("{}", static_cast<unsigned char>(42)));
- EXPECT_EQ("42", fmt::format("{}", static_cast<uint8_t>(42)));
+ EXPECT_EQ(fmt::format("{}", static_cast<unsigned char>(42)), "42");
+ EXPECT_EQ(fmt::format("{}", static_cast<uint8_t>(42)), "42");
}
TEST(format_test, format_cstring) {
check_unknown_types("test", "sp", "string");
- EXPECT_EQ("test", fmt::format("{0}", "test"));
- EXPECT_EQ("test", fmt::format("{0:s}", "test"));
+ EXPECT_EQ(fmt::format("{0}", "test"), "test");
+ EXPECT_EQ(fmt::format("{0:s}", "test"), "test");
char nonconst[] = "nonconst";
- EXPECT_EQ("nonconst", fmt::format("{0}", nonconst));
- EXPECT_THROW_MSG(
- (void)fmt::format(runtime("{0}"), static_cast<const char*>(nullptr)),
- format_error, "string pointer is null");
+ EXPECT_EQ(fmt::format("{0}", nonconst), "nonconst");
+ auto nullstr = static_cast<const char*>(nullptr);
+ EXPECT_THROW_MSG((void)fmt::format("{}", nullstr), format_error,
+ "string pointer is null");
+ EXPECT_THROW_MSG((void)fmt::format("{:s}", nullstr), format_error,
+ "string pointer is null");
}
void function_pointer_test(int, double, std::string) {}
TEST(format_test, format_pointer) {
check_unknown_types(reinterpret_cast<void*>(0x1234), "p", "pointer");
- EXPECT_EQ("0x0", fmt::format("{0}", static_cast<void*>(nullptr)));
- EXPECT_EQ("0x1234", fmt::format("{0}", reinterpret_cast<void*>(0x1234)));
- EXPECT_EQ("0x1234", fmt::format("{0:p}", reinterpret_cast<void*>(0x1234)));
+ EXPECT_EQ(fmt::format("{0}", static_cast<void*>(nullptr)), "0x0");
+ EXPECT_EQ(fmt::format("{0}", reinterpret_cast<void*>(0x1234)), "0x1234");
+ EXPECT_EQ(fmt::format("{0:p}", reinterpret_cast<void*>(0x1234)), "0x1234");
// On CHERI (or other fat-pointer) systems, the size of a pointer is greater
// than the size an integer that can hold a virtual address. There is no
// portable address-as-an-integer type (yet) in C++, so we use `size_t` as
@@ -1566,7 +1555,7 @@ TEST(format_test, format_pointer) {
EXPECT_EQ(fmt::format("{}", fmt::detail::bit_cast<const void*>(
&function_pointer_test)),
fmt::format("{}", fmt::ptr(function_pointer_test)));
- EXPECT_EQ("0x0", fmt::format("{}", nullptr));
+ EXPECT_EQ(fmt::format("{}", nullptr), "0x0");
}
TEST(format_test, write_uintptr_fallback) {
@@ -1604,9 +1593,9 @@ TEST(format_test, format_string) {
}
TEST(format_test, format_string_view) {
- EXPECT_EQ("test", fmt::format("{}", string_view("test")));
- EXPECT_EQ("\"t\\nst\"", fmt::format("{:?}", string_view("t\nst")));
- EXPECT_EQ("", fmt::format("{}", string_view()));
+ EXPECT_EQ(fmt::format("{}", string_view("test")), "test");
+ EXPECT_EQ(fmt::format("{:?}", string_view("t\nst")), "\"t\\nst\"");
+ EXPECT_EQ(fmt::format("{}", string_view()), "");
}
#ifdef FMT_USE_STRING_VIEW
@@ -1621,8 +1610,8 @@ template <> struct formatter<string_viewable> : formatter<std::string_view> {
FMT_END_NAMESPACE
TEST(format_test, format_std_string_view) {
- EXPECT_EQ("test", fmt::format("{}", std::string_view("test")));
- EXPECT_EQ("foo", fmt::format("{}", string_viewable()));
+ EXPECT_EQ(fmt::format("{}", std::string_view("test")), "test");
+ EXPECT_EQ(fmt::format("{}", string_viewable()), "foo");
}
struct explicitly_convertible_to_std_string_view {
@@ -1673,8 +1662,8 @@ FMT_END_NAMESPACE
TEST(format_test, format_custom) {
EXPECT_THROW_MSG((void)fmt::format(runtime("{:s}"), date(2012, 12, 9)),
format_error, "unknown format specifier");
- EXPECT_EQ("42", fmt::format("{0}", Answer()));
- EXPECT_EQ("0042", fmt::format("{:04}", Answer()));
+ EXPECT_EQ(fmt::format("{0}", Answer()), "42");
+ EXPECT_EQ(fmt::format("{:04}", Answer()), "0042");
}
TEST(format_test, format_to_custom) {
@@ -1694,7 +1683,7 @@ TEST(format_test, format_examples) {
std::string message = fmt::format("The answer is {}", 42);
EXPECT_EQ("The answer is 42", message);
- EXPECT_EQ("42", fmt::format("{}", 42));
+ EXPECT_EQ(fmt::format("{}", 42), "42");
memory_buffer out;
fmt::format_to(std::back_inserter(out), "The answer is {}.", 42);
@@ -1716,17 +1705,17 @@ TEST(format_test, format_examples) {
EXPECT_EQ("First, thou shalt count to three",
fmt::format("First, thou shalt count to {0}", "three"));
- EXPECT_EQ("Bring me a shrubbery", fmt::format("Bring me a {}", "shrubbery"));
- EXPECT_EQ("From 1 to 3", fmt::format("From {} to {}", 1, 3));
+ EXPECT_EQ(fmt::format("Bring me a {}", "shrubbery"), "Bring me a shrubbery");
+ EXPECT_EQ(fmt::format("From {} to {}", 1, 3), "From 1 to 3");
char buffer[buffer_size];
safe_sprintf(buffer, "%03.2f", -1.2);
EXPECT_EQ(buffer, fmt::format("{:03.2f}", -1.2));
- EXPECT_EQ("a, b, c", fmt::format("{0}, {1}, {2}", 'a', 'b', 'c'));
- EXPECT_EQ("a, b, c", fmt::format("{}, {}, {}", 'a', 'b', 'c'));
- EXPECT_EQ("c, b, a", fmt::format("{2}, {1}, {0}", 'a', 'b', 'c'));
- EXPECT_EQ("abracadabra", fmt::format("{0}{1}{0}", "abra", "cad"));
+ EXPECT_EQ(fmt::format("{0}, {1}, {2}", 'a', 'b', 'c'), "a, b, c");
+ EXPECT_EQ(fmt::format("{}, {}, {}", 'a', 'b', 'c'), "a, b, c");
+ EXPECT_EQ(fmt::format("{2}, {1}, {0}", 'a', 'b', 'c'), "c, b, a");
+ EXPECT_EQ(fmt::format("{0}{1}{0}", "abra", "cad"), "abracadabra");
EXPECT_EQ("left aligned ",
fmt::format("{:<30}", "left aligned"));
@@ -1737,16 +1726,16 @@ TEST(format_test, format_examples) {
EXPECT_EQ("***********centered***********",
fmt::format("{:*^30}", "centered"));
- EXPECT_EQ("+3.140000; -3.140000", fmt::format("{:+f}; {:+f}", 3.14, -3.14));
- EXPECT_EQ(" 3.140000; -3.140000", fmt::format("{: f}; {: f}", 3.14, -3.14));
- EXPECT_EQ("3.140000; -3.140000", fmt::format("{:-f}; {:-f}", 3.14, -3.14));
+ EXPECT_EQ(fmt::format("{:+f}; {:+f}", 3.14, -3.14), "+3.140000; -3.140000");
+ EXPECT_EQ(fmt::format("{: f}; {: f}", 3.14, -3.14), " 3.140000; -3.140000");
+ EXPECT_EQ(fmt::format("{:-f}; {:-f}", 3.14, -3.14), "3.140000; -3.140000");
EXPECT_EQ("int: 42; hex: 2a; oct: 52",
fmt::format("int: {0:d}; hex: {0:x}; oct: {0:o}", 42));
EXPECT_EQ("int: 42; hex: 0x2a; oct: 052",
fmt::format("int: {0:d}; hex: {0:#x}; oct: {0:#o}", 42));
- EXPECT_EQ("The answer is 42", fmt::format("The answer is {}", 42));
+ EXPECT_EQ(fmt::format("The answer is {}", 42), "The answer is 42");
EXPECT_THROW_MSG(
(void)fmt::format(runtime("The answer is {:d}"), "forty-two"),
format_error, "invalid format specifier");
@@ -1765,7 +1754,7 @@ TEST(format_test, print) {
}
TEST(format_test, variadic) {
- EXPECT_EQ("abc1", fmt::format("{}c{}", "ab", 1));
+ EXPECT_EQ(fmt::format("{}c{}", "ab", 1), "abc1");
}
TEST(format_test, bytes) {
@@ -1810,23 +1799,23 @@ TEST(format_test, join) {
v2.push_back(3.4f);
void* v3[2] = {&v1[0], &v1[1]};
- EXPECT_EQ("(1, 2, 3)", fmt::format("({})", join(v1, v1 + 3, ", ")));
- EXPECT_EQ("(1)", fmt::format("({})", join(v1, v1 + 1, ", ")));
- EXPECT_EQ("()", fmt::format("({})", join(v1, v1, ", ")));
- EXPECT_EQ("(001, 002, 003)", fmt::format("({:03})", join(v1, v1 + 3, ", ")));
+ EXPECT_EQ(fmt::format("({})", join(v1, v1 + 3, ", ")), "(1, 2, 3)");
+ EXPECT_EQ(fmt::format("({})", join(v1, v1 + 1, ", ")), "(1)");
+ EXPECT_EQ(fmt::format("({})", join(v1, v1, ", ")), "()");
+ EXPECT_EQ(fmt::format("({:03})", join(v1, v1 + 3, ", ")), "(001, 002, 003)");
EXPECT_EQ("(+01.20, +03.40)",
fmt::format("({:+06.2f})", join(v2.begin(), v2.end(), ", ")));
- EXPECT_EQ("1, 2, 3", fmt::format("{0:{1}}", join(v1, v1 + 3, ", "), 1));
+ EXPECT_EQ(fmt::format("{0:{1}}", join(v1, v1 + 3, ", "), 1), "1, 2, 3");
EXPECT_EQ(fmt::format("{}, {}", v3[0], v3[1]),
fmt::format("{}", join(v3, v3 + 2, ", ")));
- EXPECT_EQ("(1, 2, 3)", fmt::format("({})", join(v1, ", ")));
- EXPECT_EQ("(+01.20, +03.40)", fmt::format("({:+06.2f})", join(v2, ", ")));
+ EXPECT_EQ(fmt::format("({})", join(v1, ", ")), "(1, 2, 3)");
+ EXPECT_EQ(fmt::format("({:+06.2f})", join(v2, ", ")), "(+01.20, +03.40)");
auto v4 = std::vector<test_enum>{foo, bar, foo};
- EXPECT_EQ("0 1 0", fmt::format("{}", join(v4, " ")));
+ EXPECT_EQ(fmt::format("{}", join(v4, " ")), "0 1 0");
}
#ifdef __cpp_lib_byte
@@ -1873,45 +1862,45 @@ static constexpr const char static_with_null[3] = {'{', '}', '\0'};
static constexpr const char static_no_null[2] = {'{', '}'};
TEST(format_test, compile_time_string) {
- EXPECT_EQ("foo", fmt::format(FMT_STRING("foo")));
- EXPECT_EQ("42", fmt::format(FMT_STRING("{}"), 42));
+ EXPECT_EQ(fmt::format(FMT_STRING("foo")), "foo");
+ EXPECT_EQ(fmt::format(FMT_STRING("{}"), 42), "42");
#if FMT_USE_NONTYPE_TEMPLATE_ARGS
using namespace fmt::literals;
EXPECT_EQ("foobar", fmt::format(FMT_STRING("{foo}{bar}"), "bar"_a = "bar",
"foo"_a = "foo"));
- EXPECT_EQ("", fmt::format(FMT_STRING("")));
- EXPECT_EQ("", fmt::format(FMT_STRING(""), "arg"_a = 42));
- EXPECT_EQ("42", fmt::format(FMT_STRING("{answer}"), "answer"_a = Answer()));
- EXPECT_EQ("1 2", fmt::format(FMT_STRING("{} {two}"), 1, "two"_a = 2));
+ EXPECT_EQ(fmt::format(FMT_STRING("")), "");
+ EXPECT_EQ(fmt::format(FMT_STRING(""), "arg"_a = 42), "");
+ EXPECT_EQ(fmt::format(FMT_STRING("{answer}"), "answer"_a = Answer()), "42");
+ EXPECT_EQ(fmt::format(FMT_STRING("{} {two}"), 1, "two"_a = 2), "1 2");
#endif
(void)static_with_null;
(void)static_no_null;
#ifndef _MSC_VER
- EXPECT_EQ("42", fmt::format(FMT_STRING(static_with_null), 42));
- EXPECT_EQ("42", fmt::format(FMT_STRING(static_no_null), 42));
+ EXPECT_EQ(fmt::format(FMT_STRING(static_with_null), 42), "42");
+ EXPECT_EQ(fmt::format(FMT_STRING(static_no_null), 42), "42");
#endif
(void)with_null;
(void)no_null;
#if FMT_CPLUSPLUS >= 201703L
- EXPECT_EQ("42", fmt::format(FMT_STRING(with_null), 42));
- EXPECT_EQ("42", fmt::format(FMT_STRING(no_null), 42));
+ 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("42", fmt::format(FMT_STRING(std::string_view("{}")), 42));
+ EXPECT_EQ(fmt::format(FMT_STRING(std::string_view("{}")), 42), "42");
#endif
}
TEST(format_test, custom_format_compile_time_string) {
- EXPECT_EQ("42", fmt::format(FMT_STRING("{}"), Answer()));
+ EXPECT_EQ(fmt::format(FMT_STRING("{}"), Answer()), "42");
auto answer = Answer();
- EXPECT_EQ("42", fmt::format(FMT_STRING("{}"), answer));
+ EXPECT_EQ(fmt::format(FMT_STRING("{}"), answer), "42");
char buf[10] = {};
fmt::format_to(buf, FMT_STRING("{}"), answer);
const Answer const_answer = Answer();
- EXPECT_EQ("42", fmt::format(FMT_STRING("{}"), const_answer));
+ EXPECT_EQ(fmt::format(FMT_STRING("{}"), const_answer), "42");
}
#if FMT_USE_USER_DEFINED_LITERALS
@@ -1924,11 +1913,11 @@ TEST(format_test, named_arg_udl) {
fmt::arg("second", "cad"), fmt::arg("third", 99)),
udl_a);
- EXPECT_EQ("42", fmt::format("{answer}", "answer"_a = Answer()));
+ EXPECT_EQ(fmt::format("{answer}", "answer"_a = Answer()), "42");
}
#endif // FMT_USE_USER_DEFINED_LITERALS
-TEST(format_test, enum) { EXPECT_EQ("0", fmt::format("{}", foo)); }
+TEST(format_test, enum) { EXPECT_EQ(fmt::format("{}", foo), "0"); }
TEST(format_test, formatter_not_specialized) {
static_assert(!fmt::has_formatter<fmt::formatter<test_enum>,
@@ -1941,12 +1930,12 @@ enum big_enum : unsigned long long { big_enum_value = 5000000000ULL };
auto format_as(big_enum e) -> unsigned long long { return e; }
TEST(format_test, strong_enum) {
- EXPECT_EQ("5000000000", fmt::format("{}", big_enum_value));
+ EXPECT_EQ(fmt::format("{}", big_enum_value), "5000000000");
}
#endif
TEST(format_test, non_null_terminated_format_string) {
- EXPECT_EQ("42", fmt::format(string_view("{}foo", 2), 42));
+ EXPECT_EQ(fmt::format(string_view("{}foo", 2), 42), "42");
}
namespace adl_test {
@@ -2079,16 +2068,16 @@ struct test_output_iterator {
using pointer = void;
using reference = void;
- test_output_iterator& operator++() {
+ auto operator++() -> test_output_iterator& {
++data;
return *this;
}
- test_output_iterator operator++(int) {
+ auto operator++(int) -> test_output_iterator {
auto tmp = *this;
++data;
return tmp;
}
- char& operator*() { return *data; }
+ auto operator*() -> char& { return *data; }
};
TEST(format_test, format_to_n_output_iterator) {
@@ -2103,13 +2092,13 @@ TEST(format_test, vformat_to) {
auto args = fmt::make_format_args<context>(n);
auto s = std::string();
fmt::vformat_to(std::back_inserter(s), "{}", args);
- EXPECT_EQ("42", s);
+ EXPECT_EQ(s, "42");
s.clear();
fmt::vformat_to(std::back_inserter(s), FMT_STRING("{}"), args);
- EXPECT_EQ("42", s);
+ EXPECT_EQ(s, "42");
}
-TEST(format_test, char_traits_is_not_ambiguous) {
+TEST(format_test, char_traits_not_ambiguous) {
// Test that we don't inject detail names into the std namespace.
using namespace std;
auto c = char_traits<char>::char_type();
@@ -2156,6 +2145,13 @@ auto format_as(scoped_enum_as_string) -> std::string { return "foo"; }
struct struct_as_int {};
auto format_as(struct_as_int) -> int { return 42; }
+
+struct struct_as_const_reference {
+ const std::string name = "foo";
+};
+auto format_as(const struct_as_const_reference& s) -> const std::string& {
+ return s.name;
+}
} // namespace test
TEST(format_test, format_as) {
@@ -2163,6 +2159,7 @@ TEST(format_test, format_as) {
EXPECT_EQ(fmt::format("{}", test::scoped_enum_as_string_view()), "foo");
EXPECT_EQ(fmt::format("{}", test::scoped_enum_as_string()), "foo");
EXPECT_EQ(fmt::format("{}", test::struct_as_int()), "42");
+ EXPECT_EQ(fmt::format("{}", test::struct_as_const_reference()), "foo");
}
TEST(format_test, format_as_to_string) {
@@ -2172,7 +2169,7 @@ TEST(format_test, format_as_to_string) {
EXPECT_EQ(fmt::to_string(test::struct_as_int()), "42");
}
-template <typename Char, typename T> bool check_enabled_formatter() {
+template <typename Char, typename T> auto check_enabled_formatter() -> bool {
static_assert(std::is_default_constructible<fmt::formatter<T, Char>>::value,
"");
return true;
@@ -2198,21 +2195,20 @@ TEST(format_test, test_formatters_enabled) {
TEST(format_int_test, data) {
fmt::format_int format_int(42);
- EXPECT_EQ("42", std::string(format_int.data(), format_int.size()));
+ EXPECT_EQ(std::string(format_int.data(), format_int.size()), "42");
}
TEST(format_int_test, format_int) {
- EXPECT_EQ("42", fmt::format_int(42).str());
- EXPECT_EQ(2u, fmt::format_int(42).size());
- EXPECT_EQ("-42", fmt::format_int(-42).str());
- EXPECT_EQ(3u, fmt::format_int(-42).size());
- EXPECT_EQ("42", fmt::format_int(42ul).str());
- EXPECT_EQ("-42", fmt::format_int(-42l).str());
- EXPECT_EQ("42", fmt::format_int(42ull).str());
- EXPECT_EQ("-42", fmt::format_int(-42ll).str());
- std::ostringstream os;
- os << max_value<int64_t>();
- EXPECT_EQ(os.str(), fmt::format_int(max_value<int64_t>()).str());
+ EXPECT_EQ(fmt::format_int(42).str(), "42");
+ EXPECT_EQ(fmt::format_int(42).size(), 2u);
+ EXPECT_EQ(fmt::format_int(-42).str(), "-42");
+ EXPECT_EQ(fmt::format_int(-42).size(), 3u);
+ EXPECT_EQ(fmt::format_int(42ul).str(), "42");
+ EXPECT_EQ(fmt::format_int(-42l).str(), "-42");
+ EXPECT_EQ(fmt::format_int(42ull).str(), "42");
+ EXPECT_EQ(fmt::format_int(-42ll).str(), "-42");\
+ EXPECT_EQ(fmt::format_int(max_value<int64_t>()).str(),
+ std::to_string(max_value<int64_t>()));
}
#ifndef FMT_STATIC_THOUSANDS_SEPARATOR
@@ -2272,6 +2268,14 @@ TEST(format_test, format_named_arg_with_locale) {
"42");
}
+TEST(format_test, format_locale) {
+ auto loc = std::locale({}, new fmt::format_facet<std::locale>(","));
+ EXPECT_EQ(fmt::format(loc, "{:Lx}", 123456789), "7,5bc,d15");
+ EXPECT_EQ(fmt::format(loc, "{:#Lb}", -123456789),
+ "-0b111,010,110,111,100,110,100,010,101");
+ EXPECT_EQ(fmt::format(loc, "{:10Lo}", 12345), " 30,071");
+}
+
#endif // FMT_STATIC_THOUSANDS_SEPARATOR
struct convertible_to_nonconst_cstring {
diff --git a/test/gtest-extra-test.cc b/test/gtest-extra-test.cc
index 42340a2d..34054b68 100644
--- a/test/gtest-extra-test.cc
+++ b/test/gtest-extra-test.cc
@@ -354,7 +354,7 @@ TEST(output_redirect_test, dup_error_in_ctor) {
FMT_POSIX(close(fd));
std::unique_ptr<output_redirect> redir{nullptr};
EXPECT_SYSTEM_ERROR_NOASSERT(
- redir.reset(new output_redirect(f.get())), EBADF,
+ redir.reset(new output_redirect(f.get(), false)), EBADF,
fmt::format("cannot duplicate file descriptor {}", fd));
copy.dup2(fd); // "undo" close or dtor will fail
}
diff --git a/test/gtest-extra.cc b/test/gtest-extra.cc
index 542e4b5e..3d27cf96 100644
--- a/test/gtest-extra.cc
+++ b/test/gtest-extra.cc
@@ -11,8 +11,8 @@
using fmt::file;
-output_redirect::output_redirect(FILE* f) : file_(f) {
- flush();
+output_redirect::output_redirect(FILE* f, bool flush) : file_(f) {
+ if (flush) this->flush();
int fd = FMT_POSIX(fileno(f));
// Create a file object referring to the original file.
original_ = file::dup(fd);
diff --git a/test/gtest-extra.h b/test/gtest-extra.h
index 03a07a2a..e08c94c0 100644
--- a/test/gtest-extra.h
+++ b/test/gtest-extra.h
@@ -77,7 +77,7 @@ class output_redirect {
void restore();
public:
- explicit output_redirect(FILE* file);
+ explicit output_redirect(FILE* file, bool flush = true);
~output_redirect() noexcept;
output_redirect(const output_redirect&) = delete;
diff --git a/test/gtest/gmock-gtest-all.cc b/test/gtest/gmock-gtest-all.cc
index 7b33134f..9d3b9bc1 100644
--- a/test/gtest/gmock-gtest-all.cc
+++ b/test/gtest/gmock-gtest-all.cc
@@ -1912,7 +1912,7 @@ void AssertHelper::operator=(const Message& message) const {
namespace {
// When TEST_P is found without a matching INSTANTIATE_TEST_SUITE_P
-// to creates test cases for it, a syntetic test case is
+// to creates test cases for it, a synthetic test case is
// inserted to report ether an error or a log message.
//
// This configuration bit will likely be removed at some point.
diff --git a/test/ostream-test.cc b/test/ostream-test.cc
index b2d15466..98ee0757 100644
--- a/test/ostream-test.cc
+++ b/test/ostream-test.cc
@@ -17,7 +17,7 @@ struct test {};
// included after fmt/format.h.
namespace fmt {
template <> struct formatter<test> : formatter<int> {
- auto format(const test&, format_context& ctx) -> decltype(ctx.out()) {
+ auto format(const test&, format_context& ctx) const -> decltype(ctx.out()) {
return formatter<int>::format(42, ctx);
}
};
@@ -289,3 +289,20 @@ TEST(ostream_test, closed_ofstream) {
std::ofstream ofs;
fmt::print(ofs, "discard");
}
+
+struct unlocalized {};
+
+auto operator<<(std::ostream& os, unlocalized)
+ -> std::ostream& {
+ return os << 12345;
+}
+
+namespace fmt {
+template <> struct formatter<unlocalized> : ostream_formatter {};
+} // namespace fmt
+
+TEST(ostream_test, unlocalized) {
+ auto loc = get_locale("en_US.UTF-8");
+ std::locale::global(loc);
+ EXPECT_EQ(fmt::format(loc, "{}", unlocalized()), "12345");
+}
diff --git a/test/posix-mock.h b/test/posix-mock.h
index 4f2a42c1..54580871 100644
--- a/test/posix-mock.h
+++ b/test/posix-mock.h
@@ -30,13 +30,13 @@ namespace test {
#ifndef _MSC_VER
// Size type for read and write.
-typedef size_t size_t;
-typedef ssize_t ssize_t;
+using size_t = size_t;
+using ssize_t = ssize_t;
int open(const char* path, int oflag, int mode);
int fstat(int fd, struct stat* buf);
#else
-typedef unsigned size_t;
-typedef int ssize_t;
+using size_t = unsigned;
+using ssize_t = int;
#endif
#ifndef _WIN32
diff --git a/test/printf-test.cc b/test/printf-test.cc
index 81db9b23..7e09ecca 100644
--- a/test/printf-test.cc
+++ b/test/printf-test.cc
@@ -310,10 +310,10 @@ TEST(printf_test, dynamic_precision) {
}
}
-template <typename T> struct make_signed { typedef T type; };
+template <typename T> struct make_signed { using type = T; };
#define SPECIALIZE_MAKE_SIGNED(T, S) \
- template <> struct make_signed<T> { typedef S type; }
+ template <> struct make_signed<T> { using type = S; }
SPECIALIZE_MAKE_SIGNED(char, signed char);
SPECIALIZE_MAKE_SIGNED(unsigned char, signed char);
diff --git a/test/ranges-test.cc b/test/ranges-test.cc
index ba5c464d..8ab66b33 100644
--- a/test/ranges-test.cc
+++ b/test/ranges-test.cc
@@ -7,13 +7,19 @@
#include "fmt/ranges.h"
+#include <list>
#include <map>
+#include <numeric>
#include <queue>
#include <stack>
#include <string>
#include <utility>
#include <vector>
+#if FMT_HAS_INCLUDE(<ranges>)
+# include <ranges>
+#endif
+
#include "gtest/gtest.h"
#if !FMT_GCC_VERSION || FMT_GCC_VERSION >= 601
@@ -242,7 +248,7 @@ template <typename T> class non_const_only_range {
explicit non_const_only_range(Args&&... args)
: vec(std::forward<Args>(args)...) {}
- auto begin() -> const_iterator{ return vec.begin(); }
+ auto begin() -> const_iterator { return vec.begin(); }
auto end() -> const_iterator { return vec.end(); }
};
@@ -360,7 +366,7 @@ struct cpp20_only_range {
iterator() = default;
iterator(int i) : val(i) {}
auto operator*() const -> int { return val; }
- auto operator++() -> iterator&{
+ auto operator++() -> iterator& {
++val;
return *this;
}
@@ -416,6 +422,17 @@ TEST(ranges_test, join_range) {
}
#endif // FMT_RANGES_TEST_ENABLE_JOIN
+#if defined(__cpp_lib_ranges) && __cpp_lib_ranges >= 202302L
+TEST(ranges_test, nested_ranges) {
+ auto l = std::list{1, 2, 3};
+ auto r = std::views::iota(0, 3) | std::views::transform([&l](auto i) {
+ return std::views::take(std::ranges::subrange(l), i);
+ }) |
+ std::views::transform(std::views::reverse);
+ EXPECT_EQ(fmt::format("{}", r), "[[], [1], [2, 1]]");
+}
+#endif
+
TEST(ranges_test, is_printable) {
using fmt::detail::is_printable;
EXPECT_TRUE(is_printable(0x0323));
@@ -528,3 +545,16 @@ TEST(ranges_test, container_adaptor) {
EXPECT_EQ(fmt::format("{}", m), "[1, 2]");
}
}
+
+struct tieable {
+ int a = 3;
+ double b = 0.42;
+};
+
+auto format_as(const tieable& t) -> std::tuple<int, double> {
+ return std::tie(t.a, t.b);
+}
+
+TEST(ranges_test, format_as_tie) {
+ EXPECT_EQ(fmt::format("{}", tieable()), "(3, 0.42)");
+}
diff --git a/test/scan-test.cc b/test/scan-test.cc
index bec54134..f0ede192 100644
--- a/test/scan-test.cc
+++ b/test/scan-test.cc
@@ -10,23 +10,29 @@
#include <time.h>
#include <climits>
+#include <thread>
+#include "fmt/os.h"
#include "gmock/gmock.h"
#include "gtest-extra.h"
TEST(scan_test, read_text) {
- auto s = fmt::string_view("foo");
+ fmt::string_view s = "foo";
auto end = fmt::scan(s, "foo");
EXPECT_EQ(end, s.end());
EXPECT_THROW_MSG(fmt::scan("fob", "foo"), fmt::format_error, "invalid input");
}
TEST(scan_test, read_int) {
- auto n = int();
+ int n = 0;
fmt::scan("42", "{}", n);
EXPECT_EQ(n, 42);
fmt::scan("-42", "{}", n);
EXPECT_EQ(n, -42);
+ fmt::scan("42", "{:}", n);
+ EXPECT_EQ(n, 42);
+ EXPECT_THROW_MSG(fmt::scan(std::to_string(INT_MAX + 1u), "{}", n),
+ fmt::format_error, "number is too big");
}
TEST(scan_test, read_longlong) {
@@ -38,7 +44,7 @@ TEST(scan_test, read_longlong) {
}
TEST(scan_test, read_uint) {
- auto n = unsigned();
+ unsigned n = 0;
fmt::scan("42", "{}", n);
EXPECT_EQ(n, 42);
EXPECT_THROW_MSG(fmt::scan("-42", "{}", n), fmt::format_error,
@@ -53,52 +59,63 @@ TEST(scan_test, read_ulonglong) {
"invalid input");
}
+TEST(scan_test, read_hex) {
+ unsigned n = 0;
+ fmt::scan("2a", "{:x}", n);
+ EXPECT_EQ(n, 42);
+ auto num_digits = std::numeric_limits<unsigned>::digits / 4;
+ EXPECT_THROW_MSG(fmt::scan(fmt::format("1{:0{}}", 0, num_digits), "{:x}", n),
+ fmt::format_error, "number is too big");
+}
+
TEST(scan_test, read_string) {
- auto s = std::string();
+ std::string s;
fmt::scan("foo", "{}", s);
EXPECT_EQ(s, "foo");
}
TEST(scan_test, read_string_view) {
- auto s = fmt::string_view();
+ fmt::string_view s;
fmt::scan("foo", "{}", s);
EXPECT_EQ(s, "foo");
}
-#ifdef FMT_HAVE_STRPTIME
+TEST(scan_test, separator) {
+ int n1 = 0, n2 = 0;
+ fmt::scan("10 20", "{} {}", n1, n2);
+ EXPECT_EQ(n1, 10);
+ EXPECT_EQ(n2, 20);
+}
+
+struct num {
+ int value;
+};
+
namespace fmt {
-template <> struct scanner<tm> {
- std::string format;
-
- scan_parse_context::iterator parse(scan_parse_context& ctx) {
- auto it = ctx.begin();
- if (it != ctx.end() && *it == ':') ++it;
- auto end = it;
- while (end != ctx.end() && *end != '}') ++end;
- format.reserve(detail::to_unsigned(end - it + 1));
- format.append(it, end);
- format.push_back('\0');
- return end;
+template <> struct scanner<num> {
+ bool hex = false;
+
+ auto parse(scan_parse_context& ctx) -> scan_parse_context::iterator {
+ auto it = ctx.begin(), end = ctx.end();
+ if (it != end && *it == 'x') hex = true;
+ if (it != end && *it != '}') throw_format_error("invalid format");
+ return it;
}
template <class ScanContext>
- typename ScanContext::iterator scan(tm& t, ScanContext& ctx) {
- auto result = strptime(ctx.begin(), format.c_str(), &t);
- if (!result) throw format_error("failed to parse time");
- return result;
+ auto scan(num& n, ScanContext& ctx) const -> typename ScanContext::iterator {
+ // TODO: handle specifier
+ return fmt::scan(ctx, "{}", n.value);
}
};
} // namespace fmt
TEST(scan_test, read_custom) {
- auto input = "Date: 1985-10-25";
- auto t = tm();
- fmt::scan(input, "Date: {0:%Y-%m-%d}", t);
- EXPECT_EQ(t.tm_year, 85);
- EXPECT_EQ(t.tm_mon, 9);
- EXPECT_EQ(t.tm_mday, 25);
+ auto input = "42";
+ auto n = num();
+ fmt::scan(input, "{:}", n);
+ EXPECT_EQ(n.value, 42);
}
-#endif
TEST(scan_test, invalid_format) {
EXPECT_THROW_MSG(fmt::scan("", "{}"), fmt::format_error,
@@ -108,9 +125,64 @@ TEST(scan_test, invalid_format) {
}
TEST(scan_test, example) {
- auto key = std::string();
- auto value = int();
+ std::string key;
+ int value = 0;
fmt::scan("answer = 42", "{} = {}", key, value);
EXPECT_EQ(key, "answer");
EXPECT_EQ(value, 42);
}
+
+TEST(scan_test, end_of_input) {
+ int value = 0;
+ fmt::scan("", "{}", value);
+}
+
+#if FMT_USE_FCNTL
+TEST(scan_test, file) {
+ fmt::file read_end, write_end;
+ fmt::file::pipe(read_end, write_end);
+
+ fmt::string_view input = "10 20";
+ write_end.write(input.data(), input.size());
+ write_end.close();
+
+ int n1 = 0, n2 = 0;
+ fmt::buffered_file f = read_end.fdopen("r");
+ fmt::scan(f.get(), "{} {}", n1, n2);
+ EXPECT_EQ(n1, 10);
+ EXPECT_EQ(n2, 20);
+}
+
+TEST(scan_test, lock) {
+ fmt::file read_end, write_end;
+ fmt::file::pipe(read_end, write_end);
+
+ std::thread producer([&]() {
+ fmt::string_view input = "42 ";
+ for (int i = 0; i < 1000; ++i) write_end.write(input.data(), input.size());
+ write_end.close();
+ });
+
+ std::atomic<int> count(0);
+ fmt::buffered_file f = read_end.fdopen("r");
+ auto fun = [&]() {
+ int value = 0;
+ while (fmt::scan(f.get(), "{}", value)) {
+ if (value != 42) {
+ read_end.close();
+ EXPECT_EQ(value, 42);
+ break;
+ }
+ ++count;
+ }
+ };
+ std::thread consumer1(fun);
+ std::thread consumer2(fun);
+
+ producer.join();
+ consumer1.join();
+ consumer2.join();
+ EXPECT_EQ(count, 1000);
+
+}
+#endif // FMT_USE_FCNTL
diff --git a/test/scan.h b/test/scan.h
index a2cb2aa6..a68c77c3 100644
--- a/test/scan.h
+++ b/test/scan.h
@@ -12,43 +12,284 @@
#include "fmt/format.h"
FMT_BEGIN_NAMESPACE
-template <typename T, typename Char = char> struct scanner {
- // A deleted default constructor indicates a disabled scanner.
- scanner() = delete;
+namespace detail {
+
+inline auto is_whitespace(char c) -> bool { return c == ' ' || c == '\n'; }
+
+// If c is a hex digit returns its numeric value, othewise -1.
+inline auto to_hex_digit(char c) -> int {
+ if (c >= '0' && c <= '9') return c - '0';
+ if (c >= 'a' && c <= 'f') return c - 'a' + 10;
+ if (c >= 'A' && c <= 'F') return c - 'A' + 10;
+ return -1;
+}
+
+struct maybe_contiguous_range {
+ const char* begin;
+ const char* end;
+
+ explicit operator bool() const { return begin != nullptr; }
};
-class scan_parse_context {
+class scan_buffer {
private:
- string_view format_;
+ const char* ptr_;
+ const char* end_;
+ bool contiguous_;
+
+ protected:
+ scan_buffer(const char* ptr, const char* end, bool contiguous)
+ : ptr_(ptr), end_(end), contiguous_(contiguous) {}
+ ~scan_buffer() = default;
+
+ void set(string_view buf) {
+ ptr_ = buf.begin();
+ end_ = buf.end();
+ }
+
+ auto ptr() const -> const char* { return ptr_; }
public:
- using iterator = string_view::iterator;
+ scan_buffer(const scan_buffer&) = delete;
+ void operator=(const scan_buffer&) = delete;
- explicit FMT_CONSTEXPR scan_parse_context(string_view format)
- : format_(format) {}
+ // Fills the buffer with more input if available.
+ virtual void consume() = 0;
- FMT_CONSTEXPR iterator begin() const { return format_.begin(); }
- FMT_CONSTEXPR iterator end() const { return format_.end(); }
+ class sentinel {};
- void advance_to(iterator it) {
- format_.remove_prefix(detail::to_unsigned(it - begin()));
+ class iterator {
+ private:
+ const char** ptr_;
+ scan_buffer* buf_; // This could be merged with ptr_.
+ char value_;
+
+ static auto get_sentinel() -> const char** {
+ static const char* ptr = nullptr;
+ return &ptr;
+ }
+
+ friend class scan_buffer;
+
+ friend auto operator==(iterator lhs, sentinel) -> bool {
+ return *lhs.ptr_ == nullptr;
+ }
+ friend auto operator!=(iterator lhs, sentinel) -> bool {
+ return *lhs.ptr_ != nullptr;
+ }
+
+ iterator(scan_buffer* buf) : buf_(buf) {
+ if (buf->ptr_ == buf->end_) {
+ ptr_ = get_sentinel();
+ return;
+ }
+ ptr_ = &buf->ptr_;
+ value_ = *buf->ptr_;
+ }
+
+ friend scan_buffer& get_buffer(iterator it) { return *it.buf_; }
+
+ public:
+ iterator() : ptr_(get_sentinel()), buf_(nullptr) {}
+
+ auto operator++() -> iterator& {
+ if (!buf_->try_consume()) ptr_ = get_sentinel();
+ value_ = *buf_->ptr_;
+ return *this;
+ }
+ auto operator++(int) -> iterator {
+ iterator copy = *this;
+ ++*this;
+ return copy;
+ }
+ auto operator*() const -> char { return value_; }
+
+ auto base() const -> const char* { return buf_->ptr_; }
+
+ friend auto to_contiguous(iterator it) -> maybe_contiguous_range;
+ friend auto advance(iterator it, size_t n) -> iterator;
+ };
+
+ friend auto to_contiguous(iterator it) -> maybe_contiguous_range {
+ if (it.buf_->is_contiguous()) return {it.buf_->ptr_, it.buf_->end_};
+ return {nullptr, nullptr};
+ }
+ friend auto advance(iterator it, size_t n) -> iterator {
+ FMT_ASSERT(it.buf_->is_contiguous(), "");
+ const char*& ptr = it.buf_->ptr_;
+ ptr += n;
+ it.value_ = *ptr;
+ if (ptr == it.buf_->end_) it.ptr_ = iterator::get_sentinel();
+ return it;
+ }
+
+ auto begin() -> iterator { return this; }
+ auto end() -> sentinel { return {}; }
+
+ auto is_contiguous() const -> bool { return contiguous_; }
+
+ // Tries consuming a single code unit. Returns true iff there is more input.
+ auto try_consume() -> bool {
+ FMT_ASSERT(ptr_ != end_, "");
+ ++ptr_;
+ if (ptr_ != end_) return true;
+ consume();
+ return ptr_ != end_;
+ }
+};
+
+using scan_iterator = scan_buffer::iterator;
+using scan_sentinel = scan_buffer::sentinel;
+
+class string_scan_buffer : public scan_buffer {
+ private:
+ void consume() override {}
+
+ public:
+ explicit string_scan_buffer(string_view s)
+ : scan_buffer(s.begin(), s.end(), true) {}
+};
+
+#ifdef _WIN32
+void flockfile(FILE* f) { _lock_file(f); }
+void funlockfile(FILE* f) { _unlock_file(f); }
+int getc_unlocked(FILE* f) { return _fgetc_nolock(f); }
+#endif
+
+// A FILE wrapper. F is FILE defined as a template parameter to make
+// system-specific API detection work.
+template <typename F> class file_base {
+ protected:
+ F* file_;
+
+ public:
+ file_base(F* file) : file_(file) {}
+ operator F*() const { return file_; }
+
+ // Reads a code unit from the stream.
+ auto get() -> int {
+ int result = getc_unlocked(file_);
+ if (result == EOF && ferror(file_) != 0)
+ FMT_THROW(system_error(errno, FMT_STRING("getc failed")));
+ return result;
+ }
+
+ // Puts the code unit back into the stream buffer.
+ void unget(char c) {
+ if (ungetc(c, file_) == EOF)
+ FMT_THROW(system_error(errno, FMT_STRING("ungetc failed")));
+ }
+};
+
+// A FILE wrapper for glibc.
+template <typename F> class glibc_file : public file_base<F> {
+ public:
+ using file_base<F>::file_base;
+
+ // Returns the file's read buffer as a string_view.
+ auto buffer() const -> string_view {
+ return {this->file_->_IO_read_ptr,
+ to_unsigned(this->file_->_IO_read_end - this->file_->_IO_read_ptr)};
+ }
+};
+
+// A FILE wrapper for Apple's libc.
+template <typename F> class apple_file : public file_base<F> {
+ public:
+ using file_base<F>::file_base;
+
+ auto buffer() const -> string_view {
+ return {reinterpret_cast<char*>(this->file_->_p),
+ to_unsigned(this->file_->_r)};
+ }
+};
+
+// A fallback FILE wrapper.
+template <typename F> class fallback_file : public file_base<F> {
+ private:
+ char next_; // The next unconsumed character in the buffer.
+ bool has_next_ = false;
+
+ public:
+ using file_base<F>::file_base;
+
+ auto buffer() const -> string_view { return {&next_, has_next_ ? 1u : 0u}; }
+
+ auto get() -> int {
+ has_next_ = false;
+ return file_base<F>::get();
+ }
+
+ void unget(char c) {
+ file_base<F>::unget(c);
+ next_ = c;
+ has_next_ = true;
+ }
+};
+
+class file_scan_buffer : public scan_buffer {
+ private:
+ template <typename F, FMT_ENABLE_IF(sizeof(F::_IO_read_ptr) != 0)>
+ static auto get_file(F* f, int) -> glibc_file<F> {
+ return f;
+ }
+ template <typename F, FMT_ENABLE_IF(sizeof(F::_p) != 0)>
+ static auto get_file(F* f, int) -> apple_file<F> {
+ return f;
}
+ static auto get_file(FILE* f, ...) -> fallback_file<FILE> { return f; }
+
+ decltype(get_file(static_cast<FILE*>(nullptr), 0)) file_;
+
+ // Fills the buffer if it is empty.
+ void fill() {
+ string_view buf = file_.buffer();
+ if (buf.size() == 0) {
+ int c = file_.get();
+ // Put the character back since we are only filling the buffer.
+ if (c != EOF) file_.unget(static_cast<char>(c));
+ buf = file_.buffer();
+ }
+ set(buf);
+ }
+
+ void consume() override {
+ // Consume the current buffer content.
+ size_t n = to_unsigned(ptr() - file_.buffer().begin());
+ for (size_t i = 0; i != n; ++i) file_.get();
+ fill();
+ }
+
+ public:
+ explicit file_scan_buffer(FILE* f)
+ : scan_buffer(nullptr, nullptr, false), file_(f) {
+ flockfile(f);
+ fill();
+ }
+ ~file_scan_buffer() { funlockfile(file_); }
};
+} // namespace detail
-struct scan_context {
+template <typename T, typename Char = char> struct scanner {
+ // A deleted default constructor indicates a disabled scanner.
+ scanner() = delete;
+};
+
+class scan_parse_context {
private:
- string_view input_;
+ string_view format_;
public:
- using iterator = const char*;
+ using iterator = string_view::iterator;
- explicit FMT_CONSTEXPR scan_context(string_view input) : input_(input) {}
+ explicit FMT_CONSTEXPR scan_parse_context(string_view format)
+ : format_(format) {}
- iterator begin() const { return input_.data(); }
- iterator end() const { return begin() + input_.size(); }
+ FMT_CONSTEXPR auto begin() const -> iterator { return format_.begin(); }
+ FMT_CONSTEXPR auto end() const -> iterator { return format_.end(); }
void advance_to(iterator it) {
- input_.remove_prefix(detail::to_unsigned(it - begin()));
+ format_.remove_prefix(detail::to_unsigned(it - begin()));
}
};
@@ -64,181 +305,362 @@ enum class scan_type {
custom_type
};
-struct custom_scan_arg {
+template <typename Context> struct custom_scan_arg {
void* value;
- void (*scan)(void* arg, scan_parse_context& parse_ctx, scan_context& ctx);
+ void (*scan)(void* arg, scan_parse_context& parse_ctx, Context& ctx);
};
+} // namespace detail
-class scan_arg {
- public:
- scan_type type;
+// A scan argument. Context is a template parameter for the compiled API where
+// output can be unbuffered.
+template <typename Context> class basic_scan_arg {
+ private:
+ using scan_type = detail::scan_type;
+ scan_type type_;
union {
- int* int_value;
- unsigned* uint_value;
- long long* long_long_value;
- unsigned long long* ulong_long_value;
- std::string* string;
- fmt::string_view* string_view;
- custom_scan_arg custom;
+ int* int_value_;
+ unsigned* uint_value_;
+ long long* long_long_value_;
+ unsigned long long* ulong_long_value_;
+ std::string* string_;
+ string_view* string_view_;
+ detail::custom_scan_arg<Context> custom_;
// TODO: more types
};
- FMT_CONSTEXPR scan_arg() : type(scan_type::none_type), int_value(nullptr) {}
- FMT_CONSTEXPR scan_arg(int& value)
- : type(scan_type::int_type), int_value(&value) {}
- FMT_CONSTEXPR scan_arg(unsigned& value)
- : type(scan_type::uint_type), uint_value(&value) {}
- FMT_CONSTEXPR scan_arg(long long& value)
- : type(scan_type::long_long_type), long_long_value(&value) {}
- FMT_CONSTEXPR scan_arg(unsigned long long& value)
- : type(scan_type::ulong_long_type), ulong_long_value(&value) {}
- FMT_CONSTEXPR scan_arg(std::string& value)
- : type(scan_type::string_type), string(&value) {}
- FMT_CONSTEXPR scan_arg(fmt::string_view& value)
- : type(scan_type::string_view_type), string_view(&value) {}
- template <typename T>
- FMT_CONSTEXPR scan_arg(T& value) : type(scan_type::custom_type) {
- custom.value = &value;
- custom.scan = scan_custom_arg<T>;
- }
-
- private:
template <typename T>
static void scan_custom_arg(void* arg, scan_parse_context& parse_ctx,
- scan_context& ctx) {
- scanner<T> s;
+ Context& ctx) {
+ auto s = scanner<T>();
parse_ctx.advance_to(s.parse(parse_ctx));
ctx.advance_to(s.scan(*static_cast<T*>(arg), ctx));
}
+
+ public:
+ FMT_CONSTEXPR basic_scan_arg()
+ : type_(scan_type::none_type), int_value_(nullptr) {}
+ FMT_CONSTEXPR basic_scan_arg(int& value)
+ : type_(scan_type::int_type), int_value_(&value) {}
+ FMT_CONSTEXPR basic_scan_arg(unsigned& value)
+ : type_(scan_type::uint_type), uint_value_(&value) {}
+ FMT_CONSTEXPR basic_scan_arg(long long& value)
+ : type_(scan_type::long_long_type), long_long_value_(&value) {}
+ FMT_CONSTEXPR basic_scan_arg(unsigned long long& value)
+ : type_(scan_type::ulong_long_type), ulong_long_value_(&value) {}
+ FMT_CONSTEXPR basic_scan_arg(std::string& value)
+ : type_(scan_type::string_type), string_(&value) {}
+ FMT_CONSTEXPR basic_scan_arg(string_view& value)
+ : type_(scan_type::string_view_type), string_view_(&value) {}
+ template <typename T>
+ FMT_CONSTEXPR basic_scan_arg(T& value) : type_(scan_type::custom_type) {
+ custom_.value = &value;
+ custom_.scan = scan_custom_arg<T>;
+ }
+
+ constexpr explicit operator bool() const noexcept {
+ return type_ != scan_type::none_type;
+ }
+
+ auto type() const -> detail::scan_type { return type_; }
+
+ template <typename Visitor>
+ auto visit(Visitor&& vis) -> decltype(vis(monostate())) {
+ switch (type_) {
+ case scan_type::none_type:
+ break;
+ case scan_type::int_type:
+ return vis(*int_value_);
+ case scan_type::uint_type:
+ return vis(*uint_value_);
+ case scan_type::long_long_type:
+ return vis(*long_long_value_);
+ case scan_type::ulong_long_type:
+ return vis(*ulong_long_value_);
+ case scan_type::string_type:
+ return vis(*string_);
+ case scan_type::string_view_type:
+ return vis(*string_view_);
+ case scan_type::custom_type:
+ break;
+ }
+ return vis(monostate());
+ }
+
+ auto scan_custom(const char* parse_begin, scan_parse_context& parse_ctx,
+ Context& ctx) const -> bool {
+ if (type_ != scan_type::custom_type) return false;
+ parse_ctx.advance_to(parse_begin);
+ custom_.scan(custom_.value, parse_ctx, ctx);
+ return true;
+ }
};
-} // namespace detail
+
+class scan_context;
+using scan_arg = basic_scan_arg<scan_context>;
struct scan_args {
int size;
- const detail::scan_arg* data;
+ const scan_arg* data;
template <size_t N>
- FMT_CONSTEXPR scan_args(const std::array<detail::scan_arg, N>& store)
+ FMT_CONSTEXPR scan_args(const std::array<scan_arg, N>& store)
: size(N), data(store.data()) {
static_assert(N < INT_MAX, "too many arguments");
}
};
+class scan_context {
+ private:
+ detail::scan_buffer& buf_;
+ scan_args args_;
+
+ public:
+ using iterator = detail::scan_iterator;
+ using sentinel = detail::scan_sentinel;
+
+ explicit FMT_CONSTEXPR scan_context(detail::scan_buffer& buf, scan_args args)
+ : buf_(buf), args_(args) {}
+
+ FMT_CONSTEXPR auto arg(int id) const -> scan_arg {
+ return id < args_.size ? args_.data[id] : scan_arg();
+ }
+
+ auto begin() const -> iterator { return buf_.begin(); }
+ auto end() const -> sentinel { return {}; }
+
+ void advance_to(iterator) { buf_.consume(); }
+};
+
namespace detail {
+const char* parse_scan_specs(const char* begin, const char* end,
+ format_specs<>& specs, scan_type) {
+ while (begin != end) {
+ switch (to_ascii(*begin)) {
+ // TODO: parse more scan format specifiers
+ case 'x':
+ specs.type = presentation_type::hex_lower;
+ ++begin;
+ break;
+ case '}':
+ return begin;
+ }
+ }
+ return begin;
+}
+
+template <typename T, FMT_ENABLE_IF(std::is_unsigned<T>::value)>
+auto read(scan_iterator it, T& value)
+ -> scan_iterator {
+ if (it == scan_sentinel()) return it;
+ char c = *it;
+ if (c < '0' || c > '9') throw_format_error("invalid input");
+
+ int num_digits = 0;
+ T n = 0, prev = 0;
+ char prev_digit = c;
+ do {
+ prev = n;
+ n = n * 10 + static_cast<unsigned>(c - '0');
+ prev_digit = c;
+ c = *++it;
+ ++num_digits;
+ if (c < '0' || c > '9') break;
+ } while (it != scan_sentinel());
+
+ // Check overflow.
+ if (num_digits <= std::numeric_limits<int>::digits10) {
+ value = n;
+ return it;
+ }
+ unsigned max = to_unsigned((std::numeric_limits<int>::max)());
+ if (num_digits == std::numeric_limits<int>::digits10 + 1 &&
+ prev * 10ull + unsigned(prev_digit - '0') <= max) {
+ value = n;
+ } else {
+ throw_format_error("number is too big");
+ }
+ return it;
+}
+
+template <typename T, FMT_ENABLE_IF(std::is_unsigned<T>::value)>
+auto read_hex(scan_iterator it, T& value)
+ -> scan_iterator {
+ if (it == scan_sentinel()) return it;
+ int digit = to_hex_digit(*it);
+ if (digit < 0) throw_format_error("invalid input");
+
+ int num_digits = 0;
+ T n = 0;
+ do {
+ n = (n << 4) + static_cast<unsigned>(digit);
+ ++num_digits;
+ digit = to_hex_digit(*++it);
+ if (digit < 0) break;
+ } while (it != scan_sentinel());
+
+ // Check overflow.
+ if (num_digits <= (std::numeric_limits<T>::digits >> 2))
+ value = n;
+ else
+ throw_format_error("number is too big");
+ return it;
+}
+
+template <typename T, FMT_ENABLE_IF(std::is_unsigned<T>::value)>
+auto read(scan_iterator it, T& value, const format_specs<>& specs)
+ -> scan_iterator {
+ if (specs.type == presentation_type::hex_lower)
+ return read_hex(it, value);
+ return read(it, value);
+}
+
+template <typename T, FMT_ENABLE_IF(std::is_signed<T>::value)>
+auto read(scan_iterator it, T& value, const format_specs<>& = {})
+ -> scan_iterator {
+ bool negative = it != scan_sentinel() && *it == '-';
+ if (negative) {
+ ++it;
+ if (it == scan_sentinel()) throw_format_error("invalid input");
+ }
+ using unsigned_type = typename std::make_unsigned<T>::type;
+ unsigned_type abs_value = 0;
+ it = read(it, abs_value);
+ auto n = static_cast<T>(abs_value);
+ value = negative ? -n : n;
+ return it;
+}
+
+auto read(scan_iterator it, std::string& value, const format_specs<>& = {})
+ -> scan_iterator {
+ while (it != scan_sentinel() && *it != ' ') value.push_back(*it++);
+ return it;
+}
+
+auto read(scan_iterator it, string_view& value, const format_specs<>& = {})
+ -> scan_iterator {
+ auto range = to_contiguous(it);
+ // This could also be checked at compile time in scan.
+ if (!range) throw_format_error("string_view requires contiguous input");
+ auto p = range.begin;
+ while (p != range.end && *p != ' ') ++p;
+ size_t size = to_unsigned(p - range.begin);
+ value = {range.begin, size};
+ return advance(it, size);
+}
+
+auto read(scan_iterator it, monostate, const format_specs<>& = {})
+ -> scan_iterator {
+ return it;
+}
+
+// An argument scanner that uses the default format, e.g. decimal for integers.
+struct default_arg_scanner {
+ scan_iterator it;
+
+ template <typename T> FMT_INLINE auto operator()(T&& value) -> scan_iterator {
+ return read(it, value);
+ }
+};
+
+// An argument scanner with format specifiers.
+struct arg_scanner {
+ scan_iterator it;
+ const format_specs<>& specs;
+
+ template <typename T> auto operator()(T&& value) -> scan_iterator {
+ return read(it, value, specs);
+ }
+};
+
struct scan_handler : error_handler {
private:
scan_parse_context parse_ctx_;
scan_context scan_ctx_;
- scan_args args_;
int next_arg_id_;
- scan_arg arg_;
-
- template <typename T = unsigned> T read_uint() {
- T value = 0;
- auto it = scan_ctx_.begin(), end = scan_ctx_.end();
- while (it != end) {
- char c = *it++;
- if (c < '0' || c > '9') on_error("invalid input");
- // TODO: check overflow
- value = value * 10 + static_cast<unsigned>(c - '0');
- }
- scan_ctx_.advance_to(it);
- return value;
- }
- template <typename T = int> T read_int() {
- auto it = scan_ctx_.begin(), end = scan_ctx_.end();
- bool negative = it != end && *it == '-';
- if (negative) ++it;
- scan_ctx_.advance_to(it);
- const auto value = read_uint<typename std::make_unsigned<T>::type>();
- if (negative) return -static_cast<T>(value);
- return static_cast<T>(value);
- }
+ using sentinel = scan_buffer::sentinel;
public:
- FMT_CONSTEXPR scan_handler(string_view format, string_view input,
+ FMT_CONSTEXPR scan_handler(string_view format, scan_buffer& buf,
scan_args args)
- : parse_ctx_(format), scan_ctx_(input), args_(args), next_arg_id_(0) {}
+ : parse_ctx_(format), scan_ctx_(buf, args), next_arg_id_(0) {}
- const char* pos() const { return scan_ctx_.begin(); }
+ auto pos() const -> scan_buffer::iterator { return scan_ctx_.begin(); }
void on_text(const char* begin, const char* end) {
- auto size = to_unsigned(end - begin);
+ if (begin == end) return;
auto it = scan_ctx_.begin();
- if (it + size > scan_ctx_.end() || !std::equal(begin, end, it))
- on_error("invalid input");
- scan_ctx_.advance_to(it + size);
+ for (; begin != end; ++begin, ++it) {
+ if (it == sentinel() || *begin != *it) on_error("invalid input");
+ }
+ scan_ctx_.advance_to(it);
}
- FMT_CONSTEXPR int on_arg_id() { return on_arg_id(next_arg_id_++); }
- FMT_CONSTEXPR int on_arg_id(int id) {
- if (id >= args_.size) on_error("argument index out of range");
- arg_ = args_.data[id];
+ FMT_CONSTEXPR auto on_arg_id() -> int { return on_arg_id(next_arg_id_++); }
+ FMT_CONSTEXPR auto on_arg_id(int id) -> int {
+ if (!scan_ctx_.arg(id)) on_error("argument index out of range");
return id;
}
- FMT_CONSTEXPR int on_arg_id(string_view id) {
+ FMT_CONSTEXPR auto on_arg_id(string_view id) -> int {
if (id.data()) on_error("invalid format");
return 0;
}
- void on_replacement_field(int, const char*) {
- auto it = scan_ctx_.begin(), end = scan_ctx_.end();
- switch (arg_.type) {
- case scan_type::int_type:
- *arg_.int_value = read_int();
- break;
- case scan_type::uint_type:
- *arg_.uint_value = read_uint();
- break;
- case scan_type::long_long_type:
- *arg_.long_long_value = read_int<long long>();
- break;
- case scan_type::ulong_long_type:
- *arg_.ulong_long_value = read_uint<unsigned long long>();
- break;
- case scan_type::string_type:
- while (it != end && *it != ' ') arg_.string->push_back(*it++);
- scan_ctx_.advance_to(it);
- break;
- case scan_type::string_view_type: {
- auto s = it;
- while (it != end && *it != ' ') ++it;
- *arg_.string_view = fmt::string_view(s, to_unsigned(it - s));
- scan_ctx_.advance_to(it);
- break;
- }
- case scan_type::none_type:
- case scan_type::custom_type:
- assert(false);
- }
+ void on_replacement_field(int arg_id, const char*) {
+ scan_arg arg = scan_ctx_.arg(arg_id);
+ auto it = scan_ctx_.begin();
+ while (it != sentinel() && is_whitespace(*it)) ++it;
+ scan_ctx_.advance_to(arg.visit(default_arg_scanner{it}));
}
- const char* on_format_specs(int, const char* begin, const char*) {
- if (arg_.type != scan_type::custom_type) return begin;
- parse_ctx_.advance_to(begin);
- arg_.custom.scan(arg_.custom.value, parse_ctx_, scan_ctx_);
- return parse_ctx_.begin();
+ auto on_format_specs(int arg_id, const char* begin, const char* end) -> const
+ char* {
+ scan_arg arg = scan_ctx_.arg(arg_id);
+ if (arg.scan_custom(begin, parse_ctx_, scan_ctx_))
+ return parse_ctx_.begin();
+ auto specs = format_specs<>();
+ begin = parse_scan_specs(begin, end, specs, arg.type());
+ if (begin == end || *begin != '}') on_error("missing '}' in format string");
+ scan_ctx_.advance_to(arg.visit(arg_scanner{scan_ctx_.begin(), specs}));
+ return begin;
}
+
+ void on_error(const char* message) { error_handler::on_error(message); }
};
} // namespace detail
-template <typename... Args>
-std::array<detail::scan_arg, sizeof...(Args)> make_scan_args(Args&... args) {
+template <typename... T>
+auto make_scan_args(T&... args) -> std::array<scan_arg, sizeof...(T)> {
return {{args...}};
}
-string_view::iterator vscan(string_view input, string_view format_str,
- scan_args args) {
- detail::scan_handler h(format_str, input, args);
- detail::parse_format_string<false>(format_str, h);
- return input.begin() + (h.pos() - &*input.begin());
+void vscan(detail::scan_buffer& buf, string_view fmt, scan_args args) {
+ auto h = detail::scan_handler(fmt, buf, args);
+ detail::parse_format_string<false>(fmt, h);
+}
+
+template <typename... T>
+auto scan(string_view input, string_view fmt, T&... args)
+ -> string_view::iterator {
+ auto&& buf = detail::string_scan_buffer(input);
+ vscan(buf, fmt, make_scan_args(args...));
+ return input.begin() + (buf.begin().base() - input.data());
}
-template <typename... Args>
-string_view::iterator scan(string_view input, string_view format_str,
- Args&... args) {
- return vscan(input, format_str, make_scan_args(args...));
+template <typename InputRange, typename... T,
+ FMT_ENABLE_IF(!std::is_convertible<InputRange, string_view>::value)>
+auto scan(InputRange&& input, string_view fmt, T&... args)
+ -> decltype(std::begin(input)) {
+ auto it = std::begin(input);
+ vscan(get_buffer(it), fmt, make_scan_args(args...));
+ return it;
}
+
+template <typename... T> bool scan(std::FILE* f, string_view fmt, T&... args) {
+ auto&& buf = detail::file_scan_buffer(f);
+ vscan(buf, fmt, make_scan_args(args...));
+ return buf.begin() != buf.end();
+}
+
FMT_END_NAMESPACE
diff --git a/test/std-test.cc b/test/std-test.cc
index 41183dbf..de3feaa0 100644
--- a/test/std-test.cc
+++ b/test/std-test.cc
@@ -26,10 +26,13 @@ TEST(std_test, path) {
EXPECT_EQ(fmt::format("{}", path("foo\"bar")), "foo\"bar");
EXPECT_EQ(fmt::format("{:?}", path("foo\"bar")), "\"foo\\\"bar\"");
+ EXPECT_EQ(fmt::format("{:g}", path("/usr/bin")), "/usr/bin");
# ifdef _WIN32
- EXPECT_EQ(fmt::format("{}", path(
- L"\x0428\x0447\x0443\x0447\x044B\x043D\x0448"
- L"\x0447\x044B\x043D\x0430")),
+ EXPECT_EQ(fmt::format("{}", path("C:\\foo")), "C:\\foo");
+ EXPECT_EQ(fmt::format("{:g}", path("C:\\foo")), "C:/foo");
+
+ 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")), "�");
EXPECT_EQ(fmt::format("{:?}", path(L"\xd800")), "\"\\ud800\"");
@@ -62,6 +65,15 @@ TEST(std_test, thread_id) {
EXPECT_FALSE(fmt::format("{}", std::this_thread::get_id()).empty());
}
+#ifdef __cpp_lib_source_location
+TEST(std_test, source_location) {
+ std::source_location loc = std::source_location::current();
+ EXPECT_EQ(fmt::format("{}", loc),
+ fmt::format("{}:{}:{}: {}", loc.file_name(), loc.line(),
+ loc.column(), loc.function_name()));
+}
+#endif
+
TEST(std_test, optional) {
#ifdef __cpp_lib_optional
EXPECT_EQ(fmt::format("{}", std::optional<int>{}), "none");
@@ -90,6 +102,36 @@ TEST(std_test, optional) {
#endif
}
+namespace my_nso {
+enum class my_number {
+ one,
+ two,
+};
+auto format_as(my_number number) -> fmt::string_view {
+ return number == my_number::one ? "first" : "second";
+}
+
+class my_class {
+ public:
+ int av;
+
+ private:
+ friend auto format_as(const my_class& elm) -> std::string {
+ return fmt::to_string(elm.av);
+ }
+};
+} // namespace my_nso
+TEST(std_test, optional_format_as) {
+#ifdef __cpp_lib_optional
+ EXPECT_EQ(fmt::format("{}", std::optional<my_nso::my_number>{}), "none");
+ EXPECT_EQ(fmt::format("{}", std::optional{my_nso::my_number::one}),
+ "optional(\"first\")");
+ EXPECT_EQ(fmt::format("{}", std::optional<my_nso::my_class>{}), "none");
+ EXPECT_EQ(fmt::format("{}", std::optional{my_nso::my_class{7}}),
+ "optional(\"7\")");
+#endif
+}
+
struct throws_on_move {
throws_on_move() = default;
@@ -255,10 +297,10 @@ TEST(std_test, format_atomic) {
#ifdef __cpp_lib_atomic_flag_test
TEST(std_test, format_atomic_flag) {
std::atomic_flag f = ATOMIC_FLAG_INIT;
- (void) f.test_and_set();
+ (void)f.test_and_set();
EXPECT_EQ(fmt::format("{}", f), "true");
const std::atomic_flag cf = ATOMIC_FLAG_INIT;
EXPECT_EQ(fmt::format("{}", cf), "false");
}
-#endif // __cpp_lib_atomic_flag_test
+#endif // __cpp_lib_atomic_flag_test
diff --git a/test/util.cc b/test/util.cc
index 4ff34a91..d3f2dc73 100644
--- a/test/util.cc
+++ b/test/util.cc
@@ -39,6 +39,11 @@ std::locale get_locale(const char* name, const char* alt_name) {
auto loc = do_get_locale(name);
if (loc == std::locale::classic() && alt_name)
loc = do_get_locale(alt_name);
+#ifdef __OpenBSD__
+ // Locales are not working in OpenBSD:
+ // https://github.com/fmtlib/fmt/issues/3670.
+ loc = std::locale::classic();
+#endif
if (loc == std::locale::classic())
fmt::print(stderr, "{} locale is missing.\n", name);
return loc;
diff --git a/test/util.h b/test/util.h
index 9120e22e..803cdeea 100644
--- a/test/util.h
+++ b/test/util.h
@@ -29,9 +29,9 @@ void safe_sprintf(char (&buffer)[SIZE], const char* format, ...) {
extern const char* const file_content;
// Opens a buffered file for reading.
-fmt::buffered_file open_buffered_file(FILE** fp = nullptr);
+auto open_buffered_file(FILE** fp = nullptr) -> fmt::buffered_file;
-inline FILE* safe_fopen(const char* filename, const char* mode) {
+inline auto safe_fopen(const char* filename, const char* mode) -> FILE* {
#if defined(_WIN32) && !defined(__MINGW32__)
// Fix MSVC warning about "unsafe" fopen.
FILE* f = nullptr;
@@ -51,17 +51,17 @@ template <typename Char> class basic_test_string {
public:
explicit basic_test_string(const Char* value = empty) : value_(value) {}
- const std::basic_string<Char>& value() const { return value_; }
+ auto value() const -> const std::basic_string<Char>& { return value_; }
};
template <typename Char> const Char basic_test_string<Char>::empty[] = {0};
-typedef basic_test_string<char> test_string;
-typedef basic_test_string<wchar_t> test_wstring;
+using test_string = basic_test_string<char>;
+using test_wstring = basic_test_string<wchar_t>;
template <typename Char>
-std::basic_ostream<Char>& operator<<(std::basic_ostream<Char>& os,
- const basic_test_string<Char>& s) {
+auto operator<<(std::basic_ostream<Char>& os, const basic_test_string<Char>& s)
+ -> std::basic_ostream<Char>& {
os << s.value();
return os;
}
@@ -72,10 +72,12 @@ class date {
public:
date(int year, int month, int day) : year_(year), month_(month), day_(day) {}
- int year() const { return year_; }
- int month() const { return month_; }
- int day() const { return day_; }
+ auto year() const -> int { return year_; }
+ auto month() const -> int { return month_; }
+ auto day() const -> int { return day_; }
};
-// Returns a locale with the given name if available or classic locale otherwise.
-std::locale get_locale(const char* name, const char* alt_name = nullptr);
+// Returns a locale with the given name if available or classic locale
+// otherwise.
+auto get_locale(const char* name, const char* alt_name = nullptr)
+ -> std::locale;
diff --git a/test/xchar-test.cc b/test/xchar-test.cc
index f72e94dc..7f33fb27 100644
--- a/test/xchar-test.cc
+++ b/test/xchar-test.cc
@@ -187,18 +187,6 @@ template <typename S> std::string from_u8str(const S& str) {
return std::string(str.begin(), str.end());
}
-TEST(xchar_test, format_utf8_precision) {
- using str_type = std::basic_string<fmt::detail::char8_type>;
- auto format =
- str_type(reinterpret_cast<const fmt::detail::char8_type*>(u8"{:.4}"));
- auto str = str_type(reinterpret_cast<const fmt::detail::char8_type*>(
- u8"caf\u00e9s")); // cafés
- auto result = fmt::format(format, str);
- EXPECT_EQ(fmt::detail::compute_width(result), 4);
- EXPECT_EQ(result.size(), 5);
- EXPECT_EQ(from_u8str(result), from_u8str(str.substr(0, 5)));
-}
-
TEST(xchar_test, format_to) {
auto buf = std::vector<wchar_t>();
fmt::format_to(std::back_inserter(buf), L"{}{}", 42, L'\0');
@@ -581,7 +569,7 @@ template <class charT> struct formatter<std::complex<double>, charT> {
basic_format_parse_context<charT>& ctx) {
auto end = parse_format_specs(ctx.begin(), ctx.end(), specs_, ctx,
detail::type::float_type);
- detail::parse_float_type_spec(specs_, detail::error_handler());
+ detail::parse_float_type_spec(specs_);
return end;
}
@@ -599,9 +587,9 @@ template <class charT> struct formatter<std::complex<double>, charT> {
fmt::runtime("{:" + specs + "}"), c.imag());
auto fill_align_width = std::string();
if (specs_.width > 0) fill_align_width = fmt::format(">{}", specs_.width);
- return format_to(ctx.out(), runtime("{:" + fill_align_width + "}"),
- c.real() != 0 ? fmt::format("({}+{}i)", real, imag)
- : fmt::format("{}i", imag));
+ return fmt::format_to(ctx.out(), runtime("{:" + fill_align_width + "}"),
+ c.real() != 0 ? fmt::format("({}+{}i)", real, imag)
+ : fmt::format("{}i", imag));
}
};
FMT_END_NAMESPACE