Compare commits

...

179 Commits
text ... 6.1.2

Author SHA1 Message Date
f94b7364b9 Update version 2019-12-11 06:16:42 -08:00
7abec071b5 Update changelog 2019-12-11 06:15:06 -08:00
b7eb8c8921 Prepare for the next release 2019-12-10 21:50:14 -08:00
ae7c50185d Reintroduce sprintf_format for ABI compatibility 2019-12-10 20:44:08 -08:00
9f2e7edaeb Fix handling of types convertible to std::string_view 2019-12-09 13:25:08 -08:00
fd52de0c6b Add FMT_CUDA_TEST CMake option to enable cuda-test 2019-12-09 07:30:34 -08:00
f675cb887e Remove redundant cast 2019-12-08 18:01:59 -08:00
73a16b827f Fix handling of int128_t in format-impl-test (#1461) 2019-12-08 17:07:20 -08:00
72879db40e Clean-up sign-conversion warnings in public headers 2019-12-08 16:07:55 -08:00
d3aa0c3a28 Clean-up sign-conversion warnings in test code 2019-12-08 16:07:43 -08:00
31de9a1b80 Revert "Clean-up sign-conversion warnings in test code"
This reverts commit 227bfe62dd.
2019-12-08 15:47:24 -08:00
227bfe62dd Clean-up sign-conversion warnings in test code 2019-12-08 15:21:38 -08:00
95dfdc6cc4 Update README.rst 2019-12-07 10:12:56 -08:00
5916ff63c4 Update README.rst 2019-12-07 10:12:15 -08:00
1ab80aa92c Fix handling of types with custom formatters that are convertible to std::string_view 2019-12-06 11:40:21 -08:00
4f4d876616 Remove '%' from the docs 2019-12-06 07:06:19 -08:00
f443bd3baf Ditch decimal_formatter (#1363) 2019-12-05 19:07:45 -08:00
1219b65f21 Relax fallthrough attribute detection 2019-12-05 10:40:15 -08:00
071794ec65 Update version 2019-12-04 12:21:48 -08:00
d22e4ad85b Remove trailing comma 2019-12-04 12:20:52 -08:00
983806b0c1 Update changelog 2019-12-04 12:03:44 -08:00
02af5beb8a Bump version and update changelog 2019-12-04 10:22:07 -08:00
123e7f7fc3 Revert #1433 because of build failures (#1450) 2019-12-03 09:24:15 -08:00
168460f02c Remove TYPES 2019-12-03 06:45:00 -08:00
a64f60c849 Remove unneeded FMT_API. 2019-12-03 05:55:04 -08:00
1a599117d8 Export assert_fail with FMT_API. This fixes dll build. 2019-12-03 05:55:04 -08:00
b160123e39 Update ChangeLog.rst 2019-12-02 16:18:06 -08:00
5981588565 Fix compilation with MinGW
Commit 3bc28fcc6b ("Squelch MSVC warning exporting subclasses of
runtime_error", 2019-11-29) silenced a MSVC warning under. The MinGW
compiler also defines _WIN32, but does not support the "warning" pragma.

Introduce a helper macro to squelch the MSVC warning only when using the
Microsoft compiler.

Signed-off-by: Beat Bolli <dev@drbeat.li>
2019-12-02 12:56:46 -08:00
8bbe76af3a Add a missing decimal point in exponent notation with trailing zeros 2019-12-02 11:36:33 -08:00
4ca6821e8f Update version 2019-12-01 16:58:40 -08:00
7111a1eb9f Bump version 2019-12-01 16:50:07 -08:00
ae00bbdc91 Update changelog 2019-12-01 16:39:58 -08:00
e71e07d9fb Update changlog 2019-12-01 16:26:07 -08:00
0184df7020 Update docs 2019-12-01 14:49:56 -08:00
1cbae6e9ba Put vprint declarations in one place 2019-12-01 14:43:51 -08:00
159f89e2b8 Fixing installation directory of '*.dll' files on Windows 2019-12-01 14:36:21 -08:00
4b120b68ae Clean up includes 2019-12-01 10:19:07 -08:00
186b225d9d Update changlog 2019-12-01 10:16:01 -08:00
4cbf4888ea Update changelog 2019-12-01 10:04:38 -08:00
e31f2b3d03 Update changelog 2019-12-01 09:27:28 -08:00
62da1db62a Avoid wchar_t instantiations 2019-12-01 07:34:09 -08:00
3bc28fcc6b Squelch MSVC warning exporting subclasses of runtime_error
When compiling {fmt} as a DLL, MSVC complains that we are exporting
classes that inherit from "std::runtime_error", which we are not
exporting.

In this case, it's not really a problem because that symbol is already
exported via the C++ stdlib. So we just add a pragma to silence the
warning.
2019-11-30 12:00:25 -08:00
3c05fa46c6 Update changelog 2019-11-30 09:18:16 -08:00
ba6e330fd3 digits -> num_bits 2019-11-30 08:41:07 -08:00
6037b3cae9 Fix dangling else problem in FMT_ASSERT 2019-11-30 07:52:33 -08:00
fafb03fa6d Fix handling of fallback_uintptr 2019-11-30 07:31:38 -08:00
2f9acd1838 Remove dependency on <cassert> 2019-11-29 09:37:18 -08:00
aaf829bfb1 Fix fallback pointer formatting on big endian, take 2 2019-11-29 07:07:08 -08:00
b994a0ab13 Fix handling of missing fraction in snprintf_float 2019-11-29 06:17:29 -08:00
bb205d940d Fix fallback pointer formatting on big endian 2019-11-29 05:15:59 -08:00
ef7369ce90 Update docs 2019-11-28 08:09:46 -08:00
40e4c227db Update changelog 2019-11-28 07:43:22 -08:00
ea54b21e78 Remove invalid noexcept annotation
buffered_file& operator=(buffered_file&& other) calls close which can
throw.
2019-11-28 06:43:08 -08:00
9cbf4b087c Fix -Wconversion warnings 2019-11-28 06:43:08 -08:00
1200a34e10 Update changelog 2019-11-27 16:15:42 -08:00
9c7e2a6c6f Add missing newline 2019-11-27 11:06:40 -08:00
34e921f6fe Update docs 2019-11-27 09:26:32 -08:00
c3be0f593d Refactor floating-point formatting 2019-11-27 08:08:22 -08:00
c68703c9f4 float_spec -> float_specs 2019-11-26 15:53:24 -08:00
9a21728b0a Remove gen_digits_params 2019-11-26 11:27:53 -08:00
3de36e9348 Enable -Wswitch-enum in CI 2019-11-26 10:17:48 -08:00
4afb39bc24 Update README.rst 2019-11-26 08:33:16 -08:00
7ffa62db18 Fix precision handling in snprintf_float 2019-11-25 20:00:10 -08:00
0d07db1234 Fix handling of streamable and convertible to string types 2019-11-25 16:46:33 -08:00
d19ed6716d Fix hexfloat buffer reallocation 2019-11-25 15:54:04 -08:00
99b6e928d4 Fix handling of types with deleted rvalue conversion to string (#1421) 2019-11-25 08:30:47 -08:00
57cd3f72e9 Update comment 2019-11-24 15:34:33 -08:00
111fc127fe Remove fp::operator- 2019-11-24 13:28:15 -08:00
6003ec3f25 Simplify Grisu implementation 2019-11-24 13:24:28 -08:00
8877a67724 Instantiate snprintf_float 2019-11-24 08:57:39 -08:00
75fff1db64 Minor cleanup 2019-11-24 08:43:56 -08:00
28d7191c27 Don't print trailing zero with fixed, precision=0, and showpoint (#1417) 2019-11-24 08:23:10 -08:00
43271ba8e8 Handle null terminator at the end of the buffer 2019-11-24 07:15:25 -08:00
63a9f3fcd4 fix bad oss fuzz link in the oss-fuzz badge 2019-11-23 12:37:59 -08:00
4cf59ce734 Integrate Grisu and sprintf digit generators 2019-11-23 06:56:29 -08:00
7395472dde Refactor floating-point formatting 2019-11-22 18:49:19 -08:00
9108b25da9 Merge branch 'float' 2019-11-22 08:54:28 -08:00
4d366c68b7 Merge branch 'master' of github.com:fmtlib/fmt 2019-11-22 08:53:58 -08:00
ded1e7679e Refactor floating point formatting 2019-11-22 08:22:22 -08:00
c7edd8e570 Cleanup FP formatting 2019-11-20 13:17:03 -08:00
75108a56f6 Don't print % for nan and inf 2019-11-20 12:26:48 -08:00
3e1f70fe02 Merge write_fp into write 2019-11-20 12:20:17 -08:00
125fc5e520 Update comment 2019-11-20 11:45:25 -08:00
6793ffc1d6 Update README.rst 2019-11-20 09:33:59 -08:00
f4fcc5fd28 Update README.rst 2019-11-20 09:31:11 -08:00
4de41aa655 Move basic_writer::write_fp to where it belongs 2019-11-20 08:22:34 -08:00
404a880bd4 Make parse_arg_id more readable 2019-11-20 08:12:23 -08:00
092d2dc7b2 Merge safe-duration-cast.h into chrono.h 2019-11-20 08:05:18 -08:00
093e554211 Remove redundant qualification 2019-11-20 07:44:34 -08:00
d0696b0aa6 warning C4456: declaration of 'num_digits' hides previous local declaration 2019-11-20 06:43:46 -08:00
66d7746bb3 Use grisu for exponent notation 2019-11-19 16:55:11 -08:00
e9bff78814 Don't parse % unless FMT_DEPRECATED_PERCENT is set 2019-11-19 11:54:18 -08:00
57b6f2966d Deprecate the fmt macro 2019-11-19 11:28:17 -08:00
d79493e5ee Remove Grisu2 2019-11-19 09:50:59 -08:00
78842ce0d6 test: add default constructor for a const value
This fixed a compilation error of the OS X 10.11.6 C++ compiler:

    ./fmt/test/format-test.cc:1861:16: error: default initialization of an object of const type 'const Answer' without a user-provided default constructor
      const Answer const_answer;
                   ^
                               {}
2019-11-19 08:32:35 -08:00
5420bcce2d Make % an opt-in to improve compatibility with std::format 2019-11-18 08:04:43 -08:00
56a2e2075c Refactor float spec parsing 2019-11-18 07:37:36 -08:00
ed117baa4f Replace bool with float_format and add exponential 2019-11-17 15:52:13 -08:00
f26446290b Move float_spec_handler to internal namespace and update asserts 2019-11-17 13:47:00 -08:00
7e1cb3237a Fix indentation 2019-11-17 13:17:43 -08:00
f67783d7e6 Clarify that numeric alignment is deprecated 2019-11-17 10:24:55 -08:00
1c6d85f7bb Apply coding conventions to examples 2019-11-17 10:15:16 -08:00
4a1da44f91 Apply coding conventions to examples 2019-11-17 10:14:06 -08:00
080b6899d2 Tweak the docs 2019-11-17 10:10:32 -08:00
c01ec54fde Document and clean basic_format_parse_context 2019-11-17 09:28:26 -08:00
b0c2ab93fa Bump version 2019-11-17 08:35:26 -08:00
9b7fe2a4a1 Don't use POSIX API on UWP 2019-11-17 08:29:08 -08:00
c58b7d9c2f Use overridden locale in ostream 2019-11-17 06:22:22 -08:00
ceff9b0b2e Tweak the docs 2019-11-16 07:14:57 -08:00
3dc8639f8a [docs] Added conda 2019-11-16 06:19:33 -08:00
dcde089b4e Improve POSIX API detection 2019-11-15 07:28:02 -08:00
2145a7bdcc Move has_formatter into the public fmt namespace. (#1407)
* Move has_formatter into the public fmt namespace.

This will allow users to do SFINAE-friendly checks for
the formattability of a type.

Fixes #1369
2019-11-14 07:08:24 -08:00
52ae134f84 Remove broken CI config 2019-11-14 07:06:38 -08:00
0d6dd0cc6a Correct basic_string_view from string ctor 2019-11-14 05:57:23 -08:00
1f918159ed [clang-tidy] Replace deprecated C headers
Found with modernize-deprecated-headers

Signed-off-by: Rosen Penev <rosenp@gmail.com>
2019-11-08 09:35:26 +00:00
6868f888b2 [clang-tidy] Add missing override
Found with hicpp-use-override

Signed-off-by: Rosen Penev <rosenp@gmail.com>
2019-11-08 09:35:26 +00:00
87cd545a1e [clang-tidy] Replace {} with = default
Found with hicpp-use-equals-default

Signed-off-by: Rosen Penev <rosenp@gmail.com>
2019-11-08 09:35:26 +00:00
12f9437e22 [clang-tidy] Use auto
Found with hicpp-use-auto

Signed-off-by: Rosen Penev <rosenp@gmail.com>
2019-11-08 09:35:26 +00:00
bb0c8bfea8 [clang-tidy] Add noexcept where move is used
Found with performance-noexcept-move-constructor

Signed-off-by: Rosen Penev <rosenp@gmail.com>
2019-11-08 09:35:26 +00:00
e6e8298904 [clang-tidy] Add parentheses to macro arguments
Found with bugprone-macro-parentheses

Signed-off-by: Rosen Penev <rosenp@gmail.com>
2019-11-08 09:35:26 +00:00
0f0848e4f4 [clang-tidy] Use braced init list
Found with modernize-return-braced-init-list

Signed-off-by: Rosen Penev <rosenp@gmail.com>
2019-11-08 09:35:26 +00:00
a1fb5c7337 [clang-tidy] Changes suffixes to uppercase
Found with hicpp-uppercase-literal-suffix

Signed-off-by: Rosen Penev <rosenp@gmail.com>
2019-11-08 09:35:26 +00:00
8a411c2bca [clang-tidy] Turn deleted function to public
Found with modernize-use-equals-delete

Signed-off-by: Rosen Penev <rosenp@gmail.com>
2019-11-08 09:35:26 +00:00
0047dc10a2 Mark apidoc as rst 2019-11-06 15:16:02 +00:00
263cdef8a0 Merge branch 'master' of github.com:fmtlib/fmt 2019-11-06 13:19:56 +00:00
d4ca54253a Update docs 2019-11-06 12:48:21 +00:00
5bb7b28e15 Document members 2019-11-05 10:43:18 +00:00
1409dfe76b Try fix CI 2019-11-05 10:39:14 +00:00
f1559e1d56 Use grouping() from locale for specifier 'n' 2019-11-05 07:22:12 +00:00
ffd05e65ed basic_parse_context -> basic_format_parse_context per standard and document 2019-11-05 07:13:58 +00:00
0889856d61 Fix UTF-8 truncation 2019-11-03 11:53:15 +00:00
d6eede9e08 Remove redundant ctor 2019-11-01 12:09:04 -07:00
213e09644f Workaround X11 madness (#1388) 2019-11-01 08:47:11 -07:00
6bfc9af8c9 Add double support to compile 2019-10-30 20:45:29 -07:00
3487f1b9cd Always inline grisu_gen_digits and disable grisu2 by default 2019-10-30 16:52:00 -07:00
791294d17b Apply get_cached_power optimization by jk-jeon 2019-10-30 08:07:01 -07:00
8e700619b7 Simplify format_handler 2019-10-30 07:02:38 -07:00
58c6f8c7f5 Make unsigned-integer-overflow sanitizer happy (#1377) 2019-10-28 14:41:09 -07:00
40414b3446 Don't emit trailing zeros in exponential notation (#1376) 2019-10-28 12:31:00 -07:00
b7a157401e Simplify grisu_writer 2019-10-23 13:52:09 -07:00
7aa58c30bf Simplify NVCC checks 2019-10-23 11:32:35 -07:00
8e9bffa986 clang-format 2019-10-23 11:20:21 -07:00
ce4d87acd4 Remove obsolete comment and clang-format 2019-10-23 11:15:43 -07:00
21acc2af43 Fix more Visual Studio 2019 pedantic warnings (#1371)
* format-inl.h(444,1): warning C4804: '>>': unsafe use of type 'bool' in operation
format.h(2808,1): warning C4127: conditional expression is constant

* More fixes for VS2019 pedantic warnings

* Fix "conditional expression is constant" VS2019 warning in more specific way

* Use const_check to silence constexpr warning
2019-10-22 17:13:03 -07:00
00669427df Patch compiler error when building using nvcc
If you compile using `nvcc` and pass the option `--expt-relaxed-constexpr` it will crash with an internal compiler error. This modification prevents using `constexpr` in `fmtlib` when compiling using `nvcc` and prevents the crash.
2019-10-22 08:34:52 -07:00
d39ebf3ff2 Optimize counting 2019-10-21 06:57:42 -07:00
6498bc6d31 Simplify grisu_writer 2019-10-20 19:29:24 -07:00
a967dcbe20 Improve handling of signs 2019-10-20 19:05:38 -07:00
8498bc97dd Initialize all the things 2019-10-20 17:53:18 -07:00
e2ea940673 Handle assymetric boundaries 2019-10-20 07:55:05 -07:00
2bc5585ff0 Fix computing lower boundaries for smallest normalized double 2019-10-18 17:56:52 -07:00
bb728a572a packed_arg_bitsize -> packed_arg_bits and remove packed_arg_mask 2019-10-18 10:06:57 -07:00
36d1390e67 Implement round half to even 2019-10-18 07:21:12 -07:00
599e0aef45 Support single precision floats in grisu formatting
Fixes #1336
2019-10-18 07:08:41 -07:00
91f7619cc9 Fix Visual Studio 2019 pedantic warning C4334: '<<': result of 32-bit shift implicitly converted to 64 bits (was 64-bit shift intended?) 2019-10-14 08:55:18 -07:00
c4dc6bef24 Apply clang-format 2019-10-13 18:31:09 -07:00
646966e973 Reduce bigint capacity 2019-10-13 14:06:38 -07:00
a5abe5d95c Handle negative exponent and nonnegative power 2019-10-13 13:16:09 -07:00
1cbc5fa6cb Handle negative exponent and rename value/pow10 to numerator/denominator 2019-10-13 12:50:48 -07:00
f7a5748fd3 Partially implement (FPP)^2 2019-10-13 09:28:35 -07:00
0e94b931a2 Fix a linkage error introduced by #1360 (#1362) 2019-10-13 08:23:47 -07:00
5e58eb97b1 Implement add_compare 2019-10-13 08:05:06 -07:00
3a15ea3ea5 Rename write_double to write_fp
It handles all floating point types, not just doubles.
2019-10-12 11:41:24 -07:00
b87ac4d840 Distinguish float from double 2019-10-12 11:41:24 -07:00
a927dda9bb Use words for packed constants 2019-10-12 11:41:24 -07:00
dd11d45847 Encode types using 5 bits
This is needed to support more than the current 16 types.
2019-10-12 11:41:24 -07:00
b55551f900 Implement more comparison operators 2019-10-12 09:22:24 -07:00
96f91428c6 Add defaulted copy and move operations to format_error and system_error (#1347)
* Avoid weak vtables by providing a private virtual member function

* Add warning Wweak-vtables to clang when FMT_PEDANTIC is on

* Add defaulted copy and move operations to format_error and system_error

Compiler generated copy operations are deprecated and move operations
are not generated altogether.

* Add warning Wdeprecated to clang when FMT_PEDANTIC is on
2019-10-11 10:44:20 -07:00
b732f28c00 Deduplicate color vformat and vprint
After #1351 they became essentially the same.
2019-10-11 10:42:11 -07:00
a82c1dc6d9 use memory_buffer to make color print behave atomic #1348 (#1351) 2019-10-10 08:28:56 -07:00
2730e90186 Fix compile error in printf with gcc9 (#1354) 2019-10-09 18:58:40 -07:00
e4d6d9d7c8 Implement divmod 2019-10-09 13:40:50 -07:00
a1079e9fd6 Fix undefined in format-test (#1349)
When `MoveCtor` performs `check_move_buffer`, the buffer allocator becomes null,
but then `MoveCtor` attempts to use it to allocate a dynamic buffer. This
succeeds nevertheless because a typical `std::allocator<char>::allocate` does
not use `this`, so it does not crash when `this` is null.

Fixes #1344
2019-10-08 15:42:51 -07:00
b66bb6b71f Fix undefined in core-test and printf-test (#1345)
* Fix undefined in core-test

Fixes "reference binding to null pointer" in BufferTest.Ctor

buffer.operator[] attempts to return a reference to `buffer.ptr_[0]` when `ptr_`
in `mock_buffer<int> buffer` is null.

* Fix undefined in printf-test

Fixes "signed integer overflow" in PrintfTest.Length

This occurs in `TestLength<long long>("ll")`, since its minimum value minus one
does not fit in long long.

* Fix undefined in printf %0$

Printf counts arguments from 1.

Fixes "shift exponent -4 is negative" in PrintfTest.InvalidArgIndex.

`do_get` is called with index -1 when `basic_printf_context.arg` is called with
id 4294967295 when basic_printf_context::get_arg subtracts 1 from arg_index 0 in
the format string "%0$d".
2019-10-08 06:28:39 -07:00
b60114533f Implement more bigint operations 2019-10-06 12:49:23 -07:00
c41cea8b18 Initial implementation of square 2019-10-05 16:37:52 -07:00
0c7650373c Fix handling of types convertible to std::string_view 2019-10-05 06:58:37 -07:00
0571013709 Repoint one more Python 2 link to Python 3
One more python reference, this one acknowledging the `str.format` function.
2019-10-01 11:44:36 -07:00
d2c9276fcd let README point to python 3 instead of 2 2019-09-29 08:30:47 -07:00
42 changed files with 2650 additions and 1700 deletions

View File

@ -92,45 +92,6 @@ matrix:
# g++ 4.8 on Linux with C++11
- env: COMPILER=g++-4.8 BUILD=Debug STANDARD=11
compiler: gcc
- name: Android NDK (Gradle)
language: android
addons:
apt:
update: true
sources:
- ubuntu-toolchain-r-test
packages:
- ninja-build
- curl
- tree
android:
components:
- tools
- platform-tools
- android-25 # 7.0
- android-27 # 8.1
- android-28 # 9.0
- build-tools-28.0.3
before_install:
# Install Gradle from https://sdkman.io/
- curl -s "https://get.sdkman.io" | bash > /dev/null
- source "$HOME/.sdkman/bin/sdkman-init.sh"
- sdk version
- sdk install gradle
- sdk use gradle
- gradle --version
install:
# Accept SDK Licenses + Install NDK
- yes | sdkmanager --update > /dev/null 2>&1
- sdkmanager ndk-bundle > /dev/null 2>&1
before_script:
- pushd ./support
script:
- gradle clean
- gradle assemble
after_success:
- popd;
- tree ./libs
before_script:
- if [[ "${TRAVIS_OS_NAME}" == "linux" ]]; then export CXX=${COMPILER}; fi

View File

@ -42,6 +42,7 @@ option(FMT_DOC "Generate the doc target." ${MASTER_PROJECT})
option(FMT_INSTALL "Generate the install target." ${MASTER_PROJECT})
option(FMT_TEST "Generate the test target." ${MASTER_PROJECT})
option(FMT_FUZZ "Generate the fuzz target." OFF)
option(FMT_CUDA_TEST "Generate the cuda-test target." OFF)
project(FMT CXX)
@ -78,7 +79,7 @@ if (CMAKE_CXX_COMPILER_ID MATCHES "GNU")
-Wcast-align -Wnon-virtual-dtor
-Wctor-dtor-privacy -Wdisabled-optimization
-Winvalid-pch -Woverloaded-virtual
-Wconversion
-Wconversion -Wswitch-enum
-Wno-ctor-dtor-privacy -Wno-format-nonliteral -Wno-shadow)
if (NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 4.6)
set(PEDANTIC_COMPILE_FLAGS ${PEDANTIC_COMPILE_FLAGS} -Wnoexcept
@ -98,7 +99,7 @@ endif ()
if (CMAKE_CXX_COMPILER_ID MATCHES "Clang")
set(PEDANTIC_COMPILE_FLAGS -Wall -Wextra -pedantic -Wconversion
-Wno-sign-conversion)
-Wno-sign-conversion -Wdeprecated -Wweak-vtables)
check_cxx_compiler_flag(-Wzero-as-null-pointer-constant HAS_NULLPTR_WARNING)
if (HAS_NULLPTR_WARNING)
set(PEDANTIC_COMPILE_FLAGS ${PEDANTIC_COMPILE_FLAGS}
@ -135,10 +136,8 @@ endif ()
include(CheckSymbolExists)
if (WIN32)
check_symbol_exists(open io.h HAVE_OPEN)
check_symbol_exists(_strtod_l "${strtod_l_headers}" HAVE_STRTOD_L)
else ()
check_symbol_exists(open fcntl.h HAVE_OPEN)
check_symbol_exists(strtod_l "${strtod_l_headers}" HAVE_STRTOD_L)
endif ()
@ -152,13 +151,8 @@ endfunction()
# Define the fmt library, its includes and the needed defines.
add_headers(FMT_HEADERS chrono.h color.h compile.h core.h format.h format-inl.h
locale.h ostream.h printf.h ranges.h
safe-duration-cast.h)
set(FMT_SOURCES src/format.cc)
if (HAVE_OPEN)
add_headers(FMT_HEADERS posix.h)
set(FMT_SOURCES ${FMT_SOURCES} src/posix.cc)
endif ()
locale.h ostream.h posix.h printf.h ranges.h)
set(FMT_SOURCES src/format.cc src/posix.cc)
add_library(fmt ${FMT_SOURCES} ${FMT_HEADERS} README.rst ChangeLog.rst)
add_library(fmt::fmt ALIAS fmt)
@ -259,7 +253,9 @@ if (FMT_INSTALL)
# Install the library and headers.
install(TARGETS ${INSTALL_TARGETS} EXPORT ${targets_export_name}
DESTINATION ${FMT_LIB_DIR})
LIBRARY DESTINATION ${FMT_LIB_DIR}
ARCHIVE DESTINATION ${FMT_LIB_DIR}
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR})
install(FILES $<TARGET_PDB_FILE:${INSTALL_TARGETS}>
DESTINATION ${FMT_LIB_DIR} OPTIONAL)

View File

@ -1,3 +1,285 @@
6.1.2 - 2019-12-11
------------------
* Fixed ABI compatibility with ``libfmt.so.6.0.0``
(`#1471 <https://github.com/fmtlib/fmt/issues/1471>`_).
* Fixed handling types convertible to ``std::string_view``
(`#1451 <https://github.com/fmtlib/fmt/pull/1451>`_).
Thanks `@denizevrenci (Deniz Evrenci) <https://github.com/denizevrenci>`_.
* Made CUDA test an opt-in enabled via the ``FMT_CUDA_TEST`` CMake option.
* Fixed sign conversion warnings
(`#1440 <https://github.com/fmtlib/fmt/pull/1440>`_).
Thanks `@0x8000-0000 (Florin Iucha) <https://github.com/0x8000-0000>`_.
6.1.1 - 2019-12-04
------------------
* Fixed shared library build on Windows
(`#1443 <https://github.com/fmtlib/fmt/pull/1443>`_,
`#1445 <https://github.com/fmtlib/fmt/issues/1445>`_,
`#1446 <https://github.com/fmtlib/fmt/pull/1446>`_,
`#1450 <https://github.com/fmtlib/fmt/issues/1450>`_).
Thanks `@egorpugin (Egor Pugin) <https://github.com/egorpugin>`_,
`@bbolli (Beat Bolli) <https://github.com/bbolli>`_.
* Added a missing decimal point in exponent notation with trailing zeros.
* Removed deprecated ``format_arg_store::TYPES``.
6.1.0 - 2019-12-01
------------------
* {fmt} now formats IEEE 754 ``float`` and ``double`` using the shortest decimal
representation with correct rounding by default:
.. code:: c++
#include <cmath>
#include <fmt/core.h>
int main() {
fmt::print("{}", M_PI);
}
prints ``3.141592653589793``.
* Made the fast binary to decimal floating-point formatter the default,
simplified it and improved performance. {fmt} is now 15 times faster than
libc++'s ``std::ostringstream``, 11 times faster than ``printf`` and 10%
faster than double-conversion on `dtoa-benchmark
<https://github.com/fmtlib/dtoa-benchmark>`_:
================== ========= =======
Function Time (ns) Speedup
================== ========= =======
ostringstream 1,346.30 1.00x
ostrstream 1,195.74 1.13x
sprintf 995.08 1.35x
doubleconv 99.10 13.59x
fmt 88.34 15.24x
================== ========= =======
.. image:: https://user-images.githubusercontent.com/576385/
69767160-cdaca400-112f-11ea-9fc5-347c9f83caad.png
* {fmt} no longer converts ``float`` arguments to ``double``. In particular this
improves the default (shortest) representation of floats and makes
``fmt::format`` consistent with ``std::format`` specs
(`#1336 <https://github.com/fmtlib/fmt/issues/1336>`_,
`#1353 <https://github.com/fmtlib/fmt/issues/1353>`_,
`#1360 <https://github.com/fmtlib/fmt/pull/1360>`_,
`#1361 <https://github.com/fmtlib/fmt/pull/1361>`_):
.. code:: c++
fmt::print("{}", 0.1f);
prints ``0.1`` instead of ``0.10000000149011612``.
Thanks `@orivej (Orivej Desh) <https://github.com/orivej>`_.
* Made floating-point formatting output consistent with ``printf``/iostreams
(`#1376 <https://github.com/fmtlib/fmt/issues/1376>`_,
`#1417 <https://github.com/fmtlib/fmt/issues/1417>`_).
* Added support for 128-bit integers
(`#1287 <https://github.com/fmtlib/fmt/pull/1287>`_):
.. code:: c++
fmt::print("{}", std::numeric_limits<__int128_t>::max());
prints ``170141183460469231731687303715884105727``.
Thanks `@denizevrenci (Deniz Evrenci) <https://github.com/denizevrenci>`_.
* The overload of ``print`` that takes ``text_style`` is now atomic, i.e. the
output from different threads doesn't interleave
(`#1351 <https://github.com/fmtlib/fmt/pull/1351>`_).
Thanks `@tankiJong (Tanki Zhang) <https://github.com/tankiJong>`_.
* Made compile time in the header-only mode ~20% faster by reducing the number
of template instantiations. ``wchar_t`` overload of ``vprint`` was moved from
``fmt/core.h`` to ``fmt/format.h``.
* Added an overload of ``fmt::join`` that works with tuples
(`#1322 <https://github.com/fmtlib/fmt/issues/1322>`_,
`#1330 <https://github.com/fmtlib/fmt/pull/1330>`_):
.. code:: c++
#include <tuple>
#include <fmt/ranges.h>
int main() {
std::tuple<char, int, float> t{'a', 1, 2.0f};
fmt::print("{}", t);
}
prints ``('a', 1, 2.0)``.
Thanks `@jeremyong (Jeremy Ong) <https://github.com/jeremyong>`_.
* Changed formatting of octal zero with prefix from "00" to "0":
.. code:: c++
fmt::print("{:#o}", 0);
prints ``0``.
* The locale is now passed to ostream insertion (``<<``) operators
(`#1406 <https://github.com/fmtlib/fmt/pull/1406>`_):
.. code:: c++
#include <fmt/locale.h>
#include <fmt/ostream.h>
struct S {
double value;
};
std::ostream& operator<<(std::ostream& os, S s) {
return os << s.value;
}
int main() {
auto s = fmt::format(std::locale("fr_FR.UTF-8"), "{}", S{0.42});
// s == "0,42"
}
Thanks `@dlaugt (Daniel Laügt) <https://github.com/dlaugt>`_.
* Locale-specific number formatting now uses grouping
(`#1393 <https://github.com/fmtlib/fmt/issues/1393>`_
`#1394 <https://github.com/fmtlib/fmt/pull/1394>`_).
Thanks `@skrdaniel <https://github.com/skrdaniel>`_.
* Fixed handling of types with deleted implicit rvalue conversion to
``const char**`` (`#1421 <https://github.com/fmtlib/fmt/issues/1421>`_):
.. code:: c++
struct mystring {
operator const char*() const&;
operator const char*() &;
operator const char*() const&& = delete;
operator const char*() && = delete;
};
mystring str;
fmt::print("{}", str); // now compiles
* Enums are now mapped to correct underlying types instead of ``int``
(`#1286 <https://github.com/fmtlib/fmt/pull/1286>`_).
Thanks `@agmt (Egor Seredin) <https://github.com/agmt>`_.
* Enum classes are no longer implicitly converted to ``int``
(`#1424 <https://github.com/fmtlib/fmt/issues/1424>`_).
* Added ``basic_format_parse_context`` for consistency with C++20
``std::format`` and deprecated ``basic_parse_context``.
* Fixed handling of UTF-8 in precision
(`#1389 <https://github.com/fmtlib/fmt/issues/1389>`_,
`#1390 <https://github.com/fmtlib/fmt/pull/1390>`_).
Thanks `@tajtiattila (Attila Tajti) <https://github.com/tajtiattila>`_.
* {fmt} can now be installed on Linux, macOS and Windows with
`Conda <https://docs.conda.io/en/latest/>`__ using its
`conda-forge <https://conda-forge.org>`__
`package <https://github.com/conda-forge/fmt-feedstock>`__
(`#1410 <https://github.com/fmtlib/fmt/pull/1410>`_)::
conda install -c conda-forge fmt
Thanks `@tdegeus (Tom de Geus) <https://github.com/tdegeus>`_.
* Added a CUDA test (`#1285 <https://github.com/fmtlib/fmt/pull/1285>`_,
`#1317 <https://github.com/fmtlib/fmt/pull/1317>`_).
Thanks `@luncliff (Park DongHa) <https://github.com/luncliff>`_ and
`@risa2000 <https://github.com/risa2000>`_.
* Improved documentation (`#1276 <https://github.com/fmtlib/fmt/pull/1276>`_,
`#1291 <https://github.com/fmtlib/fmt/issues/1291>`_,
`#1296 <https://github.com/fmtlib/fmt/issues/1296>`_,
`#1315 <https://github.com/fmtlib/fmt/pull/1315>`_,
`#1332 <https://github.com/fmtlib/fmt/pull/1332>`_,
`#1337 <https://github.com/fmtlib/fmt/pull/1337>`_,
`#1395 <https://github.com/fmtlib/fmt/issues/1395>`_
`#1418 <https://github.com/fmtlib/fmt/pull/1418>`_).
Thanks
`@waywardmonkeys (Bruce Mitchener) <https://github.com/waywardmonkeys>`_,
`@pauldreik (Paul Dreik) <https://github.com/pauldreik>`_,
`@jackoalan (Jack Andersen) <https://github.com/jackoalan>`_.
* Various code improvements
(`#1358 <https://github.com/fmtlib/fmt/pull/1358>`_,
`#1407 <https://github.com/fmtlib/fmt/pull/1407>`_).
Thanks `@orivej (Orivej Desh) <https://github.com/orivej>`_,
`@dpacbach (David P. Sicilia) <https://github.com/dpacbach>`_,
* Fixed compile-time format string checks for user-defined types
(`#1292 <https://github.com/fmtlib/fmt/issues/1292>`_).
* Worked around a false positive in ``unsigned-integer-overflow`` sanitizer
(`#1377 <https://github.com/fmtlib/fmt/issues/1377>`_).
* Fixed various warnings and compilation issues
(`#1273 <https://github.com/fmtlib/fmt/issues/1273>`_,
`#1278 <https://github.com/fmtlib/fmt/pull/1278>`_,
`#1280 <https://github.com/fmtlib/fmt/pull/1280>`_,
`#1281 <https://github.com/fmtlib/fmt/issues/1281>`_,
`#1288 <https://github.com/fmtlib/fmt/issues/1288>`_,
`#1290 <https://github.com/fmtlib/fmt/pull/1290>`_,
`#1301 <https://github.com/fmtlib/fmt/pull/1301>`_,
`#1305 <https://github.com/fmtlib/fmt/issues/1305>`_,
`#1306 <https://github.com/fmtlib/fmt/issues/1306>`_,
`#1309 <https://github.com/fmtlib/fmt/issues/1309>`_,
`#1312 <https://github.com/fmtlib/fmt/pull/1312>`_,
`#1313 <https://github.com/fmtlib/fmt/issues/1313>`_,
`#1316 <https://github.com/fmtlib/fmt/issues/1316>`_,
`#1319 <https://github.com/fmtlib/fmt/issues/1319>`_,
`#1320 <https://github.com/fmtlib/fmt/pull/1320>`_,
`#1326 <https://github.com/fmtlib/fmt/pull/1326>`_,
`#1328 <https://github.com/fmtlib/fmt/pull/1328>`_,
`#1344 <https://github.com/fmtlib/fmt/issues/1344>`_,
`#1345 <https://github.com/fmtlib/fmt/pull/1345>`_,
`#1347 <https://github.com/fmtlib/fmt/pull/1347>`_,
`#1349 <https://github.com/fmtlib/fmt/pull/1349>`_,
`#1354 <https://github.com/fmtlib/fmt/issues/1354>`_,
`#1362 <https://github.com/fmtlib/fmt/issues/1362>`_,
`#1366 <https://github.com/fmtlib/fmt/issues/1366>`_,
`#1364 <https://github.com/fmtlib/fmt/pull/1364>`_,
`#1370 <https://github.com/fmtlib/fmt/pull/1370>`_,
`#1371 <https://github.com/fmtlib/fmt/pull/1371>`_,
`#1385 <https://github.com/fmtlib/fmt/issues/1385>`_,
`#1388 <https://github.com/fmtlib/fmt/issues/1388>`_,
`#1397 <https://github.com/fmtlib/fmt/pull/1397>`_,
`#1414 <https://github.com/fmtlib/fmt/pull/1414>`_,
`#1416 <https://github.com/fmtlib/fmt/pull/1416>`_,
`#1422 <https://github.com/fmtlib/fmt/issues/1422>`_
`#1427 <https://github.com/fmtlib/fmt/pull/1427>`_,
`#1431 <https://github.com/fmtlib/fmt/issues/1431>`_,
`#1433 <https://github.com/fmtlib/fmt/pull/1433>`_).
Thanks `@hhb <https://github.com/hhb>`_,
`@gsjaardema (Greg Sjaardema) <https://github.com/gsjaardema>`_,
`@gabime (Gabi Melman) <https://github.com/gabime>`_,
`@neheb (Rosen Penev) <https://github.com/neheb>`_,
`@vedranmiletic (Vedran Miletić) <https://github.com/vedranmiletic>`_,
`@dkavolis (Daumantas Kavolis) <https://github.com/dkavolis>`_,
`@mwinterb <https://github.com/mwinterb>`_,
`@orivej (Orivej Desh) <https://github.com/orivej>`_,
`@denizevrenci (Deniz Evrenci) <https://github.com/denizevrenci>`_
`@leonklingele <https://github.com/leonklingele>`_,
`@chronoxor (Ivan Shynkarenka) <https://github.com/chronoxor>`_,
`@kent-tri <https://github.com/kent-tri>`_,
`@0x8000-0000 (Florin Iucha) <https://github.com/0x8000-0000>`_,
`@marti4d (Chris Martin) <https://github.com/marti4d>`_.
6.0.0 - 2019-08-26
------------------

View File

@ -7,14 +7,14 @@
.. image:: https://ci.appveyor.com/api/projects/status/ehjkiefde6gucy1v
:target: https://ci.appveyor.com/project/vitaut/fmt
.. image:: https://oss-fuzz-build-logs.storage.googleapis.com/badges/libfmt.svg
:alt: fmt is continuously fuzzed att oss-fuzz
:target: https://bugs.chromium.org/p/oss-fuzz/issues/list?colspec=ID%20Type%20Component%20Status%20Proj%20Reported%20Owner%20Summary&q=proj%3Dlibfmt&can=1
.. image:: https://img.shields.io/badge/stackoverflow-fmt-blue.svg
:alt: Ask questions at StackOverflow with the tag fmt
:target: http://stackoverflow.com/questions/tagged/fmt
.. image:: https://oss-fuzz-build-logs.storage.googleapis.com/badges/libfmt.svg
:alt: fmt is continuously fuzzed att oss-fuzz
:target: https://bugs.chromium.org/p/oss-fuzz/issues/list?colspec=ID%20Type%20Component%20Status%20Proj%20Reported%20Owner%20Summary&q=proj%3Dfmt&can=1
**{fmt}** is an open-source formatting library for C++.
It can be used as a safe and fast alternative to (s)printf and iostreams.
@ -28,7 +28,7 @@ Features
* Replacement-based `format API <https://fmt.dev/dev/api.html>`_ with
positional arguments for localization.
* `Format string syntax <https://fmt.dev/dev/syntax.html>`_ similar to the one
of `str.format <https://docs.python.org/2/library/stdtypes.html#str.format>`_
of `str.format <https://docs.python.org/3/library/stdtypes.html#str.format>`_
in Python.
* Safe `printf implementation
<https://fmt.dev/latest/api.html#printf-formatting>`_ including
@ -81,16 +81,15 @@ Check a format string at compile time:
.. code:: c++
// test.cc
#define FMT_STRING_ALIAS 1
#include <fmt/format.h>
std::string s = format(fmt("{2}"), 42);
std::string s = format(FMT_STRING("{2}"), 42);
.. code::
$ c++ -Iinclude -std=c++14 test.cc
...
test.cc:4:17: note: in instantiation of function template specialization 'fmt::v5::format<S, int>' requested here
std::string s = format(fmt("{2}"), 42);
std::string s = format(FMT_STRING("{2}"), 42);
^
include/fmt/core.h:778:19: note: non-constexpr function 'on_error' cannot be used in a constant expression
ErrorHandler::on_error(message);
@ -122,11 +121,10 @@ Format objects of user-defined types via a simple `extension API
template <>
struct fmt::formatter<date> {
template <typename ParseContext>
constexpr auto parse(ParseContext &ctx) { return ctx.begin(); }
constexpr auto parse(format_parse_context& ctx) { return ctx.begin(); }
template <typename FormatContext>
auto format(const date &d, FormatContext &ctx) {
auto format(const date& d, FormatContext& ctx) {
return format_to(ctx.out(), "{}-{}-{}", d.year, d.month, d.day);
}
};
@ -142,12 +140,12 @@ which take arbitrary arguments (`godbolt <https://godbolt.org/g/MHjHVf>`_):
.. code:: c++
// Prints formatted error message.
void vreport_error(const char *format, fmt::format_args args) {
void vreport_error(const char* format, fmt::format_args args) {
fmt::print("Error: ");
fmt::vprint(format, args);
}
template <typename... Args>
void report_error(const char *format, const Args & ... args) {
void report_error(const char* format, const Args & ... args) {
vreport_error(format, fmt::make_format_args(args...));
}
@ -166,18 +164,17 @@ Speed tests
================= ============= ===========
Library Method Run Time, s
================= ============= ===========
libc printf 1.01
libc++ std::ostream 3.04
{fmt} 1632f72 fmt::print 0.86
tinyformat 2.0.1 tfm::printf 3.23
Boost Format 1.67 boost::format 7.98
libc printf 1.04
libc++ std::ostream 3.05
{fmt} 6.1.1 fmt::print 0.75
Boost Format 1.67 boost::format 7.24
Folly Format folly::format 2.23
================= ============= ===========
{fmt} is the fastest of the benchmarked methods, ~17% faster than ``printf``.
{fmt} is the fastest of the benchmarked methods, ~35% faster than ``printf``.
The above results were generated by building ``tinyformat_test.cpp`` on macOS
10.14.3 with ``clang++ -O3 -DSPEED_TEST -DHAVE_FORMAT``, and taking the best of
10.14.6 with ``clang++ -O3 -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
@ -187,7 +184,7 @@ further details refer to the `source
formatting (`dtoa-benchmark <https://github.com/fmtlib/dtoa-benchmark>`_)
and as fast as `double-conversion <https://github.com/google/double-conversion>`_:
.. image:: https://user-images.githubusercontent.com/576385/54883977-9fe8c000-4e28-11e9-8bde-272d122e7c52.jpg
.. image:: https://user-images.githubusercontent.com/576385/69767160-cdaca400-112f-11ea-9fc5-347c9f83caad.png
:target: https://fmt.dev/unknown_mac64_clang10.0.html
Compile time and code bloat
@ -211,7 +208,6 @@ printf 2.6 29 26
printf+string 16.4 29 26
iostreams 31.1 59 55
{fmt} 19.0 37 34
tinyformat 44.0 103 97
Boost Format 91.9 226 203
Folly Format 115.7 101 88
============= =============== ==================== ==================
@ -232,14 +228,13 @@ printf 2.2 33 30
printf+string 16.0 33 30
iostreams 28.3 56 52
{fmt} 18.2 59 50
tinyformat 32.6 88 82
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 and tinyformat are
header-only libraries so they don't provide any linkage options.
compare formatting function overhead only. Boost Format is a
header-only library so it doesn't provide any linkage options.
Running the tests
~~~~~~~~~~~~~~~~~
@ -425,20 +420,6 @@ arguments. However it has significant limitations, citing its author:
It is also quite big and has a heavy dependency, STLSoft, which might be
too restrictive for using it in some projects.
Loki SafeFormat
~~~~~~~~~~~~~~~
SafeFormat is a formatting library which uses ``printf``-like format strings and
is type safe. It doesn't support user-defined types or positional arguments and
makes unconventional use of ``operator()`` for passing format arguments.
Tinyformat
~~~~~~~~~~
This library supports ``printf``-like format strings and is very small .
It doesn't support positional arguments and wrapping it in C++98 is somewhat
difficult. Tinyformat relies on iostreams which limits its performance.
Boost Spirit.Karma
~~~~~~~~~~~~~~~~~~
@ -508,7 +489,7 @@ Some ideas used in the implementation are borrowed from `Loki
<http://clang.llvm.org/doxygen/classclang_1_1Diagnostic.html>`_ in
`Clang <http://clang.llvm.org/>`_.
Format string syntax and the documentation are based on Python's `str.format
<http://docs.python.org/2/library/stdtypes.html#str.format>`_.
<https://docs.python.org/3/library/stdtypes.html#str.format>`_.
Thanks `Doug Turnbull <https://github.com/softwaredoug>`_ for his valuable
comments and contribution to the design of the type-safe API and
`Gregory Czajkowski <https://github.com/gcflymoto>`_ for implementing binary

View File

@ -84,7 +84,7 @@
<div class="jumbotron">
<div class="tb-container">
<h1>{fmt}</h1>
<p class="lead">Small, safe and fast formatting library</p>
<p class="lead">A modern formatting library</p>
<div class="btn-group" role="group">
{% set name = 'fmt' if version.split('.')[0]|int >= 3 else 'cppformat' %}
<a class="btn btn-success"

View File

@ -17,7 +17,7 @@ The {fmt} library API consists of the following parts:
* :ref:`fmt/printf.h <printf-api>`: ``printf`` formatting
All functions and types provided by the library reside in namespace ``fmt`` and
macros have prefix ``FMT_`` or ``fmt``.
macros have prefix ``FMT_``.
.. _core-api:
@ -52,7 +52,6 @@ participate in an overload resolution if the latter is not a string.
.. doxygenfunction:: print(std::FILE *, const S&, Args&&...)
.. doxygenfunction:: vprint(std::FILE *, string_view, format_args)
.. doxygenfunction:: vprint(std::FILE *, wstring_view, wformat_args)
Named Arguments
---------------
@ -97,8 +96,11 @@ string checks, output iterator and user-defined type support.
Compile-time Format String Checks
---------------------------------
Compile-time checks are supported for built-in and string types as well as
user-defined types with ``constexpr`` ``parse`` functions in their ``formatter``
specializations.
.. doxygendefine:: FMT_STRING
.. doxygendefine:: fmt
Formatting User-defined Types
-----------------------------
@ -110,32 +112,56 @@ template and implement ``parse`` and ``format`` methods::
struct point { double x, y; };
namespace fmt {
template <>
struct formatter<point> {
template <typename ParseContext>
constexpr auto parse(ParseContext &ctx) { return ctx.begin(); }
struct fmt::formatter<point> {
// Presentation format: 'f' - fixed, 'e' - exponential.
char presentation = 'f';
// Parses format specifications of the form ['f' | 'e'].
constexpr auto parse(format_parse_context& ctx) {
// [ctx.begin(), ctx.end()) is a character range that contains a part of
// the format string starting from the format specifications to be parsed,
// e.g. in
//
// fmt::format("{:f} - point of interest", point{1, 2});
//
// the range will contain "f} - point of interest". The formatter should
// parse specifiers until '}' or the end of the range. In this example
// the formatter should parse the 'f' specifier and return an iterator
// pointing to '}'.
// Parse the presentation format and store it in the formatter:
auto it = ctx.begin(), end = ctx.end();
if (it != end && (*it == 'f' || *it == 'e')) presentation = *it++;
// Check if reached the end of the range:
if (it != end && *it != '}')
throw format_error("invalid format");
// Return an iterator past the end of the parsed range:
return it;
}
// Formats the point p using the parsed format specification (presentation)
// stored in this formatter.
template <typename FormatContext>
auto format(const point &p, FormatContext &ctx) {
return format_to(ctx.out(), "({:.1f}, {:.1f})", p.x, p.y);
auto format(const point& p, FormatContext& ctx) {
// ctx.out() is an output iterator to write to.
return format_to(
ctx.out(),
presentation == 'f' ? "({:.1f}, {:.1f})" : "({:.1e}, {:.1e})",
p.x, p.y);
}
};
}
Then you can pass objects of type ``point`` to any formatting function::
point p = {1, 2};
std::string s = fmt::format("{}", p);
std::string s = fmt::format("{:f}", p);
// s == "(1.0, 2.0)"
In the example above the ``formatter<point>::parse`` function ignores the
contents of the format string referred to by ``ctx.begin()`` so the object will
always be formatted in the same way. See ``formatter<tm>::parse`` in
:file:`fmt/chrono.h` for an advanced example of how to parse the format string and
customize the formatted output.
You can also reuse existing formatters, for example::
You can also reuse existing formatters via inheritance or composition, for
example::
enum class color {red, green, blue};
@ -143,7 +169,7 @@ You can also reuse existing formatters, for example::
struct fmt::formatter<color>: formatter<string_view> {
// parse is inherited from formatter<string_view>.
template <typename FormatContext>
auto format(color c, FormatContext &ctx) {
auto format(color c, FormatContext& ctx) {
string_view name = "unknown";
switch (c) {
case color::red: name = "red"; break;
@ -183,6 +209,9 @@ You can also write a formatter for a hierarchy of classes::
fmt::print("{}", a); // prints "B"
}
.. doxygenclass:: fmt::basic_format_parse_context
:members:
Output Iterator Support
-----------------------
@ -266,7 +295,7 @@ allocator::
template <typename ...Args>
inline custom_string format(custom_allocator alloc,
fmt::string_view format_str,
const Args & ... args) {
const Args& ... args) {
return vformat(alloc, format_str, fmt::make_format_args(args...));
}
@ -310,7 +339,7 @@ custom argument formatter class::
template <typename ...Args>
inline std::string custom_format(
fmt::string_view format_str, const Args &... args) {
fmt::string_view format_str, const Args&... args) {
return custom_vformat(format_str, fmt::make_format_args(args...));
}
@ -377,7 +406,7 @@ user-defined types that have overloaded ``operator<<``::
public:
date(int year, int month, int day): year_(year), month_(month), day_(day) {}
friend std::ostream &operator<<(std::ostream &os, const date &d) {
friend std::ostream& operator<<(std::ostream& os, const date& d) {
return os << d.year_ << '-' << d.month_ << '-' << d.day_;
}
};

View File

@ -6,7 +6,7 @@ import errno, os, shutil, sys, tempfile
from subprocess import check_call, check_output, CalledProcessError, Popen, PIPE
from distutils.version import LooseVersion
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']
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']
def pip_install(package, commit=None, **kwargs):
"Install package using pip."

View File

@ -1,17 +1,18 @@
Overview
========
**fmt** is an open-source formatting library.
It can be used as a fast and safe alternative to printf and IOStreams.
**{fmt}** is an open-source formatting library providing a fast and safe
alternative to C stdio and C++ iostreams.
.. raw:: html
<div class="panel panel-default">
<div class="panel-heading">What users say:</div>
<div class="panel-body">
Thanks for creating this library. Its been a hole in C++ for a long
time. Ive used both boost::format and loki::SPrintf, and neither felt
like the right answer. This does.
Thanks for creating this library. Its been a hole in C++ for
aa long time. Ive used both <code>boost::format</code> and
<code>loki::SPrintf</code>, and neither felt like the right answer.
This does.
</div>
</div>
@ -20,12 +21,13 @@ It can be used as a fast and safe alternative to printf and IOStreams.
Format API
----------
The replacement-based Format API provides a safe alternative to ``printf``,
``sprintf`` and friends with comparable or `better performance
<http://zverovich.net/2013/09/07/integer-to-string-conversion-in-cplusplus.html>`_.
The format API is similar in spirit to the C ``printf`` family of function but
is safer, simpler and serveral times `faster
<http://zverovich.net/2013/09/07/integer-to-string-conversion-in-cplusplus.html>`_
than common standard library implementations.
The `format string syntax <syntax.html>`_ is similar to the one used by
`str.format <http://docs.python.org/3/library/stdtypes.html#str.format>`_
in Python:
`str.format <http://docs.python.org/3/library/stdtypes.html#str.format>`_ in
Python:
.. code:: c++
@ -60,7 +62,7 @@ The Format API also supports positional arguments useful for localization:
fmt::print("I'd rather be {1} than {0}.", "right", "happy");
Named arguments can be created with ``fmt::arg``. This makes it easier to track
what goes where when multiple values are being inserted:
what goes where when multiple arguments are being formatted:
.. code:: c++
@ -72,21 +74,10 @@ an alternative, slightly terser syntax for named arguments:
.. code:: c++
using namespace fmt::literals;
fmt::print("Hello, {name}! The answer is {number}. Goodbye, {name}.",
"name"_a="World", "number"_a=42);
The ``_format`` suffix may be used to format string literals similar to Python:
.. code:: c++
std::string message = "{0}{1}{0}"_format("abra", "cad");
Other than the placement of the format string on the left of the operator,
``_format`` is functionally identical to ``fmt::format``. In order to use the
literal operators, they must be made visible with the directive
``using namespace fmt::literals;``. Note that this brings in only ``_a`` and
``_format`` but nothing else from the ``fmt`` namespace.
.. _safety:
Safety
@ -106,11 +97,10 @@ string", because the argument ``"forty-two"`` is a string while the format code
.. code:: c++
format(fmt("The answer is {:d}"), "forty-two");
format(FMT_STRING("The answer is {:d}"), "forty-two");
reports a compile-time error for the same reason on compilers that support
relaxed ``constexpr``. See `here <api.html#c.fmt>`_ for how to enable
compile-time checks.
relaxed ``constexpr``. See `here <api.html#c.fmt>`_ for details.
The following code
@ -178,13 +168,13 @@ The library is highly portable and relies only on a small set of C++11 features:
* alias templates
These are available since GCC 4.8, Clang 3.0 and MSVC 19.0 (2015). For older
compilers use fmt `version 4.x
compilers use {fmt} `version 4.x
<https://github.com/fmtlib/fmt/releases/tag/4.1.0>`_ which continues to be
maintained and only requires C++98.
The output of all formatting functions is consistent across platforms. In
particular, formatting a floating-point infinity always gives ``inf`` while the
output of ``printf`` is platform-dependent in this case. For example,
output of ``printf`` is platform-dependent. For example,
.. code::
@ -197,9 +187,9 @@ always prints ``inf``.
Ease of Use
-----------
fmt has a small self-contained code base with the core library consisting of
{fmt} has a small self-contained code base with the core library consisting of
just three header files and no external dependencies.
A permissive BSD `license <https://github.com/fmtlib/fmt#license>`_ allows
A permissive MIT `license <https://github.com/fmtlib/fmt#license>`_ allows
using the library both in open-source and commercial projects.
.. raw:: html

View File

@ -265,10 +265,6 @@ The available presentation types for floating-point values are:
| | the current locale setting to insert the appropriate |
| | number separator characters. |
+---------+----------------------------------------------------------+
| ``'%'`` | Fixed point as a percentage. This is similar to ``'f'``, |
| | but the argument is multiplied by 100 and a percent sign |
| | ``%`` is appended. |
+---------+----------------------------------------------------------+
| none | Similar to ``'g'``, except that fixed-point notation, |
| | when used, has at least one digit past the decimal |
| | point. The default precision is as high as needed to |

View File

@ -103,7 +103,17 @@ the previous section. Then compile the ``doc`` target/project, for example::
make doc
This will generate the HTML documentation in ``doc/html``.
Conda
=====
fmt can be installed on Linux, macOS and Windows with
`Conda <https://docs.conda.io/en/latest/>`__, using its
`conda-forge <https://conda-forge.org>`__
`package <https://github.com/conda-forge/fmt-feedstock>`__, as follows::
conda install -c conda-forge fmt
Android NDK
===========

View File

@ -16,16 +16,291 @@
#include <locale>
#include <sstream>
// enable safe chrono durations, unless explicitly disabled
FMT_BEGIN_NAMESPACE
// Enable safe chrono durations, unless explicitly disabled.
#ifndef FMT_SAFE_DURATION_CAST
# define FMT_SAFE_DURATION_CAST 1
#endif
#if FMT_SAFE_DURATION_CAST
# include "safe-duration-cast.h"
#endif
FMT_BEGIN_NAMESPACE
// For conversion between std::chrono::durations without undefined
// behaviour or erroneous results.
// This is a stripped down version of duration_cast, for inclusion in fmt.
// See https://github.com/pauldreik/safe_duration_cast
//
// Copyright Paul Dreik 2019
namespace safe_duration_cast {
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) {
ec = 0;
using F = std::numeric_limits<From>;
using T = std::numeric_limits<To>;
static_assert(F::is_integer, "From must be integral");
static_assert(T::is_integer, "To must be integral");
// A and B are both signed, or both unsigned.
if (F::digits <= T::digits) {
// From fits in To without any problem.
} else {
// From does not always fit in To, resort to a dynamic check.
if (from < T::min() || from > T::max()) {
// outside range.
ec = 1;
return {};
}
}
return static_cast<To>(from);
}
/**
* converts From to To, without loss. If the dynamic value of from
* can't be converted to To without loss, ec is set.
*/
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) {
ec = 0;
using F = std::numeric_limits<From>;
using T = std::numeric_limits<To>;
static_assert(F::is_integer, "From must be integral");
static_assert(T::is_integer, "To must be integral");
if (F::is_signed && !T::is_signed) {
// From may be negative, not allowed!
if (fmt::internal::is_negative(from)) {
ec = 1;
return {};
}
// From is positive. Can it always fit in To?
if (F::digits <= T::digits) {
// yes, From always fits in To.
} else {
// from may not fit in To, we have to do a dynamic check
if (from > static_cast<From>(T::max())) {
ec = 1;
return {};
}
}
}
if (!F::is_signed && T::is_signed) {
// can from be held in To?
if (F::digits < T::digits) {
// yes, From always fits in To.
} else {
// from may not fit in To, we have to do a dynamic check
if (from > static_cast<From>(T::max())) {
// outside range.
ec = 1;
return {};
}
}
}
// reaching here means all is ok for lossless conversion.
return static_cast<To>(from);
} // function
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) {
ec = 0;
return from;
} // function
// clang-format off
/**
* converts From to To if possible, otherwise ec is set.
*
* input | output
* ---------------------------------|---------------
* NaN | NaN
* Inf | Inf
* normal, fits in output | converted (possibly lossy)
* normal, does not fit in output | ec is set
* subnormal | best effort
* -Inf | -Inf
*/
// 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) {
ec = 0;
using T = std::numeric_limits<To>;
static_assert(std::is_floating_point<From>::value, "From must be floating");
static_assert(std::is_floating_point<To>::value, "To must be floating");
// catch the only happy case
if (std::isfinite(from)) {
if (from >= T::lowest() && from <= T::max()) {
return static_cast<To>(from);
}
// not within range.
ec = 1;
return {};
}
// nan and inf will be preserved
return static_cast<To>(from);
} // function
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) {
ec = 0;
static_assert(std::is_floating_point<From>::value, "From must be floating");
return from;
}
/**
* safe duration cast between integral durations
*/
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) {
using From = std::chrono::duration<FromRep, FromPeriod>;
ec = 0;
// the basic idea is that we need to convert from count() in the from type
// to count() in the To type, by multiplying it with this:
struct Factor
: std::ratio_divide<typename From::period, typename To::period> {};
static_assert(Factor::num > 0, "num must be positive");
static_assert(Factor::den > 0, "den must be positive");
// the conversion is like this: multiply from.count() with Factor::num
// /Factor::den and convert it to To::rep, all this without
// overflow/underflow. let's start by finding a suitable type that can hold
// both To, From and Factor::num
using IntermediateRep =
typename std::common_type<typename From::rep, typename To::rep,
decltype(Factor::num)>::type;
// safe conversion to IntermediateRep
IntermediateRep count =
lossless_integral_conversion<IntermediateRep>(from.count(), ec);
if (ec) {
return {};
}
// multiply with Factor::num without overflow or underflow
if (Factor::num != 1) {
const auto max1 = internal::max_value<IntermediateRep>() / Factor::num;
if (count > max1) {
ec = 1;
return {};
}
const auto min1 = std::numeric_limits<IntermediateRep>::min() / Factor::num;
if (count < min1) {
ec = 1;
return {};
}
count *= Factor::num;
}
// this can't go wrong, right? den>0 is checked earlier.
if (Factor::den != 1) {
count /= Factor::den;
}
// convert to the to type, safely
using ToRep = typename To::rep;
const ToRep tocount = lossless_integral_conversion<ToRep>(count, ec);
if (ec) {
return {};
}
return To{tocount};
}
/**
* safe duration_cast between floating point durations
*/
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) {
using From = std::chrono::duration<FromRep, FromPeriod>;
ec = 0;
if (std::isnan(from.count())) {
// nan in, gives nan out. easy.
return To{std::numeric_limits<typename To::rep>::quiet_NaN()};
}
// maybe we should also check if from is denormal, and decide what to do about
// it.
// +-inf should be preserved.
if (std::isinf(from.count())) {
return To{from.count()};
}
// the basic idea is that we need to convert from count() in the from type
// to count() in the To type, by multiplying it with this:
struct Factor
: std::ratio_divide<typename From::period, typename To::period> {};
static_assert(Factor::num > 0, "num must be positive");
static_assert(Factor::den > 0, "den must be positive");
// the conversion is like this: multiply from.count() with Factor::num
// /Factor::den and convert it to To::rep, all this without
// overflow/underflow. let's start by finding a suitable type that can hold
// both To, From and Factor::num
using IntermediateRep =
typename std::common_type<typename From::rep, typename To::rep,
decltype(Factor::num)>::type;
// force conversion of From::rep -> IntermediateRep to be safe,
// even if it will never happen be narrowing in this context.
IntermediateRep count =
safe_float_conversion<IntermediateRep>(from.count(), ec);
if (ec) {
return {};
}
// multiply with Factor::num without overflow or underflow
if (Factor::num != 1) {
constexpr auto max1 = internal::max_value<IntermediateRep>() /
static_cast<IntermediateRep>(Factor::num);
if (count > max1) {
ec = 1;
return {};
}
constexpr auto min1 = std::numeric_limits<IntermediateRep>::lowest() /
static_cast<IntermediateRep>(Factor::num);
if (count < min1) {
ec = 1;
return {};
}
count *= static_cast<IntermediateRep>(Factor::num);
}
// this can't go wrong, right? den>0 is checked earlier.
if (Factor::den != 1) {
using common_t = typename std::common_type<IntermediateRep, intmax_t>::type;
count /= static_cast<common_t>(Factor::den);
}
// convert to the to type, safely
using ToRep = typename To::rep;
const ToRep tocount = safe_float_conversion<ToRep>(count, ec);
if (ec) {
return {};
}
return To{tocount};
}
} // namespace safe_duration_cast
#endif
// Prevents expansion of a preceding token as a function-style macro.
// Usage: f FMT_NOMACRO()
@ -421,7 +696,7 @@ inline int to_nonnegative_int(T value, int upper) {
template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)>
inline T mod(T x, int y) {
return x % y;
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) {
@ -518,7 +793,10 @@ struct chrono_formatter {
explicit chrono_formatter(FormatContext& ctx, OutputIt o,
std::chrono::duration<Rep, Period> d)
: context(ctx), out(o), val(d.count()), negative(false) {
: context(ctx),
out(o),
val(static_cast<rep>(d.count())),
negative(false) {
if (d.count() < 0) {
val = 0 - val;
negative = true;
@ -582,8 +860,8 @@ struct chrono_formatter {
void write(Rep value, int width) {
write_sign();
if (isnan(value)) return write_nan();
uint32_or_64_or_128_t<int> n = to_unsigned(
to_nonnegative_int(value, max_value<int>()));
uint32_or_64_or_128_t<int> n =
to_unsigned(to_nonnegative_int(value, max_value<int>()));
int num_digits = internal::count_digits(n);
if (width > num_digits) out = std::fill_n(out, width - num_digits, '0');
out = format_decimal<char_type>(out, n, num_digits);
@ -728,7 +1006,7 @@ struct formatter<std::chrono::duration<Rep, Period>, Char> {
struct spec_handler {
formatter& f;
basic_parse_context<Char>& context;
basic_format_parse_context<Char>& context;
basic_string_view<Char> format_str;
template <typename Id> FMT_CONSTEXPR arg_ref_type make_arg_ref(Id arg_id) {
@ -748,8 +1026,8 @@ struct formatter<std::chrono::duration<Rep, Period>, Char> {
void on_error(const char* msg) { FMT_THROW(format_error(msg)); }
void on_fill(Char fill) { f.specs.fill[0] = fill; }
void on_align(align_t align) { f.specs.align = align; }
void on_width(unsigned width) { f.specs.width = width; }
void on_precision(unsigned _precision) { f.precision = _precision; }
void on_width(int width) { f.specs.width = width; }
void on_precision(int _precision) { f.precision = _precision; }
void end_precision() {}
template <typename Id> void on_dynamic_width(Id arg_id) {
@ -761,13 +1039,13 @@ struct formatter<std::chrono::duration<Rep, Period>, Char> {
}
};
using iterator = typename basic_parse_context<Char>::iterator;
using iterator = typename basic_format_parse_context<Char>::iterator;
struct parse_range {
iterator begin;
iterator end;
};
FMT_CONSTEXPR parse_range do_parse(basic_parse_context<Char>& ctx) {
FMT_CONSTEXPR parse_range do_parse(basic_format_parse_context<Char>& ctx) {
auto begin = ctx.begin(), end = ctx.end();
if (begin == end || *begin == '}') return {begin, begin};
spec_handler handler{*this, ctx, format_str};
@ -788,7 +1066,7 @@ struct formatter<std::chrono::duration<Rep, Period>, Char> {
public:
formatter() : precision(-1) {}
FMT_CONSTEXPR auto parse(basic_parse_context<Char>& ctx)
FMT_CONSTEXPR auto parse(basic_format_parse_context<Char>& ctx)
-> decltype(ctx.begin()) {
auto range = do_parse(ctx);
format_str = basic_string_view<Char>(

View File

@ -299,15 +299,15 @@ class text_style {
return static_cast<uint8_t>(ems) != 0;
}
FMT_CONSTEXPR internal::color_type get_foreground() const FMT_NOEXCEPT {
assert(has_foreground() && "no foreground specified for this style");
FMT_ASSERT(has_foreground(), "no foreground specified for this style");
return foreground_color;
}
FMT_CONSTEXPR internal::color_type get_background() const FMT_NOEXCEPT {
assert(has_background() && "no background specified for this style");
FMT_ASSERT(has_background(), "no background specified for this style");
return background_color;
}
FMT_CONSTEXPR emphasis get_emphasis() const FMT_NOEXCEPT {
assert(has_emphasis() && "no emphasis specified for this style");
FMT_ASSERT(has_emphasis(), "no emphasis specified for this style");
return ems;
}
@ -470,58 +470,41 @@ inline void reset_color(basic_memory_buffer<Char>& buffer) FMT_NOEXCEPT {
}
template <typename Char>
std::basic_string<Char> vformat(const text_style& ts,
basic_string_view<Char> format_str,
basic_format_args<buffer_context<Char> > args) {
basic_memory_buffer<Char> buffer;
void vformat_to(basic_memory_buffer<Char>& buf, const text_style& ts,
basic_string_view<Char> format_str,
basic_format_args<buffer_context<Char>> args) {
bool has_style = false;
if (ts.has_emphasis()) {
has_style = true;
ansi_color_escape<Char> escape = make_emphasis<Char>(ts.get_emphasis());
buffer.append(escape.begin(), escape.end());
auto emphasis = internal::make_emphasis<Char>(ts.get_emphasis());
buf.append(emphasis.begin(), emphasis.end());
}
if (ts.has_foreground()) {
has_style = true;
ansi_color_escape<Char> escape =
make_foreground_color<Char>(ts.get_foreground());
buffer.append(escape.begin(), escape.end());
auto foreground =
internal::make_foreground_color<Char>(ts.get_foreground());
buf.append(foreground.begin(), foreground.end());
}
if (ts.has_background()) {
has_style = true;
ansi_color_escape<Char> escape =
make_background_color<Char>(ts.get_background());
buffer.append(escape.begin(), escape.end());
auto background =
internal::make_background_color<Char>(ts.get_background());
buf.append(background.begin(), background.end());
}
internal::vformat_to(buffer, format_str, args);
vformat_to(buf, format_str, args);
if (has_style) {
reset_color<Char>(buffer);
internal::reset_color<Char>(buf);
}
return fmt::to_string(buffer);
}
} // namespace internal
template <typename S, typename Char = char_t<S> >
template <typename S, typename Char = char_t<S>>
void vprint(std::FILE* f, const text_style& ts, const S& format,
basic_format_args<buffer_context<Char> > args) {
bool has_style = false;
if (ts.has_emphasis()) {
has_style = true;
internal::fputs<Char>(internal::make_emphasis<Char>(ts.get_emphasis()), f);
}
if (ts.has_foreground()) {
has_style = true;
internal::fputs<Char>(
internal::make_foreground_color<Char>(ts.get_foreground()), f);
}
if (ts.has_background()) {
has_style = true;
internal::fputs<Char>(
internal::make_background_color<Char>(ts.get_background()), f);
}
vprint(f, format, args);
if (has_style) {
internal::reset_color<Char>(f);
}
basic_format_args<buffer_context<Char>> args) {
basic_memory_buffer<Char> buf;
internal::vformat_to(buf, ts, to_string_view(format), args);
buf.push_back(Char(0));
internal::fputs(buf.data(), f);
}
/**
@ -536,7 +519,7 @@ template <typename S, typename... Args,
void print(std::FILE* f, const text_style& ts, const S& format_str,
const Args&... args) {
internal::check_format_string<Args...>(format_str);
using context = buffer_context<char_t<S> >;
using context = buffer_context<char_t<S>>;
format_arg_store<context, Args...> as{args...};
vprint(f, ts, format_str, basic_format_args<context>(as));
}
@ -554,11 +537,13 @@ void print(const text_style& ts, const S& format_str, const Args&... args) {
return print(stdout, ts, format_str, args...);
}
template <typename S, typename Char = char_t<S> >
template <typename S, typename Char = char_t<S>>
inline std::basic_string<Char> vformat(
const text_style& ts, const S& format_str,
basic_format_args<buffer_context<Char> > args) {
return internal::vformat(ts, to_string_view(format_str), args);
basic_format_args<buffer_context<Char>> args) {
basic_memory_buffer<Char> buf;
internal::vformat_to(buf, ts, to_string_view(format_str), args);
return fmt::to_string(buf);
}
/**
@ -573,12 +558,11 @@ inline std::basic_string<Char> vformat(
"The answer is {}", 42);
\endrst
*/
template <typename S, typename... Args, typename Char = char_t<S> >
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) {
return internal::vformat(
ts, to_string_view(format_str),
{internal::make_args_checked<Args...>(format_str, args...)});
return vformat(ts, to_string_view(format_str),
{internal::make_args_checked<Args...>(format_str, args...)});
}
FMT_END_NAMESPACE

View File

@ -26,11 +26,11 @@ template <typename Char> struct format_part {
kind part_kind;
union value {
unsigned arg_index;
int arg_index;
basic_string_view<Char> str;
replacement repl;
FMT_CONSTEXPR value(unsigned index = 0) : arg_index(index) {}
FMT_CONSTEXPR value(int index = 0) : arg_index(index) {}
FMT_CONSTEXPR value(basic_string_view<Char> s) : str(s) {}
FMT_CONSTEXPR value(replacement r) : repl(r) {}
} val;
@ -40,7 +40,7 @@ template <typename Char> struct format_part {
FMT_CONSTEXPR format_part(kind k = kind::arg_index, value v = {})
: part_kind(k), val(v) {}
static FMT_CONSTEXPR format_part make_arg_index(unsigned index) {
static FMT_CONSTEXPR format_part make_arg_index(int index) {
return format_part(kind::arg_index, index);
}
static FMT_CONSTEXPR format_part make_arg_name(basic_string_view<Char> name) {
@ -62,7 +62,7 @@ template <typename Char> struct part_counter {
}
FMT_CONSTEXPR void on_arg_id() { ++num_parts; }
FMT_CONSTEXPR void on_arg_id(unsigned) { ++num_parts; }
FMT_CONSTEXPR void on_arg_id(int) { ++num_parts; }
FMT_CONSTEXPR void on_arg_id(basic_string_view<Char>) { ++num_parts; }
FMT_CONSTEXPR void on_replacement_field(const Char*) {}
@ -101,7 +101,7 @@ class format_string_compiler : public error_handler {
PartHandler handler_;
part part_;
basic_string_view<Char> format_str_;
basic_parse_context<Char> parse_context_;
basic_format_parse_context<Char> parse_context_;
public:
FMT_CONSTEXPR format_string_compiler(basic_string_view<Char> format_str,
@ -119,7 +119,7 @@ class format_string_compiler : public error_handler {
part_ = part::make_arg_index(parse_context_.next_arg_id());
}
FMT_CONSTEXPR void on_arg_id(unsigned id) {
FMT_CONSTEXPR void on_arg_id(int id) {
parse_context_.check_arg_id(id);
part_ = part::make_arg_index(id);
}
@ -136,8 +136,8 @@ class format_string_compiler : public error_handler {
FMT_CONSTEXPR const Char* on_format_specs(const Char* begin,
const Char* end) {
auto repl = typename part::replacement();
dynamic_specs_handler<basic_parse_context<Char>> handler(repl.specs,
parse_context_);
dynamic_specs_handler<basic_format_parse_context<Char>> handler(
repl.specs, parse_context_);
auto it = parse_format_specs(begin, end, handler);
if (*it != '}') on_error("missing '}' in format string");
repl.arg_id = part_.part_kind == part::kind::arg_index
@ -160,8 +160,9 @@ FMT_CONSTEXPR void compile_format_string(basic_string_view<Char> format_str,
}
template <typename Range, typename Context, typename Id>
void format_arg(basic_parse_context<typename Range::value_type>& parse_ctx,
Context& ctx, Id arg_id) {
void format_arg(
basic_format_parse_context<typename Range::value_type>& parse_ctx,
Context& ctx, Id arg_id) {
ctx.advance_to(
visit_format_arg(arg_formatter<Range>(ctx, &parse_ctx), ctx.arg(arg_id)));
}
@ -172,7 +173,8 @@ template <typename Context, typename Range, typename CompiledFormat>
auto vformat_to(Range out, CompiledFormat& cf, basic_format_args<Context> args)
-> typename Context::iterator {
using char_type = typename Context::char_type;
basic_parse_context<char_type> parse_ctx(to_string_view(cf.format_str_));
basic_format_parse_context<char_type> parse_ctx(
to_string_view(cf.format_str_));
Context ctx(out.begin(), args);
const auto& parts = cf.parts();
@ -373,6 +375,13 @@ OutputIt format_default(OutputIt out, T value) {
return std::copy(fi.data(), fi.data() + fi.size(), out);
}
template <typename Char, typename OutputIt>
OutputIt format_default(OutputIt out, double value) {
writer w(out);
w.write(value);
return w.out();
}
template <typename Char, typename OutputIt>
OutputIt format_default(OutputIt out, Char value) {
*out++ = value;
@ -503,8 +512,6 @@ template <typename CompiledFormat, typename... Args,
CompiledFormat>::value)>
std::basic_string<Char> format(const CompiledFormat& cf, const Args&... args) {
basic_memory_buffer<Char> buffer;
using range = buffer_range<Char>;
using context = buffer_context<Char>;
cf.format(std::back_inserter(buffer), args...);
return to_string(buffer);
}
@ -570,10 +577,7 @@ format_to_n_result<OutputIt> format_to_n(OutputIt out, size_t n,
template <typename CompiledFormat, typename... Args>
std::size_t formatted_size(const CompiledFormat& cf, const Args&... args) {
return format_to(
internal::counting_iterator<typename CompiledFormat::char_type>(),
cf, args...)
.count();
return format_to(internal::counting_iterator(), cf, args...).count();
}
FMT_END_NAMESPACE

View File

@ -8,7 +8,6 @@
#ifndef FMT_CORE_H_
#define FMT_CORE_H_
#include <cassert>
#include <cstdio> // std::FILE
#include <cstring>
#include <iterator>
@ -16,7 +15,7 @@
#include <type_traits>
// The fmt library version in the form major * 10000 + minor * 100 + patch.
#define FMT_VERSION 60000
#define FMT_VERSION 60102
#ifdef __has_feature
# define FMT_HAS_FEATURE(x) __has_feature(x)
@ -49,6 +48,12 @@
# define FMT_HAS_GXX_CXX11 0
#endif
#ifdef __NVCC__
# define FMT_NVCC __NVCC__
#else
# define FMT_NVCC 0
#endif
#ifdef _MSC_VER
# define FMT_MSC_VER _MSC_VER
#else
@ -60,7 +65,8 @@
#ifndef FMT_USE_CONSTEXPR
# define FMT_USE_CONSTEXPR \
(FMT_HAS_FEATURE(cxx_relaxed_constexpr) || FMT_MSC_VER >= 1910 || \
(FMT_GCC_VERSION >= 600 && __cplusplus >= 201402L))
(FMT_GCC_VERSION >= 600 && __cplusplus >= 201402L)) && \
!FMT_NVCC
#endif
#if FMT_USE_CONSTEXPR
# define FMT_CONSTEXPR constexpr
@ -134,7 +140,7 @@
#endif
// Workaround broken [[deprecated]] in the Intel compiler and NVCC.
#if defined(__INTEL_COMPILER) || defined(__NVCC__) || defined(__CUDACC__)
#if defined(__INTEL_COMPILER) || FMT_NVCC
# define FMT_DEPRECATED_ALIAS
#else
# define FMT_DEPRECATED_ALIAS FMT_DEPRECATED
@ -180,10 +186,6 @@
# define FMT_EXTERN
#endif
#ifndef FMT_ASSERT
# define FMT_ASSERT(condition, message) assert((condition) && message)
#endif
// libc++ supports string_view in pre-c++17.
#if (FMT_HAS_INCLUDE(<string_view>) && \
(__cplusplus > 201402L || defined(_LIBCPP_VERSION))) || \
@ -222,6 +224,19 @@ namespace internal {
// A workaround for gcc 4.8 to make void_t work in a SFINAE context.
template <typename... Ts> struct void_t_impl { using type = void; };
FMT_API void assert_fail(const char* file, int line, const char* message);
#ifndef FMT_ASSERT
# ifdef NDEBUG
# define FMT_ASSERT(condition, message)
# else
# define FMT_ASSERT(condition, message) \
((condition) \
? void() \
: fmt::internal::assert_fail(__FILE__, __LINE__, (message)))
# endif
#endif
#if defined(FMT_USE_STRING_VIEW)
template <typename Char> using std_string_view = std::basic_string_view<Char>;
#elif defined(FMT_USE_EXPERIMENTAL_STRING_VIEW)
@ -289,10 +304,11 @@ template <typename Char> class basic_string_view {
: data_(s), size_(std::char_traits<Char>::length(s)) {}
/** Constructs a string reference from a ``std::basic_string`` object. */
template <typename Alloc>
FMT_CONSTEXPR basic_string_view(const std::basic_string<Char, Alloc>& s)
FMT_NOEXCEPT : data_(s.data()),
size_(s.size()) {}
template <typename Traits, typename Alloc>
FMT_CONSTEXPR basic_string_view(
const std::basic_string<Char, Traits, Alloc>& s) FMT_NOEXCEPT
: data_(s.data()),
size_(s.size()) {}
template <
typename S,
@ -382,10 +398,10 @@ inline basic_string_view<Char> to_string_view(const Char* s) {
return s;
}
template <typename Char, typename Traits, typename Allocator>
template <typename Char, typename Traits, typename Alloc>
inline basic_string_view<Char> to_string_view(
const std::basic_string<Char, Traits, Allocator>& s) {
return {s.data(), s.size()};
const std::basic_string<Char, Traits, Alloc>& s) {
return s;
}
template <typename Char>
@ -430,8 +446,8 @@ template <typename S> struct char_t_impl<S, enable_if_t<is_string<S>::value>> {
};
struct error_handler {
FMT_CONSTEXPR error_handler() {}
FMT_CONSTEXPR error_handler(const error_handler&) {}
FMT_CONSTEXPR error_handler() = default;
FMT_CONSTEXPR error_handler(const error_handler&) = default;
// This function is intentionally not constexpr to give a compile-time error.
FMT_NORETURN FMT_API void on_error(const char* message);
@ -441,10 +457,24 @@ struct error_handler {
/** String's character type. */
template <typename S> using char_t = typename internal::char_t_impl<S>::type;
// Parsing context consisting of a format string range being parsed and an
// argument counter for automatic indexing.
/**
\rst
Parsing context consisting of a format string range being parsed and an
argument counter for automatic indexing.
You can use one of the following type aliases for common character types:
+-----------------------+-------------------------------------+
| Type | Definition |
+=======================+=====================================+
| format_parse_context | basic_format_parse_context<char> |
+-----------------------+-------------------------------------+
| wformat_parse_context | basic_format_parse_context<wchar_t> |
+-----------------------+-------------------------------------+
\endrst
*/
template <typename Char, typename ErrorHandler = internal::error_handler>
class basic_parse_context : private ErrorHandler {
class basic_format_parse_context : private ErrorHandler {
private:
basic_string_view<Char> format_str_;
int next_arg_id_;
@ -453,38 +483,47 @@ class basic_parse_context : private ErrorHandler {
using char_type = Char;
using iterator = typename basic_string_view<Char>::iterator;
explicit FMT_CONSTEXPR basic_parse_context(basic_string_view<Char> format_str,
ErrorHandler eh = ErrorHandler())
explicit FMT_CONSTEXPR basic_format_parse_context(
basic_string_view<Char> format_str, ErrorHandler eh = ErrorHandler())
: ErrorHandler(eh), format_str_(format_str), next_arg_id_(0) {}
// Returns an iterator to the beginning of the format string range being
// parsed.
/**
Returns an iterator to the beginning of the format string range being
parsed.
*/
FMT_CONSTEXPR iterator begin() const FMT_NOEXCEPT {
return format_str_.begin();
}
// Returns an iterator past the end of the format string range being parsed.
/**
Returns an iterator past the end of the format string range being parsed.
*/
FMT_CONSTEXPR iterator end() const FMT_NOEXCEPT { return format_str_.end(); }
// Advances the begin iterator to ``it``.
/** Advances the begin iterator to ``it``. */
FMT_CONSTEXPR void advance_to(iterator it) {
format_str_.remove_prefix(internal::to_unsigned(it - begin()));
}
// Returns the next argument index.
/**
Reports an error if using the manual argument indexing; otherwise returns
the next argument index and switches to the automatic indexing.
*/
FMT_CONSTEXPR int next_arg_id() {
if (next_arg_id_ >= 0) return next_arg_id_++;
on_error("cannot switch from manual to automatic argument indexing");
return 0;
}
FMT_CONSTEXPR bool check_arg_id(int) {
if (next_arg_id_ > 0) {
/**
Reports an error if using the automatic argument indexing; otherwise
switches to the manual indexing.
*/
FMT_CONSTEXPR void check_arg_id(int) {
if (next_arg_id_ > 0)
on_error("cannot switch from automatic to manual argument indexing");
return false;
}
next_arg_id_ = -1;
return true;
else
next_arg_id_ = -1;
}
FMT_CONSTEXPR void check_arg_id(basic_string_view<Char>) {}
@ -496,11 +535,14 @@ class basic_parse_context : private ErrorHandler {
FMT_CONSTEXPR ErrorHandler error_handler() const { return *this; }
};
using format_parse_context = basic_parse_context<char>;
using wformat_parse_context = basic_parse_context<wchar_t>;
using format_parse_context = basic_format_parse_context<char>;
using wformat_parse_context = basic_format_parse_context<wchar_t>;
using parse_context FMT_DEPRECATED_ALIAS = basic_parse_context<char>;
using wparse_context FMT_DEPRECATED_ALIAS = basic_parse_context<wchar_t>;
template <typename Char, typename ErrorHandler = internal::error_handler>
using basic_parse_context FMT_DEPRECATED_ALIAS =
basic_format_parse_context<Char, ErrorHandler>;
using parse_context FMT_DEPRECATED_ALIAS = basic_format_parse_context<char>;
using wparse_context FMT_DEPRECATED_ALIAS = basic_format_parse_context<wchar_t>;
template <typename Context> class basic_format_arg;
template <typename Context> class basic_format_args;
@ -517,20 +559,17 @@ struct FMT_DEPRECATED convert_to_int
: bool_constant<!std::is_arithmetic<T>::value &&
std::is_convertible<T, int>::value> {};
namespace internal {
// Specifies if T has an enabled formatter specialization. A type can be
// formattable even if it doesn't have a formatter e.g. via a conversion.
template <typename T, typename Context>
using has_formatter =
std::is_constructible<typename Context::template formatter_type<T>>;
namespace internal {
/** A contiguous memory buffer with an optional growing ability. */
template <typename T> class buffer {
private:
buffer(const buffer&) = delete;
void operator=(const buffer&) = delete;
T* ptr_;
std::size_t size_;
std::size_t capacity_;
@ -557,7 +596,9 @@ template <typename T> class buffer {
using value_type = T;
using const_reference = const T&;
virtual ~buffer() {}
buffer(const buffer&) = delete;
void operator=(const buffer&) = delete;
virtual ~buffer() = default;
T* begin() FMT_NOEXCEPT { return ptr_; }
T* end() FMT_NOEXCEPT { return ptr_ + size_; }
@ -657,6 +698,7 @@ enum type {
char_type,
last_integer_type = char_type,
// followed by floating-point types.
float_type,
double_type,
long_double_type,
last_numeric_type = long_double_type,
@ -683,6 +725,7 @@ FMT_TYPE_CONSTANT(int128_t, int128_type);
FMT_TYPE_CONSTANT(uint128_t, uint128_type);
FMT_TYPE_CONSTANT(bool, bool_type);
FMT_TYPE_CONSTANT(Char, char_type);
FMT_TYPE_CONSTANT(float, float_type);
FMT_TYPE_CONSTANT(double, double_type);
FMT_TYPE_CONSTANT(long double, long_double_type);
FMT_TYPE_CONSTANT(const Char*, cstring_type);
@ -705,7 +748,7 @@ template <typename Char> struct string_value {
};
template <typename Context> struct custom_value {
using parse_context = basic_parse_context<typename Context::char_type>;
using parse_context = basic_format_parse_context<typename Context::char_type>;
const void* value;
void (*format)(const void* arg, parse_context& parse_ctx, Context& ctx);
};
@ -724,6 +767,7 @@ template <typename Context> class value {
uint128_t uint128_value;
bool bool_value;
char_type char_value;
float float_value;
double double_value;
long double long_double_value;
const void* pointer;
@ -738,6 +782,7 @@ template <typename Context> class value {
value(unsigned long long val) : ulong_long_value(val) {}
value(int128_t val) : int128_value(val) {}
value(uint128_t val) : uint128_value(val) {}
value(float val) : float_value(val) {}
value(double val) : double_value(val) {}
value(long double val) : long_double_value(val) {}
value(bool val) : bool_value(val) {}
@ -765,9 +810,9 @@ template <typename Context> class value {
private:
// Formats an argument of a custom type, such as a user-defined class.
template <typename T, typename Formatter>
static void format_custom_arg(const void* arg,
basic_parse_context<char_type>& parse_ctx,
Context& ctx) {
static void format_custom_arg(
const void* arg, basic_format_parse_context<char_type>& parse_ctx,
Context& ctx) {
Formatter f;
parse_ctx.advance_to(f.parse(parse_ctx));
ctx.advance_to(f.format(*static_cast<const T*>(arg), ctx));
@ -809,7 +854,7 @@ template <typename Context> struct arg_mapper {
return val;
}
FMT_CONSTEXPR double map(float val) { return static_cast<double>(val); }
FMT_CONSTEXPR float map(float val) { return val; }
FMT_CONSTEXPR double map(double val) { return val; }
FMT_CONSTEXPR long double map(long double val) { return val; }
@ -828,6 +873,15 @@ template <typename Context> struct arg_mapper {
FMT_CONSTEXPR basic_string_view<char_type> map(const T& val) {
return basic_string_view<char_type>(val);
}
template <
typename T,
FMT_ENABLE_IF(
std::is_constructible<std_string_view<char_type>, T>::value &&
!std::is_constructible<basic_string_view<char_type>, T>::value &&
!is_string<T>::value && !has_formatter<T, Context>::value)>
FMT_CONSTEXPR basic_string_view<char_type> map(const T& val) {
return std_string_view<char_type>(val);
}
FMT_CONSTEXPR const char* map(const signed char* val) {
static_assert(std::is_same<char_type, char>::value, "invalid string type");
return reinterpret_cast<const char*>(val);
@ -857,10 +911,14 @@ template <typename Context> struct arg_mapper {
map(static_cast<typename std::underlying_type<T>::type>(val))) {
return map(static_cast<typename std::underlying_type<T>::type>(val));
}
template <typename T,
FMT_ENABLE_IF(!is_string<T>::value && !is_char<T>::value &&
(has_formatter<T, Context>::value ||
has_fallback_formatter<T, Context>::value))>
template <
typename T,
FMT_ENABLE_IF(
!is_string<T>::value && !is_char<T>::value &&
!std::is_constructible<basic_string_view<char_type>, T>::value &&
(has_formatter<T, Context>::value ||
(has_fallback_formatter<T, Context>::value &&
!std::is_constructible<std_string_view<char_type>, T>::value)))>
FMT_CONSTEXPR const T& map(const T& val) {
return val;
}
@ -877,12 +935,13 @@ template <typename Context> struct arg_mapper {
// A type constant after applying arg_mapper<Context>.
template <typename T, typename Context>
using mapped_type_constant =
type_constant<decltype(arg_mapper<Context>().map(std::declval<T>())),
type_constant<decltype(arg_mapper<Context>().map(std::declval<const T&>())),
typename Context::char_type>;
enum { packed_arg_bits = 5 };
// Maximum number of arguments with packed types.
enum { max_packed_args = 15 };
enum : unsigned long long { is_unpacked_bit = 1ull << 63 };
enum { max_packed_args = 63 / packed_arg_bits };
enum : unsigned long long { is_unpacked_bit = 1ULL << 63 };
template <typename Context> class arg_map;
} // namespace internal
@ -913,7 +972,8 @@ template <typename Context> class basic_format_arg {
public:
explicit handle(internal::custom_value<Context> custom) : custom_(custom) {}
void format(basic_parse_context<char_type>& parse_ctx, Context& ctx) const {
void format(basic_format_parse_context<char_type>& parse_ctx,
Context& ctx) const {
custom_.format(custom_.value, parse_ctx, ctx);
}
@ -973,6 +1033,8 @@ FMT_CONSTEXPR auto visit_format_arg(Visitor&& vis,
return vis(arg.value_.bool_value);
case internal::char_type:
return vis(arg.value_.char_value);
case internal::float_type:
return vis(arg.value_.float_value);
case internal::double_type:
return vis(arg.value_.double_value);
case internal::long_double_type:
@ -994,9 +1056,6 @@ namespace internal {
// A map from argument names to their values for named arguments.
template <typename Context> class arg_map {
private:
arg_map(const arg_map&) = delete;
void operator=(const arg_map&) = delete;
using char_type = typename Context::char_type;
struct entry {
@ -1014,6 +1073,8 @@ template <typename Context> class arg_map {
}
public:
arg_map(const arg_map&) = delete;
void operator=(const arg_map&) = delete;
arg_map() : map_(nullptr), size_(0) {}
void init(const basic_format_args<Context>& args);
~arg_map() { delete[] map_; }
@ -1036,6 +1097,8 @@ class locale_ref {
locale_ref() : locale_(nullptr) {}
template <typename Locale> explicit locale_ref(const Locale& loc);
explicit operator bool() const FMT_NOEXCEPT { return locale_ != nullptr; }
template <typename Locale> Locale get() const;
};
@ -1044,7 +1107,7 @@ template <typename> constexpr unsigned long long encode_types() { return 0; }
template <typename Context, typename Arg, typename... Args>
constexpr unsigned long long encode_types() {
return mapped_type_constant<Arg, Context>::value |
(encode_types<Context, Args...>() << 4);
(encode_types<Context, Args...>() << packed_arg_bits);
}
template <typename Context, typename T>
@ -1080,14 +1143,13 @@ template <typename OutputIt, typename Char> class basic_format_context {
internal::arg_map<basic_format_context> map_;
internal::locale_ref loc_;
basic_format_context(const basic_format_context&) = delete;
void operator=(const basic_format_context&) = delete;
public:
using iterator = OutputIt;
using format_arg = basic_format_arg<basic_format_context>;
template <typename T> using formatter_type = formatter<T, char_type>;
basic_format_context(const basic_format_context&) = delete;
void operator=(const basic_format_context&) = delete;
/**
Constructs a ``basic_format_context`` object. References to the arguments are
stored in the object so make sure they have appropriate lifetimes.
@ -1146,7 +1208,6 @@ template <typename Context, typename... Args> class format_arg_store {
static constexpr unsigned long long types =
is_packed ? internal::encode_types<Context, Args...>()
: internal::is_unpacked_bit | num_args;
FMT_DEPRECATED static constexpr unsigned long long TYPES = types;
format_arg_store(const Args&... args)
: data_{internal::make_arg<is_packed, Context>(args)...} {}
@ -1189,8 +1250,9 @@ template <typename Context> class basic_format_args {
bool is_packed() const { return (types_ & internal::is_unpacked_bit) == 0; }
internal::type type(int index) const {
int shift = index * 4;
return static_cast<internal::type>((types_ & (0xfull << shift)) >> shift);
int shift = index * internal::packed_arg_bits;
unsigned int mask = (1 << internal::packed_arg_bits) - 1;
return static_cast<internal::type>((types_ >> shift) & mask);
}
friend class internal::arg_map<Context>;
@ -1223,7 +1285,7 @@ template <typename Context> class basic_format_args {
*/
template <typename... Args>
basic_format_args(const format_arg_store<Context, Args...>& store)
: types_(static_cast<unsigned long long>(store.types)) {
: types_(store.types) {
set_data(store.data_);
}
@ -1417,7 +1479,7 @@ inline std::basic_string<Char> format(const S& format_str, Args&&... args) {
}
FMT_API void vprint(std::FILE* f, string_view format_str, format_args args);
FMT_API void vprint(std::FILE* f, wstring_view format_str, wformat_args args);
FMT_API void vprint(string_view format_str, format_args args);
/**
\rst
@ -1437,9 +1499,6 @@ inline void print(std::FILE* f, const S& format_str, Args&&... args) {
internal::make_args_checked<Args...>(format_str, args...));
}
FMT_API void vprint(string_view format_str, format_args args);
FMT_API void vprint(wstring_view format_str, wformat_args args);
/**
\rst
Prints formatted data to ``stdout``.

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -89,9 +89,11 @@ void write(std::basic_ostream<Char>& os, buffer<Char>& buf) {
}
template <typename Char, typename T>
void format_value(buffer<Char>& buf, const T& value) {
void format_value(buffer<Char>& buf, const T& value,
locale_ref loc = locale_ref()) {
formatbuf<Char> format_buf(buf);
std::basic_ostream<Char> output(&format_buf);
if (loc) output.imbue(loc.get<std::locale>());
output.exceptions(std::ios_base::failbit | std::ios_base::badbit);
output << value;
buf.resize(buf.size());
@ -104,7 +106,7 @@ struct fallback_formatter<T, Char, enable_if_t<is_streamable<T, Char>::value>>
template <typename Context>
auto format(const T& value, Context& ctx) -> decltype(ctx.out()) {
basic_memory_buffer<Char> buffer;
format_value(buffer, value);
format_value(buffer, value, ctx.locale());
basic_string_view<Char> str(buffer.data(), buffer.size());
return formatter<basic_string_view<Char>, Char>::format(str, ctx);
}

View File

@ -13,11 +13,10 @@
# undef __STRICT_ANSI__
#endif
#include <errno.h>
#include <fcntl.h> // for O_RDONLY
#include <locale.h> // for locale_t
#include <stdio.h>
#include <stdlib.h> // for strtod_l
#include <cerrno>
#include <clocale> // for locale_t
#include <cstdio>
#include <cstdlib> // for strtod_l
#include <cstddef>
@ -27,6 +26,18 @@
#include "format.h"
// UWP doesn't provide _pipe.
#if FMT_HAS_INCLUDE("winapifamily.h")
# include <winapifamily.h>
#endif
#if FMT_HAS_INCLUDE("fcntl.h") && \
(!defined(WINAPI_FAMILY) || (WINAPI_FAMILY == WINAPI_FAMILY_DESKTOP_APP))
# include <fcntl.h> // for O_RDONLY
# define FMT_USE_FCNTL 1
#else
# define FMT_USE_FCNTL 0
#endif
#ifndef FMT_POSIX
# if defined(_WIN32) && !defined(__MINGW32__)
// Fix warnings about deprecated symbols.
@ -54,8 +65,8 @@
#ifndef _WIN32
# define FMT_RETRY_VAL(result, expression, error_result) \
do { \
result = (expression); \
} while (result == error_result && errno == EINTR)
(result) = (expression); \
} while ((result) == (error_result) && errno == EINTR)
#else
# define FMT_RETRY_VAL(result, expression, error_result) result = (expression)
#endif
@ -132,16 +143,15 @@ class buffered_file {
explicit buffered_file(FILE* f) : file_(f) {}
public:
buffered_file(const buffered_file&) = delete;
void operator=(const buffered_file&) = delete;
// Constructs a buffered_file object which doesn't represent any file.
buffered_file() FMT_NOEXCEPT : file_(nullptr) {}
// Destroys the object closing the file it represents if any.
FMT_API ~buffered_file() FMT_NOEXCEPT;
private:
buffered_file(const buffered_file&) = delete;
void operator=(const buffered_file&) = delete;
public:
buffered_file(buffered_file&& other) FMT_NOEXCEPT : file_(other.file_) {
other.file_ = nullptr;
@ -177,6 +187,7 @@ class buffered_file {
}
};
#if FMT_USE_FCNTL
// A file. Closed file is represented by a file object with descriptor -1.
// Methods that are not declared with FMT_NOEXCEPT may throw
// fmt::system_error in case of failure. Note that some errors such as
@ -204,14 +215,13 @@ class file {
// Opens a file and constructs a file object representing this file.
FMT_API file(cstring_view path, int oflag);
private:
public:
file(const file&) = delete;
void operator=(const file&) = delete;
public:
file(file&& other) FMT_NOEXCEPT : fd_(other.fd_) { other.fd_ = -1; }
file& operator=(file&& other) {
file& operator=(file&& other) FMT_NOEXCEPT {
close();
fd_ = other.fd_;
other.fd_ = -1;
@ -260,6 +270,7 @@ class file {
// Returns the memory page size.
long getpagesize();
#endif // FMT_USE_FCNTL
#ifdef FMT_LOCALE
// A "C" numeric locale.
@ -283,11 +294,10 @@ class Locale {
locale_t locale_;
Locale(const Locale&) = delete;
void operator=(const Locale&) = delete;
public:
using type = locale_t;
Locale(const Locale&) = delete;
void operator=(const Locale&) = delete;
Locale() : locale_(newlocale(LC_NUMERIC_MASK, "C", nullptr)) {
if (!locale_) FMT_THROW(system_error(errno, "cannot create locale"));

View File

@ -1,4 +1,4 @@
// Formatting library for C++
// Formatting library for C++ - legacy printf implementation
//
// Copyright (c) 2012 - 2016, Victor Zverovich
// All rights reserved.
@ -8,7 +8,7 @@
#ifndef FMT_PRINTF_H_
#define FMT_PRINTF_H_
#include <algorithm> // std::fill_n
#include <algorithm> // std::max
#include <limits> // std::numeric_limits
#include "ostream.h"
@ -16,10 +16,6 @@
FMT_BEGIN_NAMESPACE
namespace internal {
// A helper function to suppress bogus "conditional expression is constant"
// warnings.
template <typename T> inline T const_check(T value) { return value; }
// Checks if a value fits in int - used to avoid warnings about comparing
// signed and unsigned integers.
template <bool IsSigned> struct int_checker {
@ -235,7 +231,7 @@ class printf_arg_formatter : public internal::arg_formatter_base<Range> {
printf_arg_formatter(iterator iter, format_specs& specs, context_type& ctx)
: base(Range(iter), &specs, internal::locale_ref()), context_(ctx) {}
template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)>
template <typename T, FMT_ENABLE_IF(fmt::internal::is_integral<T>::value)>
iterator operator()(T value) {
// MSVC2013 fails to compile separate overloads for bool and char_type so
// use std::is_same instead.
@ -332,17 +328,17 @@ template <typename OutputIt, typename Char> class basic_printf_context {
OutputIt out_;
basic_format_args<basic_printf_context> args_;
basic_parse_context<Char> parse_ctx_;
basic_format_parse_context<Char> parse_ctx_;
static void parse_flags(format_specs& specs, const Char*& it,
const Char* end);
// Returns the argument with specified index or, if arg_index is equal
// to the maximum unsigned value, the next argument.
format_arg get_arg(unsigned arg_index = internal::max_value<unsigned>());
// Returns the argument with specified index or, if arg_index is -1, the next
// argument.
format_arg get_arg(int arg_index = -1);
// Parses argument index, flags and width and returns the argument index.
unsigned parse_header(const Char*& it, const Char* end, format_specs& specs);
int parse_header(const Char*& it, const Char* end, format_specs& specs);
public:
/**
@ -359,9 +355,9 @@ template <typename OutputIt, typename Char> class basic_printf_context {
OutputIt out() { return out_; }
void advance_to(OutputIt it) { out_ = it; }
format_arg arg(unsigned id) const { return args_.get(id); }
format_arg arg(int id) const { return args_.get(id); }
basic_parse_context<Char>& parse_context() { return parse_ctx_; }
basic_format_parse_context<Char>& parse_context() { return parse_ctx_; }
FMT_CONSTEXPR void on_error(const char* message) {
parse_ctx_.on_error(message);
@ -401,8 +397,8 @@ void basic_printf_context<OutputIt, Char>::parse_flags(format_specs& specs,
template <typename OutputIt, typename Char>
typename basic_printf_context<OutputIt, Char>::format_arg
basic_printf_context<OutputIt, Char>::get_arg(unsigned arg_index) {
if (arg_index == internal::max_value<unsigned>())
basic_printf_context<OutputIt, Char>::get_arg(int arg_index) {
if (arg_index < 0)
arg_index = parse_ctx_.next_arg_id();
else
parse_ctx_.check_arg_id(--arg_index);
@ -410,15 +406,15 @@ basic_printf_context<OutputIt, Char>::get_arg(unsigned arg_index) {
}
template <typename OutputIt, typename Char>
unsigned basic_printf_context<OutputIt, Char>::parse_header(
int basic_printf_context<OutputIt, Char>::parse_header(
const Char*& it, const Char* end, format_specs& specs) {
unsigned arg_index = internal::max_value<unsigned>();
int arg_index = -1;
char_type c = *it;
if (c >= '0' && c <= '9') {
// Parse an argument index (if followed by '$') or a width possibly
// preceded with '0' flag(s).
internal::error_handler eh;
unsigned value = parse_nonnegative_int(it, end, eh);
int value = parse_nonnegative_int(it, end, eh);
if (it != end && *it == '$') { // value is an argument index
++it;
arg_index = value;
@ -440,8 +436,8 @@ unsigned basic_printf_context<OutputIt, Char>::parse_header(
specs.width = parse_nonnegative_int(it, end, eh);
} else if (*it == '*') {
++it;
specs.width = visit_format_arg(
internal::printf_width_handler<char_type>(specs), get_arg());
specs.width = static_cast<int>(visit_format_arg(
internal::printf_width_handler<char_type>(specs), get_arg()));
}
}
return arg_index;
@ -468,7 +464,8 @@ OutputIt basic_printf_context<OutputIt, Char>::format() {
specs.align = align::right;
// Parse argument index, flags and width.
unsigned arg_index = parse_header(it, end, specs);
int arg_index = parse_header(it, end, specs);
if (arg_index == 0) on_error("argument index out of range");
// Parse precision.
if (it != end && *it == '.') {
@ -476,11 +473,11 @@ OutputIt basic_printf_context<OutputIt, Char>::format() {
c = it != end ? *it : 0;
if ('0' <= c && c <= '9') {
internal::error_handler eh;
specs.precision = static_cast<int>(parse_nonnegative_int(it, end, eh));
specs.precision = parse_nonnegative_int(it, end, eh);
} else if (c == '*') {
++it;
specs.precision =
visit_format_arg(internal::printf_precision_handler(), get_arg());
static_cast<int>(visit_format_arg(internal::printf_precision_handler(), get_arg()));
} else {
specs.precision = 0;
}

View File

@ -1,293 +0,0 @@
/*
* For conversion between std::chrono::durations without undefined
* behaviour or erroneous results.
* This is a stripped down version of duration_cast, for inclusion in fmt.
* See https://github.com/pauldreik/safe_duration_cast
*
* Copyright Paul Dreik 2019
*
* This file is licensed under the fmt license, see format.h
*/
#include <chrono>
#include <cmath>
#include <limits>
#include <type_traits>
#include "format.h"
FMT_BEGIN_NAMESPACE
namespace safe_duration_cast {
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) {
ec = 0;
using F = std::numeric_limits<From>;
using T = std::numeric_limits<To>;
static_assert(F::is_integer, "From must be integral");
static_assert(T::is_integer, "To must be integral");
// A and B are both signed, or both unsigned.
if (F::digits <= T::digits) {
// From fits in To without any problem.
} else {
// From does not always fit in To, resort to a dynamic check.
if (from < T::min() || from > T::max()) {
// outside range.
ec = 1;
return {};
}
}
return static_cast<To>(from);
}
/**
* converts From to To, without loss. If the dynamic value of from
* can't be converted to To without loss, ec is set.
*/
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) {
ec = 0;
using F = std::numeric_limits<From>;
using T = std::numeric_limits<To>;
static_assert(F::is_integer, "From must be integral");
static_assert(T::is_integer, "To must be integral");
if (F::is_signed && !T::is_signed) {
// From may be negative, not allowed!
if (fmt::internal::is_negative(from)) {
ec = 1;
return {};
}
// From is positive. Can it always fit in To?
if (F::digits <= T::digits) {
// yes, From always fits in To.
} else {
// from may not fit in To, we have to do a dynamic check
if (from > static_cast<From>(T::max())) {
ec = 1;
return {};
}
}
}
if (!F::is_signed && T::is_signed) {
// can from be held in To?
if (F::digits < T::digits) {
// yes, From always fits in To.
} else {
// from may not fit in To, we have to do a dynamic check
if (from > static_cast<From>(T::max())) {
// outside range.
ec = 1;
return {};
}
}
}
// reaching here means all is ok for lossless conversion.
return static_cast<To>(from);
} // function
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) {
ec = 0;
return from;
} // function
// clang-format off
/**
* converts From to To if possible, otherwise ec is set.
*
* input | output
* ---------------------------------|---------------
* NaN | NaN
* Inf | Inf
* normal, fits in output | converted (possibly lossy)
* normal, does not fit in output | ec is set
* subnormal | best effort
* -Inf | -Inf
*/
// 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) {
ec = 0;
using T = std::numeric_limits<To>;
static_assert(std::is_floating_point<From>::value, "From must be floating");
static_assert(std::is_floating_point<To>::value, "To must be floating");
// catch the only happy case
if (std::isfinite(from)) {
if (from >= T::lowest() && from <= T::max()) {
return static_cast<To>(from);
}
// not within range.
ec = 1;
return {};
}
// nan and inf will be preserved
return static_cast<To>(from);
} // function
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) {
ec = 0;
static_assert(std::is_floating_point<From>::value, "From must be floating");
return from;
}
/**
* safe duration cast between integral durations
*/
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) {
using From = std::chrono::duration<FromRep, FromPeriod>;
ec = 0;
// the basic idea is that we need to convert from count() in the from type
// to count() in the To type, by multiplying it with this:
struct Factor
: std::ratio_divide<typename From::period, typename To::period> {};
static_assert(Factor::num > 0, "num must be positive");
static_assert(Factor::den > 0, "den must be positive");
// the conversion is like this: multiply from.count() with Factor::num
// /Factor::den and convert it to To::rep, all this without
// overflow/underflow. let's start by finding a suitable type that can hold
// both To, From and Factor::num
using IntermediateRep =
typename std::common_type<typename From::rep, typename To::rep,
decltype(Factor::num)>::type;
// safe conversion to IntermediateRep
IntermediateRep count =
lossless_integral_conversion<IntermediateRep>(from.count(), ec);
if (ec) {
return {};
}
// multiply with Factor::num without overflow or underflow
if (Factor::num != 1) {
const auto max1 = internal::max_value<IntermediateRep>() / Factor::num;
if (count > max1) {
ec = 1;
return {};
}
const auto min1 = std::numeric_limits<IntermediateRep>::min() / Factor::num;
if (count < min1) {
ec = 1;
return {};
}
count *= Factor::num;
}
// this can't go wrong, right? den>0 is checked earlier.
if (Factor::den != 1) {
count /= Factor::den;
}
// convert to the to type, safely
using ToRep = typename To::rep;
const ToRep tocount = lossless_integral_conversion<ToRep>(count, ec);
if (ec) {
return {};
}
return To{tocount};
}
/**
* safe duration_cast between floating point durations
*/
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) {
using From = std::chrono::duration<FromRep, FromPeriod>;
ec = 0;
if (std::isnan(from.count())) {
// nan in, gives nan out. easy.
return To{std::numeric_limits<typename To::rep>::quiet_NaN()};
}
// maybe we should also check if from is denormal, and decide what to do about
// it.
// +-inf should be preserved.
if (std::isinf(from.count())) {
return To{from.count()};
}
// the basic idea is that we need to convert from count() in the from type
// to count() in the To type, by multiplying it with this:
struct Factor
: std::ratio_divide<typename From::period, typename To::period> {};
static_assert(Factor::num > 0, "num must be positive");
static_assert(Factor::den > 0, "den must be positive");
// the conversion is like this: multiply from.count() with Factor::num
// /Factor::den and convert it to To::rep, all this without
// overflow/underflow. let's start by finding a suitable type that can hold
// both To, From and Factor::num
using IntermediateRep =
typename std::common_type<typename From::rep, typename To::rep,
decltype(Factor::num)>::type;
// force conversion of From::rep -> IntermediateRep to be safe,
// even if it will never happen be narrowing in this context.
IntermediateRep count =
safe_float_conversion<IntermediateRep>(from.count(), ec);
if (ec) {
return {};
}
// multiply with Factor::num without overflow or underflow
if (Factor::num != 1) {
constexpr auto max1 = internal::max_value<IntermediateRep>() /
static_cast<IntermediateRep>(Factor::num);
if (count > max1) {
ec = 1;
return {};
}
constexpr auto min1 = std::numeric_limits<IntermediateRep>::lowest() /
static_cast<IntermediateRep>(Factor::num);
if (count < min1) {
ec = 1;
return {};
}
count *= static_cast<IntermediateRep>(Factor::num);
}
// this can't go wrong, right? den>0 is checked earlier.
if (Factor::den != 1) {
using common_t = typename std::common_type<IntermediateRep, intmax_t>::type;
count /= static_cast<common_t>(Factor::den);
}
// convert to the to type, safely
using ToRep = typename To::rep;
const ToRep tocount = safe_float_conversion<ToRep>(count, ec);
if (ec) {
return {};
}
return To{tocount};
}
} // namespace safe_duration_cast
FMT_END_NAMESPACE

View File

@ -8,11 +8,125 @@
#include "fmt/format-inl.h"
FMT_BEGIN_NAMESPACE
namespace internal {
template <typename T>
int format_float(char* buf, std::size_t size, const char* format, int precision,
T value) {
#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
if (precision > 100000)
throw std::runtime_error(
"fuzz mode - avoid large allocation inside snprintf");
#endif
// Suppress the warning about nonliteral format string.
auto snprintf_ptr = FMT_SNPRINTF;
return precision < 0 ? snprintf_ptr(buf, size, format, value)
: snprintf_ptr(buf, size, format, precision, value);
}
struct sprintf_specs {
int precision;
char type;
bool alt : 1;
template <typename Char>
constexpr sprintf_specs(basic_format_specs<Char> specs)
: precision(specs.precision), type(specs.type), alt(specs.alt) {}
constexpr bool has_precision() const { return precision >= 0; }
};
// This is deprecated and is kept only to preserve ABI compatibility.
template <typename Double>
char* sprintf_format(Double value, internal::buffer<char>& buf,
sprintf_specs specs) {
// Buffer capacity must be non-zero, otherwise MSVC's vsnprintf_s will fail.
FMT_ASSERT(buf.capacity() != 0, "empty buffer");
// Build format string.
enum { max_format_size = 10 }; // longest format: %#-*.*Lg
char format[max_format_size];
char* format_ptr = format;
*format_ptr++ = '%';
if (specs.alt || !specs.type) *format_ptr++ = '#';
if (specs.precision >= 0) {
*format_ptr++ = '.';
*format_ptr++ = '*';
}
if (std::is_same<Double, long double>::value) *format_ptr++ = 'L';
char type = specs.type;
if (type == '%')
type = 'f';
else if (type == 0 || type == 'n')
type = 'g';
#if FMT_MSC_VER
if (type == 'F') {
// MSVC's printf doesn't support 'F'.
type = 'f';
}
#endif
*format_ptr++ = type;
*format_ptr = '\0';
// Format using snprintf.
char* start = nullptr;
char* decimal_point_pos = nullptr;
for (;;) {
std::size_t buffer_size = buf.capacity();
start = &buf[0];
int result =
format_float(start, buffer_size, format, specs.precision, value);
if (result >= 0) {
unsigned n = internal::to_unsigned(result);
if (n < buf.capacity()) {
// Find the decimal point.
auto p = buf.data(), end = p + n;
if (*p == '+' || *p == '-') ++p;
if (specs.type != 'a' && specs.type != 'A') {
while (p < end && *p >= '0' && *p <= '9') ++p;
if (p < end && *p != 'e' && *p != 'E') {
decimal_point_pos = p;
if (!specs.type) {
// Keep only one trailing zero after the decimal point.
++p;
if (*p == '0') ++p;
while (p != end && *p >= '1' && *p <= '9') ++p;
char* where = p;
while (p != end && *p == '0') ++p;
if (p == end || *p < '0' || *p > '9') {
if (p != end) std::memmove(where, p, to_unsigned(end - p));
n -= static_cast<unsigned>(p - where);
}
}
}
}
buf.resize(n);
break; // The buffer is large enough - continue with formatting.
}
buf.reserve(n + 1);
} else {
// If result is negative we ask to increase the capacity by at least 1,
// but as std::vector, the buffer grows exponentially.
buf.reserve(buf.capacity() + 1);
}
}
return decimal_point_pos;
}
} // namespace internal
template FMT_API char* internal::sprintf_format(double, internal::buffer<char>&,
sprintf_specs);
template FMT_API char* internal::sprintf_format(long double,
internal::buffer<char>&,
sprintf_specs);
template struct FMT_API internal::basic_data<void>;
// Workaround a bug in MSVC2013 that prevents instantiation of grisu_format.
bool (*instantiate_grisu_format)(double, internal::buffer<char>&, int, unsigned,
int&) = internal::grisu_format;
// Workaround a bug in MSVC2013 that prevents instantiation of format_float.
int (*instantiate_format_float)(double, int, internal::float_specs,
internal::buffer<char>&) =
internal::format_float;
#ifndef FMT_STATIC_THOUSANDS_SEPARATOR
template FMT_API internal::locale_ref::locale_ref(const std::locale& loc);
@ -21,6 +135,7 @@ template FMT_API std::locale internal::locale_ref::get<std::locale>() const;
// Explicit instantiations for char.
template FMT_API std::string internal::grouping_impl<char>(locale_ref);
template FMT_API char internal::thousands_sep_impl(locale_ref);
template FMT_API char internal::decimal_point_impl(locale_ref);
@ -35,23 +150,27 @@ template FMT_API std::string internal::vformat<char>(
template FMT_API format_context::iterator internal::vformat_to(
internal::buffer<char>&, string_view, basic_format_args<format_context>);
template FMT_API char* internal::sprintf_format(double, internal::buffer<char>&,
sprintf_specs);
template FMT_API char* internal::sprintf_format(long double,
internal::buffer<char>&,
sprintf_specs);
template FMT_API int internal::snprintf_float(double, int,
internal::float_specs,
internal::buffer<char>&);
template FMT_API int internal::snprintf_float(long double, int,
internal::float_specs,
internal::buffer<char>&);
template FMT_API int internal::format_float(double, int, internal::float_specs,
internal::buffer<char>&);
template FMT_API int internal::format_float(long double, int,
internal::float_specs,
internal::buffer<char>&);
// Explicit instantiations for wchar_t.
template FMT_API std::string internal::grouping_impl<wchar_t>(locale_ref);
template FMT_API wchar_t internal::thousands_sep_impl(locale_ref);
template FMT_API wchar_t internal::decimal_point_impl(locale_ref);
template FMT_API void internal::buffer<wchar_t>::append(const wchar_t*,
const wchar_t*);
template FMT_API void internal::arg_map<wformat_context>::init(
const basic_format_args<wformat_context>&);
template FMT_API std::wstring internal::vformat<wchar_t>(
wstring_view, basic_format_args<wformat_context>);
FMT_END_NAMESPACE

View File

@ -13,6 +13,8 @@
#include "fmt/posix.h"
#include <climits>
#if FMT_USE_FCNTL
#include <sys/stat.h>
#include <sys/types.h>
@ -39,8 +41,8 @@
# ifdef __MINGW32__
# define _SH_DENYNO 0x40
# endif
#endif // _WIN32
#endif // FMT_USE_FCNTL
#ifdef fileno
# undef fileno
@ -94,6 +96,7 @@ int buffered_file::fileno() const {
return fd;
}
#if FMT_USE_FCNTL
file::file(cstring_view path, int oflag) {
int mode = S_IRUSR | S_IWUSR;
#if defined(_WIN32) && !defined(__MINGW32__)
@ -230,4 +233,5 @@ long getpagesize() {
return size;
#endif
}
#endif // FMT_USE_FCNTL
FMT_END_NAMESPACE

2
support/Vagrantfile vendored
View File

@ -3,7 +3,7 @@
# A vagrant config for testing against gcc-4.8.
Vagrant.configure("2") do |config|
config.vm.box = "ubuntu/trusty64"
config.vm.box = "ubuntu/xenial64"
config.vm.provider "virtualbox" do |vb|
vb.memory = "4096"

View File

@ -47,8 +47,6 @@ target_compile_definitions(gmock
set(TEST_MAIN_SRC test-main.cc gtest-extra.cc gtest-extra.h util.cc)
add_library(test-main STATIC ${TEST_MAIN_SRC})
target_compile_definitions(test-main PUBLIC
FMT_USE_FILE_DESCRIPTORS=$<BOOL:${HAVE_OPEN}>)
target_include_directories(test-main SYSTEM PUBLIC gtest gmock)
target_link_libraries(test-main gmock fmt)
@ -113,12 +111,11 @@ add_fmt_test(custom-formatter-test)
add_fmt_test(ranges-test)
add_fmt_test(scan-test)
if (HAVE_OPEN AND NOT MSVC_BUILD_STATIC)
if (NOT MSVC_BUILD_STATIC)
add_fmt_executable(posix-mock-test
posix-mock-test.cc ../src/format.cc ${TEST_MAIN_SRC})
target_include_directories(
posix-mock-test PRIVATE ${PROJECT_SOURCE_DIR}/include)
target_compile_definitions(posix-mock-test PRIVATE FMT_USE_FILE_DESCRIPTORS=1)
target_link_libraries(posix-mock-test gmock)
target_include_directories(posix-mock-test SYSTEM PUBLIC gtest gmock)
if (FMT_PEDANTIC)
@ -232,20 +229,22 @@ if (FMT_PEDANTIC AND NOT WIN32)
"-DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE}")
endif ()
# Activate optional CUDA tests if CUDA is found. For version selection, see
# Activate optional CUDA tests if CUDA is found. For version selection see
# https://docs.nvidia.com/cuda/cuda-c-programming-guide/index.html#cpp14-language-features
if (${CMAKE_VERSION} VERSION_LESS 3.15)
find_package(CUDA 9.0)
else ()
include(CheckLanguage)
check_language(CUDA)
if (CMAKE_CUDA_COMPILER)
enable_language(CUDA OPTIONAL)
set(CUDA_FOUND TRUE)
if (FMT_CUDA_TEST)
if (${CMAKE_VERSION} VERSION_LESS 3.15)
find_package(CUDA 9.0)
else ()
include(CheckLanguage)
check_language(CUDA)
if (CMAKE_CUDA_COMPILER)
enable_language(CUDA OPTIONAL)
set(CUDA_FOUND TRUE)
endif ()
endif ()
if (CUDA_FOUND)
add_subdirectory(cuda-test)
add_test(NAME cuda-test COMMAND fmt-in-cuda-test)
endif ()
endif ()
if (CUDA_FOUND)
add_subdirectory(cuda-test)
add_test(NAME cuda-test COMMAND fmt-in-cuda-test)
endif ()

View File

@ -20,3 +20,14 @@ TEST(AssertTest, Fail) {
EXPECT_DEBUG_DEATH_IF_SUPPORTED(FMT_ASSERT(false, "don't panic!"),
"don't panic!");
}
bool test_condition = false;
TEST(AssertTest, DanglingElse) {
bool executed_else = false;
if (test_condition)
FMT_ASSERT(true, "");
else
executed_else = true;
EXPECT_TRUE(executed_else);
}

View File

@ -103,7 +103,7 @@ template <typename T> struct mock_buffer : buffer<T> {
TEST(BufferTest, Ctor) {
{
mock_buffer<int> buffer;
EXPECT_EQ(nullptr, &buffer[0]);
EXPECT_EQ(nullptr, buffer.data());
EXPECT_EQ(static_cast<size_t>(0), buffer.size());
EXPECT_EQ(static_cast<size_t>(0), buffer.capacity());
}
@ -127,8 +127,13 @@ TEST(BufferTest, Ctor) {
struct dying_buffer : test_buffer<int> {
MOCK_METHOD0(die, void());
~dying_buffer() { die(); }
private:
virtual void avoid_weak_vtable();
};
void dying_buffer::avoid_weak_vtable() {}
TEST(BufferTest, VirtualDtor) {
typedef StrictMock<dying_buffer> stict_mock_buffer;
stict_mock_buffer* mock_buffer = new stict_mock_buffer();
@ -285,8 +290,6 @@ VISIT_TYPE(long, long long);
VISIT_TYPE(unsigned long, unsigned long long);
#endif
VISIT_TYPE(float, double);
#define CHECK_ARG_(Char, expected, value) \
{ \
testing::StrictMock<mock_visitor<decltype(expected)>> visitor; \
@ -450,11 +453,11 @@ template <> struct formatter<enabled_formatter> {
FMT_END_NAMESPACE
TEST(CoreTest, HasFormatter) {
using fmt::internal::has_formatter;
using fmt::has_formatter;
using context = fmt::format_context;
EXPECT_TRUE((has_formatter<enabled_formatter, context>::value));
EXPECT_FALSE((has_formatter<disabled_formatter, context>::value));
EXPECT_FALSE((has_formatter<disabled_formatter_convertible, context>::value));
static_assert(has_formatter<enabled_formatter, context>::value, "");
static_assert(!has_formatter<disabled_formatter, context>::value, "");
static_assert(!has_formatter<disabled_formatter_convertible, context>::value, "");
}
struct convertible_to_int {
@ -613,6 +616,17 @@ TEST(FormatterTest, FormatExplicitlyConvertibleToStringView) {
EXPECT_EQ("foo", fmt::format("{}", explicitly_convertible_to_string_view()));
}
#ifdef FMT_USE_STRING_VIEW
struct explicitly_convertible_to_std_string_view {
explicit operator std::string_view() const { return "foo"; }
};
TEST(FormatterTest, FormatExplicitlyConvertibleToStdStringView) {
EXPECT_EQ("foo",
fmt::format("{}", explicitly_convertible_to_std_string_view()));
}
#endif
struct explicitly_convertible_to_wstring_view {
explicit operator fmt::wstring_view() const { return L"foo"; }
};
@ -621,17 +635,15 @@ TEST(FormatterTest, FormatExplicitlyConvertibleToWStringView) {
EXPECT_EQ(L"foo",
fmt::format(L"{}", explicitly_convertible_to_wstring_view()));
}
#endif
struct explicitly_convertible_to_string_like {
template <typename String,
typename = typename std::enable_if<std::is_constructible<
String, const char*, std::size_t>::value>::type>
explicit operator String() const {
return String("foo", 3u);
}
struct disabled_rvalue_conversion {
operator const char*() const& { return "foo"; }
operator const char*()& { return "foo"; }
operator const char*() const&& = delete;
operator const char*()&& = delete;
};
TEST(FormatterTest, FormatExplicitlyConvertibleToStringLike) {
EXPECT_EQ("foo", fmt::format("{}", explicitly_convertible_to_string_like()));
TEST(FormatterTest, DisabledRValueConversion) {
EXPECT_EQ("foo", fmt::format("{}", disabled_rvalue_conversion()));
}
#endif

View File

@ -8,6 +8,7 @@
#ifndef FMT_FORMAT_
#define FMT_FORMAT_
#include <cassert>
#include <variant>
#include "fmt/format.h"
@ -666,8 +667,7 @@ struct formatter {
break;
case internal::double_type:
case internal::long_double_type:
handle_float_type_spec(type_spec,
internal::float_type_checker<decltype(eh)>(eh));
internal::parse_float_type_spec(specs_, eh);
break;
case internal::cstring_type:
internal::handle_cstring_type_spec(

View File

@ -35,6 +35,46 @@ TEST(BigIntTest, Construct) {
EXPECT_EQ("123456789abcedf0", fmt::format("{}", bigint(0x123456789abcedf0)));
}
TEST(BigIntTest, Compare) {
bigint n1(42);
bigint n2(42);
EXPECT_EQ(compare(n1, n2), 0);
n2 <<= 32;
EXPECT_LT(compare(n1, n2), 0);
bigint n3(43);
EXPECT_LT(compare(n1, n3), 0);
EXPECT_GT(compare(n3, n1), 0);
bigint n4(42 * 0x100000001);
EXPECT_LT(compare(n2, n4), 0);
EXPECT_GT(compare(n4, n2), 0);
}
TEST(BigIntTest, AddCompare) {
EXPECT_LT(
add_compare(bigint(0xffffffff), bigint(0xffffffff), bigint(1) <<= 64), 0);
EXPECT_LT(add_compare(bigint(1) <<= 32, bigint(1), bigint(1) <<= 96), 0);
EXPECT_GT(add_compare(bigint(1) <<= 32, bigint(0), bigint(0xffffffff)), 0);
EXPECT_GT(add_compare(bigint(0), bigint(1) <<= 32, bigint(0xffffffff)), 0);
EXPECT_GT(add_compare(bigint(42), bigint(1), bigint(42)), 0);
EXPECT_GT(add_compare(bigint(0xffffffff), bigint(1), bigint(0xffffffff)), 0);
EXPECT_LT(add_compare(bigint(10), bigint(10), bigint(22)), 0);
EXPECT_LT(add_compare(bigint(0x100000010), bigint(0x100000010),
bigint(0x300000010)),
0);
EXPECT_GT(add_compare(bigint(0x1ffffffff), bigint(0x100000002),
bigint(0x300000000)),
0);
EXPECT_EQ(add_compare(bigint(0x1ffffffff), bigint(0x100000002),
bigint(0x300000001)),
0);
EXPECT_LT(add_compare(bigint(0x1ffffffff), bigint(0x100000002),
bigint(0x300000002)),
0);
EXPECT_LT(add_compare(bigint(0x1ffffffff), bigint(0x100000002),
bigint(0x300000003)),
0);
}
TEST(BigIntTest, ShiftLeft) {
bigint n(0x42);
n <<= 0;
@ -47,30 +87,133 @@ TEST(BigIntTest, ShiftLeft) {
TEST(BigIntTest, Multiply) {
bigint n(0x42);
EXPECT_THROW(n *= 0, assertion_failure);
n *= 1;
EXPECT_EQ("42", fmt::format("{}", n));
n *= 2;
EXPECT_EQ("84", fmt::format("{}", n));
n *= 0x12345678;
EXPECT_EQ("962fc95e0", fmt::format("{}", n));
auto max = max_value<uint32_t>();
bigint bigmax(max);
bigmax *= max;
bigint bigmax(max_value<uint32_t>());
bigmax *= max_value<uint32_t>();
EXPECT_EQ("fffffffe00000001", fmt::format("{}", bigmax));
bigmax.assign(max_value<uint64_t>());
bigmax *= max_value<uint64_t>();
EXPECT_EQ("fffffffffffffffe0000000000000001", fmt::format("{}", bigmax));
}
template <bool is_iec559> void test_construct_from_double() {
TEST(BigIntTest, Accumulator) {
fmt::internal::accumulator acc;
EXPECT_EQ(acc.lower, 0);
EXPECT_EQ(acc.upper, 0);
acc.upper = 12;
acc.lower = 34;
EXPECT_EQ(static_cast<uint32_t>(acc), 34);
acc += 56;
EXPECT_EQ(acc.lower, 90);
acc += fmt::internal::max_value<uint64_t>();
EXPECT_EQ(acc.upper, 13);
EXPECT_EQ(acc.lower, 89);
acc >>= 32;
EXPECT_EQ(acc.upper, 0);
EXPECT_EQ(acc.lower, 13 * 0x100000000);
}
TEST(BigIntTest, Square) {
bigint n0(0);
n0.square();
EXPECT_EQ("0", fmt::format("{}", n0));
bigint n1(0x100);
n1.square();
EXPECT_EQ("10000", fmt::format("{}", n1));
bigint n2(0xfffffffff);
n2.square();
EXPECT_EQ("ffffffffe000000001", fmt::format("{}", n2));
bigint n3(max_value<uint64_t>());
n3.square();
EXPECT_EQ("fffffffffffffffe0000000000000001", fmt::format("{}", n3));
bigint n4;
n4.assign_pow10(10);
EXPECT_EQ("2540be400", fmt::format("{}", n4));
}
TEST(BigIntTest, DivModAssignZeroDivisor) {
bigint zero(0);
EXPECT_THROW(bigint(0).divmod_assign(zero), assertion_failure);
EXPECT_THROW(bigint(42).divmod_assign(zero), assertion_failure);
}
TEST(BigIntTest, DivModAssignSelf) {
bigint n(100);
EXPECT_THROW(n.divmod_assign(n), assertion_failure);
}
TEST(BigIntTest, DivModAssignUnaligned) {
// (42 << 340) / pow(10, 100):
bigint n1(42);
n1 <<= 340;
bigint n2;
n2.assign_pow10(100);
int result = n1.divmod_assign(n2);
EXPECT_EQ(result, 9406);
EXPECT_EQ("10f8353019583bfc29ffc8f564e1b9f9d819dbb4cf783e4507eca1539220p96",
fmt::format("{}", n1));
}
TEST(BigIntTest, DivModAssign) {
// 100 / 10:
bigint n1(100);
int result = n1.divmod_assign(bigint(10));
EXPECT_EQ(result, 10);
EXPECT_EQ("0", fmt::format("{}", n1));
// pow(10, 100) / (42 << 320):
n1.assign_pow10(100);
result = n1.divmod_assign(bigint(42) <<= 320);
EXPECT_EQ(result, 111);
EXPECT_EQ("13ad2594c37ceb0b2784c4ce0bf38ace408e211a7caab24308a82e8f10p96",
fmt::format("{}", n1));
// 42 / 100:
bigint n2(42);
n1.assign_pow10(2);
result = n2.divmod_assign(n1);
EXPECT_EQ(result, 0);
EXPECT_EQ("2a", fmt::format("{}", n2));
}
template <bool is_iec559> void run_double_tests() {
fmt::print("warning: double is not IEC559, skipping FP tests\n");
}
template <> void test_construct_from_double<true>() {
auto v = fp(1.23);
EXPECT_EQ(v.f, 0x13ae147ae147aeu);
EXPECT_EQ(v.e, -52);
template <> void run_double_tests<true>() {
// Construct from double.
EXPECT_EQ(fp(1.23), fp(0x13ae147ae147aeu, -52));
// Compute boundaries:
fp value;
// Normalized & not power of 2 - equidistant boundaries:
auto b = value.assign_with_boundaries(1.23);
EXPECT_EQ(value, fp(0x0013ae147ae147ae, -52));
EXPECT_EQ(b.lower, 0x9d70a3d70a3d6c00);
EXPECT_EQ(b.upper, 0x9d70a3d70a3d7400);
// Normalized power of 2 - lower boundary is closer:
b = value.assign_with_boundaries(1.9807040628566084e+28); // 2**94
EXPECT_EQ(value, fp(0x0010000000000000, 42));
EXPECT_EQ(b.lower, 0x7ffffffffffffe00);
EXPECT_EQ(b.upper, 0x8000000000000400);
// Smallest normalized double - equidistant boundaries:
b = value.assign_with_boundaries(2.2250738585072014e-308);
EXPECT_EQ(value, fp(0x0010000000000000, -1074));
EXPECT_EQ(b.lower, 0x7ffffffffffffc00);
EXPECT_EQ(b.upper, 0x8000000000000400);
// Subnormal - equidistant boundaries:
b = value.assign_with_boundaries(4.9406564584124654e-324);
EXPECT_EQ(value, fp(0x0000000000000001, -1074));
EXPECT_EQ(b.lower, 0x4000000000000000);
EXPECT_EQ(b.upper, 0xc000000000000000);
}
TEST(FPTest, ConstructFromDouble) {
test_construct_from_double<std::numeric_limits<double>::is_iec559>();
TEST(FPTest, DoubleTests) {
run_double_tests<std::numeric_limits<double>::is_iec559>();
}
TEST(FPTest, Normalize) {
@ -80,30 +223,31 @@ TEST(FPTest, Normalize) {
EXPECT_EQ(-6, normalized.e);
}
TEST(FPTest, ComputeBoundariesSubnormal) {
auto v = fp(0xbeef, 42);
fp lower, upper;
v.compute_boundaries(lower, upper);
EXPECT_EQ(0xbeee800000000000, lower.f);
EXPECT_EQ(-6, lower.e);
EXPECT_EQ(0xbeef800000000000, upper.f);
EXPECT_EQ(-6, upper.e);
}
TEST(FPTest, ComputeBoundaries) {
auto v = fp(0x10000000000000, 42);
fp lower, upper;
v.compute_boundaries(lower, upper);
EXPECT_EQ(0x7ffffffffffffe00, lower.f);
EXPECT_EQ(31, lower.e);
EXPECT_EQ(0x8000000000000400, upper.f);
EXPECT_EQ(31, upper.e);
}
TEST(FPTest, Subtract) {
auto v = fp(123, 1) - fp(102, 1);
EXPECT_EQ(v.f, 21u);
EXPECT_EQ(v.e, 1);
TEST(FPTest, ComputeFloatBoundaries) {
struct {
double x, lower, upper;
} tests[] = {
// regular
{1.5f, 1.4999999403953552, 1.5000000596046448},
// boundary
{1.0f, 0.9999999701976776, 1.0000000596046448},
// min normal
{1.1754944e-38f, 1.1754942807573643e-38, 1.1754944208872107e-38},
// max subnormal
{1.1754942e-38f, 1.1754941406275179e-38, 1.1754942807573643e-38},
// min subnormal
{1e-45f, 7.006492321624085e-46, 2.1019476964872256e-45},
};
for (auto test : tests) {
fp vlower = normalize(fp(test.lower));
fp vupper = normalize(fp(test.upper));
vlower.f >>= vupper.e - vlower.e;
vlower.e = vupper.e;
fp value;
auto b = value.assign_float_with_boundaries(test.x);
EXPECT_EQ(vlower.f, b.lower);
EXPECT_EQ(vupper.f, b.upper);
}
}
TEST(FPTest, Multiply) {
@ -133,7 +277,7 @@ TEST(FPTest, GetRoundDirection) {
EXPECT_EQ(fmt::internal::up, get_round_direction(100, 51, 0));
EXPECT_EQ(fmt::internal::down, get_round_direction(100, 40, 10));
EXPECT_EQ(fmt::internal::up, get_round_direction(100, 60, 10));
for (int i = 41; i < 60; ++i)
for (size_t i = 41; i < 60; ++i)
EXPECT_EQ(fmt::internal::unknown, get_round_direction(100, i, 10));
uint64_t max = max_value<uint64_t>();
EXPECT_THROW(get_round_direction(100, 100, 0), assertion_failure);
@ -174,8 +318,7 @@ TEST(FPTest, FixedHandler) {
TEST(FPTest, GrisuFormatCompilesWithNonIEEEDouble) {
fmt::memory_buffer buf;
int exp = 0;
grisu_format(4.2f, buf, -1, false, exp);
format_float(0.42, -1, fmt::internal::float_specs(), buf);
}
template <typename T> struct value_extractor {
@ -185,14 +328,14 @@ template <typename T> struct value_extractor {
throw std::runtime_error(fmt::format("invalid type {}", typeid(U).name()));
}
#ifdef __apple_build_version__
#if FMT_USE_INT128
// Apple Clang does not define typeid for __int128_t and __uint128_t.
FMT_NORETURN T operator()(__int128_t) {
throw std::runtime_error(fmt::format("invalid type {}", "__int128_t"));
FMT_NORETURN T operator()(fmt::internal::int128_t) {
throw std::runtime_error("invalid type __int128_t");
}
FMT_NORETURN T operator()(__uint128_t) {
throw std::runtime_error(fmt::format("invalid type {}", "__uint128_t"));
FMT_NORETURN T operator()(fmt::internal::uint128_t) {
throw std::runtime_error("invalid type __uint128_t");
}
#endif
};
@ -289,8 +432,7 @@ TEST(FormatTest, CountCodePoints) {
// Tests fmt::internal::count_digits for integer type Int.
template <typename Int> void test_count_digits() {
for (Int i = 0; i < 10; ++i) EXPECT_EQ(1u, fmt::internal::count_digits(i));
for (Int i = 1, n = 1, end = max_value<Int>() / 10; n <= end;
++i) {
for (Int i = 1, n = 1, end = max_value<Int>() / 10; n <= end; ++i) {
n *= 10;
EXPECT_EQ(i, fmt::internal::count_digits(n - 1));
EXPECT_EQ(i + 1, fmt::internal::count_digits(n));
@ -305,7 +447,7 @@ TEST(UtilTest, CountDigits) {
TEST(UtilTest, WriteUIntPtr) {
fmt::memory_buffer buf;
fmt::internal::writer writer(buf);
writer.write_pointer(fmt::internal::bit_cast<fmt::internal::fallback_uintptr>(
writer.write_pointer(fmt::internal::fallback_uintptr(
reinterpret_cast<void*>(0xface)),
nullptr);
EXPECT_EQ("0xface", to_string(buf));

View File

@ -20,8 +20,14 @@
# include <windows.h>
#endif
// Check if fmt/format.h compiles with the X11 index macro defined.
#define index(x, y) no nice things
#include "fmt/color.h"
#include "fmt/format.h"
#undef index
#include "gmock.h"
#include "gtest-extra.h"
#include "mock-allocator.h"
@ -34,13 +40,13 @@
using std::size_t;
using fmt::basic_memory_buffer;
using fmt::internal::basic_writer;
using fmt::internal::max_value;
using fmt::format;
using fmt::format_error;
using fmt::memory_buffer;
using fmt::string_view;
using fmt::wmemory_buffer;
using fmt::internal::basic_writer;
using fmt::internal::max_value;
using testing::Return;
using testing::StrictMock;
@ -163,8 +169,7 @@ TEST(UtilTest, Increment) {
}
TEST(UtilTest, ParseNonnegativeInt) {
if (max_value<int>() !=
static_cast<int>(static_cast<unsigned>(1) << 31)) {
if (max_value<int>() != static_cast<int>(static_cast<unsigned>(1) << 31)) {
fmt::print("Skipping parse_nonnegative_int test\n");
return;
}
@ -182,7 +187,7 @@ TEST(UtilTest, ParseNonnegativeInt) {
}
TEST(IteratorTest, CountingIterator) {
fmt::internal::counting_iterator<char> it;
fmt::internal::counting_iterator it;
auto prev = it++;
EXPECT_EQ(prev.count(), 0);
EXPECT_EQ(it.count(), 1);
@ -271,7 +276,7 @@ static void check_move_buffer(
EXPECT_EQ(alloc, buffer2.get_allocator().get());
}
TEST(MemoryBufferTest, MoveCtor) {
TEST(MemoryBufferTest, MoveCtorInlineBuffer) {
std::allocator<char> alloc;
basic_memory_buffer<char, 5, TestAllocator> buffer((TestAllocator(&alloc)));
const char test[] = "test";
@ -281,15 +286,22 @@ TEST(MemoryBufferTest, MoveCtor) {
// dynamic allocation.
buffer.push_back('a');
check_move_buffer("testa", buffer);
}
TEST(MemoryBufferTest, MoveCtorDynamicBuffer) {
std::allocator<char> alloc;
basic_memory_buffer<char, 4, TestAllocator> buffer((TestAllocator(&alloc)));
const char test[] = "test";
buffer.append(test, test + 4);
const char* inline_buffer_ptr = &buffer[0];
// Adding one more character causes the content to move from the inline to
// a dynamically allocated buffer.
buffer.push_back('b');
basic_memory_buffer<char, 5, TestAllocator> buffer2(std::move(buffer));
buffer.push_back('a');
basic_memory_buffer<char, 4, TestAllocator> buffer2(std::move(buffer));
// Move should rip the guts of the first buffer.
EXPECT_EQ(inline_buffer_ptr, &buffer[0]);
EXPECT_EQ("testab", std::string(&buffer2[0], buffer2.size()));
EXPECT_GT(buffer2.capacity(), 5u);
EXPECT_EQ("testa", std::string(&buffer2[0], buffer2.size()));
EXPECT_GT(buffer2.capacity(), 4u);
}
static void check_move_assign_buffer(const char* str,
@ -526,9 +538,8 @@ TEST(UtilTest, FormatWindowsError) {
EXPECT_EQ(fmt::format("test: {}", utf8_message.str()),
fmt::to_string(actual_message));
actual_message.resize(0);
fmt::internal::format_windows_error(
actual_message, ERROR_FILE_EXISTS,
fmt::string_view(0, max_value<size_t>()));
fmt::internal::format_windows_error(actual_message, ERROR_FILE_EXISTS,
fmt::string_view(0, max_value<size_t>()));
EXPECT_EQ(fmt::format("error {}", ERROR_FILE_EXISTS),
fmt::to_string(actual_message));
}
@ -674,9 +685,7 @@ TEST(WriterTest, WriteString) {
CHECK_WRITE_WCHAR("abc");
}
TEST(WriterTest, WriteWideString) {
CHECK_WRITE_WCHAR(L"abc");
}
TEST(WriterTest, WriteWideString) { CHECK_WRITE_WCHAR(L"abc"); }
TEST(FormatToTest, FormatWithoutArgs) {
std::string s;
@ -1189,12 +1198,37 @@ TEST(FormatterTest, Precision) {
"precision not allowed for this argument type");
EXPECT_THROW_MSG(format("{0:.2f}", 42ull), format_error,
"precision not allowed for this argument type");
EXPECT_THROW_MSG(format("{0:.2%}", 42), format_error,
"precision not allowed for this argument type");
EXPECT_THROW_MSG(format("{0:3.0}", 'x'), format_error,
"precision not allowed for this argument type");
EXPECT_EQ("1.2", format("{0:.2}", 1.2345));
EXPECT_EQ("1.2", format("{0:.2}", 1.2345l));
EXPECT_EQ("1.2e+56", format("{:.2}", 1.234e56));
EXPECT_EQ("1e+00", format("{:.0e}", 1.0L));
EXPECT_EQ(" 0.0e+00", format("{:9.1e}", 0.0));
EXPECT_EQ(
"4.9406564584124654417656879286822137236505980261432476442558568250067550"
"727020875186529983636163599237979656469544571773092665671035593979639877"
"479601078187812630071319031140452784581716784898210368871863605699873072"
"305000638740915356498438731247339727316961514003171538539807412623856559"
"117102665855668676818703956031062493194527159149245532930545654440112748"
"012970999954193198940908041656332452475714786901472678015935523861155013"
"480352649347201937902681071074917033322268447533357208324319361e-324",
format("{:.494}", 4.9406564584124654E-324));
EXPECT_EQ(
"-0X1.41FE3FFE71C9E000000000000000000000000000000000000000000000000000000"
"000000000000000000000000000000000000000000000000000000000000000000000000"
"000000000000000000000000000000000000000000000000000000000000000000000000"
"000000000000000000000000000000000000000000000000000000000000000000000000"
"000000000000000000000000000000000000000000000000000000000000000000000000"
"000000000000000000000000000000000000000000000000000000000000000000000000"
"000000000000000000000000000000000000000000000000000000000000000000000000"
"000000000000000000000000000000000000000000000000000000000000000000000000"
"000000000000000000000000000000000000000000000000000000000000000000000000"
"000000000000000000000000000000000000000000000000000000000000000000000000"
"000000000000000000000000000000000000000000000000000000000000000000000000"
"000000000000000000000000000000000000000000000000000P+127",
format("{:.838A}", -2.14001164E+38));
EXPECT_EQ("123.", format("{:#.0f}", 123.0));
EXPECT_THROW_MSG(format("{0:.2}", reinterpret_cast<void*>(0xcafe)),
format_error,
@ -1462,11 +1496,7 @@ TEST(FormatterTest, FormatOct) {
}
TEST(FormatterTest, FormatIntLocale) {
EXPECT_EQ("123", format("{:n}", 123));
EXPECT_EQ("1,234", format("{:n}", 1234));
EXPECT_EQ("1,234,567", format("{:n}", 1234567));
EXPECT_EQ("4,294,967,295",
format("{:n}", max_value<uint32_t>()));
EXPECT_EQ("1234", format("{:n}", 1234));
}
struct ConvertibleToLongLong {
@ -1479,7 +1509,6 @@ TEST(FormatterTest, FormatConvertibleToLongLong) {
TEST(FormatterTest, FormatFloat) {
EXPECT_EQ("392.500000", format("{0:f}", 392.5f));
EXPECT_EQ("12.500000%", format("{0:%}", 0.125f));
}
TEST(FormatterTest, FormatDouble) {
@ -1492,8 +1521,6 @@ TEST(FormatterTest, FormatDouble) {
EXPECT_EQ("392.65", format("{:G}", 392.65));
EXPECT_EQ("392.650000", format("{:f}", 392.65));
EXPECT_EQ("392.650000", format("{:F}", 392.65));
EXPECT_EQ("12.500000%", format("{:%}", 0.125));
EXPECT_EQ("12.34%", format("{:.2%}", 0.1234432));
char buffer[BUFFER_SIZE];
safe_sprintf(buffer, "%e", 392.65);
EXPECT_EQ(buffer, format("{0:e}", 392.65));
@ -1533,7 +1560,6 @@ TEST(FormatterTest, FormatNaN) {
EXPECT_EQ("nan ", format("{:<7}", nan));
EXPECT_EQ(" nan ", format("{:^7}", nan));
EXPECT_EQ(" nan", format("{:>7}", nan));
EXPECT_EQ("nan%", format("{:%}", nan));
}
TEST(FormatterTest, FormatInfinity) {
@ -1546,7 +1572,6 @@ TEST(FormatterTest, FormatInfinity) {
EXPECT_EQ("inf ", format("{:<7}", inf));
EXPECT_EQ(" inf ", format("{:^7}", inf));
EXPECT_EQ(" inf", format("{:>7}", inf));
EXPECT_EQ("inf%", format("{:%}", inf));
}
TEST(FormatterTest, FormatLongDouble) {
@ -1557,8 +1582,6 @@ TEST(FormatterTest, FormatLongDouble) {
EXPECT_EQ("392.65", format("{0:G}", 392.65l));
EXPECT_EQ("392.650000", format("{0:f}", 392.65l));
EXPECT_EQ("392.650000", format("{0:F}", 392.65l));
EXPECT_EQ("12.500000%", format("{:%}", 0.125l));
EXPECT_EQ("12.34%", format("{:.2%}", 0.1234432l));
char buffer[BUFFER_SIZE];
safe_sprintf(buffer, "%Le", 392.65l);
EXPECT_EQ(buffer, format("{0:e}", 392.65l));
@ -1662,6 +1685,26 @@ TEST(FormatterTest, FormatStdStringView) {
EXPECT_EQ("test", format("{}", std::string_view("test")));
EXPECT_EQ("foo", format("{}", string_viewable()));
}
struct explicitly_convertible_to_std_string_view {
explicit operator std::string_view() const { return "foo"; }
};
namespace fmt {
template <>
struct formatter<explicitly_convertible_to_std_string_view>
: formatter<std::string_view> {
auto format(const explicitly_convertible_to_std_string_view& v,
format_context& ctx) -> decltype(ctx.out()) {
return format_to(ctx.out(), "'{}'", std::string_view(v));
}
};
} // namespace fmt
TEST(FormatterTest, FormatExplicitlyConvertibleToStdStringView) {
EXPECT_EQ("'foo'",
fmt::format("{}", explicitly_convertible_to_std_string_view()));
}
#endif
FMT_BEGIN_NAMESPACE
@ -1805,12 +1848,11 @@ TEST(FormatIntTest, FormatInt) {
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(os.str(), fmt::format_int(max_value<int64_t>()).str());
}
TEST(FormatTest, Print) {
#if FMT_USE_FILE_DESCRIPTORS
#if FMT_USE_FCNTL
EXPECT_WRITE(stdout, fmt::print("Don't {}!", "panic"), "Don't panic!");
EXPECT_WRITE(stderr, fmt::print(stderr, "Don't {}!", "panic"),
"Don't panic!");
@ -1831,7 +1873,7 @@ TEST(FormatTest, Dynamic) {
std::string result = fmt::vformat(
"{} and {} and {}", fmt::basic_format_args<ctx>(
args.data(), static_cast<unsigned>(args.size())));
args.data(), static_cast<int>(args.size())));
EXPECT_EQ("42 and abc1 and 1.5", result);
}
@ -1917,7 +1959,7 @@ TEST(FormatTest, CustomFormatCompileTimeString) {
EXPECT_EQ("42", fmt::format(FMT_STRING("{}"), Answer()));
Answer answer;
EXPECT_EQ("42", fmt::format(FMT_STRING("{}"), answer));
const Answer const_answer;
const Answer const_answer = Answer();
EXPECT_EQ("42", fmt::format(FMT_STRING("{}"), const_answer));
}
@ -1965,8 +2007,9 @@ enum TestEnum { A };
TEST(FormatTest, Enum) { EXPECT_EQ("0", fmt::format("{}", A)); }
TEST(FormatTest, FormatterNotSpecialized) {
EXPECT_FALSE((fmt::internal::has_formatter<fmt::formatter<TestEnum>,
fmt::format_context>::value));
static_assert(
!fmt::has_formatter<fmt::formatter<TestEnum>, fmt::format_context>::value,
"");
}
#if FMT_HAS_FEATURE(cxx_strong_enums)
@ -2144,12 +2187,12 @@ TEST(FormatTest, WideFormatToN) {
struct test_arg_id_handler {
enum result { NONE, EMPTY, INDEX, NAME, ERROR };
result res = NONE;
unsigned index = 0;
int index = 0;
string_view name;
FMT_CONSTEXPR void operator()() { res = EMPTY; }
FMT_CONSTEXPR void operator()(unsigned i) {
FMT_CONSTEXPR void operator()(int i) {
res = INDEX;
index = i;
}
@ -2185,9 +2228,9 @@ struct test_format_specs_handler {
fmt::align_t align = fmt::align::none;
char fill = 0;
unsigned width = 0;
int width = 0;
fmt::internal::arg_ref<char> width_ref;
unsigned precision = 0;
int precision = 0;
fmt::internal::arg_ref<char> precision_ref;
char type = 0;
@ -2213,14 +2256,14 @@ struct test_format_specs_handler {
FMT_CONSTEXPR void on_hash() { res = HASH; }
FMT_CONSTEXPR void on_zero() { res = ZERO; }
FMT_CONSTEXPR void on_width(unsigned w) { width = w; }
FMT_CONSTEXPR void on_width(int w) { width = w; }
FMT_CONSTEXPR void on_dynamic_width(fmt::internal::auto_id) {}
FMT_CONSTEXPR void on_dynamic_width(unsigned index) { width_ref = index; }
FMT_CONSTEXPR void on_dynamic_width(int index) { width_ref = index; }
FMT_CONSTEXPR void on_dynamic_width(string_view) {}
FMT_CONSTEXPR void on_precision(unsigned p) { precision = p; }
FMT_CONSTEXPR void on_precision(int p) { precision = p; }
FMT_CONSTEXPR void on_dynamic_precision(fmt::internal::auto_id) {}
FMT_CONSTEXPR void on_dynamic_precision(unsigned index) {
FMT_CONSTEXPR void on_dynamic_precision(int index) {
precision_ref = index;
}
FMT_CONSTEXPR void on_dynamic_precision(string_view) {}
@ -2257,7 +2300,7 @@ TEST(FormatTest, ConstexprParseFormatSpecs) {
struct test_parse_context {
typedef char char_type;
FMT_CONSTEXPR unsigned next_arg_id() { return 11; }
FMT_CONSTEXPR int next_arg_id() { return 11; }
template <typename Id> FMT_CONSTEXPR void check_arg_id(Id) {}
FMT_CONSTEXPR const char* begin() { return nullptr; }
@ -2286,9 +2329,9 @@ struct test_context {
template <size_t N>
FMT_CONSTEXPR fmt::format_specs parse_specs(const char (&s)[N]) {
fmt::format_specs specs;
test_parse_context parse_ctx;
test_context ctx{};
auto specs = fmt::format_specs();
auto parse_ctx = test_parse_context();
auto ctx = test_context();
fmt::internal::specs_handler<test_parse_context, test_context> h(
specs, parse_ctx, ctx);
parse_format_specs(s, s + N, h);
@ -2448,11 +2491,11 @@ TEST(FormatTest, FormatStringErrors) {
EXPECT_ERROR("{:10000000000}", "number is too big", int);
EXPECT_ERROR("{:.10000000000}", "number is too big", int);
EXPECT_ERROR_NOARGS("{:x}", "argument index out of range");
#if FMT_NUMERIC_ALIGN
# if FMT_NUMERIC_ALIGN
EXPECT_ERROR("{0:=5", "unknown format specifier", int);
EXPECT_ERROR("{:=}", "format specifier requires numeric argument",
const char*);
#endif
# endif
EXPECT_ERROR("{:+}", "format specifier requires numeric argument",
const char*);
EXPECT_ERROR("{:-}", "format specifier requires numeric argument",
@ -2608,3 +2651,13 @@ TEST(FormatTest, FormatCustomChar) {
EXPECT_EQ(result.size(), 1);
EXPECT_EQ(result[0], mychar('x'));
}
TEST(FormatTest, FormatUTF8Precision) {
using str_type = std::basic_string<char8_t>;
str_type format(reinterpret_cast<const char8_t*>(u8"{:.4}"));
str_type str(reinterpret_cast<const char8_t*>(u8"caf\u00e9s")); // cafés
auto result = fmt::format(format, str);
EXPECT_EQ(fmt::internal::count_code_points(result), 4);
EXPECT_EQ(result.size(), 5);
EXPECT_EQ(result, str.substr(0, 5));
}

View File

@ -48,10 +48,28 @@ TEST(GrisuTest, Prettify) {
EXPECT_EQ("1e-05", fmt::format("{}", 1e-5));
EXPECT_EQ("9.999e-05", fmt::format("{}", 9.999e-5));
EXPECT_EQ("10000000000.0", fmt::format("{}", 1e10));
EXPECT_EQ("1e+11", fmt::format("{}", 1e11));
EXPECT_EQ("100000000000.0", fmt::format("{}", 1e11));
EXPECT_EQ("12340000000.0", 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("0.10000000149011612", fmt::format("{}", double(0.1f)));
}
TEST(GrisuTest, ZeroPrecision) { EXPECT_EQ("1", fmt::format("{:.0}", 1.0)); }
TEST(GrisuTest, Fallback) {
EXPECT_EQ("1e+23", fmt::format("{}", 1e23));
EXPECT_EQ("9e-265", fmt::format("{}", 9e-265));
EXPECT_EQ("5.423717798060526e+125",
fmt::format("{}", 5.423717798060526e+125));
EXPECT_EQ("1.372371880954233e-288",
fmt::format("{}", 1.372371880954233e-288));
EXPECT_EQ("55388492.622190244", fmt::format("{}", 55388492.622190244));
EXPECT_EQ("2.2506787569811123e-253",
fmt::format("{}", 2.2506787569811123e-253));
EXPECT_EQ("1103618912042992.8", fmt::format("{}", 1103618912042992.8));
// pow(2, -25) - assymetric boundaries:
EXPECT_EQ("2.9802322387695312e-08",
fmt::format("{}", 2.9802322387695312e-08));
}

View File

@ -74,14 +74,6 @@ TEST_F(SingleEvaluationTest, FailedEXPECT_SYSTEM_ERROR) {
EXPECT_EQ(s_ + 1, p_);
}
// Tests that when EXPECT_WRITE fails, it evaluates its message argument
// exactly once.
TEST_F(SingleEvaluationTest, FailedEXPECT_WRITE) {
EXPECT_NONFATAL_FAILURE(EXPECT_WRITE(stdout, std::printf("test"), p_++),
"01234");
EXPECT_EQ(s_ + 1, p_);
}
// Tests that assertion arguments are evaluated exactly once.
TEST_F(SingleEvaluationTest, ExceptionTests) {
// successful EXPECT_THROW_MSG
@ -163,6 +155,15 @@ TEST_F(SingleEvaluationTest, SystemErrorTests) {
EXPECT_EQ(4, b_);
}
#if FMT_USE_FCNTL
// Tests that when EXPECT_WRITE fails, it evaluates its message argument
// exactly once.
TEST_F(SingleEvaluationTest, FailedEXPECT_WRITE) {
EXPECT_NONFATAL_FAILURE(EXPECT_WRITE(stdout, std::printf("test"), p_++),
"01234");
EXPECT_EQ(s_ + 1, p_);
}
// Tests that assertion arguments are evaluated exactly once.
TEST_F(SingleEvaluationTest, WriteTests) {
// successful EXPECT_WRITE
@ -187,6 +188,24 @@ TEST_F(SingleEvaluationTest, WriteTests) {
EXPECT_EQ(2, b_);
}
// Tests EXPECT_WRITE.
TEST(ExpectTest, EXPECT_WRITE) {
EXPECT_WRITE(stdout, do_nothing(), "");
EXPECT_WRITE(stdout, std::printf("test"), "test");
EXPECT_WRITE(stderr, std::fprintf(stderr, "test"), "test");
EXPECT_NONFATAL_FAILURE(EXPECT_WRITE(stdout, std::printf("that"), "this"),
"Expected: this\n"
" Actual: that");
}
TEST(StreamingAssertionsTest, EXPECT_WRITE) {
EXPECT_WRITE(stdout, std::printf("test"), "test") << "unexpected failure";
EXPECT_NONFATAL_FAILURE(EXPECT_WRITE(stdout, std::printf("test"), "other")
<< "expected failure",
"expected failure");
}
#endif // FMT_USE_FCNTL
// Tests that the compiler will not complain about unreachable code in the
// EXPECT_THROW_MSG macro.
TEST(ExpectThrowTest, DoesNotGenerateUnreachableCodeWarning) {
@ -280,16 +299,6 @@ TEST(ExpectTest, EXPECT_SYSTEM_ERROR) {
format_system_error(EDOM, "test")));
}
// Tests EXPECT_WRITE.
TEST(ExpectTest, EXPECT_WRITE) {
EXPECT_WRITE(stdout, do_nothing(), "");
EXPECT_WRITE(stdout, std::printf("test"), "test");
EXPECT_WRITE(stderr, std::fprintf(stderr, "test"), "test");
EXPECT_NONFATAL_FAILURE(EXPECT_WRITE(stdout, std::printf("that"), "this"),
"Expected: this\n"
" Actual: that");
}
TEST(StreamingAssertionsTest, EXPECT_THROW_MSG) {
EXPECT_THROW_MSG(throw_exception(), std::exception, "test")
<< "unexpected failure";
@ -308,20 +317,13 @@ TEST(StreamingAssertionsTest, EXPECT_SYSTEM_ERROR) {
"expected failure");
}
TEST(StreamingAssertionsTest, EXPECT_WRITE) {
EXPECT_WRITE(stdout, std::printf("test"), "test") << "unexpected failure";
EXPECT_NONFATAL_FAILURE(EXPECT_WRITE(stdout, std::printf("test"), "other")
<< "expected failure",
"expected failure");
}
TEST(UtilTest, FormatSystemError) {
fmt::memory_buffer out;
fmt::format_system_error(out, EDOM, "test message");
EXPECT_EQ(to_string(out), format_system_error(EDOM, "test message"));
}
#if FMT_USE_FILE_DESCRIPTORS
#if FMT_USE_FCNTL
using fmt::buffered_file;
using fmt::error_code;

View File

@ -7,7 +7,7 @@
#include "gtest-extra.h"
#if FMT_USE_FILE_DESCRIPTORS
#if FMT_USE_FCNTL
using fmt::file;
@ -78,7 +78,7 @@ std::string read(file& f, std::size_t count) {
return buffer;
}
#endif // FMT_USE_FILE_DESCRIPTORS
#endif // FMT_USE_FCNTL
std::string format_system_error(int error_code, fmt::string_view message) {
fmt::memory_buffer out;

View File

@ -10,16 +10,7 @@
#include <string>
#include "gmock.h"
#include "fmt/core.h"
#ifndef FMT_USE_FILE_DESCRIPTORS
# define FMT_USE_FILE_DESCRIPTORS 0
#endif
#if FMT_USE_FILE_DESCRIPTORS
# include "fmt/posix.h"
#endif
#include "fmt/posix.h"
#define FMT_TEST_THROW_(statement, expected_exception, expected_message, fail) \
GTEST_AMBIGUOUS_ELSE_BLOCKER_ \
@ -65,7 +56,7 @@ std::string format_system_error(int error_code, fmt::string_view message);
EXPECT_THROW_MSG(statement, fmt::system_error, \
format_system_error(error_code, message))
#if FMT_USE_FILE_DESCRIPTORS
#if FMT_USE_FCNTL
// Captures file output by redirecting it to a pipe.
// The output it can handle is limited by the pipe capacity.
@ -151,7 +142,9 @@ std::string read(fmt::file& f, std::size_t count);
# define EXPECT_READ(file, expected_content) \
EXPECT_EQ(expected_content, read(file, std::strlen(expected_content)))
#endif // FMT_USE_FILE_DESCRIPTORS
#else
# define EXPECT_WRITE(file, statement, expected_output) SUCCEED()
#endif // FMT_USE_FCNTL
template <typename Mock> struct ScopedMock : testing::StrictMock<Mock> {
ScopedMock() { Mock::instance = this; }

View File

@ -8,45 +8,82 @@
#include "fmt/locale.h"
#include "gmock.h"
using fmt::internal::max_value;
#ifndef FMT_STATIC_THOUSANDS_SEPARATOR
template <typename Char> struct numpunct : std::numpunct<Char> {
protected:
Char do_decimal_point() const FMT_OVERRIDE { return '?'; }
std::string do_grouping() const FMT_OVERRIDE { return "\03"; }
Char do_thousands_sep() const FMT_OVERRIDE { return '~'; }
};
template <typename Char> struct no_grouping : std::numpunct<Char> {
protected:
Char do_decimal_point() const FMT_OVERRIDE { return '.'; }
std::string do_grouping() const FMT_OVERRIDE { return ""; }
Char do_thousands_sep() const FMT_OVERRIDE { return ','; }
};
template <typename Char> struct special_grouping : std::numpunct<Char> {
protected:
Char do_decimal_point() const FMT_OVERRIDE { return '.'; }
std::string do_grouping() const FMT_OVERRIDE { return "\03\02"; }
Char do_thousands_sep() const FMT_OVERRIDE { return ','; }
};
template <typename Char> struct small_grouping : std::numpunct<Char> {
protected:
Char do_decimal_point() const FMT_OVERRIDE { return '.'; }
std::string do_grouping() const FMT_OVERRIDE { return "\01"; }
Char do_thousands_sep() const FMT_OVERRIDE { return ','; }
};
TEST(LocaleTest, DoubleDecimalPoint) {
std::locale loc(std::locale(), new numpunct<char>());
EXPECT_EQ("1?23", fmt::format(loc, "{:n}", 1.23));
// Test with Grisu disabled.
fmt::memory_buffer buf;
fmt::internal::writer w(buf, fmt::internal::locale_ref(loc));
auto specs = fmt::format_specs();
specs.type = 'n';
w.write_double<double, false>(1.23, specs);
EXPECT_EQ(fmt::to_string(buf), "1?23");
}
TEST(LocaleTest, Format) {
std::locale loc(std::locale(), new numpunct<char>());
EXPECT_EQ("1,234,567", fmt::format(std::locale(), "{:n}", 1234567));
EXPECT_EQ("1234567", fmt::format(std::locale(), "{:n}", 1234567));
EXPECT_EQ("1~234~567", fmt::format(loc, "{:n}", 1234567));
fmt::format_arg_store<fmt::format_context, int> as{1234567};
EXPECT_EQ("1~234~567", fmt::vformat(loc, "{:n}", fmt::format_args(as)));
std::string s;
fmt::format_to(std::back_inserter(s), loc, "{:n}", 1234567);
EXPECT_EQ("1~234~567", s);
std::locale no_grouping_loc(std::locale(), new no_grouping<char>());
EXPECT_EQ("1234567", fmt::format(no_grouping_loc, "{:n}", 1234567));
std::locale special_grouping_loc(std::locale(), new special_grouping<char>());
EXPECT_EQ("1,23,45,678", fmt::format(special_grouping_loc, "{:n}", 12345678));
std::locale small_grouping_loc(std::locale(), new small_grouping<char>());
EXPECT_EQ("4,2,9,4,9,6,7,2,9,5",
fmt::format(small_grouping_loc, "{:n}", max_value<uint32_t>()));
}
TEST(LocaleTest, WFormat) {
std::locale loc(std::locale(), new numpunct<wchar_t>());
EXPECT_EQ(L"1,234,567", fmt::format(std::locale(), L"{:n}", 1234567));
EXPECT_EQ(L"1234567", fmt::format(std::locale(), L"{:n}", 1234567));
EXPECT_EQ(L"1~234~567", fmt::format(loc, L"{:n}", 1234567));
fmt::format_arg_store<fmt::wformat_context, int> as{1234567};
EXPECT_EQ(L"1~234~567", fmt::vformat(loc, L"{:n}", fmt::wformat_args(as)));
auto sep =
std::use_facet<std::numpunct<wchar_t>>(std::locale("C")).thousands_sep();
auto result = sep == ',' ? L"1,234,567" : L"1234567";
EXPECT_EQ(result, fmt::format(std::locale("C"), L"{:n}", 1234567));
EXPECT_EQ(L"1234567", fmt::format(std::locale("C"), L"{:n}", 1234567));
std::locale no_grouping_loc(std::locale(), new no_grouping<wchar_t>());
EXPECT_EQ(L"1234567", fmt::format(no_grouping_loc, L"{:n}", 1234567));
std::locale special_grouping_loc(std::locale(),
new special_grouping<wchar_t>());
EXPECT_EQ(L"1,23,45,678",
fmt::format(special_grouping_loc, L"{:n}", 12345678));
std::locale small_grouping_loc(std::locale(), new small_grouping<wchar_t>());
EXPECT_EQ(L"4,2,9,4,9,6,7,2,9,5",
fmt::format(small_grouping_loc, L"{:n}", max_value<uint32_t>()));
}
#endif // FMT_STATIC_THOUSANDS_SEPARATOR

View File

@ -187,8 +187,8 @@ TEST(OStreamTest, Join) {
#if FMT_USE_CONSTEXPR
TEST(OStreamTest, ConstexprString) {
EXPECT_EQ("42", format(fmt("{}"), std::string("42")));
EXPECT_EQ("a string", format(fmt("{0}"), TestString("a string")));
EXPECT_EQ("42", format(FMT_STRING("{}"), std::string("42")));
EXPECT_EQ("a string", format(FMT_STRING("{0}"), TestString("a string")));
}
#endif
@ -243,8 +243,7 @@ TEST(FormatTest, UDL) {
}
#endif
template <typename T>
struct convertible {
template <typename T> struct convertible {
T value;
explicit convertible(const T& val) : value(val) {}
operator T() const { return value; }
@ -255,3 +254,47 @@ TEST(OStreamTest, DisableBuiltinOStreamOperators) {
EXPECT_EQ(L"42", fmt::format(L"{:d}", convertible<unsigned short>(42)));
EXPECT_EQ("foo", fmt::format("{}", convertible<const char*>("foo")));
}
struct explicitly_convertible_to_string_like {
template <typename String,
typename = typename std::enable_if<std::is_constructible<
String, const char*, std::size_t>::value>::type>
explicit operator String() const {
return String("foo", 3u);
}
};
TEST(FormatterTest, FormatExplicitlyConvertibleToStringLike) {
EXPECT_EQ("foo", fmt::format("{}", explicitly_convertible_to_string_like()));
}
std::ostream& operator<<(std::ostream& os,
explicitly_convertible_to_string_like) {
return os << "bar";
}
TEST(FormatterTest, FormatExplicitlyConvertibleToStringLikeIgnoreInserter) {
EXPECT_EQ("foo", fmt::format("{}", explicitly_convertible_to_string_like()));
}
#ifdef FMT_USE_STRING_VIEW
struct explicitly_convertible_to_std_string_view {
explicit operator fmt::internal::std_string_view<char>() const {
return {"foo", 3u};
}
};
TEST(FormatterTest, FormatExplicitlyConvertibleToStdStringView) {
EXPECT_EQ("foo", fmt::format("{}", explicitly_convertible_to_string_like()));
}
std::ostream& operator<<(std::ostream& os,
explicitly_convertible_to_std_string_view) {
return os << "bar";
}
TEST(FormatterTest, FormatExplicitlyConvertibleToStdStringViewIgnoreInserter) {
EXPECT_EQ("foo",
fmt::format("{}", explicitly_convertible_to_std_string_view()));
}
#endif // FMT_USE_STRING_VIEW

View File

@ -30,7 +30,6 @@
using fmt::buffered_file;
using fmt::error_code;
using fmt::file;
using testing::_;
using testing::Return;
@ -198,6 +197,9 @@ static void write_file(fmt::cstring_view filename, fmt::string_view content) {
f.print("{}", content);
}
#if FMT_USE_FCNTL
using fmt::file;
TEST(UtilTest, GetPageSize) {
#ifdef _WIN32
SYSTEM_INFO si = {};
@ -429,6 +431,7 @@ TEST(BufferedFileTest, FilenoNoRetry) {
EXPECT_EQ(2, fileno_count);
fileno_count = 0;
}
#endif // FMT_USE_FCNTL
struct TestMock {
static TestMock* instance;

View File

@ -19,6 +19,9 @@
using fmt::buffered_file;
using fmt::error_code;
#if FMT_USE_FCNTL
using fmt::file;
// Checks if the file is open by reading one character from it.
@ -376,3 +379,4 @@ TEST(LocaleTest, Strtod) {
EXPECT_EQ(start + 3, ptr);
}
#endif
#endif // FMT_USE_FCNTL

View File

@ -5,12 +5,13 @@
//
// For the license information refer to format.h.
#include "fmt/printf.h"
#include <cctype>
#include <climits>
#include <cstring>
#include "fmt/core.h"
#include "fmt/printf.h"
#include "gtest-extra.h"
#include "util.h"
@ -300,15 +301,15 @@ void TestLength(const char* length_spec, U value) {
using fmt::internal::const_check;
if (const_check(max <= static_cast<unsigned>(max_value<int>()))) {
signed_value = static_cast<int>(value);
unsigned_value = static_cast<unsigned>(value);
unsigned_value = static_cast<unsigned long long>(value);
} else if (const_check(max <= max_value<unsigned>())) {
signed_value = static_cast<unsigned>(value);
unsigned_value = static_cast<unsigned>(value);
unsigned_value = static_cast<unsigned long long>(value);
}
if (sizeof(U) <= sizeof(int) && sizeof(int) < sizeof(T)) {
signed_value = static_cast<long long>(value);
unsigned_value =
static_cast<typename std::make_unsigned<unsigned>::type>(value);
unsigned_value = static_cast<unsigned long long>(
static_cast<typename std::make_unsigned<unsigned>::type>(value));
} else {
signed_value = static_cast<typename make_signed<T>::type>(value);
unsigned_value = static_cast<typename std::make_unsigned<T>::type>(value);
@ -337,7 +338,9 @@ template <typename T> void TestLength(const char* length_spec) {
TestLength<T>(length_spec, -42);
TestLength<T>(length_spec, min);
TestLength<T>(length_spec, max);
TestLength<T>(length_spec, static_cast<long long>(min) - 1);
long long long_long_min = std::numeric_limits<long long>::min();
if (static_cast<long long>(min) > long_long_min)
TestLength<T>(length_spec, static_cast<long long>(min) - 1);
unsigned long long long_long_max = max_value<long long>();
if (static_cast<unsigned long long>(max) < long_long_max)
TestLength<T>(length_spec, static_cast<long long>(max) + 1);
@ -408,6 +411,7 @@ TEST(PrintfTest, Float) {
EXPECT_PRINTF("392.65", "%G", 392.65);
EXPECT_PRINTF("392", "%g", 392.0);
EXPECT_PRINTF("392", "%G", 392.0);
EXPECT_PRINTF("4.56e-07", "%g", 0.000000456);
safe_sprintf(buffer, "%a", -392.65);
EXPECT_EQ(buffer, format("{:a}", -392.65));
safe_sprintf(buffer, "%A", -392.65);
@ -474,7 +478,7 @@ enum E { A = 42 };
TEST(PrintfTest, Enum) { EXPECT_PRINTF("42", "%d", A); }
#if FMT_USE_FILE_DESCRIPTORS
#if FMT_USE_FCNTL
TEST(PrintfTest, Examples) {
const char* weekday = "Thursday";
const char* month = "August";
@ -586,8 +590,7 @@ class custom_printf_arg_formatter : public formatter_t {
if (round(value * pow(10, specs()->precision)) == 0.0) value = 0;
return formatter_t::operator()(value);
}
}
;
};
typedef fmt::basic_format_args<context_t> format_args_t;

View File

@ -6,6 +6,8 @@
// For the license information refer to format.h.
#include <array>
#include <cassert>
#include <climits>
#include "fmt/format.h"
@ -135,21 +137,20 @@ struct scan_handler : error_handler {
char c = *it++;
if (c < '0' || c > '9') on_error("invalid input");
// TODO: check overflow
value = value * 10 + (c - '0');
value = value * 10 + static_cast<unsigned>(c - '0');
}
scan_ctx_.advance_to(it);
return value;
}
template <typename T = int> T read_int() {
T value = 0;
auto it = scan_ctx_.begin(), end = scan_ctx_.end();
bool negative = it != end && *it == '-';
if (negative) ++it;
scan_ctx_.advance_to(it);
value = read_uint<typename std::make_unsigned<T>::type>();
if (negative) value = -value;
return value;
const auto value = read_uint<typename std::make_unsigned<T>::type>();
if (negative) return -static_cast<T>(value);
return static_cast<T>(value);
}
public:
@ -159,7 +160,7 @@ struct scan_handler : error_handler {
const char* pos() const { return scan_ctx_.begin(); }
void on_text(const char* begin, const char* end) {
auto size = end - begin;
auto size = to_unsigned(end - begin);
auto it = scan_ctx_.begin();
if (it + size > scan_ctx_.end() ||
!std::equal(begin, end, make_checked(it, size))) {
@ -197,11 +198,12 @@ struct scan_handler : error_handler {
case scan_type::string_view_type: {
auto s = it;
while (it != end && *it != ' ') ++it;
*arg_.string_view = fmt::string_view(s, it - s);
*arg_.string_view = fmt::string_view(s, to_unsigned(it - s));
scan_ctx_.advance_to(it);
break;
}
default:
case scan_type::none_type:
case scan_type::custom_type:
assert(false);
}
}

View File

@ -61,11 +61,11 @@ TEST(StdFormatTest, Int) {
string s0 = format("{}", 42); // s0 == "42"
string s1 = format("{0:b} {0:d} {0:o} {0:x}", 42); // s1 == "101010 42 52 2a"
string s2 = format("{0:#x} {0:#X}", 42); // s2 == "0x2a 0X2A"
string s3 = format("{:n}", 1234); // s3 == "1,234" (depends on the locale)
string s3 = format("{:n}", 1234); // s3 == "1234" (depends on the locale)
EXPECT_EQ(s0, "42");
EXPECT_EQ(s1, "101010 42 52 2a");
EXPECT_EQ(s2, "0x2a 0X2A");
EXPECT_EQ(s3, "1,234");
EXPECT_EQ(s3, "1234");
}
#include <format>

View File

@ -14,8 +14,13 @@
class assertion_failure : public std::logic_error {
public:
explicit assertion_failure(const char* message) : std::logic_error(message) {}
private:
virtual void avoid_weak_vtable();
};
void assertion_failure::avoid_weak_vtable() {}
#define FMT_ASSERT(condition, message) \
if (!(condition)) throw assertion_failure(message);

View File

@ -33,11 +33,17 @@ std::string get_system_error(int error_code) {
const char* const FILE_CONTENT = "Don't panic!";
fmt::buffered_file open_buffered_file(FILE** fp) {
#if FMT_USE_FCNTL
fmt::file read_end, write_end;
fmt::file::pipe(read_end, write_end);
write_end.write(FILE_CONTENT, std::strlen(FILE_CONTENT));
write_end.close();
fmt::buffered_file f = read_end.fdopen("r");
if (fp) *fp = f.get();
#else
fmt::buffered_file f("test-file", "w");
fputs(FILE_CONTENT, f.get());
if (fp) *fp = f.get();
#endif
return f;
}