Update {fmt} to 10.0.0

This commit is contained in:
Marek Roszko 2023-07-15 13:49:47 -04:00
parent 76cef27014
commit a5cf2716b6
21 changed files with 3295 additions and 2282 deletions

View File

@ -139,7 +139,7 @@ int PL_EDIT_TOOL::Main( const TOOL_EVENT& aEvent )
{ {
m_frame->SaveCopyInUndoList(); m_frame->SaveCopyInUndoList();
} }
catch( const fmt::v9::format_error& exc ) catch( const fmt::v10::format_error& exc )
{ {
wxLogWarning( wxS( "Exception \"%s\" serializing string ocurred." ), wxLogWarning( wxS( "Exception \"%s\" serializing string ocurred." ),
exc.what() ); exc.what() );

View File

@ -197,7 +197,7 @@ int PL_POINT_EDITOR::Main( const TOOL_EVENT& aEvent )
{ {
m_frame->SaveCopyInUndoList(); m_frame->SaveCopyInUndoList();
} }
catch( const fmt::v9::format_error& exc ) catch( const fmt::v10::format_error& exc )
{ {
wxLogWarning( wxS( "Exception \"%s\" serializing string ocurred." ), wxLogWarning( wxS( "Exception \"%s\" serializing string ocurred." ),
exc.what() ); exc.what() );

View File

@ -1,4 +1,4 @@
cmake_minimum_required(VERSION 3.1...3.18) cmake_minimum_required(VERSION 3.8...3.26)
# 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)
@ -24,15 +24,86 @@ function(join result_var)
set(${result_var} "${result}" PARENT_SCOPE) set(${result_var} "${result}" PARENT_SCOPE)
endfunction() endfunction()
# DEPRECATED! Should be merged into add_module_library.
function(enable_module target) function(enable_module target)
if (MSVC) if (MSVC)
set(BMI ${CMAKE_CURRENT_BINARY_DIR}/${target}.ifc) set(BMI ${CMAKE_CURRENT_BINARY_DIR}/${target}.ifc)
target_compile_options(${target} target_compile_options(${target}
PRIVATE /interface /ifcOutput ${BMI} PRIVATE /interface /ifcOutput ${BMI}
INTERFACE /reference fmt=${BMI}) INTERFACE /reference fmt=${BMI})
set_target_properties(${target} PROPERTIES ADDITIONAL_CLEAN_FILES ${BMI})
set_source_files_properties(${BMI} PROPERTIES GENERATED ON)
endif () endif ()
set_target_properties(${target} PROPERTIES ADDITIONAL_CLEAN_FILES ${BMI}) endfunction()
set_source_files_properties(${BMI} PROPERTIES GENERATED ON)
# Adds a library compiled with C++20 module support.
# `enabled` is a CMake variables that specifies if modules are enabled.
# If modules are disabled `add_module_library` falls back to creating a
# non-modular library.
#
# Usage:
# add_module_library(<name> [sources...] FALLBACK [sources...] [IF enabled])
function(add_module_library name)
cmake_parse_arguments(AML "" "IF" "FALLBACK" ${ARGN})
set(sources ${AML_UNPARSED_ARGUMENTS})
add_library(${name})
set_target_properties(${name} PROPERTIES LINKER_LANGUAGE CXX)
if (NOT ${${AML_IF}})
# Create a non-modular library.
target_sources(${name} PRIVATE ${AML_FALLBACK})
return()
endif ()
# Modules require C++20.
target_compile_features(${name} PUBLIC cxx_std_20)
if (CMAKE_COMPILER_IS_GNUCXX)
target_compile_options(${name} PUBLIC -fmodules-ts)
endif ()
# `std` is affected by CMake options and may be higher than C++20.
get_target_property(std ${name} CXX_STANDARD)
if (CMAKE_CXX_COMPILER_ID MATCHES "Clang")
set(pcms)
foreach (src ${sources})
get_filename_component(pcm ${src} NAME_WE)
set(pcm ${pcm}.pcm)
# Propagate -fmodule-file=*.pcm to targets that link with this library.
target_compile_options(
${name} PUBLIC -fmodule-file=${CMAKE_CURRENT_BINARY_DIR}/${pcm})
# Use an absolute path to prevent target_link_libraries prepending -l
# to it.
set(pcms ${pcms} ${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.
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})
endfunction() endfunction()
include(CMakeParseArguments) include(CMakeParseArguments)
@ -75,7 +146,7 @@ option(FMT_WERROR "Halt the compilation with an error on compiler warnings."
# 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_DOC "Generate the doc target." ${FMT_MASTER_PROJECT})
option(FMT_INSTALL "Generate the install target." ${FMT_MASTER_PROJECT}) option(FMT_INSTALL "Generate the install target." ON)
option(FMT_TEST "Generate the test target." ${FMT_MASTER_PROJECT}) 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_CUDA_TEST "Generate the cuda-test target." OFF) option(FMT_CUDA_TEST "Generate the cuda-test target." OFF)
@ -83,16 +154,6 @@ option(FMT_OS "Include core requiring OS (Windows/Posix) " 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)
set(FMT_CAN_MODULE OFF)
if (CMAKE_CXX_STANDARD GREATER 17 AND
# msvc 16.10-pre4
MSVC AND CMAKE_CXX_COMPILER_VERSION VERSION_GREATER 19.29.30035)
set(FMT_CAN_MODULE OFF)
endif ()
if (NOT FMT_CAN_MODULE)
set(FMT_MODULE OFF)
message(STATUS "Module support is disabled.")
endif ()
if (FMT_TEST AND FMT_MODULE) if (FMT_TEST AND FMT_MODULE)
# The tests require {fmt} to be compiled as traditional library # The tests require {fmt} to be compiled as traditional library
message(STATUS "Testing is incompatible with build mode 'module'.") message(STATUS "Testing is incompatible with build mode 'module'.")
@ -101,6 +162,10 @@ 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 core.h
file(READ include/fmt/core.h core_h) file(READ include/fmt/core.h core_h)
@ -118,23 +183,15 @@ message(STATUS "Version: ${FMT_VERSION}")
message(STATUS "Build type: ${CMAKE_BUILD_TYPE}") message(STATUS "Build type: ${CMAKE_BUILD_TYPE}")
if (NOT CMAKE_RUNTIME_OUTPUT_DIRECTORY) if (NOT CMAKE_RUNTIME_OUTPUT_DIRECTORY)
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin) set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/bin)
endif () endif ()
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH}
"${CMAKE_CURRENT_SOURCE_DIR}/support/cmake") "${CMAKE_CURRENT_SOURCE_DIR}/support/cmake")
include(cxx14) include(CheckCXXCompilerFlag)
include(JoinPaths) include(JoinPaths)
list(FIND CMAKE_CXX_COMPILE_FEATURES "cxx_variadic_templates" index)
if (${index} GREATER -1)
# Use cxx_variadic_templates instead of more appropriate cxx_std_11 for
# compatibility with older CMake versions.
set(FMT_REQUIRED_FEATURES cxx_variadic_templates)
endif ()
message(STATUS "Required features: ${FMT_REQUIRED_FEATURES}")
if (FMT_MASTER_PROJECT AND NOT DEFINED CMAKE_CXX_VISIBILITY_PRESET) if (FMT_MASTER_PROJECT AND NOT DEFINED CMAKE_CXX_VISIBILITY_PRESET)
set_verbose(CMAKE_CXX_VISIBILITY_PRESET hidden CACHE STRING set_verbose(CMAKE_CXX_VISIBILITY_PRESET hidden CACHE STRING
"Preset for the export of private symbols") "Preset for the export of private symbols")
@ -220,16 +277,18 @@ endfunction()
add_headers(FMT_HEADERS args.h chrono.h color.h compile.h core.h format.h add_headers(FMT_HEADERS args.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)
if (FMT_MODULE) set(FMT_SOURCES src/format.cc)
set(FMT_SOURCES src/fmt.cc) if (FMT_OS)
elseif (FMT_OS) set(FMT_SOURCES ${FMT_SOURCES} src/os.cc)
set(FMT_SOURCES src/format.cc src/os.cc)
else()
set(FMT_SOURCES src/format.cc)
endif () endif ()
add_library(fmt STATIC ${FMT_SOURCES} ${FMT_HEADERS} README.rst ChangeLog.rst) add_module_library(fmt src/fmt.cc FALLBACK
${FMT_SOURCES} ${FMT_HEADERS} README.rst ChangeLog.rst
IF FMT_MODULE)
add_library(fmt::fmt ALIAS fmt) add_library(fmt::fmt ALIAS fmt)
if (FMT_MODULE)
enable_module(fmt)
endif ()
if (FMT_WERROR) if (FMT_WERROR)
target_compile_options(fmt PRIVATE ${WERROR_FLAG}) target_compile_options(fmt PRIVATE ${WERROR_FLAG})
@ -237,11 +296,8 @@ endif ()
if (FMT_PEDANTIC) if (FMT_PEDANTIC)
target_compile_options(fmt PRIVATE ${PEDANTIC_COMPILE_FLAGS}) target_compile_options(fmt PRIVATE ${PEDANTIC_COMPILE_FLAGS})
endif () endif ()
if (FMT_MODULE)
enable_module(fmt)
endif ()
target_compile_features(fmt INTERFACE ${FMT_REQUIRED_FEATURES}) target_compile_features(fmt PUBLIC cxx_std_11)
target_include_directories(fmt ${FMT_SYSTEM_HEADERS_ATTRIBUTE} PUBLIC target_include_directories(fmt ${FMT_SYSTEM_HEADERS_ATTRIBUTE} PUBLIC
$<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/include> $<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/include>
@ -262,13 +318,7 @@ if (CMAKE_BUILD_TYPE STREQUAL "Debug")
endif () endif ()
if (BUILD_SHARED_LIBS) if (BUILD_SHARED_LIBS)
if (UNIX AND NOT APPLE AND NOT ${CMAKE_SYSTEM_NAME} MATCHES "SunOS" AND target_compile_definitions(fmt PRIVATE FMT_LIB_EXPORT INTERFACE FMT_SHARED)
NOT EMSCRIPTEN)
# Fix rpmlint warning:
# unused-direct-shlib-dependency /usr/lib/libformat.so.1.1.0 /lib/libm.so.6.
target_link_libraries(fmt -Wl,--as-needed)
endif ()
target_compile_definitions(fmt PRIVATE FMT_EXPORT INTERFACE FMT_SHARED)
endif () endif ()
if (FMT_SAFE_DURATION_CAST) if (FMT_SAFE_DURATION_CAST)
target_compile_definitions(fmt PUBLIC FMT_SAFE_DURATION_CAST) target_compile_definitions(fmt PUBLIC FMT_SAFE_DURATION_CAST)
@ -278,7 +328,7 @@ 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)
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 ${FMT_REQUIRED_FEATURES}) target_compile_features(fmt-header-only INTERFACE cxx_std_11)
target_include_directories(fmt-header-only ${FMT_SYSTEM_HEADERS_ATTRIBUTE} INTERFACE target_include_directories(fmt-header-only ${FMT_SYSTEM_HEADERS_ATTRIBUTE} INTERFACE
$<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/include> $<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/include>
@ -345,8 +395,6 @@ if (FMT_INSTALL)
install(EXPORT ${targets_export_name} DESTINATION ${FMT_CMAKE_DIR} install(EXPORT ${targets_export_name} DESTINATION ${FMT_CMAKE_DIR}
NAMESPACE fmt::) NAMESPACE fmt::)
install(FILES $<TARGET_PDB_FILE:${INSTALL_TARGETS}>
DESTINATION ${FMT_LIB_DIR} OPTIONAL)
install(FILES "${pkgconfig}" DESTINATION "${FMT_PKGCONFIG_DIR}") install(FILES "${pkgconfig}" DESTINATION "${FMT_PKGCONFIG_DIR}")
endif () endif ()

View File

@ -1,3 +1,434 @@
10.0.0 - 2023-05-09
-------------------
* Replaced Grisu with a new floating-point formatting algorithm for given
precision (`#3262 <https://github.com/fmtlib/fmt/issues/3262>`_,
`#2750 <https://github.com/fmtlib/fmt/issues/2750>`_,
`#3269 <https://github.com/fmtlib/fmt/pull/3269>`_,
`#3276 <https://github.com/fmtlib/fmt/pull/3276>`_).
The new algorithm is based on Dragonbox already used for the
shortest representation and gives substantial performance improvement:
.. image:: https://user-images.githubusercontent.com/33922675/
211956670-84891a09-6867-47d9-82fc-3230da7abe0f.png
* Red: new algorithm
* Green: new algorithm with ``FMT_USE_FULL_CACHE_DRAGONBOX`` defined to 1
* Blue: old algorithm
Thanks `@jk-jeon (Junekey Jeon) <https://github.com/jk-jeon>`_.
* Replaced ``snprintf``-based hex float formatter with an internal
implementation (`#3179 <https://github.com/fmtlib/fmt/pull/3179>`_,
`#3203 <https://github.com/fmtlib/fmt/pull/3203>`_).
This removes the last usage of ``s(n)printf`` in {fmt}.
Thanks `@phprus (Vladislav Shchapov) <https://github.com/phprus>`_.
* Fixed alignment of floating-point numbers with localization
(`#3263 <https://github.com/fmtlib/fmt/issues/3263>`_,
`#3272 <https://github.com/fmtlib/fmt/pull/3272>`_).
Thanks `@ShawnZhong (Shawn Zhong) <https://github.com/ShawnZhong>`_.
* Improved C++20 module support
(`#3134 <https://github.com/fmtlib/fmt/pull/3134>`_,
`#3254 <https://github.com/fmtlib/fmt/pull/3254>`_,
`#3386 <https://github.com/fmtlib/fmt/pull/3386>`_,
`#3387 <https://github.com/fmtlib/fmt/pull/3387>`_,
`#3388 <https://github.com/fmtlib/fmt/pull/3388>`_,
`#3392 <https://github.com/fmtlib/fmt/pull/3392>`_,
`#3397 <https://github.com/fmtlib/fmt/pull/3397>`_,
`#3399 <https://github.com/fmtlib/fmt/pull/3399>`_,
`#3400 <https://github.com/fmtlib/fmt/pull/3400>`_).
Thanks `@laitingsheng (Tinson Lai) <https://github.com/laitingsheng>`_,
`@Orvid (Orvid King) <https://github.com/Orvid>`_,
`@DanielaE (Daniela Engert) <https://github.com/DanielaE>`_.
Switched to the `modules CMake library <https://github.com/vitaut/modules>`_
which allows building {fmt} as a C++20 module with clang::
CXX=clang++ cmake -DFMT_MODULE=ON .
make
* Made ``format_as`` work with any user-defined type and not just enums.
For example (`godbolt <https://godbolt.org/z/b7rqhq5Kh>`__):
.. code:: c++
#include <fmt/format.h>
struct floaty_mc_floatface {
double value;
};
auto format_as(floaty_mc_floatface f) { return f.value; }
int main() {
fmt::print("{:8}\n", floaty_mc_floatface{0.42}); // prints " 0.42"
}
* Removed deprecated implicit conversions for enums and conversions to primitive
types for compatibility with ``std::format`` and to prevent potential ODR
violations. Use ``format_as`` instead.
* Added support for fill, align and width to the time point formatter
(`#3237 <https://github.com/fmtlib/fmt/issues/3237>`_,
`#3260 <https://github.com/fmtlib/fmt/pull/3260>`_,
`#3275 <https://github.com/fmtlib/fmt/pull/3275>`_).
For example (`godbolt <https://godbolt.org/z/rKP6MGz6c>`__):
.. code:: c++
#include <fmt/chrono.h>
int main() {
// prints " 2023"
fmt::print("{:>8%Y}\n", std::chrono::system_clock::now());
}
Thanks `@ShawnZhong (Shawn Zhong) <https://github.com/ShawnZhong>`_.
* Implemented formatting of subseconds
(`#2207 <https://github.com/fmtlib/fmt/issues/2207>`_,
`#3117 <https://github.com/fmtlib/fmt/issues/3117>`_,
`#3115 <https://github.com/fmtlib/fmt/pull/3115>`_,
`#3143 <https://github.com/fmtlib/fmt/pull/3143>`_,
`#3144 <https://github.com/fmtlib/fmt/pull/3144>`_,
`#3349 <https://github.com/fmtlib/fmt/pull/3349>`_).
For example (`godbolt <https://godbolt.org/z/45738oGEo>`__):
.. code:: c++
#include <fmt/chrono.h>
int main() {
// prints 01.234567
fmt::print("{:%S}\n", std::chrono::microseconds(1234567));
}
Thanks `@patrickroocks (Patrick Roocks) <https://github.com/patrickroocks>`_
`@phprus (Vladislav Shchapov) <https://github.com/phprus>`_,
`@BRevzin (Barry Revzin) <https://github.com/BRevzin>`_.
* Added precision support to ``%S``
(`#3148 <https://github.com/fmtlib/fmt/pull/3148>`_).
Thanks `@SappyJoy (Stepan Ponomaryov) <https://github.com/SappyJoy>`_
* Added support for ``std::utc_time``
(`#3098 <https://github.com/fmtlib/fmt/issues/3098>`_,
`#3110 <https://github.com/fmtlib/fmt/pull/3110>`_).
Thanks `@patrickroocks (Patrick Roocks) <https://github.com/patrickroocks>`_.
* Switched formatting of ``std::chrono::system_clock`` from local time to UTC
for compatibility with the standard
(`#3199 <https://github.com/fmtlib/fmt/issues/3199>`_,
`#3230 <https://github.com/fmtlib/fmt/pull/3230>`_).
Thanks `@ned14 (Niall Douglas) <https://github.com/ned14>`_.
* Added support for ``%Ez`` and ``%Oz`` to chrono formatters.
(`#3220 <https://github.com/fmtlib/fmt/issues/3220>`_,
`#3222 <https://github.com/fmtlib/fmt/pull/3222>`_).
Thanks `@phprus (Vladislav Shchapov) <https://github.com/phprus>`_.
* Improved validation of format specifiers for ``std::chrono::duration``
(`#3219 <https://github.com/fmtlib/fmt/issues/3219>`_,
`#3232 <https://github.com/fmtlib/fmt/pull/3232>`_).
Thanks `@ShawnZhong (Shawn Zhong) <https://github.com/ShawnZhong>`_.
* Fixed formatting of time points before the epoch
(`#3117 <https://github.com/fmtlib/fmt/issues/3117>`_,
`#3261 <https://github.com/fmtlib/fmt/pull/3261>`_).
For example (`godbolt <https://godbolt.org/z/f7bcznb3W>`__):
.. code:: c++
#include <fmt/chrono.h>
int main() {
auto t = std::chrono::system_clock::from_time_t(0) -
std::chrono::milliseconds(250);
fmt::print("{:%S}\n", t); // prints 59.750000000
}
Thanks `@ShawnZhong (Shawn Zhong) <https://github.com/ShawnZhong>`_.
* Experimental: implemented glibc extension for padding seconds, minutes and
hours (`#2959 <https://github.com/fmtlib/fmt/issues/2959>`_,
`#3271 <https://github.com/fmtlib/fmt/pull/3271>`_).
Thanks `@ShawnZhong (Shawn Zhong) <https://github.com/ShawnZhong>`_.
* Added a formatter for ``std::exception``
(`#2977 <https://github.com/fmtlib/fmt/issues/2977>`_,
`#3012 <https://github.com/fmtlib/fmt/issues/3012>`_,
`#3062 <https://github.com/fmtlib/fmt/pull/3062>`_,
`#3076 <https://github.com/fmtlib/fmt/pull/3076>`_,
`#3119 <https://github.com/fmtlib/fmt/pull/3119>`_).
For example (`godbolt <https://godbolt.org/z/8xoWGs9e4>`__):
.. code:: c++
#include <fmt/std.h>
#include <vector>
int main() {
try {
std::vector<bool>().at(0);
} catch(const std::exception& e) {
fmt::print("{}", e);
}
}
prints::
vector<bool>::_M_range_check: __n (which is 0) >= this->size() (which is 0)
on libstdc++.
Thanks `@zach2good (Zach Toogood) <https://github.com/zach2good>`_ and
`@phprus (Vladislav Shchapov) <https://github.com/phprus>`_.
* Moved ``std::error_code`` formatter from ``fmt/os.h`` to ``fmt/std.h``.
(`#3125 <https://github.com/fmtlib/fmt/pull/3125>`_).
Thanks `@phprus (Vladislav Shchapov) <https://github.com/phprus>`_.
* Added formatters for standard container adapters: ``std::priority_queue``,
``std::queue`` and ``std::stack``
(`#3215 <https://github.com/fmtlib/fmt/issues/3215>`_,
`#3279 <https://github.com/fmtlib/fmt/pull/3279>`_).
For example (`godbolt <https://godbolt.org/z/74h1xY9qK>`__):
.. code:: c++
#include <fmt/ranges.h>
#include <stack>
#include <vector>
int main() {
auto s = std::stack<bool, std::vector<bool>>();
for (auto b: {true, false, true}) s.push(b);
fmt::print("{}\n", s); // prints [true, false, true]
}
Thanks `@ShawnZhong (Shawn Zhong) <https://github.com/ShawnZhong>`_.
* Added a formatter for ``std::optional`` to ``fmt/std.h``.
Thanks `@tom-huntington <https://github.com/tom-huntington>`_.
* Fixed formatting of valueless by exception variants
(`#3347 <https://github.com/fmtlib/fmt/pull/3347>`_).
Thanks `@TheOmegaCarrot <https://github.com/TheOmegaCarrot>`_.
* Made ``fmt::ptr`` accept ``unique_ptr`` with a custom deleter
(`#3177 <https://github.com/fmtlib/fmt/pull/3177>`_).
Thanks `@hmbj (Hans-Martin B. Jensen) <https://github.com/hmbj>`_.
* Fixed formatting of noncopyable ranges and nested ranges of chars
(`#3158 <https://github.com/fmtlib/fmt/pull/3158>`_
`#3286 <https://github.com/fmtlib/fmt/issues/3286>`_,
`#3290 <https://github.com/fmtlib/fmt/pull/3290>`_).
Thanks `@BRevzin (Barry Revzin) <https://github.com/BRevzin>`_.
* Fixed issues with formatting of paths and ranges of paths
(`#3319 <https://github.com/fmtlib/fmt/issues/3319>`_,
`#3321 <https://github.com/fmtlib/fmt/pull/3321>`_
`#3322 <https://github.com/fmtlib/fmt/issues/3322>`_).
Thanks `@phprus (Vladislav Shchapov) <https://github.com/phprus>`_.
* Improved handling of invalid Unicode in paths.
* Enabled compile-time checks on Apple clang 14 and later
(`#3331 <https://github.com/fmtlib/fmt/pull/3331>`_).
Thanks `@cloyce (Cloyce D. Spradling) <https://github.com/cloyce>`_.
* Improved compile-time checks of named arguments
(`#3105 <https://github.com/fmtlib/fmt/issues/3105>`_,
`#3214 <https://github.com/fmtlib/fmt/pull/3214>`_).
Thanks `@rbrich (Radek Brich) <https://github.com/rbrich>`_.
* Fixed formatting when both alignment and ``0`` are given
(`#3236 <https://github.com/fmtlib/fmt/issues/3236>`_,
`#3248 <https://github.com/fmtlib/fmt/pull/3248>`_).
Thanks `@ShawnZhong (Shawn Zhong) <https://github.com/ShawnZhong>`_.
* Improved Unicode support in the experimental file API on Windows
(`#3234 <https://github.com/fmtlib/fmt/issues/3234>`_,
`#3293 <https://github.com/fmtlib/fmt/pull/3293>`_).
Thanks `@Fros1er (Froster) <https://github.com/Fros1er>`_.
* Unified UTF transcoding
(`#3416 <https://github.com/fmtlib/fmt/pull/3416>`_).
Thanks `@phprus (Vladislav Shchapov) <https://github.com/phprus>`_.
* Added support for UTF-8 digit separators via an experimental locale facet
(`#1861 <https://github.com/fmtlib/fmt/issues/1861>`_).
For example (`godbolt <https://godbolt.org/z/f7bcznb3W>`__):
.. code:: c++
auto loc = std::locale(
std::locale(), new fmt::format_facet<std::locale>(""));
auto s = fmt::format(loc, "{:L}", 1000);
where ```` is U+2019 used as a digit separator in the de_CH locale.
* Added an overload of ``formatted_size`` that takes a locale
(`#3084 <https://github.com/fmtlib/fmt/issues/3084>`_,
`#3087 <https://github.com/fmtlib/fmt/pull/3087>`_).
Thanks `@gerboengels <https://github.com/gerboengels>`_.
* Removed the deprecated ``FMT_DEPRECATED_OSTREAM``.
* Fixed a UB when using a null ``std::string_view`` with ``fmt::to_string``
or format string compilation
(`#3241 <https://github.com/fmtlib/fmt/issues/3241>`_,
`#3244 <https://github.com/fmtlib/fmt/pull/3244>`_).
Thanks `@phprus (Vladislav Shchapov) <https://github.com/phprus>`_.
* Added ``starts_with`` to the fallback ``string_view`` implementation
(`#3080 <https://github.com/fmtlib/fmt/pull/3080>`_).
Thanks `@phprus (Vladislav Shchapov) <https://github.com/phprus>`_.
* Added ``fmt::basic_format_string::get()`` for compatibility with
``basic_format_string`` (`#3111 <https://github.com/fmtlib/fmt/pull/3111>`_).
Thanks `@huangqinjin <https://github.com/huangqinjin>`_.
* Added ``println`` for compatibility with C++23
(`#3267 <https://github.com/fmtlib/fmt/pull/3267>`_).
Thanks `@ShawnZhong (Shawn Zhong) <https://github.com/ShawnZhong>`_.
* Improved documentation
(`#3108 <https://github.com/fmtlib/fmt/issues/3108>`_,
`#3169 <https://github.com/fmtlib/fmt/issues/3169>`_,
`#3243 <https://github.com/fmtlib/fmt/pull/3243>`_).
`#3404 <https://github.com/fmtlib/fmt/pull/3404>`_).
Thanks `@Cleroth <https://github.com/Cleroth>`_ and
`@Vertexwahn <https://github.com/Vertexwahn>`_.
* Improved build configuration and tests
(`#3118 <https://github.com/fmtlib/fmt/pull/3118>`_,
`#3120 <https://github.com/fmtlib/fmt/pull/3120>`_,
`#3188 <https://github.com/fmtlib/fmt/pull/3188>`_,
`#3189 <https://github.com/fmtlib/fmt/issues/3189>`_,
`#3198 <https://github.com/fmtlib/fmt/pull/3198>`_,
`#3205 <https://github.com/fmtlib/fmt/pull/3205>`_,
`#3207 <https://github.com/fmtlib/fmt/pull/3207>`_,
`#3210 <https://github.com/fmtlib/fmt/pull/3210>`_,
`#3240 <https://github.com/fmtlib/fmt/pull/3240>`_,
`#3256 <https://github.com/fmtlib/fmt/pull/3256>`_,
`#3264 <https://github.com/fmtlib/fmt/pull/3264>`_,
`#3299 <https://github.com/fmtlib/fmt/issues/3299>`_,
`#3302 <https://github.com/fmtlib/fmt/pull/3302>`_,
`#3312 <https://github.com/fmtlib/fmt/pull/3312>`_,
`#3317 <https://github.com/fmtlib/fmt/issues/3317>`_,
`#3328 <https://github.com/fmtlib/fmt/pull/3328>`_,
`#3333 <https://github.com/fmtlib/fmt/pull/3333>`_,
`#3369 <https://github.com/fmtlib/fmt/pull/3369>`_,
`#3373 <https://github.com/fmtlib/fmt/issues/3373>`_,
`#3395 <https://github.com/fmtlib/fmt/pull/3395>`_,
`#3406 <https://github.com/fmtlib/fmt/pull/3406>`_,
`#3411 <https://github.com/fmtlib/fmt/pull/3411>`_).
Thanks `@dimztimz (Dimitrij Mijoski) <https://github.com/dimztimz>`_,
`@phprus (Vladislav Shchapov) <https://github.com/phprus>`_,
`@DavidKorczynski <https://github.com/DavidKorczynski>`_,
`@ChrisThrasher (Chris Thrasher) <https://github.com/ChrisThrasher>`_,
`@FrancoisCarouge (François Carouge) <https://github.com/FrancoisCarouge>`_,
`@kennyweiss (Kenny Weiss) <https://github.com/kennyweiss>`_,
`@luzpaz <https://github.com/luzpaz>`_,
`@codeinred (Alecto Irene Perez) <https://github.com/codeinred>`_,
`@Mixaill (Mikhail Paulyshka) <https://github.com/Mixaill>`_,
`@joycebrum (Joyce) <https://github.com/joycebrum>`_,
`@kevinhwang (Kevin Hwang) <https://github.com/kevinhwang>`_,
`@Vertexwahn <https://github.com/Vertexwahn>`_.
* Fixed a regression in handling empty format specifiers after a colon (``{:}``)
(`#3086 <https://github.com/fmtlib/fmt/pull/3086>`_).
Thanks `@oxidase (Michael Krasnyk) <https://github.com/oxidase>`_.
* Worked around a broken implementation of ``std::is_constant_evaluated`` in
some versions of libstdc++ on clang
(`#3247 <https://github.com/fmtlib/fmt/issues/3247>`_,
`#3281 <https://github.com/fmtlib/fmt/pull/3281>`_).
Thanks `@phprus (Vladislav Shchapov) <https://github.com/phprus>`_.
* Fixed formatting of volatile variables
(`#3068 <https://github.com/fmtlib/fmt/pull/3068>`_).
* Fixed various warnings and compilation issues
(`#3057 <https://github.com/fmtlib/fmt/pull/3057>`_,
`#3066 <https://github.com/fmtlib/fmt/pull/3066>`_,
`#3072 <https://github.com/fmtlib/fmt/pull/3072>`_,
`#3082 <https://github.com/fmtlib/fmt/pull/3082>`_,
`#3091 <https://github.com/fmtlib/fmt/pull/3091>`_,
`#3092 <https://github.com/fmtlib/fmt/issues/3092>`_,
`#3093 <https://github.com/fmtlib/fmt/pull/3093>`_,
`#3095 <https://github.com/fmtlib/fmt/pull/3095>`_,
`#3096 <https://github.com/fmtlib/fmt/issues/3096>`_,
`#3097 <https://github.com/fmtlib/fmt/pull/3097>`_,
`#3128 <https://github.com/fmtlib/fmt/issues/3128>`_,
`#3129 <https://github.com/fmtlib/fmt/pull/3129>`_,
`#3137 <https://github.com/fmtlib/fmt/pull/3137>`_,
`#3139 <https://github.com/fmtlib/fmt/pull/3139>`_,
`#3140 <https://github.com/fmtlib/fmt/issues/3140>`_,
`#3142 <https://github.com/fmtlib/fmt/pull/3142>`_,
`#3149 <https://github.com/fmtlib/fmt/issues/3149>`_,
`#3150 <https://github.com/fmtlib/fmt/pull/3150>`_,
`#3154 <https://github.com/fmtlib/fmt/issues/3154>`_,
`#3163 <https://github.com/fmtlib/fmt/issues/3163>`_,
`#3178 <https://github.com/fmtlib/fmt/issues/3178>`_,
`#3184 <https://github.com/fmtlib/fmt/pull/3184>`_,
`#3196 <https://github.com/fmtlib/fmt/pull/3196>`_,
`#3204 <https://github.com/fmtlib/fmt/issues/3204>`_,
`#3206 <https://github.com/fmtlib/fmt/pull/3206>`_,
`#3208 <https://github.com/fmtlib/fmt/pull/3208>`_,
`#3213 <https://github.com/fmtlib/fmt/issues/3213>`_,
`#3216 <https://github.com/fmtlib/fmt/pull/3216>`_,
`#3224 <https://github.com/fmtlib/fmt/issues/3224>`_,
`#3226 <https://github.com/fmtlib/fmt/issues/3226>`_,
`#3228 <https://github.com/fmtlib/fmt/issues/3228>`_,
`#3229 <https://github.com/fmtlib/fmt/pull/3229>`_,
`#3259 <https://github.com/fmtlib/fmt/pull/3259>`_,
`#3274 <https://github.com/fmtlib/fmt/issues/3274>`_,
`#3287 <https://github.com/fmtlib/fmt/issues/3287>`_,
`#3288 <https://github.com/fmtlib/fmt/pull/3288>`_,
`#3292 <https://github.com/fmtlib/fmt/issues/3292>`_,
`#3295 <https://github.com/fmtlib/fmt/pull/3295>`_,
`#3296 <https://github.com/fmtlib/fmt/pull/3296>`_,
`#3298 <https://github.com/fmtlib/fmt/issues/3298>`_,
`#3325 <https://github.com/fmtlib/fmt/issues/3325>`_,
`#3326 <https://github.com/fmtlib/fmt/pull/3326>`_,
`#3334 <https://github.com/fmtlib/fmt/issues/3334>`_,
`#3342 <https://github.com/fmtlib/fmt/issues/3342>`_,
`#3343 <https://github.com/fmtlib/fmt/pull/3343>`_,
`#3351 <https://github.com/fmtlib/fmt/issues/3351>`_,
`#3352 <https://github.com/fmtlib/fmt/pull/3352>`_,
`#3362 <https://github.com/fmtlib/fmt/pull/3362>`_,
`#3365 <https://github.com/fmtlib/fmt/issues/3365>`_,
`#3366 <https://github.com/fmtlib/fmt/pull/3366>`_,
`#3374 <https://github.com/fmtlib/fmt/pull/3374>`_,
`#3377 <https://github.com/fmtlib/fmt/issues/3377>`_,
`#3378 <https://github.com/fmtlib/fmt/pull/3378>`_,
`#3381 <https://github.com/fmtlib/fmt/issues/3381>`_,
`#3398 <https://github.com/fmtlib/fmt/pull/3398>`_,
`#3413 <https://github.com/fmtlib/fmt/pull/3413>`_,
`#3415 <https://github.com/fmtlib/fmt/issues/3415>`_).
Thanks `@phprus (Vladislav Shchapov) <https://github.com/phprus>`_,
`@gsjaardema (Greg Sjaardema) <https://github.com/gsjaardema>`_,
`@NewbieOrange <https://github.com/NewbieOrange>`_,
`@EngineLessCC (VivyaCC) <https://github.com/EngineLessCC>`_,
`@asmaloney (Andy Maloney) <https://github.com/asmaloney>`_,
`@HazardyKnusperkeks (Björn Schäpers)
<https://github.com/HazardyKnusperkeks>`_,
`@sergiud (Sergiu Deitsch) <https://github.com/sergiud>`_,
`@Youw (Ihor Dutchak) <https://github.com/Youw>`_,
`@thesmurph <https://github.com/thesmurph>`_,
`@czudziakm (Maksymilian Czudziak) <https://github.com/czudziakm>`_,
`@Roman-Koshelev <https://github.com/Roman-Koshelev>`_,
`@chronoxor (Ivan Shynkarenka) <https://github.com/chronoxor>`_,
`@ShawnZhong (Shawn Zhong) <https://github.com/ShawnZhong>`_,
`@russelltg (Russell Greene) <https://github.com/russelltg>`_,
`@glebm (Gleb Mazovetskiy) <https://github.com/glebm>`_,
`@tmartin-gh <https://github.com/tmartin-gh>`_,
`@Zhaojun-Liu (June Liu) <https://github.com/Zhaojun-Liu>`_,
`@louiswins (Louis Wilson) <https://github.com/louiswins>`_,
`@mogemimi <https://github.com/mogemimi>`_.
9.1.0 - 2022-08-27 9.1.0 - 2022-08-27
------------------ ------------------
@ -105,6 +536,7 @@
`#2982 <https://github.com/fmtlib/fmt/pull/2982>`_, `#2982 <https://github.com/fmtlib/fmt/pull/2982>`_,
`#2985 <https://github.com/fmtlib/fmt/pull/2985>`_, `#2985 <https://github.com/fmtlib/fmt/pull/2985>`_,
`#2988 <https://github.com/fmtlib/fmt/issues/2988>`_, `#2988 <https://github.com/fmtlib/fmt/issues/2988>`_,
`#2989 <https://github.com/fmtlib/fmt/issues/2989>`_,
`#3000 <https://github.com/fmtlib/fmt/issues/3000>`_, `#3000 <https://github.com/fmtlib/fmt/issues/3000>`_,
`#3006 <https://github.com/fmtlib/fmt/issues/3006>`_, `#3006 <https://github.com/fmtlib/fmt/issues/3006>`_,
`#3014 <https://github.com/fmtlib/fmt/issues/3014>`_, `#3014 <https://github.com/fmtlib/fmt/issues/3014>`_,
@ -2260,7 +2692,7 @@
<https://github.com/kwesolowski>`_. <https://github.com/kwesolowski>`_.
* Replaced ``FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION`` with the ``FMT_FUZZ`` * Replaced ``FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION`` with the ``FMT_FUZZ``
macro to prevent interferring with fuzzing of projects using {fmt} macro to prevent interfering with fuzzing of projects using {fmt}
(`#1650 <https://github.com/fmtlib/fmt/pull/1650>`_). (`#1650 <https://github.com/fmtlib/fmt/pull/1650>`_).
Thanks `@asraa (Asra Ali) <https://github.com/asraa>`_. Thanks `@asraa (Asra Ali) <https://github.com/asraa>`_.

View File

@ -1,4 +1,4 @@
Copyright (c) 2012 - present, Victor Zverovich Copyright (c) 2012 - present, Victor Zverovich and {fmt} contributors
Permission is hereby granted, free of charge, to any person obtaining Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the a copy of this software and associated documentation files (the

View File

@ -47,7 +47,8 @@ Features
* `Format string syntax <https://fmt.dev/latest/syntax.html>`_ similar to Python's * `Format string syntax <https://fmt.dev/latest/syntax.html>`_ similar 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, shortness and * Fast IEEE 754 floating-point formatter with correct rounding, shortness and
round-trip guarantees round-trip guarantees using the `Dragonbox <https://github.com/jk-jeon/dragonbox>`_
algorithm
* Safe `printf implementation * Safe `printf implementation
<https://fmt.dev/latest/api.html#printf-formatting>`_ including the POSIX <https://fmt.dev/latest/api.html#printf-formatting>`_ including the POSIX
extension for positional arguments extension for positional arguments
@ -191,24 +192,24 @@ Speed tests
================= ============= =========== ================= ============= ===========
Library Method Run Time, s Library Method Run Time, s
================= ============= =========== ================= ============= ===========
libc printf 1.04 libc printf 0.91
libc++ std::ostream 3.05 libc++ std::ostream 2.49
{fmt} 6.1.1 fmt::print 0.75 {fmt} 9.1 fmt::print 0.74
Boost Format 1.67 boost::format 7.24 Boost Format 1.80 boost::format 6.26
Folly Format folly::format 2.23 Folly Format folly::format 1.87
================= ============= =========== ================= ============= ===========
{fmt} is the fastest of the benchmarked methods, ~35% faster than ``printf``. {fmt} is the fastest of the benchmarked methods, ~20% faster than ``printf``.
The above results were generated by building ``tinyformat_test.cpp`` on macOS The above results were generated by building ``tinyformat_test.cpp`` on macOS
10.14.6 with ``clang++ -O3 -DNDEBUG -DSPEED_TEST -DHAVE_FORMAT``, and taking the 12.6.1 with ``clang++ -O3 -DNDEBUG -DSPEED_TEST -DHAVE_FORMAT``, and taking the
best of three runs. In the test, the format string ``"%0.10f:%04d:%+g:%s:%p:%c:%%\n"`` best of three runs. In the test, the format string ``"%0.10f:%04d:%+g:%s:%p:%c:%%\n"``
or equivalent is filled 2,000,000 times with output sent to ``/dev/null``; for or equivalent is filled 2,000,000 times with output sent to ``/dev/null``; for
further details refer to the `source further details refer to the `source
<https://github.com/fmtlib/format-benchmark/blob/master/src/tinyformat-test.cc>`_. <https://github.com/fmtlib/format-benchmark/blob/master/src/tinyformat-test.cc>`_.
{fmt} is up to 20-30x faster than ``std::ostringstream`` and ``sprintf`` on {fmt} is up to 20-30x faster than ``std::ostringstream`` and ``sprintf`` on
floating-point formatting (`dtoa-benchmark <https://github.com/fmtlib/dtoa-benchmark>`_) IEEE754 ``float`` and ``double`` formatting (`dtoa-benchmark <https://github.com/fmtlib/dtoa-benchmark>`_)
and faster than `double-conversion <https://github.com/google/double-conversion>`_ and and faster than `double-conversion <https://github.com/google/double-conversion>`_ and
`ryu <https://github.com/ulfjack/ryu>`_: `ryu <https://github.com/ulfjack/ryu>`_:
@ -322,8 +323,10 @@ Projects using this library
* `ccache <https://ccache.dev/>`_: a compiler cache * `ccache <https://ccache.dev/>`_: a compiler cache
* `ClickHouse <https://github.com/ClickHouse/ClickHouse>`_: analytical database * `ClickHouse <https://github.com/ClickHouse/ClickHouse>`_: an analytical database
management system management system
* `Contour <https://github.com/contour-terminal/contour/>`_: a modern terminal emulator
* `CUAUV <https://cuauv.org/>`_: Cornell University's autonomous underwater * `CUAUV <https://cuauv.org/>`_: Cornell University's autonomous underwater
vehicle vehicle
@ -360,6 +363,10 @@ Projects using this library
* `Knuth <https://kth.cash/>`_: high-performance Bitcoin full-node * `Knuth <https://kth.cash/>`_: high-performance Bitcoin full-node
* `libunicode <https://github.com/contour-terminal/libunicode/>`_: a modern C++17 Unicode library
* `MariaDB <https://mariadb.org/>`_: relational database management system
* `Microsoft Verona <https://github.com/microsoft/verona>`_: * `Microsoft Verona <https://github.com/microsoft/verona>`_:
research programming language for concurrent ownership research programming language for concurrent ownership
@ -413,6 +420,9 @@ Projects using this library
* `TrinityCore <https://github.com/TrinityCore/TrinityCore>`_: open-source * `TrinityCore <https://github.com/TrinityCore/TrinityCore>`_: open-source
MMORPG framework MMORPG framework
* `🐙 userver framework <https://userver.tech/>`_: open-source asynchronous
framework with a rich set of abstractions and database drivers
* `Windows Terminal <https://github.com/microsoft/terminal>`_: the new Windows * `Windows Terminal <https://github.com/microsoft/terminal>`_: the new Windows
terminal terminal
@ -523,8 +533,7 @@ Maintainers
----------- -----------
The {fmt} library is maintained by Victor Zverovich (`vitaut The {fmt} library is maintained by Victor Zverovich (`vitaut
<https://github.com/vitaut>`_) and Jonathan Müller (`foonathan <https://github.com/vitaut>`_) with contributions from many other people.
<https://github.com/foonathan>`_) with contributions from many other people.
See `Contributors <https://github.com/fmtlib/fmt/graphs/contributors>`_ and See `Contributors <https://github.com/fmtlib/fmt/graphs/contributors>`_ and
`Releases <https://github.com/fmtlib/fmt/releases>`_ for some of the names. `Releases <https://github.com/fmtlib/fmt/releases>`_ for some of the names.
Let us know if your contribution is not listed or mentioned incorrectly and Let us know if your contribution is not listed or mentioned incorrectly and

File diff suppressed because it is too large Load Diff

View File

@ -11,7 +11,7 @@
#include "format.h" #include "format.h"
FMT_BEGIN_NAMESPACE FMT_BEGIN_NAMESPACE
FMT_MODULE_EXPORT_BEGIN FMT_BEGIN_EXPORT
enum class color : uint32_t { enum class color : uint32_t {
alice_blue = 0xF0F8FF, // rgb(240,248,255) alice_blue = 0xF0F8FF, // rgb(240,248,255)
@ -423,26 +423,6 @@ FMT_CONSTEXPR ansi_color_escape<Char> make_emphasis(emphasis em) noexcept {
return ansi_color_escape<Char>(em); return ansi_color_escape<Char>(em);
} }
template <typename Char> inline void fputs(const Char* chars, FILE* stream) {
int result = std::fputs(chars, stream);
if (result < 0)
FMT_THROW(system_error(errno, FMT_STRING("cannot write to file")));
}
template <> inline void fputs<wchar_t>(const wchar_t* chars, FILE* stream) {
int result = std::fputws(chars, stream);
if (result < 0)
FMT_THROW(system_error(errno, FMT_STRING("cannot write to file")));
}
template <typename Char> inline void reset_color(FILE* stream) {
fputs("\x1b[0m", stream);
}
template <> inline void reset_color<wchar_t>(FILE* stream) {
fputs(L"\x1b[0m", stream);
}
template <typename Char> inline void reset_color(buffer<Char>& buffer) { template <typename Char> inline void reset_color(buffer<Char>& buffer) {
auto reset_color = string_view("\x1b[0m"); auto reset_color = string_view("\x1b[0m");
buffer.append(reset_color.begin(), reset_color.end()); buffer.append(reset_color.begin(), reset_color.end());
@ -479,17 +459,19 @@ void vformat_to(buffer<Char>& buf, const text_style& ts,
FMT_END_DETAIL_NAMESPACE FMT_END_DETAIL_NAMESPACE
template <typename S, typename Char = char_t<S>> inline void vprint(std::FILE* f, const text_style& ts, string_view fmt,
void vprint(std::FILE* f, const text_style& ts, const S& format, format_args args) {
basic_format_args<buffer_context<type_identity_t<Char>>> args) { // Legacy wide streams are not supported.
basic_memory_buffer<Char> buf; auto buf = memory_buffer();
detail::vformat_to(buf, ts, detail::to_string_view(format), args); detail::vformat_to(buf, ts, fmt, args);
if (detail::is_utf8()) { if (detail::is_utf8()) {
detail::print(f, basic_string_view<Char>(buf.begin(), buf.size())); detail::print(f, string_view(buf.begin(), buf.size()));
} else { return;
buf.push_back(Char(0));
detail::fputs(buf.data(), f);
} }
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")));
} }
/** /**
@ -566,7 +548,7 @@ OutputIt vformat_to(
basic_format_args<buffer_context<type_identity_t<Char>>> args) { basic_format_args<buffer_context<type_identity_t<Char>>> args) {
auto&& buf = detail::get_buffer<Char>(out); auto&& buf = detail::get_buffer<Char>(out);
detail::vformat_to(buf, ts, format_str, args); detail::vformat_to(buf, ts, format_str, args);
return detail::get_iterator(buf); return detail::get_iterator(buf, out);
} }
/** /**
@ -645,7 +627,7 @@ FMT_CONSTEXPR auto styled(const T& value, text_style ts)
return detail::styled_arg<remove_cvref_t<T>>{value, ts}; return detail::styled_arg<remove_cvref_t<T>>{value, ts};
} }
FMT_MODULE_EXPORT_END FMT_END_EXPORT
FMT_END_NAMESPACE FMT_END_NAMESPACE
#endif // FMT_COLOR_H_ #endif // FMT_COLOR_H_

View File

@ -331,14 +331,14 @@ template <typename T, typename Char> struct parse_specs_result {
int next_arg_id; int next_arg_id;
}; };
constexpr int manual_indexing_id = -1; enum { manual_indexing_id = -1 };
template <typename T, typename Char> template <typename T, typename Char>
constexpr parse_specs_result<T, Char> parse_specs(basic_string_view<Char> str, constexpr parse_specs_result<T, Char> parse_specs(basic_string_view<Char> str,
size_t pos, int next_arg_id) { size_t pos, int next_arg_id) {
str.remove_prefix(pos); str.remove_prefix(pos);
auto ctx = compile_parse_context<Char>(str, max_value<int>(), nullptr, {}, auto ctx =
next_arg_id); compile_parse_context<Char>(str, max_value<int>(), nullptr, next_arg_id);
auto f = formatter<T, Char>(); auto f = formatter<T, Char>();
auto end = f.parse(ctx); auto end = f.parse(ctx);
return {f, pos + fmt::detail::to_unsigned(end - str.data()), return {f, pos + fmt::detail::to_unsigned(end - str.data()),
@ -348,22 +348,18 @@ 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_ref<Char> arg_id; arg_ref<Char> arg_id;
constexpr int operator()() { constexpr int on_auto() {
FMT_ASSERT(false, "handler cannot be used with automatic indexing"); FMT_ASSERT(false, "handler cannot be used with automatic indexing");
return 0; return 0;
} }
constexpr int operator()(int id) { constexpr int on_index(int id) {
arg_id = arg_ref<Char>(id); arg_id = arg_ref<Char>(id);
return 0; return 0;
} }
constexpr int operator()(basic_string_view<Char> id) { constexpr int on_name(basic_string_view<Char> id) {
arg_id = arg_ref<Char>(id); arg_id = arg_ref<Char>(id);
return 0; return 0;
} }
constexpr void on_error(const char* message) {
FMT_THROW(format_error(message));
}
}; };
template <typename Char> struct parse_arg_id_result { template <typename Char> struct parse_arg_id_result {
@ -501,7 +497,7 @@ constexpr auto compile(S format_str) {
#endif // defined(__cpp_if_constexpr) && defined(__cpp_return_type_deduction) #endif // defined(__cpp_if_constexpr) && defined(__cpp_return_type_deduction)
} // namespace detail } // namespace detail
FMT_MODULE_EXPORT_BEGIN FMT_BEGIN_EXPORT
#if defined(__cpp_if_constexpr) && defined(__cpp_return_type_deduction) #if defined(__cpp_if_constexpr) && defined(__cpp_return_type_deduction)
@ -605,7 +601,7 @@ template <detail_exported::fixed_string Str> constexpr auto operator""_cf() {
} // namespace literals } // namespace literals
#endif #endif
FMT_MODULE_EXPORT_END FMT_END_EXPORT
FMT_END_NAMESPACE FMT_END_NAMESPACE
#endif // FMT_COMPILE_H_ #endif // FMT_COMPILE_H_

File diff suppressed because it is too large Load Diff

View File

@ -9,13 +9,9 @@
#define FMT_FORMAT_INL_H_ #define FMT_FORMAT_INL_H_
#include <algorithm> #include <algorithm>
#include <cctype>
#include <cerrno> // errno #include <cerrno> // errno
#include <climits> #include <climits>
#include <cmath> #include <cmath>
#include <cstdarg>
#include <cstring> // std::memmove
#include <cwchar>
#include <exception> #include <exception>
#ifndef FMT_STATIC_THOUSANDS_SEPARATOR #ifndef FMT_STATIC_THOUSANDS_SEPARATOR
@ -115,16 +111,43 @@ template <typename Char> FMT_FUNC Char decimal_point_impl(locale_ref) {
return '.'; return '.';
} }
#endif #endif
FMT_FUNC auto write_loc(appender out, loc_value value,
const format_specs<>& specs, locale_ref loc) -> bool {
#ifndef FMT_STATIC_THOUSANDS_SEPARATOR
auto locale = loc.get<std::locale>();
// We cannot use the num_put<char> facet because it may produce output in
// a wrong encoding.
using facet = format_facet<std::locale>;
if (std::has_facet<facet>(locale))
return std::use_facet<facet>(locale).put(out, value, specs);
return facet(locale).put(out, value, specs);
#endif
return false;
}
} // namespace detail } // namespace detail
#if !FMT_MSC_VERSION template <typename Locale> typename Locale::id format_facet<Locale>::id;
FMT_API FMT_FUNC format_error::~format_error() noexcept = default;
#ifndef FMT_STATIC_THOUSANDS_SEPARATOR
template <typename Locale> format_facet<Locale>::format_facet(Locale& loc) {
auto& numpunct = std::use_facet<std::numpunct<char>>(loc);
grouping_ = numpunct.grouping();
if (!grouping_.empty()) separator_ = std::string(1, numpunct.thousands_sep());
}
template <>
FMT_API FMT_FUNC auto format_facet<std::locale>::do_put(
appender out, loc_value val, const format_specs<>& specs) const -> bool {
return val.visit(
detail::loc_writer<>{out, specs, separator_, grouping_, decimal_point_});
}
#endif #endif
FMT_FUNC std::system_error vsystem_error(int error_code, string_view format_str, FMT_FUNC std::system_error vsystem_error(int error_code, string_view fmt,
format_args args) { format_args args) {
auto ec = std::error_code(error_code, std::generic_category()); auto ec = std::error_code(error_code, std::generic_category());
return std::system_error(ec, vformat(format_str, args)); return std::system_error(ec, vformat(fmt, args));
} }
namespace detail { namespace detail {
@ -143,58 +166,8 @@ FMT_CONSTEXPR inline uint64_t rotr(uint64_t n, uint32_t r) noexcept {
return (n >> r) | (n << (64 - r)); return (n >> r) | (n << (64 - r));
} }
// Computes 128-bit result of multiplication of two 64-bit unsigned integers.
inline uint128_fallback umul128(uint64_t x, uint64_t y) noexcept {
#if FMT_USE_INT128
auto p = static_cast<uint128_opt>(x) * static_cast<uint128_opt>(y);
return {static_cast<uint64_t>(p >> 64), static_cast<uint64_t>(p)};
#elif defined(_MSC_VER) && defined(_M_X64)
auto result = uint128_fallback();
result.lo_ = _umul128(x, y, &result.hi_);
return result;
#else
const uint64_t mask = static_cast<uint64_t>(max_value<uint32_t>());
uint64_t a = x >> 32;
uint64_t b = x & mask;
uint64_t c = y >> 32;
uint64_t d = y & mask;
uint64_t ac = a * c;
uint64_t bc = b * c;
uint64_t ad = a * d;
uint64_t bd = b * d;
uint64_t intermediate = (bd >> 32) + (ad & mask) + (bc & mask);
return {ac + (intermediate >> 32) + (ad >> 32) + (bc >> 32),
(intermediate << 32) + (bd & mask)};
#endif
}
// Implementation of Dragonbox algorithm: https://github.com/jk-jeon/dragonbox. // Implementation of Dragonbox algorithm: https://github.com/jk-jeon/dragonbox.
namespace dragonbox { namespace dragonbox {
// Computes upper 64 bits of multiplication of two 64-bit unsigned integers.
inline uint64_t umul128_upper64(uint64_t x, uint64_t y) noexcept {
#if FMT_USE_INT128
auto p = static_cast<uint128_opt>(x) * static_cast<uint128_opt>(y);
return static_cast<uint64_t>(p >> 64);
#elif defined(_MSC_VER) && defined(_M_X64)
return __umulh(x, y);
#else
return umul128(x, y).high();
#endif
}
// Computes upper 128 bits of multiplication of a 64-bit unsigned integer and a
// 128-bit unsigned integer.
inline uint128_fallback umul192_upper128(uint64_t x,
uint128_fallback y) noexcept {
uint128_fallback r = umul128(x, y.high());
r += umul128_upper64(x, y.low());
return r;
}
// Computes upper 64 bits of multiplication of a 32-bit unsigned integer and a // Computes upper 64 bits of multiplication of a 32-bit unsigned integer and a
// 64-bit unsigned integer. // 64-bit unsigned integer.
inline uint64_t umul96_upper64(uint32_t x, uint64_t y) noexcept { inline uint64_t umul96_upper64(uint32_t x, uint64_t y) noexcept {
@ -216,25 +189,13 @@ inline uint64_t umul96_lower64(uint32_t x, uint64_t y) noexcept {
return x * y; return x * y;
} }
// Computes floor(log10(pow(2, e))) for e in [-2620, 2620] using the method from
// https://fmt.dev/papers/Dragonbox.pdf#page=28, section 6.1.
inline int floor_log10_pow2(int e) noexcept {
FMT_ASSERT(e <= 2620 && e >= -2620, "too large exponent");
static_assert((-1 >> 1) == -1, "right shift is not arithmetic");
return (e * 315653) >> 20;
}
// Various fast log computations. // Various fast log computations.
inline int floor_log2_pow10(int e) noexcept {
FMT_ASSERT(e <= 1233 && e >= -1233, "too large exponent");
return (e * 1741647) >> 19;
}
inline int floor_log10_pow2_minus_log10_4_over_3(int e) noexcept { inline int floor_log10_pow2_minus_log10_4_over_3(int e) noexcept {
FMT_ASSERT(e <= 2936 && e >= -2985, "too large exponent"); FMT_ASSERT(e <= 2936 && e >= -2985, "too large exponent");
return (e * 631305 - 261663) >> 21; return (e * 631305 - 261663) >> 21;
} }
static constexpr struct { FMT_INLINE_VARIABLE constexpr struct {
uint32_t divisor; uint32_t divisor;
int shift_amount; int shift_amount;
} div_small_pow10_infos[] = {{10, 16}, {100, 16}}; } div_small_pow10_infos[] = {{10, 16}, {100, 16}};
@ -288,7 +249,7 @@ inline uint64_t divide_by_10_to_kappa_plus_1(uint64_t n) noexcept {
} }
// Various subroutines using pow10 cache // Various subroutines using pow10 cache
template <class T> struct cache_accessor; template <typename T> struct cache_accessor;
template <> struct cache_accessor<float> { template <> struct cache_accessor<float> {
using carrier_uint = float_info<float>::carrier_uint; using carrier_uint = float_info<float>::carrier_uint;
@ -1009,8 +970,23 @@ template <> struct cache_accessor<double> {
{0xfcf62c1dee382c42, 0x46729e03dd9ed7b6}, {0xfcf62c1dee382c42, 0x46729e03dd9ed7b6},
{0x9e19db92b4e31ba9, 0x6c07a2c26a8346d2}, {0x9e19db92b4e31ba9, 0x6c07a2c26a8346d2},
{0xc5a05277621be293, 0xc7098b7305241886}, {0xc5a05277621be293, 0xc7098b7305241886},
{ 0xf70867153aa2db38, {0xf70867153aa2db38, 0xb8cbee4fc66d1ea8},
0xb8cbee4fc66d1ea8 } {0x9a65406d44a5c903, 0x737f74f1dc043329},
{0xc0fe908895cf3b44, 0x505f522e53053ff3},
{0xf13e34aabb430a15, 0x647726b9e7c68ff0},
{0x96c6e0eab509e64d, 0x5eca783430dc19f6},
{0xbc789925624c5fe0, 0xb67d16413d132073},
{0xeb96bf6ebadf77d8, 0xe41c5bd18c57e890},
{0x933e37a534cbaae7, 0x8e91b962f7b6f15a},
{0xb80dc58e81fe95a1, 0x723627bbb5a4adb1},
{0xe61136f2227e3b09, 0xcec3b1aaa30dd91d},
{0x8fcac257558ee4e6, 0x213a4f0aa5e8a7b2},
{0xb3bd72ed2af29e1f, 0xa988e2cd4f62d19e},
{0xe0accfa875af45a7, 0x93eb1b80a33b8606},
{0x8c6c01c9498d8b88, 0xbc72f130660533c4},
{0xaf87023b9bf0ee6a, 0xeb8fad7c7f8680b5},
{ 0xdb68c2ca82ed2a05,
0xa67398db9f6820e2 }
#else #else
{0xff77b1fcbebcdc4f, 0x25e8e89c13bb0f7b}, {0xff77b1fcbebcdc4f, 0x25e8e89c13bb0f7b},
{0xce5d73ff402d98e3, 0xfb0a3d212dc81290}, {0xce5d73ff402d98e3, 0xfb0a3d212dc81290},
@ -1034,8 +1010,8 @@ template <> struct cache_accessor<double> {
{0x8da471a9de737e24, 0x5ceaecfed289e5d3}, {0x8da471a9de737e24, 0x5ceaecfed289e5d3},
{0xe4d5e82392a40515, 0x0fabaf3feaa5334b}, {0xe4d5e82392a40515, 0x0fabaf3feaa5334b},
{0xb8da1662e7b00a17, 0x3d6a751f3b936244}, {0xb8da1662e7b00a17, 0x3d6a751f3b936244},
{ 0x95527a5202df0ccb, {0x95527a5202df0ccb, 0x0f37801e0c43ebc9},
0x0f37801e0c43ebc9 } {0xf13e34aabb430a15, 0x647726b9e7c68ff0}
#endif #endif
}; };
@ -1138,8 +1114,12 @@ template <> struct cache_accessor<double> {
} }
}; };
FMT_FUNC uint128_fallback get_cached_power(int k) noexcept {
return cache_accessor<double>::get_cached_power(k);
}
// Various integer checks // Various integer checks
template <class T> template <typename T>
bool is_left_endpoint_integer_shorter_interval(int exponent) noexcept { bool is_left_endpoint_integer_shorter_interval(int exponent) noexcept {
const int case_shorter_interval_left_endpoint_lower_threshold = 2; const int case_shorter_interval_left_endpoint_lower_threshold = 2;
const int case_shorter_interval_left_endpoint_upper_threshold = 3; const int case_shorter_interval_left_endpoint_upper_threshold = 3;
@ -1150,8 +1130,12 @@ bool is_left_endpoint_integer_shorter_interval(int exponent) noexcept {
// Remove trailing zeros from n and return the number of zeros removed (float) // Remove trailing zeros from n and return the number of zeros removed (float)
FMT_INLINE int remove_trailing_zeros(uint32_t& n) noexcept { FMT_INLINE int remove_trailing_zeros(uint32_t& n) noexcept {
FMT_ASSERT(n != 0, ""); FMT_ASSERT(n != 0, "");
// Modular inverse of 5 (mod 2^32): (mod_inv_5 * 5) mod 2^32 = 1.
// See https://github.com/fmtlib/fmt/issues/3163 for more details.
const uint32_t mod_inv_5 = 0xcccccccd; const uint32_t mod_inv_5 = 0xcccccccd;
const uint32_t mod_inv_25 = mod_inv_5 * mod_inv_5; // Casts are needed to workaround a bug in MSVC 19.22 and older.
const uint32_t mod_inv_25 =
static_cast<uint32_t>(uint64_t(mod_inv_5) * mod_inv_5);
int s = 0; int s = 0;
while (true) { while (true) {
@ -1165,7 +1149,6 @@ FMT_INLINE int remove_trailing_zeros(uint32_t& n) noexcept {
n = q; n = q;
s |= 1; s |= 1;
} }
return s; return s;
} }
@ -1223,7 +1206,7 @@ FMT_INLINE int remove_trailing_zeros(uint64_t& n) noexcept {
} }
// The main algorithm for shorter interval case // The main algorithm for shorter interval case
template <class T> template <typename T>
FMT_INLINE decimal_fp<T> shorter_interval_case(int exponent) noexcept { FMT_INLINE decimal_fp<T> shorter_interval_case(int exponent) noexcept {
decimal_fp<T> ret_value; decimal_fp<T> ret_value;
// Compute k and beta // Compute k and beta
@ -1394,17 +1377,6 @@ small_divisor_case_label:
return ret_value; return ret_value;
} }
} // namespace dragonbox } // namespace dragonbox
#ifdef _MSC_VER
FMT_FUNC auto fmt_snprintf(char* buf, size_t size, const char* fmt, ...)
-> int {
auto args = va_list();
va_start(args, fmt);
int result = vsnprintf_s(buf, size, _TRUNCATE, fmt, args);
va_end(args);
return result;
}
#endif
} // namespace detail } // namespace detail
template <> struct formatter<detail::bigint> { template <> struct formatter<detail::bigint> {
@ -1413,9 +1385,8 @@ template <> struct formatter<detail::bigint> {
return ctx.begin(); return ctx.begin();
} }
template <typename FormatContext> auto format(const detail::bigint& n, format_context& ctx) const
auto format(const detail::bigint& n, FormatContext& ctx) const -> -> format_context::iterator {
typename FormatContext::iterator {
auto out = ctx.out(); auto out = ctx.out();
bool first = true; bool first = true;
for (auto i = n.bigits_.size(); i > 0; --i) { for (auto i = n.bigits_.size(); i > 0; --i) {
@ -1474,57 +1445,44 @@ FMT_FUNC std::string vformat(string_view fmt, format_args args) {
} }
namespace detail { namespace detail {
#ifdef _WIN32 #ifndef _WIN32
FMT_FUNC bool write_console(std::FILE*, string_view) { return false; }
#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( //
void*, const void*, dword, dword*, void*); void*, const void*, dword, dword*, void*);
FMT_FUNC bool write_console(std::FILE* f, string_view text) { FMT_FUNC bool write_console(std::FILE* f, string_view text) {
auto fd = _fileno(f); auto fd = _fileno(f);
if (_isatty(fd)) { if (!_isatty(fd)) return false;
detail::utf8_to_utf16 u16(string_view(text.data(), text.size())); auto u16 = utf8_to_utf16(text);
auto written = detail::dword(); auto written = dword();
if (detail::WriteConsoleW(reinterpret_cast<void*>(_get_osfhandle(fd)), return WriteConsoleW(reinterpret_cast<void*>(_get_osfhandle(fd)), u16.c_str(),
u16.c_str(), static_cast<uint32_t>(u16.size()), static_cast<uint32_t>(u16.size()), &written, nullptr);
&written, nullptr)) {
return true;
}
}
// We return false if the file descriptor was not TTY, or it was but
// SetConsoleW failed which can happen if the output has been redirected to
// NUL. In both cases when we return false, we should attempt to do regular
// write via fwrite or std::ostream::write.
return false;
}
#endif
FMT_FUNC void print(std::FILE* f, string_view text) {
#ifdef _WIN32
if (write_console(f, text)) return;
#endif
detail::fwrite_fully(text.data(), 1, text.size(), f);
}
} // namespace detail
FMT_FUNC void vprint(std::FILE* f, string_view format_str, format_args args) {
memory_buffer buffer;
detail::vformat_to(buffer, format_str, args);
detail::print(f, {buffer.data(), buffer.size()});
} }
#ifdef _WIN32
// Print assuming legacy (non-Unicode) encoding. // Print assuming legacy (non-Unicode) encoding.
FMT_FUNC void detail::vprint_mojibake(std::FILE* f, string_view format_str, FMT_FUNC void vprint_mojibake(std::FILE* f, string_view fmt, format_args args) {
format_args args) { auto buffer = memory_buffer();
memory_buffer buffer; detail::vformat_to(buffer, fmt,
detail::vformat_to(buffer, format_str,
basic_format_args<buffer_context<char>>(args)); basic_format_args<buffer_context<char>>(args));
fwrite_fully(buffer.data(), 1, buffer.size(), f); fwrite_fully(buffer.data(), 1, buffer.size(), f);
} }
#endif #endif
FMT_FUNC void vprint(string_view format_str, format_args args) { FMT_FUNC void print(std::FILE* f, string_view text) {
vprint(stdout, format_str, args); if (!write_console(f, text)) fwrite_fully(text.data(), 1, text.size(), f);
}
} // namespace detail
FMT_FUNC void vprint(std::FILE* f, string_view fmt, format_args args) {
auto buffer = memory_buffer();
detail::vformat_to(buffer, fmt, args);
detail::print(f, {buffer.data(), buffer.size()});
}
FMT_FUNC void vprint(string_view fmt, format_args args) {
vprint(stdout, fmt, args);
} }
namespace detail { namespace detail {

File diff suppressed because it is too large Load Diff

View File

@ -71,7 +71,7 @@
#define FMT_RETRY(result, expression) FMT_RETRY_VAL(result, expression, -1) #define FMT_RETRY(result, expression) FMT_RETRY_VAL(result, expression, -1)
FMT_BEGIN_NAMESPACE FMT_BEGIN_NAMESPACE
FMT_MODULE_EXPORT_BEGIN FMT_BEGIN_EXPORT
/** /**
\rst \rst
@ -120,48 +120,10 @@ template <typename Char> class basic_cstring_view {
using cstring_view = basic_cstring_view<char>; using cstring_view = basic_cstring_view<char>;
using wcstring_view = basic_cstring_view<wchar_t>; using wcstring_view = basic_cstring_view<wchar_t>;
template <typename Char> struct formatter<std::error_code, Char> {
template <typename ParseContext>
FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
return ctx.begin();
}
template <typename FormatContext>
FMT_CONSTEXPR auto format(const std::error_code& ec, FormatContext& ctx) const
-> decltype(ctx.out()) {
auto out = ctx.out();
out = detail::write_bytes(out, ec.category().name(),
basic_format_specs<Char>());
out = detail::write<Char>(out, Char(':'));
out = detail::write<Char>(out, ec.value());
return out;
}
};
#ifdef _WIN32 #ifdef _WIN32
FMT_API const std::error_category& system_category() noexcept; FMT_API const std::error_category& system_category() noexcept;
FMT_BEGIN_DETAIL_NAMESPACE FMT_BEGIN_DETAIL_NAMESPACE
// A converter from UTF-16 to UTF-8.
// It is only provided for Windows since other systems support UTF-8 natively.
class utf16_to_utf8 {
private:
memory_buffer buffer_;
public:
utf16_to_utf8() {}
FMT_API explicit utf16_to_utf8(basic_string_view<wchar_t> s);
operator string_view() const { return string_view(&buffer_[0], size()); }
size_t size() const { return buffer_.size() - 1; }
const char* c_str() const { return &buffer_[0]; }
std::string str() const { return std::string(&buffer_[0], size()); }
// Performs conversion returning a system error code instead of
// throwing exception on conversion error. This method may still throw
// in case of memory allocation error.
FMT_API int convert(basic_string_view<wchar_t> s);
};
FMT_API void format_windows_error(buffer<char>& out, int error_code, FMT_API void format_windows_error(buffer<char>& out, int error_code,
const char* message) noexcept; const char* message) noexcept;
FMT_END_DETAIL_NAMESPACE FMT_END_DETAIL_NAMESPACE
@ -355,6 +317,12 @@ class FMT_API file {
// 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.
buffered_file fdopen(const char* mode); buffered_file fdopen(const char* mode);
# if defined(_WIN32) && !defined(__MINGW32__)
// Opens a file and constructs a file object representing this file by
// wcstring_view filename. Windows only.
static file open_windows_file(wcstring_view path, int oflag);
# endif
}; };
// Returns the memory page size. // Returns the memory page size.
@ -397,6 +365,28 @@ struct ostream_params {
# endif # endif
}; };
class file_buffer final : public buffer<char> {
file file_;
FMT_API void grow(size_t) override;
public:
FMT_API file_buffer(cstring_view path, const ostream_params& params);
FMT_API file_buffer(file_buffer&& other);
FMT_API ~file_buffer();
void flush() {
if (size() == 0) return;
file_.write(data(), size() * sizeof(data()[0]));
clear();
}
void close() {
flush();
file_.close();
}
};
FMT_END_DETAIL_NAMESPACE FMT_END_DETAIL_NAMESPACE
// Added {} below to work around default constructor error known to // Added {} below to work around default constructor error known to
@ -404,49 +394,32 @@ FMT_END_DETAIL_NAMESPACE
constexpr detail::buffer_size buffer_size{}; constexpr detail::buffer_size buffer_size{};
/** A fast output stream which is not thread-safe. */ /** A fast output stream which is not thread-safe. */
class FMT_API ostream final : private detail::buffer<char> { class FMT_API ostream {
private: private:
file file_; FMT_MSC_WARNING(suppress : 4251)
detail::file_buffer buffer_;
void grow(size_t) override;
ostream(cstring_view path, const detail::ostream_params& params) ostream(cstring_view path, const detail::ostream_params& params)
: file_(path, params.oflag) { : buffer_(path, params) {}
set(new char[params.buffer_size], params.buffer_size);
}
public: public:
ostream(ostream&& other) ostream(ostream&& other) : buffer_(std::move(other.buffer_)) {}
: detail::buffer<char>(other.data(), other.size(), other.capacity()),
file_(std::move(other.file_)) {
other.clear();
other.set(nullptr, 0);
}
~ostream() {
flush();
delete[] data();
}
void flush() { ~ostream();
if (size() == 0) return;
file_.write(data(), size()); void flush() { buffer_.flush(); }
clear();
}
template <typename... T> template <typename... T>
friend ostream output_file(cstring_view path, T... params); friend ostream output_file(cstring_view path, T... params);
void close() { void close() { buffer_.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(detail::buffer_appender<char>(*this), fmt, vformat_to(detail::buffer_appender<char>(buffer_), fmt,
fmt::make_format_args(args...)); fmt::make_format_args(args...));
} }
}; };
@ -472,7 +445,7 @@ inline ostream output_file(cstring_view path, T... params) {
} }
#endif // FMT_USE_FCNTL #endif // FMT_USE_FCNTL
FMT_MODULE_EXPORT_END FMT_END_EXPORT
FMT_END_NAMESPACE FMT_END_NAMESPACE
#endif // FMT_OS_H_ #endif // FMT_OS_H_

View File

@ -8,8 +8,8 @@
#ifndef FMT_OSTREAM_H_ #ifndef FMT_OSTREAM_H_
#define FMT_OSTREAM_H_ #define FMT_OSTREAM_H_
#include <fstream> #include <fstream> // std::filebuf
#include <ostream>
#if defined(_WIN32) && defined(__GLIBCXX__) #if defined(_WIN32) && defined(__GLIBCXX__)
# include <ext/stdio_filebuf.h> # include <ext/stdio_filebuf.h>
# include <ext/stdio_sync_filebuf.h> # include <ext/stdio_sync_filebuf.h>
@ -21,48 +21,14 @@
FMT_BEGIN_NAMESPACE FMT_BEGIN_NAMESPACE
template <typename OutputIt, typename Char> class basic_printf_context;
namespace detail { namespace detail {
// Checks if T has a user-defined operator<<.
template <typename T, typename Char, typename Enable = void>
class is_streamable {
private:
template <typename U>
static auto test(int)
-> bool_constant<sizeof(std::declval<std::basic_ostream<Char>&>()
<< std::declval<U>()) != 0>;
template <typename> static auto test(...) -> std::false_type;
using result = decltype(test<T>(0));
public:
is_streamable() = default;
static const bool value = result::value;
};
// Formatting of built-in types and arrays is intentionally disabled because
// it's handled by standard (non-ostream) formatters.
template <typename T, typename Char>
struct is_streamable<
T, Char,
enable_if_t<
std::is_arithmetic<T>::value || std::is_array<T>::value ||
std::is_pointer<T>::value || std::is_same<T, char8_type>::value ||
std::is_convertible<T, fmt::basic_string_view<Char>>::value ||
std::is_same<T, std_string_view<Char>>::value ||
(std::is_convertible<T, int>::value && !std::is_enum<T>::value)>>
: std::false_type {};
// 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 {
struct file_access_tag {}; struct file_access_tag {};
} // namespace } // namespace
template <class Tag, class BufType, FILE* BufType::*FileMemberPtr> template <typename Tag, typename BufType, FILE* BufType::*FileMemberPtr>
class file_access { class file_access {
friend auto get_file(BufType& obj) -> FILE* { return obj.*FileMemberPtr; } friend auto get_file(BufType& obj) -> FILE* { return obj.*FileMemberPtr; }
}; };
@ -84,10 +50,10 @@ inline bool write_ostream_unicode(std::ostream& os, fmt::string_view data) {
#elif defined(_WIN32) && defined(__GLIBCXX__) #elif defined(_WIN32) && defined(__GLIBCXX__)
auto* rdbuf = os.rdbuf(); auto* rdbuf = os.rdbuf();
FILE* c_file; FILE* c_file;
if (auto* fbuf_sy = dynamic_cast<__gnu_cxx::stdio_sync_filebuf<char>*>(rdbuf)) if (auto* sfbuf = dynamic_cast<__gnu_cxx::stdio_sync_filebuf<char>*>(rdbuf))
c_file = fbuf_sy->file(); c_file = sfbuf->file();
else if (auto* fbuf_as = dynamic_cast<__gnu_cxx::stdio_filebuf<char>*>(rdbuf)) else if (auto* fbuf = dynamic_cast<__gnu_cxx::stdio_filebuf<char>*>(rdbuf))
c_file = fbuf_as->file(); c_file = fbuf->file();
else else
return false; return false;
if (c_file) return write_console(c_file, data); if (c_file) return write_console(c_file, data);
@ -145,7 +111,7 @@ struct basic_ostream_formatter : formatter<basic_string_view<Char>, Char> {
auto format(const T& value, basic_format_context<OutputIt, Char>& ctx) const auto format(const T& value, basic_format_context<OutputIt, Char>& ctx) const
-> OutputIt { -> OutputIt {
auto buffer = basic_memory_buffer<Char>(); auto buffer = basic_memory_buffer<Char>();
format_value(buffer, value, ctx.locale()); detail::format_value(buffer, value, ctx.locale());
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);
} }
@ -180,13 +146,6 @@ auto streamed(const T& value) -> detail::streamed_view<T> {
namespace detail { namespace detail {
// Formats an object of type T that has an overloaded ostream operator<<.
template <typename T, typename Char>
struct fallback_formatter<T, Char, enable_if_t<is_streamable<T, Char>::value>>
: basic_ostream_formatter<Char> {
using basic_ostream_formatter<Char>::format;
};
inline void vprint_directly(std::ostream& os, string_view format_str, inline void vprint_directly(std::ostream& os, string_view format_str,
format_args args) { format_args args) {
auto buffer = memory_buffer(); auto buffer = memory_buffer();
@ -232,6 +191,19 @@ void print(std::wostream& os,
vprint(os, fmt, fmt::make_format_args<buffer_context<wchar_t>>(args...)); vprint(os, fmt, fmt::make_format_args<buffer_context<wchar_t>>(args...));
} }
FMT_MODULE_EXPORT template <typename... T>
void println(std::ostream& os, format_string<T...> fmt, T&&... args) {
fmt::print(os, "{}\n", fmt::format(fmt, std::forward<T>(args)...));
}
FMT_MODULE_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

@ -14,7 +14,7 @@
#include "format.h" #include "format.h"
FMT_BEGIN_NAMESPACE FMT_BEGIN_NAMESPACE
FMT_MODULE_EXPORT_BEGIN FMT_BEGIN_EXPORT
template <typename T> struct printf_formatter { printf_formatter() = delete; }; template <typename T> struct printf_formatter { printf_formatter() = delete; };
@ -81,13 +81,13 @@ class 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)>
int operator()(T value) { int operator()(T value) {
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))
FMT_THROW(format_error("number is too big")); throw_format_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)>
int operator()(T) { int operator()(T) {
FMT_THROW(format_error("precision is not integer")); throw_format_error("precision is not integer");
return 0; return 0;
} }
}; };
@ -194,12 +194,10 @@ template <typename Char> struct get_cstring {
// left alignment if it is negative. // left alignment if it is negative.
template <typename Char> class printf_width_handler { template <typename Char> class printf_width_handler {
private: private:
using format_specs = basic_format_specs<Char>; format_specs<Char>& specs_;
format_specs& specs_;
public: public:
explicit printf_width_handler(format_specs& specs) : specs_(specs) {} explicit printf_width_handler(format_specs<Char>& 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)>
unsigned operator()(T value) { unsigned operator()(T value) {
@ -209,24 +207,31 @@ template <typename Char> class printf_width_handler {
width = 0 - width; width = 0 - width;
} }
unsigned int_max = max_value<int>(); unsigned int_max = max_value<int>();
if (width > int_max) FMT_THROW(format_error("number is too big")); if (width > int_max) throw_format_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)>
unsigned operator()(T) { unsigned operator()(T) {
FMT_THROW(format_error("width is not integer")); throw_format_error("width is not integer");
return 0; return 0;
} }
}; };
// Workaround for a bug with the XL compiler when initializing
// printf_arg_formatter's base class.
template <typename Char>
auto make_arg_formatter(buffer_appender<Char> iter, format_specs<Char>& s)
-> arg_formatter<Char> {
return {iter, s, locale_ref()};
}
// The ``printf`` argument formatter. // The ``printf`` argument formatter.
template <typename OutputIt, typename Char> template <typename OutputIt, typename Char>
class printf_arg_formatter : public arg_formatter<Char> { class printf_arg_formatter : public arg_formatter<Char> {
private: private:
using base = arg_formatter<Char>; using base = arg_formatter<Char>;
using context_type = basic_printf_context<OutputIt, Char>; using context_type = basic_printf_context<OutputIt, Char>;
using format_specs = basic_format_specs<Char>;
context_type& context_; context_type& context_;
@ -237,8 +242,8 @@ class printf_arg_formatter : public arg_formatter<Char> {
} }
public: public:
printf_arg_formatter(OutputIt iter, format_specs& s, context_type& ctx) printf_arg_formatter(OutputIt iter, format_specs<Char>& s, context_type& ctx)
: base{iter, s, locale_ref()}, context_(ctx) {} : base(make_arg_formatter(iter, s)), context_(ctx) {}
OutputIt operator()(monostate value) { return base::operator()(value); } OutputIt operator()(monostate value) { return base::operator()(value); }
@ -247,7 +252,7 @@ class printf_arg_formatter : public arg_formatter<Char> {
// 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) {
format_specs fmt_specs = this->specs; format_specs<Char> fmt_specs = this->specs;
if (fmt_specs.type != presentation_type::none && if (fmt_specs.type != presentation_type::none &&
fmt_specs.type != presentation_type::chr) { fmt_specs.type != presentation_type::chr) {
return (*this)(static_cast<int>(value)); return (*this)(static_cast<int>(value));
@ -300,8 +305,7 @@ class printf_arg_formatter : public arg_formatter<Char> {
}; };
template <typename Char> template <typename Char>
void parse_flags(basic_format_specs<Char>& specs, const Char*& it, void parse_flags(format_specs<Char>& specs, const Char*& it, const Char* end) {
const Char* end) {
for (; it != end; ++it) { for (; it != end; ++it) {
switch (*it) { switch (*it) {
case '-': case '-':
@ -328,8 +332,8 @@ void parse_flags(basic_format_specs<Char>& specs, const Char*& it,
} }
template <typename Char, typename GetArg> template <typename Char, typename GetArg>
int parse_header(const Char*& it, const Char* end, int parse_header(const Char*& it, const Char* end, format_specs<Char>& specs,
basic_format_specs<Char>& specs, GetArg get_arg) { GetArg get_arg) {
int arg_index = -1; int arg_index = -1;
Char c = *it; Char c = *it;
if (c >= '0' && c <= '9') { if (c >= '0' && c <= '9') {
@ -344,7 +348,7 @@ int parse_header(const Char*& it, const Char* end,
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) FMT_THROW(format_error("number is too big")); if (value == -1) throw_format_error("number is too big");
specs.width = value; specs.width = value;
return arg_index; return arg_index;
} }
@ -355,7 +359,7 @@ int parse_header(const Char*& it, const Char* end,
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) FMT_THROW(format_error("number is too big")); if (specs.width == -1) throw_format_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>(visit_format_arg(
@ -365,12 +369,52 @@ int parse_header(const Char*& it, const Char* end,
return arg_index; return arg_index;
} }
inline auto parse_printf_presentation_type(char c, type t)
-> presentation_type {
using pt = presentation_type;
constexpr auto integral_set = sint_set | uint_set | bool_set | char_set;
switch (c) {
case 'd':
return in(t, integral_set) ? pt::dec : pt::none;
case 'o':
return in(t, integral_set) ? pt::oct : pt::none;
case 'x':
return in(t, integral_set) ? pt::hex_lower : pt::none;
case 'X':
return in(t, integral_set) ? pt::hex_upper : pt::none;
case 'a':
return in(t, float_set) ? pt::hexfloat_lower : pt::none;
case 'A':
return in(t, float_set) ? pt::hexfloat_upper : pt::none;
case 'e':
return in(t, float_set) ? pt::exp_lower : pt::none;
case 'E':
return in(t, float_set) ? pt::exp_upper : 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 OutputIt = buffer_appender<Char>; using iterator = buffer_appender<Char>;
auto out = OutputIt(buf); auto out = iterator(buf);
auto context = basic_printf_context<OutputIt, Char>(out, args); auto context = basic_printf_context<iterator, Char>(out, args);
auto parse_ctx = basic_printf_parse_context<Char>(format); auto parse_ctx = basic_printf_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
@ -387,26 +431,25 @@ void vprintf(buffer<Char>& buf, basic_string_view<Char> format,
const Char* end = parse_ctx.end(); const Char* end = parse_ctx.end();
auto it = start; auto it = start;
while (it != end) { while (it != end) {
if (!detail::find<false, Char>(it, end, '%', it)) { if (!find<false, Char>(it, end, '%', it)) {
it = end; // detail::find leaves it == nullptr if it doesn't find '%' it = end; // find leaves it == nullptr if it doesn't find '%'.
break; break;
} }
Char c = *it++; Char c = *it++;
if (it != end && *it == c) { if (it != end && *it == c) {
out = detail::write( out = write(out, basic_string_view<Char>(start, to_unsigned(it - start)));
out, basic_string_view<Char>(start, detail::to_unsigned(it - start)));
start = ++it; start = ++it;
continue; continue;
} }
out = detail::write(out, basic_string_view<Char>( out =
start, detail::to_unsigned(it - 1 - start))); write(out, basic_string_view<Char>(start, to_unsigned(it - 1 - start)));
basic_format_specs<Char> specs; auto specs = format_specs<Char>();
specs.align = align::right; specs.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) parse_ctx.on_error("argument not found"); if (arg_index == 0) throw_format_error("argument not found");
// Parse precision. // Parse precision.
if (it != end && *it == '.') { if (it != end && *it == '.') {
@ -417,7 +460,7 @@ void vprintf(buffer<Char>& buf, basic_string_view<Char> format,
} else if (c == '*') { } else if (c == '*') {
++it; ++it;
specs.precision = static_cast<int>( specs.precision = static_cast<int>(
visit_format_arg(detail::printf_precision_handler(), get_arg(-1))); visit_format_arg(printf_precision_handler(), get_arg(-1)));
} else { } else {
specs.precision = 0; specs.precision = 0;
} }
@ -429,17 +472,15 @@ void vprintf(buffer<Char>& buf, basic_string_view<Char> format,
if (specs.precision >= 0 && arg.is_integral()) if (specs.precision >= 0 && arg.is_integral())
specs.fill[0] = specs.fill[0] =
' '; // Ignore '0' flag for non-numeric types or if '-' present. ' '; // Ignore '0' flag for non-numeric types or if '-' present.
if (specs.precision >= 0 && arg.type() == detail::type::cstring_type) { if (specs.precision >= 0 && arg.type() == type::cstring_type) {
auto str = visit_format_arg(detail::get_cstring<Char>(), arg); auto str = visit_format_arg(get_cstring<Char>(), arg);
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());
arg = detail::make_arg<basic_printf_context<OutputIt, Char>>( arg = make_arg<basic_printf_context<iterator, Char>>(
basic_string_view<Char>( basic_string_view<Char>(
str, detail::to_unsigned(nul != str_end ? nul - str str, to_unsigned(nul != str_end ? nul - str : specs.precision)));
: specs.precision)));
} }
if (specs.alt && visit_format_arg(detail::is_zero_int(), arg)) if (specs.alt && visit_format_arg(is_zero_int(), arg)) specs.alt = false;
specs.alt = false;
if (specs.fill[0] == '0') { if (specs.fill[0] == '0') {
if (arg.is_arithmetic() && specs.align != align::left) if (arg.is_arithmetic() && specs.align != align::left)
specs.align = align::numeric; specs.align = align::numeric;
@ -451,7 +492,6 @@ void vprintf(buffer<Char>& buf, basic_string_view<Char> format,
// Parse length and convert the argument to the required type. // Parse length and convert the argument to the required type.
c = it != end ? *it++ : 0; c = it != end ? *it++ : 0;
Char t = it != end ? *it : 0; Char t = it != end ? *it : 0;
using detail::convert_arg;
switch (c) { switch (c) {
case 'h': case 'h':
if (t == 'h') { if (t == 'h') {
@ -490,7 +530,7 @@ void vprintf(buffer<Char>& buf, basic_string_view<Char> format,
} }
// Parse type. // Parse type.
if (it == end) FMT_THROW(format_error("invalid format string")); if (it == end) throw_format_error("invalid format string");
char type = static_cast<char>(*it++); char type = static_cast<char>(*it++);
if (arg.is_integral()) { if (arg.is_integral()) {
// Normalize type. // Normalize type.
@ -501,22 +541,21 @@ void vprintf(buffer<Char>& buf, basic_string_view<Char> format,
break; break;
case 'c': case 'c':
visit_format_arg( visit_format_arg(
detail::char_converter<basic_printf_context<OutputIt, Char>>(arg), char_converter<basic_printf_context<iterator, Char>>(arg), arg);
arg);
break; break;
} }
} }
specs.type = parse_presentation_type(type); specs.type = parse_printf_presentation_type(type, arg.type());
if (specs.type == presentation_type::none) if (specs.type == presentation_type::none)
parse_ctx.on_error("invalid type specifier"); throw_format_error("invalid format specifier");
start = it; start = it;
// Format argument. // Format argument.
out = visit_format_arg( out = visit_format_arg(
detail::printf_arg_formatter<OutputIt, Char>(out, specs, context), arg); printf_arg_formatter<iterator, Char>(out, specs, context), arg);
} }
detail::write(out, basic_string_view<Char>(start, to_unsigned(it - start))); write(out, basic_string_view<Char>(start, to_unsigned(it - start)));
} }
FMT_END_DETAIL_NAMESPACE FMT_END_DETAIL_NAMESPACE
@ -559,9 +598,9 @@ inline auto vsprintf(
const S& fmt, const S& fmt,
basic_format_args<basic_printf_context_t<type_identity_t<Char>>> args) basic_format_args<basic_printf_context_t<type_identity_t<Char>>> args)
-> std::basic_string<Char> { -> std::basic_string<Char> {
basic_memory_buffer<Char> buffer; auto buf = basic_memory_buffer<Char>();
vprintf(buffer, detail::to_string_view(fmt), args); detail::vprintf(buf, detail::to_string_view(fmt), args);
return to_string(buffer); return to_string(buf);
} }
/** /**
@ -586,10 +625,10 @@ inline auto vfprintf(
std::FILE* f, const S& fmt, std::FILE* f, const S& fmt,
basic_format_args<basic_printf_context_t<type_identity_t<Char>>> args) basic_format_args<basic_printf_context_t<type_identity_t<Char>>> args)
-> int { -> int {
basic_memory_buffer<Char> buffer; auto buf = basic_memory_buffer<Char>();
vprintf(buffer, detail::to_string_view(fmt), args); detail::vprintf(buf, detail::to_string_view(fmt), args);
size_t size = buffer.size(); size_t size = buf.size();
return std::fwrite(buffer.data(), sizeof(Char), size, f) < size return std::fwrite(buf.data(), sizeof(Char), size, f) < size
? -1 ? -1
: static_cast<int>(size); : static_cast<int>(size);
} }
@ -634,7 +673,7 @@ inline auto printf(const S& fmt, const T&... args) -> int {
fmt::make_format_args<basic_printf_context_t<char_t<S>>>(args...)); fmt::make_format_args<basic_printf_context_t<char_t<S>>>(args...));
} }
FMT_MODULE_EXPORT_END FMT_END_EXPORT
FMT_END_NAMESPACE FMT_END_NAMESPACE
#endif // FMT_PRINTF_H_ #endif // FMT_PRINTF_H_

View File

@ -22,27 +22,25 @@ FMT_BEGIN_NAMESPACE
namespace detail { namespace detail {
template <typename RangeT, typename OutputIterator> template <typename Range, typename OutputIt>
OutputIterator copy(const RangeT& range, OutputIterator out) { auto copy(const Range& range, OutputIt out) -> OutputIt {
for (auto it = range.begin(), end = range.end(); it != end; ++it) for (auto it = range.begin(), end = range.end(); it != end; ++it)
*out++ = *it; *out++ = *it;
return out; return out;
} }
template <typename OutputIterator> template <typename OutputIt>
OutputIterator copy(const char* str, OutputIterator out) { auto copy(const char* str, OutputIt out) -> OutputIt {
while (*str) *out++ = *str++; while (*str) *out++ = *str++;
return out; return out;
} }
template <typename OutputIterator> template <typename OutputIt> auto copy(char ch, OutputIt out) -> OutputIt {
OutputIterator copy(char ch, OutputIterator out) {
*out++ = ch; *out++ = ch;
return out; return out;
} }
template <typename OutputIterator> template <typename OutputIt> auto copy(wchar_t ch, OutputIt out) -> OutputIt {
OutputIterator copy(wchar_t ch, OutputIterator out) {
*out++ = ch; *out++ = ch;
return out; return out;
} }
@ -69,7 +67,7 @@ template <typename T> class is_map {
template <typename> static void check(...); template <typename> static void check(...);
public: public:
#ifdef FMT_FORMAT_MAP_AS_LIST #ifdef FMT_FORMAT_MAP_AS_LIST // DEPRECATED!
static constexpr const bool value = false; static constexpr const bool value = false;
#else #else
static constexpr const bool value = static constexpr const bool value =
@ -82,7 +80,7 @@ template <typename T> class is_set {
template <typename> static void check(...); template <typename> static void check(...);
public: public:
#ifdef FMT_FORMAT_SET_AS_LIST #ifdef FMT_FORMAT_SET_AS_LIST // DEPRECATED!
static constexpr const bool value = false; static constexpr const bool value = false;
#else #else
static constexpr const bool value = static constexpr const bool value =
@ -157,8 +155,9 @@ 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>())),
enable_if_t<std::is_copy_constructible<T>::value>>> // the extra int here is because older versions of MSVC don't
: std::true_type {}; // SFINAE properly unless there are distinct types
int>> : std::true_type {};
template <typename T> template <typename T>
struct is_range_<T, void> struct is_range_<T, void>
@ -211,41 +210,61 @@ 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... I> template <std::size_t... Is>
static std::true_type check2(index_sequence<I...>, static std::true_type check2(index_sequence<Is...>,
integer_sequence<bool, (I == I)...>); integer_sequence<bool, (Is == Is)...>);
static std::false_type check2(...); static std::false_type check2(...);
template <std::size_t... I> template <std::size_t... Is>
static decltype(check2( static decltype(check2(
index_sequence<I...>{}, index_sequence<Is...>{},
integer_sequence< integer_sequence<
bool, (is_formattable<typename std::tuple_element<I, T>::type, bool, (is_formattable<typename std::tuple_element<Is, T>::type,
C>::value)...>{})) check(index_sequence<I...>); C>::value)...>{})) check(index_sequence<Is...>);
public: public:
static constexpr const bool value = static constexpr const bool value =
decltype(check(tuple_index_sequence<T>{}))::value; decltype(check(tuple_index_sequence<T>{}))::value;
}; };
template <class Tuple, class F, size_t... Is> template <typename Tuple, typename F, size_t... Is>
void for_each(index_sequence<Is...>, Tuple&& tup, F&& f) noexcept { FMT_CONSTEXPR void for_each(index_sequence<Is...>, Tuple&& t, F&& f) {
using std::get; using std::get;
// using free function get<I>(T) now. // Using a free function get<Is>(Tuple) now.
const int _[] = {0, ((void)f(get<Is>(tup)), 0)...}; const int unused[] = {0, ((void)f(get<Is>(t)), 0)...};
(void)_; // blocks warnings ignore_unused(unused);
} }
template <class T> template <typename Tuple, typename F>
FMT_CONSTEXPR make_index_sequence<std::tuple_size<T>::value> get_indexes( FMT_CONSTEXPR void for_each(Tuple&& t, F&& f) {
T const&) { for_each(tuple_index_sequence<remove_cvref_t<Tuple>>(),
return {}; std::forward<Tuple>(t), std::forward<F>(f));
} }
template <class Tuple, class F> void for_each(Tuple&& tup, F&& f) { template <typename Tuple1, typename Tuple2, typename F, size_t... Is>
const auto indexes = get_indexes(tup); void for_each2(index_sequence<Is...>, Tuple1&& t1, Tuple2&& t2, F&& f) {
for_each(indexes, std::forward<Tuple>(tup), std::forward<F>(f)); using std::get;
const int unused[] = {0, ((void)f(get<Is>(t1), get<Is>(t2)), 0)...};
ignore_unused(unused);
} }
template <typename Tuple1, typename Tuple2, typename F>
void for_each2(Tuple1&& t1, Tuple2&& t2, F&& f) {
for_each2(tuple_index_sequence<remove_cvref_t<Tuple1>>(),
std::forward<Tuple1>(t1), std::forward<Tuple2>(t2),
std::forward<F>(f));
}
namespace tuple {
// Workaround a bug in MSVC 2019 (v140).
template <typename Char, typename... T>
using result_t = std::tuple<formatter<remove_cvref_t<T>, Char>...>;
using std::get;
template <typename Tuple, typename Char, std::size_t... Is>
auto get_formatters(index_sequence<Is...>)
-> result_t<Char, decltype(get<Is>(std::declval<Tuple>()))...>;
} // namespace tuple
#if FMT_MSC_VERSION && FMT_MSC_VERSION < 1920 #if FMT_MSC_VERSION && FMT_MSC_VERSION < 1920
// Older MSVC doesn't get the reference type correctly for arrays. // Older MSVC doesn't get the reference type correctly for arrays.
template <typename R> struct range_reference_type_impl { template <typename R> struct range_reference_type_impl {
@ -269,45 +288,37 @@ using range_reference_type =
template <typename Range> template <typename Range>
using uncvref_type = remove_cvref_t<range_reference_type<Range>>; using uncvref_type = remove_cvref_t<range_reference_type<Range>>;
template <typename Range> template <typename Formatter>
using uncvref_first_type = FMT_CONSTEXPR auto maybe_set_debug_format(Formatter& f, bool set)
remove_cvref_t<decltype(std::declval<range_reference_type<Range>>().first)>; -> decltype(f.set_debug_format(set)) {
f.set_debug_format(set);
template <typename Range>
using uncvref_second_type = remove_cvref_t<
decltype(std::declval<range_reference_type<Range>>().second)>;
template <typename OutputIt> OutputIt write_delimiter(OutputIt out) {
*out++ = ',';
*out++ = ' ';
return out;
} }
template <typename Formatter>
FMT_CONSTEXPR void maybe_set_debug_format(Formatter&, ...) {}
template <typename Char, typename OutputIt> // These are not generic lambdas for compatibility with C++11.
auto write_range_entry(OutputIt out, basic_string_view<Char> str) -> OutputIt { template <typename ParseContext> struct parse_empty_specs {
return write_escaped_string(out, str); template <typename Formatter> FMT_CONSTEXPR void operator()(Formatter& f) {
} f.parse(ctx);
detail::maybe_set_debug_format(f, true);
}
ParseContext& ctx;
};
template <typename FormatContext> struct format_tuple_element {
using char_type = typename FormatContext::char_type;
template <typename Char, typename OutputIt, typename T, template <typename T>
FMT_ENABLE_IF(std::is_convertible<T, std_string_view<char>>::value)> void operator()(const formatter<T, char_type>& f, const T& v) {
inline auto write_range_entry(OutputIt out, const T& str) -> OutputIt { if (i > 0)
auto sv = std_string_view<Char>(str); ctx.advance_to(detail::copy_str<char_type>(separator, ctx.out()));
return write_range_entry<Char>(out, basic_string_view<Char>(sv)); ctx.advance_to(f.format(v, ctx));
} ++i;
}
template <typename Char, typename OutputIt, typename Arg, int i;
FMT_ENABLE_IF(std::is_same<Arg, Char>::value)> FormatContext& ctx;
OutputIt write_range_entry(OutputIt out, const Arg v) { basic_string_view<char_type> separator;
return write_escaped_char(out, v); };
}
template <
typename Char, typename OutputIt, typename Arg,
FMT_ENABLE_IF(!is_std_string_like<typename std::decay<Arg>::type>::value &&
!std::is_same<Arg, Char>::value)>
OutputIt write_range_entry(OutputIt out, const Arg& v) {
return write<Char>(out, v);
}
} // namespace detail } // namespace detail
@ -321,29 +332,20 @@ template <typename T, typename C> struct is_tuple_formattable {
detail::is_tuple_formattable_<T, C>::value; detail::is_tuple_formattable_<T, C>::value;
}; };
template <typename TupleT, typename Char> template <typename Tuple, typename Char>
struct formatter<TupleT, Char, struct formatter<Tuple, Char,
enable_if_t<fmt::is_tuple_like<TupleT>::value && enable_if_t<fmt::is_tuple_like<Tuple>::value &&
fmt::is_tuple_formattable<TupleT, Char>::value>> { fmt::is_tuple_formattable<Tuple, Char>::value>> {
private: private:
decltype(detail::tuple::get_formatters<Tuple, Char>(
detail::tuple_index_sequence<Tuple>())) formatters_;
basic_string_view<Char> separator_ = detail::string_literal<Char, ',', ' '>{}; basic_string_view<Char> separator_ = detail::string_literal<Char, ',', ' '>{};
basic_string_view<Char> opening_bracket_ = basic_string_view<Char> opening_bracket_ =
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, ')'>{};
// C++11 generic lambda for format().
template <typename FormatContext> struct format_each {
template <typename T> void operator()(const T& v) {
if (i > 0) out = detail::copy_str<Char>(separator, out);
out = detail::write_range_entry<Char>(out, v);
++i;
}
int i;
typename FormatContext::iterator& out;
basic_string_view<Char> separator;
};
public: public:
FMT_CONSTEXPR formatter() {} FMT_CONSTEXPR formatter() {}
@ -359,17 +361,21 @@ struct formatter<TupleT, Char,
template <typename ParseContext> template <typename ParseContext>
FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) { FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
return ctx.begin(); auto it = ctx.begin();
if (it != ctx.end() && *it != '}')
FMT_THROW(format_error("invalid format specifier"));
detail::for_each(formatters_, detail::parse_empty_specs<ParseContext>{ctx});
return it;
} }
template <typename FormatContext = format_context> template <typename FormatContext>
auto format(const TupleT& values, FormatContext& ctx) const auto format(const Tuple& value, FormatContext& ctx) const
-> decltype(ctx.out()) { -> decltype(ctx.out()) {
auto out = ctx.out(); ctx.advance_to(detail::copy_str<Char>(opening_bracket_, ctx.out()));
out = detail::copy_str<Char>(opening_bracket_, out); detail::for_each2(
detail::for_each(values, format_each<FormatContext>{0, out, separator_}); formatters_, value,
out = detail::copy_str<Char>(closing_bracket_, out); detail::format_tuple_element<FormatContext>{0, ctx, separator_});
return out; return detail::copy_str<Char>(closing_bracket_, ctx.out());
} }
}; };
@ -398,12 +404,10 @@ template <typename Context> struct range_mapper {
}; };
template <typename Char, typename Element> template <typename Char, typename Element>
using range_formatter_type = conditional_t< using range_formatter_type =
is_formattable<Element, Char>::value,
formatter<remove_cvref_t<decltype(range_mapper<buffer_context<Char>>{}.map( formatter<remove_cvref_t<decltype(range_mapper<buffer_context<Char>>{}.map(
std::declval<Element>()))>, std::declval<Element>()))>,
Char>, Char>;
fallback_formatter<Element, Char>>;
template <typename R> template <typename R>
using maybe_const_range = using maybe_const_range =
@ -413,11 +417,8 @@ using maybe_const_range =
#if !FMT_MSC_VERSION || FMT_MSC_VERSION >= 1910 #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
: disjunction< : is_formattable<uncvref_type<maybe_const_range<R>>, Char> {};
is_formattable<uncvref_type<maybe_const_range<R>>, Char>,
has_fallback_formatter<uncvref_type<maybe_const_range<R>>, Char>> {};
#endif #endif
} // namespace detail } // namespace detail
template <typename T, typename Char, typename Enable = void> template <typename T, typename Char, typename Enable = void>
@ -426,32 +427,16 @@ struct range_formatter;
template <typename T, typename Char> template <typename T, typename Char>
struct range_formatter< struct range_formatter<
T, Char, T, Char,
enable_if_t<conjunction< enable_if_t<conjunction<std::is_same<T, remove_cvref_t<T>>,
std::is_same<T, remove_cvref_t<T>>, is_formattable<T, Char>>::value>> {
disjunction<is_formattable<T, Char>,
detail::has_fallback_formatter<T, Char>>>::value>> {
private: private:
detail::range_formatter_type<Char, T> underlying_; detail::range_formatter_type<Char, T> underlying_;
bool custom_specs_ = false;
basic_string_view<Char> separator_ = detail::string_literal<Char, ',', ' '>{}; basic_string_view<Char> separator_ = detail::string_literal<Char, ',', ' '>{};
basic_string_view<Char> opening_bracket_ = basic_string_view<Char> opening_bracket_ =
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, ']'>{};
template <class U>
FMT_CONSTEXPR static auto maybe_set_debug_format(U& u, int)
-> decltype(u.set_debug_format()) {
u.set_debug_format();
}
template <class U>
FMT_CONSTEXPR static void maybe_set_debug_format(U&, ...) {}
FMT_CONSTEXPR void maybe_set_debug_format() {
maybe_set_debug_format(underlying_, 0);
}
public: public:
FMT_CONSTEXPR range_formatter() {} FMT_CONSTEXPR range_formatter() {}
@ -473,31 +458,24 @@ struct range_formatter<
FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) { 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();
if (it == end || *it == '}') {
maybe_set_debug_format();
return it;
}
if (*it == 'n') { if (it != end && *it == 'n') {
set_brackets({}, {}); set_brackets({}, {});
++it; ++it;
} }
if (*it == '}') { if (it != end && *it != '}') {
maybe_set_debug_format(); if (*it != ':') FMT_THROW(format_error("invalid format specifier"));
return it; ++it;
} else {
detail::maybe_set_debug_format(underlying_, true);
} }
if (*it != ':')
FMT_THROW(format_error("no other top-level range formatters supported"));
custom_specs_ = true;
++it;
ctx.advance_to(it); ctx.advance_to(it);
return underlying_.parse(ctx); return underlying_.parse(ctx);
} }
template <typename R, class 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; detail::range_mapper<buffer_context<Char>> mapper;
auto out = ctx.out(); auto out = ctx.out();
@ -507,7 +485,6 @@ struct range_formatter<
auto end = detail::range_end(range); auto end = detail::range_end(range);
for (; it != end; ++it) { for (; it != end; ++it) {
if (i > 0) out = detail::copy_str<Char>(separator_, out); if (i > 0) out = detail::copy_str<Char>(separator_, out);
;
ctx.advance_to(out); ctx.advance_to(out);
out = underlying_.format(mapper.map(*it), ctx); out = underlying_.format(mapper.map(*it), ctx);
++i; ++i;
@ -520,13 +497,14 @@ struct range_formatter<
enum class range_format { disabled, map, set, sequence, string, debug_string }; enum class range_format { disabled, map, set, sequence, string, debug_string };
namespace detail { namespace detail {
template <typename T> struct range_format_kind_ { template <typename T>
static constexpr auto value = std::is_same<range_reference_type<T>, T>::value struct range_format_kind_
? range_format::disabled : std::integral_constant<range_format,
: is_map<T>::value ? range_format::map std::is_same<uncvref_type<T>, T>::value
: is_set<T>::value ? range_format::set ? range_format::disabled
: range_format::sequence; : 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> template <range_format K, typename R, typename Char, typename Enable = void>
struct range_default_formatter; struct range_default_formatter;
@ -601,9 +579,6 @@ template <typename Char, typename... T> struct tuple_join_view : detail::view {
: tuple(t), sep{s} {} : tuple(t), sep{s} {}
}; };
template <typename Char, typename... T>
using tuple_arg_join = tuple_join_view<Char, T...>;
// Define FMT_TUPLE_JOIN_SPECIFIERS to enable experimental format specifiers // Define FMT_TUPLE_JOIN_SPECIFIERS to enable experimental format specifiers
// support in tuple_join. It is disabled by default because of issues with // support in tuple_join. It is disabled by default because of issues with
// the dynamic width and precision. // the dynamic width and precision.
@ -673,7 +648,42 @@ struct formatter<tuple_join_view<Char, T...>, Char> {
} }
}; };
FMT_MODULE_EXPORT_BEGIN namespace detail {
// Check if T has an interface like a container adaptor (e.g. std::stack,
// std::queue, std::priority_queue).
template <typename T> class is_container_adaptor_like {
template <typename U> static auto check(U* p) -> typename U::container_type;
template <typename> static void check(...);
public:
static constexpr const bool value =
!std::is_void<decltype(check<T>(nullptr))>::value;
};
template <typename Container> struct all {
const Container& c;
auto begin() const -> typename Container::const_iterator { return c.begin(); }
auto end() const -> typename Container::const_iterator { return c.end(); }
};
} // namespace detail
template <typename T, typename Char>
struct formatter<T, Char,
enable_if_t<detail::is_container_adaptor_like<T>::value>>
: formatter<detail::all<typename T::container_type>, Char> {
using all = detail::all<typename T::container_type>;
template <typename FormatContext>
auto format(const T& t, FormatContext& ctx) const -> decltype(ctx.out()) {
struct getter : T {
static auto get(const T& t) -> all {
return {t.*(&getter::c)}; // Access c through the derived class.
}
};
return formatter<all>::format(getter::get(t), ctx);
}
};
FMT_BEGIN_EXPORT
/** /**
\rst \rst
@ -716,7 +726,7 @@ auto join(std::initializer_list<T> list, string_view sep)
return join(std::begin(list), std::end(list), sep); return join(std::begin(list), std::end(list), sep);
} }
FMT_MODULE_EXPORT_END FMT_END_EXPORT
FMT_END_NAMESPACE FMT_END_NAMESPACE
#endif // FMT_RANGES_H_ #endif // FMT_RANGES_H_

View File

@ -8,8 +8,12 @@
#ifndef FMT_STD_H_ #ifndef FMT_STD_H_
#define FMT_STD_H_ #define FMT_STD_H_
#include <cstdlib>
#include <exception>
#include <memory>
#include <thread> #include <thread>
#include <type_traits> #include <type_traits>
#include <typeinfo>
#include <utility> #include <utility>
#include "ostream.h" #include "ostream.h"
@ -25,6 +29,19 @@
# if FMT_HAS_INCLUDE(<variant>) # if FMT_HAS_INCLUDE(<variant>)
# include <variant> # include <variant>
# endif # endif
# if FMT_HAS_INCLUDE(<optional>)
# include <optional>
# endif
#endif
// GCC 4 does not support FMT_HAS_INCLUDE.
#if FMT_HAS_INCLUDE(<cxxabi.h>) || defined(__GLIBCXX__)
# include <cxxabi.h>
// Android NDK with gabi++ library on some architectures does not implement
// abi::__cxa_demangle().
# ifndef __GABIXX_CXXABI_H__
# define FMT_HAS_ABI_CXA_DEMANGLE
# endif
#endif #endif
#ifdef __cpp_lib_filesystem #ifdef __cpp_lib_filesystem
@ -39,12 +56,13 @@ void write_escaped_path(basic_memory_buffer<Char>& quoted,
} }
# ifdef _WIN32 # ifdef _WIN32
template <> template <>
inline void write_escaped_path<char>(basic_memory_buffer<char>& quoted, inline void write_escaped_path<char>(memory_buffer& quoted,
const std::filesystem::path& p) { const std::filesystem::path& p) {
auto s = p.u8string(); auto buf = basic_memory_buffer<wchar_t>();
write_escaped_string<char>( write_escaped_string<wchar_t>(std::back_inserter(buf), p.native());
std::back_inserter(quoted), // Convert UTF-16 to UTF-8.
string_view(reinterpret_cast<const char*>(s.c_str()), s.size())); if (!unicode_to_utf8<wchar_t>::convert(quoted, {buf.data(), buf.size()}))
FMT_THROW(std::runtime_error("invalid utf16"));
} }
# endif # endif
template <> template <>
@ -57,13 +75,19 @@ inline void write_escaped_path<std::filesystem::path::value_type>(
} // namespace detail } // namespace detail
FMT_MODULE_EXPORT
template <typename Char> template <typename Char>
struct formatter<std::filesystem::path, Char> struct formatter<std::filesystem::path, Char>
: formatter<basic_string_view<Char>> { : formatter<basic_string_view<Char>> {
template <typename ParseContext> FMT_CONSTEXPR auto parse(ParseContext& ctx) {
auto out = formatter<basic_string_view<Char>>::parse(ctx);
this->set_debug_format(false);
return out;
}
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 ->
typename FormatContext::iterator { typename FormatContext::iterator {
basic_memory_buffer<Char> quoted; auto quoted = basic_memory_buffer<Char>();
detail::write_escaped_path(quoted, p); detail::write_escaped_path(quoted, p);
return formatter<basic_string_view<Char>>::format( return formatter<basic_string_view<Char>>::format(
basic_string_view<Char>(quoted.data(), quoted.size()), ctx); basic_string_view<Char>(quoted.data(), quoted.size()), ctx);
@ -73,12 +97,58 @@ FMT_END_NAMESPACE
#endif #endif
FMT_BEGIN_NAMESPACE FMT_BEGIN_NAMESPACE
FMT_MODULE_EXPORT
template <typename Char> template <typename Char>
struct formatter<std::thread::id, Char> : basic_ostream_formatter<Char> {}; struct formatter<std::thread::id, Char> : basic_ostream_formatter<Char> {};
FMT_END_NAMESPACE FMT_END_NAMESPACE
#ifdef __cpp_lib_optional
FMT_BEGIN_NAMESPACE
FMT_MODULE_EXPORT
template <typename T, typename Char>
struct formatter<std::optional<T>, Char,
std::enable_if_t<is_formattable<T, Char>::value>> {
private:
formatter<T, Char> underlying_;
static constexpr basic_string_view<Char> optional =
detail::string_literal<Char, 'o', 'p', 't', 'i', 'o', 'n', 'a', 'l',
'('>{};
static constexpr basic_string_view<Char> none =
detail::string_literal<Char, 'n', 'o', 'n', 'e'>{};
template <class U>
FMT_CONSTEXPR static auto maybe_set_debug_format(U& u, bool set)
-> decltype(u.set_debug_format(set)) {
u.set_debug_format(set);
}
template <class U>
FMT_CONSTEXPR static void maybe_set_debug_format(U&, ...) {}
public:
template <typename ParseContext> FMT_CONSTEXPR auto parse(ParseContext& ctx) {
maybe_set_debug_format(underlying_, true);
return underlying_.parse(ctx);
}
template <typename FormatContext>
auto format(std::optional<T> const& opt, FormatContext& ctx) const
-> decltype(ctx.out()) {
if (!opt) return detail::write<Char>(ctx.out(), none);
auto out = ctx.out();
out = detail::write<Char>(out, optional);
ctx.advance_to(out);
out = underlying_.format(*opt, ctx);
return detail::write(out, ')');
}
};
FMT_END_NAMESPACE
#endif // __cpp_lib_optional
#ifdef __cpp_lib_variant #ifdef __cpp_lib_variant
FMT_BEGIN_NAMESPACE FMT_BEGIN_NAMESPACE
FMT_MODULE_EXPORT
template <typename Char> struct formatter<std::monostate, Char> { template <typename Char> struct formatter<std::monostate, Char> {
template <typename ParseContext> template <typename ParseContext>
FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) { FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
@ -100,19 +170,16 @@ template <typename T>
using variant_index_sequence = using variant_index_sequence =
std::make_index_sequence<std::variant_size<T>::value>; std::make_index_sequence<std::variant_size<T>::value>;
// variant_size and variant_alternative check. template <typename> struct is_variant_like_ : std::false_type {};
template <typename T, typename U = void> template <typename... Types>
struct is_variant_like_ : std::false_type {}; struct is_variant_like_<std::variant<Types...>> : std::true_type {};
template <typename T>
struct is_variant_like_<T, std::void_t<decltype(std::variant_size<T>::value)>>
: std::true_type {};
// formattable element check // formattable element check.
template <typename T, typename C> class is_variant_formattable_ { template <typename T, typename C> class is_variant_formattable_ {
template <std::size_t... I> template <std::size_t... Is>
static std::conjunction< static std::conjunction<
is_formattable<std::variant_alternative_t<I, T>, C>...> is_formattable<std::variant_alternative_t<Is, T>, C>...>
check(std::index_sequence<I...>); check(std::index_sequence<Is...>);
public: public:
static constexpr const bool value = static constexpr const bool value =
@ -130,7 +197,6 @@ auto write_variant_alternative(OutputIt out, const T& v) -> OutputIt {
} }
} // namespace detail } // namespace detail
template <typename T> struct is_variant_like { template <typename T> struct is_variant_like {
static constexpr const bool value = detail::is_variant_like_<T>::value; static constexpr const bool value = detail::is_variant_like_<T>::value;
}; };
@ -140,6 +206,7 @@ template <typename T, typename C> struct is_variant_formattable {
detail::is_variant_formattable_<T, C>::value; detail::is_variant_formattable_<T, C>::value;
}; };
FMT_MODULE_EXPORT
template <typename Variant, typename Char> template <typename Variant, typename Char>
struct formatter< struct formatter<
Variant, Char, Variant, Char,
@ -156,16 +223,127 @@ struct formatter<
auto out = ctx.out(); auto out = ctx.out();
out = detail::write<Char>(out, "variant("); out = detail::write<Char>(out, "variant(");
std::visit( try {
[&](const auto& v) { std::visit(
out = detail::write_variant_alternative<Char>(out, v); [&](const auto& v) {
}, out = detail::write_variant_alternative<Char>(out, v);
value); },
value);
} catch (const std::bad_variant_access&) {
detail::write<Char>(out, "valueless by exception");
}
*out++ = ')'; *out++ = ')';
return out; return out;
} }
}; };
FMT_END_NAMESPACE FMT_END_NAMESPACE
#endif // __cpp_lib_variant
FMT_BEGIN_NAMESPACE
FMT_MODULE_EXPORT
template <typename Char> struct formatter<std::error_code, Char> {
template <typename ParseContext>
FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
return ctx.begin();
}
template <typename FormatContext>
FMT_CONSTEXPR auto format(const std::error_code& ec, FormatContext& ctx) const
-> decltype(ctx.out()) {
auto out = ctx.out();
out = detail::write_bytes(out, ec.category().name(), format_specs<Char>());
out = detail::write<Char>(out, Char(':'));
out = detail::write<Char>(out, ec.value());
return out;
}
};
FMT_MODULE_EXPORT
template <typename T, typename Char>
struct formatter<
T, Char,
typename std::enable_if<std::is_base_of<std::exception, T>::value>::type> {
private:
bool with_typename_ = false;
public:
FMT_CONSTEXPR auto parse(basic_format_parse_context<Char>& ctx)
-> decltype(ctx.begin()) {
auto it = ctx.begin();
auto end = ctx.end();
if (it == end || *it == '}') return it;
if (*it == 't') {
++it;
with_typename_ = true;
}
return it;
}
template <typename OutputIt>
auto format(const std::exception& ex,
basic_format_context<OutputIt, Char>& ctx) const -> OutputIt {
format_specs<Char> spec;
auto out = ctx.out();
if (!with_typename_)
return detail::write_bytes(out, string_view(ex.what()), spec);
const std::type_info& ti = typeid(ex);
#ifdef FMT_HAS_ABI_CXA_DEMANGLE
int status = 0;
std::size_t size = 0;
std::unique_ptr<char, decltype(&std::free)> 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 #endif
out = detail::write<Char>(out, Char(':'));
out = detail::write<Char>(out, Char(' '));
out = detail::write_bytes(out, string_view(ex.what()), spec);
return out;
}
};
FMT_END_NAMESPACE
#endif // FMT_STD_H_ #endif // FMT_STD_H_

View File

@ -12,13 +12,32 @@
#include "format.h" #include "format.h"
#ifndef FMT_STATIC_THOUSANDS_SEPARATOR
# include <locale>
#endif
FMT_BEGIN_NAMESPACE FMT_BEGIN_NAMESPACE
namespace detail { 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>;
}
FMT_MODULE_EXPORT_BEGIN inline auto write_loc(std::back_insert_iterator<detail::buffer<wchar_t>> out,
loc_value value, const format_specs<wchar_t>& specs,
locale_ref loc) -> bool {
#ifndef FMT_STATIC_THOUSANDS_SEPARATOR
auto& numpunct =
std::use_facet<std::numpunct<wchar_t>>(loc.get<std::locale>());
auto separator = std::wstring();
auto grouping = numpunct.grouping();
if (!grouping.empty()) separator = std::wstring(1, numpunct.thousands_sep());
return value.visit(loc_writer<wchar_t>{out, specs, separator, grouping, {}});
#endif
return false;
}
} // namespace detail
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 = basic_format_parse_context<wchar_t>;
@ -33,7 +52,9 @@ inline auto runtime(wstring_view s) -> wstring_view { return s; }
#else #else
template <typename... Args> template <typename... Args>
using wformat_string = basic_format_string<wchar_t, type_identity_t<Args>...>; using wformat_string = basic_format_string<wchar_t, type_identity_t<Args>...>;
inline auto runtime(wstring_view s) -> basic_runtime<wchar_t> { return {{s}}; } inline auto runtime(wstring_view s) -> runtime_format_string<wchar_t> {
return {{s}};
}
#endif #endif
template <> struct is_char<wchar_t> : std::true_type {}; template <> struct is_char<wchar_t> : std::true_type {};
@ -126,7 +147,7 @@ auto vformat_to(OutputIt out, const S& format_str,
-> 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(format_str), args);
return detail::get_iterator(buf); return detail::get_iterator(buf, out);
} }
template <typename OutputIt, typename S, typename... Args, template <typename OutputIt, typename S, typename... Args,
@ -149,7 +170,7 @@ inline auto vformat_to(
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(format_str), args,
detail::locale_ref(loc)); detail::locale_ref(loc));
return detail::get_iterator(buf); return detail::get_iterator(buf, out);
} }
template < template <
@ -160,7 +181,7 @@ template <
inline auto format_to(OutputIt out, const Locale& loc, const S& format_str, inline auto format_to(OutputIt out, const Locale& loc, const S& format_str,
Args&&... args) -> Args&&... args) ->
typename std::enable_if<enable, OutputIt>::type { typename std::enable_if<enable, OutputIt>::type {
return vformat_to(out, loc, to_string_view(format_str), return vformat_to(out, loc, detail::to_string_view(format_str),
fmt::make_format_args<buffer_context<Char>>(args...)); fmt::make_format_args<buffer_context<Char>>(args...));
} }
@ -217,13 +238,22 @@ template <typename... T> void print(wformat_string<T...> fmt, T&&... args) {
return vprint(wstring_view(fmt), fmt::make_wformat_args(args...)); return vprint(wstring_view(fmt), fmt::make_wformat_args(args...));
} }
template <typename... T>
void println(std::FILE* f, wformat_string<T...> fmt, T&&... args) {
return print(f, L"{}\n", fmt::format(fmt, std::forward<T>(args)...));
}
template <typename... T> void println(wformat_string<T...> fmt, T&&... args) {
return print(L"{}\n", fmt::format(fmt, std::forward<T>(args)...));
}
/** /**
Converts *value* to ``std::wstring`` using the default format for type *T*. 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);
} }
FMT_MODULE_EXPORT_END FMT_END_EXPORT
FMT_END_NAMESPACE FMT_END_NAMESPACE
#endif // FMT_XCHAR_H_ #endif // FMT_XCHAR_H_

View File

@ -1,49 +1,44 @@
module; module;
#ifndef __cpp_modules
# error Module not supported.
#endif
// put all implementation-provided headers into the global module fragment
// to prevent attachment to this module
#if !defined(_CRT_SECURE_NO_WARNINGS) && defined(_MSC_VER)
# define _CRT_SECURE_NO_WARNINGS
#endif
#if !defined(WIN32_LEAN_AND_MEAN) && defined(_WIN32)
# define WIN32_LEAN_AND_MEAN
#endif
// Put all implementation-provided headers into the global module fragment
// to prevent attachment to this module.
#include <algorithm> #include <algorithm>
#include <cctype>
#include <cerrno> #include <cerrno>
#include <chrono> #include <chrono>
#include <climits> #include <climits>
#include <clocale>
#include <cmath> #include <cmath>
#include <cstdarg>
#include <cstddef> #include <cstddef>
#include <cstdint> #include <cstdint>
#include <cstdio> #include <cstdio>
#include <cstdlib> #include <cstdlib>
#include <cstring> #include <cstring>
#include <ctime> #include <ctime>
#include <cwchar>
#include <exception> #include <exception>
#include <filesystem>
#include <fstream>
#include <functional> #include <functional>
#include <iterator> #include <iterator>
#include <limits> #include <limits>
#include <locale> #include <locale>
#include <memory> #include <memory>
#include <optional>
#include <ostream> #include <ostream>
#include <sstream>
#include <stdexcept> #include <stdexcept>
#include <string> #include <string>
#include <string_view> #include <string_view>
#include <system_error> #include <system_error>
#include <thread>
#include <type_traits> #include <type_traits>
#include <typeinfo>
#include <utility> #include <utility>
#include <variant>
#include <vector> #include <vector>
#include <version>
#if _MSC_VER #if __has_include(<cxxabi.h>)
# include <cxxabi.h>
#endif
#if defined(_MSC_VER) || defined(__MINGW32__)
# include <intrin.h> # include <intrin.h>
#endif #endif
#if defined __APPLE__ || defined(__FreeBSD__) #if defined __APPLE__ || defined(__FreeBSD__)
@ -65,22 +60,38 @@ module;
# endif # endif
#endif #endif
#ifdef _WIN32 #ifdef _WIN32
# if defined(__GLIBCXX__)
# include <ext/stdio_filebuf.h>
# include <ext/stdio_sync_filebuf.h>
# elif defined(_LIBCPP_VERSION)
# include <__std_stream>
# endif
# define WIN32_LEAN_AND_MEAN
# include <windows.h> # include <windows.h>
#endif #endif
export module fmt; export module fmt;
#define FMT_MODULE_EXPORT export #define FMT_MODULE_EXPORT export
#define FMT_MODULE_EXPORT_BEGIN export { #define FMT_BEGIN_EXPORT export {
#define FMT_MODULE_EXPORT_END } #define FMT_END_EXPORT }
#define FMT_BEGIN_DETAIL_NAMESPACE \ #define FMT_BEGIN_DETAIL_NAMESPACE \
} \ } \
namespace detail { namespace detail {
#define FMT_END_DETAIL_NAMESPACE \ #define FMT_END_DETAIL_NAMESPACE \
} \ } \
export { export {
// all library-provided declarations and definitions // If you define FMT_ATTACH_TO_GLOBAL_MODULE
// must be in the module purview to be exported // - all declarations are detached from module 'fmt'
// - the module behaves like a traditional static library, too
// - all library symbols are mangled traditionally
// - you can mix TUs with either importing or #including the {fmt} API
#ifdef FMT_ATTACH_TO_GLOBAL_MODULE
extern "C++" {
#endif
// All library-provided declarations and definitions must be in the module
// purview to be exported.
#include "fmt/args.h" #include "fmt/args.h"
#include "fmt/chrono.h" #include "fmt/chrono.h"
#include "fmt/color.h" #include "fmt/color.h"
@ -88,11 +99,17 @@ export module fmt;
#include "fmt/format.h" #include "fmt/format.h"
#include "fmt/os.h" #include "fmt/os.h"
#include "fmt/printf.h" #include "fmt/printf.h"
#include "fmt/std.h"
#include "fmt/xchar.h" #include "fmt/xchar.h"
#ifdef FMT_ATTACH_TO_GLOBAL_MODULE
}
#endif
// gcc doesn't yet implement private module fragments // gcc doesn't yet implement private module fragments
#if !FMT_GCC_VERSION #if !FMT_GCC_VERSION
module : private; module :private;
#endif #endif
#include "format.cc" #include "format.cc"

View File

@ -28,12 +28,8 @@ template FMT_API auto decimal_point_impl(locale_ref) -> char;
template FMT_API void buffer<char>::append(const char*, const char*); template FMT_API void buffer<char>::append(const char*, const char*);
// DEPRECATED!
// There is no correspondent extern template in format.h because of
// incompatibility between clang and gcc (#2377).
template FMT_API void vformat_to(buffer<char>&, string_view, template FMT_API void vformat_to(buffer<char>&, string_view,
basic_format_args<FMT_BUFFER_CONTEXT(char)>, typename vformat_args<>::type, locale_ref);
locale_ref);
// Explicit instantiations for wchar_t. // Explicit instantiations for wchar_t.

View File

@ -72,34 +72,6 @@ inline std::size_t convert_rwcount(std::size_t count) { return count; }
FMT_BEGIN_NAMESPACE FMT_BEGIN_NAMESPACE
#ifdef _WIN32 #ifdef _WIN32
detail::utf16_to_utf8::utf16_to_utf8(basic_string_view<wchar_t> s) {
if (int error_code = convert(s)) {
FMT_THROW(windows_error(error_code,
"cannot convert string from UTF-16 to UTF-8"));
}
}
int detail::utf16_to_utf8::convert(basic_string_view<wchar_t> s) {
if (s.size() > INT_MAX) return ERROR_INVALID_PARAMETER;
int s_size = static_cast<int>(s.size());
if (s_size == 0) {
// WideCharToMultiByte does not support zero length, handle separately.
buffer_.resize(1);
buffer_[0] = 0;
return 0;
}
int length = WideCharToMultiByte(CP_UTF8, 0, s.data(), s_size, nullptr, 0,
nullptr, nullptr);
if (length == 0) return GetLastError();
buffer_.resize(length + 1);
length = WideCharToMultiByte(CP_UTF8, 0, s.data(), s_size, &buffer_[0],
length, nullptr, nullptr);
if (length == 0) return GetLastError();
buffer_[length] = 0;
return 0;
}
namespace detail { namespace detail {
class system_message { class system_message {
@ -140,8 +112,8 @@ class utf8_system_category final : public std::error_category {
std::string message(int error_code) const override { std::string message(int error_code) const override {
system_message msg(error_code); system_message msg(error_code);
if (msg) { if (msg) {
utf16_to_utf8 utf8_message; unicode_to_utf8<wchar_t> utf8_message;
if (utf8_message.convert(msg) == ERROR_SUCCESS) { if (utf8_message.convert(msg)) {
return utf8_message.str(); return utf8_message.str();
} }
} }
@ -167,9 +139,10 @@ void detail::format_windows_error(detail::buffer<char>& out, int error_code,
FMT_TRY { FMT_TRY {
system_message msg(error_code); system_message msg(error_code);
if (msg) { if (msg) {
utf16_to_utf8 utf8_message; unicode_to_utf8<wchar_t> utf8_message;
if (utf8_message.convert(msg) == ERROR_SUCCESS) { if (utf8_message.convert(msg)) {
fmt::format_to(buffer_appender<char>(out), "{}: {}", message, utf8_message); fmt::format_to(buffer_appender<char>(out), FMT_STRING("{}: {}"),
message, string_view(utf8_message));
return; return;
} }
} }
@ -192,37 +165,47 @@ buffered_file::buffered_file(cstring_view filename, cstring_view mode) {
FMT_RETRY_VAL(file_, FMT_SYSTEM(fopen(filename.c_str(), mode.c_str())), FMT_RETRY_VAL(file_, FMT_SYSTEM(fopen(filename.c_str(), mode.c_str())),
nullptr); nullptr);
if (!file_) if (!file_)
FMT_THROW(system_error(errno, "cannot open file {}", filename.c_str())); FMT_THROW(system_error(errno, FMT_STRING("cannot open file {}"),
filename.c_str()));
} }
void buffered_file::close() { void buffered_file::close() {
if (!file_) return; if (!file_) return;
int result = FMT_SYSTEM(fclose(file_)); int result = FMT_SYSTEM(fclose(file_));
file_ = nullptr; file_ = nullptr;
if (result != 0) FMT_THROW(system_error(errno, "cannot close file")); if (result != 0)
FMT_THROW(system_error(errno, FMT_STRING("cannot close file")));
} }
int buffered_file::descriptor() const { int buffered_file::descriptor() const {
#ifdef fileno // fileno is a macro on OpenBSD so we cannot use FMT_POSIX_CALL.
int fd = fileno(file_);
#else
int fd = FMT_POSIX_CALL(fileno(file_)); int fd = FMT_POSIX_CALL(fileno(file_));
if (fd == -1) FMT_THROW(system_error(errno, "cannot get file descriptor")); #endif
if (fd == -1)
FMT_THROW(system_error(errno, FMT_STRING("cannot get file descriptor")));
return fd; return fd;
} }
#if FMT_USE_FCNTL #if FMT_USE_FCNTL
file::file(cstring_view path, int oflag) {
# ifdef _WIN32 # ifdef _WIN32
using mode_t = int; using mode_t = int;
# endif # endif
constexpr mode_t 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;
file::file(cstring_view path, int oflag) {
# if defined(_WIN32) && !defined(__MINGW32__) # if defined(_WIN32) && !defined(__MINGW32__)
fd_ = -1; fd_ = -1;
FMT_POSIX_CALL(sopen_s(&fd_, path.c_str(), oflag, _SH_DENYNO, mode)); auto converted = detail::utf8_to_utf16(string_view(path.c_str()));
*this = file::open_windows_file(converted.c_str(), oflag);
# else # else
FMT_RETRY(fd_, FMT_POSIX_CALL(open(path.c_str(), oflag, mode))); FMT_RETRY(fd_, FMT_POSIX_CALL(open(path.c_str(), oflag, default_open_mode)));
# endif
if (fd_ == -1) if (fd_ == -1)
FMT_THROW(system_error(errno, "cannot open file {}", path.c_str())); FMT_THROW(
system_error(errno, FMT_STRING("cannot open file {}"), path.c_str()));
# endif
} }
file::~file() noexcept { file::~file() noexcept {
@ -238,7 +221,8 @@ void file::close() {
// See http://linux.derkeiler.com/Mailing-Lists/Kernel/2005-09/3000.html // See http://linux.derkeiler.com/Mailing-Lists/Kernel/2005-09/3000.html
int result = FMT_POSIX_CALL(close(fd_)); int result = FMT_POSIX_CALL(close(fd_));
fd_ = -1; fd_ = -1;
if (result != 0) FMT_THROW(system_error(errno, "cannot close file")); if (result != 0)
FMT_THROW(system_error(errno, FMT_STRING("cannot close file")));
} }
long long file::size() const { long long file::size() const {
@ -260,7 +244,7 @@ long long file::size() const {
using Stat = struct stat; using Stat = struct stat;
Stat file_stat = Stat(); Stat file_stat = Stat();
if (FMT_POSIX_CALL(fstat(fd_, &file_stat)) == -1) if (FMT_POSIX_CALL(fstat(fd_, &file_stat)) == -1)
FMT_THROW(system_error(errno, "cannot get file attributes")); FMT_THROW(system_error(errno, FMT_STRING("cannot get file attributes")));
static_assert(sizeof(long long) >= sizeof(file_stat.st_size), static_assert(sizeof(long long) >= sizeof(file_stat.st_size),
"return type of file::size is not large enough"); "return type of file::size is not large enough");
return file_stat.st_size; return file_stat.st_size;
@ -270,14 +254,16 @@ long long file::size() const {
std::size_t file::read(void* buffer, std::size_t count) { std::size_t file::read(void* buffer, std::size_t count) {
rwresult result = 0; rwresult result = 0;
FMT_RETRY(result, FMT_POSIX_CALL(read(fd_, buffer, convert_rwcount(count)))); FMT_RETRY(result, FMT_POSIX_CALL(read(fd_, buffer, convert_rwcount(count))));
if (result < 0) FMT_THROW(system_error(errno, "cannot read from file")); if (result < 0)
FMT_THROW(system_error(errno, FMT_STRING("cannot read from file")));
return detail::to_unsigned(result); return detail::to_unsigned(result);
} }
std::size_t file::write(const void* buffer, std::size_t count) { std::size_t file::write(const void* buffer, std::size_t count) {
rwresult result = 0; rwresult result = 0;
FMT_RETRY(result, FMT_POSIX_CALL(write(fd_, buffer, convert_rwcount(count)))); FMT_RETRY(result, FMT_POSIX_CALL(write(fd_, buffer, convert_rwcount(count))));
if (result < 0) FMT_THROW(system_error(errno, "cannot write to file")); if (result < 0)
FMT_THROW(system_error(errno, FMT_STRING("cannot write to file")));
return detail::to_unsigned(result); return detail::to_unsigned(result);
} }
@ -286,7 +272,8 @@ file file::dup(int fd) {
// http://pubs.opengroup.org/onlinepubs/009695399/functions/dup.html // http://pubs.opengroup.org/onlinepubs/009695399/functions/dup.html
int new_fd = FMT_POSIX_CALL(dup(fd)); int new_fd = FMT_POSIX_CALL(dup(fd));
if (new_fd == -1) if (new_fd == -1)
FMT_THROW(system_error(errno, "cannot duplicate file descriptor {}", fd)); FMT_THROW(system_error(
errno, FMT_STRING("cannot duplicate file descriptor {}"), fd));
return file(new_fd); return file(new_fd);
} }
@ -294,8 +281,9 @@ void file::dup2(int fd) {
int result = 0; int result = 0;
FMT_RETRY(result, FMT_POSIX_CALL(dup2(fd_, fd))); FMT_RETRY(result, FMT_POSIX_CALL(dup2(fd_, fd)));
if (result == -1) { if (result == -1) {
FMT_THROW(system_error(errno, "cannot duplicate file descriptor {} to {}", FMT_THROW(system_error(
fd_, fd)); errno, FMT_STRING("cannot duplicate file descriptor {} to {}"), fd_,
fd));
} }
} }
@ -320,7 +308,8 @@ void file::pipe(file& read_end, file& write_end) {
// http://pubs.opengroup.org/onlinepubs/009696799/functions/pipe.html // http://pubs.opengroup.org/onlinepubs/009696799/functions/pipe.html
int result = FMT_POSIX_CALL(pipe(fds)); int result = FMT_POSIX_CALL(pipe(fds));
# endif # endif
if (result != 0) FMT_THROW(system_error(errno, "cannot create pipe")); 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 // The following assignments don't throw because read_fd and write_fd
// are closed. // are closed.
read_end = file(fds[0]); read_end = file(fds[0]);
@ -334,28 +323,68 @@ buffered_file file::fdopen(const char* mode) {
# else # else
FILE* f = FMT_POSIX_CALL(fdopen(fd_, mode)); FILE* f = FMT_POSIX_CALL(fdopen(fd_, mode));
# endif # endif
if (!f) if (!f) {
FMT_THROW( FMT_THROW(system_error(
system_error(errno, "cannot associate stream with file descriptor")); errno, FMT_STRING("cannot associate stream with file descriptor")));
}
buffered_file bf(f); buffered_file bf(f);
fd_ = -1; fd_ = -1;
return bf; return bf;
} }
# if defined(_WIN32) && !defined(__MINGW32__)
file file::open_windows_file(wcstring_view path, int oflag) {
int fd = -1;
auto err = _wsopen_s(&fd, path.c_str(), oflag, _SH_DENYNO, default_open_mode);
if (fd == -1) {
FMT_THROW(
system_error(err, FMT_STRING("cannot open file {}"),
detail::unicode_to_utf8<wchar_t>(path.c_str()).c_str()));
}
return file(fd);
}
# endif
# if !defined(__MSDOS__)
long getpagesize() { long getpagesize() {
# ifdef _WIN32 # ifdef _WIN32
SYSTEM_INFO si; SYSTEM_INFO si;
GetSystemInfo(&si); GetSystemInfo(&si);
return si.dwPageSize; return si.dwPageSize;
# else # else
long size = FMT_POSIX_CALL(sysconf(_SC_PAGESIZE)); long size = FMT_POSIX_CALL(sysconf(_SC_PAGESIZE));
if (size < 0) FMT_THROW(system_error(errno, "cannot get memory page size")); if (size < 0)
FMT_THROW(system_error(errno, FMT_STRING("cannot get memory page size")));
return size; return size;
# endif # endif
} }
# endif
FMT_API void ostream::grow(size_t) { namespace detail {
void file_buffer::grow(size_t) {
if (this->size() == this->capacity()) flush(); if (this->size() == this->capacity()) flush();
} }
file_buffer::file_buffer(cstring_view path,
const detail::ostream_params& params)
: file_(path, params.oflag) {
set(new char[params.buffer_size], params.buffer_size);
}
file_buffer::file_buffer(file_buffer&& other)
: detail::buffer<char>(other.data(), other.size(), other.capacity()),
file_(std::move(other.file_)) {
other.clear();
other.set(nullptr, 0);
}
file_buffer::~file_buffer() {
flush();
delete[] data();
}
} // namespace detail
ostream::~ostream() = default;
#endif // FMT_USE_FCNTL #endif // FMT_USE_FCNTL
FMT_END_NAMESPACE FMT_END_NAMESPACE