Compare commits

..

6 Commits

Author SHA1 Message Date
lightningterror a5ffb0ef1d
Merge b4787a2d21 into de9d08075e 2025-01-17 05:26:35 +01:00
TheLastRar de9d08075e Misc: Don't use deprecated fmt/core.h header 2025-01-17 04:35:29 +01:00
TheLastRar c58f6f2f70 3rdparty: Update fmt to v11.1.2 2025-01-17 04:35:29 +01:00
TheLastRar 56aa5d9657 SmallString: Prep for fmt update 2025-01-17 04:35:29 +01:00
Penguino 754057b496 GameDB: Add Ougon no Kaze VU1 clamping, JoJo romanization consistency
- Added VU1 Clamping (Extra + Preserve Sign) to Ougon no Kaze (SLPM-65140, SLPM-66853, SLPM-61030) to fix the shading of the enemy's Stand in Chapter 11-2
- Changed "Jojo" romanization to "JoJo" (SLPM-66853, SLPS-25686)
- Changed "Kimyouna" to "Kimyou na" in Ougon no Kaze [Best Price] (SLPM-66853)
2025-01-17 04:31:31 +01:00
lightningterror 513c44f07f DumpRunner: Fix compare when frames are missing. 2025-01-17 04:29:37 +01:00
67 changed files with 9837 additions and 12048 deletions

View File

@ -1,4 +1,4 @@
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. # Fallback for using newer policies on CMake <3.12.
if (${CMAKE_VERSION} VERSION_LESS 3.12) if (${CMAKE_VERSION} VERSION_LESS 3.12)
@ -36,6 +36,12 @@ function(enable_module target)
endif () endif ()
endfunction() endfunction()
set(FMT_USE_CMAKE_MODULES FALSE)
if (CMAKE_VERSION VERSION_GREATER_EQUAL 3.28 AND
CMAKE_GENERATOR STREQUAL "Ninja")
set(FMT_USE_CMAKE_MODULES TRUE)
endif ()
# Adds a library compiled with C++20 module support. # Adds a library compiled with C++20 module support.
# `enabled` is a CMake variables that specifies if modules are enabled. # `enabled` is a CMake variables that specifies if modules are enabled.
# If modules are disabled `add_module_library` falls back to creating a # If modules are disabled `add_module_library` falls back to creating a
@ -53,6 +59,7 @@ function(add_module_library name)
if (NOT ${${AML_IF}}) if (NOT ${${AML_IF}})
# Create a non-modular library. # Create a non-modular library.
target_sources(${name} PRIVATE ${AML_FALLBACK}) target_sources(${name} PRIVATE ${AML_FALLBACK})
set_target_properties(${name} PROPERTIES CXX_SCAN_FOR_MODULES OFF)
return() return()
endif () endif ()
@ -62,48 +69,55 @@ function(add_module_library name)
target_compile_options(${name} PUBLIC -fmodules-ts) target_compile_options(${name} PUBLIC -fmodules-ts)
endif () endif ()
# `std` is affected by CMake options and may be higher than C++20. target_compile_definitions(${name} PRIVATE FMT_MODULE)
get_target_property(std ${name} CXX_STANDARD)
if (CMAKE_CXX_COMPILER_ID MATCHES "Clang") if (FMT_USE_CMAKE_MODULES)
set(pcms) target_sources(${name} PUBLIC FILE_SET fmt TYPE CXX_MODULES
foreach (src ${sources}) FILES ${sources})
get_filename_component(pcm ${src} NAME_WE) else()
set(pcm ${pcm}.pcm) # `std` is affected by CMake options and may be higher than C++20.
get_target_property(std ${name} CXX_STANDARD)
# Propagate -fmodule-file=*.pcm to targets that link with this library. if (CMAKE_CXX_COMPILER_ID MATCHES "Clang")
target_compile_options( set(pcms)
${name} PUBLIC -fmodule-file=${CMAKE_CURRENT_BINARY_DIR}/${pcm}) foreach (src ${sources})
get_filename_component(pcm ${src} NAME_WE)
set(pcm ${pcm}.pcm)
# Use an absolute path to prevent target_link_libraries prepending -l # Propagate -fmodule-file=*.pcm to targets that link with this library.
# to it. target_compile_options(
set(pcms ${pcms} ${CMAKE_CURRENT_BINARY_DIR}/${pcm}) ${name} PUBLIC -fmodule-file=${CMAKE_CURRENT_BINARY_DIR}/${pcm})
add_custom_command(
OUTPUT ${pcm}
COMMAND ${CMAKE_CXX_COMPILER}
-std=c++${std} -x c++-module --precompile -c
-o ${pcm} ${CMAKE_CURRENT_SOURCE_DIR}/${src}
"-I$<JOIN:$<TARGET_PROPERTY:${name},INCLUDE_DIRECTORIES>,;-I>"
# Required by the -I generator expression above.
COMMAND_EXPAND_LISTS
DEPENDS ${src})
endforeach ()
# Add .pcm files as sources to make sure they are built before the library. # Use an absolute path to prevent target_link_libraries prepending -l
set(sources) # to it.
foreach (pcm ${pcms}) set(pcms ${pcms} ${CMAKE_CURRENT_BINARY_DIR}/${pcm})
get_filename_component(pcm_we ${pcm} NAME_WE) add_custom_command(
set(obj ${pcm_we}.o) OUTPUT ${pcm}
# Use an absolute path to prevent target_link_libraries prepending -l. COMMAND ${CMAKE_CXX_COMPILER}
set(sources ${sources} ${pcm} ${CMAKE_CURRENT_BINARY_DIR}/${obj}) -std=c++${std} -x c++-module --precompile -c
add_custom_command( -o ${pcm} ${CMAKE_CURRENT_SOURCE_DIR}/${src}
OUTPUT ${obj} "-I$<JOIN:$<TARGET_PROPERTY:${name},INCLUDE_DIRECTORIES>,;-I>"
COMMAND ${CMAKE_CXX_COMPILER} $<TARGET_PROPERTY:${name},COMPILE_OPTIONS> # Required by the -I generator expression above.
-c -o ${obj} ${pcm} COMMAND_EXPAND_LISTS
DEPENDS ${pcm}) DEPENDS ${src})
endforeach () endforeach ()
endif ()
target_sources(${name} PRIVATE ${sources}) # Add .pcm files as sources to make sure they are built before the library.
set(sources)
foreach (pcm ${pcms})
get_filename_component(pcm_we ${pcm} NAME_WE)
set(obj ${pcm_we}.o)
# Use an absolute path to prevent target_link_libraries prepending -l.
set(sources ${sources} ${pcm} ${CMAKE_CURRENT_BINARY_DIR}/${obj})
add_custom_command(
OUTPUT ${obj}
COMMAND ${CMAKE_CXX_COMPILER} $<TARGET_PROPERTY:${name},COMPILE_OPTIONS>
-c -o ${obj} ${pcm}
DEPENDS ${pcm})
endforeach ()
endif ()
target_sources(${name} PRIVATE ${sources})
endif()
endfunction() endfunction()
include(CMakeParseArguments) include(CMakeParseArguments)
@ -145,21 +159,33 @@ option(FMT_WERROR "Halt the compilation with an error on compiler warnings."
OFF) OFF)
# Options that control generation of various targets. # Options that control generation of various targets.
option(FMT_DOC "Generate the doc target." ${FMT_MASTER_PROJECT})
option(FMT_INSTALL "Generate the install target." ON) option(FMT_INSTALL "Generate the install target." ON)
option(FMT_TEST "Generate the test target." ${FMT_MASTER_PROJECT})
option(FMT_FUZZ "Generate the fuzz target." OFF) option(FMT_FUZZ "Generate the fuzz target." OFF)
option(FMT_OS "Include core requiring OS (Windows/Posix) " ON) option(FMT_CUDA_TEST "Generate the cuda-test target." OFF)
option(FMT_OS "Include OS-specific APIs." ON)
option(FMT_MODULE "Build a module instead of a traditional library." OFF) option(FMT_MODULE "Build a module instead of a traditional library." OFF)
option(FMT_SYSTEM_HEADERS "Expose headers with marking them as system." OFF) option(FMT_SYSTEM_HEADERS "Expose headers with marking them as system." OFF)
option(FMT_UNICODE "Enable Unicode support." ON)
if (FMT_TEST AND FMT_MODULE)
# The tests require {fmt} to be compiled as traditional library
message(STATUS "Testing is incompatible with build mode 'module'.")
endif ()
set(FMT_SYSTEM_HEADERS_ATTRIBUTE "") set(FMT_SYSTEM_HEADERS_ATTRIBUTE "")
if (FMT_SYSTEM_HEADERS) if (FMT_SYSTEM_HEADERS)
set(FMT_SYSTEM_HEADERS_ATTRIBUTE SYSTEM) set(FMT_SYSTEM_HEADERS_ATTRIBUTE SYSTEM)
endif () endif ()
if (CMAKE_SYSTEM_NAME STREQUAL "MSDOS")
set(FMT_TEST OFF)
message(STATUS "MSDOS is incompatible with gtest")
endif ()
# Get version from core.h # Get version from base.h
file(READ include/fmt/core.h core_h) file(READ include/fmt/base.h base_h)
if (NOT core_h MATCHES "FMT_VERSION ([0-9]+)([0-9][0-9])([0-9][0-9])") if (NOT base_h MATCHES "FMT_VERSION ([0-9]+)([0-9][0-9])([0-9][0-9])")
message(FATAL_ERROR "Cannot get FMT_VERSION from core.h.") message(FATAL_ERROR "Cannot get FMT_VERSION from base.h.")
endif () endif ()
# Use math to skip leading zeros if any. # Use math to skip leading zeros if any.
math(EXPR CPACK_PACKAGE_VERSION_MAJOR ${CMAKE_MATCH_1}) math(EXPR CPACK_PACKAGE_VERSION_MAJOR ${CMAKE_MATCH_1})
@ -167,7 +193,7 @@ math(EXPR CPACK_PACKAGE_VERSION_MINOR ${CMAKE_MATCH_2})
math(EXPR CPACK_PACKAGE_VERSION_PATCH ${CMAKE_MATCH_3}) math(EXPR CPACK_PACKAGE_VERSION_PATCH ${CMAKE_MATCH_3})
join(FMT_VERSION ${CPACK_PACKAGE_VERSION_MAJOR}.${CPACK_PACKAGE_VERSION_MINOR}. join(FMT_VERSION ${CPACK_PACKAGE_VERSION_MAJOR}.${CPACK_PACKAGE_VERSION_MINOR}.
${CPACK_PACKAGE_VERSION_PATCH}) ${CPACK_PACKAGE_VERSION_PATCH})
message(STATUS "Version: ${FMT_VERSION}") message(STATUS "{fmt} version: ${FMT_VERSION}")
message(STATUS "Build type: ${CMAKE_BUILD_TYPE}") message(STATUS "Build type: ${CMAKE_BUILD_TYPE}")
@ -214,7 +240,13 @@ if (CMAKE_CXX_COMPILER_ID MATCHES "GNU")
endif () endif ()
if (NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 6.0) if (NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 6.0)
set(PEDANTIC_COMPILE_FLAGS ${PEDANTIC_COMPILE_FLAGS} -Wshift-overflow=2 set(PEDANTIC_COMPILE_FLAGS ${PEDANTIC_COMPILE_FLAGS} -Wshift-overflow=2
-Wnull-dereference -Wduplicated-cond) -Wduplicated-cond)
# Workaround for GCC regression
# [12/13/14/15 regression] New (since gcc 12) false positive null-dereference in vector.resize
# https://gcc.gnu.org/bugzilla/show_bug.cgi?id=108860
if (CMAKE_CXX_COMPILER_VERSION VERSION_LESS 12.0)
set(PEDANTIC_COMPILE_FLAGS ${PEDANTIC_COMPILE_FLAGS} -Wnull-dereference)
endif ()
endif () endif ()
set(WERROR_FLAG -Werror) set(WERROR_FLAG -Werror)
endif () endif ()
@ -263,13 +295,10 @@ function(add_headers VAR)
endfunction() endfunction()
# Define the fmt library, its includes and the needed defines. # Define the fmt library, its includes and the needed defines.
add_headers(FMT_HEADERS args.h chrono.h color.h compile.h core.h format.h add_headers(FMT_HEADERS args.h base.h chrono.h color.h compile.h core.h format.h
format-inl.h os.h ostream.h printf.h ranges.h std.h format-inl.h os.h ostream.h printf.h ranges.h std.h
xchar.h) xchar.h)
set(FMT_SOURCES src/format.cc) set(FMT_SOURCES src/format.cc)
if (FMT_OS)
set(FMT_SOURCES ${FMT_SOURCES} src/os.cc)
endif ()
add_module_library(fmt src/fmt.cc FALLBACK add_module_library(fmt src/fmt.cc FALLBACK
${FMT_SOURCES} ${FMT_HEADERS} README.md ChangeLog.md ${FMT_SOURCES} ${FMT_HEADERS} README.md ChangeLog.md
@ -277,6 +306,10 @@ add_module_library(fmt src/fmt.cc FALLBACK
add_library(fmt::fmt ALIAS fmt) add_library(fmt::fmt ALIAS fmt)
if (FMT_MODULE) if (FMT_MODULE)
enable_module(fmt) enable_module(fmt)
elseif (FMT_OS)
target_sources(fmt PRIVATE src/os.cc)
else()
target_compile_definitions(fmt PRIVATE FMT_OS=0)
endif () endif ()
if (FMT_WERROR) if (FMT_WERROR)
@ -292,7 +325,7 @@ else ()
message(WARNING "Feature cxx_std_11 is unknown for the CXX compiler") message(WARNING "Feature cxx_std_11 is unknown for the CXX compiler")
endif () endif ()
target_include_directories(fmt ${FMT_SYSTEM_HEADERS_ATTRIBUTE} PUBLIC target_include_directories(fmt ${FMT_SYSTEM_HEADERS_ATTRIBUTE} BEFORE PUBLIC
$<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/include> $<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/include>
$<INSTALL_INTERFACE:${FMT_INC_DIR}>) $<INSTALL_INTERFACE:${FMT_INC_DIR}>)
@ -328,11 +361,21 @@ endif ()
add_library(fmt-header-only INTERFACE) add_library(fmt-header-only INTERFACE)
add_library(fmt::fmt-header-only ALIAS fmt-header-only) add_library(fmt::fmt-header-only ALIAS fmt-header-only)
if (NOT MSVC)
# Unicode is always supported on compilers other than MSVC.
elseif (FMT_UNICODE)
# Unicode support requires compiling with /utf-8.
target_compile_options(fmt PUBLIC $<$<AND:$<COMPILE_LANGUAGE:CXX>,$<CXX_COMPILER_ID:MSVC>>:/utf-8>)
target_compile_options(fmt-header-only INTERFACE $<$<AND:$<COMPILE_LANGUAGE:CXX>,$<CXX_COMPILER_ID:MSVC>>:/utf-8>)
else ()
target_compile_definitions(fmt PUBLIC FMT_UNICODE=0)
endif ()
target_compile_definitions(fmt-header-only INTERFACE FMT_HEADER_ONLY=1) target_compile_definitions(fmt-header-only INTERFACE FMT_HEADER_ONLY=1)
target_compile_features(fmt-header-only INTERFACE cxx_std_11) target_compile_features(fmt-header-only INTERFACE cxx_std_11)
target_include_directories(fmt-header-only target_include_directories(fmt-header-only
${FMT_SYSTEM_HEADERS_ATTRIBUTE} INTERFACE ${FMT_SYSTEM_HEADERS_ATTRIBUTE} BEFORE INTERFACE
$<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/include> $<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/include>
$<INSTALL_INTERFACE:${FMT_INC_DIR}>) $<INSTALL_INTERFACE:${FMT_INC_DIR}>)
@ -377,12 +420,20 @@ if (FMT_INSTALL)
set(INSTALL_TARGETS fmt fmt-header-only) set(INSTALL_TARGETS fmt fmt-header-only)
set(INSTALL_FILE_SET)
if (FMT_USE_CMAKE_MODULES)
set(INSTALL_FILE_SET FILE_SET fmt DESTINATION "${FMT_INC_DIR}/fmt")
endif()
# Install the library and headers. # Install the library and headers.
install(TARGETS ${INSTALL_TARGETS} EXPORT ${targets_export_name} install(TARGETS ${INSTALL_TARGETS}
COMPONENT fmt-core
EXPORT ${targets_export_name}
LIBRARY DESTINATION ${FMT_LIB_DIR} LIBRARY DESTINATION ${FMT_LIB_DIR}
ARCHIVE DESTINATION ${FMT_LIB_DIR} ARCHIVE DESTINATION ${FMT_LIB_DIR}
PUBLIC_HEADER DESTINATION "${FMT_INC_DIR}/fmt" PUBLIC_HEADER DESTINATION "${FMT_INC_DIR}/fmt"
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}) RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
${INSTALL_FILE_SET})
# Use a namespace because CMake provides better diagnostics for namespaced # Use a namespace because CMake provides better diagnostics for namespaced
# imported targets. # imported targets.
@ -390,13 +441,61 @@ if (FMT_INSTALL)
FILE ${PROJECT_BINARY_DIR}/${targets_export_name}.cmake) FILE ${PROJECT_BINARY_DIR}/${targets_export_name}.cmake)
# Install version, config and target files. # Install version, config and target files.
install( install(FILES ${project_config} ${version_config}
FILES ${project_config} ${version_config} DESTINATION ${FMT_CMAKE_DIR}
DESTINATION ${FMT_CMAKE_DIR}) COMPONENT fmt-core)
install(EXPORT ${targets_export_name} DESTINATION ${FMT_CMAKE_DIR} install(EXPORT ${targets_export_name} DESTINATION ${FMT_CMAKE_DIR}
NAMESPACE fmt::) NAMESPACE fmt::
COMPONENT fmt-core)
install(FILES "${pkgconfig}" DESTINATION "${FMT_PKGCONFIG_DIR}") install(FILES "${pkgconfig}" DESTINATION "${FMT_PKGCONFIG_DIR}"
COMPONENT fmt-core)
endif ()
function(add_doc_target)
find_program(DOXYGEN doxygen
PATHS "$ENV{ProgramFiles}/doxygen/bin"
"$ENV{ProgramFiles\(x86\)}/doxygen/bin")
if (NOT DOXYGEN)
message(STATUS "Target 'doc' disabled because doxygen not found")
return ()
endif ()
find_program(MKDOCS mkdocs)
if (NOT MKDOCS)
message(STATUS "Target 'doc' disabled because mkdocs not found")
return ()
endif ()
set(sources )
foreach (source api.md index.md syntax.md get-started.md fmt.css fmt.js)
set(sources ${sources} doc/${source})
endforeach()
add_custom_target(
doc
COMMAND
${CMAKE_COMMAND}
-E env PYTHONPATH=${CMAKE_CURRENT_SOURCE_DIR}/support/python
${MKDOCS} build -f ${CMAKE_CURRENT_SOURCE_DIR}/support/mkdocs.yml
# MkDocs requires the site dir to be outside of the doc dir.
--site-dir ${CMAKE_CURRENT_BINARY_DIR}/doc-html
--no-directory-urls
SOURCES ${sources})
include(GNUInstallDirs)
install(DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/doc-html/
DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/doc/fmt
COMPONENT fmt-doc OPTIONAL)
endfunction()
if (FMT_DOC)
add_doc_target()
endif ()
if (FMT_TEST)
enable_testing()
add_subdirectory(test)
endif () endif ()
# Control fuzzing independent of the unit tests. # Control fuzzing independent of the unit tests.
@ -421,8 +520,7 @@ if (FMT_MASTER_PROJECT AND EXISTS ${gitignore})
string(REPLACE "*" ".*" line "${line}") string(REPLACE "*" ".*" line "${line}")
set(ignored_files ${ignored_files} "${line}$" "${line}/") set(ignored_files ${ignored_files} "${line}$" "${line}/")
endforeach () endforeach ()
set(ignored_files ${ignored_files} set(ignored_files ${ignored_files} /.git /build/doxyxml .vagrant)
/.git /breathe /format-benchmark sphinx/ .buildinfo .doctrees)
set(CPACK_SOURCE_GENERATOR ZIP) set(CPACK_SOURCE_GENERATOR ZIP)
set(CPACK_SOURCE_IGNORE_FILES ${ignored_files}) set(CPACK_SOURCE_IGNORE_FILES ${ignored_files})

File diff suppressed because it is too large Load Diff

View File

