forked from fmtlib/fmt
Compare commits
77 Commits
esp-idf-re
...
master
Author | SHA1 | Date | |
---|---|---|---|
ec628561c2 | |||
cbb18c237a | |||
6b0082e6c7 | |||
52a99a67f7 | |||
4548d1eae2 | |||
050d41e857 | |||
1c023c0087 | |||
b35d4e40fe | |||
acaf83f40f | |||
05aa783779 | |||
05dda9490d | |||
caf4fcb207 | |||
e0d3e346d2 | |||
19276d7325 | |||
2a2c6e676f | |||
3b7f58a8b3 | |||
e9bbd4069e | |||
857cce7a83 | |||
081d5b0d8b | |||
baae1ed658 | |||
2ac6c5ca8b | |||
d9063baf22 | |||
f7542c5761 | |||
130cf54cbc | |||
8e0ca0589f | |||
bf497ac068 | |||
bb8d50f04b | |||
f76603f21e | |||
f918289363 | |||
72e883e163 | |||
b3bf23f3c4 | |||
349e1c48d1 | |||
79dbd3f192 | |||
2dd4fa8742 | |||
44f3d8a77c | |||
06b20387ae | |||
649f2460db | |||
7529af8f99 | |||
a3a74fa7f3 | |||
8ef4db4b96 | |||
492a99c964 | |||
3baaa8d899 | |||
0e01e46c11 | |||
f6ca4ea199 | |||
a8a73da7e4 | |||
aa3c5a4127 | |||
bfdf50d183 | |||
571a9b7b26 | |||
6c088be8ec | |||
016b1faede | |||
e25370093a | |||
d4987546a4 | |||
5bdce181f1 | |||
a4b7b24b7b | |||
fac60bd4f5 | |||
f5be4a8a9a | |||
84e6661517 | |||
ac3240439c | |||
8894ae87fe | |||
ca608547e5 | |||
23cf4055a0 | |||
46c8301ee9 | |||
a79a979828 | |||
457bb6a98f | |||
61aef41110 | |||
2a45fd30fe | |||
24296cff1c | |||
3d1d20a6ac | |||
0302c527c6 | |||
154eccfeb1 | |||
35dc5def30 | |||
e1fc481d65 | |||
e8259c5298 | |||
6379251554 | |||
951fd9e66f | |||
be89b9a41e | |||
28e2d3b640 |
2
.github/dependabot.yml
vendored
2
.github/dependabot.yml
vendored
@ -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
|
||||
|
2
.github/workflows/doc.yml
vendored
2
.github/workflows/doc.yml
vendored
@ -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: |
|
||||
|
2
.github/workflows/linux.yml
vendored
2
.github/workflows/linux.yml
vendored
@ -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'
|
||||
|
2
.github/workflows/macos.yml
vendored
2
.github/workflows/macos.yml
vendored
@ -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'
|
||||
|
6
.github/workflows/scorecard.yml
vendored
6
.github/workflows/scorecard.yml
vendored
@ -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
|
||||
|
4
.github/workflows/windows.yml
vendored
4
.github/workflows/windows.yml
vendored
@ -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 }
|
||||
|
@ -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
5282
ChangeLog.md
Normal file
File diff suppressed because it is too large
Load Diff
5922
ChangeLog.rst
5922
ChangeLog.rst
File diff suppressed because it is too large
Load Diff
36
README.rst
36
README.rst
@ -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.
|
||||
|
12
doc/_static/bootstrap.min.js
vendored
12
doc/_static/bootstrap.min.js
vendored
File diff suppressed because one or more lines are too long
136
doc/api.rst
136
doc/api.rst
@ -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:
|
||||
|
@ -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):
|
||||
|
@ -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
|
||||
==========================
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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>
|
||||
|
@ -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>
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
@ -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...));
|
||||
}
|
||||
};
|
||||
|
@ -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};
|
||||
}
|
||||
|
||||
|
@ -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:
|
||||
|
@ -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_
|
||||
|
@ -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_
|
||||
|
@ -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>
|
||||
|
14
src/os.cc
14
src/os.cc
@ -18,8 +18,8 @@
|
||||
# include <sys/stat.h>
|
||||
# include <sys/types.h>
|
||||
|
||||
# ifdef _WRS_KERNEL // VxWorks7 kernel
|
||||
# include <ioLib.h> // getpagesize
|
||||
# ifdef _WRS_KERNEL // VxWorks7 kernel
|
||||
# include <ioLib.h> // getpagesize
|
||||
# endif
|
||||
|
||||
# ifndef _WIN32
|
||||
@ -182,10 +182,14 @@ void buffered_file::close() {
|
||||
}
|
||||
|
||||
int buffered_file::descriptor() const {
|
||||
#ifdef fileno // fileno is a macro on OpenBSD so we cannot use FMT_POSIX_CALL.
|
||||
int fd = fileno(file_);
|
||||
#else
|
||||
#if !defined(fileno)
|
||||
int fd = FMT_POSIX_CALL(fileno(file_));
|
||||
#elif defined(FMT_HAS_SYSTEM)
|
||||
// fileno is a macro on OpenBSD so we cannot use FMT_POSIX_CALL.
|
||||
# define FMT_DISABLE_MACRO
|
||||
int fd = FMT_SYSTEM(fileno FMT_DISABLE_MACRO(file_));
|
||||
#else
|
||||
int fd = fileno(file_);
|
||||
#endif
|
||||
if (fd == -1)
|
||||
FMT_THROW(system_error(errno, FMT_STRING("cannot get file descriptor")));
|
||||
|
@ -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)
|
||||
|
@ -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('')
|
||||
|
||||
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])
|
@ -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
|
||||
|
@ -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)));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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) {
|
||||
|
@ -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; }
|
||||
|
||||
|
@ -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]]");
|
||||
}
|
||||
|
||||
|
@ -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");
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
}
|
||||
|
Reference in New Issue
Block a user