Compare commits

...

77 Commits

Author SHA1 Message Date
ec628561c2 Fix formatting 2023-11-13 06:41:28 -10:00
cbb18c237a Add support for CMake 3.28 C++ modules (#3679) 2023-11-13 08:34:42 -08:00
6b0082e6c7 Improve OpenBSD workaround 2023-11-07 09:24:33 -10:00
52a99a67f7 Set PDB path for Visual Studio (#3702)
Ensure the PDB files are output into the same directory and with the same name
as the static library when using Visual Studio.

Resolves fmtlib#3701.
2023-11-07 08:22:52 -08:00
4548d1eae2 Make write_escaped_path more portable 2023-11-07 05:46:15 -10:00
050d41e857 Make get_path_string more portable 2023-11-06 14:34:53 -10:00
1c023c0087 Update bootstrap 2023-11-06 08:20:01 -10:00
b35d4e40fe fix: use FMT_HAS_INCLUDE instead of __has_include 2023-11-03 15:10:40 -07:00
acaf83f40f feat: enable building with gcc 4.8 2023-11-03 02:38:05 +09:00
05aa783779 feat: include xlocale.h only if exists 2023-11-03 02:38:05 +09:00
05dda9490d Bump ossf/scorecard-action from 2.2.0 to 2.3.1 (#3697)
Bumps [ossf/scorecard-action](https://github.com/ossf/scorecard-action) from 2.2.0 to 2.3.1.
- [Release notes](https://github.com/ossf/scorecard-action/releases)
- [Changelog](https://github.com/ossf/scorecard-action/blob/main/RELEASE.md)
- [Commits](08b4669551...0864cf1902)

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-11-01 12:38:06 -07:00
caf4fcb207 Bump github/codeql-action from 2.21.5 to 2.22.5 (#3696)
Bumps [github/codeql-action](https://github.com/github/codeql-action) from 2.21.5 to 2.22.5.
- [Release notes](https://github.com/github/codeql-action/releases)
- [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md)
- [Commits](00e563ead9...74483a38d3)

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-11-01 12:37:23 -07:00
e0d3e346d2 Wrap Char in array to avoid pointer arithmetic (#3695)
This resolves the following finding reported by Coverity Static Analysis
v2023.6.1 on line 1964 of fmt/include/fmt/format.h:

  ptr_arith: Using &v as an array. This might corrupt or misinterpret
             adjacent memory locations.
2023-10-31 14:05:46 -07:00
19276d7325 Fix an inconsistentcy between to_string and format 2023-10-28 08:05:11 -07:00
2a2c6e676f Fix flushing C++ iostreams before calling write_console() (#3689)
This change correctly implements https://wg21.link/P2539/ for both
C streams and C++ iostreams.

Fixes #3688.
2023-10-25 13:13:31 -07:00
3b7f58a8b3 add buffer flush before direct write 2023-10-24 16:02:37 -07:00
e9bbd4069e Update README.rst 2023-10-22 09:13:14 -07:00
857cce7a83 Update README.rst 2023-10-22 08:45:50 -07:00
081d5b0d8b Update README.rst 2023-10-22 08:30:04 -07:00
baae1ed658 add meson instructions to usage docs (#3677) 2023-10-18 11:53:47 -07:00
2ac6c5ca8b Fix error C2668 on Windows with option /std:c++latest (#3680)
* Namespace-qualify to avoid ambiguity with std::format_to for format-test.cc

When build fmt with MSVC under option /std:c++latest, it failed due to `error 2668: 'std::format_to': ambiguous call to overloaded function`, so add namespace to qualify the call to format_to to avoid this issue.
2023-10-16 09:54:02 -07:00
d9063baf22 Fix perf regression in ostream::print 2023-10-15 08:23:36 -07:00
f7542c5761 Apply clang-format 2023-10-14 06:52:43 -07:00
130cf54cbc Use a more sensible locale in tests 2023-10-08 10:49:21 -07:00
8e0ca0589f Use a more sensible locale in tests 2023-10-08 09:21:02 -07:00
bf497ac068 Cleanup test 2023-10-08 07:30:08 -07:00
bb8d50f04b add a suffix for days and fix the one for minutes (#3664) 2023-10-07 12:45:34 -07:00
f76603f21e fix: make std::bitset formattable again (#3660)
* fix: make std::bitset formattable again

It used to be formattable via operator<<(ostream&) implicitly. Make it
formattable again, but this time via formatter specialization.

* fix: make nested_formatter constexpr default constructible
2023-10-03 09:53:47 -07:00
f918289363 Bump actions/checkout from 4.0.0 to 4.1.0 (#3666)
Bumps [actions/checkout](https://github.com/actions/checkout) from 4.0.0 to 4.1.0.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](3df4ab11eb...8ade135a41)

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-10-01 09:49:05 -07:00
72e883e163 Diagnose unsupported code unit types 2023-10-01 08:37:21 -07:00
b3bf23f3c4 Remove leftover usage of the __std_stream header (#3663)
2dd4fa8742 removed all usage of __std_stream because
it is no longer available with clang v17. That commit missed one place
where the header was still used (only used when building with -DFMT_MODULE=ON).
Remove it there too.

See #3654
2023-10-01 08:16:42 -07:00
349e1c48d1 Update README.rst (#3661)
Signed-off-by: Joyce <joycebrum@google.com>
2023-09-30 07:16:04 -07:00
79dbd3f192 feat: add security policy to readme (#3655)
Signed-off-by: Joyce <joycebrum@google.com>
2023-09-28 16:50:14 -07:00
2dd4fa8742 Remove an ostream hack incompatible with libc++ 17 2023-09-26 12:42:37 -07:00
44f3d8a77c README.rst: fix broken LICENSE link (#3653)
The `.rst` suffix was dropped for this file a while ago.
2023-09-22 15:54:40 -07:00
06b20387ae Optimize fractional_part_rounding_thresholds 2023-09-22 13:26:57 -07:00
649f2460db Apply clang-format 2023-09-22 09:07:43 -07:00
7529af8f99 Workaround intel bug (#3652)
* Workaround intel bug

Potential workaround / restructure for the intel bug that is the cause of #3645.

Make the variable in the external struct instead an embedded static constexpr variable in the only function that uses the variable.

* Finish the proposed change -- remove struct accessor

* Refactor proposed intel fix.

Moved variable out of function to avoid specialization on Float.  Made it a separate function that is called from format_float.

* Fix incorrect function name.

* Add missing inline.
2023-09-20 18:57:55 -07:00
a3a74fa7f3 fix: mark fmt::streamed() as constexpr (#3650)
Because it's just performing a very basic type conversion that can be
done at constexpr time.

My use case simultaneously creates a
`fmt::basic_format_string<some_type_conversion<Args...>>` instance and
performs `some_type_conversion<Args>(args)...`. `some_type_conversion`
optionally applies `fmt::streamed(arg)` to a subset of types. This needs
to be `constexpr` because `basic_format_string`'s constructor is
`consteval`.
2023-09-19 08:42:34 -07:00
8ef4db4b96 Use datatype of underlying data (#3647) 2023-09-19 07:31:36 -07:00
492a99c964 Fix error: 'char_traits<custom_char>' is deprecated: char_traits<T> for T not equal to char, wchar_t, char8_t, char16_t or char32_t is non-standard and is provided for a temporary period. It will be removed in LLVM 18, so please migrate off of it. [-Werror,-Wdeprecated-declarations] (#3634)
Signed-off-by: Vladislav Shchapov <vladislav@shchapov.ru>
2023-09-18 15:46:17 -07:00
3baaa8d899 Update docs 2023-09-18 14:54:52 -07:00
0e01e46c11 Implement nested formatter 2023-09-18 14:21:21 -07:00
f6ca4ea199 Avoid a space in the UDL definition (#3610)
* Avoid a space in the UDL definition except on GCC before 4.9

Clang 18 has grown a warning about the space being deprecated which
is enabled by default in their nightly binaries. However GCC before 4.9
will reject the UDL definition unless there is a space there, so we need
to keep the space conditionally for it.

* Remove UDLs on GCC before 4.9 to simplify things

GCC before 4.9 rejects the syntax that is now
rejected on more modern compilers.

* Disable compile-error-test on GCC < 4.9

This avoids the UDL tests failing as GCC < 4.9 can not parse UDLs
without a space, but the space is malformed in modern compilers.
2023-09-18 08:09:42 -07:00
a8a73da7e4 Add an option to avoid wchar APIs on Windows (#3636)
With this, fmt can be used on Windows 98 and the Original Xbox with:

    set(FMT_OS OFF)

It is not exposed as a CMake option but one can define it manually
in the fmt subproject, e.g.:

    target_compile_definitions(fmt PUBLIC FMT_WINDOWS_NO_WCHAR)

Fixes #3631
2023-09-17 08:49:51 -07:00
aa3c5a4127 Clarify I/O error handling 2023-09-16 08:20:27 -07:00
bfdf50d183 Minor cleanup 2023-09-16 08:16:59 -07:00
571a9b7b26 Replace usage of C++17 library feature with C++11 (#3638) 2023-09-16 08:07:03 -07:00
6c088be8ec Cleanup handling of visibility 2023-09-16 07:40:08 -07:00
016b1faede Fix symbol leak (#3627)
Signed-off-by: Vladislav Shchapov <vladislav@shchapov.ru>
2023-09-16 07:26:36 -07:00
e25370093a Remove unnecessary spaces (#3642) 2023-09-16 06:55:22 -07:00
d4987546a4 Add an experimental nested formatter 2023-09-10 11:56:55 -07:00
5bdce181f1 Mark styled_arg as a view to prevent lifetime issues 2023-09-09 08:16:45 -07:00
cyy
a4b7b24b7b fix redundant redeclaration of ‘constexpr’ static data member (#3630) 2023-09-08 16:20:32 -07:00
fac60bd4f5 Remove type cast as mxe(mingw32) compiler complains about useless-cast (#3624)
Remove type cast as mxe(mingw32) compiler complains about useless-cast
when FMT_PEDANTIC && FMT_WERROR options are enabled
"""
error: useless cast to type 'class fmt::v10::basic_format_args<fmt::v10::basic_format_context<fmt::v10::appender, char> >' [-Werror=useless-cast]
 1449 |                      basic_format_args<buffer_context<char>>(args));
"""
2023-09-05 07:37:27 -07:00
f5be4a8a9a Bump actions/checkout from 3.5.3 to 4.0.0 (#3623)
Bumps [actions/checkout](https://github.com/actions/checkout) from 3.5.3 to 4.0.0.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](c85c95e3d7...3df4ab11eb)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-09-04 11:24:28 -07:00
84e6661517 Workaround a gcc 6.4 issue 2023-09-04 11:23:14 -07:00
ac3240439c Update dependabot.yml 2023-09-04 10:23:00 -07:00
8894ae87fe Bump github/codeql-action from 2.21.4 to 2.21.5 (#3622)
Bumps [github/codeql-action](https://github.com/github/codeql-action) from 2.21.4 to 2.21.5.
- [Release notes](https://github.com/github/codeql-action/releases)
- [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md)
- [Commits](a09933a12a...00e563ead9)

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-09-04 10:19:51 -07:00
ca608547e5 Workaround a C++11 issue 2023-09-04 10:07:14 -07:00
23cf4055a0 Simplify URLs 2023-09-04 10:00:13 -07:00
46c8301ee9 Remove rst2md 2023-09-04 09:38:06 -07:00
a79a979828 Cleanup ranges test 2023-09-04 09:19:40 -07:00
457bb6a98f Merge the copyright comment since there are many contributors 2023-09-04 09:09:01 -07:00
61aef41110 Cleanup changelog 2023-09-04 09:05:07 -07:00
2a45fd30fe Drop the rst suffix from the license file 2023-09-04 08:18:03 -07:00
24296cff1c Update ChangeLog.md 2023-09-04 08:16:16 -07:00
3d1d20a6ac Update ChangeLog.md 2023-09-04 08:10:34 -07:00
0302c527c6 Update ChangeLog.md 2023-09-04 07:59:30 -07:00
154eccfeb1 Convert changelog to markdown for compatibility with release notes 2023-09-04 07:15:18 -07:00
35dc5def30 Revert "Bump actions/checkout from 3.5.3 to 3.6.0 (#3615)"
This reverts commit e8259c5298.
2023-09-04 07:12:32 -07:00
e1fc481d65 Merge the copyright comment since there are many contributors 2023-09-04 06:54:07 -07:00
e8259c5298 Bump actions/checkout from 3.5.3 to 3.6.0 (#3615)
Bumps [actions/checkout](https://github.com/actions/checkout) from 3.5.3 to 3.6.0.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](c85c95e3d7...f43a0e5ff2)

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-08-28 13:55:28 -07:00
6379251554 Update version 2023-08-28 06:29:25 -07:00
951fd9e66f Update changelog 2023-08-28 06:28:15 -07:00
be89b9a41e Merge branch 'release' of github.com:fmtlib/fmt 2023-08-28 06:24:29 -07:00
28e2d3b640 Bump version 2023-08-26 08:29:50 -07:00
37 changed files with 5888 additions and 6419 deletions

View File

@ -3,6 +3,6 @@ updates:
- package-ecosystem: "github-actions" # Necessary to update action hashs
directory: "/"
schedule:
interval: "weekly"
interval: "monthly"
# Allow up to 3 opened pull requests for github-actions versions
open-pull-requests-limit: 3

View File

@ -11,7 +11,7 @@ jobs:
runs-on: ubuntu-20.04
steps:
- uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3
- uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 # v4.1.0
- name: Add ubuntu mirrors
run: |

View File

@ -57,7 +57,7 @@ jobs:
- shared: -DBUILD_SHARED_LIBS=ON
steps:
- uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3
- uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 # v4.1.0
- name: Set timezone
run: sudo timedatectl set-timezone 'Asia/Yekaterinburg'

View File

@ -22,7 +22,7 @@ jobs:
runs-on: '${{ matrix.os }}'
steps:
- uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3
- uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 # v4.1.0
- name: Set timezone
run: sudo systemsetup -settimezone 'Asia/Yekaterinburg'

View File

@ -29,12 +29,12 @@ jobs:
steps:
- name: "Checkout code"
uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3
uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 # v4.1.0
with:
persist-credentials: false
- name: "Run analysis"
uses: ossf/scorecard-action@08b4669551908b1024bb425080c797723083c031 # v2.2.0
uses: ossf/scorecard-action@0864cf19026789058feabb7e87baa5f140aac736 # v2.3.1
with:
results_file: results.sarif
results_format: sarif
@ -60,6 +60,6 @@ jobs:
# Upload the results to GitHub's code scanning dashboard.
- name: "Upload to code-scanning"
uses: github/codeql-action/upload-sarif@a09933a12a80f87b87005513f0abb1494c27a716 # v2.21.4
uses: github/codeql-action/upload-sarif@74483a38d39275f33fcff5f35b679b5ca4a26a99 # v2.22.5
with:
sarif_file: results.sarif

View File

@ -41,7 +41,7 @@ jobs:
standard: 20
steps:
- uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3
- uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 # v4.1.0
- name: Set timezone
run: tzutil /s "Ekaterinburg Standard Time"
@ -88,7 +88,7 @@ jobs:
release: false
msystem: ${{matrix.sys}}
pacboy: cc:p cmake:p ninja:p lld:p
- uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3
- uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 # v4.1.0
- name: Configure
run: cmake -B ../build -DBUILD_SHARED_LIBS=ON -DCMAKE_BUILD_TYPE=Debug
env: { LDFLAGS: -fuse-ld=lld }

View File

@ -1,9 +1,9 @@
cmake_minimum_required(VERSION 3.8...3.26)
cmake_minimum_required(VERSION 3.8...3.28)
# Fallback for using newer policies on CMake <3.12.
if(${CMAKE_VERSION} VERSION_LESS 3.12)
if (${CMAKE_VERSION} VERSION_LESS 3.12)
cmake_policy(VERSION ${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION})
endif()
endif ()
# Determine if fmt is built as a subproject (using add_subdirectory)
# or if it is the master project.
@ -26,7 +26,7 @@ endfunction()
# DEPRECATED! Should be merged into add_module_library.
function(enable_module target)
if (MSVC)
if (MSVC AND CMAKE_VERSION VERSION_LESS 3.28)
set(BMI ${CMAKE_CURRENT_BINARY_DIR}/${target}.ifc)
target_compile_options(${target}
PRIVATE /interface /ifcOutput ${BMI}
@ -65,7 +65,7 @@ function(add_module_library name)
# `std` is affected by CMake options and may be higher than C++20.
get_target_property(std ${name} CXX_STANDARD)
if (CMAKE_CXX_COMPILER_ID MATCHES "Clang")
if (CMAKE_CXX_COMPILER_ID MATCHES "Clang" AND CMAKE_VERSION VERSION_LESS 3.28)
set(pcms)
foreach (src ${sources})
get_filename_component(pcm ${src} NAME_WE)
@ -103,7 +103,12 @@ function(add_module_library name)
DEPENDS ${pcm})
endforeach ()
endif ()
target_sources(${name} PRIVATE ${sources})
if (CMAKE_VERSION VERSION_LESS 3.28)
target_sources(${name} PRIVATE ${sources})
else ()
target_sources(${name} PUBLIC FILE_SET fmt_module TYPE CXX_MODULES
FILES ${sources})
endif ()
endfunction()
include(CMakeParseArguments)
@ -162,10 +167,10 @@ set(FMT_SYSTEM_HEADERS_ATTRIBUTE "")
if (FMT_SYSTEM_HEADERS)
set(FMT_SYSTEM_HEADERS_ATTRIBUTE SYSTEM)
endif ()
if(CMAKE_SYSTEM_NAME STREQUAL "MSDOS")
if (CMAKE_SYSTEM_NAME STREQUAL "MSDOS")
set(FMT_TEST OFF)
message(STATUS "MSDOS is incompatible with gtest")
endif()
endif ()
# Get version from core.h
file(READ include/fmt/core.h core_h)
@ -283,7 +288,7 @@ if (FMT_OS)
endif ()
add_module_library(fmt src/fmt.cc FALLBACK
${FMT_SOURCES} ${FMT_HEADERS} README.rst ChangeLog.rst
${FMT_SOURCES} ${FMT_HEADERS} README.rst ChangeLog.md
IF FMT_MODULE)
add_library(fmt::fmt ALIAS fmt)
if (FMT_MODULE)
@ -312,7 +317,15 @@ set(FMT_DEBUG_POSTFIX d CACHE STRING "Debug library postfix.")
set_target_properties(fmt PROPERTIES
VERSION ${FMT_VERSION} SOVERSION ${CPACK_PACKAGE_VERSION_MAJOR}
PUBLIC_HEADER "${FMT_HEADERS}"
DEBUG_POSTFIX "${FMT_DEBUG_POSTFIX}")
DEBUG_POSTFIX "${FMT_DEBUG_POSTFIX}"
# Workaround for Visual Studio 2017:
# Ensure the .pdb is created with the same name and in the same directory
# as the .lib. Newer VS versions already do this by default, but there is no
# harm in setting it for those too. Ignored by other generators.
COMPILE_PDB_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}"
COMPILE_PDB_NAME "fmt"
COMPILE_PDB_NAME_DEBUG "fmt${FMT_DEBUG_POSTFIX}")
# Set FMT_LIB_NAME for pkg-config fmt.pc. We cannot use the OUTPUT_NAME target
# property because it's not set by default.
@ -326,7 +339,7 @@ if (BUILD_SHARED_LIBS)
endif ()
if (FMT_SAFE_DURATION_CAST)
target_compile_definitions(fmt PUBLIC FMT_SAFE_DURATION_CAST)
endif()
endif ()
add_library(fmt-header-only INTERFACE)
add_library(fmt::fmt-header-only ALIAS fmt-header-only)
@ -334,7 +347,8 @@ add_library(fmt::fmt-header-only ALIAS fmt-header-only)
target_compile_definitions(fmt-header-only INTERFACE FMT_HEADER_ONLY=1)
target_compile_features(fmt-header-only INTERFACE cxx_std_11)
target_include_directories(fmt-header-only ${FMT_SYSTEM_HEADERS_ATTRIBUTE} INTERFACE
target_include_directories(fmt-header-only
${FMT_SYSTEM_HEADERS_ATTRIBUTE} INTERFACE
$<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/include>
$<INSTALL_INTERFACE:${FMT_INC_DIR}>)
@ -384,8 +398,16 @@ if (FMT_INSTALL)
LIBRARY DESTINATION ${FMT_LIB_DIR}
ARCHIVE DESTINATION ${FMT_LIB_DIR}
PUBLIC_HEADER DESTINATION "${FMT_INC_DIR}/fmt"
FILE_SET fmt_module DESTINATION "${FMT_LIB_DIR}/cxx/miu"
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR})
if (FMT_MODULE AND CMAKE_VERSION VERSION_GREATER_EQUAL 3.28)
# Install format.cc and os.cc which are included by the fmt.cc module
# interface file.
install(FILES src/format.cc src/os.cc
DESTINATION "${FMT_LIB_DIR}/cxx/miu/src")
endif ()
# Use a namespace because CMake provides better diagnostics for namespaced
# imported targets.
export(TARGETS ${INSTALL_TARGETS} NAMESPACE fmt::

5282
ChangeLog.md Normal file

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -46,7 +46,8 @@ Features
* Simple `format API <https://fmt.dev/latest/api.html>`_ with positional arguments
for localization
* Implementation of `C++20 std::format
<https://en.cppreference.com/w/cpp/utility/format>`__
<https://en.cppreference.com/w/cpp/utility/format>`__ and `C++23 std::print
<https://en.cppreference.com/w/cpp/io/print>`__
* `Format string syntax <https://fmt.dev/latest/syntax.html>`_ similar to Python's
`format <https://docs.python.org/3/library/stdtypes.html#str.format>`_
* Fast IEEE 754 floating-point formatter with correct rounding, shortness and
@ -92,7 +93,7 @@ Examples
.. code:: c++
#include <fmt/core.h>
int main() {
fmt::print("Hello, world!\n");
}
@ -177,15 +178,15 @@ This can be `5 to 9 times faster than fprintf
fmt::print(fg(fmt::color::crimson) | fmt::emphasis::bold,
"Hello, {}!\n", "world");
fmt::print(fg(fmt::color::floral_white) | bg(fmt::color::slate_gray) |
fmt::emphasis::underline, "Hello, {}!\n", "мир");
fmt::emphasis::underline, "Olá, {}!\n", "Mundo");
fmt::print(fg(fmt::color::steel_blue) | fmt::emphasis::italic,
"Hello, {}!\n", "世界");
"你好{}\n", "世界");
}
Output on a modern terminal:
Output on a modern terminal with Unicode support:
.. image:: https://user-images.githubusercontent.com/
576385/88485597-d312f600-cf2b-11ea-9cbe-61f535a86e28.png
.. image:: https://github.com/fmtlib/fmt/assets/
576385/2a93c904-d6fa-4aa6-b453-2618e1c327d7
Benchmarks
----------
@ -294,7 +295,7 @@ Then you can run the speed test::
or the bloat test::
$ make bloat-test
Migrating code
--------------
@ -315,13 +316,13 @@ Projects using this library
an open-source library for mathematical programming
* `Aseprite <https://github.com/aseprite/aseprite>`_:
animated sprite editor & pixel art tool
animated sprite editor & pixel art tool
* `AvioBook <https://www.aviobook.aero/en>`_: a comprehensive aircraft
operations suite
* `Blizzard Battle.net <https://battle.net/>`_: an online gaming platform
* `Celestia <https://celestia.space/>`_: real-time 3D visualization of space
* `Ceph <https://ceph.com/>`_: a scalable distributed storage system
@ -330,7 +331,7 @@ Projects using this library
* `ClickHouse <https://github.com/ClickHouse/ClickHouse>`_: an analytical database
management system
* `Contour <https://github.com/contour-terminal/contour/>`_: a modern terminal emulator
* `CUAUV <https://cuauv.org/>`_: Cornell University's autonomous underwater
@ -391,7 +392,7 @@ Projects using this library
* `quasardb <https://www.quasardb.net/>`_: a distributed, high-performance,
associative database
* `Quill <https://github.com/odygrd/quill>`_: asynchronous low-latency logging library
* `QKW <https://github.com/ravijanjam/qkw>`_: generalizing aliasing to simplify
@ -521,7 +522,7 @@ License
-------
{fmt} is distributed under the MIT `license
<https://github.com/fmtlib/fmt/blob/master/LICENSE.rst>`_.
<https://github.com/fmtlib/fmt/blob/master/LICENSE>`_.
Documentation License
---------------------
@ -543,3 +544,10 @@ See `Contributors <https://github.com/fmtlib/fmt/graphs/contributors>`_ and
`Releases <https://github.com/fmtlib/fmt/releases>`_ for some of the names.
Let us know if your contribution is not listed or mentioned incorrectly and
we'll make it right.
Security Policy
---------------
To report a security issue, please disclose it at `security advisory <https://github.com/fmtlib/fmt/security/advisories/new>`_.
This project is maintained by a team of volunteers on a reasonable-effort basis. As such, please give us at least 90 days to work on a fix before public exposure.

File diff suppressed because one or more lines are too long

View File

@ -49,6 +49,10 @@ checked at compile time in C++20. To pass a runtime format string wrap it in
*args* is an argument list representing objects to be formatted.
I/O errors are reported as `std::system_error
<https://en.cppreference.com/w/cpp/error/system_error>`_ exceptions unless
specified otherwise.
.. _format:
.. doxygenfunction:: format(format_string<T...> fmt, T&&... args) -> std::string
@ -104,7 +108,7 @@ with the same format specifiers. The ``format_as`` function should take an
object of your type and return an object of a formattable type. It should be
defined in the same namespace as your type.
Example (https://godbolt.org/z/r7vvGE1v7)::
Example (https://godbolt.org/z/nvME4arz8)::
#include <fmt/format.h>
@ -119,67 +123,13 @@ Example (https://godbolt.org/z/r7vvGE1v7)::
fmt::print("{}\n", kevin_namespacy::film::se7en); // prints "7"
}
Using the specialization API is more complex but gives you full control over
parsing and formatting. To use this method specialize the ``formatter`` struct
template for your type and implement ``parse`` and ``format`` methods.
For example::
Using specialization is more complex but gives you full control over parsing and
formatting. To use this method specialize the ``formatter`` struct template for
your type and implement ``parse`` and ``format`` methods.
#include <fmt/core.h>
struct point {
double x, y;
};
template <> 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) -> format_parse_context::iterator {
// [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 '}'.
// Please also note that this character range may be empty, in case of
// the "{}" format string, so therefore you should check ctx.begin()
// for equality with ctx.end().
// 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.
auto format(const point& p, format_context& ctx) const -> format_context::iterator {
// ctx.out() is an output iterator to write to.
return presentation == 'f'
? fmt::format_to(ctx.out(), "({:.1f}, {:.1f})", p.x, p.y)
: fmt::format_to(ctx.out(), "({:.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("{:f}", p);
// s == "(1.0, 2.0)"
You can also reuse existing formatters via inheritance or composition, for
example::
The recommended way of defining a formatter is by reusing an existing one via
inheritance or composition. This way you can support standard format specifiers
without implementing them yourself. For example::
// color.h:
#include <fmt/core.h>
@ -207,9 +157,9 @@ example::
}
Note that ``formatter<string_view>::format`` is defined in ``fmt/format.h`` so
it has to be included in the source file.
Since ``parse`` is inherited from ``formatter<string_view>`` it will recognize
all string format specifications, for example
it has to be included in the source file. Since ``parse`` is inherited from
``formatter<string_view>`` it will recognize all string format specifications,
for example
.. code-block:: c++
@ -217,6 +167,64 @@ all string format specifications, for example
will return ``" blue"``.
The experimental ``nested_formatter`` provides an easy way applying a formatter
to one or more subobjects.
For example::
#include <fmt/format.h>
struct point {
double x, y;
};
template <>
struct fmt::formatter<point> : nested_formatter<double> {
auto format(point p, format_context& ctx) const {
return write_padded(ctx, [=](auto out) {
return format_to(out, "({}, {})", nested(p.x), nested(p.y));
});
}
};
int main() {
fmt::print("[{:>20.2f}]", point{1, 2});
}
prints::
[ (1.00, 2.00)]
Notice that fill, align and width are applied to the whole object which is the
recommended behavior while the remaining specifiers apply to elements.
In general the formatter has the following form::
template <> struct fmt::formatter<T> {
// Parses format specifiers and stores them in the formatter.
//
// [ctx.begin(), ctx.end()) is a, possibly empty, character range that
// contains a part of the format string starting from the format
// specifications to be parsed, e.g. in
//
// fmt::format("{:f} continued", ...);
//
// the range will contain "f} continued". 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 '}'.
constexpr auto parse(format_parse_context& ctx)
-> format_parse_context::iterator;
// Formats value using the parsed format specification stored in this
// formatter and writes the output to ctx.out().
auto format(const T& value, format_context& ctx) const
-> format_context::iterator;
};
It is recommended to at least support fill, align and width that apply to the
whole object and have the same semantics as in standard formatters.
You can also write a formatter for a hierarchy of classes::
// demo.h:

View File

@ -4,7 +4,7 @@
import errno, os, re, sys
from subprocess import check_call, CalledProcessError, Popen, PIPE, STDOUT
versions = ['1.0.0', '1.1.0', '2.0.0', '3.0.2', '4.0.0', '4.1.0', '5.0.0', '5.1.0', '5.2.0', '5.2.1', '5.3.0', '6.0.0', '6.1.0', '6.1.1', '6.1.2', '6.2.0', '6.2.1', '7.0.0', '7.0.1', '7.0.2', '7.0.3', '7.1.0', '7.1.1', '7.1.2', '7.1.3', '8.0.0', '8.0.1', '8.1.0', '8.1.1', '9.0.0', '9.1.0', '10.0.0', '10.1.0', '10.1.1']
versions = ['1.0.0', '1.1.0', '2.0.0', '3.0.2', '4.0.0', '4.1.0', '5.0.0', '5.1.0', '5.2.0', '5.2.1', '5.3.0', '6.0.0', '6.1.0', '6.1.1', '6.1.2', '6.2.0', '6.2.1', '7.0.0', '7.0.1', '7.0.2', '7.0.3', '7.1.0', '7.1.1', '7.1.2', '7.1.3', '8.0.0', '8.0.1', '8.1.0', '8.1.1', '9.0.0', '9.1.0', '10.0.0', '10.1.0', '10.1.1', '10.1.1']
class Pip:
def __init__(self, venv_dir):

View File

@ -134,6 +134,44 @@ For ``build2`` newcomers or to get more details and use cases, you can read the
``build2``
`toolchain introduction <https://build2.org/build2-toolchain/doc/build2-toolchain-intro.xhtml>`_.
Usage with Meson
================
`Meson's WrapDB <https://mesonbuild.com/Wrapdb-projects.html>` includes a ``fmt``
package, which repackages fmt to be built by Meson as a subproject.
**Usage:**
- Install the ``fmt`` subproject from the WrapDB by running::
meson wrap install fmt
from the root of your project.
- In your project's ``meson.build`` file, add an entry for the new subproject::
fmt = subproject('fmt')
fmt_dep = fmt.get_variable('fmt_dep')
- Include the new dependency object to link with fmt::
my_build_target = executable('name', 'src/main.cc', dependencies: [fmt_dep])
**Options:**
If desired, ``fmt`` may be built as a static library, or as a header-only
library.
For a static build, use the following subproject definition::
fmt = subproject('fmt', default_options: 'default_library=static')
fmt_dep = fmt.get_variable('fmt_dep')
For the header-only version, use::
fmt = subproject('fmt')
fmt_dep = fmt.get_variable('fmt_header_only_dep')
Building the Documentation
==========================

View File

@ -336,8 +336,6 @@ template <typename CodeUnit> struct codecvt_result {
CodeUnit buf[max_size];
CodeUnit* end;
};
template <typename CodeUnit>
constexpr const size_t codecvt_result<CodeUnit>::max_size;
template <typename CodeUnit>
void write_codecvt(codecvt_result<CodeUnit>& out, string_view in_buf,
@ -584,8 +582,9 @@ template <typename Period> FMT_CONSTEXPR inline const char* get_units() {
if (std::is_same<Period, std::tera>::value) return "Ts";
if (std::is_same<Period, std::peta>::value) return "Ps";
if (std::is_same<Period, std::exa>::value) return "Es";
if (std::is_same<Period, std::ratio<60>>::value) return "m";
if (std::is_same<Period, std::ratio<60>>::value) return "min";
if (std::is_same<Period, std::ratio<3600>>::value) return "h";
if (std::is_same<Period, std::ratio<86400>>::value) return "d";
return nullptr;
}

View File

@ -427,9 +427,10 @@ template <typename Char> inline void reset_color(buffer<Char>& buffer) {
buffer.append(reset_color.begin(), reset_color.end());
}
template <typename T> struct styled_arg {
template <typename T> struct styled_arg : detail::view {
const T& value;
text_style style;
styled_arg(const T& v, text_style s) : value(v), style(s) {}
};
template <typename Char>

View File

@ -18,7 +18,7 @@
#include <type_traits>
// The fmt library version in the form major * 10000 + minor * 100 + patch.
#define FMT_VERSION 100100
#define FMT_VERSION 100101
#if defined(__clang__) && !defined(__ibmxl__)
# define FMT_CLANG_VERSION (__clang_major__ * 100 + __clang_minor__)
@ -185,18 +185,20 @@
# define FMT_END_EXPORT
#endif
#if FMT_GCC_VERSION || FMT_CLANG_VERSION
# define FMT_VISIBILITY(value) __attribute__((visibility(value)))
#else
# define FMT_VISIBILITY(value)
#endif
#if !defined(FMT_HEADER_ONLY) && defined(_WIN32)
# ifdef FMT_LIB_EXPORT
# if defined(FMT_LIB_EXPORT)
# define FMT_API __declspec(dllexport)
# elif defined(FMT_SHARED)
# define FMT_API __declspec(dllimport)
# endif
#else
# if defined(FMT_LIB_EXPORT) || defined(FMT_SHARED)
# if defined(__GNUC__) || defined(__clang__)
# define FMT_API __attribute__((visibility("default")))
# endif
# endif
#elif defined(FMT_LIB_EXPORT) || defined(FMT_SHARED)
# define FMT_API FMT_VISIBILITY("default")
#endif
#ifndef FMT_API
# define FMT_API
@ -263,7 +265,9 @@ template <typename T>
using remove_const_t = typename std::remove_const<T>::type;
template <typename T>
using remove_cvref_t = typename std::remove_cv<remove_reference_t<T>>::type;
template <typename T> struct type_identity { using type = T; };
template <typename T> struct type_identity {
using type = T;
};
template <typename T> using type_identity_t = typename type_identity<T>::type;
template <typename T>
using underlying_t = typename std::underlying_type<T>::type;
@ -1504,7 +1508,9 @@ FMT_CONSTEXPR auto copy_str(R&& rng, OutputIt out) -> OutputIt {
#if FMT_GCC_VERSION && FMT_GCC_VERSION < 500
// A workaround for gcc 4.8 to make void_t work in a SFINAE context.
template <typename...> struct void_t_impl { using type = void; };
template <typename...> struct void_t_impl {
using type = void;
};
template <typename... T> using void_t = typename void_t_impl<T...>::type;
#else
template <typename...> using void_t = void;
@ -2300,9 +2306,12 @@ FMT_CONSTEXPR FMT_INLINE auto parse_format_specs(
dynamic_format_specs<Char>& specs;
type arg_type;
FMT_CONSTEXPR auto operator()(pres type, int set) -> const Char* {
if (!in(arg_type, set)) throw_format_error("invalid format specifier");
specs.type = type;
FMT_CONSTEXPR auto operator()(pres pres_type, int set) -> const Char* {
if (!in(arg_type, set)) {
if (arg_type == type::none_type) return begin;
throw_format_error("invalid format specifier");
}
specs.type = pres_type;
return begin + 1;
}
} parse_presentation_type{begin, specs, arg_type};
@ -2319,6 +2328,7 @@ FMT_CONSTEXPR FMT_INLINE auto parse_format_specs(
case '+':
case '-':
case ' ':
if (arg_type == type::none_type) return begin;
enter_state(state::sign, in(arg_type, sint_set | float_set));
switch (c) {
case '+':
@ -2334,14 +2344,17 @@ FMT_CONSTEXPR FMT_INLINE auto parse_format_specs(
++begin;
break;
case '#':
if (arg_type == type::none_type) return begin;
enter_state(state::hash, is_arithmetic_type(arg_type));
specs.alt = true;
++begin;
break;
case '0':
enter_state(state::zero);
if (!is_arithmetic_type(arg_type))
if (!is_arithmetic_type(arg_type)) {
if (arg_type == type::none_type) return begin;
throw_format_error("format specifier requires numeric argument");
}
if (specs.align == align::none) {
// Ignore 0 if align is specified for compatibility with std::format.
specs.align = align::numeric;
@ -2363,12 +2376,14 @@ FMT_CONSTEXPR FMT_INLINE auto parse_format_specs(
begin = parse_dynamic_spec(begin, end, specs.width, specs.width_ref, ctx);
break;
case '.':
if (arg_type == type::none_type) return begin;
enter_state(state::precision,
in(arg_type, float_set | string_set | cstring_set));
begin = parse_precision(begin, end, specs.precision, specs.precision_ref,
ctx);
break;
case 'L':
if (arg_type == type::none_type) return begin;
enter_state(state::locale, is_arithmetic_type(arg_type));
specs.localized = true;
++begin;
@ -2541,8 +2556,8 @@ FMT_CONSTEXPR auto parse_format_specs(ParseContext& ctx)
decltype(arg_mapper<context>().map(std::declval<const T&>())),
typename strip_named_arg<T>::type>;
#if defined(__cpp_if_constexpr)
if constexpr (std::is_default_constructible_v<
formatter<mapped_type, char_type>>) {
if constexpr (std::is_default_constructible<
formatter<mapped_type, char_type>>::value) {
return formatter<mapped_type, char_type>().parse(ctx);
} else {
type_is_unformattable_for<T, char_type> _;
@ -2667,7 +2682,9 @@ template <typename Char = char> struct vformat_args {
using type = basic_format_args<
basic_format_context<std::back_insert_iterator<buffer<Char>>, Char>>;
};
template <> struct vformat_args<char> { using type = format_args; };
template <> struct vformat_args<char> {
using type = format_args;
};
// Use vformat_args and avoid type_identity to keep symbols short.
template <typename Char>

View File

@ -18,7 +18,7 @@
# include <locale>
#endif
#ifdef _WIN32
#if defined(_WIN32) && !defined(FMT_WINDOWS_NO_WCHAR)
# include <io.h> // _isatty
#endif
@ -73,9 +73,8 @@ FMT_FUNC void report_error(format_func func, int error_code,
}
// A wrapper around fwrite that throws on error.
inline void fwrite_fully(const void* ptr, size_t size, size_t count,
FILE* stream) {
size_t written = std::fwrite(ptr, size, count, stream);
inline void fwrite_fully(const void* ptr, size_t count, FILE* stream) {
size_t written = std::fwrite(ptr, 1, count, stream);
if (written < count)
FMT_THROW(system_error(errno, FMT_STRING("cannot write to file")));
}
@ -985,8 +984,7 @@ template <> struct cache_accessor<double> {
{0xe0accfa875af45a7, 0x93eb1b80a33b8606},
{0x8c6c01c9498d8b88, 0xbc72f130660533c4},
{0xaf87023b9bf0ee6a, 0xeb8fad7c7f8680b5},
{ 0xdb68c2ca82ed2a05,
0xa67398db9f6820e2 }
{0xdb68c2ca82ed2a05, 0xa67398db9f6820e2}
#else
{0xff77b1fcbebcdc4f, 0x25e8e89c13bb0f7b},
{0xce5d73ff402d98e3, 0xfb0a3d212dc81290},
@ -1132,7 +1130,7 @@ FMT_INLINE int remove_trailing_zeros(uint32_t& n, int s = 0) noexcept {
FMT_ASSERT(n != 0, "");
// Modular inverse of 5 (mod 2^32): (mod_inv_5 * 5) mod 2^32 = 1.
constexpr uint32_t mod_inv_5 = 0xcccccccd;
constexpr uint32_t mod_inv_25 = 0xc28f5c29; // = mod_inv_5 * mod_inv_5
constexpr uint32_t mod_inv_25 = 0xc28f5c29; // = mod_inv_5 * mod_inv_5
while (true) {
auto q = rotr(n * mod_inv_25, 2);
@ -1168,7 +1166,7 @@ FMT_INLINE int remove_trailing_zeros(uint64_t& n) noexcept {
// If n is not divisible by 10^8, work with n itself.
constexpr uint64_t mod_inv_5 = 0xcccccccccccccccd;
constexpr uint64_t mod_inv_25 = 0x8f5c28f5c28f5c29; // = mod_inv_5 * mod_inv_5
constexpr uint64_t mod_inv_25 = 0x8f5c28f5c28f5c29; // mod_inv_5 * mod_inv_5
int s = 0;
while (true) {
@ -1426,33 +1424,38 @@ FMT_FUNC std::string vformat(string_view fmt, format_args args) {
}
namespace detail {
#ifndef _WIN32
FMT_FUNC bool write_console(std::FILE*, string_view) { return false; }
#if !defined(_WIN32) || defined(FMT_WINDOWS_NO_WCHAR)
FMT_FUNC bool write_console(int, string_view) { return false; }
#else
using dword = conditional_t<sizeof(long) == 4, unsigned long, unsigned>;
extern "C" __declspec(dllimport) int __stdcall WriteConsoleW( //
void*, const void*, dword, dword*, void*);
FMT_FUNC bool write_console(std::FILE* f, string_view text) {
auto fd = _fileno(f);
if (!_isatty(fd)) return false;
FMT_FUNC bool write_console(int fd, string_view text) {
auto u16 = utf8_to_utf16(text);
auto written = dword();
return WriteConsoleW(reinterpret_cast<void*>(_get_osfhandle(fd)), u16.c_str(),
static_cast<uint32_t>(u16.size()), &written, nullptr) != 0;
static_cast<dword>(u16.size()), nullptr, nullptr) != 0;
}
#endif
#ifdef _WIN32
// Print assuming legacy (non-Unicode) encoding.
FMT_FUNC void vprint_mojibake(std::FILE* f, string_view fmt, format_args args) {
auto buffer = memory_buffer();
detail::vformat_to(buffer, fmt,
basic_format_args<buffer_context<char>>(args));
fwrite_fully(buffer.data(), 1, buffer.size(), f);
detail::vformat_to(buffer, fmt, args);
fwrite_fully(buffer.data(), buffer.size(), f);
}
#endif
FMT_FUNC void print(std::FILE* f, string_view text) {
if (!write_console(f, text)) fwrite_fully(text.data(), 1, text.size(), f);
#ifdef _WIN32
int fd = _fileno(f);
if (_isatty(fd)) {
std::fflush(f);
if (write_console(fd, text)) return;
}
#endif
fwrite_fully(text.data(), text.size(), f);
}
} // namespace detail

View File

@ -93,10 +93,11 @@
# define FMT_NO_UNIQUE_ADDRESS
#endif
#if FMT_GCC_VERSION || defined(__clang__)
# define FMT_VISIBILITY(value) __attribute__((visibility(value)))
// Visibility when compiled as a shared library/object.
#if defined(FMT_LIB_EXPORT) || defined(FMT_SHARED)
# define FMT_SO_VISIBILITY(value) FMT_VISIBILITY(value)
#else
# define FMT_VISIBILITY(value)
# define FMT_SO_VISIBILITY(value)
#endif
#ifdef __has_builtin
@ -152,7 +153,10 @@ FMT_END_NAMESPACE
#ifndef FMT_USE_USER_DEFINED_LITERALS
// EDG based compilers (Intel, NVIDIA, Elbrus, etc), GCC and MSVC support UDLs.
# if (FMT_HAS_FEATURE(cxx_user_literals) || FMT_GCC_VERSION >= 407 || \
//
// GCC before 4.9 requires a space in `operator"" _a` which is invalid in later
// compiler versions.
# if (FMT_HAS_FEATURE(cxx_user_literals) || FMT_GCC_VERSION >= 409 || \
FMT_MSC_VERSION >= 1900) && \
(!defined(__EDG_VERSION__) || __EDG_VERSION__ >= /* UDL feature */ 480)
# define FMT_USE_USER_DEFINED_LITERALS 1
@ -1034,7 +1038,7 @@ struct is_contiguous<basic_memory_buffer<T, SIZE, Allocator>> : std::true_type {
FMT_END_EXPORT
namespace detail {
FMT_API bool write_console(std::FILE* f, string_view text);
FMT_API bool write_console(int fd, string_view text);
FMT_API void print(std::FILE*, string_view);
} // namespace detail
@ -1046,7 +1050,7 @@ FMT_BEGIN_EXPORT
#endif
/** An error reported from a formatting function. */
class FMT_VISIBILITY("default") format_error : public std::runtime_error {
class FMT_SO_VISIBILITY("default") format_error : public std::runtime_error {
public:
using std::runtime_error::runtime_error;
};
@ -1394,7 +1398,7 @@ FMT_CONSTEXPR inline auto format_uint(It out, UInt value, int num_digits,
return out;
}
// Buffer should be large enough to hold all digits (digits / BASE_BITS + 1).
char buffer[num_bits<UInt>() / BASE_BITS + 1];
char buffer[num_bits<UInt>() / BASE_BITS + 1] = {};
format_uint<BASE_BITS>(buffer, value, num_digits, upper);
return detail::copy_str_noinline<Char>(buffer, buffer + num_digits, out);
}
@ -1736,29 +1740,6 @@ FMT_CONSTEXPR inline fp operator*(fp x, fp y) {
return {multiply(x.f, y.f), x.e + y.e + 64};
}
template <typename T = void> struct basic_data {
// For checking rounding thresholds.
// The kth entry is chosen to be the smallest integer such that the
// upper 32-bits of 10^(k+1) times it is strictly bigger than 5 * 10^k.
static constexpr uint32_t fractional_part_rounding_thresholds[8] = {
2576980378U, // ceil(2^31 + 2^32/10^1)
2190433321U, // ceil(2^31 + 2^32/10^2)
2151778616U, // ceil(2^31 + 2^32/10^3)
2147913145U, // ceil(2^31 + 2^32/10^4)
2147526598U, // ceil(2^31 + 2^32/10^5)
2147487943U, // ceil(2^31 + 2^32/10^6)
2147484078U, // ceil(2^31 + 2^32/10^7)
2147483691U // ceil(2^31 + 2^32/10^8)
};
};
// This is a struct rather than an alias to avoid shadowing warnings in gcc.
struct data : basic_data<> {};
#if FMT_CPLUSPLUS < 201703L
template <typename T>
constexpr uint32_t basic_data<T>::fractional_part_rounding_thresholds[];
#endif
template <typename T, bool doublish = num_bits<T>() == num_bits<double>()>
using convert_float_result =
conditional_t<std::is_same<T, float>::value || doublish, double, T>;
@ -1977,11 +1958,12 @@ auto write_escaped_string(OutputIt out, basic_string_view<Char> str)
template <typename Char, typename OutputIt>
auto write_escaped_char(OutputIt out, Char v) -> OutputIt {
Char v_array[1] = {v};
*out++ = static_cast<Char>('\'');
if ((needs_escape(static_cast<uint32_t>(v)) && v != static_cast<Char>('"')) ||
v == static_cast<Char>('\'')) {
out = write_escaped_cp(
out, find_escape_result<Char>{&v, &v + 1, static_cast<uint32_t>(v)});
out, find_escape_result<Char>{v_array, v_array + 1, static_cast<uint32_t>(v)});
} else {
*out++ = v;
}
@ -3029,7 +3011,7 @@ class bigint {
bigits_.resize(to_unsigned(num_bigits + exp_difference));
for (int i = num_bigits - 1, j = i + exp_difference; i >= 0; --i, --j)
bigits_[j] = bigits_[i];
std::uninitialized_fill_n(bigits_.data(), exp_difference, 0);
std::uninitialized_fill_n(bigits_.data(), exp_difference, 0u);
exp_ -= exp_difference;
}
@ -3178,8 +3160,10 @@ FMT_CONSTEXPR20 inline void format_dragon(basic_fp<uint128_t> value,
}
if (buf[0] == overflow) {
buf[0] = '1';
if ((flags & dragon::fixed) != 0) buf.push_back('0');
else ++exp10;
if ((flags & dragon::fixed) != 0)
buf.push_back('0');
else
++exp10;
}
return;
}
@ -3276,6 +3260,17 @@ FMT_CONSTEXPR20 void format_hexfloat(Float value, int precision,
format_hexfloat(static_cast<double>(value), precision, specs, buf);
}
constexpr uint32_t fractional_part_rounding_thresholds(int index) {
// For checking rounding thresholds.
// The kth entry is chosen to be the smallest integer such that the
// upper 32-bits of 10^(k+1) times it is strictly bigger than 5 * 10^k.
// It is equal to ceil(2^31 + 2^32/10^(k + 1)).
// These are stored in a string literal because we cannot have static arrays
// in constexpr functions and non-static ones are poorly optimized.
return U"\x9999999a\x828f5c29\x80418938\x80068db9\x8000a7c6\x800010c7"
U"\x800001ae\x8000002b"[index];
}
template <typename Float>
FMT_CONSTEXPR20 auto format_float(Float value, int precision, float_specs specs,
buffer<char>& buf) -> int {
@ -3480,12 +3475,12 @@ FMT_CONSTEXPR20 auto format_float(Float value, int precision, float_specs specs,
// fractional part is strictly larger than 1/2.
if (precision < 9) {
uint32_t fractional_part = static_cast<uint32_t>(prod);
should_round_up = fractional_part >=
data::fractional_part_rounding_thresholds
[8 - number_of_digits_to_print] ||
((fractional_part >> 31) &
((digits & 1) | (second_third_subsegments != 0) |
has_more_segments)) != 0;
should_round_up =
fractional_part >= fractional_part_rounding_thresholds(
8 - number_of_digits_to_print) ||
((fractional_part >> 31) &
((digits & 1) | (second_third_subsegments != 0) |
has_more_segments)) != 0;
}
// Rounding at the subsegment boundary.
// In this case, the fractional part is at least 1/2 if and only if
@ -3520,12 +3515,12 @@ FMT_CONSTEXPR20 auto format_float(Float value, int precision, float_specs specs,
// of 19 digits, so in this case the third segment should be
// consisting of a genuine digit from the input.
uint32_t fractional_part = static_cast<uint32_t>(prod);
should_round_up = fractional_part >=
data::fractional_part_rounding_thresholds
[8 - number_of_digits_to_print] ||
((fractional_part >> 31) &
((digits & 1) | (third_subsegment != 0) |
has_more_segments)) != 0;
should_round_up =
fractional_part >= fractional_part_rounding_thresholds(
8 - number_of_digits_to_print) ||
((fractional_part >> 31) &
((digits & 1) | (third_subsegment != 0) |
has_more_segments)) != 0;
}
// Rounding at the subsegment boundary.
else {
@ -3757,8 +3752,11 @@ template <typename Char, typename OutputIt, typename T,
FMT_CONSTEXPR auto write(OutputIt out, const T& value)
-> enable_if_t<mapped_type_constant<T, Context>::value == type::custom_type,
OutputIt> {
auto formatter = typename Context::template formatter_type<T>();
auto parse_ctx = typename Context::parse_context_type({});
formatter.parse(parse_ctx);
auto ctx = Context(out, {}, {});
return typename Context::template formatter_type<T>().format(value, ctx);
return formatter.format(value, ctx);
}
// An argument visitor that formats the argument and writes it via the output
@ -4198,6 +4196,59 @@ template <typename T> struct formatter<group_digits_view<T>> : formatter<T> {
}
};
template <typename T> struct nested_view {
const formatter<T>* fmt;
const T* value;
};
template <typename T> struct formatter<nested_view<T>> {
FMT_CONSTEXPR auto parse(format_parse_context& ctx) -> const char* {
return ctx.begin();
}
auto format(nested_view<T> view, format_context& ctx) const
-> decltype(ctx.out()) {
return view.fmt->format(*view.value, ctx);
}
};
template <typename T> struct nested_formatter {
private:
int width_;
detail::fill_t<char> fill_;
align_t align_ : 4;
formatter<T> formatter_;
public:
constexpr nested_formatter() : width_(0), align_(align_t::none) {}
FMT_CONSTEXPR auto parse(format_parse_context& ctx) -> const char* {
auto specs = detail::dynamic_format_specs<char>();
auto it = parse_format_specs(ctx.begin(), ctx.end(), specs, ctx,
detail::type::none_type);
width_ = specs.width;
fill_ = specs.fill;
align_ = specs.align;
ctx.advance_to(it);
return formatter_.parse(ctx);
}
template <typename F>
auto write_padded(format_context& ctx, F write) const -> decltype(ctx.out()) {
if (width_ == 0) return write(ctx.out());
auto buf = memory_buffer();
write(std::back_inserter(buf));
auto specs = format_specs<>();
specs.width = width_;
specs.fill = fill_;
specs.align = align_;
return detail::write(ctx.out(), string_view(buf.data(), buf.size()), specs);
}
auto nested(const T& value) const -> nested_view<T> {
return nested_view<T>{&formatter_, &value};
}
};
// DEPRECATED! join_view will be moved to ranges.h.
template <typename It, typename Sentinel, typename Char = char>
struct join_view : detail::view {
@ -4426,7 +4477,7 @@ template <detail_exported::fixed_string Str> constexpr auto operator""_a() {
return detail::udl_arg<char_t, sizeof(Str.data) / sizeof(char_t), Str>();
}
# else
constexpr auto operator"" _a(const char* s, size_t) -> detail::udl_arg<char> {
constexpr auto operator""_a(const char* s, size_t) -> detail::udl_arg<char> {
return {s};
}
# endif

View File

@ -13,12 +13,14 @@
#include <cstdio>
#include <system_error> // std::system_error
#if defined __APPLE__ || defined(__FreeBSD__)
# include <xlocale.h> // for LC_NUMERIC_MASK on OS X
#endif
#include "format.h"
#if defined __APPLE__ || defined(__FreeBSD__)
# if FMT_HAS_INCLUDE(<xlocale.h>)
# include <xlocale.h> // for LC_NUMERIC_MASK on OS X
# endif
#endif
#ifndef FMT_USE_FCNTL
// UWP doesn't provide _pipe.
# if FMT_HAS_INCLUDE("winapifamily.h")
@ -46,6 +48,7 @@
// Calls to system functions are wrapped in FMT_SYSTEM for testability.
#ifdef FMT_SYSTEM
# define FMT_HAS_SYSTEM
# define FMT_POSIX_CALL(call) FMT_SYSTEM(call)
#else
# define FMT_SYSTEM(call) ::call
@ -419,7 +422,7 @@ class FMT_API ostream {
output to the file.
*/
template <typename... T> void print(format_string<T...> fmt, T&&... args) {
vformat_to(detail::buffer_appender<char>(buffer_), fmt,
vformat_to(std::back_inserter(buffer_), fmt,
fmt::make_format_args(args...));
}
};

View File

@ -10,11 +10,12 @@
#include <fstream> // std::filebuf
#if defined(_WIN32) && defined(__GLIBCXX__)
# include <ext/stdio_filebuf.h>
# include <ext/stdio_sync_filebuf.h>
#elif defined(_WIN32) && defined(_LIBCPP_VERSION)
# include <__std_stream>
#ifdef _WIN32
# ifdef __GLIBCXX__
# include <ext/stdio_filebuf.h>
# include <ext/stdio_sync_filebuf.h>
# endif
# include <io.h>
#endif
#include "format.h"
@ -37,31 +38,34 @@ class file_access {
template class file_access<file_access_tag, std::filebuf,
&std::filebuf::_Myfile>;
auto get_file(std::filebuf&) -> FILE*;
#elif defined(_WIN32) && defined(_LIBCPP_VERSION)
template class file_access<file_access_tag, std::__stdoutbuf<char>,
&std::__stdoutbuf<char>::__file_>;
auto get_file(std::__stdoutbuf<char>&) -> FILE*;
#endif
inline bool write_ostream_unicode(std::ostream& os, fmt::string_view data) {
FILE* f = nullptr;
#if FMT_MSC_VERSION
if (auto* buf = dynamic_cast<std::filebuf*>(os.rdbuf()))
if (FILE* f = get_file(*buf)) return write_console(f, data);
#elif defined(_WIN32) && defined(__GLIBCXX__)
auto* rdbuf = os.rdbuf();
FILE* c_file;
if (auto* sfbuf = dynamic_cast<__gnu_cxx::stdio_sync_filebuf<char>*>(rdbuf))
c_file = sfbuf->file();
else if (auto* fbuf = dynamic_cast<__gnu_cxx::stdio_filebuf<char>*>(rdbuf))
c_file = fbuf->file();
f = get_file(*buf);
else
return false;
#elif defined(_WIN32) && defined(__GLIBCXX__)
auto* rdbuf = os.rdbuf();
if (auto* sfbuf = dynamic_cast<__gnu_cxx::stdio_sync_filebuf<char>*>(rdbuf))
f = sfbuf->file();
else if (auto* fbuf = dynamic_cast<__gnu_cxx::stdio_filebuf<char>*>(rdbuf))
f = fbuf->file();
else
return false;
if (c_file) return write_console(c_file, data);
#elif defined(_WIN32) && defined(_LIBCPP_VERSION)
if (auto* buf = dynamic_cast<std::__stdoutbuf<char>*>(os.rdbuf()))
if (FILE* f = get_file(*buf)) return write_console(f, data);
#else
ignore_unused(os, data);
ignore_unused(os, data, f);
#endif
#ifdef _WIN32
if (f) {
int fd = _fileno(f);
if (_isatty(fd)) {
os.flush();
return write_console(fd, data);
}
}
#endif
return false;
}
@ -98,7 +102,9 @@ void format_value(buffer<Char>& buf, const T& value,
output.exceptions(std::ios_base::failbit | std::ios_base::badbit);
}
template <typename T> struct streamed_view { const T& value; };
template <typename T> struct streamed_view {
const T& value;
};
} // namespace detail
@ -140,7 +146,7 @@ struct formatter<detail::streamed_view<T>, Char>
\endrst
*/
template <typename T>
auto streamed(const T& value) -> detail::streamed_view<T> {
constexpr auto streamed(const T& value) -> detail::streamed_view<T> {
return {value};
}

View File

@ -16,13 +16,19 @@
FMT_BEGIN_NAMESPACE
FMT_BEGIN_EXPORT
template <typename T> struct printf_formatter { printf_formatter() = delete; };
template <typename T> struct printf_formatter {
printf_formatter() = delete;
};
template <typename Char> class basic_printf_context {
private:
detail::buffer_appender<Char> out_;
basic_format_args<basic_printf_context> args_;
static_assert(std::is_same<Char, char>::value ||
std::is_same<Char, wchar_t>::value,
"Unsupported code unit type.");
public:
using char_type = Char;
using parse_context_type = basic_format_parse_context<Char>;
@ -102,7 +108,9 @@ struct is_zero_int {
template <typename T> struct make_unsigned_or_bool : std::make_unsigned<T> {};
template <> struct make_unsigned_or_bool<bool> { using type = bool; };
template <> struct make_unsigned_or_bool<bool> {
using type = bool;
};
template <typename T, typename Context> class arg_converter {
private:

View File

@ -1,13 +1,9 @@
// Formatting library for C++ - experimental range support
// Formatting library for C++ - range and tuple support
//
// Copyright (c) 2012 - present, Victor Zverovich
// Copyright (c) 2012 - present, Victor Zverovich and {fmt} contributors
// All rights reserved.
//
// For the license information refer to format.h.
//
// Copyright (c) 2018 - present, Remotion (Igor Schulz)
// All Rights Reserved
// {fmt} support for ranges, containers and types tuple interface.
#ifndef FMT_RANGES_H_
#define FMT_RANGES_H_

View File

@ -64,38 +64,29 @@ FMT_BEGIN_NAMESPACE
namespace detail {
template <typename Char> auto get_path_string(const std::filesystem::path& p) {
return p.string<Char>();
template <typename Char, typename PathChar> auto get_path_string(
const std::filesystem::path& p, const std::basic_string<PathChar>& native) {
if constexpr (std::is_same_v<Char, char> && std::is_same_v<PathChar, wchar_t>)
return to_utf8<wchar_t>(native, to_utf8_error_policy::replace);
else
return p.string<Char>();
}
template <typename Char>
template <typename Char, typename PathChar>
void write_escaped_path(basic_memory_buffer<Char>& quoted,
const std::filesystem::path& p) {
write_escaped_string<Char>(std::back_inserter(quoted), p.string<Char>());
}
# ifdef _WIN32
template <>
inline auto get_path_string<char>(const std::filesystem::path& p) {
return to_utf8<wchar_t>(p.native(), to_utf8_error_policy::replace);
}
template <>
inline void write_escaped_path<char>(memory_buffer& quoted,
const std::filesystem::path& p) {
auto buf = basic_memory_buffer<wchar_t>();
write_escaped_string<wchar_t>(std::back_inserter(buf), p.native());
bool valid = to_utf8<wchar_t>::convert(quoted, {buf.data(), buf.size()});
FMT_ASSERT(valid, "invalid utf16");
}
# endif // _WIN32
template <>
inline void write_escaped_path<std::filesystem::path::value_type>(
basic_memory_buffer<std::filesystem::path::value_type>& quoted,
const std::filesystem::path& p) {
write_escaped_string<std::filesystem::path::value_type>(
std::back_inserter(quoted), p.native());
const std::filesystem::path& p,
const std::basic_string<PathChar>& native) {
if constexpr (std::is_same_v<Char, char> && std::is_same_v<PathChar, wchar_t>) {
auto buf = basic_memory_buffer<wchar_t>();
write_escaped_string<wchar_t>(std::back_inserter(buf), native);
bool valid = to_utf8<wchar_t>::convert(quoted, {buf.data(), buf.size()});
FMT_ASSERT(valid, "invalid utf16");
} else if constexpr (std::is_same_v<Char, PathChar>) {
write_escaped_string<std::filesystem::path::value_type>(
std::back_inserter(quoted), native);
} else {
write_escaped_string<Char>(std::back_inserter(quoted), p.string<Char>());
}
}
} // namespace detail
@ -131,11 +122,11 @@ template <typename Char> struct formatter<std::filesystem::path, Char> {
detail::handle_dynamic_spec<detail::width_checker>(specs.width, width_ref_,
ctx);
if (!debug_) {
auto s = detail::get_path_string<Char>(p);
auto s = detail::get_path_string<Char>(p, p.native());
return detail::write(ctx.out(), basic_string_view<Char>(s), specs);
}
auto quoted = basic_memory_buffer<Char>();
detail::write_escaped_path(quoted, p);
detail::write_escaped_path(quoted, p, p.native());
return detail::write(ctx.out(),
basic_string_view<Char>(quoted.data(), quoted.size()),
specs);
@ -145,6 +136,32 @@ FMT_END_NAMESPACE
#endif
FMT_BEGIN_NAMESPACE
FMT_EXPORT
template <std::size_t N, typename Char>
struct formatter<std::bitset<N>, Char> : nested_formatter<string_view> {
private:
// Functor because C++11 doesn't support generic lambdas.
struct writer {
const std::bitset<N>& bs;
template <typename OutputIt>
FMT_CONSTEXPR OutputIt operator()(OutputIt out) {
for (auto pos = N; pos > 0; --pos) {
out = detail::write<Char>(out, bs[pos - 1] ? Char('1') : Char('0'));
}
return out;
}
};
public:
template <typename FormatContext>
auto format(const std::bitset<N>& bs, FormatContext& ctx) const
-> decltype(ctx.out()) {
return write_padded(ctx, writer{bs});
}
};
FMT_EXPORT
template <typename Char>
struct formatter<std::thread::id, Char> : basic_ostream_formatter<Char> {};
@ -451,15 +468,14 @@ struct formatter<std::atomic<T>, Char,
#ifdef __cpp_lib_atomic_flag_test
FMT_EXPORT
template <typename Char>
struct formatter<std::atomic_flag, Char>
: formatter<bool, Char> {
struct formatter<std::atomic_flag, Char> : formatter<bool, Char> {
template <typename FormatContext>
auto format(const std::atomic_flag& v, FormatContext& ctx) const
-> decltype(ctx.out()) {
return formatter<bool, Char>::format(v.test(), ctx);
}
};
#endif // __cpp_lib_atomic_flag_test
#endif // __cpp_lib_atomic_flag_test
FMT_END_NAMESPACE
#endif // FMT_STD_H_

View File

@ -63,8 +63,6 @@ module;
# if defined(__GLIBCXX__)
# include <ext/stdio_filebuf.h>
# include <ext/stdio_sync_filebuf.h>
# elif defined(_LIBCPP_VERSION)
# include <__std_stream>
# endif
# define WIN32_LEAN_AND_MEAN
# include <windows.h>

View File

@ -18,8 +18,8 @@
# include <sys/stat.h>
# include <sys/types.h>
# ifdef _WRS_KERNEL // VxWorks7 kernel
# include <ioLib.h> // getpagesize
# ifdef _WRS_KERNEL // VxWorks7 kernel
# include <ioLib.h> // getpagesize
# endif
# ifndef _WIN32
@ -182,10 +182,14 @@ void buffered_file::close() {
}
int buffered_file::descriptor() const {
#ifdef fileno // fileno is a macro on OpenBSD so we cannot use FMT_POSIX_CALL.
int fd = fileno(file_);
#else
#if !defined(fileno)
int fd = FMT_POSIX_CALL(fileno(file_));
#elif defined(FMT_HAS_SYSTEM)
// fileno is a macro on OpenBSD so we cannot use FMT_POSIX_CALL.
# define FMT_DISABLE_MACRO
int fd = FMT_SYSTEM(fileno FMT_DISABLE_MACRO(file_));
#else
int fd = fileno(file_);
#endif
if (fd == -1)
FMT_THROW(system_error(errno, FMT_STRING("cannot get file descriptor")));

View File

@ -231,7 +231,7 @@ def release(args):
# Convert changelog from RST to GitHub-flavored Markdown and get the
# version.
changelog = 'ChangeLog.rst'
changelog = 'ChangeLog.md'
changelog_path = os.path.join(fmt_repo.dir, changelog)
import rst2md
changes, version = rst2md.convert(changelog_path)

View File

@ -1,166 +0,0 @@
#!/usr/bin/env python
# reStructuredText (RST) to GitHub-flavored Markdown converter
import re, sys
from docutils import core, nodes, writers
def is_github_ref(node):
return re.match('https://github.com/.*/(issues|pull)/.*', node['refuri'])
class Translator(nodes.NodeVisitor):
def __init__(self, document):
nodes.NodeVisitor.__init__(self, document)
self.output = ''
self.indent = 0
self.preserve_newlines = False
def write(self, text):
self.output += text.replace('\n', '\n' + ' ' * self.indent)
def visit_document(self, node):
pass
def depart_document(self, node):
pass
def visit_section(self, node):
pass
def depart_section(self, node):
# Skip all sections except the first one.
raise nodes.StopTraversal
def visit_title(self, node):
self.version = re.match(r'(\d+\.\d+\.\d+).*', node.children[0]).group(1)
raise nodes.SkipChildren
def visit_title_reference(self, node):
raise Exception(node)
def depart_title(self, node):
pass
def visit_Text(self, node):
if not self.preserve_newlines:
node = node.replace('\n', ' ')
self.write(node)
def depart_Text(self, node):
pass
def visit_bullet_list(self, node):
pass
def depart_bullet_list(self, node):
pass
def visit_list_item(self, node):
self.write('* ')
self.indent += 2
def depart_list_item(self, node):
self.indent -= 2
self.write('\n\n')
def visit_paragraph(self, node):
self.write('\n\n')
def depart_paragraph(self, node):
pass
def visit_reference(self, node):
if not is_github_ref(node):
self.write('[')
def depart_reference(self, node):
if not is_github_ref(node):
self.write('](' + node['refuri'] + ')')
def visit_target(self, node):
pass
def depart_target(self, node):
pass
def visit_literal(self, node):
self.write('`')
def depart_literal(self, node):
self.write('`')
def visit_literal_block(self, node):
self.write('\n\n```')
if 'c++' in node['classes']:
self.write('c++')
self.write('\n')
self.preserve_newlines = True
def depart_literal_block(self, node):
self.write('\n```\n')
self.preserve_newlines = False
def visit_inline(self, node):
pass
def depart_inline(self, node):
pass
def visit_image(self, node):
self.write('![](' + node['uri'] + ')')
def depart_image(self, node):
pass
def write_row(self, row, widths):
for i, entry in enumerate(row):
text = entry[0][0] if len(entry) > 0 else ''
if i != 0:
self.write('|')
self.write('{:{}}'.format(text, widths[i]))
self.write('\n')
def visit_table(self, node):
table = node.children[0]
colspecs = table[:-2]
thead = table[-2]
tbody = table[-1]
widths = [int(cs['colwidth']) for cs in colspecs]
sep = '|'.join(['-' * w for w in widths]) + '\n'
self.write('\n\n')
self.write_row(thead[0], widths)
self.write(sep)
for row in tbody:
self.write_row(row, widths)
raise nodes.SkipChildren
def depart_table(self, node):
pass
def visit_system_message(self, node):
pass
def depart_system_message(self, node):
pass
class MDWriter(writers.Writer):
"""GitHub-flavored markdown writer"""
supported = ('md',)
"""Formats this writer supports."""
def translate(self):
translator = Translator(self.document)
self.document.walkabout(translator)
self.output = (translator.output, translator.version)
def convert(rst_path):
"""Converts RST file to Markdown."""
return core.publish_file(source_path=rst_path, writer=MDWriter())
if __name__ == '__main__':
convert(sys.argv[1])

View File

@ -175,7 +175,12 @@ if (FMT_PEDANTIC)
endif ()
# These tests are disabled on Windows because they take too long.
if (FMT_PEDANTIC AND NOT WIN32)
# They are disabled on GCC < 4.9 because it can not parse UDLs without
# a space after `operator""` but that is an incorrect syntax for any more
# modern compiler.
if (FMT_PEDANTIC AND NOT WIN32 AND NOT (
CMAKE_CXX_COMPILER_ID MATCHES "GNU" AND
CMAKE_CXX_COMPILER_VERSION VERSION_LESS 4.9))
# Test if incorrect API usages produce compilation error.
add_test(compile-error-test ${CMAKE_CTEST_COMMAND}
--build-and-test

View File

@ -454,8 +454,11 @@ TEST(chrono_test, format_default) {
fmt::format("{}", std::chrono::duration<int, std::peta>(42)));
EXPECT_EQ("42Es",
fmt::format("{}", std::chrono::duration<int, std::exa>(42)));
EXPECT_EQ("42m", fmt::format("{}", std::chrono::minutes(42)));
EXPECT_EQ("42min", fmt::format("{}", std::chrono::minutes(42)));
EXPECT_EQ("42h", fmt::format("{}", std::chrono::hours(42)));
# if defined(__cpp_lib_chrono) && __cpp_lib_chrono >= 201907L
EXPECT_EQ("42d", fmt::format("{}", std::chrono::days(42)));
# endif
EXPECT_EQ(
"42[15]s",
fmt::format("{}", std::chrono::duration<int, std::ratio<15, 1>>(42)));
@ -740,21 +743,20 @@ TEST(chrono_test, unsigned_duration) {
}
TEST(chrono_test, weekday) {
auto loc = get_locale("ru_RU.UTF-8");
auto loc = get_locale("es_ES.UTF-8");
std::locale::global(loc);
auto mon = fmt::weekday(1);
auto sat = fmt::weekday(6);
auto tm = std::tm();
tm.tm_wday = static_cast<int>(mon.c_encoding());
tm.tm_wday = static_cast<int>(sat.c_encoding());
EXPECT_EQ(fmt::format("{}", mon), "Mon");
EXPECT_EQ(fmt::format("{:%a}", tm), "Mon");
EXPECT_EQ(fmt::format("{}", sat), "Sat");
EXPECT_EQ(fmt::format("{:%a}", tm), "Sat");
if (loc != std::locale::classic()) {
EXPECT_THAT((std::vector<std::string>{"пн", "Пн", "пнд", "Пнд"}),
Contains(fmt::format(loc, "{:L}", mon)));
EXPECT_THAT((std::vector<std::string>{"пн", "Пн", "пнд", "Пнд"}),
Contains(fmt::format(loc, "{:%a}", tm)));
auto saturdays = std::vector<std::string>{"sáb", "sá."};
EXPECT_THAT(saturdays, Contains(fmt::format(loc, "{:L}", sat)));
EXPECT_THAT(saturdays, Contains(fmt::format(loc, "{:%a}", tm)));
}
}

View File

@ -351,7 +351,7 @@ TEST(format_impl_test, write_dragon_even) {
if (!FMT_MSC_VERSION) EXPECT_EQ(s, "33554450");
}
#ifdef _WIN32
#if defined(_WIN32) && !defined(FMT_WINDOWS_NO_WCHAR)
# include <windows.h>
TEST(format_impl_test, write_console_signature) {

View File

@ -443,6 +443,10 @@ TEST(memory_buffer_test, max_size_allocator_overflow) {
EXPECT_THROW(buffer.resize(161), std::exception);
}
TEST(format_test, exception_from_lib) {
EXPECT_THROW_MSG(fmt::throw_format_error("test"), format_error, "test");
}
TEST(format_test, escape) {
EXPECT_EQ("{", fmt::format("{{"));
EXPECT_EQ("before {", fmt::format("before {{"));
@ -1775,6 +1779,26 @@ TEST(format_test, group_digits_view) {
EXPECT_EQ(fmt::format("{:8}", fmt::group_digits(1000)), " 1,000");
}
#ifdef __cpp_generic_lambdas
struct point {
double x, y;
};
FMT_BEGIN_NAMESPACE
template <> struct formatter<point> : nested_formatter<double> {
auto format(point p, format_context& ctx) const -> decltype(ctx.out()) {
return write_padded(ctx, [this, p](auto out) -> decltype(out) {
return fmt::format_to(out, "({}, {})", nested(p.x), nested(p.y));
});
}
};
FMT_END_NAMESPACE
TEST(format_test, nested_formatter) {
EXPECT_EQ(fmt::format("{:>16.2f}", point{1, 2}), " (1.00, 2.00)");
}
#endif // __cpp_generic_lambdas
enum test_enum { foo, bar };
auto format_as(test_enum e) -> int { return e; }

View File

@ -1,13 +1,9 @@
// Formatting library for C++ - the core API
// Formatting library for C++ - ranges tests
//
// Copyright (c) 2012 - present, Victor Zverovich
// Copyright (c) 2012 - present, Victor Zverovich and {fmt} contributors
// All rights reserved.
//
// For the license information refer to format.h.
//
// Copyright (c) 2018 - present, Remotion (Igor Schulz)
// All Rights Reserved
// {fmt} support for ranges, containers and types tuple interface.
#include "fmt/ranges.h"
@ -63,13 +59,18 @@ TEST(ranges_test, format_vector) {
EXPECT_EQ(fmt::format("{:n:n:}", vvc), "a, b, c, a, b, c");
}
TEST(ranges_test, format_vector2) {
TEST(ranges_test, format_nested_vector) {
auto v = std::vector<std::vector<int>>{{1, 2}, {3, 5}, {7, 11}};
EXPECT_EQ(fmt::format("{}", v), "[[1, 2], [3, 5], [7, 11]]");
EXPECT_EQ(fmt::format("{:::#x}", v), "[[0x1, 0x2], [0x3, 0x5], [0x7, 0xb]]");
EXPECT_EQ(fmt::format("{:n:n:#x}", v), "0x1, 0x2, 0x3, 0x5, 0x7, 0xb");
}
TEST(ranges_test, to_string_vector) {
auto v = std::vector<std::string>{"a", "b", "c"};
EXPECT_EQ(fmt::to_string(v), "[\"a\", \"b\", \"c\"]");
}
TEST(ranges_test, format_map) {
auto m = std::map<std::string, int>{{"one", 1}, {"two", 2}};
EXPECT_EQ(fmt::format("{}", m), "{\"one\": 1, \"two\": 2}");
@ -83,7 +84,7 @@ TEST(ranges_test, format_set) {
// Models std::flat_set close enough to test if no ambiguous lookup of a
// formatter happens due to the flat_set type matching is_set and
// is_container_adaptor_like
// is_container_adaptor_like.
template <typename T> class flat_set {
public:
using key_type = T;
@ -95,11 +96,11 @@ template <typename T> class flat_set {
template <typename... Ts>
explicit flat_set(Ts&&... args) : c{std::forward<Ts>(args)...} {}
iterator begin() { return c.begin(); }
const_iterator begin() const { return c.begin(); }
auto begin() -> iterator { return c.begin(); }
auto end() -> iterator { return c.end(); }
iterator end() { return c.end(); }
const_iterator end() const { return c.end(); }
auto begin() const -> const_iterator { return c.begin(); }
auto end() const -> const_iterator { return c.end(); }
private:
std::vector<T> c;
@ -116,7 +117,6 @@ struct box {
};
auto begin(const box& b) -> const int* { return &b.value; }
auto end(const box& b) -> const int* { return &b.value + 1; }
} // namespace adl
@ -173,11 +173,12 @@ struct tuple_like {
int i;
std::string str;
template <size_t N> fmt::enable_if_t<N == 0, int> get() const noexcept {
template <size_t N>
auto get() const noexcept -> fmt::enable_if_t<N == 0, int> {
return i;
}
template <size_t N>
fmt::enable_if_t<N == 1, fmt::string_view> get() const noexcept {
auto get() const noexcept -> fmt::enable_if_t<N == 1, fmt::string_view> {
return str;
}
};
@ -210,8 +211,8 @@ TEST(ranges_test, format_to) {
}
template <typename Char> struct path_like {
const path_like* begin() const;
const path_like* end() const;
auto begin() const -> const path_like*;
auto end() const -> const path_like*;
operator std::basic_string<Char>() const;
};
@ -241,8 +242,8 @@ template <typename T> class non_const_only_range {
explicit non_const_only_range(Args&&... args)
: vec(std::forward<Args>(args)...) {}
const_iterator begin() { return vec.begin(); }
const_iterator end() { return vec.end(); }
auto begin() -> const_iterator{ return vec.begin(); }
auto end() -> const_iterator { return vec.end(); }
};
template <typename T> class noncopyable_range {
@ -259,16 +260,16 @@ template <typename T> class noncopyable_range {
noncopyable_range(noncopyable_range const&) = delete;
noncopyable_range(noncopyable_range&) = delete;
iterator begin() { return vec.begin(); }
iterator end() { return vec.end(); }
auto begin() -> iterator { return vec.begin(); }
auto end() -> iterator { return vec.end(); }
};
TEST(ranges_test, range) {
noncopyable_range<int> w(3u, 0);
auto&& w = noncopyable_range<int>(3u, 0);
EXPECT_EQ(fmt::format("{}", w), "[0, 0, 0]");
EXPECT_EQ(fmt::format("{}", noncopyable_range<int>(3u, 0)), "[0, 0, 0]");
non_const_only_range<int> x(3u, 0);
auto x = non_const_only_range<int>(3u, 0);
EXPECT_EQ(fmt::format("{}", x), "[0, 0, 0]");
EXPECT_EQ(fmt::format("{}", non_const_only_range<int>(3u, 0)), "[0, 0, 0]");
@ -343,8 +344,8 @@ bool operator!=(const char* p, zstring_sentinel) { return *p != '\0'; }
struct zstring {
const char* p;
const char* begin() const { return p; }
zstring_sentinel end() const { return {}; }
auto begin() const -> const char* { return p; }
auto end() const -> zstring_sentinel { return {}; }
};
# ifdef __cpp_lib_ranges
@ -358,20 +359,22 @@ struct cpp20_only_range {
iterator() = default;
iterator(int i) : val(i) {}
int operator*() const { return val; }
iterator& operator++() {
auto operator*() const -> int { return val; }
auto operator++() -> iterator&{
++val;
return *this;
}
void operator++(int) { ++*this; }
bool operator==(const iterator& rhs) const { return val == rhs.val; }
auto operator==(const iterator& rhs) const -> bool {
return val == rhs.val;
}
};
int lo;
int hi;
iterator begin() const { return iterator(lo); }
iterator end() const { return iterator(hi); }
auto begin() const -> iterator { return iterator(lo); }
auto end() const -> iterator { return iterator(hi); }
};
static_assert(std::input_iterator<cpp20_only_range::iterator>);
@ -385,12 +388,12 @@ TEST(ranges_test, join_sentinel) {
}
TEST(ranges_test, join_range) {
noncopyable_range<int> w(3u, 0);
auto&& w = noncopyable_range<int>(3u, 0);
EXPECT_EQ(fmt::format("{}", fmt::join(w, ",")), "0,0,0");
EXPECT_EQ(fmt::format("{}", fmt::join(noncopyable_range<int>(3u, 0), ",")),
"0,0,0");
non_const_only_range<int> x(3u, 0);
auto x = non_const_only_range<int>(3u, 0);
EXPECT_EQ(fmt::format("{}", fmt::join(x, ",")), "0,0,0");
EXPECT_EQ(fmt::format("{}", fmt::join(non_const_only_range<int>(3u, 0), ",")),
"0,0,0");
@ -456,10 +459,10 @@ template <typename R> struct fmt_ref_view {
};
TEST(ranges_test, range_of_range_of_mixed_const) {
std::vector<std::vector<int>> v = {{1, 2, 3}, {4, 5}};
auto v = std::vector<std::vector<int>>{{1, 2, 3}, {4, 5}};
EXPECT_EQ(fmt::format("{}", v), "[[1, 2, 3], [4, 5]]");
fmt_ref_view<decltype(v)> r{&v};
auto r = fmt_ref_view<decltype(v)>{&v};
EXPECT_EQ(fmt::format("{}", r), "[[1, 2, 3], [4, 5]]");
}

View File

@ -237,6 +237,13 @@ TEST(std_test, format_const_bit_reference) {
EXPECT_EQ(fmt::format("{} {}", v[0], v[1]), "true false");
}
TEST(std_test, format_bitset) {
auto bs = std::bitset<6>(42);
EXPECT_EQ(fmt::format("{}", bs), "101010");
EXPECT_EQ(fmt::format("{:0>8}", bs), "00101010");
EXPECT_EQ(fmt::format("{:-^12}", bs), "---101010---");
}
TEST(std_test, format_atomic) {
std::atomic<bool> b(false);
EXPECT_EQ(fmt::format("{}", b), "false");

View File

@ -37,9 +37,8 @@ std::locale do_get_locale(const char* name) {
std::locale get_locale(const char* name, const char* alt_name) {
auto loc = do_get_locale(name);
if (loc == std::locale::classic() && alt_name) {
if (loc == std::locale::classic() && alt_name)
loc = do_get_locale(alt_name);
}
if (loc == std::locale::classic())
fmt::print(stderr, "{} locale is missing.\n", name);
return loc;

View File

@ -102,11 +102,71 @@ struct custom_char {
template <typename T>
constexpr custom_char(T val) : value(static_cast<int>(val)) {}
operator char() const {
constexpr operator char() const {
return value <= 0xff ? static_cast<char>(value) : '\0';
}
constexpr bool operator<(custom_char c) const { return value < c.value; }
};
namespace std {
template <> struct char_traits<custom_char> {
using char_type = custom_char;
using int_type = int;
using off_type = streamoff;
using pos_type = streampos;
using state_type = mbstate_t;
static constexpr void assign(char_type& r, const char_type& a) { r = a; }
static constexpr bool eq(char_type a, char_type b) { return a == b; }
static constexpr bool lt(char_type a, char_type b) { return a < b; }
static FMT_CONSTEXPR int compare(const char_type* s1, const char_type* s2,
size_t count) {
for (; count; count--, s1++, s2++) {
if (lt(*s1, *s2)) return -1;
if (lt(*s2, *s1)) return 1;
}
return 0;
}
static FMT_CONSTEXPR size_t length(const char_type* s) {
size_t count = 0;
while (!eq(*s++, custom_char(0))) count++;
return count;
}
static const char_type* find(const char_type*, size_t, const char_type&);
static FMT_CONSTEXPR char_type* move(char_type* dest, const char_type* src,
size_t count) {
if (count == 0) return dest;
char_type* ret = dest;
if (src < dest) {
dest += count;
src += count;
for (; count; count--) assign(*--dest, *--src);
} else if (src > dest)
copy(dest, src, count);
return ret;
}
static FMT_CONSTEXPR char_type* copy(char_type* dest, const char_type* src,
size_t count) {
char_type* ret = dest;
for (; count; count--) assign(*dest++, *src++);
return ret;
}
static FMT_CONSTEXPR char_type* assign(char_type* dest, std::size_t count,
char_type a) {
char_type* ret = dest;
for (; count; count--) assign(*dest++, a);
return ret;
}
static int_type not_eof(int_type);
static char_type to_char_type(int_type);
static int_type to_int_type(char_type);
static bool eq_int_type(int_type, int_type);
static int_type eof();
};
} // namespace std
auto to_ascii(custom_char c) -> char { return c; }
FMT_BEGIN_NAMESPACE
@ -554,17 +614,14 @@ TEST(locale_test, complex) {
}
TEST(locale_test, chrono_weekday) {
auto loc = get_locale("ru_RU.UTF-8", "Russian_Russia.1251");
auto loc = get_locale("es_ES.UTF-8", "Spanish_Spain.1252");
auto loc_old = std::locale::global(loc);
auto mon = fmt::weekday(1);
EXPECT_EQ(fmt::format(L"{}", mon), L"Mon");
auto sat = fmt::weekday(6);
EXPECT_EQ(fmt::format(L"{}", sat), L"Sat");
if (loc != std::locale::classic()) {
// {L"\x43F\x43D", L"\x41F\x43D", L"\x43F\x43D\x434", L"\x41F\x43D\x434"}
// {L"пн", L"Пн", L"пнд", L"Пнд"}
EXPECT_THAT(
(std::vector<std::wstring>{L"\x43F\x43D", L"\x41F\x43D",
L"\x43F\x43D\x434", L"\x41F\x43D\x434"}),
Contains(fmt::format(loc, L"{:L}", mon)));
// L'\xE1' is 'á'.
auto saturdays = std::vector<std::wstring>{L"s\xE1""b", L"s\xE1."};
EXPECT_THAT(saturdays, Contains(fmt::format(loc, L"{:L}", sat)));
}
std::locale::global(loc_old);
}