@ -20,16 +20,16 @@ that help victims of the war in Ukraine: <https://www.stopputin.net/>.
Q&A: ask questions on [StackOverflow with the tag Q&A: ask questions on [StackOverflow with the tag
fmt](https://stackoverflow.com/questions/tagged/fmt). fmt](https://stackoverflow.com/questions/tagged/fmt).
Try {fmt} in [Compiler Explorer](https://godbolt.org/z/Eq5763). Try {fmt} in [Compiler Explorer](https://godbolt.org/z/8Mx1EW73v).
# Features # Features
- Simple [format API](https://fmt.dev/latest/api.html) with positional - Simple [format API](https://fmt.dev/latest/api/) with positional
arguments for localization arguments for localization
- Implementation of [C++20 - Implementation of [C++20
std::format](https://en.cppreference.com/w/cpp/utility/format) and std::format](https://en.cppreference.com/w/cpp/utility/format) and
[C++23 std::print](https://en.cppreference.com/w/cpp/io/print) [C++23 std::print](https://en.cppreference.com/w/cpp/io/print)
- [Format string syntax](https://fmt.dev/latest/syntax.html) similar - [Format string syntax](https://fmt.dev/latest/syntax/) similar
to Python\'s to Python\'s
[format](https://docs.python.org/3/library/stdtypes.html#str.format) [format](https://docs.python.org/3/library/stdtypes.html#str.format)
- Fast IEEE 754 floating-point formatter with correct rounding, - Fast IEEE 754 floating-point formatter with correct rounding,
@ -37,10 +37,10 @@ Try {fmt} in [Compiler Explorer](https://godbolt.org/z/Eq5763).
[Dragonbox](https://github.com/jk-jeon/dragonbox) algorithm [Dragonbox](https://github.com/jk-jeon/dragonbox) algorithm
- Portable Unicode support - Portable Unicode support
- Safe [printf - Safe [printf
implementation](https://fmt.dev/latest/api.html#printf-formatting) implementation](https://fmt.dev/latest/api/#printf-formatting)
including the POSIX extension for positional arguments including the POSIX extension for positional arguments
- Extensibility: [support for user-defined - Extensibility: [support for user-defined
types](https://fmt.dev/latest/api.html#formatting-user-defined-types) types](https://fmt.dev/latest/api/#formatting-user-defined-types)
- High performance: faster than common standard library - High performance: faster than common standard library
implementations of `(s)printf`, iostreams, `to_string` and implementations of `(s)printf`, iostreams, `to_string` and
`to_chars`, see [Speed tests](#speed-tests) and [Converting a `to_chars`, see [Speed tests](#speed-tests) and [Converting a
@ -58,8 +58,8 @@ Try {fmt} in [Compiler Explorer](https://godbolt.org/z/Eq5763).
buffer overflow errors buffer overflow errors
- Ease of use: small self-contained code base, no external - Ease of use: small self-contained code base, no external
dependencies, permissive MIT dependencies, permissive MIT
[license](https://github.com/fmtlib/fmt/blob/master/LICENSE.rst) [license](https://github.com/fmtlib/fmt/blob/master/LICENSE)
- [Portability](https://fmt.dev/latest/index.html#portability) with - [Portability](https://fmt.dev/latest/#portability) with
consistent output across platforms and support for older compilers consistent output across platforms and support for older compilers
- Clean warning-free codebase even on high warning levels such as - Clean warning-free codebase even on high warning levels such as
`-Wall -Wextra -pedantic` `-Wall -Wextra -pedantic`
@ -203,43 +203,38 @@ and [ryu](https://github.com/ulfjack/ryu):
## Compile time and code bloat ## Compile time and code bloat
The script The script [bloat-test.py][test] from [format-benchmark][bench] tests compile
[bloat-test.py](https://github.com/fmtlib/format-benchmark/blob/master/bloat-test.py) time and code bloat for nontrivial projects. It generates 100 translation units
from [format-benchmark](https://github.com/fmtlib/format-benchmark) and uses `printf()` or its alternative five times in each to simulate a
tests compile time and code bloat for nontrivial projects. It generates medium-sized project. The resulting executable size and compile time (Apple
100 translation units and uses `printf()` or its alternative five times clang version 15.0.0 (clang-1500.1.0.2.5), macOS Sonoma, best of three) is shown
in each to simulate a medium-sized project. The resulting executable in the following tables.
size and compile time (Apple LLVM version 8.1.0 (clang-802.0.42), macOS
Sierra, best of three) is shown in the following tables. [test]: https://github.com/fmtlib/format-benchmark/blob/master/bloat-test.py
[bench]: https://github.com/fmtlib/format-benchmark
**Optimized build (-O3)** **Optimized build (-O3)**
| Method | Compile Time, s | Executable size, KiB | Stripped size, KiB | | Method | Compile Time, s | Executable size, KiB | Stripped size, KiB |
|---------------|-----------------|----------------------|--------------------| |---------------|-----------------|----------------------|--------------------|
| printf | 2.6 | 29 | 26 | | printf | 1.6 | 54 | 50 |
| printf+string | 16.4 | 29 | 26 | | IOStreams | 25.9 | 98 | 84 |
| iostreams | 31.1 | 59 | 55 | | fmt 83652df | 4.8 | 54 | 50 |
| {fmt} | 19.0 | 37 | 34 | | tinyformat | 29.1 | 161 | 136 |
| Boost Format | 91.9 | 226 | 203 | | Boost Format | 55.0 | 530 | 317 |
| Folly Format | 115.7 | 101 | 88 |
As you can see, {fmt} has 60% less overhead in terms of resulting binary {fmt} is fast to compile and is comparable to `printf` in terms of per-call
code size compared to iostreams and comes pretty close to `printf`. binary size (within a rounding error on this system).
Boost Format and Folly Format have the largest overheads.
`printf+string` is the same as `printf` but with an extra `<string>`
include to measure the overhead of the latter.
**Non-optimized build** **Non-optimized build**
| Method | Compile Time, s | Executable size, KiB | Stripped size, KiB | | Method | Compile Time, s | Executable size, KiB | Stripped size, KiB |
|---------------|-----------------|----------------------|--------------------| |---------------|-----------------|----------------------|--------------------|
| printf | 2.2 | 33 | 30 | | printf | 1.4 | 54 | 50 |
| printf+string | 16.0 | 33 | 30 | | IOStreams | 23.4 | 92 | 68 |
| iostreams | 28.3 | 56 | 52 | | {fmt} 83652df | 4.4 | 89 | 85 |
| {fmt} | 18.2 | 59 | 50 | | tinyformat | 24.5 | 204 | 161 |
| Boost Format | 54.1 | 365 | 303 | | Boost Format | 36.4 | 831 | 462 |
| Folly Format | 79.9 | 445 | 430 |
`libc`, `lib(std)c++`, and `libfmt` are all linked as shared libraries `libc`, `lib(std)c++`, and `libfmt` are all linked as shared libraries
to compare formatting function overhead only. Boost Format is a to compare formatting function overhead only. Boost Format is a
@ -248,7 +243,7 @@ header-only library so it doesn\'t provide any linkage options.
## Running the tests ## Running the tests
Please refer to [Building the Please refer to [Building the
library](https://fmt.dev/latest/usage.html#building-the-library) for library](https://fmt.dev/latest/get-started/#building-from-source) for
instructions on how to build the library and run the unit tests. instructions on how to build the library and run the unit tests.
Benchmarks reside in a separate repository, Benchmarks reside in a separate repository,
@ -270,8 +265,7 @@ or the bloat test:
# Migrating code # Migrating code
[clang-tidy](https://clang.llvm.org/extra/clang-tidy/) v17 (not yet [clang-tidy](https://clang.llvm.org/extra/clang-tidy/) v18 provides the
released) provides the
[modernize-use-std-print](https://clang.llvm.org/extra/clang-tidy/checks/modernize/use-std-print.html) [modernize-use-std-print](https://clang.llvm.org/extra/clang-tidy/checks/modernize/use-std-print.html)
check that is capable of converting occurrences of `printf` and check that is capable of converting occurrences of `printf` and
`fprintf` to `fmt::print` if configured to do so. (By default it `fprintf` to `fmt::print` if configured to do so. (By default it
@ -297,13 +291,14 @@ converts to `std::print`.)
- [ccache](https://ccache.dev/): a compiler cache - [ccache](https://ccache.dev/): a compiler cache
- [ClickHouse](https://github.com/ClickHouse/ClickHouse): an - [ClickHouse](https://github.com/ClickHouse/ClickHouse): an
analytical database management system analytical database management system
- [ContextVision](https://www.contextvision.com/): medical imaging software
- [Contour](https://github.com/contour-terminal/contour/): a modern - [Contour](https://github.com/contour-terminal/contour/): a modern
terminal emulator terminal emulator
- [CUAUV](https://cuauv.org/): Cornell University\'s autonomous - [CUAUV](https://cuauv.org/): Cornell University\'s autonomous
underwater vehicle underwater vehicle
- [Drake](https://drake.mit.edu/): a planning, control, and analysis - [Drake](https://drake.mit.edu/): a planning, control, and analysis
toolbox for nonlinear dynamical systems (MIT) toolbox for nonlinear dynamical systems (MIT)
- [Envoy](https://lyft.github.io/envoy/): C++ L7 proxy and - [Envoy](https://github.com/envoyproxy/envoy): C++ L7 proxy and
communication bus (Lyft) communication bus (Lyft)
- [FiveM](https://fivem.net/): a modification framework for GTA V - [FiveM](https://fivem.net/): a modification framework for GTA V
- [fmtlog](https://github.com/MengRao/fmtlog): a performant - [fmtlog](https://github.com/MengRao/fmtlog): a performant
@ -343,7 +338,7 @@ converts to `std::print`.)
- [Quill](https://github.com/odygrd/quill): asynchronous low-latency - [Quill](https://github.com/odygrd/quill): asynchronous low-latency
logging library logging library
- [QKW](https://github.com/ravijanjam/qkw): generalizing aliasing to - [QKW](https://github.com/ravijanjam/qkw): generalizing aliasing to
simplify navigation, and executing complex multi-line terminal simplify navigation, and execute complex multi-line terminal
command sequences command sequences
- [redis-cerberus](https://github.com/HunanTV/redis-cerberus): a Redis - [redis-cerberus](https://github.com/HunanTV/redis-cerberus): a Redis
cluster proxy cluster proxy
@ -432,7 +427,7 @@ code bloat issues (see [Benchmarks](#benchmarks)).
## FastFormat ## FastFormat
This is an interesting library that is fast, safe, and has positional This is an interesting library that is fast, safe and has positional
arguments. However, it has significant limitations, citing its author: arguments. However, it has significant limitations, citing its author:
> Three features that have no hope of being accommodated within the > Three features that have no hope of being accommodated within the
@ -442,8 +437,8 @@ arguments. However, it has significant limitations, citing its author:
> - Octal/hexadecimal encoding > - Octal/hexadecimal encoding
> - Runtime width/alignment specification > - Runtime width/alignment specification
It is also quite big and has a heavy dependency, STLSoft, which might be It is also quite big and has a heavy dependency, on STLSoft, which might be
too restrictive for using it in some projects. too restrictive for use in some projects.
## Boost Spirit.Karma ## Boost Spirit.Karma
@ -462,7 +457,7 @@ second](http://www.zverovich.net/2020/06/13/fast-int-to-string-revisited.html).
# Documentation License # Documentation License
The [Format String Syntax](https://fmt.dev/latest/syntax.html) section The [Format String Syntax](https://fmt.dev/latest/syntax/) section
in the documentation is based on the one from Python [string module in the documentation is based on the one from Python [string module
documentation](https://docs.python.org/3/library/string.html#module-string). documentation](https://docs.python.org/3/library/string.html#module-string).
For this reason, the documentation is distributed under the Python For this reason, the documentation is distributed under the Python
@ -486,5 +481,5 @@ To report a security issue, please disclose it at [security
advisory](https://github.com/fmtlib/fmt/security/advisories/new). advisory](https://github.com/fmtlib/fmt/security/advisories/new).
This project is maintained by a team of volunteers on a This project is maintained by a team of volunteers on a
reasonable-effort basis. As such, please give us at least 90 days to reasonable-effort basis. As such, please give us at least *90* days to
work on a fix before public exposure. work on a fix before public exposure.

View File

@ -8,14 +8,15 @@
#ifndef FMT_ARGS_H_ #ifndef FMT_ARGS_H_
#define FMT_ARGS_H_ #define FMT_ARGS_H_
#include <functional> // std::reference_wrapper #ifndef FMT_MODULE
#include <memory> // std::unique_ptr # include <functional> // std::reference_wrapper
#include <vector> # include <memory> // std::unique_ptr
# include <vector>
#endif
#include "core.h" #include "format.h" // std_string_view
FMT_BEGIN_NAMESPACE FMT_BEGIN_NAMESPACE
namespace detail { namespace detail {
template <typename T> struct is_reference_wrapper : std::false_type {}; template <typename T> struct is_reference_wrapper : std::false_type {};
@ -28,15 +29,18 @@ auto unwrap(const std::reference_wrapper<T>& v) -> const T& {
return static_cast<const T&>(v); return static_cast<const T&>(v);
} }
class dynamic_arg_list { // node is defined outside dynamic_arg_list to workaround a C2504 bug in MSVC
// Workaround for clang's -Wweak-vtables. Unlike for regular classes, for // 2022 (v17.10.0).
// templates it doesn't complain about inability to deduce single translation //
// unit for placing vtable. So storage_node_base is made a fake template. // Workaround for clang's -Wweak-vtables. Unlike for regular classes, for
template <typename = void> struct node { // templates it doesn't complain about inability to deduce single translation
virtual ~node() = default; // unit for placing vtable. So node is made a fake template.
std::unique_ptr<node<>> next; template <typename = void> struct node {
}; virtual ~node() = default;
std::unique_ptr<node<>> next;
};
class dynamic_arg_list {
template <typename T> struct typed_node : node<> { template <typename T> struct typed_node : node<> {
T value; T value;
@ -62,28 +66,18 @@ class dynamic_arg_list {
} // namespace detail } // namespace detail
/** /**
\rst * A dynamic list of formatting arguments with storage.
A dynamic version of `fmt::format_arg_store`. *
It's equipped with a storage to potentially temporary objects which lifetimes * It can be implicitly converted into `fmt::basic_format_args` for passing
could be shorter than the format arguments object. * into type-erased formatting functions such as `fmt::vformat`.
It can be implicitly converted into `~fmt::basic_format_args` for passing
into type-erased formatting functions such as `~fmt::vformat`.
\endrst
*/ */
template <typename Context> template <typename Context> class dynamic_format_arg_store {
class dynamic_format_arg_store
#if FMT_GCC_VERSION && FMT_GCC_VERSION < 409
// Workaround a GCC template argument substitution bug.
: public basic_format_args<Context>
#endif
{
private: private:
using char_type = typename Context::char_type; using char_type = typename Context::char_type;
template <typename T> struct need_copy { template <typename T> struct need_copy {
static constexpr detail::type mapped_type = static constexpr detail::type mapped_type =
detail::mapped_type_constant<T, Context>::value; detail::mapped_type_constant<T, char_type>::value;
enum { enum {
value = !(detail::is_reference_wrapper<T>::value || value = !(detail::is_reference_wrapper<T>::value ||
@ -96,7 +90,7 @@ class dynamic_format_arg_store
}; };
template <typename T> template <typename T>
using stored_type = conditional_t< using stored_t = conditional_t<
std::is_convertible<T, std::basic_string<char_type>>::value && std::is_convertible<T, std::basic_string<char_type>>::value &&
!detail::is_reference_wrapper<T>::value, !detail::is_reference_wrapper<T>::value,
std::basic_string<char_type>, T>; std::basic_string<char_type>, T>;
@ -111,80 +105,72 @@ class dynamic_format_arg_store
friend class basic_format_args<Context>; friend class basic_format_args<Context>;
auto get_types() const -> unsigned long long {
return detail::is_unpacked_bit | data_.size() |
(named_info_.empty()
? 0ULL
: static_cast<unsigned long long>(detail::has_named_args_bit));
}
auto data() const -> const basic_format_arg<Context>* { auto data() const -> const basic_format_arg<Context>* {
return named_info_.empty() ? data_.data() : data_.data() + 1; return named_info_.empty() ? data_.data() : data_.data() + 1;
} }
template <typename T> void emplace_arg(const T& arg) { template <typename T> void emplace_arg(const T& arg) {
data_.emplace_back(detail::make_arg<Context>(arg)); data_.emplace_back(arg);
} }
template <typename T> template <typename T>
void emplace_arg(const detail::named_arg<char_type, T>& arg) { void emplace_arg(const detail::named_arg<char_type, T>& arg) {
if (named_info_.empty()) { if (named_info_.empty())
constexpr const detail::named_arg_info<char_type>* zero_ptr{nullptr}; data_.insert(data_.begin(), basic_format_arg<Context>(nullptr, 0));
data_.insert(data_.begin(), {zero_ptr, 0}); data_.emplace_back(detail::unwrap(arg.value));
}
data_.emplace_back(detail::make_arg<Context>(detail::unwrap(arg.value)));
auto pop_one = [](std::vector<basic_format_arg<Context>>* data) { auto pop_one = [](std::vector<basic_format_arg<Context>>* data) {
data->pop_back(); data->pop_back();
}; };
std::unique_ptr<std::vector<basic_format_arg<Context>>, decltype(pop_one)> std::unique_ptr<std::vector<basic_format_arg<Context>>, decltype(pop_one)>
guard{&data_, pop_one}; guard{&data_, pop_one};
named_info_.push_back({arg.name, static_cast<int>(data_.size() - 2u)}); named_info_.push_back({arg.name, static_cast<int>(data_.size() - 2u)});
data_[0].value_.named_args = {named_info_.data(), named_info_.size()}; data_[0] = {named_info_.data(), named_info_.size()};
guard.release(); guard.release();
} }
public: public:
constexpr dynamic_format_arg_store() = default; constexpr dynamic_format_arg_store() = default;
operator basic_format_args<Context>() const {
return basic_format_args<Context>(data(), static_cast<int>(data_.size()),
!named_info_.empty());
}
/** /**
\rst * Adds an argument into the dynamic store for later passing to a formatting
Adds an argument into the dynamic store for later passing to a formatting * function.
function. *
* Note that custom types and string types (but not string views) are copied
Note that custom types and string types (but not string views) are copied * into the store dynamically allocating memory if necessary.
into the store dynamically allocating memory if necessary. *
* **Example**:
**Example**:: *
* fmt::dynamic_format_arg_store<fmt::format_context> store;
fmt::dynamic_format_arg_store<fmt::format_context> store; * store.push_back(42);
store.push_back(42); * store.push_back("abc");
store.push_back("abc"); * store.push_back(1.5f);
store.push_back(1.5f); * std::string result = fmt::vformat("{} and {} and {}", store);
std::string result = fmt::vformat("{} and {} and {}", store); */
\endrst
*/
template <typename T> void push_back(const T& arg) { template <typename T> void push_back(const T& arg) {
if (detail::const_check(need_copy<T>::value)) if (detail::const_check(need_copy<T>::value))
emplace_arg(dynamic_args_.push<stored_type<T>>(arg)); emplace_arg(dynamic_args_.push<stored_t<T>>(arg));
else else
emplace_arg(detail::unwrap(arg)); emplace_arg(detail::unwrap(arg));
} }
/** /**
\rst * Adds a reference to the argument into the dynamic store for later passing
Adds a reference to the argument into the dynamic store for later passing to * to a formatting function.
a formatting function. *
* **Example**:
**Example**:: *
* fmt::dynamic_format_arg_store<fmt::format_context> store;
fmt::dynamic_format_arg_store<fmt::format_context> store; * char band[] = "Rolling Stones";
char band[] = "Rolling Stones"; * store.push_back(std::cref(band));
store.push_back(std::cref(band)); * band[9] = 'c'; // Changing str affects the output.
band[9] = 'c'; // Changing str affects the output. * std::string result = fmt::vformat("{}", store);
std::string result = fmt::vformat("{}", store); * // result == "Rolling Scones"
// result == "Rolling Scones" */
\endrst
*/
template <typename T> void push_back(std::reference_wrapper<T> arg) { template <typename T> void push_back(std::reference_wrapper<T> arg) {
static_assert( static_assert(
need_copy<T>::value, need_copy<T>::value,
@ -193,41 +179,40 @@ class dynamic_format_arg_store
} }
/** /**
Adds named argument into the dynamic store for later passing to a formatting * Adds named argument into the dynamic store for later passing to a
function. ``std::reference_wrapper`` is supported to avoid copying of the * formatting function. `std::reference_wrapper` is supported to avoid
argument. The name is always copied into the store. * copying of the argument. The name is always copied into the store.
*/ */
template <typename T> template <typename T>
void push_back(const detail::named_arg<char_type, T>& arg) { void push_back(const detail::named_arg<char_type, T>& arg) {
const char_type* arg_name = const char_type* arg_name =
dynamic_args_.push<std::basic_string<char_type>>(arg.name).c_str(); dynamic_args_.push<std::basic_string<char_type>>(arg.name).c_str();
if (detail::const_check(need_copy<T>::value)) { if (detail::const_check(need_copy<T>::value)) {
emplace_arg( emplace_arg(
fmt::arg(arg_name, dynamic_args_.push<stored_type<T>>(arg.value))); fmt::arg(arg_name, dynamic_args_.push<stored_t<T>>(arg.value)));
} else { } else {
emplace_arg(fmt::arg(arg_name, arg.value)); emplace_arg(fmt::arg(arg_name, arg.value));
} }
} }
/** Erase all elements from the store */ /// Erase all elements from the store.
void clear() { void clear() {
data_.clear(); data_.clear();
named_info_.clear(); named_info_.clear();
dynamic_args_ = detail::dynamic_arg_list(); dynamic_args_ = {};
} }
/** /// Reserves space to store at least `new_cap` arguments including
\rst /// `new_cap_named` named arguments.
Reserves space to store at least *new_cap* arguments including
*new_cap_named* named arguments.
\endrst
*/
void reserve(size_t new_cap, size_t new_cap_named) { void reserve(size_t new_cap, size_t new_cap_named) {
FMT_ASSERT(new_cap >= new_cap_named, FMT_ASSERT(new_cap >= new_cap_named,
"Set of arguments includes set of named arguments"); "set of arguments includes set of named arguments");
data_.reserve(new_cap); data_.reserve(new_cap);
named_info_.reserve(new_cap_named); named_info_.reserve(new_cap_named);
} }
/// Returns the number of elements in the store.
size_t size() const noexcept { return data_.size(); }
}; };
FMT_END_NAMESPACE FMT_END_NAMESPACE

2958
3rdparty/fmt/include/fmt/base.h vendored Normal file

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -227,7 +227,7 @@ struct color_type {
}; };
} // namespace detail } // namespace detail
/** A text style consisting of foreground and background colors and emphasis. */ /// A text style consisting of foreground and background colors and emphasis.
class text_style { class text_style {
public: public:
FMT_CONSTEXPR text_style(emphasis em = emphasis()) noexcept FMT_CONSTEXPR text_style(emphasis em = emphasis()) noexcept
@ -239,7 +239,7 @@ class text_style {
foreground_color = rhs.foreground_color; foreground_color = rhs.foreground_color;
} else if (rhs.set_foreground_color) { } else if (rhs.set_foreground_color) {
if (!foreground_color.is_rgb || !rhs.foreground_color.is_rgb) if (!foreground_color.is_rgb || !rhs.foreground_color.is_rgb)
FMT_THROW(format_error("can't OR a terminal color")); report_error("can't OR a terminal color");
foreground_color.value.rgb_color |= rhs.foreground_color.value.rgb_color; foreground_color.value.rgb_color |= rhs.foreground_color.value.rgb_color;
} }
@ -248,7 +248,7 @@ class text_style {
background_color = rhs.background_color; background_color = rhs.background_color;
} else if (rhs.set_background_color) { } else if (rhs.set_background_color) {
if (!background_color.is_rgb || !rhs.background_color.is_rgb) if (!background_color.is_rgb || !rhs.background_color.is_rgb)
FMT_THROW(format_error("can't OR a terminal color")); report_error("can't OR a terminal color");
background_color.value.rgb_color |= rhs.background_color.value.rgb_color; background_color.value.rgb_color |= rhs.background_color.value.rgb_color;
} }
@ -310,13 +310,13 @@ class text_style {
emphasis ems; emphasis ems;
}; };
/** Creates a text style from the foreground (text) color. */ /// Creates a text style from the foreground (text) color.
FMT_CONSTEXPR inline auto fg(detail::color_type foreground) noexcept FMT_CONSTEXPR inline auto fg(detail::color_type foreground) noexcept
-> text_style { -> text_style {
return text_style(true, foreground); return text_style(true, foreground);
} }
/** Creates a text style from the background color. */ /// Creates a text style from the background color.
FMT_CONSTEXPR inline auto bg(detail::color_type background) noexcept FMT_CONSTEXPR inline auto bg(detail::color_type background) noexcept
-> text_style { -> text_style {
return text_style(false, background); return text_style(false, background);
@ -330,7 +330,7 @@ FMT_CONSTEXPR inline auto operator|(emphasis lhs, emphasis rhs) noexcept
namespace detail { namespace detail {
template <typename Char> struct ansi_color_escape { template <typename Char> struct ansi_color_escape {
FMT_CONSTEXPR ansi_color_escape(detail::color_type text_color, FMT_CONSTEXPR ansi_color_escape(color_type text_color,
const char* esc) noexcept { const char* esc) noexcept {
// If we have a terminal color, we need to output another escape code // If we have a terminal color, we need to output another escape code
// sequence. // sequence.
@ -390,8 +390,8 @@ template <typename Char> struct ansi_color_escape {
FMT_CONSTEXPR operator const Char*() const noexcept { return buffer; } FMT_CONSTEXPR operator const Char*() const noexcept { return buffer; }
FMT_CONSTEXPR auto begin() const noexcept -> const Char* { return buffer; } FMT_CONSTEXPR auto begin() const noexcept -> const Char* { return buffer; }
FMT_CONSTEXPR_CHAR_TRAITS auto end() const noexcept -> const Char* { FMT_CONSTEXPR20 auto end() const noexcept -> const Char* {
return buffer + std::char_traits<Char>::length(buffer); return buffer + basic_string_view<Char>(buffer).size();
} }
private: private:
@ -412,13 +412,13 @@ template <typename Char> struct ansi_color_escape {
}; };
template <typename Char> template <typename Char>
FMT_CONSTEXPR auto make_foreground_color(detail::color_type foreground) noexcept FMT_CONSTEXPR auto make_foreground_color(color_type foreground) noexcept
-> ansi_color_escape<Char> { -> ansi_color_escape<Char> {
return ansi_color_escape<Char>(foreground, "\x1b[38;2;"); return ansi_color_escape<Char>(foreground, "\x1b[38;2;");
} }
template <typename Char> template <typename Char>
FMT_CONSTEXPR auto make_background_color(detail::color_type background) noexcept FMT_CONSTEXPR auto make_background_color(color_type background) noexcept
-> ansi_color_escape<Char> { -> ansi_color_escape<Char> {
return ansi_color_escape<Char>(background, "\x1b[48;2;"); return ansi_color_escape<Char>(background, "\x1b[48;2;");
} }
@ -434,7 +434,7 @@ template <typename Char> inline void reset_color(buffer<Char>& buffer) {
buffer.append(reset_color.begin(), reset_color.end()); buffer.append(reset_color.begin(), reset_color.end());
} }
template <typename T> struct styled_arg : detail::view { template <typename T> struct styled_arg : view {
const T& value; const T& value;
text_style style; text_style style;
styled_arg(const T& v, text_style s) : value(v), style(s) {} styled_arg(const T& v, text_style s) : value(v), style(s) {}
@ -442,145 +442,115 @@ template <typename T> struct styled_arg : detail::view {
template <typename Char> template <typename Char>
void vformat_to(buffer<Char>& buf, const text_style& ts, void vformat_to(buffer<Char>& buf, const text_style& ts,
basic_string_view<Char> format_str, basic_string_view<Char> fmt,
basic_format_args<buffer_context<type_identity_t<Char>>> args) { basic_format_args<buffered_context<Char>> args) {
bool has_style = false; bool has_style = false;
if (ts.has_emphasis()) { if (ts.has_emphasis()) {
has_style = true; has_style = true;
auto emphasis = detail::make_emphasis<Char>(ts.get_emphasis()); auto emphasis = make_emphasis<Char>(ts.get_emphasis());
buf.append(emphasis.begin(), emphasis.end()); buf.append(emphasis.begin(), emphasis.end());
} }
if (ts.has_foreground()) { if (ts.has_foreground()) {
has_style = true; has_style = true;
auto foreground = detail::make_foreground_color<Char>(ts.get_foreground()); auto foreground = make_foreground_color<Char>(ts.get_foreground());
buf.append(foreground.begin(), foreground.end()); buf.append(foreground.begin(), foreground.end());
} }
if (ts.has_background()) { if (ts.has_background()) {
has_style = true; has_style = true;
auto background = detail::make_background_color<Char>(ts.get_background()); auto background = make_background_color<Char>(ts.get_background());
buf.append(background.begin(), background.end()); buf.append(background.begin(), background.end());
} }
detail::vformat_to(buf, format_str, args, {}); vformat_to(buf, fmt, args);
if (has_style) detail::reset_color<Char>(buf); if (has_style) reset_color<Char>(buf);
} }
} // namespace detail } // namespace detail
inline void vprint(std::FILE* f, const text_style& ts, string_view fmt, inline void vprint(FILE* f, const text_style& ts, string_view fmt,
format_args args) { format_args args) {
// Legacy wide streams are not supported.
auto buf = memory_buffer(); auto buf = memory_buffer();
detail::vformat_to(buf, ts, fmt, args); detail::vformat_to(buf, ts, fmt, args);
if (detail::is_utf8()) { print(f, FMT_STRING("{}"), string_view(buf.begin(), buf.size()));
detail::print(f, string_view(buf.begin(), buf.size()));
return;
}
buf.push_back('\0');
int result = std::fputs(buf.data(), f);
if (result < 0)
FMT_THROW(system_error(errno, FMT_STRING("cannot write to file")));
} }
/** /**
\rst * Formats a string and prints it to the specified file stream using ANSI
Formats a string and prints it to the specified file stream using ANSI * escape sequences to specify text formatting.
escape sequences to specify text formatting. *
* **Example**:
**Example**:: *
* fmt::print(fmt::emphasis::bold | fg(fmt::color::red),
fmt::print(fmt::emphasis::bold | fg(fmt::color::red), * "Elapsed time: {0:.2f} seconds", 1.23);
"Elapsed time: {0:.2f} seconds", 1.23);
\endrst
*/ */
template <typename S, typename... Args, template <typename... T>
FMT_ENABLE_IF(detail::is_string<S>::value)> void print(FILE* f, const text_style& ts, format_string<T...> fmt,
void print(std::FILE* f, const text_style& ts, const S& format_str, T&&... args) {
const Args&... args) { vprint(f, ts, fmt.str, vargs<T...>{{args...}});
vprint(f, ts, format_str,
fmt::make_format_args<buffer_context<char_t<S>>>(args...));
} }
/** /**
\rst * Formats a string and prints it to stdout using ANSI escape sequences to
Formats a string and prints it to stdout using ANSI escape sequences to * specify text formatting.
specify text formatting. *
* **Example**:
**Example**:: *
* fmt::print(fmt::emphasis::bold | fg(fmt::color::red),
fmt::print(fmt::emphasis::bold | fg(fmt::color::red), * "Elapsed time: {0:.2f} seconds", 1.23);
"Elapsed time: {0:.2f} seconds", 1.23);
\endrst
*/ */
template <typename S, typename... Args, template <typename... T>
FMT_ENABLE_IF(detail::is_string<S>::value)> void print(const text_style& ts, format_string<T...> fmt, T&&... args) {
void print(const text_style& ts, const S& format_str, const Args&... args) { return print(stdout, ts, fmt, std::forward<T>(args)...);
return print(stdout, ts, format_str, args...);
} }
template <typename S, typename Char = char_t<S>> inline auto vformat(const text_style& ts, string_view fmt, format_args args)
inline auto vformat( -> std::string {
const text_style& ts, const S& format_str, auto buf = memory_buffer();
basic_format_args<buffer_context<type_identity_t<Char>>> args) detail::vformat_to(buf, ts, fmt, args);
-> std::basic_string<Char> {
basic_memory_buffer<Char> buf;
detail::vformat_to(buf, ts, detail::to_string_view(format_str), args);
return fmt::to_string(buf); return fmt::to_string(buf);
} }
/** /**
\rst * Formats arguments and returns the result as a string using ANSI escape
Formats arguments and returns the result as a string using ANSI * sequences to specify text formatting.
escape sequences to specify text formatting. *
* **Example**:
**Example**:: *
* ```
#include <fmt/color.h> * #include <fmt/color.h>
std::string message = fmt::format(fmt::emphasis::bold | fg(fmt::color::red), * std::string message = fmt::format(fmt::emphasis::bold | fg(fmt::color::red),
"The answer is {}", 42); * "The answer is {}", 42);
\endrst * ```
*/ */
template <typename S, typename... Args, typename Char = char_t<S>> template <typename... T>
inline auto format(const text_style& ts, const S& format_str, inline auto format(const text_style& ts, format_string<T...> fmt, T&&... args)
const Args&... args) -> std::basic_string<Char> { -> std::string {
return fmt::vformat(ts, detail::to_string_view(format_str), return fmt::vformat(ts, fmt.str, vargs<T...>{{args...}});
fmt::make_format_args<buffer_context<Char>>(args...));
} }
/** /// Formats a string with the given text_style and writes the output to `out`.
Formats a string with the given text_style and writes the output to ``out``. template <typename OutputIt,
*/ FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, char>::value)>
template <typename OutputIt, typename Char, auto vformat_to(OutputIt out, const text_style& ts, string_view fmt,
FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, Char>::value)> format_args args) -> OutputIt {
auto vformat_to(OutputIt out, const text_style& ts, auto&& buf = detail::get_buffer<char>(out);
basic_string_view<Char> format_str, detail::vformat_to(buf, ts, fmt, args);
basic_format_args<buffer_context<type_identity_t<Char>>> args)
-> OutputIt {
auto&& buf = detail::get_buffer<Char>(out);
detail::vformat_to(buf, ts, format_str, args);
return detail::get_iterator(buf, out); return detail::get_iterator(buf, out);
} }
/** /**
\rst * Formats arguments with the given text style, writes the result to the output
Formats arguments with the given text_style, writes the result to the output * iterator `out` and returns the iterator past the end of the output range.
iterator ``out`` and returns the iterator past the end of the output range. *
* **Example**:
**Example**:: *
* std::vector<char> out;
std::vector<char> out; * fmt::format_to(std::back_inserter(out),
fmt::format_to(std::back_inserter(out), * fmt::emphasis::bold | fg(fmt::color::red), "{}", 42);
fmt::emphasis::bold | fg(fmt::color::red), "{}", 42); */
\endrst template <typename OutputIt, typename... T,
*/ FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, char>::value)>
template < inline auto format_to(OutputIt out, const text_style& ts,
typename OutputIt, typename S, typename... Args, format_string<T...> fmt, T&&... args) -> OutputIt {
bool enable = detail::is_output_iterator<OutputIt, char_t<S>>::value && return vformat_to(out, ts, fmt.str, vargs<T...>{{args...}});
detail::is_string<S>::value>
inline auto format_to(OutputIt out, const text_style& ts, const S& format_str,
Args&&... args) ->
typename std::enable_if<enable, OutputIt>::type {
return vformat_to(out, ts, detail::to_string_view(format_str),
fmt::make_format_args<buffer_context<char_t<S>>>(args...));
} }
template <typename T, typename Char> template <typename T, typename Char>
@ -589,47 +559,44 @@ struct formatter<detail::styled_arg<T>, Char> : formatter<T, Char> {
auto format(const detail::styled_arg<T>& arg, FormatContext& ctx) const auto format(const detail::styled_arg<T>& arg, FormatContext& ctx) const
-> decltype(ctx.out()) { -> decltype(ctx.out()) {
const auto& ts = arg.style; const auto& ts = arg.style;
const auto& value = arg.value;
auto out = ctx.out(); auto out = ctx.out();
bool has_style = false; bool has_style = false;
if (ts.has_emphasis()) { if (ts.has_emphasis()) {
has_style = true; has_style = true;
auto emphasis = detail::make_emphasis<Char>(ts.get_emphasis()); auto emphasis = detail::make_emphasis<Char>(ts.get_emphasis());
out = std::copy(emphasis.begin(), emphasis.end(), out); out = detail::copy<Char>(emphasis.begin(), emphasis.end(), out);
} }
if (ts.has_foreground()) { if (ts.has_foreground()) {
has_style = true; has_style = true;
auto foreground = auto foreground =
detail::make_foreground_color<Char>(ts.get_foreground()); detail::make_foreground_color<Char>(ts.get_foreground());
out = std::copy(foreground.begin(), foreground.end(), out); out = detail::copy<Char>(foreground.begin(), foreground.end(), out);
} }
if (ts.has_background()) { if (ts.has_background()) {
has_style = true; has_style = true;
auto background = auto background =
detail::make_background_color<Char>(ts.get_background()); detail::make_background_color<Char>(ts.get_background());
out = std::copy(background.begin(), background.end(), out); out = detail::copy<Char>(background.begin(), background.end(), out);
} }
out = formatter<T, Char>::format(value, ctx); out = formatter<T, Char>::format(arg.value, ctx);
if (has_style) { if (has_style) {
auto reset_color = string_view("\x1b[0m"); auto reset_color = string_view("\x1b[0m");
out = std::copy(reset_color.begin(), reset_color.end(), out); out = detail::copy<Char>(reset_color.begin(), reset_color.end(), out);
} }
return out; return out;
} }
}; };
/** /**
\rst * Returns an argument that will be formatted using ANSI escape sequences,
Returns an argument that will be formatted using ANSI escape sequences, * to be used in a formatting function.
to be used in a formatting function. *
* **Example**:
**Example**:: *
* fmt::print("Elapsed time: {0:.2f} seconds",
fmt::print("Elapsed time: {0:.2f} seconds", * fmt::styled(1.23, fmt::fg(fmt::color::green) |
fmt::styled(1.23, fmt::fg(fmt::color::green) | * fmt::bg(fmt::color::blue)));
fmt::bg(fmt::color::blue)));
\endrst
*/ */
template <typename T> template <typename T>
FMT_CONSTEXPR auto styled(const T& value, text_style ts) FMT_CONSTEXPR auto styled(const T& value, text_style ts)

View File

@ -8,49 +8,44 @@
#ifndef FMT_COMPILE_H_ #ifndef FMT_COMPILE_H_
#define FMT_COMPILE_H_ #define FMT_COMPILE_H_
#ifndef FMT_MODULE
# include <iterator> // std::back_inserter
#endif
#include "format.h" #include "format.h"
FMT_BEGIN_NAMESPACE FMT_BEGIN_NAMESPACE
namespace detail {
template <typename Char, typename InputIt>
FMT_CONSTEXPR inline auto copy_str(InputIt begin, InputIt end,
counting_iterator it) -> counting_iterator {
return it + (end - begin);
}
// A compile-time string which is compiled into fast formatting code. // A compile-time string which is compiled into fast formatting code.
class compiled_string {}; FMT_EXPORT class compiled_string {};
namespace detail {
template <typename S> template <typename S>
struct is_compiled_string : std::is_base_of<compiled_string, S> {}; struct is_compiled_string : std::is_base_of<compiled_string, S> {};
/** /**
\rst * Converts a string literal `s` into a format string that will be parsed at
Converts a string literal *s* into a format string that will be parsed at * compile time and converted into efficient formatting code. Requires C++17
compile time and converted into efficient formatting code. Requires C++17 * `constexpr if` compiler support.
``constexpr if`` compiler support. *
* **Example**:
**Example**:: *
* // Converts 42 into std::string using the most efficient method and no
// Converts 42 into std::string using the most efficient method and no * // runtime format string processing.
// runtime format string processing. * std::string s = fmt::format(FMT_COMPILE("{}"), 42);
std::string s = fmt::format(FMT_COMPILE("{}"), 42);
\endrst
*/ */
#if defined(__cpp_if_constexpr) && defined(__cpp_return_type_deduction) #if defined(__cpp_if_constexpr) && defined(__cpp_return_type_deduction)
# define FMT_COMPILE(s) \ # define FMT_COMPILE(s) FMT_STRING_IMPL(s, fmt::compiled_string)
FMT_STRING_IMPL(s, fmt::detail::compiled_string, explicit)
#else #else
# define FMT_COMPILE(s) FMT_STRING(s) # define FMT_COMPILE(s) FMT_STRING(s)
#endif #endif
#if FMT_USE_NONTYPE_TEMPLATE_ARGS #if FMT_USE_NONTYPE_TEMPLATE_ARGS
template <typename Char, size_t N, template <typename Char, size_t N, fmt::detail::fixed_string<Char, N> Str>
fmt::detail_exported::fixed_string<Char, N> Str>
struct udl_compiled_string : compiled_string { struct udl_compiled_string : compiled_string {
using char_type = Char; using char_type = Char;
explicit constexpr operator basic_string_view<char_type>() const { constexpr explicit operator basic_string_view<char_type>() const {
return {Str.data, N - 1}; return {Str.data, N - 1};
} }
}; };
@ -75,6 +70,29 @@ constexpr const auto& get([[maybe_unused]] const T& first,
return detail::get<N - 1>(rest...); return detail::get<N - 1>(rest...);
} }
# if FMT_USE_NONTYPE_TEMPLATE_ARGS
template <int N, typename T, typename... Args, typename Char>
constexpr auto get_arg_index_by_name(basic_string_view<Char> name) -> int {
if constexpr (is_static_named_arg<T>()) {
if (name == T::name) return N;
}
if constexpr (sizeof...(Args) > 0)
return get_arg_index_by_name<N + 1, Args...>(name);
(void)name; // Workaround an MSVC bug about "unused" parameter.
return -1;
}
# endif
template <typename... Args, typename Char>
FMT_CONSTEXPR auto get_arg_index_by_name(basic_string_view<Char> name) -> int {
# if FMT_USE_NONTYPE_TEMPLATE_ARGS
if constexpr (sizeof...(Args) > 0)
return get_arg_index_by_name<0, Args...>(name);
# endif
(void)name;
return -1;
}
template <typename Char, typename... Args> template <typename Char, typename... Args>
constexpr int get_arg_index_by_name(basic_string_view<Char> name, constexpr int get_arg_index_by_name(basic_string_view<Char> name,
type_list<Args...>) { type_list<Args...>) {
@ -144,11 +162,12 @@ template <typename Char, typename T, int N> struct field {
template <typename OutputIt, typename... Args> template <typename OutputIt, typename... Args>
constexpr OutputIt format(OutputIt out, const Args&... args) const { constexpr OutputIt format(OutputIt out, const Args&... args) const {
const T& arg = get_arg_checked<T, N>(args...); const T& arg = get_arg_checked<T, N>(args...);
if constexpr (std::is_convertible_v<T, basic_string_view<Char>>) { if constexpr (std::is_convertible<T, basic_string_view<Char>>::value) {
auto s = basic_string_view<Char>(arg); auto s = basic_string_view<Char>(arg);
return copy_str<Char>(s.begin(), s.end(), out); return copy<Char>(s.begin(), s.end(), out);
} else {
return write<Char>(out, arg);
} }
return write<Char>(out, arg);
} }
}; };
@ -236,13 +255,12 @@ constexpr size_t parse_text(basic_string_view<Char> str, size_t pos) {
} }
template <typename Args, size_t POS, int ID, typename S> template <typename Args, size_t POS, int ID, typename S>
constexpr auto compile_format_string(S format_str); constexpr auto compile_format_string(S fmt);
template <typename Args, size_t POS, int ID, typename T, typename S> template <typename Args, size_t POS, int ID, typename T, typename S>
constexpr auto parse_tail(T head, S format_str) { constexpr auto parse_tail(T head, S fmt) {
if constexpr (POS != if constexpr (POS != basic_string_view<typename S::char_type>(fmt).size()) {
basic_string_view<typename S::char_type>(format_str).size()) { constexpr auto tail = compile_format_string<Args, POS, ID>(fmt);
constexpr auto tail = compile_format_string<Args, POS, ID>(format_str);
if constexpr (std::is_same<remove_cvref_t<decltype(tail)>, if constexpr (std::is_same<remove_cvref_t<decltype(tail)>,
unknown_format>()) unknown_format>())
return tail; return tail;
@ -274,6 +292,7 @@ constexpr parse_specs_result<T, Char> parse_specs(basic_string_view<Char> str,
} }
template <typename Char> struct arg_id_handler { template <typename Char> struct arg_id_handler {
arg_id_kind kind;
arg_ref<Char> arg_id; arg_ref<Char> arg_id;
constexpr int on_auto() { constexpr int on_auto() {
@ -281,25 +300,28 @@ template <typename Char> struct arg_id_handler {
return 0; return 0;
} }
constexpr int on_index(int id) { constexpr int on_index(int id) {
kind = arg_id_kind::index;
arg_id = arg_ref<Char>(id); arg_id = arg_ref<Char>(id);
return 0; return 0;
} }
constexpr int on_name(basic_string_view<Char> id) { constexpr int on_name(basic_string_view<Char> id) {
kind = arg_id_kind::name;
arg_id = arg_ref<Char>(id); arg_id = arg_ref<Char>(id);
return 0; return 0;
} }
}; };
template <typename Char> struct parse_arg_id_result { template <typename Char> struct parse_arg_id_result {
arg_id_kind kind;
arg_ref<Char> arg_id; arg_ref<Char> arg_id;
const Char* arg_id_end; const Char* arg_id_end;
}; };
template <int ID, typename Char> template <int ID, typename Char>
constexpr auto parse_arg_id(const Char* begin, const Char* end) { constexpr auto parse_arg_id(const Char* begin, const Char* end) {
auto handler = arg_id_handler<Char>{arg_ref<Char>{}}; auto handler = arg_id_handler<Char>{arg_id_kind::none, arg_ref<Char>{}};
auto arg_id_end = parse_arg_id(begin, end, handler); auto arg_id_end = parse_arg_id(begin, end, handler);
return parse_arg_id_result<Char>{handler.arg_id, arg_id_end}; return parse_arg_id_result<Char>{handler.kind, handler.arg_id, arg_id_end};
} }
template <typename T, typename Enable = void> struct field_type { template <typename T, typename Enable = void> struct field_type {
@ -313,14 +335,13 @@ struct field_type<T, enable_if_t<detail::is_named_arg<T>::value>> {
template <typename T, typename Args, size_t END_POS, int ARG_INDEX, int NEXT_ID, template <typename T, typename Args, size_t END_POS, int ARG_INDEX, int NEXT_ID,
typename S> typename S>
constexpr auto parse_replacement_field_then_tail(S format_str) { constexpr auto parse_replacement_field_then_tail(S fmt) {
using char_type = typename S::char_type; using char_type = typename S::char_type;
constexpr auto str = basic_string_view<char_type>(format_str); constexpr auto str = basic_string_view<char_type>(fmt);
constexpr char_type c = END_POS != str.size() ? str[END_POS] : char_type(); constexpr char_type c = END_POS != str.size() ? str[END_POS] : char_type();
if constexpr (c == '}') { if constexpr (c == '}') {
return parse_tail<Args, END_POS + 1, NEXT_ID>( return parse_tail<Args, END_POS + 1, NEXT_ID>(
field<char_type, typename field_type<T>::type, ARG_INDEX>(), field<char_type, typename field_type<T>::type, ARG_INDEX>(), fmt);
format_str);
} else if constexpr (c != ':') { } else if constexpr (c != ':') {
FMT_THROW(format_error("expected ':'")); FMT_THROW(format_error("expected ':'"));
} else { } else {
@ -333,7 +354,7 @@ constexpr auto parse_replacement_field_then_tail(S format_str) {
return parse_tail<Args, result.end + 1, result.next_arg_id>( return parse_tail<Args, result.end + 1, result.next_arg_id>(
spec_field<char_type, typename field_type<T>::type, ARG_INDEX>{ spec_field<char_type, typename field_type<T>::type, ARG_INDEX>{
result.fmt}, result.fmt},
format_str); fmt);
} }
} }
} }
@ -341,22 +362,21 @@ constexpr auto parse_replacement_field_then_tail(S format_str) {
// Compiles a non-empty format string and returns the compiled representation // Compiles a non-empty format string and returns the compiled representation
// or unknown_format() on unrecognized input. // or unknown_format() on unrecognized input.
template <typename Args, size_t POS, int ID, typename S> template <typename Args, size_t POS, int ID, typename S>
constexpr auto compile_format_string(S format_str) { constexpr auto compile_format_string(S fmt) {
using char_type = typename S::char_type; using char_type = typename S::char_type;
constexpr auto str = basic_string_view<char_type>(format_str); constexpr auto str = basic_string_view<char_type>(fmt);
if constexpr (str[POS] == '{') { if constexpr (str[POS] == '{') {
if constexpr (POS + 1 == str.size()) if constexpr (POS + 1 == str.size())
FMT_THROW(format_error("unmatched '{' in format string")); FMT_THROW(format_error("unmatched '{' in format string"));
if constexpr (str[POS + 1] == '{') { if constexpr (str[POS + 1] == '{') {
return parse_tail<Args, POS + 2, ID>(make_text(str, POS, 1), format_str); return parse_tail<Args, POS + 2, ID>(make_text(str, POS, 1), fmt);
} else if constexpr (str[POS + 1] == '}' || str[POS + 1] == ':') { } else if constexpr (str[POS + 1] == '}' || str[POS + 1] == ':') {
static_assert(ID != manual_indexing_id, static_assert(ID != manual_indexing_id,
"cannot switch from manual to automatic argument indexing"); "cannot switch from manual to automatic argument indexing");
constexpr auto next_id = constexpr auto next_id =
ID != manual_indexing_id ? ID + 1 : manual_indexing_id; ID != manual_indexing_id ? ID + 1 : manual_indexing_id;
return parse_replacement_field_then_tail<get_type<ID, Args>, Args, return parse_replacement_field_then_tail<get_type<ID, Args>, Args,
POS + 1, ID, next_id>( POS + 1, ID, next_id>(fmt);
format_str);
} else { } else {
constexpr auto arg_id_result = constexpr auto arg_id_result =
parse_arg_id<ID>(str.data() + POS + 1, str.data() + str.size()); parse_arg_id<ID>(str.data() + POS + 1, str.data() + str.size());
@ -364,28 +384,27 @@ constexpr auto compile_format_string(S format_str) {
constexpr char_type c = constexpr char_type c =
arg_id_end_pos != str.size() ? str[arg_id_end_pos] : char_type(); arg_id_end_pos != str.size() ? str[arg_id_end_pos] : char_type();
static_assert(c == '}' || c == ':', "missing '}' in format string"); static_assert(c == '}' || c == ':', "missing '}' in format string");
if constexpr (arg_id_result.arg_id.kind == arg_id_kind::index) { if constexpr (arg_id_result.kind == arg_id_kind::index) {
static_assert( static_assert(
ID == manual_indexing_id || ID == 0, ID == manual_indexing_id || ID == 0,
"cannot switch from automatic to manual argument indexing"); "cannot switch from automatic to manual argument indexing");
constexpr auto arg_index = arg_id_result.arg_id.val.index; constexpr auto arg_index = arg_id_result.arg_id.index;
return parse_replacement_field_then_tail<get_type<arg_index, Args>, return parse_replacement_field_then_tail<get_type<arg_index, Args>,
Args, arg_id_end_pos, Args, arg_id_end_pos,
arg_index, manual_indexing_id>( arg_index, manual_indexing_id>(
format_str); fmt);
} else if constexpr (arg_id_result.arg_id.kind == arg_id_kind::name) { } else if constexpr (arg_id_result.kind == arg_id_kind::name) {
constexpr auto arg_index = constexpr auto arg_index =
get_arg_index_by_name(arg_id_result.arg_id.val.name, Args{}); get_arg_index_by_name(arg_id_result.arg_id.name, Args{});
if constexpr (arg_index >= 0) { if constexpr (arg_index >= 0) {
constexpr auto next_id = constexpr auto next_id =
ID != manual_indexing_id ? ID + 1 : manual_indexing_id; ID != manual_indexing_id ? ID + 1 : manual_indexing_id;
return parse_replacement_field_then_tail< return parse_replacement_field_then_tail<
decltype(get_type<arg_index, Args>::value), Args, arg_id_end_pos, decltype(get_type<arg_index, Args>::value), Args, arg_id_end_pos,
arg_index, next_id>(format_str); arg_index, next_id>(fmt);
} else if constexpr (c == '}') { } else if constexpr (c == '}') {
return parse_tail<Args, arg_id_end_pos + 1, ID>( return parse_tail<Args, arg_id_end_pos + 1, ID>(
runtime_named_field<char_type>{arg_id_result.arg_id.val.name}, runtime_named_field<char_type>{arg_id_result.arg_id.name}, fmt);
format_str);
} else if constexpr (c == ':') { } else if constexpr (c == ':') {
return unknown_format(); // no type info for specs parsing return unknown_format(); // no type info for specs parsing
} }
@ -394,29 +413,26 @@ constexpr auto compile_format_string(S format_str) {
} else if constexpr (str[POS] == '}') { } else if constexpr (str[POS] == '}') {
if constexpr (POS + 1 == str.size()) if constexpr (POS + 1 == str.size())
FMT_THROW(format_error("unmatched '}' in format string")); FMT_THROW(format_error("unmatched '}' in format string"));
return parse_tail<Args, POS + 2, ID>(make_text(str, POS, 1), format_str); return parse_tail<Args, POS + 2, ID>(make_text(str, POS, 1), fmt);
} else { } else {
constexpr auto end = parse_text(str, POS + 1); constexpr auto end = parse_text(str, POS + 1);
if constexpr (end - POS > 1) { if constexpr (end - POS > 1) {
return parse_tail<Args, end, ID>(make_text(str, POS, end - POS), return parse_tail<Args, end, ID>(make_text(str, POS, end - POS), fmt);
format_str);
} else { } else {
return parse_tail<Args, end, ID>(code_unit<char_type>{str[POS]}, return parse_tail<Args, end, ID>(code_unit<char_type>{str[POS]}, fmt);
format_str);
} }
} }
} }
template <typename... Args, typename S, template <typename... Args, typename S,
FMT_ENABLE_IF(detail::is_compiled_string<S>::value)> FMT_ENABLE_IF(detail::is_compiled_string<S>::value)>
constexpr auto compile(S format_str) { constexpr auto compile(S fmt) {
constexpr auto str = basic_string_view<typename S::char_type>(format_str); constexpr auto str = basic_string_view<typename S::char_type>(fmt);
if constexpr (str.size() == 0) { if constexpr (str.size() == 0) {
return detail::make_text(str, 0, 0); return detail::make_text(str, 0, 0);
} else { } else {
constexpr auto result = constexpr auto result =
detail::compile_format_string<detail::type_list<Args...>, 0, 0>( detail::compile_format_string<detail::type_list<Args...>, 0, 0>(fmt);
format_str);
return result; return result;
} }
} }
@ -488,40 +504,40 @@ FMT_CONSTEXPR OutputIt format_to(OutputIt out, const S&, Args&&... args) {
template <typename OutputIt, typename S, typename... Args, template <typename OutputIt, typename S, typename... Args,
FMT_ENABLE_IF(detail::is_compiled_string<S>::value)> FMT_ENABLE_IF(detail::is_compiled_string<S>::value)>
auto format_to_n(OutputIt out, size_t n, const S& format_str, Args&&... args) auto format_to_n(OutputIt out, size_t n, const S& fmt, Args&&... args)
-> format_to_n_result<OutputIt> { -> format_to_n_result<OutputIt> {
using traits = detail::fixed_buffer_traits; using traits = detail::fixed_buffer_traits;
auto buf = detail::iterator_buffer<OutputIt, char, traits>(out, n); auto buf = detail::iterator_buffer<OutputIt, char, traits>(out, n);
fmt::format_to(std::back_inserter(buf), format_str, fmt::format_to(std::back_inserter(buf), fmt, std::forward<Args>(args)...);
std::forward<Args>(args)...);
return {buf.out(), buf.count()}; return {buf.out(), buf.count()};
} }
template <typename S, typename... Args, template <typename S, typename... Args,
FMT_ENABLE_IF(detail::is_compiled_string<S>::value)> FMT_ENABLE_IF(detail::is_compiled_string<S>::value)>
FMT_CONSTEXPR20 auto formatted_size(const S& format_str, const Args&... args) FMT_CONSTEXPR20 auto formatted_size(const S& fmt, const Args&... args)
-> size_t { -> size_t {
return fmt::format_to(detail::counting_iterator(), format_str, args...) auto buf = detail::counting_buffer<>();
.count(); fmt::format_to(appender(buf), fmt, args...);
return buf.count();
} }
template <typename S, typename... Args, template <typename S, typename... Args,
FMT_ENABLE_IF(detail::is_compiled_string<S>::value)> FMT_ENABLE_IF(detail::is_compiled_string<S>::value)>
void print(std::FILE* f, const S& format_str, const Args&... args) { void print(std::FILE* f, const S& fmt, const Args&... args) {
memory_buffer buffer; auto buf = memory_buffer();
fmt::format_to(std::back_inserter(buffer), format_str, args...); fmt::format_to(appender(buf), fmt, args...);
detail::print(f, {buffer.data(), buffer.size()}); detail::print(f, {buf.data(), buf.size()});
} }
template <typename S, typename... Args, template <typename S, typename... Args,
FMT_ENABLE_IF(detail::is_compiled_string<S>::value)> FMT_ENABLE_IF(detail::is_compiled_string<S>::value)>
void print(const S& format_str, const Args&... args) { void print(const S& fmt, const Args&... args) {
print(stdout, format_str, args...); print(stdout, fmt, args...);
} }
#if FMT_USE_NONTYPE_TEMPLATE_ARGS #if FMT_USE_NONTYPE_TEMPLATE_ARGS
inline namespace literals { inline namespace literals {
template <detail_exported::fixed_string Str> constexpr auto operator""_cf() { template <detail::fixed_string Str> constexpr auto operator""_cf() {
using char_t = remove_cvref_t<decltype(Str.data[0])>; using char_t = remove_cvref_t<decltype(Str.data[0])>;
return detail::udl_compiled_string<char_t, sizeof(Str.data) / sizeof(char_t), return detail::udl_compiled_string<char_t, sizeof(Str.data) / sizeof(char_t),
Str>(); Str>();

File diff suppressed because it is too large Load Diff

View File

@ -8,36 +8,36 @@
#ifndef FMT_FORMAT_INL_H_ #ifndef FMT_FORMAT_INL_H_
#define FMT_FORMAT_INL_H_ #define FMT_FORMAT_INL_H_
#include <algorithm> #ifndef FMT_MODULE
#include <cerrno> // errno # include <algorithm>
#include <climits> # include <cerrno> // errno
#include <cmath> # include <climits>
#include <exception> # include <cmath>
# include <exception>
#ifndef FMT_STATIC_THOUSANDS_SEPARATOR
# include <locale>
#endif #endif
#if defined(_WIN32) && !defined(FMT_WINDOWS_NO_WCHAR) #if defined(_WIN32) && !defined(FMT_USE_WRITE_CONSOLE)
# include <io.h> // _isatty # include <io.h> // _isatty
#endif #endif
#include "format.h" #include "format.h"
#if FMT_USE_LOCALE
# include <locale>
#endif
#ifndef FMT_FUNC
# define FMT_FUNC
#endif
FMT_BEGIN_NAMESPACE FMT_BEGIN_NAMESPACE
namespace detail { namespace detail {
FMT_FUNC void assert_fail(const char* file, int line, const char* message) { FMT_FUNC void assert_fail(const char* file, int line, const char* message) {
// Use unchecked std::fprintf to avoid triggering another assertion when // Use unchecked std::fprintf to avoid triggering another assertion when
// writing to stderr fails // writing to stderr fails.
std::fprintf(stderr, "%s:%d: assertion failed: %s", file, line, message); fprintf(stderr, "%s:%d: assertion failed: %s", file, line, message);
// Chosen instead of std::abort to satisfy Clang in CUDA mode during device abort();
// code pass.
std::terminate();
}
FMT_FUNC void throw_format_error(const char* message) {
FMT_THROW(format_error(message));
} }
FMT_FUNC void format_error_code(detail::buffer<char>& out, int error_code, FMT_FUNC void format_error_code(detail::buffer<char>& out, int error_code,
@ -56,89 +56,105 @@ FMT_FUNC void format_error_code(detail::buffer<char>& out, int error_code,
++error_code_size; ++error_code_size;
} }
error_code_size += detail::to_unsigned(detail::count_digits(abs_value)); error_code_size += detail::to_unsigned(detail::count_digits(abs_value));
auto it = buffer_appender<char>(out); auto it = appender(out);
if (message.size() <= inline_buffer_size - error_code_size) if (message.size() <= inline_buffer_size - error_code_size)
fmt::format_to(it, FMT_STRING("{}{}"), message, SEP); fmt::format_to(it, FMT_STRING("{}{}"), message, SEP);
fmt::format_to(it, FMT_STRING("{}{}"), ERROR_STR, error_code); fmt::format_to(it, FMT_STRING("{}{}"), ERROR_STR, error_code);
FMT_ASSERT(out.size() <= inline_buffer_size, ""); FMT_ASSERT(out.size() <= inline_buffer_size, "");
} }
FMT_FUNC void report_error(format_func func, int error_code, FMT_FUNC void do_report_error(format_func func, int error_code,
const char* message) noexcept { const char* message) noexcept {
memory_buffer full_message; memory_buffer full_message;
func(full_message, error_code, message); func(full_message, error_code, message);
// Don't use fwrite_fully because the latter may throw. // Don't use fwrite_all because the latter may throw.
if (std::fwrite(full_message.data(), full_message.size(), 1, stderr) > 0) if (std::fwrite(full_message.data(), full_message.size(), 1, stderr) > 0)
std::fputc('\n', stderr); std::fputc('\n', stderr);
} }
// A wrapper around fwrite that throws on error. // A wrapper around fwrite that throws on error.
inline void fwrite_fully(const void* ptr, size_t count, FILE* stream) { inline void fwrite_all(const void* ptr, size_t count, FILE* stream) {
size_t written = std::fwrite(ptr, 1, count, stream); size_t written = std::fwrite(ptr, 1, count, stream);
if (written < count) if (written < count)
FMT_THROW(system_error(errno, FMT_STRING("cannot write to file"))); FMT_THROW(system_error(errno, FMT_STRING("cannot write to file")));
} }
#ifndef FMT_STATIC_THOUSANDS_SEPARATOR #if FMT_USE_LOCALE
using std::locale;
using std::numpunct;
using std::use_facet;
template <typename Locale> template <typename Locale>
locale_ref::locale_ref(const Locale& loc) : locale_(&loc) { locale_ref::locale_ref(const Locale& loc) : locale_(&loc) {
static_assert(std::is_same<Locale, std::locale>::value, ""); static_assert(std::is_same<Locale, locale>::value, "");
} }
#else
struct locale {};
template <typename Char> struct numpunct {
auto grouping() const -> std::string { return "\03"; }
auto thousands_sep() const -> Char { return ','; }
auto decimal_point() const -> Char { return '.'; }
};
template <typename Facet> Facet use_facet(locale) { return {}; }
#endif // FMT_USE_LOCALE
template <typename Locale> auto locale_ref::get() const -> Locale { template <typename Locale> auto locale_ref::get() const -> Locale {
static_assert(std::is_same<Locale, std::locale>::value, ""); static_assert(std::is_same<Locale, locale>::value, "");
return locale_ ? *static_cast<const std::locale*>(locale_) : std::locale(); #if FMT_USE_LOCALE
if (locale_) return *static_cast<const locale*>(locale_);
#endif
return locale();
} }
template <typename Char> template <typename Char>
FMT_FUNC auto thousands_sep_impl(locale_ref loc) -> thousands_sep_result<Char> { FMT_FUNC auto thousands_sep_impl(locale_ref loc) -> thousands_sep_result<Char> {
auto& facet = std::use_facet<std::numpunct<Char>>(loc.get<std::locale>()); auto&& facet = use_facet<numpunct<Char>>(loc.get<locale>());
auto grouping = facet.grouping(); auto grouping = facet.grouping();
auto thousands_sep = grouping.empty() ? Char() : facet.thousands_sep(); auto thousands_sep = grouping.empty() ? Char() : facet.thousands_sep();
return {std::move(grouping), thousands_sep}; return {std::move(grouping), thousands_sep};
} }
template <typename Char> template <typename Char>
FMT_FUNC auto decimal_point_impl(locale_ref loc) -> Char { FMT_FUNC auto decimal_point_impl(locale_ref loc) -> Char {
return std::use_facet<std::numpunct<Char>>(loc.get<std::locale>()) return use_facet<numpunct<Char>>(loc.get<locale>()).decimal_point();
.decimal_point();
} }
#else
template <typename Char>
FMT_FUNC auto thousands_sep_impl(locale_ref) -> thousands_sep_result<Char> {
return {"\03", FMT_STATIC_THOUSANDS_SEPARATOR};
}
template <typename Char> FMT_FUNC Char decimal_point_impl(locale_ref) {
return '.';
}
#endif
#if FMT_USE_LOCALE
FMT_FUNC auto write_loc(appender out, loc_value value, FMT_FUNC auto write_loc(appender out, loc_value value,
const format_specs<>& specs, locale_ref loc) -> bool { const format_specs& specs, locale_ref loc) -> bool {
#ifndef FMT_STATIC_THOUSANDS_SEPARATOR
auto locale = loc.get<std::locale>(); auto locale = loc.get<std::locale>();
// We cannot use the num_put<char> facet because it may produce output in // We cannot use the num_put<char> facet because it may produce output in
// a wrong encoding. // a wrong encoding.
using facet = format_facet<std::locale>; using facet = format_facet<std::locale>;
if (std::has_facet<facet>(locale)) if (std::has_facet<facet>(locale))
return std::use_facet<facet>(locale).put(out, value, specs); return use_facet<facet>(locale).put(out, value, specs);
return facet(locale).put(out, value, specs); return facet(locale).put(out, value, specs);
#endif
return false;
} }
#endif
} // namespace detail } // namespace detail
FMT_FUNC void report_error(const char* message) {
#if FMT_USE_EXCEPTIONS
// Use FMT_THROW instead of throw to avoid bogus unreachable code warnings
// from MSVC.
FMT_THROW(format_error(message));
#else
fputs(message, stderr);
abort();
#endif
}
template <typename Locale> typename Locale::id format_facet<Locale>::id; template <typename Locale> typename Locale::id format_facet<Locale>::id;
#ifndef FMT_STATIC_THOUSANDS_SEPARATOR
template <typename Locale> format_facet<Locale>::format_facet(Locale& loc) { template <typename Locale> format_facet<Locale>::format_facet(Locale& loc) {
auto& numpunct = std::use_facet<std::numpunct<char>>(loc); auto& np = detail::use_facet<detail::numpunct<char>>(loc);
grouping_ = numpunct.grouping(); grouping_ = np.grouping();
if (!grouping_.empty()) separator_ = std::string(1, numpunct.thousands_sep()); if (!grouping_.empty()) separator_ = std::string(1, np.thousands_sep());
} }
#if FMT_USE_LOCALE
template <> template <>
FMT_API FMT_FUNC auto format_facet<std::locale>::do_put( FMT_API FMT_FUNC auto format_facet<std::locale>::do_put(
appender out, loc_value val, const format_specs<>& specs) const -> bool { appender out, loc_value val, const format_specs& specs) const -> bool {
return val.visit( return val.visit(
detail::loc_writer<>{out, specs, separator_, grouping_, decimal_point_}); detail::loc_writer<>{out, specs, separator_, grouping_, decimal_point_});
} }
@ -1411,7 +1427,7 @@ FMT_FUNC void format_system_error(detail::buffer<char>& out, int error_code,
const char* message) noexcept { const char* message) noexcept {
FMT_TRY { FMT_TRY {
auto ec = std::error_code(error_code, std::generic_category()); auto ec = std::error_code(error_code, std::generic_category());
write(std::back_inserter(out), std::system_error(ec, message).what()); detail::write(appender(out), std::system_error(ec, message).what());
return; return;
} }
FMT_CATCH(...) {} FMT_CATCH(...) {}
@ -1420,7 +1436,7 @@ FMT_FUNC void format_system_error(detail::buffer<char>& out, int error_code,
FMT_FUNC void report_system_error(int error_code, FMT_FUNC void report_system_error(int error_code,
const char* message) noexcept { const char* message) noexcept {
report_error(format_system_error, error_code, message); do_report_error(format_system_error, error_code, message);
} }
FMT_FUNC auto vformat(string_view fmt, format_args args) -> std::string { FMT_FUNC auto vformat(string_view fmt, format_args args) -> std::string {
@ -1432,9 +1448,252 @@ FMT_FUNC auto vformat(string_view fmt, format_args args) -> std::string {
} }
namespace detail { namespace detail {
#if !defined(_WIN32) || defined(FMT_WINDOWS_NO_WCHAR)
FMT_FUNC void vformat_to(buffer<char>& buf, string_view fmt, format_args args,
locale_ref loc) {
auto out = appender(buf);
if (fmt.size() == 2 && equal2(fmt.data(), "{}"))
return args.get(0).visit(default_arg_formatter<char>{out});
parse_format_string(
fmt, format_handler<char>{parse_context<char>(fmt), {out, args, loc}});
}
template <typename T> struct span {
T* data;
size_t size;
};
template <typename F> auto flockfile(F* f) -> decltype(_lock_file(f)) {
_lock_file(f);
}
template <typename F> auto funlockfile(F* f) -> decltype(_unlock_file(f)) {
_unlock_file(f);
}
#ifndef getc_unlocked
template <typename F> auto getc_unlocked(F* f) -> decltype(_fgetc_nolock(f)) {
return _fgetc_nolock(f);
}
#endif
template <typename F = FILE, typename Enable = void>
struct has_flockfile : std::false_type {};
template <typename F>
struct has_flockfile<F, void_t<decltype(flockfile(&std::declval<F&>()))>>
: std::true_type {};
// A FILE wrapper. F is FILE defined as a template parameter to make system API
// detection work.
template <typename F> class file_base {
public:
F* file_;
public:
file_base(F* file) : file_(file) {}
operator F*() const { return file_; }
// Reads a code unit from the stream.
auto get() -> int {
int result = getc_unlocked(file_);
if (result == EOF && ferror(file_) != 0)
FMT_THROW(system_error(errno, FMT_STRING("getc failed")));
return result;
}
// Puts the code unit back into the stream buffer.
void unget(char c) {
if (ungetc(c, file_) == EOF)
FMT_THROW(system_error(errno, FMT_STRING("ungetc failed")));
}
void flush() { fflush(this->file_); }
};
// A FILE wrapper for glibc.
template <typename F> class glibc_file : public file_base<F> {
private:
enum {
line_buffered = 0x200, // _IO_LINE_BUF
unbuffered = 2 // _IO_UNBUFFERED
};
public:
using file_base<F>::file_base;
auto is_buffered() const -> bool {
return (this->file_->_flags & unbuffered) == 0;
}
void init_buffer() {
if (this->file_->_IO_write_ptr) return;
// Force buffer initialization by placing and removing a char in a buffer.
assume(this->file_->_IO_write_ptr >= this->file_->_IO_write_end);
putc_unlocked(0, this->file_);
--this->file_->_IO_write_ptr;
}
// Returns the file's read buffer.
auto get_read_buffer() const -> span<const char> {
auto ptr = this->file_->_IO_read_ptr;
return {ptr, to_unsigned(this->file_->_IO_read_end - ptr)};
}
// Returns the file's write buffer.
auto get_write_buffer() const -> span<char> {
auto ptr = this->file_->_IO_write_ptr;
return {ptr, to_unsigned(this->file_->_IO_buf_end - ptr)};
}
void advance_write_buffer(size_t size) { this->file_->_IO_write_ptr += size; }
bool needs_flush() const {
if ((this->file_->_flags & line_buffered) == 0) return false;
char* end = this->file_->_IO_write_end;
return memchr(end, '\n', to_unsigned(this->file_->_IO_write_ptr - end));
}
void flush() { fflush_unlocked(this->file_); }
};
// A FILE wrapper for Apple's libc.
template <typename F> class apple_file : public file_base<F> {
private:
enum {
line_buffered = 1, // __SNBF
unbuffered = 2 // __SLBF
};
public:
using file_base<F>::file_base;
auto is_buffered() const -> bool {
return (this->file_->_flags & unbuffered) == 0;
}
void init_buffer() {
if (this->file_->_p) return;
// Force buffer initialization by placing and removing a char in a buffer.
putc_unlocked(0, this->file_);
--this->file_->_p;
++this->file_->_w;
}
auto get_read_buffer() const -> span<const char> {
return {reinterpret_cast<char*>(this->file_->_p),
to_unsigned(this->file_->_r)};
}
auto get_write_buffer() const -> span<char> {
return {reinterpret_cast<char*>(this->file_->_p),
to_unsigned(this->file_->_bf._base + this->file_->_bf._size -
this->file_->_p)};
}
void advance_write_buffer(size_t size) {
this->file_->_p += size;
this->file_->_w -= size;
}
bool needs_flush() const {
if ((this->file_->_flags & line_buffered) == 0) return false;
return memchr(this->file_->_p + this->file_->_w, '\n',
to_unsigned(-this->file_->_w));
}
};
// A fallback FILE wrapper.
template <typename F> class fallback_file : public file_base<F> {
private:
char next_; // The next unconsumed character in the buffer.
bool has_next_ = false;
public:
using file_base<F>::file_base;
auto is_buffered() const -> bool { return false; }
auto needs_flush() const -> bool { return false; }
void init_buffer() {}
auto get_read_buffer() const -> span<const char> {
return {&next_, has_next_ ? 1u : 0u};
}
auto get_write_buffer() const -> span<char> { return {nullptr, 0}; }
void advance_write_buffer(size_t) {}
auto get() -> int {
has_next_ = false;
return file_base<F>::get();
}
void unget(char c) {
file_base<F>::unget(c);
next_ = c;
has_next_ = true;
}
};
#ifndef FMT_USE_FALLBACK_FILE
# define FMT_USE_FALLBACK_FILE 0
#endif
template <typename F,
FMT_ENABLE_IF(sizeof(F::_p) != 0 && !FMT_USE_FALLBACK_FILE)>
auto get_file(F* f, int) -> apple_file<F> {
return f;
}
template <typename F,
FMT_ENABLE_IF(sizeof(F::_IO_read_ptr) != 0 && !FMT_USE_FALLBACK_FILE)>
inline auto get_file(F* f, int) -> glibc_file<F> {
return f;
}
inline auto get_file(FILE* f, ...) -> fallback_file<FILE> { return f; }
using file_ref = decltype(get_file(static_cast<FILE*>(nullptr), 0));
template <typename F = FILE, typename Enable = void>
class file_print_buffer : public buffer<char> {
public:
explicit file_print_buffer(F*) : buffer(nullptr, size_t()) {}
};
template <typename F>
class file_print_buffer<F, enable_if_t<has_flockfile<F>::value>>
: public buffer<char> {
private:
file_ref file_;
static void grow(buffer<char>& base, size_t) {
auto& self = static_cast<file_print_buffer&>(base);
self.file_.advance_write_buffer(self.size());
if (self.file_.get_write_buffer().size == 0) self.file_.flush();
auto buf = self.file_.get_write_buffer();
FMT_ASSERT(buf.size > 0, "");
self.set(buf.data, buf.size);
self.clear();
}
public:
explicit file_print_buffer(F* f) : buffer(grow, size_t()), file_(f) {
flockfile(f);
file_.init_buffer();
auto buf = file_.get_write_buffer();
set(buf.data, buf.size);
}
~file_print_buffer() {
file_.advance_write_buffer(size());
bool flush = file_.needs_flush();
F* f = file_; // Make funlockfile depend on the template parameter F
funlockfile(f); // for the system API detection to work.
if (flush) fflush(file_);
}
};
#if !defined(_WIN32) || defined(FMT_USE_WRITE_CONSOLE)
FMT_FUNC auto write_console(int, string_view) -> bool { return false; } FMT_FUNC auto write_console(int, string_view) -> bool { return false; }
FMT_FUNC auto write_console(std::FILE*, string_view) -> bool { return false; }
#else #else
using dword = conditional_t<sizeof(long) == 4, unsigned long, unsigned>; using dword = conditional_t<sizeof(long) == 4, unsigned long, unsigned>;
extern "C" __declspec(dllimport) int __stdcall WriteConsoleW( // extern "C" __declspec(dllimport) int __stdcall WriteConsoleW( //
@ -1445,39 +1704,51 @@ FMT_FUNC bool write_console(int fd, string_view text) {
return WriteConsoleW(reinterpret_cast<void*>(_get_osfhandle(fd)), u16.c_str(), return WriteConsoleW(reinterpret_cast<void*>(_get_osfhandle(fd)), u16.c_str(),
static_cast<dword>(u16.size()), nullptr, nullptr) != 0; static_cast<dword>(u16.size()), nullptr, nullptr) != 0;
} }
FMT_FUNC auto write_console(std::FILE* f, string_view text) -> bool {
return write_console(_fileno(f), text);
}
#endif #endif
#ifdef _WIN32 #ifdef _WIN32
// Print assuming legacy (non-Unicode) encoding. // Print assuming legacy (non-Unicode) encoding.
FMT_FUNC void vprint_mojibake(std::FILE* f, string_view fmt, format_args args) { FMT_FUNC void vprint_mojibake(std::FILE* f, string_view fmt, format_args args,
bool newline) {
auto buffer = memory_buffer(); auto buffer = memory_buffer();
detail::vformat_to(buffer, fmt, args); detail::vformat_to(buffer, fmt, args);
fwrite_fully(buffer.data(), buffer.size(), f); if (newline) buffer.push_back('\n');
fwrite_all(buffer.data(), buffer.size(), f);
} }
#endif #endif
FMT_FUNC void print(std::FILE* f, string_view text) { FMT_FUNC void print(std::FILE* f, string_view text) {
#ifdef _WIN32 #if defined(_WIN32) && !defined(FMT_USE_WRITE_CONSOLE)
int fd = _fileno(f); int fd = _fileno(f);
if (_isatty(fd)) { if (_isatty(fd)) {
std::fflush(f); std::fflush(f);
if (write_console(fd, text)) return; if (write_console(fd, text)) return;
} }
#endif #endif
fwrite_fully(text.data(), text.size(), f); fwrite_all(text.data(), text.size(), f);
} }
} // namespace detail } // namespace detail
FMT_FUNC void vprint(std::FILE* f, string_view fmt, format_args args) { FMT_FUNC void vprint_buffered(std::FILE* f, string_view fmt, format_args args) {
auto buffer = memory_buffer(); auto buffer = memory_buffer();
detail::vformat_to(buffer, fmt, args); detail::vformat_to(buffer, fmt, args);
detail::print(f, {buffer.data(), buffer.size()}); detail::print(f, {buffer.data(), buffer.size()});
} }
FMT_FUNC void vprint(std::FILE* f, string_view fmt, format_args args) {
if (!detail::file_ref(f).is_buffered() || !detail::has_flockfile<>())
return vprint_buffered(f, fmt, args);
auto&& buffer = detail::file_print_buffer<>(f);
return detail::vformat_to(buffer, fmt, args);
}
FMT_FUNC void vprintln(std::FILE* f, string_view fmt, format_args args) {
auto buffer = memory_buffer();
detail::vformat_to(buffer, fmt, args);
buffer.push_back('\n');
detail::print(f, {buffer.data(), buffer.size()});
}
FMT_FUNC void vprint(string_view fmt, format_args args) { FMT_FUNC void vprint(string_view fmt, format_args args) {
vprint(stdout, fmt, args); vprint(stdout, fmt, args);
} }

File diff suppressed because it is too large Load Diff

View File

@ -8,18 +8,18 @@
#ifndef FMT_OS_H_ #ifndef FMT_OS_H_
#define FMT_OS_H_ #define FMT_OS_H_
#include <cerrno>
#include <cstddef>
#include <cstdio>
#include <system_error> // std::system_error
#include "format.h" #include "format.h"
#if defined __APPLE__ || defined(__FreeBSD__) #ifndef FMT_MODULE
# include <cerrno>
# include <cstddef>
# include <cstdio>
# include <system_error> // std::system_error
# if FMT_HAS_INCLUDE(<xlocale.h>) # if FMT_HAS_INCLUDE(<xlocale.h>)
# include <xlocale.h> // for LC_NUMERIC_MASK on OS X # include <xlocale.h> // LC_NUMERIC_MASK on macOS
# endif # endif
#endif #endif // FMT_MODULE
#ifndef FMT_USE_FCNTL #ifndef FMT_USE_FCNTL
// UWP doesn't provide _pipe. // UWP doesn't provide _pipe.
@ -77,46 +77,33 @@ FMT_BEGIN_NAMESPACE
FMT_BEGIN_EXPORT FMT_BEGIN_EXPORT
/** /**
\rst * A reference to a null-terminated string. It can be constructed from a C
A reference to a null-terminated string. It can be constructed from a C * string or `std::string`.
string or ``std::string``. *
* You can use one of the following type aliases for common character types:
You can use one of the following type aliases for common character types: *
* +---------------+-----------------------------+
+---------------+-----------------------------+ * | Type | Definition |
| Type | Definition | * +===============+=============================+
+===============+=============================+ * | cstring_view | basic_cstring_view<char> |
| cstring_view | basic_cstring_view<char> | * +---------------+-----------------------------+
+---------------+-----------------------------+ * | wcstring_view | basic_cstring_view<wchar_t> |
| wcstring_view | basic_cstring_view<wchar_t> | * +---------------+-----------------------------+
+---------------+-----------------------------+ *
* This class is most useful as a parameter type for functions that wrap C APIs.
This class is most useful as a parameter type to allow passing
different types of strings to a function, for example::
template <typename... Args>
std::string format(cstring_view format_str, const Args & ... args);
format("{}", 42);
format(std::string("{}"), 42);
\endrst
*/ */
template <typename Char> class basic_cstring_view { template <typename Char> class basic_cstring_view {
private: private:
const Char* data_; const Char* data_;
public: public:
/** Constructs a string reference object from a C string. */ /// Constructs a string reference object from a C string.
basic_cstring_view(const Char* s) : data_(s) {} basic_cstring_view(const Char* s) : data_(s) {}
/** /// Constructs a string reference from an `std::string` object.
\rst
Constructs a string reference from an ``std::string`` object.
\endrst
*/
basic_cstring_view(const std::basic_string<Char>& s) : data_(s.c_str()) {} basic_cstring_view(const std::basic_string<Char>& s) : data_(s.c_str()) {}
/** Returns the pointer to a C string. */ /// Returns the pointer to a C string.
auto c_str() const -> const Char* { return data_; } auto c_str() const -> const Char* { return data_; }
}; };
@ -131,41 +118,38 @@ FMT_API void format_windows_error(buffer<char>& out, int error_code,
const char* message) noexcept; const char* message) noexcept;
} }
FMT_API std::system_error vwindows_error(int error_code, string_view format_str, FMT_API std::system_error vwindows_error(int error_code, string_view fmt,
format_args args); format_args args);
/** /**
\rst * Constructs a `std::system_error` object with the description of the form
Constructs a :class:`std::system_error` object with the description *
of the form * <message>: <system-message>
*
.. parsed-literal:: * where `<message>` is the formatted message and `<system-message>` is the
*<message>*: *<system-message>* * system message corresponding to the error code.
* `error_code` is a Windows error code as given by `GetLastError`.
where *<message>* is the formatted message and *<system-message>* is the * If `error_code` is not a valid error code such as -1, the system message
system message corresponding to the error code. * will look like "error -1".
*error_code* is a Windows error code as given by ``GetLastError``. *
If *error_code* is not a valid error code such as -1, the system message * **Example**:
will look like "error -1". *
* // This throws a system_error with the description
**Example**:: * // cannot open file 'madeup': The system cannot find the file
* specified.
// This throws a system_error with the description * // or similar (system message may vary).
// cannot open file 'madeup': The system cannot find the file specified. * const char *filename = "madeup";
// or similar (system message may vary). * LPOFSTRUCT of = LPOFSTRUCT();
const char *filename = "madeup"; * HFILE file = OpenFile(filename, &of, OF_READ);
LPOFSTRUCT of = LPOFSTRUCT(); * if (file == HFILE_ERROR) {
HFILE file = OpenFile(filename, &of, OF_READ); * throw fmt::windows_error(GetLastError(),
if (file == HFILE_ERROR) { * "cannot open file '{}'", filename);
throw fmt::windows_error(GetLastError(), * }
"cannot open file '{}'", filename); */
} template <typename... T>
\endrst auto windows_error(int error_code, string_view message, const T&... args)
*/ -> std::system_error {
template <typename... Args> return vwindows_error(error_code, message, vargs<T...>{{args...}});
std::system_error windows_error(int error_code, string_view message,
const Args&... args) {
return vwindows_error(error_code, message, fmt::make_format_args(args...));
} }
// Reports a Windows error without throwing an exception. // Reports a Windows error without throwing an exception.
@ -180,8 +164,8 @@ inline auto system_category() noexcept -> const std::error_category& {
// std::system is not available on some platforms such as iOS (#2248). // std::system is not available on some platforms such as iOS (#2248).
#ifdef __OSX__ #ifdef __OSX__
template <typename S, typename... Args, typename Char = char_t<S>> template <typename S, typename... Args, typename Char = char_t<S>>
void say(const S& format_str, Args&&... args) { void say(const S& fmt, Args&&... args) {
std::system(format("say \"{}\"", format(format_str, args...)).c_str()); std::system(format("say \"{}\"", format(fmt, args...)).c_str());
} }
#endif #endif
@ -192,24 +176,24 @@ class buffered_file {
friend class file; friend class file;
explicit buffered_file(FILE* f) : file_(f) {} inline explicit buffered_file(FILE* f) : file_(f) {}
public: public:
buffered_file(const buffered_file&) = delete; buffered_file(const buffered_file&) = delete;
void operator=(const buffered_file&) = delete; void operator=(const buffered_file&) = delete;
// Constructs a buffered_file object which doesn't represent any file. // Constructs a buffered_file object which doesn't represent any file.
buffered_file() noexcept : file_(nullptr) {} inline buffered_file() noexcept : file_(nullptr) {}
// Destroys the object closing the file it represents if any. // Destroys the object closing the file it represents if any.
FMT_API ~buffered_file() noexcept; FMT_API ~buffered_file() noexcept;
public: public:
buffered_file(buffered_file&& other) noexcept : file_(other.file_) { inline buffered_file(buffered_file&& other) noexcept : file_(other.file_) {
other.file_ = nullptr; other.file_ = nullptr;
} }
auto operator=(buffered_file&& other) -> buffered_file& { inline auto operator=(buffered_file&& other) -> buffered_file& {
close(); close();
file_ = other.file_; file_ = other.file_;
other.file_ = nullptr; other.file_ = nullptr;
@ -223,21 +207,20 @@ class buffered_file {
FMT_API void close(); FMT_API void close();
// Returns the pointer to a FILE object representing this file. // Returns the pointer to a FILE object representing this file.
auto get() const noexcept -> FILE* { return file_; } inline auto get() const noexcept -> FILE* { return file_; }
FMT_API auto descriptor() const -> int; FMT_API auto descriptor() const -> int;
void vprint(string_view format_str, format_args args) { template <typename... T>
fmt::vprint(file_, format_str, args); inline void print(string_view fmt, const T&... args) {
} fmt::vargs<T...> vargs = {{args...}};
detail::is_locking<T...>() ? fmt::vprint_buffered(file_, fmt, vargs)
template <typename... Args> : fmt::vprint(file_, fmt, vargs);
inline void print(string_view format_str, const Args&... args) {
vprint(format_str, fmt::make_format_args(args...));
} }
}; };
#if FMT_USE_FCNTL #if FMT_USE_FCNTL
// A file. Closed file is represented by a file object with descriptor -1. // A file. Closed file is represented by a file object with descriptor -1.
// Methods that are not declared with noexcept may throw // Methods that are not declared with noexcept may throw
// fmt::system_error in case of failure. Note that some errors such as // fmt::system_error in case of failure. Note that some errors such as
@ -251,6 +234,8 @@ class FMT_API file {
// Constructs a file object with a given descriptor. // Constructs a file object with a given descriptor.
explicit file(int fd) : fd_(fd) {} explicit file(int fd) : fd_(fd) {}
friend struct pipe;
public: public:
// Possible values for the oflag argument to the constructor. // Possible values for the oflag argument to the constructor.
enum { enum {
@ -263,7 +248,7 @@ class FMT_API file {
}; };
// Constructs a file object which doesn't represent any file. // Constructs a file object which doesn't represent any file.
file() noexcept : fd_(-1) {} inline file() noexcept : fd_(-1) {}
// Opens a file and constructs a file object representing this file. // Opens a file and constructs a file object representing this file.
file(cstring_view path, int oflag); file(cstring_view path, int oflag);
@ -272,10 +257,10 @@ class FMT_API file {
file(const file&) = delete; file(const file&) = delete;
void operator=(const file&) = delete; void operator=(const file&) = delete;
file(file&& other) noexcept : fd_(other.fd_) { other.fd_ = -1; } inline file(file&& other) noexcept : fd_(other.fd_) { other.fd_ = -1; }
// Move assignment is not noexcept because close may throw. // Move assignment is not noexcept because close may throw.
auto operator=(file&& other) -> file& { inline auto operator=(file&& other) -> file& {
close(); close();
fd_ = other.fd_; fd_ = other.fd_;
other.fd_ = -1; other.fd_ = -1;
@ -286,7 +271,7 @@ class FMT_API file {
~file() noexcept; ~file() noexcept;
// Returns the file descriptor. // Returns the file descriptor.
auto descriptor() const noexcept -> int { return fd_; } inline auto descriptor() const noexcept -> int { return fd_; }
// Closes the file. // Closes the file.
void close(); void close();
@ -313,11 +298,6 @@ class FMT_API file {
// necessary. // necessary.
void dup2(int fd, std::error_code& ec) noexcept; void dup2(int fd, std::error_code& ec) noexcept;
// Creates a pipe setting up read_end and write_end file objects for reading
// and writing respectively.
// DEPRECATED! Taking files as out parameters is deprecated.
static void pipe(file& read_end, file& write_end);
// Creates a buffered_file object associated with this file and detaches // Creates a buffered_file object associated with this file and detaches
// this file object from the file. // this file object from the file.
auto fdopen(const char* mode) -> buffered_file; auto fdopen(const char* mode) -> buffered_file;
@ -329,15 +309,24 @@ class FMT_API file {
# endif # endif
}; };
struct FMT_API pipe {
file read_end;
file write_end;
// Creates a pipe setting up read_end and write_end file objects for reading
// and writing respectively.
pipe();
};
// Returns the memory page size. // Returns the memory page size.
auto getpagesize() -> long; auto getpagesize() -> long;
namespace detail { namespace detail {
struct buffer_size { struct buffer_size {
buffer_size() = default; constexpr buffer_size() = default;
size_t value = 0; size_t value = 0;
auto operator=(size_t val) const -> buffer_size { FMT_CONSTEXPR auto operator=(size_t val) const -> buffer_size {
auto bs = buffer_size(); auto bs = buffer_size();
bs.value = val; bs.value = val;
return bs; return bs;
@ -348,7 +337,7 @@ struct ostream_params {
int oflag = file::WRONLY | file::CREATE | file::TRUNC; int oflag = file::WRONLY | file::CREATE | file::TRUNC;
size_t buffer_size = BUFSIZ > 32768 ? BUFSIZ : 32768; size_t buffer_size = BUFSIZ > 32768 ? BUFSIZ : 32768;
ostream_params() {} constexpr ostream_params() {}
template <typename... T> template <typename... T>
ostream_params(T... params, int new_oflag) : ostream_params(params...) { ostream_params(T... params, int new_oflag) : ostream_params(params...) {
@ -369,79 +358,62 @@ struct ostream_params {
# endif # endif
}; };
class file_buffer final : public buffer<char> { } // namespace detail
FMT_INLINE_VARIABLE constexpr auto buffer_size = detail::buffer_size();
/// A fast buffered output stream for writing from a single thread. Writing from
/// multiple threads without external synchronization may result in a data race.
class FMT_API ostream : private detail::buffer<char> {
private:
file file_; file file_;
FMT_API void grow(size_t) override; ostream(cstring_view path, const detail::ostream_params& params);
static void grow(buffer<char>& buf, size_t);
public: public:
FMT_API file_buffer(cstring_view path, const ostream_params& params); ostream(ostream&& other) noexcept;
FMT_API file_buffer(file_buffer&& other); ~ostream();
FMT_API ~file_buffer();
void flush() { operator writer() {
detail::buffer<char>& buf = *this;
return buf;
}
inline void flush() {
if (size() == 0) return; if (size() == 0) return;
file_.write(data(), size() * sizeof(data()[0])); file_.write(data(), size() * sizeof(data()[0]));
clear(); clear();
} }
void close() {
flush();
file_.close();
}
};
} // namespace detail
// Added {} below to work around default constructor error known to
// occur in Xcode versions 7.2.1 and 8.2.1.
constexpr detail::buffer_size buffer_size{};
/** A fast output stream which is not thread-safe. */
class FMT_API ostream {
private:
FMT_MSC_WARNING(suppress : 4251)
detail::file_buffer buffer_;
ostream(cstring_view path, const detail::ostream_params& params)
: buffer_(path, params) {}
public:
ostream(ostream&& other) : buffer_(std::move(other.buffer_)) {}
~ostream();
void flush() { buffer_.flush(); }
template <typename... T> template <typename... T>
friend auto output_file(cstring_view path, T... params) -> ostream; friend auto output_file(cstring_view path, T... params) -> ostream;
void close() { buffer_.close(); } inline void close() {
flush();
file_.close();
}
/** /// Formats `args` according to specifications in `fmt` and writes the
Formats ``args`` according to specifications in ``fmt`` and writes the /// output to the file.
output to the file.
*/
template <typename... T> void print(format_string<T...> fmt, T&&... args) { template <typename... T> void print(format_string<T...> fmt, T&&... args) {
vformat_to(std::back_inserter(buffer_), fmt, vformat_to(appender(*this), fmt.str, vargs<T...>{{args...}});
fmt::make_format_args(args...));
} }
}; };
/** /**
\rst * Opens a file for writing. Supported parameters passed in `params`:
Opens a file for writing. Supported parameters passed in *params*: *
* - `<integer>`: Flags passed to [open](
* ``<integer>``: Flags passed to `open * https://pubs.opengroup.org/onlinepubs/007904875/functions/open.html)
<https://pubs.opengroup.org/onlinepubs/007904875/functions/open.html>`_ * (`file::WRONLY | file::CREATE | file::TRUNC` by default)
(``file::WRONLY | file::CREATE | file::TRUNC`` by default) * - `buffer_size=<integer>`: Output buffer size
* ``buffer_size=<integer>``: Output buffer size *
* **Example**:
**Example**:: *
* auto out = fmt::output_file("guide.txt");
auto out = fmt::output_file("guide.txt"); * out.print("Don't {}", "Panic");
out.print("Don't {}", "Panic");
\endrst
*/ */
template <typename... T> template <typename... T>
inline auto output_file(cstring_view path, T... params) -> ostream { inline auto output_file(cstring_view path, T... params) -> ostream {

View File

@ -8,7 +8,9 @@
#ifndef FMT_OSTREAM_H_ #ifndef FMT_OSTREAM_H_
#define FMT_OSTREAM_H_ #define FMT_OSTREAM_H_
#include <fstream> // std::filebuf #ifndef FMT_MODULE
# include <fstream> // std::filebuf
#endif
#ifdef _WIN32 #ifdef _WIN32
# ifdef __GLIBCXX__ # ifdef __GLIBCXX__
@ -18,42 +20,19 @@
# include <io.h> # include <io.h>
#endif #endif
#include "format.h" #include "chrono.h" // formatbuf
#ifdef _MSVC_STL_UPDATE
# define FMT_MSVC_STL_UPDATE _MSVC_STL_UPDATE
#elif defined(_MSC_VER) && _MSC_VER < 1912 // VS 15.5
# define FMT_MSVC_STL_UPDATE _MSVC_LANG
#else
# define FMT_MSVC_STL_UPDATE 0
#endif
FMT_BEGIN_NAMESPACE FMT_BEGIN_NAMESPACE
namespace detail { namespace detail {
template <typename Streambuf> class formatbuf : public Streambuf {
private:
using char_type = typename Streambuf::char_type;
using streamsize = decltype(std::declval<Streambuf>().sputn(nullptr, 0));
using int_type = typename Streambuf::int_type;
using traits_type = typename Streambuf::traits_type;
buffer<char_type>& buffer_;
public:
explicit formatbuf(buffer<char_type>& buf) : buffer_(buf) {}
protected:
// The put area is always empty. This makes the implementation simpler and has
// the advantage that the streambuf and the buffer are always in sync and
// sputc never writes into uninitialized memory. A disadvantage is that each
// call to sputc always results in a (virtual) call to overflow. There is no
// disadvantage here for sputn since this always results in a call to xsputn.
auto overflow(int_type ch) -> int_type override {
if (!traits_type::eq_int_type(ch, traits_type::eof()))
buffer_.push_back(static_cast<char_type>(ch));
return ch;
}
auto xsputn(const char_type* s, streamsize count) -> streamsize override {
buffer_.append(s, s + count);
return count;
}
};
// Generate a unique explicit instantion in every translation unit using a tag // Generate a unique explicit instantion in every translation unit using a tag
// type in an anonymous namespace. // type in an anonymous namespace.
namespace { namespace {
@ -64,53 +43,18 @@ class file_access {
friend auto get_file(BufType& obj) -> FILE* { return obj.*FileMemberPtr; } friend auto get_file(BufType& obj) -> FILE* { return obj.*FileMemberPtr; }
}; };
#if FMT_MSC_VERSION #if FMT_MSVC_STL_UPDATE
template class file_access<file_access_tag, std::filebuf, template class file_access<file_access_tag, std::filebuf,
&std::filebuf::_Myfile>; &std::filebuf::_Myfile>;
auto get_file(std::filebuf&) -> FILE*; auto get_file(std::filebuf&) -> FILE*;
#endif #endif
inline auto write_ostream_unicode(std::ostream& os, fmt::string_view data)
-> bool {
FILE* f = nullptr;
#if FMT_MSC_VERSION && false
if (auto* buf = dynamic_cast<std::filebuf*>(os.rdbuf()))
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;
#else
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;
}
inline auto write_ostream_unicode(std::wostream&,
fmt::basic_string_view<wchar_t>) -> bool {
return false;
}
// Write the content of buf to os. // Write the content of buf to os.
// It is a separate function rather than a part of vprint to simplify testing. // It is a separate function rather than a part of vprint to simplify testing.
template <typename Char> template <typename Char>
void write_buffer(std::basic_ostream<Char>& os, buffer<Char>& buf) { void write_buffer(std::basic_ostream<Char>& os, buffer<Char>& buf) {
const Char* buf_data = buf.data(); const Char* buf_data = buf.data();
using unsigned_streamsize = std::make_unsigned<std::streamsize>::type; using unsigned_streamsize = make_unsigned_t<std::streamsize>;
unsigned_streamsize size = buf.size(); unsigned_streamsize size = buf.size();
unsigned_streamsize max_size = to_unsigned(max_value<std::streamsize>()); unsigned_streamsize max_size = to_unsigned(max_value<std::streamsize>());
do { do {
@ -121,21 +65,9 @@ void write_buffer(std::basic_ostream<Char>& os, buffer<Char>& buf) {
} while (size != 0); } while (size != 0);
} }
template <typename Char, typename T>
void format_value(buffer<Char>& buf, const T& value) {
auto&& format_buf = formatbuf<std::basic_streambuf<Char>>(buf);
auto&& output = std::basic_ostream<Char>(&format_buf);
#if !defined(FMT_STATIC_THOUSANDS_SEPARATOR)
output.imbue(std::locale::classic()); // The default is always unlocalized.
#endif
output << value;
output.exceptions(std::ios_base::failbit | std::ios_base::badbit);
}
template <typename T> struct streamed_view { template <typename T> struct streamed_view {
const T& value; const T& value;
}; };
} // namespace detail } // namespace detail
// Formats an object of type T that has an overloaded ostream operator<<. // Formats an object of type T that has an overloaded ostream operator<<.
@ -143,11 +75,14 @@ template <typename Char>
struct basic_ostream_formatter : formatter<basic_string_view<Char>, Char> { struct basic_ostream_formatter : formatter<basic_string_view<Char>, Char> {
void set_debug_format() = delete; void set_debug_format() = delete;
template <typename T, typename OutputIt> template <typename T, typename Context>
auto format(const T& value, basic_format_context<OutputIt, Char>& ctx) const auto format(const T& value, Context& ctx) const -> decltype(ctx.out()) {
-> OutputIt {
auto buffer = basic_memory_buffer<Char>(); auto buffer = basic_memory_buffer<Char>();
detail::format_value(buffer, value); auto&& formatbuf = detail::formatbuf<std::basic_streambuf<Char>>(buffer);
auto&& output = std::basic_ostream<Char>(&formatbuf);
output.imbue(std::locale::classic()); // The default is always unlocalized.
output << value;
output.exceptions(std::ios_base::failbit | std::ios_base::badbit);
return formatter<basic_string_view<Char>, Char>::format( return formatter<basic_string_view<Char>, Char>::format(
{buffer.data(), buffer.size()}, ctx); {buffer.data(), buffer.size()}, ctx);
} }
@ -158,73 +93,67 @@ using ostream_formatter = basic_ostream_formatter<char>;
template <typename T, typename Char> template <typename T, typename Char>
struct formatter<detail::streamed_view<T>, Char> struct formatter<detail::streamed_view<T>, Char>
: basic_ostream_formatter<Char> { : basic_ostream_formatter<Char> {
template <typename OutputIt> template <typename Context>
auto format(detail::streamed_view<T> view, auto format(detail::streamed_view<T> view, Context& ctx) const
basic_format_context<OutputIt, Char>& ctx) const -> OutputIt { -> decltype(ctx.out()) {
return basic_ostream_formatter<Char>::format(view.value, ctx); return basic_ostream_formatter<Char>::format(view.value, ctx);
} }
}; };
/** /**
\rst * Returns a view that formats `value` via an ostream `operator<<`.
Returns a view that formats `value` via an ostream ``operator<<``. *
* **Example**:
**Example**:: *
* fmt::print("Current thread id: {}\n",
fmt::print("Current thread id: {}\n", * fmt::streamed(std::this_thread::get_id()));
fmt::streamed(std::this_thread::get_id()));
\endrst
*/ */
template <typename T> template <typename T>
constexpr auto streamed(const T& value) -> detail::streamed_view<T> { constexpr auto streamed(const T& value) -> detail::streamed_view<T> {
return {value}; return {value};
} }
namespace detail { inline void vprint(std::ostream& os, string_view fmt, format_args args) {
inline void vprint_directly(std::ostream& os, string_view format_str,
format_args args) {
auto buffer = memory_buffer(); auto buffer = memory_buffer();
detail::vformat_to(buffer, format_str, args); detail::vformat_to(buffer, fmt, args);
detail::write_buffer(os, buffer); FILE* f = nullptr;
} #if FMT_MSVC_STL_UPDATE && FMT_USE_RTTI
if (auto* buf = dynamic_cast<std::filebuf*>(os.rdbuf()))
} // namespace detail f = detail::get_file(*buf);
#elif defined(_WIN32) && defined(__GLIBCXX__) && FMT_USE_RTTI
FMT_EXPORT template <typename Char> auto* rdbuf = os.rdbuf();
void vprint(std::basic_ostream<Char>& os, if (auto* sfbuf = dynamic_cast<__gnu_cxx::stdio_sync_filebuf<char>*>(rdbuf))
basic_string_view<type_identity_t<Char>> format_str, f = sfbuf->file();
basic_format_args<buffer_context<type_identity_t<Char>>> args) { else if (auto* fbuf = dynamic_cast<__gnu_cxx::stdio_filebuf<char>*>(rdbuf))
auto buffer = basic_memory_buffer<Char>(); f = fbuf->file();
detail::vformat_to(buffer, format_str, args); #endif
if (detail::write_ostream_unicode(os, {buffer.data(), buffer.size()})) return; #ifdef _WIN32
if (f) {
int fd = _fileno(f);
if (_isatty(fd)) {
os.flush();
if (detail::write_console(fd, {buffer.data(), buffer.size()})) return;
}
}
#endif
detail::ignore_unused(f);
detail::write_buffer(os, buffer); detail::write_buffer(os, buffer);
} }
/** /**
\rst * Prints formatted data to the stream `os`.
Prints formatted data to the stream *os*. *
* **Example**:
**Example**:: *
* fmt::print(cerr, "Don't {}!", "panic");
fmt::print(cerr, "Don't {}!", "panic");
\endrst
*/ */
FMT_EXPORT template <typename... T> FMT_EXPORT template <typename... T>
void print(std::ostream& os, format_string<T...> fmt, T&&... args) { void print(std::ostream& os, format_string<T...> fmt, T&&... args) {
const auto& vargs = fmt::make_format_args(args...); fmt::vargs<T...> vargs = {{args...}};
if (detail::is_utf8()) if (detail::use_utf8) return vprint(os, fmt.str, vargs);
vprint(os, fmt, vargs); auto buffer = memory_buffer();
else detail::vformat_to(buffer, fmt.str, vargs);
detail::vprint_directly(os, fmt, vargs); detail::write_buffer(os, buffer);
}
FMT_EXPORT
template <typename... Args>
void print(std::wostream& os,
basic_format_string<wchar_t, type_identity_t<Args>...> fmt,
Args&&... args) {
vprint(os, fmt, fmt::make_format_args<buffer_context<wchar_t>>(args...));
} }
FMT_EXPORT template <typename... T> FMT_EXPORT template <typename... T>
@ -232,14 +161,6 @@ void println(std::ostream& os, format_string<T...> fmt, T&&... args) {
fmt::print(os, "{}\n", fmt::format(fmt, std::forward<T>(args)...)); fmt::print(os, "{}\n", fmt::format(fmt, std::forward<T>(args)...));
} }
FMT_EXPORT
template <typename... Args>
void println(std::wostream& os,
basic_format_string<wchar_t, type_identity_t<Args>...> fmt,
Args&&... args) {
print(os, L"{}\n", fmt::format(fmt, std::forward<Args>(args)...));
}
FMT_END_NAMESPACE FMT_END_NAMESPACE
#endif // FMT_OSTREAM_H_ #endif // FMT_OSTREAM_H_

View File

@ -8,8 +8,10 @@
#ifndef FMT_PRINTF_H_ #ifndef FMT_PRINTF_H_
#define FMT_PRINTF_H_ #define FMT_PRINTF_H_
#include <algorithm> // std::max #ifndef FMT_MODULE
#include <limits> // std::numeric_limits # include <algorithm> // std::max
# include <limits> // std::numeric_limits
#endif
#include "format.h" #include "format.h"
@ -22,7 +24,7 @@ template <typename T> struct printf_formatter {
template <typename Char> class basic_printf_context { template <typename Char> class basic_printf_context {
private: private:
detail::buffer_appender<Char> out_; basic_appender<Char> out_;
basic_format_args<basic_printf_context> args_; basic_format_args<basic_printf_context> args_;
static_assert(std::is_same<Char, char>::value || static_assert(std::is_same<Char, char>::value ||
@ -31,43 +33,53 @@ template <typename Char> class basic_printf_context {
public: public:
using char_type = Char; using char_type = Char;
using parse_context_type = basic_format_parse_context<Char>; using parse_context_type = parse_context<Char>;
template <typename T> using formatter_type = printf_formatter<T>; template <typename T> using formatter_type = printf_formatter<T>;
enum { builtin_types = 1 };
/** /// Constructs a `printf_context` object. References to the arguments are
\rst /// stored in the context object so make sure they have appropriate lifetimes.
Constructs a ``printf_context`` object. References to the arguments are basic_printf_context(basic_appender<Char> out,
stored in the context object so make sure they have appropriate lifetimes.
\endrst
*/
basic_printf_context(detail::buffer_appender<Char> out,
basic_format_args<basic_printf_context> args) basic_format_args<basic_printf_context> args)
: out_(out), args_(args) {} : out_(out), args_(args) {}
auto out() -> detail::buffer_appender<Char> { return out_; } auto out() -> basic_appender<Char> { return out_; }
void advance_to(detail::buffer_appender<Char>) {} void advance_to(basic_appender<Char>) {}
auto locale() -> detail::locale_ref { return {}; } auto locale() -> detail::locale_ref { return {}; }
auto arg(int id) const -> basic_format_arg<basic_printf_context> { auto arg(int id) const -> basic_format_arg<basic_printf_context> {
return args_.get(id); return args_.get(id);
} }
FMT_CONSTEXPR void on_error(const char* message) {
detail::error_handler().on_error(message);
}
}; };
namespace detail { namespace detail {
// Return the result via the out param to workaround gcc bug 77539.
template <bool IS_CONSTEXPR, typename T, typename Ptr = const T*>
FMT_CONSTEXPR auto find(Ptr first, Ptr last, T value, Ptr& out) -> bool {
for (out = first; out != last; ++out) {
if (*out == value) return true;
}
return false;
}
template <>
inline auto find<false, char>(const char* first, const char* last, char value,
const char*& out) -> bool {
out =
static_cast<const char*>(memchr(first, value, to_unsigned(last - first)));
return out != nullptr;
}
// Checks if a value fits in int - used to avoid warnings about comparing // Checks if a value fits in int - used to avoid warnings about comparing
// signed and unsigned integers. // signed and unsigned integers.
template <bool IsSigned> struct int_checker { template <bool IsSigned> struct int_checker {
template <typename T> static auto fits_in_int(T value) -> bool { template <typename T> static auto fits_in_int(T value) -> bool {
unsigned max = max_value<int>(); unsigned max = to_unsigned(max_value<int>());
return value <= max; return value <= max;
} }
static auto fits_in_int(bool) -> bool { return true; } inline static auto fits_in_int(bool) -> bool { return true; }
}; };
template <> struct int_checker<true> { template <> struct int_checker<true> {
@ -75,20 +87,20 @@ template <> struct int_checker<true> {
return value >= (std::numeric_limits<int>::min)() && return value >= (std::numeric_limits<int>::min)() &&
value <= max_value<int>(); value <= max_value<int>();
} }
static auto fits_in_int(int) -> bool { return true; } inline static auto fits_in_int(int) -> bool { return true; }
}; };
struct printf_precision_handler { struct printf_precision_handler {
template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)> template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)>
auto operator()(T value) -> int { auto operator()(T value) -> int {
if (!int_checker<std::numeric_limits<T>::is_signed>::fits_in_int(value)) if (!int_checker<std::numeric_limits<T>::is_signed>::fits_in_int(value))
throw_format_error("number is too big"); report_error("number is too big");
return (std::max)(static_cast<int>(value), 0); return (std::max)(static_cast<int>(value), 0);
} }
template <typename T, FMT_ENABLE_IF(!std::is_integral<T>::value)> template <typename T, FMT_ENABLE_IF(!std::is_integral<T>::value)>
auto operator()(T) -> int { auto operator()(T) -> int {
throw_format_error("precision is not integer"); report_error("precision is not integer");
return 0; return 0;
} }
}; };
@ -133,25 +145,19 @@ template <typename T, typename Context> class arg_converter {
using target_type = conditional_t<std::is_same<T, void>::value, U, T>; using target_type = conditional_t<std::is_same<T, void>::value, U, T>;
if (const_check(sizeof(target_type) <= sizeof(int))) { if (const_check(sizeof(target_type) <= sizeof(int))) {
// Extra casts are used to silence warnings. // Extra casts are used to silence warnings.
if (is_signed) { using unsigned_type = typename make_unsigned_or_bool<target_type>::type;
auto n = static_cast<int>(static_cast<target_type>(value)); if (is_signed)
arg_ = detail::make_arg<Context>(n); arg_ = static_cast<int>(static_cast<target_type>(value));
} else { else
using unsigned_type = typename make_unsigned_or_bool<target_type>::type; arg_ = static_cast<unsigned>(static_cast<unsigned_type>(value));
auto n = static_cast<unsigned>(static_cast<unsigned_type>(value));
arg_ = detail::make_arg<Context>(n);
}
} else { } else {
if (is_signed) { // glibc's printf doesn't sign extend arguments of smaller types:
// glibc's printf doesn't sign extend arguments of smaller types: // std::printf("%lld", -42); // prints "4294967254"
// std::printf("%lld", -42); // prints "4294967254" // but we don't have to do the same because it's a UB.
// but we don't have to do the same because it's a UB. if (is_signed)
auto n = static_cast<long long>(value); arg_ = static_cast<long long>(value);
arg_ = detail::make_arg<Context>(n); else
} else { arg_ = static_cast<typename make_unsigned_or_bool<U>::type>(value);
auto n = static_cast<typename make_unsigned_or_bool<U>::type>(value);
arg_ = detail::make_arg<Context>(n);
}
} }
} }
@ -165,7 +171,7 @@ template <typename T, typename Context> class arg_converter {
// unsigned). // unsigned).
template <typename T, typename Context, typename Char> template <typename T, typename Context, typename Char>
void convert_arg(basic_format_arg<Context>& arg, Char type) { void convert_arg(basic_format_arg<Context>& arg, Char type) {
visit_format_arg(arg_converter<T, Context>(arg, type), arg); arg.visit(arg_converter<T, Context>(arg, type));
} }
// Converts an integer argument to char for printf. // Converts an integer argument to char for printf.
@ -178,8 +184,7 @@ template <typename Context> class char_converter {
template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)> template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)>
void operator()(T value) { void operator()(T value) {
auto c = static_cast<typename Context::char_type>(value); arg_ = static_cast<typename Context::char_type>(value);
arg_ = detail::make_arg<Context>(c);
} }
template <typename T, FMT_ENABLE_IF(!std::is_integral<T>::value)> template <typename T, FMT_ENABLE_IF(!std::is_integral<T>::value)>
@ -195,28 +200,28 @@ template <typename Char> struct get_cstring {
// Checks if an argument is a valid printf width specifier and sets // Checks if an argument is a valid printf width specifier and sets
// left alignment if it is negative. // left alignment if it is negative.
template <typename Char> class printf_width_handler { class printf_width_handler {
private: private:
format_specs<Char>& specs_; format_specs& specs_;
public: public:
explicit printf_width_handler(format_specs<Char>& specs) : specs_(specs) {} inline explicit printf_width_handler(format_specs& specs) : specs_(specs) {}
template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)> template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)>
auto operator()(T value) -> unsigned { auto operator()(T value) -> unsigned {
auto width = static_cast<uint32_or_64_or_128_t<T>>(value); auto width = static_cast<uint32_or_64_or_128_t<T>>(value);
if (detail::is_negative(value)) { if (detail::is_negative(value)) {
specs_.align = align::left; specs_.set_align(align::left);
width = 0 - width; width = 0 - width;
} }
unsigned int_max = max_value<int>(); unsigned int_max = to_unsigned(max_value<int>());
if (width > int_max) throw_format_error("number is too big"); if (width > int_max) report_error("number is too big");
return static_cast<unsigned>(width); return static_cast<unsigned>(width);
} }
template <typename T, FMT_ENABLE_IF(!std::is_integral<T>::value)> template <typename T, FMT_ENABLE_IF(!std::is_integral<T>::value)>
auto operator()(T) -> unsigned { auto operator()(T) -> unsigned {
throw_format_error("width is not integer"); report_error("width is not integer");
return 0; return 0;
} }
}; };
@ -224,12 +229,12 @@ template <typename Char> class printf_width_handler {
// Workaround for a bug with the XL compiler when initializing // Workaround for a bug with the XL compiler when initializing
// printf_arg_formatter's base class. // printf_arg_formatter's base class.
template <typename Char> template <typename Char>
auto make_arg_formatter(buffer_appender<Char> iter, format_specs<Char>& s) auto make_arg_formatter(basic_appender<Char> iter, format_specs& s)
-> arg_formatter<Char> { -> arg_formatter<Char> {
return {iter, s, locale_ref()}; return {iter, s, locale_ref()};
} }
// The ``printf`` argument formatter. // The `printf` argument formatter.
template <typename Char> template <typename Char>
class printf_arg_formatter : public arg_formatter<Char> { class printf_arg_formatter : public arg_formatter<Char> {
private: private:
@ -240,105 +245,96 @@ class printf_arg_formatter : public arg_formatter<Char> {
void write_null_pointer(bool is_string = false) { void write_null_pointer(bool is_string = false) {
auto s = this->specs; auto s = this->specs;
s.type = presentation_type::none; s.set_type(presentation_type::none);
write_bytes(this->out, is_string ? "(null)" : "(nil)", s); write_bytes<Char>(this->out, is_string ? "(null)" : "(nil)", s);
}
template <typename T> void write(T value) {
detail::write<Char>(this->out, value, this->specs, this->locale);
} }
public: public:
printf_arg_formatter(buffer_appender<Char> iter, format_specs<Char>& s, printf_arg_formatter(basic_appender<Char> iter, format_specs& s,
context_type& ctx) context_type& ctx)
: base(make_arg_formatter(iter, s)), context_(ctx) {} : base(make_arg_formatter(iter, s)), context_(ctx) {}
void operator()(monostate value) { base::operator()(value); } void operator()(monostate value) { write(value); }
template <typename T, FMT_ENABLE_IF(detail::is_integral<T>::value)> template <typename T, FMT_ENABLE_IF(detail::is_integral<T>::value)>
void operator()(T value) { void operator()(T value) {
// MSVC2013 fails to compile separate overloads for bool and Char so use // MSVC2013 fails to compile separate overloads for bool and Char so use
// std::is_same instead. // std::is_same instead.
if (!std::is_same<T, Char>::value) { if (!std::is_same<T, Char>::value) {
base::operator()(value); write(value);
return; return;
} }
format_specs<Char> fmt_specs = this->specs; format_specs s = this->specs;
if (fmt_specs.type != presentation_type::none && if (s.type() != presentation_type::none &&
fmt_specs.type != presentation_type::chr) { s.type() != presentation_type::chr) {
return (*this)(static_cast<int>(value)); return (*this)(static_cast<int>(value));
} }
fmt_specs.sign = sign::none; s.set_sign(sign::none);
fmt_specs.alt = false; s.clear_alt();
fmt_specs.fill[0] = ' '; // Ignore '0' flag for char types. s.set_fill(' '); // Ignore '0' flag for char types.
// align::numeric needs to be overwritten here since the '0' flag is // align::numeric needs to be overwritten here since the '0' flag is
// ignored for non-numeric types // ignored for non-numeric types
if (fmt_specs.align == align::none || fmt_specs.align == align::numeric) if (s.align() == align::none || s.align() == align::numeric)
fmt_specs.align = align::right; s.set_align(align::right);
write<Char>(this->out, static_cast<Char>(value), fmt_specs); detail::write<Char>(this->out, static_cast<Char>(value), s);
} }
template <typename T, FMT_ENABLE_IF(std::is_floating_point<T>::value)> template <typename T, FMT_ENABLE_IF(std::is_floating_point<T>::value)>
void operator()(T value) { void operator()(T value) {
base::operator()(value); write(value);
} }
/** Formats a null-terminated C string. */
void operator()(const char* value) { void operator()(const char* value) {
if (value) if (value)
base::operator()(value); write(value);
else else
write_null_pointer(this->specs.type != presentation_type::pointer); write_null_pointer(this->specs.type() != presentation_type::pointer);
} }
/** Formats a null-terminated wide C string. */
void operator()(const wchar_t* value) { void operator()(const wchar_t* value) {
if (value) if (value)
base::operator()(value); write(value);
else else
write_null_pointer(this->specs.type != presentation_type::pointer); write_null_pointer(this->specs.type() != presentation_type::pointer);
} }
void operator()(basic_string_view<Char> value) { base::operator()(value); } void operator()(basic_string_view<Char> value) { write(value); }
/** Formats a pointer. */
void operator()(const void* value) { void operator()(const void* value) {
if (value) if (value)
base::operator()(value); write(value);
else else
write_null_pointer(); write_null_pointer();
} }
/** Formats an argument of a custom (user-defined) type. */
void operator()(typename basic_format_arg<context_type>::handle handle) { void operator()(typename basic_format_arg<context_type>::handle handle) {
auto parse_ctx = basic_format_parse_context<Char>({}); auto parse_ctx = parse_context<Char>({});
handle.format(parse_ctx, context_); handle.format(parse_ctx, context_);
} }
}; };
template <typename Char> template <typename Char>
void parse_flags(format_specs<Char>& specs, const Char*& it, const Char* end) { void parse_flags(format_specs& specs, const Char*& it, const Char* end) {
for (; it != end; ++it) { for (; it != end; ++it) {
switch (*it) { switch (*it) {
case '-': case '-': specs.set_align(align::left); break;
specs.align = align::left; case '+': specs.set_sign(sign::plus); break;
break; case '0': specs.set_fill('0'); break;
case '+':
specs.sign = sign::plus;
break;
case '0':
specs.fill[0] = '0';
break;
case ' ': case ' ':
if (specs.sign != sign::plus) specs.sign = sign::space; if (specs.sign() != sign::plus) specs.set_sign(sign::space);
break; break;
case '#': case '#': specs.set_alt(); break;
specs.alt = true; default: return;
break;
default:
return;
} }
} }
} }
template <typename Char, typename GetArg> template <typename Char, typename GetArg>
auto parse_header(const Char*& it, const Char* end, format_specs<Char>& specs, auto parse_header(const Char*& it, const Char* end, format_specs& specs,
GetArg get_arg) -> int { GetArg get_arg) -> int {
int arg_index = -1; int arg_index = -1;
Char c = *it; Char c = *it;
@ -350,11 +346,11 @@ auto parse_header(const Char*& it, const Char* end, format_specs<Char>& specs,
++it; ++it;
arg_index = value != -1 ? value : max_value<int>(); arg_index = value != -1 ? value : max_value<int>();
} else { } else {
if (c == '0') specs.fill[0] = '0'; if (c == '0') specs.set_fill('0');
if (value != 0) { if (value != 0) {
// Nonzero value means that we parsed width and don't need to // Nonzero value means that we parsed width and don't need to
// parse it or flags again, so return now. // parse it or flags again, so return now.
if (value == -1) throw_format_error("number is too big"); if (value == -1) report_error("number is too big");
specs.width = value; specs.width = value;
return arg_index; return arg_index;
} }
@ -365,63 +361,47 @@ auto parse_header(const Char*& it, const Char* end, format_specs<Char>& specs,
if (it != end) { if (it != end) {
if (*it >= '0' && *it <= '9') { if (*it >= '0' && *it <= '9') {
specs.width = parse_nonnegative_int(it, end, -1); specs.width = parse_nonnegative_int(it, end, -1);
if (specs.width == -1) throw_format_error("number is too big"); if (specs.width == -1) report_error("number is too big");
} else if (*it == '*') { } else if (*it == '*') {
++it; ++it;
specs.width = static_cast<int>(visit_format_arg( specs.width = static_cast<int>(
detail::printf_width_handler<Char>(specs), get_arg(-1))); get_arg(-1).visit(detail::printf_width_handler(specs)));
} }
} }
return arg_index; return arg_index;
} }
inline auto parse_printf_presentation_type(char c, type t) inline auto parse_printf_presentation_type(char c, type t, bool& upper)
-> presentation_type { -> presentation_type {
using pt = presentation_type; using pt = presentation_type;
constexpr auto integral_set = sint_set | uint_set | bool_set | char_set; constexpr auto integral_set = sint_set | uint_set | bool_set | char_set;
switch (c) { switch (c) {
case 'd': case 'd': return in(t, integral_set) ? pt::dec : pt::none;
return in(t, integral_set) ? pt::dec : pt::none; case 'o': return in(t, integral_set) ? pt::oct : pt::none;
case 'o': case 'X': upper = true; FMT_FALLTHROUGH;
return in(t, integral_set) ? pt::oct : pt::none; case 'x': return in(t, integral_set) ? pt::hex : pt::none;
case 'x': case 'E': upper = true; FMT_FALLTHROUGH;
return in(t, integral_set) ? pt::hex_lower : pt::none; case 'e': return in(t, float_set) ? pt::exp : pt::none;
case 'X': case 'F': upper = true; FMT_FALLTHROUGH;
return in(t, integral_set) ? pt::hex_upper : pt::none; case 'f': return in(t, float_set) ? pt::fixed : pt::none;
case 'a': case 'G': upper = true; FMT_FALLTHROUGH;
return in(t, float_set) ? pt::hexfloat_lower : pt::none; case 'g': return in(t, float_set) ? pt::general : pt::none;
case 'A': case 'A': upper = true; FMT_FALLTHROUGH;
return in(t, float_set) ? pt::hexfloat_upper : pt::none; case 'a': return in(t, float_set) ? pt::hexfloat : pt::none;
case 'e': case 'c': return in(t, integral_set) ? pt::chr : pt::none;
return in(t, float_set) ? pt::exp_lower : pt::none; case 's': return in(t, string_set | cstring_set) ? pt::string : pt::none;
case 'E': case 'p': return in(t, pointer_set | cstring_set) ? pt::pointer : pt::none;
return in(t, float_set) ? pt::exp_upper : pt::none; default: return pt::none;
case 'f':
return in(t, float_set) ? pt::fixed_lower : pt::none;
case 'F':
return in(t, float_set) ? pt::fixed_upper : pt::none;
case 'g':
return in(t, float_set) ? pt::general_lower : pt::none;
case 'G':
return in(t, float_set) ? pt::general_upper : pt::none;
case 'c':
return in(t, integral_set) ? pt::chr : pt::none;
case 's':
return in(t, string_set | cstring_set) ? pt::string : pt::none;
case 'p':
return in(t, pointer_set | cstring_set) ? pt::pointer : pt::none;
default:
return pt::none;
} }
} }
template <typename Char, typename Context> template <typename Char, typename Context>
void vprintf(buffer<Char>& buf, basic_string_view<Char> format, void vprintf(buffer<Char>& buf, basic_string_view<Char> format,
basic_format_args<Context> args) { basic_format_args<Context> args) {
using iterator = buffer_appender<Char>; using iterator = basic_appender<Char>;
auto out = iterator(buf); auto out = iterator(buf);
auto context = basic_printf_context<Char>(out, args); auto context = basic_printf_context<Char>(out, args);
auto parse_ctx = basic_format_parse_context<Char>(format); auto parse_ctx = parse_context<Char>(format);
// Returns the argument with specified index or, if arg_index is -1, the next // Returns the argument with specified index or, if arg_index is -1, the next
// argument. // argument.
@ -449,12 +429,12 @@ void vprintf(buffer<Char>& buf, basic_string_view<Char> format,
} }
write(out, basic_string_view<Char>(start, to_unsigned(it - 1 - start))); write(out, basic_string_view<Char>(start, to_unsigned(it - 1 - start)));
auto specs = format_specs<Char>(); auto specs = format_specs();
specs.align = align::right; specs.set_align(align::right);
// Parse argument index, flags and width. // Parse argument index, flags and width.
int arg_index = parse_header(it, end, specs, get_arg); int arg_index = parse_header(it, end, specs, get_arg);
if (arg_index == 0) throw_format_error("argument not found"); if (arg_index == 0) report_error("argument not found");
// Parse precision. // Parse precision.
if (it != end && *it == '.') { if (it != end && *it == '.') {
@ -464,8 +444,8 @@ void vprintf(buffer<Char>& buf, basic_string_view<Char> format,
specs.precision = parse_nonnegative_int(it, end, 0); specs.precision = parse_nonnegative_int(it, end, 0);
} else if (c == '*') { } else if (c == '*') {
++it; ++it;
specs.precision = static_cast<int>( specs.precision =
visit_format_arg(printf_precision_handler(), get_arg(-1))); static_cast<int>(get_arg(-1).visit(printf_precision_handler()));
} else { } else {
specs.precision = 0; specs.precision = 0;
} }
@ -474,25 +454,26 @@ void vprintf(buffer<Char>& buf, basic_string_view<Char> format,
auto arg = get_arg(arg_index); auto arg = get_arg(arg_index);
// For d, i, o, u, x, and X conversion specifiers, if a precision is // For d, i, o, u, x, and X conversion specifiers, if a precision is
// specified, the '0' flag is ignored // specified, the '0' flag is ignored
if (specs.precision >= 0 && arg.is_integral()) { if (specs.precision >= 0 && is_integral_type(arg.type())) {
// Ignore '0' for non-numeric types or if '-' present. // Ignore '0' for non-numeric types or if '-' present.
specs.fill[0] = ' '; specs.set_fill(' ');
} }
if (specs.precision >= 0 && arg.type() == type::cstring_type) { if (specs.precision >= 0 && arg.type() == type::cstring_type) {
auto str = visit_format_arg(get_cstring<Char>(), arg); auto str = arg.visit(get_cstring<Char>());
auto str_end = str + specs.precision; auto str_end = str + specs.precision;
auto nul = std::find(str, str_end, Char()); auto nul = std::find(str, str_end, Char());
auto sv = basic_string_view<Char>( auto sv = basic_string_view<Char>(
str, to_unsigned(nul != str_end ? nul - str : specs.precision)); str, to_unsigned(nul != str_end ? nul - str : specs.precision));
arg = make_arg<basic_printf_context<Char>>(sv); arg = sv;
} }
if (specs.alt && visit_format_arg(is_zero_int(), arg)) specs.alt = false; if (specs.alt() && arg.visit(is_zero_int())) specs.clear_alt();
if (specs.fill[0] == '0') { if (specs.fill_unit<Char>() == '0') {
if (arg.is_arithmetic() && specs.align != align::left) if (is_arithmetic_type(arg.type()) && specs.align() != align::left) {
specs.align = align::numeric; specs.set_align(align::numeric);
else } else {
specs.fill[0] = ' '; // Ignore '0' flag for non-numeric types or if '-' // Ignore '0' flag for non-numeric types or if '-' flag is also present.
// flag is also present. specs.set_fill(' ');
}
} }
// Parse length and convert the argument to the required type. // Parse length and convert the argument to the required type.
@ -517,47 +498,39 @@ void vprintf(buffer<Char>& buf, basic_string_view<Char> format,
convert_arg<long>(arg, t); convert_arg<long>(arg, t);
} }
break; break;
case 'j': case 'j': convert_arg<intmax_t>(arg, t); break;
convert_arg<intmax_t>(arg, t); case 'z': convert_arg<size_t>(arg, t); break;
break; case 't': convert_arg<std::ptrdiff_t>(arg, t); break;
case 'z':
convert_arg<size_t>(arg, t);
break;
case 't':
convert_arg<std::ptrdiff_t>(arg, t);
break;
case 'L': case 'L':
// printf produces garbage when 'L' is omitted for long double, no // printf produces garbage when 'L' is omitted for long double, no
// need to do the same. // need to do the same.
break; break;
default: default: --it; convert_arg<void>(arg, c);
--it;
convert_arg<void>(arg, c);
} }
// Parse type. // Parse type.
if (it == end) throw_format_error("invalid format string"); if (it == end) report_error("invalid format string");
char type = static_cast<char>(*it++); char type = static_cast<char>(*it++);
if (arg.is_integral()) { if (is_integral_type(arg.type())) {
// Normalize type. // Normalize type.
switch (type) { switch (type) {
case 'i': case 'i':
case 'u': case 'u': type = 'd'; break;
type = 'd';
break;
case 'c': case 'c':
visit_format_arg(char_converter<basic_printf_context<Char>>(arg), arg); arg.visit(char_converter<basic_printf_context<Char>>(arg));
break; break;
} }
} }
specs.type = parse_printf_presentation_type(type, arg.type()); bool upper = false;
if (specs.type == presentation_type::none) specs.set_type(parse_printf_presentation_type(type, arg.type(), upper));
throw_format_error("invalid format specifier"); if (specs.type() == presentation_type::none)
report_error("invalid format specifier");
if (upper) specs.set_upper();
start = it; start = it;
// Format argument. // Format argument.
visit_format_arg(printf_arg_formatter<Char>(out, specs, context), arg); arg.visit(printf_arg_formatter<Char>(out, specs, context));
} }
write(out, basic_string_view<Char>(start, to_unsigned(it - start))); write(out, basic_string_view<Char>(start, to_unsigned(it - start)));
} }
@ -569,56 +542,44 @@ using wprintf_context = basic_printf_context<wchar_t>;
using printf_args = basic_format_args<printf_context>; using printf_args = basic_format_args<printf_context>;
using wprintf_args = basic_format_args<wprintf_context>; using wprintf_args = basic_format_args<wprintf_context>;
/** /// Constructs an `format_arg_store` object that contains references to
\rst /// arguments and can be implicitly converted to `printf_args`.
Constructs an `~fmt::format_arg_store` object that contains references to template <typename Char = char, typename... T>
arguments and can be implicitly converted to `~fmt::printf_args`. inline auto make_printf_args(T&... args)
\endrst -> decltype(fmt::make_format_args<basic_printf_context<Char>>(args...)) {
*/ return fmt::make_format_args<basic_printf_context<Char>>(args...);
template <typename... T>
inline auto make_printf_args(const T&... args)
-> format_arg_store<printf_context, T...> {
return {args...};
} }
// DEPRECATED! template <typename Char> struct vprintf_args {
template <typename... T> using type = basic_format_args<basic_printf_context<Char>>;
inline auto make_wprintf_args(const T&... args) };
-> format_arg_store<wprintf_context, T...> {
return {args...};
}
template <typename Char> template <typename Char>
inline auto vsprintf( inline auto vsprintf(basic_string_view<Char> fmt,
basic_string_view<Char> fmt, typename vprintf_args<Char>::type args)
basic_format_args<basic_printf_context<type_identity_t<Char>>> args)
-> std::basic_string<Char> { -> std::basic_string<Char> {
auto buf = basic_memory_buffer<Char>(); auto buf = basic_memory_buffer<Char>();
detail::vprintf(buf, fmt, args); detail::vprintf(buf, fmt, args);
return to_string(buf); return {buf.data(), buf.size()};
} }
/** /**
\rst * Formats `args` according to specifications in `fmt` and returns the result
Formats arguments and returns the result as a string. * as as string.
*
**Example**:: * **Example**:
*
std::string message = fmt::sprintf("The answer is %d", 42); * std::string message = fmt::sprintf("The answer is %d", 42);
\endrst */
*/ template <typename S, typename... T, typename Char = detail::char_t<S>>
template <typename S, typename... T,
typename Char = enable_if_t<detail::is_string<S>::value, char_t<S>>>
inline auto sprintf(const S& fmt, const T&... args) -> std::basic_string<Char> { inline auto sprintf(const S& fmt, const T&... args) -> std::basic_string<Char> {
return vsprintf(detail::to_string_view(fmt), return vsprintf(detail::to_string_view(fmt),
fmt::make_format_args<basic_printf_context<Char>>(args...)); fmt::make_format_args<basic_printf_context<Char>>(args...));
} }
template <typename Char> template <typename Char>
inline auto vfprintf( inline auto vfprintf(std::FILE* f, basic_string_view<Char> fmt,
std::FILE* f, basic_string_view<Char> fmt, typename vprintf_args<Char>::type args) -> int {
basic_format_args<basic_printf_context<type_identity_t<Char>>> args)
-> int {
auto buf = basic_memory_buffer<Char>(); auto buf = basic_memory_buffer<Char>();
detail::vprintf(buf, fmt, args); detail::vprintf(buf, fmt, args);
size_t size = buf.size(); size_t size = buf.size();
@ -628,36 +589,33 @@ inline auto vfprintf(
} }
/** /**
\rst * Formats `args` according to specifications in `fmt` and writes the output
Prints formatted data to the file *f*. * to `f`.
*
**Example**:: * **Example**:
*
fmt::fprintf(stderr, "Don't %s!", "panic"); * fmt::fprintf(stderr, "Don't %s!", "panic");
\endrst
*/ */
template <typename S, typename... T, typename Char = char_t<S>> template <typename S, typename... T, typename Char = detail::char_t<S>>
inline auto fprintf(std::FILE* f, const S& fmt, const T&... args) -> int { inline auto fprintf(std::FILE* f, const S& fmt, const T&... args) -> int {
return vfprintf(f, detail::to_string_view(fmt), return vfprintf(f, detail::to_string_view(fmt),
fmt::make_format_args<basic_printf_context<Char>>(args...)); make_printf_args<Char>(args...));
} }
template <typename Char> template <typename Char>
FMT_DEPRECATED inline auto vprintf( FMT_DEPRECATED inline auto vprintf(basic_string_view<Char> fmt,
basic_string_view<Char> fmt, typename vprintf_args<Char>::type args)
basic_format_args<basic_printf_context<type_identity_t<Char>>> args)
-> int { -> int {
return vfprintf(stdout, fmt, args); return vfprintf(stdout, fmt, args);
} }
/** /**
\rst * Formats `args` according to specifications in `fmt` and writes the output
Prints formatted data to ``stdout``. * to `stdout`.
*
**Example**:: * **Example**:
*
fmt::printf("Elapsed time: %.2f seconds", 1.23); * fmt::printf("Elapsed time: %.2f seconds", 1.23);
\endrst
*/ */
template <typename... T> template <typename... T>
inline auto printf(string_view fmt, const T&... args) -> int { inline auto printf(string_view fmt, const T&... args) -> int {
@ -666,7 +624,7 @@ inline auto printf(string_view fmt, const T&... args) -> int {
template <typename... T> template <typename... T>
FMT_DEPRECATED inline auto printf(basic_string_view<wchar_t> fmt, FMT_DEPRECATED inline auto printf(basic_string_view<wchar_t> fmt,
const T&... args) -> int { const T&... args) -> int {
return vfprintf(stdout, fmt, make_wprintf_args(args...)); return vfprintf(stdout, fmt, make_printf_args<wchar_t>(args...));
} }
FMT_END_EXPORT FMT_END_EXPORT

View File

@ -8,67 +8,31 @@
#ifndef FMT_RANGES_H_ #ifndef FMT_RANGES_H_
#define FMT_RANGES_H_ #define FMT_RANGES_H_
#include <initializer_list> #ifndef FMT_MODULE
#include <tuple> # include <initializer_list>
#include <type_traits> # include <iterator>
# include <string>
# include <tuple>
# include <type_traits>
# include <utility>
#endif
#include "format.h" #include "format.h"
FMT_BEGIN_NAMESPACE FMT_BEGIN_NAMESPACE
FMT_EXPORT
enum class range_format { disabled, map, set, sequence, string, debug_string };
namespace detail { namespace detail {
template <typename Range, typename OutputIt>
auto copy(const Range& range, OutputIt out) -> OutputIt {
for (auto it = range.begin(), end = range.end(); it != end; ++it)
*out++ = *it;
return out;
}
template <typename OutputIt>
auto copy(const char* str, OutputIt out) -> OutputIt {
while (*str) *out++ = *str++;
return out;
}
template <typename OutputIt> auto copy(char ch, OutputIt out) -> OutputIt {
*out++ = ch;
return out;
}
template <typename OutputIt> auto copy(wchar_t ch, OutputIt out) -> OutputIt {
*out++ = ch;
return out;
}
// Returns true if T has a std::string-like interface, like std::string_view.
template <typename T> class is_std_string_like {
template <typename U>
static auto check(U* p)
-> decltype((void)p->find('a'), p->length(), (void)p->data(), int());
template <typename> static void check(...);
public:
static constexpr const bool value =
is_string<T>::value ||
std::is_convertible<T, std_string_view<char>>::value ||
!std::is_void<decltype(check<T>(nullptr))>::value;
};
template <typename Char>
struct is_std_string_like<fmt::basic_string_view<Char>> : std::true_type {};
template <typename T> class is_map { template <typename T> class is_map {
template <typename U> static auto check(U*) -> typename U::mapped_type; template <typename U> static auto check(U*) -> typename U::mapped_type;
template <typename> static void check(...); template <typename> static void check(...);
public: public:
#ifdef FMT_FORMAT_MAP_AS_LIST // DEPRECATED!
static constexpr const bool value = false;
#else
static constexpr const bool value = static constexpr const bool value =
!std::is_void<decltype(check<T>(nullptr))>::value; !std::is_void<decltype(check<T>(nullptr))>::value;
#endif
}; };
template <typename T> class is_set { template <typename T> class is_set {
@ -76,26 +40,10 @@ template <typename T> class is_set {
template <typename> static void check(...); template <typename> static void check(...);
public: public:
#ifdef FMT_FORMAT_SET_AS_LIST // DEPRECATED!
static constexpr const bool value = false;
#else
static constexpr const bool value = static constexpr const bool value =
!std::is_void<decltype(check<T>(nullptr))>::value && !is_map<T>::value; !std::is_void<decltype(check<T>(nullptr))>::value && !is_map<T>::value;
#endif
}; };
template <typename... Ts> struct conditional_helper {};
template <typename T, typename _ = void> struct is_range_ : std::false_type {};
#if !FMT_MSC_VERSION || FMT_MSC_VERSION > 1800
# define FMT_DECLTYPE_RETURN(val) \
->decltype(val) { return val; } \
static_assert( \
true, "") // This makes it so that a semicolon is required after the
// macro, which helps clang-format handle the formatting.
// C array overload // C array overload
template <typename T, std::size_t N> template <typename T, std::size_t N>
auto range_begin(const T (&arr)[N]) -> const T* { auto range_begin(const T (&arr)[N]) -> const T* {
@ -110,17 +58,21 @@ template <typename T, typename Enable = void>
struct has_member_fn_begin_end_t : std::false_type {}; struct has_member_fn_begin_end_t : std::false_type {};
template <typename T> template <typename T>
struct has_member_fn_begin_end_t<T, void_t<decltype(std::declval<T>().begin()), struct has_member_fn_begin_end_t<T, void_t<decltype(*std::declval<T>().begin()),
decltype(std::declval<T>().end())>> decltype(std::declval<T>().end())>>
: std::true_type {}; : std::true_type {};
// Member function overload // Member function overloads.
template <typename T> template <typename T>
auto range_begin(T&& rng) FMT_DECLTYPE_RETURN(static_cast<T&&>(rng).begin()); auto range_begin(T&& rng) -> decltype(static_cast<T&&>(rng).begin()) {
return static_cast<T&&>(rng).begin();
}
template <typename T> template <typename T>
auto range_end(T&& rng) FMT_DECLTYPE_RETURN(static_cast<T&&>(rng).end()); auto range_end(T&& rng) -> decltype(static_cast<T&&>(rng).end()) {
return static_cast<T&&>(rng).end();
}
// ADL overload. Only participates in overload resolution if member functions // ADL overloads. Only participate in overload resolution if member functions
// are not found. // are not found.
template <typename T> template <typename T>
auto range_begin(T&& rng) auto range_begin(T&& rng)
@ -141,31 +93,30 @@ struct has_mutable_begin_end : std::false_type {};
template <typename T> template <typename T>
struct has_const_begin_end< struct has_const_begin_end<
T, T, void_t<decltype(*detail::range_begin(
void_t< std::declval<const remove_cvref_t<T>&>())),
decltype(detail::range_begin(std::declval<const remove_cvref_t<T>&>())), decltype(detail::range_end(
decltype(detail::range_end(std::declval<const remove_cvref_t<T>&>()))>> std::declval<const remove_cvref_t<T>&>()))>>
: std::true_type {}; : std::true_type {};
template <typename T> template <typename T>
struct has_mutable_begin_end< struct has_mutable_begin_end<
T, void_t<decltype(detail::range_begin(std::declval<T>())), T, void_t<decltype(*detail::range_begin(std::declval<T&>())),
decltype(detail::range_end(std::declval<T>())), decltype(detail::range_end(std::declval<T&>())),
// the extra int here is because older versions of MSVC don't // the extra int here is because older versions of MSVC don't
// SFINAE properly unless there are distinct types // SFINAE properly unless there are distinct types
int>> : std::true_type {}; int>> : std::true_type {};
template <typename T, typename _ = void> struct is_range_ : std::false_type {};
template <typename T> template <typename T>
struct is_range_<T, void> struct is_range_<T, void>
: std::integral_constant<bool, (has_const_begin_end<T>::value || : std::integral_constant<bool, (has_const_begin_end<T>::value ||
has_mutable_begin_end<T>::value)> {}; has_mutable_begin_end<T>::value)> {};
# undef FMT_DECLTYPE_RETURN
#endif
// tuple_size and tuple_element check. // tuple_size and tuple_element check.
template <typename T> class is_tuple_like_ { template <typename T> class is_tuple_like_ {
template <typename U> template <typename U, typename V = typename std::remove_cv<U>::type>
static auto check(U* p) -> decltype(std::tuple_size<U>::value, int()); static auto check(U* p) -> decltype(std::tuple_size<V>::value, 0);
template <typename> static void check(...); template <typename> static void check(...);
public: public:
@ -206,12 +157,13 @@ class is_tuple_formattable_ {
static constexpr const bool value = false; static constexpr const bool value = false;
}; };
template <typename T, typename C> class is_tuple_formattable_<T, C, true> { template <typename T, typename C> class is_tuple_formattable_<T, C, true> {
template <std::size_t... Is> template <size_t... Is>
static auto check2(index_sequence<Is...>, static auto all_true(index_sequence<Is...>,
integer_sequence<bool, (Is == Is)...>) -> std::true_type; integer_sequence<bool, (Is >= 0)...>) -> std::true_type;
static auto check2(...) -> std::false_type; static auto all_true(...) -> std::false_type;
template <std::size_t... Is>
static auto check(index_sequence<Is...>) -> decltype(check2( template <size_t... Is>
static auto check(index_sequence<Is...>) -> decltype(all_true(
index_sequence<Is...>{}, index_sequence<Is...>{},
integer_sequence<bool, integer_sequence<bool,
(is_formattable<typename std::tuple_element<Is, T>::type, (is_formattable<typename std::tuple_element<Is, T>::type,
@ -292,21 +244,32 @@ FMT_CONSTEXPR auto maybe_set_debug_format(Formatter& f, bool set)
template <typename Formatter> template <typename Formatter>
FMT_CONSTEXPR void maybe_set_debug_format(Formatter&, ...) {} FMT_CONSTEXPR void maybe_set_debug_format(Formatter&, ...) {}
template <typename T>
struct range_format_kind_
: std::integral_constant<range_format,
std::is_same<uncvref_type<T>, T>::value
? range_format::disabled
: is_map<T>::value ? range_format::map
: is_set<T>::value ? range_format::set
: range_format::sequence> {};
template <range_format K>
using range_format_constant = std::integral_constant<range_format, K>;
// These are not generic lambdas for compatibility with C++11. // These are not generic lambdas for compatibility with C++11.
template <typename ParseContext> struct parse_empty_specs { template <typename Char> struct parse_empty_specs {
template <typename Formatter> FMT_CONSTEXPR void operator()(Formatter& f) { template <typename Formatter> FMT_CONSTEXPR void operator()(Formatter& f) {
f.parse(ctx); f.parse(ctx);
detail::maybe_set_debug_format(f, true); detail::maybe_set_debug_format(f, true);
} }
ParseContext& ctx; parse_context<Char>& ctx;
}; };
template <typename FormatContext> struct format_tuple_element { template <typename FormatContext> struct format_tuple_element {
using char_type = typename FormatContext::char_type; using char_type = typename FormatContext::char_type;
template <typename T> template <typename T>
void operator()(const formatter<T, char_type>& f, const T& v) { void operator()(const formatter<T, char_type>& f, const T& v) {
if (i > 0) if (i > 0) ctx.advance_to(detail::copy<char_type>(separator, ctx.out()));
ctx.advance_to(detail::copy_str<char_type>(separator, ctx.out()));
ctx.advance_to(f.format(v, ctx)); ctx.advance_to(f.format(v, ctx));
++i; ++i;
} }
@ -355,66 +318,48 @@ struct formatter<Tuple, Char,
closing_bracket_ = close; closing_bracket_ = close;
} }
template <typename ParseContext> FMT_CONSTEXPR auto parse(parse_context<Char>& ctx) -> const Char* {
FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
auto it = ctx.begin(); auto it = ctx.begin();
if (it != ctx.end() && *it != '}') auto end = ctx.end();
FMT_THROW(format_error("invalid format specifier")); if (it != end && detail::to_ascii(*it) == 'n') {
detail::for_each(formatters_, detail::parse_empty_specs<ParseContext>{ctx}); ++it;
set_brackets({}, {});
set_separator({});
}
if (it != end && *it != '}') report_error("invalid format specifier");
ctx.advance_to(it);
detail::for_each(formatters_, detail::parse_empty_specs<Char>{ctx});
return it; return it;
} }
template <typename FormatContext> template <typename FormatContext>
auto format(const Tuple& value, FormatContext& ctx) const auto format(const Tuple& value, FormatContext& ctx) const
-> decltype(ctx.out()) { -> decltype(ctx.out()) {
ctx.advance_to(detail::copy_str<Char>(opening_bracket_, ctx.out())); ctx.advance_to(detail::copy<Char>(opening_bracket_, ctx.out()));
detail::for_each2( detail::for_each2(
formatters_, value, formatters_, value,
detail::format_tuple_element<FormatContext>{0, ctx, separator_}); detail::format_tuple_element<FormatContext>{0, ctx, separator_});
return detail::copy_str<Char>(closing_bracket_, ctx.out()); return detail::copy<Char>(closing_bracket_, ctx.out());
} }
}; };
template <typename T, typename Char> struct is_range { template <typename T, typename Char> struct is_range {
static constexpr const bool value = static constexpr const bool value =
detail::is_range_<T>::value && !detail::is_std_string_like<T>::value && detail::is_range_<T>::value && !detail::has_to_string_view<T>::value;
!std::is_convertible<T, std::basic_string<Char>>::value &&
!std::is_convertible<T, detail::std_string_view<Char>>::value;
}; };
namespace detail { namespace detail {
template <typename Context> struct range_mapper {
using mapper = arg_mapper<Context>;
template <typename T,
FMT_ENABLE_IF(has_formatter<remove_cvref_t<T>, Context>::value)>
static auto map(T&& value) -> T&& {
return static_cast<T&&>(value);
}
template <typename T,
FMT_ENABLE_IF(!has_formatter<remove_cvref_t<T>, Context>::value)>
static auto map(T&& value)
-> decltype(mapper().map(static_cast<T&&>(value))) {
return mapper().map(static_cast<T&&>(value));
}
};
template <typename Char, typename Element> template <typename Char, typename Element>
using range_formatter_type = using range_formatter_type = formatter<remove_cvref_t<Element>, Char>;
formatter<remove_cvref_t<decltype(range_mapper<buffer_context<Char>>{}.map(
std::declval<Element>()))>,
Char>;
template <typename R> template <typename R>
using maybe_const_range = using maybe_const_range =
conditional_t<has_const_begin_end<R>::value, const R, R>; conditional_t<has_const_begin_end<R>::value, const R, R>;
// Workaround a bug in MSVC 2015 and earlier.
#if !FMT_MSC_VERSION || FMT_MSC_VERSION >= 1910
template <typename R, typename Char> template <typename R, typename Char>
struct is_formattable_delayed struct is_formattable_delayed
: is_formattable<uncvref_type<maybe_const_range<R>>, Char> {}; : is_formattable<uncvref_type<maybe_const_range<R>>, Char> {};
#endif
} // namespace detail } // namespace detail
template <typename...> struct conjunction : std::true_type {}; template <typename...> struct conjunction : std::true_type {};
@ -438,6 +383,24 @@ struct range_formatter<
detail::string_literal<Char, '['>{}; detail::string_literal<Char, '['>{};
basic_string_view<Char> closing_bracket_ = basic_string_view<Char> closing_bracket_ =
detail::string_literal<Char, ']'>{}; detail::string_literal<Char, ']'>{};
bool is_debug = false;
template <typename Output, typename It, typename Sentinel, typename U = T,
FMT_ENABLE_IF(std::is_same<U, Char>::value)>
auto write_debug_string(Output& out, It it, Sentinel end) const -> Output {
auto buf = basic_memory_buffer<Char>();
for (; it != end; ++it) buf.push_back(*it);
auto specs = format_specs();
specs.set_type(presentation_type::debug);
return detail::write<Char>(
out, basic_string_view<Char>(buf.data(), buf.size()), specs);
}
template <typename Output, typename It, typename Sentinel, typename U = T,
FMT_ENABLE_IF(!std::is_same<U, Char>::value)>
auto write_debug_string(Output& out, It, Sentinel) const -> Output {
return out;
}
public: public:
FMT_CONSTEXPR range_formatter() {} FMT_CONSTEXPR range_formatter() {}
@ -456,21 +419,40 @@ struct range_formatter<
closing_bracket_ = close; closing_bracket_ = close;
} }
template <typename ParseContext> FMT_CONSTEXPR auto parse(parse_context<Char>& ctx) -> const Char* {
FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
auto it = ctx.begin(); auto it = ctx.begin();
auto end = ctx.end(); auto end = ctx.end();
detail::maybe_set_debug_format(underlying_, true);
if (it == end) return underlying_.parse(ctx);
if (it != end && *it == 'n') { switch (detail::to_ascii(*it)) {
case 'n':
set_brackets({}, {}); set_brackets({}, {});
++it; ++it;
break;
case '?':
is_debug = true;
set_brackets({}, {});
++it;
if (it == end || *it != 's') report_error("invalid format specifier");
FMT_FALLTHROUGH;
case 's':
if (!std::is_same<T, Char>::value)
report_error("invalid format specifier");
if (!is_debug) {
set_brackets(detail::string_literal<Char, '"'>{},
detail::string_literal<Char, '"'>{});
set_separator({});
detail::maybe_set_debug_format(underlying_, false);
}
++it;
return it;
} }
if (it != end && *it != '}') { if (it != end && *it != '}') {
if (*it != ':') FMT_THROW(format_error("invalid format specifier")); if (*it != ':') report_error("invalid format specifier");
detail::maybe_set_debug_format(underlying_, false);
++it; ++it;
} else {
detail::maybe_set_debug_format(underlying_, true);
} }
ctx.advance_to(it); ctx.advance_to(it);
@ -479,80 +461,26 @@ struct range_formatter<
template <typename R, typename FormatContext> template <typename R, typename FormatContext>
auto format(R&& range, FormatContext& ctx) const -> decltype(ctx.out()) { auto format(R&& range, FormatContext& ctx) const -> decltype(ctx.out()) {
detail::range_mapper<buffer_context<Char>> mapper;
auto out = ctx.out(); auto out = ctx.out();
out = detail::copy_str<Char>(opening_bracket_, out);
int i = 0;
auto it = detail::range_begin(range); auto it = detail::range_begin(range);
auto end = detail::range_end(range); auto end = detail::range_end(range);
if (is_debug) return write_debug_string(out, std::move(it), end);
out = detail::copy<Char>(opening_bracket_, out);
int i = 0;
for (; it != end; ++it) { for (; it != end; ++it) {
if (i > 0) out = detail::copy_str<Char>(separator_, out); if (i > 0) out = detail::copy<Char>(separator_, out);
ctx.advance_to(out); ctx.advance_to(out);
auto&& item = *it; auto&& item = *it; // Need an lvalue
out = underlying_.format(mapper.map(item), ctx); out = underlying_.format(item, ctx);
++i; ++i;
} }
out = detail::copy_str<Char>(closing_bracket_, out); out = detail::copy<Char>(closing_bracket_, out);
return out; return out;
} }
}; };
enum class range_format { disabled, map, set, sequence, string, debug_string }; FMT_EXPORT
namespace detail {
template <typename T>
struct range_format_kind_
: std::integral_constant<range_format,
std::is_same<uncvref_type<T>, T>::value
? range_format::disabled
: is_map<T>::value ? range_format::map
: is_set<T>::value ? range_format::set
: range_format::sequence> {};
template <range_format K, typename R, typename Char, typename Enable = void>
struct range_default_formatter;
template <range_format K>
using range_format_constant = std::integral_constant<range_format, K>;
template <range_format K, typename R, typename Char>
struct range_default_formatter<
K, R, Char,
enable_if_t<(K == range_format::sequence || K == range_format::map ||
K == range_format::set)>> {
using range_type = detail::maybe_const_range<R>;
range_formatter<detail::uncvref_type<range_type>, Char> underlying_;
FMT_CONSTEXPR range_default_formatter() { init(range_format_constant<K>()); }
FMT_CONSTEXPR void init(range_format_constant<range_format::set>) {
underlying_.set_brackets(detail::string_literal<Char, '{'>{},
detail::string_literal<Char, '}'>{});
}
FMT_CONSTEXPR void init(range_format_constant<range_format::map>) {
underlying_.set_brackets(detail::string_literal<Char, '{'>{},
detail::string_literal<Char, '}'>{});
underlying_.underlying().set_brackets({}, {});
underlying_.underlying().set_separator(
detail::string_literal<Char, ':', ' '>{});
}
FMT_CONSTEXPR void init(range_format_constant<range_format::sequence>) {}
template <typename ParseContext>
FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
return underlying_.parse(ctx);
}
template <typename FormatContext>
auto format(range_type& range, FormatContext& ctx) const
-> decltype(ctx.out()) {
return underlying_.format(range, ctx);
}
};
} // namespace detail
template <typename T, typename Char, typename Enable = void> template <typename T, typename Char, typename Enable = void>
struct range_format_kind struct range_format_kind
: conditional_t< : conditional_t<
@ -562,23 +490,189 @@ struct range_format_kind
template <typename R, typename Char> template <typename R, typename Char>
struct formatter< struct formatter<
R, Char, R, Char,
enable_if_t<conjunction<bool_constant<range_format_kind<R, Char>::value != enable_if_t<conjunction<
range_format::disabled> bool_constant<
// Workaround a bug in MSVC 2015 and earlier. range_format_kind<R, Char>::value != range_format::disabled &&
#if !FMT_MSC_VERSION || FMT_MSC_VERSION >= 1910 range_format_kind<R, Char>::value != range_format::map &&
, range_format_kind<R, Char>::value != range_format::string &&
detail::is_formattable_delayed<R, Char> range_format_kind<R, Char>::value != range_format::debug_string>,
#endif detail::is_formattable_delayed<R, Char>>::value>> {
>::value>> private:
: detail::range_default_formatter<range_format_kind<R, Char>::value, R, using range_type = detail::maybe_const_range<R>;
Char> { range_formatter<detail::uncvref_type<range_type>, Char> range_formatter_;
public:
using nonlocking = void;
FMT_CONSTEXPR formatter() {
if (detail::const_check(range_format_kind<R, Char>::value !=
range_format::set))
return;
range_formatter_.set_brackets(detail::string_literal<Char, '{'>{},
detail::string_literal<Char, '}'>{});
}
FMT_CONSTEXPR auto parse(parse_context<Char>& ctx) -> const Char* {
return range_formatter_.parse(ctx);
}
template <typename FormatContext>
auto format(range_type& range, FormatContext& ctx) const
-> decltype(ctx.out()) {
return range_formatter_.format(range, ctx);
}
}; };
template <typename Char, typename... T> struct tuple_join_view : detail::view { // A map formatter.
const std::tuple<T...>& tuple; template <typename R, typename Char>
struct formatter<
R, Char,
enable_if_t<range_format_kind<R, Char>::value == range_format::map>> {
private:
using map_type = detail::maybe_const_range<R>;
using element_type = detail::uncvref_type<map_type>;
decltype(detail::tuple::get_formatters<element_type, Char>(
detail::tuple_index_sequence<element_type>())) formatters_;
bool no_delimiters_ = false;
public:
FMT_CONSTEXPR formatter() {}
FMT_CONSTEXPR auto parse(parse_context<Char>& ctx) -> const Char* {
auto it = ctx.begin();
auto end = ctx.end();
if (it != end) {
if (detail::to_ascii(*it) == 'n') {
no_delimiters_ = true;
++it;
}
if (it != end && *it != '}') {
if (*it != ':') report_error("invalid format specifier");
++it;
}
ctx.advance_to(it);
}
detail::for_each(formatters_, detail::parse_empty_specs<Char>{ctx});
return it;
}
template <typename FormatContext>
auto format(map_type& map, FormatContext& ctx) const -> decltype(ctx.out()) {
auto out = ctx.out();
basic_string_view<Char> open = detail::string_literal<Char, '{'>{};
if (!no_delimiters_) out = detail::copy<Char>(open, out);
int i = 0;
basic_string_view<Char> sep = detail::string_literal<Char, ',', ' '>{};
for (auto&& value : map) {
if (i > 0) out = detail::copy<Char>(sep, out);
ctx.advance_to(out);
detail::for_each2(formatters_, value,
detail::format_tuple_element<FormatContext>{
0, ctx, detail::string_literal<Char, ':', ' '>{}});
++i;
}
basic_string_view<Char> close = detail::string_literal<Char, '}'>{};
if (!no_delimiters_) out = detail::copy<Char>(close, out);
return out;
}
};
// A (debug_)string formatter.
template <typename R, typename Char>
struct formatter<
R, Char,
enable_if_t<range_format_kind<R, Char>::value == range_format::string ||
range_format_kind<R, Char>::value ==
range_format::debug_string>> {
private:
using range_type = detail::maybe_const_range<R>;
using string_type =
conditional_t<std::is_constructible<
detail::std_string_view<Char>,
decltype(detail::range_begin(std::declval<R>())),
decltype(detail::range_end(std::declval<R>()))>::value,
detail::std_string_view<Char>, std::basic_string<Char>>;
formatter<string_type, Char> underlying_;
public:
FMT_CONSTEXPR auto parse(parse_context<Char>& ctx) -> const Char* {
return underlying_.parse(ctx);
}
template <typename FormatContext>
auto format(range_type& range, FormatContext& ctx) const
-> decltype(ctx.out()) {
auto out = ctx.out();
if (detail::const_check(range_format_kind<R, Char>::value ==
range_format::debug_string))
*out++ = '"';
out = underlying_.format(
string_type{detail::range_begin(range), detail::range_end(range)}, ctx);
if (detail::const_check(range_format_kind<R, Char>::value ==
range_format::debug_string))
*out++ = '"';
return out;
}
};
template <typename It, typename Sentinel, typename Char = char>
struct join_view : detail::view {
It begin;
Sentinel end;
basic_string_view<Char> sep; basic_string_view<Char> sep;
tuple_join_view(const std::tuple<T...>& t, basic_string_view<Char> s) join_view(It b, Sentinel e, basic_string_view<Char> s)
: begin(std::move(b)), end(e), sep(s) {}
};
template <typename It, typename Sentinel, typename Char>
struct formatter<join_view<It, Sentinel, Char>, Char> {
private:
using value_type =
#ifdef __cpp_lib_ranges
std::iter_value_t<It>;
#else
typename std::iterator_traits<It>::value_type;
#endif
formatter<remove_cvref_t<value_type>, Char> value_formatter_;
using view = conditional_t<std::is_copy_constructible<It>::value,
const join_view<It, Sentinel, Char>,
join_view<It, Sentinel, Char>>;
public:
using nonlocking = void;
FMT_CONSTEXPR auto parse(parse_context<Char>& ctx) -> const Char* {
return value_formatter_.parse(ctx);
}
template <typename FormatContext>
auto format(view& value, FormatContext& ctx) const -> decltype(ctx.out()) {
using iter =
conditional_t<std::is_copy_constructible<view>::value, It, It&>;
iter it = value.begin;
auto out = ctx.out();
if (it == value.end) return out;
out = value_formatter_.format(*it, ctx);
++it;
while (it != value.end) {
out = detail::copy<Char>(value.sep.begin(), value.sep.end(), out);
ctx.advance_to(out);
out = value_formatter_.format(*it, ctx);
++it;
}
return out;
}
};
template <typename Char, typename Tuple> struct tuple_join_view : detail::view {
const Tuple& tuple;
basic_string_view<Char> sep;
tuple_join_view(const Tuple& t, basic_string_view<Char> s)
: tuple(t), sep{s} {} : tuple(t), sep{s} {}
}; };
@ -589,65 +683,64 @@ template <typename Char, typename... T> struct tuple_join_view : detail::view {
# define FMT_TUPLE_JOIN_SPECIFIERS 0 # define FMT_TUPLE_JOIN_SPECIFIERS 0
#endif #endif
template <typename Char, typename... T> template <typename Char, typename Tuple>
struct formatter<tuple_join_view<Char, T...>, Char> { struct formatter<tuple_join_view<Char, Tuple>, Char,
template <typename ParseContext> enable_if_t<is_tuple_like<Tuple>::value>> {
FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) { FMT_CONSTEXPR auto parse(parse_context<Char>& ctx) -> const Char* {
return do_parse(ctx, std::integral_constant<size_t, sizeof...(T)>()); return do_parse(ctx, std::tuple_size<Tuple>());
} }
template <typename FormatContext> template <typename FormatContext>
auto format(const tuple_join_view<Char, T...>& value, auto format(const tuple_join_view<Char, Tuple>& value,
FormatContext& ctx) const -> typename FormatContext::iterator { FormatContext& ctx) const -> typename FormatContext::iterator {
return do_format(value, ctx, return do_format(value, ctx, std::tuple_size<Tuple>());
std::integral_constant<size_t, sizeof...(T)>());
} }
private: private:
std::tuple<formatter<typename std::decay<T>::type, Char>...> formatters_; decltype(detail::tuple::get_formatters<Tuple, Char>(
detail::tuple_index_sequence<Tuple>())) formatters_;
template <typename ParseContext> FMT_CONSTEXPR auto do_parse(parse_context<Char>& ctx,
FMT_CONSTEXPR auto do_parse(ParseContext& ctx,
std::integral_constant<size_t, 0>) std::integral_constant<size_t, 0>)
-> decltype(ctx.begin()) { -> const Char* {
return ctx.begin(); return ctx.begin();
} }
template <typename ParseContext, size_t N> template <size_t N>
FMT_CONSTEXPR auto do_parse(ParseContext& ctx, FMT_CONSTEXPR auto do_parse(parse_context<Char>& ctx,
std::integral_constant<size_t, N>) std::integral_constant<size_t, N>)
-> decltype(ctx.begin()) { -> const Char* {
auto end = ctx.begin(); auto end = ctx.begin();
#if FMT_TUPLE_JOIN_SPECIFIERS #if FMT_TUPLE_JOIN_SPECIFIERS
end = std::get<sizeof...(T) - N>(formatters_).parse(ctx); end = std::get<std::tuple_size<Tuple>::value - N>(formatters_).parse(ctx);
if (N > 1) { if (N > 1) {
auto end1 = do_parse(ctx, std::integral_constant<size_t, N - 1>()); auto end1 = do_parse(ctx, std::integral_constant<size_t, N - 1>());
if (end != end1) if (end != end1)
FMT_THROW(format_error("incompatible format specs for tuple elements")); report_error("incompatible format specs for tuple elements");
} }
#endif #endif
return end; return end;
} }
template <typename FormatContext> template <typename FormatContext>
auto do_format(const tuple_join_view<Char, T...>&, FormatContext& ctx, auto do_format(const tuple_join_view<Char, Tuple>&, FormatContext& ctx,
std::integral_constant<size_t, 0>) const -> std::integral_constant<size_t, 0>) const ->
typename FormatContext::iterator { typename FormatContext::iterator {
return ctx.out(); return ctx.out();
} }
template <typename FormatContext, size_t N> template <typename FormatContext, size_t N>
auto do_format(const tuple_join_view<Char, T...>& value, FormatContext& ctx, auto do_format(const tuple_join_view<Char, Tuple>& value, FormatContext& ctx,
std::integral_constant<size_t, N>) const -> std::integral_constant<size_t, N>) const ->
typename FormatContext::iterator { typename FormatContext::iterator {
auto out = std::get<sizeof...(T) - N>(formatters_) using std::get;
.format(std::get<sizeof...(T) - N>(value.tuple), ctx); auto out =
if (N > 1) { std::get<std::tuple_size<Tuple>::value - N>(formatters_)
out = std::copy(value.sep.begin(), value.sep.end(), out); .format(get<std::tuple_size<Tuple>::value - N>(value.tuple), ctx);
ctx.advance_to(out); if (N <= 1) return out;
return do_format(value, ctx, std::integral_constant<size_t, N - 1>()); out = detail::copy<Char>(value.sep, out);
} ctx.advance_to(out);
return out; return do_format(value, ctx, std::integral_constant<size_t, N - 1>());
} }
}; };
@ -691,40 +784,57 @@ struct formatter<
FMT_BEGIN_EXPORT FMT_BEGIN_EXPORT
/// Returns a view that formats the iterator range `[begin, end)` with elements
/// separated by `sep`.
template <typename It, typename Sentinel>
auto join(It begin, Sentinel end, string_view sep) -> join_view<It, Sentinel> {
return {std::move(begin), end, sep};
}
/** /**
\rst * Returns a view that formats `range` with elements separated by `sep`.
Returns an object that formats `tuple` with elements separated by `sep`. *
* **Example**:
**Example**:: *
* auto v = std::vector<int>{1, 2, 3};
std::tuple<int, char> t = {1, 'a'}; * fmt::print("{}", fmt::join(v, ", "));
fmt::print("{}", fmt::join(t, ", ")); * // Output: 1, 2, 3
// Output: "1, a" *
\endrst * `fmt::join` applies passed format specifiers to the range elements:
*
* fmt::print("{:02}", fmt::join(v, ", "));
* // Output: 01, 02, 03
*/ */
template <typename... T> template <typename Range, FMT_ENABLE_IF(!is_tuple_like<Range>::value)>
FMT_CONSTEXPR auto join(const std::tuple<T...>& tuple, string_view sep) auto join(Range&& r, string_view sep)
-> tuple_join_view<char, T...> { -> join_view<decltype(detail::range_begin(r)),
return {tuple, sep}; decltype(detail::range_end(r))> {
return {detail::range_begin(r), detail::range_end(r), sep};
} }
template <typename... T> /**
FMT_CONSTEXPR auto join(const std::tuple<T...>& tuple, * Returns an object that formats `std::tuple` with elements separated by `sep`.
basic_string_view<wchar_t> sep) *
-> tuple_join_view<wchar_t, T...> { * **Example**:
*
* auto t = std::tuple<int, char>{1, 'a'};
* fmt::print("{}", fmt::join(t, ", "));
* // Output: 1, a
*/
template <typename Tuple, FMT_ENABLE_IF(is_tuple_like<Tuple>::value)>
FMT_CONSTEXPR auto join(const Tuple& tuple, string_view sep)
-> tuple_join_view<char, Tuple> {
return {tuple, sep}; return {tuple, sep};
} }
/** /**
\rst * Returns an object that formats `std::initializer_list` with elements
Returns an object that formats `initializer_list` with elements separated by * separated by `sep`.
`sep`. *
* **Example**:
**Example**:: *
* fmt::print("{}", fmt::join({1, 2, 3}, ", "));
fmt::print("{}", fmt::join({1, 2, 3}, ", ")); * // Output: "1, 2, 3"
// Output: "1, 2, 3"
\endrst
*/ */
template <typename T> template <typename T>
auto join(std::initializer_list<T> list, string_view sep) auto join(std::initializer_list<T> list, string_view sep)

View File

@ -8,39 +8,49 @@
#ifndef FMT_STD_H_ #ifndef FMT_STD_H_
#define FMT_STD_H_ #define FMT_STD_H_
#include <atomic>
#include <bitset>
#include <cstdlib>
#include <exception>
#include <memory>
#include <thread>
#include <type_traits>
#include <typeinfo>
#include <utility>
#include <vector>
#include "format.h" #include "format.h"
#include "ostream.h" #include "ostream.h"
#ifndef FMT_MODULE
# include <atomic>
# include <bitset>
# include <complex>
# include <cstdlib>
# include <exception>
# include <functional>
# include <memory>
# include <thread>
# include <type_traits>
# include <typeinfo>
# include <utility>
# include <vector>
// Check FMT_CPLUSPLUS to suppress a bogus warning in MSVC.
# if FMT_CPLUSPLUS >= 201703L
# if FMT_HAS_INCLUDE(<filesystem>) && \
(!defined(FMT_CPP_LIB_FILESYSTEM) || FMT_CPP_LIB_FILESYSTEM != 0)
# include <filesystem>
# endif
# if FMT_HAS_INCLUDE(<variant>)
# include <variant>
# endif
# if FMT_HAS_INCLUDE(<optional>)
# include <optional>
# endif
# endif
// Use > instead of >= in the version check because <source_location> may be
// available after C++17 but before C++20 is marked as implemented.
# if FMT_CPLUSPLUS > 201703L && FMT_HAS_INCLUDE(<source_location>)
# include <source_location>
# endif
# if FMT_CPLUSPLUS > 202002L && FMT_HAS_INCLUDE(<expected>)
# include <expected>
# endif
#endif // FMT_MODULE
#if FMT_HAS_INCLUDE(<version>) #if FMT_HAS_INCLUDE(<version>)
# include <version> # include <version>
#endif #endif
// Checking FMT_CPLUSPLUS for warning suppression in MSVC.
#if FMT_CPLUSPLUS >= 201703L
# if FMT_HAS_INCLUDE(<filesystem>)
# include <filesystem>
# endif
# if FMT_HAS_INCLUDE(<variant>)
# include <variant>
# endif
# if FMT_HAS_INCLUDE(<optional>)
# include <optional>
# endif
#endif
#if FMT_CPLUSPLUS > 201703L && FMT_HAS_INCLUDE(<source_location>)
# include <source_location>
#endif
// GCC 4 does not support FMT_HAS_INCLUDE. // GCC 4 does not support FMT_HAS_INCLUDE.
#if FMT_HAS_INCLUDE(<cxxabi.h>) || defined(__GLIBCXX__) #if FMT_HAS_INCLUDE(<cxxabi.h>) || defined(__GLIBCXX__)
@ -52,17 +62,6 @@
# endif # endif
#endif #endif
// Check if typeid is available.
#ifndef FMT_USE_TYPEID
// __RTTI is for EDG compilers. In MSVC typeid is available without RTTI.
# if defined(__GXX_RTTI) || FMT_HAS_FEATURE(cxx_rtti) || FMT_MSC_VERSION || \
defined(__INTEL_RTTI__) || defined(__RTTI)
# define FMT_USE_TYPEID 1
# else
# define FMT_USE_TYPEID 0
# endif
#endif
// For older Xcode versions, __cpp_lib_xxx flags are inaccurately defined. // For older Xcode versions, __cpp_lib_xxx flags are inaccurately defined.
#ifndef FMT_CPP_LIB_FILESYSTEM #ifndef FMT_CPP_LIB_FILESYSTEM
# ifdef __cpp_lib_filesystem # ifdef __cpp_lib_filesystem
@ -117,7 +116,7 @@ void write_escaped_path(basic_memory_buffer<Char>& quoted,
FMT_EXPORT FMT_EXPORT
template <typename Char> struct formatter<std::filesystem::path, Char> { template <typename Char> struct formatter<std::filesystem::path, Char> {
private: private:
format_specs<Char> specs_; format_specs specs_;
detail::arg_ref<Char> width_ref_; detail::arg_ref<Char> width_ref_;
bool debug_ = false; bool debug_ = false;
char path_type_ = 0; char path_type_ = 0;
@ -125,33 +124,33 @@ template <typename Char> struct formatter<std::filesystem::path, Char> {
public: public:
FMT_CONSTEXPR void set_debug_format(bool set = true) { debug_ = set; } FMT_CONSTEXPR void set_debug_format(bool set = true) { debug_ = set; }
template <typename ParseContext> FMT_CONSTEXPR auto parse(ParseContext& ctx) { FMT_CONSTEXPR auto parse(parse_context<Char>& ctx) {
auto it = ctx.begin(), end = ctx.end(); auto it = ctx.begin(), end = ctx.end();
if (it == end) return it; if (it == end) return it;
it = detail::parse_align(it, end, specs_); it = detail::parse_align(it, end, specs_);
if (it == end) return it; if (it == end) return it;
it = detail::parse_dynamic_spec(it, end, specs_.width, width_ref_, ctx); Char c = *it;
if ((c >= '0' && c <= '9') || c == '{')
it = detail::parse_width(it, end, specs_, width_ref_, ctx);
if (it != end && *it == '?') { if (it != end && *it == '?') {
debug_ = true; debug_ = true;
++it; ++it;
} }
if (it != end && (*it == 'g')) path_type_ = *it++; if (it != end && (*it == 'g')) path_type_ = detail::to_ascii(*it++);
return it; return it;
} }
template <typename FormatContext> template <typename FormatContext>
auto format(const std::filesystem::path& p, FormatContext& ctx) const { auto format(const std::filesystem::path& p, FormatContext& ctx) const {
auto specs = specs_; auto specs = specs_;
# ifdef _WIN32 auto path_string =
auto path_string = !path_type_ ? p.native() : p.generic_wstring(); !path_type_ ? p.native()
# else : p.generic_string<std::filesystem::path::value_type>();
auto path_string = !path_type_ ? p.native() : p.generic_string();
# endif
detail::handle_dynamic_spec<detail::width_checker>(specs.width, width_ref_, detail::handle_dynamic_spec(specs.dynamic_width(), specs.width, width_ref_,
ctx); ctx);
if (!debug_) { if (!debug_) {
auto s = detail::get_path_string<Char>(p, path_string); auto s = detail::get_path_string<Char>(p, path_string);
return detail::write(ctx.out(), basic_string_view<Char>(s), specs); return detail::write(ctx.out(), basic_string_view<Char>(s), specs);
@ -163,13 +162,30 @@ template <typename Char> struct formatter<std::filesystem::path, Char> {
specs); specs);
} }
}; };
class path : public std::filesystem::path {
public:
auto display_string() const -> std::string {
const std::filesystem::path& base = *this;
return fmt::format(FMT_STRING("{}"), base);
}
auto system_string() const -> std::string { return string(); }
auto generic_display_string() const -> std::string {
const std::filesystem::path& base = *this;
return fmt::format(FMT_STRING("{:g}"), base);
}
auto generic_system_string() const -> std::string { return generic_string(); }
};
FMT_END_NAMESPACE FMT_END_NAMESPACE
#endif // FMT_CPP_LIB_FILESYSTEM #endif // FMT_CPP_LIB_FILESYSTEM
FMT_BEGIN_NAMESPACE FMT_BEGIN_NAMESPACE
FMT_EXPORT FMT_EXPORT
template <std::size_t N, typename Char> template <std::size_t N, typename Char>
struct formatter<std::bitset<N>, Char> : nested_formatter<string_view> { struct formatter<std::bitset<N>, Char>
: nested_formatter<basic_string_view<Char>, Char> {
private: private:
// Functor because C++11 doesn't support generic lambdas. // Functor because C++11 doesn't support generic lambdas.
struct writer { struct writer {
@ -189,7 +205,7 @@ struct formatter<std::bitset<N>, Char> : nested_formatter<string_view> {
template <typename FormatContext> template <typename FormatContext>
auto format(const std::bitset<N>& bs, FormatContext& ctx) const auto format(const std::bitset<N>& bs, FormatContext& ctx) const
-> decltype(ctx.out()) { -> decltype(ctx.out()) {
return write_padded(ctx, writer{bs}); return this->write_padded(ctx, writer{bs});
} }
}; };
@ -222,7 +238,7 @@ struct formatter<std::optional<T>, Char,
FMT_CONSTEXPR static void maybe_set_debug_format(U&, ...) {} FMT_CONSTEXPR static void maybe_set_debug_format(U&, ...) {}
public: public:
template <typename ParseContext> FMT_CONSTEXPR auto parse(ParseContext& ctx) { FMT_CONSTEXPR auto parse(parse_context<Char>& ctx) {
maybe_set_debug_format(underlying_, true); maybe_set_debug_format(underlying_, true);
return underlying_.parse(ctx); return underlying_.parse(ctx);
} }
@ -242,13 +258,62 @@ struct formatter<std::optional<T>, Char,
FMT_END_NAMESPACE FMT_END_NAMESPACE
#endif // __cpp_lib_optional #endif // __cpp_lib_optional
#if defined(__cpp_lib_expected) || FMT_CPP_LIB_VARIANT
FMT_BEGIN_NAMESPACE
namespace detail {
template <typename Char, typename OutputIt, typename T>
auto write_escaped_alternative(OutputIt out, const T& v) -> OutputIt {
if constexpr (has_to_string_view<T>::value)
return write_escaped_string<Char>(out, detail::to_string_view(v));
if constexpr (std::is_same_v<T, Char>) return write_escaped_char(out, v);
return write<Char>(out, v);
}
} // namespace detail
FMT_END_NAMESPACE
#endif
#ifdef __cpp_lib_expected
FMT_BEGIN_NAMESPACE
FMT_EXPORT
template <typename T, typename E, typename Char>
struct formatter<std::expected<T, E>, Char,
std::enable_if_t<(std::is_void<T>::value ||
is_formattable<T, Char>::value) &&
is_formattable<E, Char>::value>> {
FMT_CONSTEXPR auto parse(parse_context<Char>& ctx) -> const Char* {
return ctx.begin();
}
template <typename FormatContext>
auto format(const std::expected<T, E>& value, FormatContext& ctx) const
-> decltype(ctx.out()) {
auto out = ctx.out();
if (value.has_value()) {
out = detail::write<Char>(out, "expected(");
if constexpr (!std::is_void<T>::value)
out = detail::write_escaped_alternative<Char>(out, *value);
} else {
out = detail::write<Char>(out, "unexpected(");
out = detail::write_escaped_alternative<Char>(out, value.error());
}
*out++ = ')';
return out;
}
};
FMT_END_NAMESPACE
#endif // __cpp_lib_expected
#ifdef __cpp_lib_source_location #ifdef __cpp_lib_source_location
FMT_BEGIN_NAMESPACE FMT_BEGIN_NAMESPACE
FMT_EXPORT FMT_EXPORT
template <> struct formatter<std::source_location> { template <> struct formatter<std::source_location> {
template <typename ParseContext> FMT_CONSTEXPR auto parse(ParseContext& ctx) { FMT_CONSTEXPR auto parse(parse_context<>& ctx) { return ctx.begin(); }
return ctx.begin();
}
template <typename FormatContext> template <typename FormatContext>
auto format(const std::source_location& loc, FormatContext& ctx) const auto format(const std::source_location& loc, FormatContext& ctx) const
@ -291,16 +356,6 @@ template <typename T, typename C> class is_variant_formattable_ {
decltype(check(variant_index_sequence<T>{}))::value; decltype(check(variant_index_sequence<T>{}))::value;
}; };
template <typename Char, typename OutputIt, typename T>
auto write_variant_alternative(OutputIt out, const T& v) -> OutputIt {
if constexpr (is_string<T>::value)
return write_escaped_string<Char>(out, detail::to_string_view(v));
else if constexpr (std::is_same_v<T, Char>)
return write_escaped_char(out, v);
else
return write<Char>(out, v);
}
} // namespace detail } // namespace detail
template <typename T> struct is_variant_like { template <typename T> struct is_variant_like {
@ -314,8 +369,7 @@ template <typename T, typename C> struct is_variant_formattable {
FMT_EXPORT FMT_EXPORT
template <typename Char> struct formatter<std::monostate, Char> { template <typename Char> struct formatter<std::monostate, Char> {
template <typename ParseContext> FMT_CONSTEXPR auto parse(parse_context<Char>& ctx) -> const Char* {
FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
return ctx.begin(); return ctx.begin();
} }
@ -332,8 +386,7 @@ struct formatter<
Variant, Char, Variant, Char,
std::enable_if_t<std::conjunction_v< std::enable_if_t<std::conjunction_v<
is_variant_like<Variant>, is_variant_formattable<Variant, Char>>>> { is_variant_like<Variant>, is_variant_formattable<Variant, Char>>>> {
template <typename ParseContext> FMT_CONSTEXPR auto parse(parse_context<Char>& ctx) -> const Char* {
FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
return ctx.begin(); return ctx.begin();
} }
@ -346,7 +399,7 @@ struct formatter<
FMT_TRY { FMT_TRY {
std::visit( std::visit(
[&](const auto& v) { [&](const auto& v) {
out = detail::write_variant_alternative<Char>(out, v); out = detail::write_escaped_alternative<Char>(out, v);
}, },
value); value);
} }
@ -362,23 +415,128 @@ FMT_END_NAMESPACE
FMT_BEGIN_NAMESPACE FMT_BEGIN_NAMESPACE
FMT_EXPORT FMT_EXPORT
template <typename Char> struct formatter<std::error_code, Char> { template <> struct formatter<std::error_code> {
template <typename ParseContext> private:
FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) { format_specs specs_;
return ctx.begin(); detail::arg_ref<char> width_ref_;
public:
FMT_CONSTEXPR auto parse(parse_context<>& ctx) -> const char* {
auto it = ctx.begin(), end = ctx.end();
if (it == end) return it;
it = detail::parse_align(it, end, specs_);
if (it == end) return it;
char c = *it;
if ((c >= '0' && c <= '9') || c == '{')
it = detail::parse_width(it, end, specs_, width_ref_, ctx);
return it;
} }
template <typename FormatContext> template <typename FormatContext>
FMT_CONSTEXPR auto format(const std::error_code& ec, FormatContext& ctx) const FMT_CONSTEXPR20 auto format(const std::error_code& ec,
-> decltype(ctx.out()) { FormatContext& ctx) const -> decltype(ctx.out()) {
auto out = ctx.out(); auto specs = specs_;
out = detail::write_bytes(out, ec.category().name(), format_specs<Char>()); detail::handle_dynamic_spec(specs.dynamic_width(), specs.width, width_ref_,
out = detail::write<Char>(out, Char(':')); ctx);
out = detail::write<Char>(out, ec.value()); memory_buffer buf;
return out; buf.append(string_view(ec.category().name()));
buf.push_back(':');
detail::write<char>(appender(buf), ec.value());
return detail::write<char>(ctx.out(), string_view(buf.data(), buf.size()),
specs);
} }
}; };
#if FMT_USE_RTTI
namespace detail {
template <typename Char, typename OutputIt>
auto write_demangled_name(OutputIt out, const std::type_info& ti) -> OutputIt {
# ifdef FMT_HAS_ABI_CXA_DEMANGLE
int status = 0;
std::size_t size = 0;
std::unique_ptr<char, void (*)(void*)> demangled_name_ptr(
abi::__cxa_demangle(ti.name(), nullptr, &size, &status), &std::free);
string_view demangled_name_view;
if (demangled_name_ptr) {
demangled_name_view = demangled_name_ptr.get();
// Normalization of stdlib inline namespace names.
// libc++ inline namespaces.
// std::__1::* -> std::*
// std::__1::__fs::* -> std::*
// libstdc++ inline namespaces.
// std::__cxx11::* -> std::*
// std::filesystem::__cxx11::* -> std::filesystem::*
if (demangled_name_view.starts_with("std::")) {
char* begin = demangled_name_ptr.get();
char* to = begin + 5; // std::
for (char *from = to, *end = begin + demangled_name_view.size();
from < end;) {
// This is safe, because demangled_name is NUL-terminated.
if (from[0] == '_' && from[1] == '_') {
char* next = from + 1;
while (next < end && *next != ':') next++;
if (next[0] == ':' && next[1] == ':') {
from = next + 2;
continue;
}
}
*to++ = *from++;
}
demangled_name_view = {begin, detail::to_unsigned(to - begin)};
}
} else {
demangled_name_view = string_view(ti.name());
}
return detail::write_bytes<Char>(out, demangled_name_view);
# elif FMT_MSC_VERSION
const string_view demangled_name(ti.name());
for (std::size_t i = 0; i < demangled_name.size(); ++i) {
auto sub = demangled_name;
sub.remove_prefix(i);
if (sub.starts_with("enum ")) {
i += 4;
continue;
}
if (sub.starts_with("class ") || sub.starts_with("union ")) {
i += 5;
continue;
}
if (sub.starts_with("struct ")) {
i += 6;
continue;
}
if (*sub.begin() != ' ') *out++ = *sub.begin();
}
return out;
# else
return detail::write_bytes<Char>(out, string_view(ti.name()));
# endif
}
} // namespace detail
FMT_EXPORT
template <typename Char>
struct formatter<std::type_info, Char // DEPRECATED! Mixing code unit types.
> {
public:
FMT_CONSTEXPR auto parse(parse_context<Char>& ctx) -> const Char* {
return ctx.begin();
}
template <typename Context>
auto format(const std::type_info& ti, Context& ctx) const
-> decltype(ctx.out()) {
return detail::write_demangled_name<Char>(ctx.out(), ti);
}
};
#endif
FMT_EXPORT FMT_EXPORT
template <typename T, typename Char> template <typename T, typename Char>
struct formatter< struct formatter<
@ -388,81 +546,29 @@ struct formatter<
bool with_typename_ = false; bool with_typename_ = false;
public: public:
FMT_CONSTEXPR auto parse(basic_format_parse_context<Char>& ctx) FMT_CONSTEXPR auto parse(parse_context<Char>& ctx) -> const Char* {
-> decltype(ctx.begin()) {
auto it = ctx.begin(); auto it = ctx.begin();
auto end = ctx.end(); auto end = ctx.end();
if (it == end || *it == '}') return it; if (it == end || *it == '}') return it;
if (*it == 't') { if (*it == 't') {
++it; ++it;
with_typename_ = FMT_USE_TYPEID != 0; with_typename_ = FMT_USE_RTTI != 0;
} }
return it; return it;
} }
template <typename OutputIt> template <typename Context>
auto format(const std::exception& ex, auto format(const std::exception& ex, Context& ctx) const
basic_format_context<OutputIt, Char>& ctx) const -> OutputIt { -> decltype(ctx.out()) {
format_specs<Char> spec;
auto out = ctx.out(); auto out = ctx.out();
if (!with_typename_) #if FMT_USE_RTTI
return detail::write_bytes(out, string_view(ex.what()), spec); if (with_typename_) {
out = detail::write_demangled_name<Char>(out, typeid(ex));
#if FMT_USE_TYPEID *out++ = ':';
const std::type_info& ti = typeid(ex); *out++ = ' ';
# ifdef FMT_HAS_ABI_CXA_DEMANGLE
int status = 0;
std::size_t size = 0;
std::unique_ptr<char, void (*)(void*)> demangled_name_ptr(
abi::__cxa_demangle(ti.name(), nullptr, &size, &status), &std::free);
string_view demangled_name_view;
if (demangled_name_ptr) {
demangled_name_view = demangled_name_ptr.get();
// Normalization of stdlib inline namespace names.
// libc++ inline namespaces.
// std::__1::* -> std::*
// std::__1::__fs::* -> std::*
// libstdc++ inline namespaces.
// std::__cxx11::* -> std::*
// std::filesystem::__cxx11::* -> std::filesystem::*
if (demangled_name_view.starts_with("std::")) {
char* begin = demangled_name_ptr.get();
char* to = begin + 5; // std::
for (char *from = to, *end = begin + demangled_name_view.size();
from < end;) {
// This is safe, because demangled_name is NUL-terminated.
if (from[0] == '_' && from[1] == '_') {
char* next = from + 1;
while (next < end && *next != ':') next++;
if (next[0] == ':' && next[1] == ':') {
from = next + 2;
continue;
}
}
*to++ = *from++;
}
demangled_name_view = {begin, detail::to_unsigned(to - begin)};
}
} else {
demangled_name_view = string_view(ti.name());
} }
out = detail::write_bytes(out, demangled_name_view, spec);
# elif FMT_MSC_VERSION
string_view demangled_name_view(ti.name());
if (demangled_name_view.starts_with("class "))
demangled_name_view.remove_prefix(6);
else if (demangled_name_view.starts_with("struct "))
demangled_name_view.remove_prefix(7);
out = detail::write_bytes(out, demangled_name_view, spec);
# else
out = detail::write_bytes(out, string_view(ti.name()), spec);
# endif
*out++ = ':';
*out++ = ' ';
return detail::write_bytes(out, string_view(ex.what()), spec);
#endif #endif
return detail::write_bytes<Char>(out, string_view(ex.what()));
} }
}; };
@ -509,6 +615,14 @@ struct formatter<BitRef, Char,
} }
}; };
template <typename T, typename Deleter>
auto ptr(const std::unique_ptr<T, Deleter>& p) -> const void* {
return p.get();
}
template <typename T> auto ptr(const std::shared_ptr<T>& p) -> const void* {
return p.get();
}
FMT_EXPORT FMT_EXPORT
template <typename T, typename Char> template <typename T, typename Char>
struct formatter<std::atomic<T>, Char, struct formatter<std::atomic<T>, Char,
@ -533,5 +647,80 @@ struct formatter<std::atomic_flag, Char> : formatter<bool, Char> {
}; };
#endif // __cpp_lib_atomic_flag_test #endif // __cpp_lib_atomic_flag_test
FMT_EXPORT
template <typename T, typename Char> struct formatter<std::complex<T>, Char> {
private:
detail::dynamic_format_specs<Char> specs_;
template <typename FormatContext, typename OutputIt>
FMT_CONSTEXPR auto do_format(const std::complex<T>& c,
detail::dynamic_format_specs<Char>& specs,
FormatContext& ctx, OutputIt out) const
-> OutputIt {
if (c.real() != 0) {
*out++ = Char('(');
out = detail::write<Char>(out, c.real(), specs, ctx.locale());
specs.set_sign(sign::plus);
out = detail::write<Char>(out, c.imag(), specs, ctx.locale());
if (!detail::isfinite(c.imag())) *out++ = Char(' ');
*out++ = Char('i');
*out++ = Char(')');
return out;
}
out = detail::write<Char>(out, c.imag(), specs, ctx.locale());
if (!detail::isfinite(c.imag())) *out++ = Char(' ');
*out++ = Char('i');
return out;
}
public:
FMT_CONSTEXPR auto parse(parse_context<Char>& ctx) -> const Char* {
if (ctx.begin() == ctx.end() || *ctx.begin() == '}') return ctx.begin();
return parse_format_specs(ctx.begin(), ctx.end(), specs_, ctx,
detail::type_constant<T, Char>::value);
}
template <typename FormatContext>
auto format(const std::complex<T>& c, FormatContext& ctx) const
-> decltype(ctx.out()) {
auto specs = specs_;
if (specs.dynamic()) {
detail::handle_dynamic_spec(specs.dynamic_width(), specs.width,
specs.width_ref, ctx);
detail::handle_dynamic_spec(specs.dynamic_precision(), specs.precision,
specs.precision_ref, ctx);
}
if (specs.width == 0) return do_format(c, specs, ctx, ctx.out());
auto buf = basic_memory_buffer<Char>();
auto outer_specs = format_specs();
outer_specs.width = specs.width;
outer_specs.copy_fill_from(specs);
outer_specs.set_align(specs.align());
specs.width = 0;
specs.set_fill({});
specs.set_align(align::none);
do_format(c, specs, ctx, basic_appender<Char>(buf));
return detail::write<Char>(ctx.out(),
basic_string_view<Char>(buf.data(), buf.size()),
outer_specs);
}
};
FMT_EXPORT
template <typename T, typename Char>
struct formatter<std::reference_wrapper<T>, Char,
enable_if_t<is_formattable<remove_cvref_t<T>, Char>::value>>
: formatter<remove_cvref_t<T>, Char> {
template <typename FormatContext>
auto format(std::reference_wrapper<T> ref, FormatContext& ctx) const
-> decltype(ctx.out()) {
return formatter<remove_cvref_t<T>, Char>::format(ref.get(), ctx);
}
};
FMT_END_NAMESPACE FMT_END_NAMESPACE
#endif // FMT_STD_H_ #endif // FMT_STD_H_

View File

@ -8,12 +8,16 @@
#ifndef FMT_XCHAR_H_ #ifndef FMT_XCHAR_H_
#define FMT_XCHAR_H_ #define FMT_XCHAR_H_
#include <cwchar> #include "color.h"
#include "format.h" #include "format.h"
#include "ostream.h"
#include "ranges.h"
#ifndef FMT_STATIC_THOUSANDS_SEPARATOR #ifndef FMT_MODULE
# include <locale> # include <cwchar>
# if FMT_USE_LOCALE
# include <locale>
# endif
#endif #endif
FMT_BEGIN_NAMESPACE FMT_BEGIN_NAMESPACE
@ -22,10 +26,26 @@ namespace detail {
template <typename T> template <typename T>
using is_exotic_char = bool_constant<!std::is_same<T, char>::value>; using is_exotic_char = bool_constant<!std::is_same<T, char>::value>;
inline auto write_loc(std::back_insert_iterator<detail::buffer<wchar_t>> out, template <typename S, typename = void> struct format_string_char {};
loc_value value, const format_specs<wchar_t>& specs,
locale_ref loc) -> bool { template <typename S>
#ifndef FMT_STATIC_THOUSANDS_SEPARATOR struct format_string_char<
S, void_t<decltype(sizeof(detail::to_string_view(std::declval<S>())))>> {
using type = char_t<S>;
};
template <typename S>
struct format_string_char<
S, enable_if_t<std::is_base_of<detail::compile_string, S>::value>> {
using type = typename S::char_type;
};
template <typename S>
using format_string_char_t = typename format_string_char<S>::type;
inline auto write_loc(basic_appender<wchar_t> out, loc_value value,
const format_specs& specs, locale_ref loc) -> bool {
#if FMT_USE_LOCALE
auto& numpunct = auto& numpunct =
std::use_facet<std::numpunct<wchar_t>>(loc.get<std::locale>()); std::use_facet<std::numpunct<wchar_t>>(loc.get<std::locale>());
auto separator = std::wstring(); auto separator = std::wstring();
@ -40,42 +60,79 @@ inline auto write_loc(std::back_insert_iterator<detail::buffer<wchar_t>> out,
FMT_BEGIN_EXPORT FMT_BEGIN_EXPORT
using wstring_view = basic_string_view<wchar_t>; using wstring_view = basic_string_view<wchar_t>;
using wformat_parse_context = basic_format_parse_context<wchar_t>; using wformat_parse_context = parse_context<wchar_t>;
using wformat_context = buffer_context<wchar_t>; using wformat_context = buffered_context<wchar_t>;
using wformat_args = basic_format_args<wformat_context>; using wformat_args = basic_format_args<wformat_context>;
using wmemory_buffer = basic_memory_buffer<wchar_t>; using wmemory_buffer = basic_memory_buffer<wchar_t>;
#if FMT_GCC_VERSION && FMT_GCC_VERSION < 409 template <typename Char, typename... T> struct basic_fstring {
// Workaround broken conversion on older gcc. private:
template <typename... Args> using wformat_string = wstring_view; basic_string_view<Char> str_;
inline auto runtime(wstring_view s) -> wstring_view { return s; }
#else static constexpr int num_static_named_args =
template <typename... Args> detail::count_static_named_args<T...>();
using wformat_string = basic_format_string<wchar_t, type_identity_t<Args>...>;
using checker = detail::format_string_checker<
Char, static_cast<int>(sizeof...(T)), num_static_named_args,
num_static_named_args != detail::count_named_args<T...>()>;
using arg_pack = detail::arg_pack<T...>;
public:
using t = basic_fstring;
template <typename S,
FMT_ENABLE_IF(
std::is_convertible<const S&, basic_string_view<Char>>::value)>
FMT_CONSTEVAL FMT_ALWAYS_INLINE basic_fstring(const S& s) : str_(s) {
if (FMT_USE_CONSTEVAL)
detail::parse_format_string<Char>(s, checker(s, arg_pack()));
}
template <typename S,
FMT_ENABLE_IF(std::is_base_of<detail::compile_string, S>::value&&
std::is_same<typename S::char_type, Char>::value)>
FMT_ALWAYS_INLINE basic_fstring(const S&) : str_(S()) {
FMT_CONSTEXPR auto sv = basic_string_view<Char>(S());
FMT_CONSTEXPR int ignore =
(parse_format_string(sv, checker(sv, arg_pack())), 0);
detail::ignore_unused(ignore);
}
basic_fstring(runtime_format_string<Char> fmt) : str_(fmt.str) {}
operator basic_string_view<Char>() const { return str_; }
auto get() const -> basic_string_view<Char> { return str_; }
};
template <typename Char, typename... T>
using basic_format_string = basic_fstring<Char, T...>;
template <typename... T>
using wformat_string = typename basic_format_string<wchar_t, T...>::t;
inline auto runtime(wstring_view s) -> runtime_format_string<wchar_t> { inline auto runtime(wstring_view s) -> runtime_format_string<wchar_t> {
return {{s}}; return {{s}};
} }
#endif
template <> struct is_char<wchar_t> : std::true_type {}; template <> struct is_char<wchar_t> : std::true_type {};
template <> struct is_char<detail::char8_type> : std::true_type {};
template <> struct is_char<char16_t> : std::true_type {}; template <> struct is_char<char16_t> : std::true_type {};
template <> struct is_char<char32_t> : std::true_type {}; template <> struct is_char<char32_t> : std::true_type {};
#ifdef __cpp_char8_t
template <> struct is_char<char8_t> : bool_constant<detail::is_utf8_enabled> {};
#endif
template <typename... T> template <typename... T>
constexpr auto make_wformat_args(const T&... args) constexpr auto make_wformat_args(T&... args)
-> format_arg_store<wformat_context, T...> { -> decltype(fmt::make_format_args<wformat_context>(args...)) {
return {args...}; return fmt::make_format_args<wformat_context>(args...);
} }
#if !FMT_USE_NONTYPE_TEMPLATE_ARGS
inline namespace literals { inline namespace literals {
#if FMT_USE_USER_DEFINED_LITERALS && !FMT_USE_NONTYPE_TEMPLATE_ARGS inline auto operator""_a(const wchar_t* s, size_t) -> detail::udl_arg<wchar_t> {
constexpr auto operator""_a(const wchar_t* s, size_t)
-> detail::udl_arg<wchar_t> {
return {s}; return {s};
} }
#endif
} // namespace literals } // namespace literals
#endif
template <typename It, typename Sentinel> template <typename It, typename Sentinel>
auto join(It begin, Sentinel end, wstring_view sep) auto join(It begin, Sentinel end, wstring_view sep)
@ -83,9 +140,9 @@ auto join(It begin, Sentinel end, wstring_view sep)
return {begin, end, sep}; return {begin, end, sep};
} }
template <typename Range> template <typename Range, FMT_ENABLE_IF(!is_tuple_like<Range>::value)>
auto join(Range&& range, wstring_view sep) auto join(Range&& range, wstring_view sep)
-> join_view<detail::iterator_t<Range>, detail::sentinel_t<Range>, -> join_view<decltype(std::begin(range)), decltype(std::end(range)),
wchar_t> { wchar_t> {
return join(std::begin(range), std::end(range), sep); return join(std::begin(range), std::end(range), sep);
} }
@ -96,13 +153,19 @@ auto join(std::initializer_list<T> list, wstring_view sep)
return join(std::begin(list), std::end(list), sep); return join(std::begin(list), std::end(list), sep);
} }
template <typename Tuple, FMT_ENABLE_IF(is_tuple_like<Tuple>::value)>
auto join(const Tuple& tuple, basic_string_view<wchar_t> sep)
-> tuple_join_view<wchar_t, Tuple> {
return {tuple, sep};
}
template <typename Char, FMT_ENABLE_IF(!std::is_same<Char, char>::value)> template <typename Char, FMT_ENABLE_IF(!std::is_same<Char, char>::value)>
auto vformat(basic_string_view<Char> format_str, auto vformat(basic_string_view<Char> fmt,
basic_format_args<buffer_context<type_identity_t<Char>>> args) typename detail::vformat_args<Char>::type args)
-> std::basic_string<Char> { -> std::basic_string<Char> {
auto buf = basic_memory_buffer<Char>(); auto buf = basic_memory_buffer<Char>();
detail::vformat_to(buf, format_str, args); detail::vformat_to(buf, fmt, args);
return to_string(buf); return {buf.data(), buf.size()};
} }
template <typename... T> template <typename... T>
@ -110,110 +173,122 @@ auto format(wformat_string<T...> fmt, T&&... args) -> std::wstring {
return vformat(fmt::wstring_view(fmt), fmt::make_wformat_args(args...)); return vformat(fmt::wstring_view(fmt), fmt::make_wformat_args(args...));
} }
template <typename OutputIt, typename... T>
auto format_to(OutputIt out, wformat_string<T...> fmt, T&&... args)
-> OutputIt {
return vformat_to(out, fmt::wstring_view(fmt),
fmt::make_wformat_args(args...));
}
// Pass char_t as a default template parameter instead of using // Pass char_t as a default template parameter instead of using
// std::basic_string<char_t<S>> to reduce the symbol size. // std::basic_string<char_t<S>> to reduce the symbol size.
template <typename S, typename... T, typename Char = char_t<S>, template <typename S, typename... T,
typename Char = detail::format_string_char_t<S>,
FMT_ENABLE_IF(!std::is_same<Char, char>::value && FMT_ENABLE_IF(!std::is_same<Char, char>::value &&
!std::is_same<Char, wchar_t>::value)> !std::is_same<Char, wchar_t>::value)>
auto format(const S& format_str, T&&... args) -> std::basic_string<Char> { auto format(const S& fmt, T&&... args) -> std::basic_string<Char> {
return vformat(detail::to_string_view(format_str), return vformat(detail::to_string_view(fmt),
fmt::make_format_args<buffer_context<Char>>(args...)); fmt::make_format_args<buffered_context<Char>>(args...));
} }
template <typename Locale, typename S, typename Char = char_t<S>, template <typename Locale, typename S,
typename Char = detail::format_string_char_t<S>,
FMT_ENABLE_IF(detail::is_locale<Locale>::value&& FMT_ENABLE_IF(detail::is_locale<Locale>::value&&
detail::is_exotic_char<Char>::value)> detail::is_exotic_char<Char>::value)>
inline auto vformat( inline auto vformat(const Locale& loc, const S& fmt,
const Locale& loc, const S& format_str, typename detail::vformat_args<Char>::type args)
basic_format_args<buffer_context<type_identity_t<Char>>> args)
-> std::basic_string<Char> { -> std::basic_string<Char> {
return detail::vformat(loc, detail::to_string_view(format_str), args); auto buf = basic_memory_buffer<Char>();
detail::vformat_to(buf, detail::to_string_view(fmt), args,
detail::locale_ref(loc));
return {buf.data(), buf.size()};
} }
template <typename Locale, typename S, typename... T, typename Char = char_t<S>, template <typename Locale, typename S, typename... T,
typename Char = detail::format_string_char_t<S>,
FMT_ENABLE_IF(detail::is_locale<Locale>::value&& FMT_ENABLE_IF(detail::is_locale<Locale>::value&&
detail::is_exotic_char<Char>::value)> detail::is_exotic_char<Char>::value)>
inline auto format(const Locale& loc, const S& format_str, T&&... args) inline auto format(const Locale& loc, const S& fmt, T&&... args)
-> std::basic_string<Char> { -> std::basic_string<Char> {
return detail::vformat(loc, detail::to_string_view(format_str), return vformat(loc, detail::to_string_view(fmt),
fmt::make_format_args<buffer_context<Char>>(args...)); fmt::make_format_args<buffered_context<Char>>(args...));
} }
template <typename OutputIt, typename S, typename Char = char_t<S>, template <typename OutputIt, typename S,
typename Char = detail::format_string_char_t<S>,
FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, Char>::value&& FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, Char>::value&&
detail::is_exotic_char<Char>::value)> detail::is_exotic_char<Char>::value)>
auto vformat_to(OutputIt out, const S& format_str, auto vformat_to(OutputIt out, const S& fmt,
basic_format_args<buffer_context<type_identity_t<Char>>> args) typename detail::vformat_args<Char>::type args) -> OutputIt {
-> OutputIt {
auto&& buf = detail::get_buffer<Char>(out); auto&& buf = detail::get_buffer<Char>(out);
detail::vformat_to(buf, detail::to_string_view(format_str), args); detail::vformat_to(buf, detail::to_string_view(fmt), args);
return detail::get_iterator(buf, out); return detail::get_iterator(buf, out);
} }
template <typename OutputIt, typename S, typename... T, template <typename OutputIt, typename S, typename... T,
typename Char = char_t<S>, typename Char = detail::format_string_char_t<S>,
FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, Char>::value&& FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, Char>::value &&
detail::is_exotic_char<Char>::value)> !std::is_same<Char, char>::value &&
!std::is_same<Char, wchar_t>::value)>
inline auto format_to(OutputIt out, const S& fmt, T&&... args) -> OutputIt { inline auto format_to(OutputIt out, const S& fmt, T&&... args) -> OutputIt {
return vformat_to(out, detail::to_string_view(fmt), return vformat_to(out, detail::to_string_view(fmt),
fmt::make_format_args<buffer_context<Char>>(args...)); fmt::make_format_args<buffered_context<Char>>(args...));
} }
template <typename Locale, typename S, typename OutputIt, typename... Args, template <typename Locale, typename S, typename OutputIt, typename... Args,
typename Char = char_t<S>, typename Char = detail::format_string_char_t<S>,
FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, Char>::value&& FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, Char>::value&&
detail::is_locale<Locale>::value&& detail::is_locale<Locale>::value&&
detail::is_exotic_char<Char>::value)> detail::is_exotic_char<Char>::value)>
inline auto vformat_to( inline auto vformat_to(OutputIt out, const Locale& loc, const S& fmt,
OutputIt out, const Locale& loc, const S& format_str, typename detail::vformat_args<Char>::type args)
basic_format_args<buffer_context<type_identity_t<Char>>> args) -> OutputIt { -> OutputIt {
auto&& buf = detail::get_buffer<Char>(out); auto&& buf = detail::get_buffer<Char>(out);
vformat_to(buf, detail::to_string_view(format_str), args, vformat_to(buf, detail::to_string_view(fmt), args, detail::locale_ref(loc));
detail::locale_ref(loc));
return detail::get_iterator(buf, out); return detail::get_iterator(buf, out);
} }
template <typename OutputIt, typename Locale, typename S, typename... T, template <typename Locale, typename OutputIt, typename S, typename... T,
typename Char = char_t<S>, typename Char = detail::format_string_char_t<S>,
bool enable = detail::is_output_iterator<OutputIt, Char>::value && bool enable = detail::is_output_iterator<OutputIt, Char>::value &&
detail::is_locale<Locale>::value && detail::is_locale<Locale>::value &&
detail::is_exotic_char<Char>::value> detail::is_exotic_char<Char>::value>
inline auto format_to(OutputIt out, const Locale& loc, const S& format_str, inline auto format_to(OutputIt out, const Locale& loc, const S& fmt,
T&&... args) -> T&&... args) ->
typename std::enable_if<enable, OutputIt>::type { typename std::enable_if<enable, OutputIt>::type {
return vformat_to(out, loc, detail::to_string_view(format_str), return vformat_to(out, loc, detail::to_string_view(fmt),
fmt::make_format_args<buffer_context<Char>>(args...)); fmt::make_format_args<buffered_context<Char>>(args...));
} }
template <typename OutputIt, typename Char, typename... Args, template <typename OutputIt, typename Char, typename... Args,
FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, Char>::value&& FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, Char>::value&&
detail::is_exotic_char<Char>::value)> detail::is_exotic_char<Char>::value)>
inline auto vformat_to_n( inline auto vformat_to_n(OutputIt out, size_t n, basic_string_view<Char> fmt,
OutputIt out, size_t n, basic_string_view<Char> format_str, typename detail::vformat_args<Char>::type args)
basic_format_args<buffer_context<type_identity_t<Char>>> args)
-> format_to_n_result<OutputIt> { -> format_to_n_result<OutputIt> {
using traits = detail::fixed_buffer_traits; using traits = detail::fixed_buffer_traits;
auto buf = detail::iterator_buffer<OutputIt, Char, traits>(out, n); auto buf = detail::iterator_buffer<OutputIt, Char, traits>(out, n);
detail::vformat_to(buf, format_str, args); detail::vformat_to(buf, fmt, args);
return {buf.out(), buf.count()}; return {buf.out(), buf.count()};
} }
template <typename OutputIt, typename S, typename... T, template <typename OutputIt, typename S, typename... T,
typename Char = char_t<S>, typename Char = detail::format_string_char_t<S>,
FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, Char>::value&& FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, Char>::value&&
detail::is_exotic_char<Char>::value)> detail::is_exotic_char<Char>::value)>
inline auto format_to_n(OutputIt out, size_t n, const S& fmt, T&&... args) inline auto format_to_n(OutputIt out, size_t n, const S& fmt, T&&... args)
-> format_to_n_result<OutputIt> { -> format_to_n_result<OutputIt> {
return vformat_to_n(out, n, detail::to_string_view(fmt), return vformat_to_n(out, n, fmt::basic_string_view<Char>(fmt),
fmt::make_format_args<buffer_context<Char>>(args...)); fmt::make_format_args<buffered_context<Char>>(args...));
} }
template <typename S, typename... T, typename Char = char_t<S>, template <typename S, typename... T,
typename Char = detail::format_string_char_t<S>,
FMT_ENABLE_IF(detail::is_exotic_char<Char>::value)> FMT_ENABLE_IF(detail::is_exotic_char<Char>::value)>
inline auto formatted_size(const S& fmt, T&&... args) -> size_t { inline auto formatted_size(const S& fmt, T&&... args) -> size_t {
auto buf = detail::counting_buffer<Char>(); auto buf = detail::counting_buffer<Char>();
detail::vformat_to(buf, detail::to_string_view(fmt), detail::vformat_to(buf, detail::to_string_view(fmt),
fmt::make_format_args<buffer_context<Char>>(args...)); fmt::make_format_args<buffered_context<Char>>(args...));
return buf.count(); return buf.count();
} }
@ -247,9 +322,48 @@ template <typename... T> void println(wformat_string<T...> fmt, T&&... args) {
return print(L"{}\n", fmt::format(fmt, std::forward<T>(args)...)); return print(L"{}\n", fmt::format(fmt, std::forward<T>(args)...));
} }
/** inline auto vformat(const text_style& ts, wstring_view fmt, wformat_args args)
Converts *value* to ``std::wstring`` using the default format for type *T*. -> std::wstring {
*/ auto buf = wmemory_buffer();
detail::vformat_to(buf, ts, fmt, args);
return {buf.data(), buf.size()};
}
template <typename... T>
inline auto format(const text_style& ts, wformat_string<T...> fmt, T&&... args)
-> std::wstring {
return fmt::vformat(ts, fmt, fmt::make_wformat_args(args...));
}
template <typename... T>
FMT_DEPRECATED void print(std::FILE* f, const text_style& ts,
wformat_string<T...> fmt, const T&... args) {
vprint(f, ts, fmt, fmt::make_wformat_args(args...));
}
template <typename... T>
FMT_DEPRECATED void print(const text_style& ts, wformat_string<T...> fmt,
const T&... args) {
return print(stdout, ts, fmt, args...);
}
inline void vprint(std::wostream& os, wstring_view fmt, wformat_args args) {
auto buffer = basic_memory_buffer<wchar_t>();
detail::vformat_to(buffer, fmt, args);
detail::write_buffer(os, buffer);
}
template <typename... T>
void print(std::wostream& os, wformat_string<T...> fmt, T&&... args) {
vprint(os, fmt, fmt::make_format_args<buffered_context<wchar_t>>(args...));
}
template <typename... T>
void println(std::wostream& os, wformat_string<T...> fmt, T&&... args) {
print(os, L"{}\n", fmt::format(fmt, std::forward<T>(args)...));
}
/// Converts `value` to `std::wstring` using the default format for type `T`.
template <typename T> inline auto to_wstring(const T& value) -> std::wstring { template <typename T> inline auto to_wstring(const T& value) -> std::wstring {
return format(FMT_STRING(L"{}"), value); return format(FMT_STRING(L"{}"), value);
} }

View File

@ -1,38 +1,57 @@
module; module;
#ifdef _MSVC_LANG
# define FMT_CPLUSPLUS _MSVC_LANG
#else
# define FMT_CPLUSPLUS __cplusplus
#endif
// Put all implementation-provided headers into the global module fragment // Put all implementation-provided headers into the global module fragment
// to prevent attachment to this module. // to prevent attachment to this module.
#include <algorithm> #ifndef FMT_IMPORT_STD
# include <algorithm>
# include <bitset>
# include <chrono>
# include <cmath>
# include <complex>
# include <cstddef>
# include <cstdint>
# include <cstdio>
# include <cstdlib>
# include <cstring>
# include <ctime>
# include <exception>
# if FMT_CPLUSPLUS > 202002L
# include <expected>
# endif
# include <filesystem>
# include <fstream>
# include <functional>
# include <iterator>
# include <limits>
# include <locale>
# include <memory>
# include <optional>
# include <ostream>
# include <source_location>
# include <stdexcept>
# include <string>
# include <string_view>
# include <system_error>
# include <thread>
# include <type_traits>
# include <typeinfo>
# include <utility>
# include <variant>
# include <vector>
#else
# include <limits.h>
# include <stdint.h>
# include <stdio.h>
# include <time.h>
#endif
#include <cerrno> #include <cerrno>
#include <chrono>
#include <climits> #include <climits>
#include <cmath>
#include <cstddef>
#include <cstdint>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <ctime>
#include <exception>
#include <filesystem>
#include <fstream>
#include <functional>
#include <iterator>
#include <limits>
#include <locale>
#include <memory>
#include <optional>
#include <ostream>
#include <stdexcept>
#include <string>
#include <string_view>
#include <system_error>
#include <thread>
#include <type_traits>
#include <typeinfo>
#include <utility>
#include <variant>
#include <vector>
#include <version> #include <version>
#if __has_include(<cxxabi.h>) #if __has_include(<cxxabi.h>)
@ -70,6 +89,10 @@ module;
export module fmt; export module fmt;
#ifdef FMT_IMPORT_STD
import std;
#endif
#define FMT_EXPORT export #define FMT_EXPORT export
#define FMT_BEGIN_EXPORT export { #define FMT_BEGIN_EXPORT export {
#define FMT_END_EXPORT } #define FMT_END_EXPORT }
@ -83,6 +106,10 @@ export module fmt;
extern "C++" { extern "C++" {
#endif #endif
#ifndef FMT_OS
# define FMT_OS 1
#endif
// All library-provided declarations and definitions must be in the module // All library-provided declarations and definitions must be in the module
// purview to be exported. // purview to be exported.
#include "fmt/args.h" #include "fmt/args.h"
@ -90,8 +117,12 @@ extern "C++" {
#include "fmt/color.h" #include "fmt/color.h"
#include "fmt/compile.h" #include "fmt/compile.h"
#include "fmt/format.h" #include "fmt/format.h"
#include "fmt/os.h" #if FMT_OS
# include "fmt/os.h"
#endif
#include "fmt/ostream.h"
#include "fmt/printf.h" #include "fmt/printf.h"
#include "fmt/ranges.h"
#include "fmt/std.h" #include "fmt/std.h"
#include "fmt/xchar.h" #include "fmt/xchar.h"
@ -104,5 +135,17 @@ extern "C++" {
module :private; module :private;
#endif #endif
#include "format.cc" #ifdef FMT_ATTACH_TO_GLOBAL_MODULE
#include "os.cc" extern "C++" {
#endif
#if FMT_HAS_INCLUDE("format.cc")
# include "format.cc"
#endif
#if FMT_OS && FMT_HAS_INCLUDE("os.cc")
# include "os.cc"
#endif
#ifdef FMT_ATTACH_TO_GLOBAL_MODULE
}
#endif

View File

@ -15,7 +15,8 @@ template FMT_API auto dragonbox::to_decimal(float x) noexcept
template FMT_API auto dragonbox::to_decimal(double x) noexcept template FMT_API auto dragonbox::to_decimal(double x) noexcept
-> dragonbox::decimal_fp<double>; -> dragonbox::decimal_fp<double>;
#ifndef FMT_STATIC_THOUSANDS_SEPARATOR #if FMT_USE_LOCALE
// DEPRECATED! locale_ref in the detail namespace
template FMT_API locale_ref::locale_ref(const std::locale& loc); template FMT_API locale_ref::locale_ref(const std::locale& loc);
template FMT_API auto locale_ref::get<std::locale>() const -> std::locale; template FMT_API auto locale_ref::get<std::locale>() const -> std::locale;
#endif #endif
@ -26,8 +27,10 @@ template FMT_API auto thousands_sep_impl(locale_ref)
-> thousands_sep_result<char>; -> thousands_sep_result<char>;
template FMT_API auto decimal_point_impl(locale_ref) -> char; template FMT_API auto decimal_point_impl(locale_ref) -> char;
// DEPRECATED!
template FMT_API void buffer<char>::append(const char*, const char*); template FMT_API void buffer<char>::append(const char*, const char*);
// DEPRECATED!
template FMT_API void vformat_to(buffer<char>&, string_view, template FMT_API void vformat_to(buffer<char>&, string_view,
typename vformat_args<>::type, locale_ref); typename vformat_args<>::type, locale_ref);

152
3rdparty/fmt/src/os.cc vendored
View File

@ -12,47 +12,51 @@
#include "fmt/os.h" #include "fmt/os.h"
#include <climits> #ifndef FMT_MODULE
# include <climits>
#if FMT_USE_FCNTL # if FMT_USE_FCNTL
# include <sys/stat.h> # include <sys/stat.h>
# include <sys/types.h> # include <sys/types.h>
# ifdef _WRS_KERNEL // VxWorks7 kernel # ifdef _WRS_KERNEL // VxWorks7 kernel
# include <ioLib.h> // getpagesize # include <ioLib.h> // getpagesize
# endif
# ifndef _WIN32
# include <unistd.h>
# else
# ifndef WIN32_LEAN_AND_MEAN
# define WIN32_LEAN_AND_MEAN
# endif
# include <io.h>
# endif // _WIN32
# endif // FMT_USE_FCNTL
# ifdef _WIN32
# include <windows.h>
# endif # endif
#endif
# ifndef _WIN32
# include <unistd.h>
# else
# ifndef WIN32_LEAN_AND_MEAN
# define WIN32_LEAN_AND_MEAN
# endif
# include <io.h>
# ifndef S_IRUSR
# define S_IRUSR _S_IREAD
# endif
# ifndef S_IWUSR
# define S_IWUSR _S_IWRITE
# endif
# ifndef S_IRGRP
# define S_IRGRP 0
# endif
# ifndef S_IWGRP
# define S_IWGRP 0
# endif
# ifndef S_IROTH
# define S_IROTH 0
# endif
# ifndef S_IWOTH
# define S_IWOTH 0
# endif
# endif // _WIN32
#endif // FMT_USE_FCNTL
#ifdef _WIN32 #ifdef _WIN32
# include <windows.h> # ifndef S_IRUSR
# define S_IRUSR _S_IREAD
# endif
# ifndef S_IWUSR
# define S_IWUSR _S_IWRITE
# endif
# ifndef S_IRGRP
# define S_IRGRP 0
# endif
# ifndef S_IWGRP
# define S_IWGRP 0
# endif
# ifndef S_IROTH
# define S_IROTH 0
# endif
# ifndef S_IWOTH
# define S_IWOTH 0
# endif
#endif #endif
namespace { namespace {
@ -156,7 +160,7 @@ void detail::format_windows_error(detail::buffer<char>& out, int error_code,
} }
void report_windows_error(int error_code, const char* message) noexcept { void report_windows_error(int error_code, const char* message) noexcept {
report_error(detail::format_windows_error, error_code, message); do_report_error(detail::format_windows_error, error_code, message);
} }
#endif // _WIN32 #endif // _WIN32
@ -182,12 +186,14 @@ void buffered_file::close() {
} }
int buffered_file::descriptor() const { int buffered_file::descriptor() const {
#if !defined(fileno) #ifdef FMT_HAS_SYSTEM
// fileno is a macro on OpenBSD.
# ifdef fileno
# undef fileno
# endif
int fd = FMT_POSIX_CALL(fileno(file_)); int fd = FMT_POSIX_CALL(fileno(file_));
#elif defined(FMT_HAS_SYSTEM) #elif defined(_WIN32)
// fileno is a macro on OpenBSD so we cannot use FMT_POSIX_CALL. int fd = _fileno(file_);
# define FMT_DISABLE_MACRO
int fd = FMT_SYSTEM(fileno FMT_DISABLE_MACRO(file_));
#else #else
int fd = fileno(file_); int fd = fileno(file_);
#endif #endif
@ -200,6 +206,7 @@ int buffered_file::descriptor() const {
# ifdef _WIN32 # ifdef _WIN32
using mode_t = int; using mode_t = int;
# endif # endif
constexpr mode_t default_open_mode = constexpr mode_t default_open_mode =
S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH; S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH;
@ -301,29 +308,6 @@ void file::dup2(int fd, std::error_code& ec) noexcept {
if (result == -1) ec = std::error_code(errno, std::generic_category()); if (result == -1) ec = std::error_code(errno, std::generic_category());
} }
void file::pipe(file& read_end, file& write_end) {
// Close the descriptors first to make sure that assignments don't throw
// and there are no leaks.
read_end.close();
write_end.close();
int fds[2] = {};
# ifdef _WIN32
// Make the default pipe capacity same as on Linux 2.6.11+.
enum { DEFAULT_CAPACITY = 65536 };
int result = FMT_POSIX_CALL(pipe(fds, DEFAULT_CAPACITY, _O_BINARY));
# else
// Don't retry as the pipe function doesn't return EINTR.
// http://pubs.opengroup.org/onlinepubs/009696799/functions/pipe.html
int result = FMT_POSIX_CALL(pipe(fds));
# endif
if (result != 0)
FMT_THROW(system_error(errno, FMT_STRING("cannot create pipe")));
// The following assignments don't throw because read_fd and write_fd
// are closed.
read_end = file(fds[0]);
write_end = file(fds[1]);
}
buffered_file file::fdopen(const char* mode) { buffered_file file::fdopen(const char* mode) {
// Don't retry as fdopen doesn't return EINTR. // Don't retry as fdopen doesn't return EINTR.
# if defined(__MINGW32__) && defined(_POSIX_) # if defined(__MINGW32__) && defined(_POSIX_)
@ -352,6 +336,24 @@ file file::open_windows_file(wcstring_view path, int oflag) {
} }
# endif # endif
pipe::pipe() {
int fds[2] = {};
# ifdef _WIN32
// Make the default pipe capacity same as on Linux 2.6.11+.
enum { DEFAULT_CAPACITY = 65536 };
int result = FMT_POSIX_CALL(pipe(fds, DEFAULT_CAPACITY, _O_BINARY));
# else
// Don't retry as the pipe function doesn't return EINTR.
// http://pubs.opengroup.org/onlinepubs/009696799/functions/pipe.html
int result = FMT_POSIX_CALL(pipe(fds));
# endif
if (result != 0)
FMT_THROW(system_error(errno, FMT_STRING("cannot create pipe")));
// The following assignments don't throw.
read_end = file(fds[0]);
write_end = file(fds[1]);
}
# if !defined(__MSDOS__) # if !defined(__MSDOS__)
long getpagesize() { long getpagesize() {
# ifdef _WIN32 # ifdef _WIN32
@ -372,31 +374,25 @@ long getpagesize() {
} }
# endif # endif
namespace detail { void ostream::grow(buffer<char>& buf, size_t) {
if (buf.size() == buf.capacity()) static_cast<ostream&>(buf).flush();
void file_buffer::grow(size_t) {
if (this->size() == this->capacity()) flush();
} }
file_buffer::file_buffer(cstring_view path, ostream::ostream(cstring_view path, const detail::ostream_params& params)
const detail::ostream_params& params) : buffer<char>(grow), file_(path, params.oflag) {
: file_(path, params.oflag) {
set(new char[params.buffer_size], params.buffer_size); set(new char[params.buffer_size], params.buffer_size);
} }
file_buffer::file_buffer(file_buffer&& other) ostream::ostream(ostream&& other) noexcept
: detail::buffer<char>(other.data(), other.size(), other.capacity()), : buffer<char>(grow, other.data(), other.size(), other.capacity()),
file_(std::move(other.file_)) { file_(std::move(other.file_)) {
other.clear(); other.clear();
other.set(nullptr, 0); other.set(nullptr, 0);
} }
file_buffer::~file_buffer() { ostream::~ostream() {
flush(); flush();
delete[] data(); delete[] data();
} }
} // namespace detail
ostream::~ostream() = default;
#endif // FMT_USE_FCNTL #endif // FMT_USE_FCNTL
FMT_END_NAMESPACE FMT_END_NAMESPACE

View File

@ -33957,6 +33957,8 @@ SLPM-61030:
region: "NTSC-J" region: "NTSC-J"
gsHWFixes: gsHWFixes:
recommendedBlendingLevel: 2 # Fixes text box opacity. recommendedBlendingLevel: 2 # Fixes text box opacity.
clampModes:
vu1ClampMode: 3 # Fixes shading on Chapter 11-2's enemy.
SLPM-61031: SLPM-61031:
name: "電撃PS2 / 電撃PlayStation D52" name: "電撃PS2 / 電撃PlayStation D52"
name-sort: "でんげき PS2 でんげきPlayStation D52" name-sort: "でんげき PS2 でんげきPlayStation D52"
@ -39516,6 +39518,8 @@ SLPM-65140:
region: "NTSC-J" region: "NTSC-J"
gsHWFixes: gsHWFixes:
recommendedBlendingLevel: 2 # Fixes text box opacity. recommendedBlendingLevel: 2 # Fixes text box opacity.
clampModes:
vu1ClampMode: 3 # Fixes shading on Chapter 11-2's enemy.
SLPM-65141: SLPM-65141:
name: "続せがれいじり 変珍たませがれ" name: "続せがれいじり 変珍たませがれ"
name-sort: "ぞくせがれいじり へんちんたませがれ" name-sort: "ぞくせがれいじり へんちんたませがれ"
@ -49740,10 +49744,12 @@ SLPM-66852:
SLPM-66853: SLPM-66853:
name: "ジョジョの奇妙な冒険 黄金の旋風 [Best Price]" name: "ジョジョの奇妙な冒険 黄金の旋風 [Best Price]"
name-sort: "じょじょのきみょうなぼうけん おうごんのせんぷう [Best Price]" name-sort: "じょじょのきみょうなぼうけん おうごんのせんぷう [Best Price]"
name-en: "Jojo no Kimyouna Bouken - Ougon no Kaze [Best Price]" name-en: "JoJo no Kimyou na Bouken - Ougon no Kaze [Best Price]"
region: "NTSC-J" region: "NTSC-J"
gsHWFixes: gsHWFixes:
recommendedBlendingLevel: 2 # Fixes text box opacity. recommendedBlendingLevel: 2 # Fixes text box opacity.
clampModes:
vu1ClampMode: 3 # Fixes shading on Chapter 11-2's enemy.
SLPM-66854: SLPM-66854:
name: "ストリートファイターゼロ ファイターズ ジェネレーション [Best Price]" name: "ストリートファイターゼロ ファイターズ ジェネレーション [Best Price]"
name-sort: "すとりーとふぁいたーぜろ ふぁいたーず じぇねれーしょん [Best Price]" name-sort: "すとりーとふぁいたーぜろ ふぁいたーず じぇねれーしょん [Best Price]"
@ -58419,7 +58425,7 @@ SLPS-25685:
SLPS-25686: SLPS-25686:
name: "ジョジョの奇妙な冒険 ファントムブラッド" name: "ジョジョの奇妙な冒険 ファントムブラッド"
name-sort: "じょじょのきみょうなぼうけん ふぁんとむぶらっど" name-sort: "じょじょのきみょうなぼうけん ふぁんとむぶらっど"
name-en: "Jojo no Kimyou na Bouken - Phantom Blood" name-en: "JoJo no Kimyou na Bouken - Phantom Blood"
region: "NTSC-J" region: "NTSC-J"
gsHWFixes: gsHWFixes:
halfPixelOffset: 4 # Fixes post positioning. halfPixelOffset: 4 # Fixes post positioning.

View File

@ -125,7 +125,7 @@ elseif(_M_ARM64)
endif() endif()
# Prevent fmt from being built with exceptions, or being thrown at call sites. # Prevent fmt from being built with exceptions, or being thrown at call sites.
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DFMT_EXCEPTIONS=0") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DFMT_USE_EXCEPTIONS=0 -DFMT_USE_RTTI=0")
add_subdirectory(3rdparty/fmt EXCLUDE_FROM_ALL) add_subdirectory(3rdparty/fmt EXCLUDE_FROM_ALL)
# Deliberately at the end. We don't want to set the flag on third-party projects. # Deliberately at the end. We don't want to set the flag on third-party projects.

View File

@ -7,7 +7,6 @@
#include "Threading.h" #include "Threading.h"
#include <mutex> #include <mutex>
#include "fmt/core.h"
#ifdef _WIN32 #ifdef _WIN32
#include "RedtapeWindows.h" #include "RedtapeWindows.h"

View File

@ -5,7 +5,7 @@
#include "Pcsx2Defs.h" #include "Pcsx2Defs.h"
#include "fmt/core.h" #include "fmt/base.h"
#include <cstdarg> #include <cstdarg>
#include <string> #include <string>

View File

@ -5,7 +5,7 @@
#include "Pcsx2Defs.h" #include "Pcsx2Defs.h"
#include "fmt/core.h" #include "fmt/format.h"
#include <string> #include <string>

View File

@ -17,7 +17,7 @@
#include <ucontext.h> #include <ucontext.h>
#include <unistd.h> #include <unistd.h>
#include "fmt/core.h" #include "fmt/format.h"
#if defined(__FreeBSD__) #if defined(__FreeBSD__)
#include "cpuinfo.h" #include "cpuinfo.h"

View File

@ -11,7 +11,7 @@
#include "common/Threading.h" #include "common/Threading.h"
#include "common/WindowInfo.h" #include "common/WindowInfo.h"
#include "fmt/core.h" #include "fmt/format.h"
#include <ctype.h> #include <ctype.h>
#include <time.h> #include <time.h>

View File

@ -5,11 +5,12 @@
#include "Pcsx2Defs.h" #include "Pcsx2Defs.h"
#include "fmt/core.h" #include "fmt/base.h"
#include <algorithm> #include <algorithm>
#include <cstdarg> #include <cstdarg>
#include <cstring> #include <cstring>
#include <iterator>
#include <limits> #include <limits>
#include <string> #include <string>
#include <string_view> #include <string_view>
@ -177,7 +178,7 @@ public:
__fi const char* end_ptr() const { return m_buffer + m_length; } __fi const char* end_ptr() const { return m_buffer + m_length; }
// STL adapters // STL adapters
__fi void push_back(value_type&& val) { append(val); } __fi void push_back(value_type val) { append(val); }
// returns a string view for this string // returns a string view for this string
std::string_view view() const; std::string_view view() const;
@ -413,7 +414,7 @@ __fi void SmallStringBase::format(fmt::format_string<T...> fmt, T&&... args)
} \ } \
\ \
template <typename FormatContext> \ template <typename FormatContext> \
auto format(const type& str, FormatContext& ctx) \ auto format(const type& str, FormatContext& ctx) const \
{ \ { \
return fmt::format_to(ctx.out(), "{}", str.view()); \ return fmt::format_to(ctx.out(), "{}", str.view()); \
} \ } \

View File

@ -10,7 +10,7 @@
#include <sstream> #include <sstream>
#include <algorithm> #include <algorithm>
#include "fmt/core.h" #include "fmt/format.h"
#ifdef _WIN32 #ifdef _WIN32
#include "RedtapeWindows.h" #include "RedtapeWindows.h"

View File

@ -10,7 +10,6 @@
#include "common/RedtapeWindows.h" #include "common/RedtapeWindows.h"
#include "common/StringUtil.h" #include "common/StringUtil.h"
#include "fmt/core.h"
#include "fmt/format.h" #include "fmt/format.h"
#include <mutex> #include <mutex>

View File

@ -8,8 +8,6 @@
#include "common/Threading.h" #include "common/Threading.h"
#include "common/WindowInfo.h" #include "common/WindowInfo.h"
#include "fmt/core.h"
#include <mmsystem.h> #include <mmsystem.h>
#include <timeapi.h> #include <timeapi.h>
#include <VersionHelpers.h> #include <VersionHelpers.h>

View File

@ -13,7 +13,7 @@
#include "common/RedtapeWindows.h" #include "common/RedtapeWindows.h"
#endif #endif
#include "fmt/core.h" #include "fmt/format.h"
#include "common/Assertions.h" #include "common/Assertions.h"
#include "common/Console.h" #include "common/Console.h"

View File

@ -113,8 +113,15 @@ def check_regression_test(baselinedir, testdir, name):
path2 = os.path.join(dir2, imagename) path2 = os.path.join(dir2, imagename)
if not os.path.isfile(path2): if not os.path.isfile(path2):
print("--- Frame %u for %s is missing in test set" % (framenum, name)) print("--- Frame %u for %s is missing in test set" % (framenum, name))
write("<h1>{}</h1>".format(name)) if first_fail:
write("<pre>--- Frame %u for %s is missing in test set</pre>" % (framenum, name)) write("<h1>{}</h1>".format(name))
if first_fail == False:
write("</table>")
write("<pre>--- Frame %u for %s is missing in test set</pre>" % (framenum, name))
write("</div>")
else:
write("<pre>--- Frame %u for %s is missing in test set</pre>" % (framenum, name))
return False return False
if not compare_frames(path1, path2): if not compare_frames(path1, path2):

View File

@ -9,7 +9,6 @@
#include <QtCore/QFile> #include <QtCore/QFile>
#include "common/Console.h" #include "common/Console.h"
#include "fmt/core.h"
#include "VMManager.h" #include "VMManager.h"
#include "Models/BreakpointModel.h" #include "Models/BreakpointModel.h"

View File

@ -51,7 +51,7 @@
#include <QtGui/QClipboard> #include <QtGui/QClipboard>
#include <QtGui/QInputMethod> #include <QtGui/QInputMethod>
#include "fmt/core.h" #include "fmt/format.h"
#include <cmath> #include <cmath>
#include <csignal> #include <csignal>

View File

@ -23,7 +23,7 @@
#include <memory> #include <memory>
#include <time.h> #include <time.h>
#include "fmt/core.h" #include "fmt/format.h"
// TODO: FIXME! Should be platform specific. // TODO: FIXME! Should be platform specific.
#ifdef _WIN32 #ifdef _WIN32

View File

@ -9,7 +9,7 @@
#include "common/MD5Digest.h" #include "common/MD5Digest.h"
#include "common/StringUtil.h" #include "common/StringUtil.h"
#include "fmt/core.h" #include "fmt/format.h"
#include <algorithm> #include <algorithm>

View File

@ -8,7 +8,7 @@
#include "common/FileSystem.h" #include "common/FileSystem.h"
#include "common/StringUtil.h" #include "common/StringUtil.h"
#include "fmt/core.h" #include "fmt/format.h"
#include <errno.h> #include <errno.h>

View File

@ -5,7 +5,7 @@
#include "common/RedtapeWilCom.h" #include "common/RedtapeWilCom.h"
#include "common/StringUtil.h" #include "common/StringUtil.h"
#include "fmt/core.h" #include "fmt/format.h"
#include <stdio.h> #include <stdio.h>
#include <WinSock2.h> #include <WinSock2.h>

View File

@ -21,7 +21,7 @@
#include "common/StringUtil.h" #include "common/StringUtil.h"
#include "common/Timer.h" #include "common/Timer.h"
#include "fmt/core.h" #include "fmt/format.h"
#include "IconsFontAwesome5.h" #include "IconsFontAwesome5.h"
#include <algorithm> #include <algorithm>

View File

@ -16,7 +16,7 @@
#include "imgui.h" #include "imgui.h"
#include "fmt/core.h" #include "fmt/format.h"
#include "common/Error.h" #include "common/Error.h"
#include "common/FileSystem.h" #include "common/FileSystem.h"

View File

@ -18,7 +18,7 @@
#include <sstream> #include <sstream>
#include "ryml_std.hpp" #include "ryml_std.hpp"
#include "ryml.hpp" #include "ryml.hpp"
#include "fmt/core.h" #include "fmt/format.h"
#include "fmt/ranges.h" #include "fmt/ranges.h"
#include <fstream> #include <fstream>
#include <mutex> #include <mutex>

View File

@ -9,7 +9,7 @@
#include "common/WrappedMemCopy.h" #include "common/WrappedMemCopy.h"
#include "fmt/core.h" #include "fmt/format.h"
using namespace R5900; using namespace R5900;

View File

@ -47,7 +47,7 @@
#include "imgui_internal.h" #include "imgui_internal.h"
#include "fmt/chrono.h" #include "fmt/chrono.h"
#include "fmt/core.h" #include "fmt/format.h"
#include <array> #include <array>
#include <bitset> #include <bitset>

View File

@ -3,7 +3,7 @@
#define IMGUI_DEFINE_MATH_OPERATORS #define IMGUI_DEFINE_MATH_OPERATORS
#include "fmt/core.h" #include "fmt/format.h"
#include "Host.h" #include "Host.h"
#include "GS/Renderers/Common/GSDevice.h" #include "GS/Renderers/Common/GSDevice.h"
#include "GS/Renderers/Common/GSTexture.h" #include "GS/Renderers/Common/GSTexture.h"

View File

@ -23,7 +23,7 @@
#include "common/Path.h" #include "common/Path.h"
#include "common/Timer.h" #include "common/Timer.h"
#include "fmt/core.h" #include "fmt/format.h"
#include "imgui.h" #include "imgui.h"
#include "imgui_internal.h" #include "imgui_internal.h"
#include "common/Image.h" #include "common/Image.h"

View File

@ -16,7 +16,7 @@
#include "IconsPromptFont.h" #include "IconsPromptFont.h"
#include "fmt/core.h" #include "fmt/format.h"
#include <array> #include <array>
#include <atomic> #include <atomic>

View File

@ -10,6 +10,8 @@
#include "IconsPromptFont.h" #include "IconsPromptFont.h"
#include "fmt/format.h"
#include <cmath> #include <cmath>
static const char* s_axis_names[XInputSource::NUM_AXES] = { static const char* s_axis_names[XInputSource::NUM_AXES] = {

View File

@ -43,5 +43,5 @@
// We use fmt a fair bit now. // We use fmt a fair bit now.
// fmt pch breaks GCC in debug builds: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=114370 // fmt pch breaks GCC in debug builds: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=114370
#if !defined(__GNUC__) || defined(__clang__) #if !defined(__GNUC__) || defined(__clang__)
#include "fmt/core.h" #include "fmt/format.h"
#endif #endif

View File

@ -7,7 +7,7 @@
#include "SIO/Pad/PadDualshock2.h" #include "SIO/Pad/PadDualshock2.h"
#include "SIO/Sio.h" #include "SIO/Sio.h"
#include <fmt/core.h> #include "fmt/format.h"
PadData::PadData(const int port, const int slot) PadData::PadData(const int port, const int slot)
{ {

View File

@ -10,7 +10,7 @@
#include "GS.h" #include "GS.h"
#include "Host.h" #include "Host.h"
#include <fmt/core.h> #include "fmt/format.h"
namespace InputRec namespace InputRec
{ {

View File

@ -20,7 +20,7 @@
#include "Host.h" #include "Host.h"
#include "IconsPromptFont.h" #include "IconsPromptFont.h"
#include "fmt/core.h" #include "fmt/format.h"
#include <map> #include <map>

View File

@ -17,7 +17,7 @@
#include "common/StringUtil.h" #include "common/StringUtil.h"
#include "common/Timer.h" #include "common/Timer.h"
#include "fmt/core.h" #include "fmt/format.h"
#include "ryml_std.hpp" #include "ryml_std.hpp"
#include "ryml.hpp" #include "ryml.hpp"

View File

@ -10,8 +10,6 @@
#include "Config.h" #include "Config.h"
#include "fmt/core.h"
//#define DEBUG_WRITE_FOLDER_CARD_IN_MEMORY_TO_FILE_ON_CHANGE //#define DEBUG_WRITE_FOLDER_CARD_IN_MEMORY_TO_FILE_ON_CHANGE
// -------------------------------------------------------------------------------------- // --------------------------------------------------------------------------------------

View File

@ -37,7 +37,7 @@
#include "common/StringUtil.h" #include "common/StringUtil.h"
#include "common/ZipHelpers.h" #include "common/ZipHelpers.h"
#include "fmt/core.h" #include "fmt/format.h"
#include <csetjmp> #include <csetjmp>
#include <png.h> #include <png.h>

View File

@ -13,7 +13,7 @@
#include "R3000A.h" #include "R3000A.h"
#include "R5900.h" #include "R5900.h"
#include "fmt/core.h" #include "fmt/format.h"
#include <cctype> #include <cctype>
#include <cstdarg> #include <cstdarg>

View File

@ -57,7 +57,7 @@
#include "IconsPromptFont.h" #include "IconsPromptFont.h"
#include "cpuinfo.h" #include "cpuinfo.h"
#include "discord_rpc.h" #include "discord_rpc.h"
#include "fmt/core.h" #include "fmt/format.h"
#include <atomic> #include <atomic>
#include <mutex> #include <mutex>

View File

@ -4,7 +4,6 @@
#pragma once #pragma once
#include <array> #include <array>
#include "fmt/core.h"
#include "common/AlignedMalloc.h" #include "common/AlignedMalloc.h"
// nVifBlock - Ordered for Hashing; the 'num' and 'upkType' fields are // nVifBlock - Ordered for Hashing; the 'num' and 'upkType' fields are

View File

@ -6,7 +6,7 @@
#include "Common.h" #include "Common.h"
#include "IopHw.h" #include "IopHw.h"
#include "fmt/core.h" #include "fmt/format.h"
namespace IopMemory { namespace IopMemory {
namespace Internal { namespace Internal {

View File

@ -5,7 +5,7 @@
#include "R3000A.h" #include "R3000A.h"
#include "IopMem.h" #include "IopMem.h"
#include "fmt/core.h" #include "fmt/format.h"
static std::string psxout_buf; static std::string psxout_buf;

View File

@ -3,7 +3,7 @@
#pragma once #pragma once
#include "fmt/core.h" #include "fmt/format.h"
#define eeAddrInRange(name, addr) \ #define eeAddrInRange(name, addr) \
(addr >= EEMemoryMap::name##_Start && addr < EEMemoryMap::name##_End) (addr >= EEMemoryMap::name##_Start && addr < EEMemoryMap::name##_End)

View File

@ -27,7 +27,7 @@
#include "common/BitUtils.h" #include "common/BitUtils.h"
#include "common/Error.h" #include "common/Error.h"
#include "fmt/core.h" #include "fmt/format.h"
#include <bit> #include <bit>
#ifdef _M_X86 #ifdef _M_X86

View File

@ -5,7 +5,6 @@
#include "MTVU.h" #include "MTVU.h"
#include "common/Perf.h" #include "common/Perf.h"
#include "common/StringUtil.h" #include "common/StringUtil.h"
#include "fmt/core.h"
void dVifReset(int idx) void dVifReset(int idx)
{ {

View File

@ -3,7 +3,6 @@
#include "Vif_UnpackSSE.h" #include "Vif_UnpackSSE.h"
#include "common/Perf.h" #include "common/Perf.h"
#include "fmt/core.h"
#define xMOV8(regX, loc) xMOVSSZX(regX, loc) #define xMOV8(regX, loc) xMOVSSZX(regX, loc)
#define xMOV16(regX, loc) xMOVSSZX(regX, loc) #define xMOV16(regX, loc) xMOVSSZX(regX, loc)

View File

@ -26,8 +26,6 @@
#include "common/Perf.h" #include "common/Perf.h"
#include "DebugTools/Breakpoints.h" #include "DebugTools/Breakpoints.h"
#include "fmt/core.h"
// #define DUMP_BLOCKS 1 // #define DUMP_BLOCKS 1
// #define TRACE_BLOCKS 1 // #define TRACE_BLOCKS 1

View File

@ -8,7 +8,7 @@
#include "common/FileSystem.h" #include "common/FileSystem.h"
#include "common/Path.h" #include "common/Path.h"
#include "fmt/core.h" #include "fmt/format.h"
// writes text directly to mVU.logFile, no newlines appended. // writes text directly to mVU.logFile, no newlines appended.
_mVUt void __mVULog(const char* fmt, ...) _mVUt void __mVULog(const char* fmt, ...)

View File

@ -7,7 +7,7 @@
#include "common/ScopedGuard.h" #include "common/ScopedGuard.h"
#include "common/StringUtil.h" #include "common/StringUtil.h"
#include "fmt/core.h" #include "fmt/format.h"
#if defined(_WIN32) #if defined(_WIN32)
#include "7z.h" #include "7z.h"