mirror of
https://github.com/fmtlib/fmt.git
synced 2025-07-30 02:37:36 +02:00
Merge the std branch
This commit is contained in:
3
.github/pull_request_template
vendored
3
.github/pull_request_template
vendored
@ -1,3 +0,0 @@
|
|||||||
<!---
|
|
||||||
Please make sure you've followed the guidelines outlined in the CONTRIBUTING.rst file.
|
|
||||||
--->
|
|
3
.gitignore
vendored
3
.gitignore
vendored
@ -1,5 +1,6 @@
|
|||||||
bin/
|
bin/
|
||||||
/_CPack_Packages
|
/_CPack_Packages
|
||||||
|
/CMakeScripts
|
||||||
/doc/doxyxml
|
/doc/doxyxml
|
||||||
/doc/html
|
/doc/html
|
||||||
virtualenv
|
virtualenv
|
||||||
@ -8,6 +9,7 @@ virtualenv
|
|||||||
*~
|
*~
|
||||||
*.a
|
*.a
|
||||||
*.so*
|
*.so*
|
||||||
|
*.xcodeproj
|
||||||
*.zip
|
*.zip
|
||||||
cmake_install.cmake
|
cmake_install.cmake
|
||||||
CPack*.cmake
|
CPack*.cmake
|
||||||
@ -15,5 +17,6 @@ fmt-*.cmake
|
|||||||
CTestTestfile.cmake
|
CTestTestfile.cmake
|
||||||
CMakeCache.txt
|
CMakeCache.txt
|
||||||
CMakeFiles
|
CMakeFiles
|
||||||
|
FMT.build
|
||||||
Makefile
|
Makefile
|
||||||
run-msbuild.bat
|
run-msbuild.bat
|
||||||
|
16
.travis.yml
16
.travis.yml
@ -14,14 +14,24 @@ env:
|
|||||||
6pxmyzLHSn1ZR7OX5rfPvwM3tOyZ3H0=
|
6pxmyzLHSn1ZR7OX5rfPvwM3tOyZ3H0=
|
||||||
matrix:
|
matrix:
|
||||||
- BUILD=Doc
|
- BUILD=Doc
|
||||||
- BUILD=Debug STANDARD=0x
|
- BUILD=Debug STANDARD=14
|
||||||
- BUILD=Release STANDARD=98
|
- BUILD=Release STANDARD=14
|
||||||
- BUILD=Release STANDARD=0x
|
|
||||||
|
|
||||||
matrix:
|
matrix:
|
||||||
exclude:
|
exclude:
|
||||||
- os: osx
|
- os: osx
|
||||||
env: BUILD=Doc
|
env: BUILD=Doc
|
||||||
|
|
||||||
|
# Install gcc-6 for extended constexpr support.
|
||||||
|
addons:
|
||||||
|
apt:
|
||||||
|
sources:
|
||||||
|
- ubuntu-toolchain-r-test
|
||||||
|
packages:
|
||||||
|
- g++-6
|
||||||
|
|
||||||
|
before_install:
|
||||||
|
- if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then export CXX=g++-6; fi
|
||||||
|
|
||||||
script:
|
script:
|
||||||
- support/travis-build.py
|
- support/travis-build.py
|
||||||
|
119
CMakeLists.txt
119
CMakeLists.txt
@ -2,14 +2,6 @@ message(STATUS "CMake version: ${CMAKE_VERSION}")
|
|||||||
|
|
||||||
cmake_minimum_required(VERSION 2.8.12)
|
cmake_minimum_required(VERSION 2.8.12)
|
||||||
|
|
||||||
if (POLICY CMP0048) # Version variables
|
|
||||||
cmake_policy(SET CMP0048 OLD)
|
|
||||||
endif ()
|
|
||||||
|
|
||||||
if (POLICY CMP0063) # Visibility
|
|
||||||
cmake_policy(SET CMP0063 OLD)
|
|
||||||
endif (POLICY CMP0063)
|
|
||||||
|
|
||||||
# Determine if fmt is built as a subproject (using add_subdirectory)
|
# Determine if fmt is built as a subproject (using add_subdirectory)
|
||||||
# or if it is the master project.
|
# or if it is the master project.
|
||||||
set(MASTER_PROJECT OFF)
|
set(MASTER_PROJECT OFF)
|
||||||
@ -41,14 +33,14 @@ option(FMT_PEDANTIC "Enable extra warnings and expensive tests." OFF)
|
|||||||
option(FMT_DOC "Generate the doc target." ${MASTER_PROJECT})
|
option(FMT_DOC "Generate the doc target." ${MASTER_PROJECT})
|
||||||
option(FMT_INSTALL "Generate the install target." ${MASTER_PROJECT})
|
option(FMT_INSTALL "Generate the install target." ${MASTER_PROJECT})
|
||||||
option(FMT_TEST "Generate the test target." ${MASTER_PROJECT})
|
option(FMT_TEST "Generate the test target." ${MASTER_PROJECT})
|
||||||
option(FMT_USE_CPP11 "Enable the addition of C++11 compiler flags." ON)
|
option(FMT_USE_CPP14 "Enable the addition of C++14 compiler flags." ON)
|
||||||
|
|
||||||
project(FMT)
|
project(FMT)
|
||||||
|
|
||||||
# Starting with cmake 3.0 VERSION is part of the project command.
|
# Get version from core.h
|
||||||
file(READ fmt/format.h format_h)
|
file(READ include/fmt/core.h core_h)
|
||||||
if (NOT format_h MATCHES "FMT_VERSION ([0-9]+)([0-9][0-9])([0-9][0-9])")
|
if (NOT core_h MATCHES "FMT_VERSION ([0-9]+)([0-9][0-9])([0-9][0-9])")
|
||||||
message(FATAL_ERROR "Cannot get FMT_VERSION from format.h.")
|
message(FATAL_ERROR "Cannot get FMT_VERSION from core.h.")
|
||||||
endif ()
|
endif ()
|
||||||
# Use math to skip leading zeros if any.
|
# Use math to skip leading zeros if any.
|
||||||
math(EXPR CPACK_PACKAGE_VERSION_MAJOR ${CMAKE_MATCH_1})
|
math(EXPR CPACK_PACKAGE_VERSION_MAJOR ${CMAKE_MATCH_1})
|
||||||
@ -65,7 +57,7 @@ set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)
|
|||||||
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(cxx11)
|
include(cxx14)
|
||||||
|
|
||||||
if (CMAKE_COMPILER_IS_GNUCXX OR (CMAKE_CXX_COMPILER_ID MATCHES "Clang"))
|
if (CMAKE_COMPILER_IS_GNUCXX OR (CMAKE_CXX_COMPILER_ID MATCHES "Clang"))
|
||||||
set(PEDANTIC_COMPILE_FLAGS -Wall -Wextra -Wshadow -pedantic)
|
set(PEDANTIC_COMPILE_FLAGS -Wall -Wextra -Wshadow -pedantic)
|
||||||
@ -94,7 +86,104 @@ else ()
|
|||||||
check_symbol_exists(open fcntl.h HAVE_OPEN)
|
check_symbol_exists(open fcntl.h HAVE_OPEN)
|
||||||
endif ()
|
endif ()
|
||||||
|
|
||||||
add_subdirectory(fmt)
|
function(add_headers VAR)
|
||||||
|
set(headers ${${VAR}})
|
||||||
|
foreach (header ${ARGN})
|
||||||
|
set(headers ${headers} include/fmt/${header})
|
||||||
|
endforeach()
|
||||||
|
set(${VAR} ${headers} PARENT_SCOPE)
|
||||||
|
endfunction()
|
||||||
|
|
||||||
|
# Define the fmt library, its includes and the needed defines.
|
||||||
|
# format.cc is added to FMT_HEADERS for the header-only configuration.
|
||||||
|
add_headers(FMT_HEADERS core.h format.h format.cc locale.h ostream.h printf.h
|
||||||
|
time.h)
|
||||||
|
if (HAVE_OPEN)
|
||||||
|
add_headers(FMT_HEADERS posix.h)
|
||||||
|
add_headers(FMT_SOURCES posix.cc)
|
||||||
|
endif ()
|
||||||
|
|
||||||
|
add_library(fmt ${FMT_SOURCES} ${FMT_HEADERS} README.rst ChangeLog.rst)
|
||||||
|
|
||||||
|
# Starting with CMake 3.1 the CXX_STANDARD property can be used instead.
|
||||||
|
# Don't export -std since it may break projects that use other standards.
|
||||||
|
target_compile_options(fmt PRIVATE ${CPP14_FLAG})
|
||||||
|
if (FMT_PEDANTIC)
|
||||||
|
target_compile_options(fmt PRIVATE ${PEDANTIC_COMPILE_FLAGS})
|
||||||
|
endif ()
|
||||||
|
|
||||||
|
target_include_directories(fmt PUBLIC
|
||||||
|
$<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/include>
|
||||||
|
$<INSTALL_INTERFACE:include>)
|
||||||
|
|
||||||
|
set_target_properties(fmt PROPERTIES
|
||||||
|
VERSION ${FMT_VERSION} SOVERSION ${CPACK_PACKAGE_VERSION_MAJOR}
|
||||||
|
DEBUG_POSTFIX d)
|
||||||
|
|
||||||
|
if (BUILD_SHARED_LIBS)
|
||||||
|
if (UNIX AND NOT APPLE)
|
||||||
|
# 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 ()
|
||||||
|
|
||||||
|
# Additionally define a header-only library when CMake is new enough.
|
||||||
|
if (CMAKE_VERSION VERSION_GREATER 3.1.0 OR CMAKE_VERSION VERSION_EQUAL 3.1.0)
|
||||||
|
add_library(fmt-header-only INTERFACE)
|
||||||
|
|
||||||
|
target_compile_definitions(fmt-header-only INTERFACE FMT_HEADER_ONLY=1)
|
||||||
|
|
||||||
|
target_include_directories(fmt-header-only INTERFACE
|
||||||
|
$<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/include>
|
||||||
|
$<INSTALL_INTERFACE:include>)
|
||||||
|
endif ()
|
||||||
|
|
||||||
|
# Install targets.
|
||||||
|
if (FMT_INSTALL)
|
||||||
|
include(GNUInstallDirs)
|
||||||
|
include(CMakePackageConfigHelpers)
|
||||||
|
set(FMT_CMAKE_DIR ${CMAKE_INSTALL_LIBDIR}/cmake/fmt CACHE STRING
|
||||||
|
"Installation directory for cmake files, relative to ${CMAKE_INSTALL_PREFIX}.")
|
||||||
|
set(version_config ${PROJECT_BINARY_DIR}/fmt-config-version.cmake)
|
||||||
|
set(project_config ${PROJECT_BINARY_DIR}/fmt-config.cmake)
|
||||||
|
set(targets_export_name fmt-targets)
|
||||||
|
|
||||||
|
set (INSTALL_TARGETS fmt)
|
||||||
|
if (TARGET fmt-header-only)
|
||||||
|
set(INSTALL_TARGETS ${INSTALL_TARGETS} fmt-header-only)
|
||||||
|
endif ()
|
||||||
|
|
||||||
|
set(FMT_LIB_DIR ${CMAKE_INSTALL_LIBDIR} CACHE STRING
|
||||||
|
"Installation directory for libraries, relative to ${CMAKE_INSTALL_PREFIX}.")
|
||||||
|
|
||||||
|
set(FMT_INC_DIR ${CMAKE_INSTALL_INCLUDEDIR}/fmt CACHE STRING
|
||||||
|
"Installation directory for include files, relative to ${CMAKE_INSTALL_PREFIX}.")
|
||||||
|
|
||||||
|
# Generate the version, config and target files into the build directory.
|
||||||
|
write_basic_package_version_file(
|
||||||
|
${version_config}
|
||||||
|
VERSION ${FMT_VERSION}
|
||||||
|
COMPATIBILITY AnyNewerVersion)
|
||||||
|
configure_package_config_file(
|
||||||
|
${PROJECT_SOURCE_DIR}/support/cmake/fmt-config.cmake.in
|
||||||
|
${project_config}
|
||||||
|
INSTALL_DESTINATION ${FMT_CMAKE_DIR})
|
||||||
|
export(TARGETS ${INSTALL_TARGETS}
|
||||||
|
FILE ${PROJECT_BINARY_DIR}/${targets_export_name}.cmake)
|
||||||
|
|
||||||
|
# Install version, config and target files.
|
||||||
|
install(
|
||||||
|
FILES ${project_config} ${version_config}
|
||||||
|
DESTINATION ${FMT_CMAKE_DIR})
|
||||||
|
install(EXPORT ${targets_export_name} DESTINATION ${FMT_CMAKE_DIR})
|
||||||
|
|
||||||
|
# Install the library and headers.
|
||||||
|
install(TARGETS ${INSTALL_TARGETS} EXPORT ${targets_export_name}
|
||||||
|
DESTINATION ${FMT_LIB_DIR})
|
||||||
|
install(FILES ${FMT_HEADERS} DESTINATION ${FMT_INC_DIR})
|
||||||
|
endif ()
|
||||||
|
|
||||||
if (FMT_DOC)
|
if (FMT_DOC)
|
||||||
add_subdirectory(doc)
|
add_subdirectory(doc)
|
||||||
|
@ -6,6 +6,7 @@ All C++ code must adhere to `Google C++ Style Guide
|
|||||||
exceptions:
|
exceptions:
|
||||||
|
|
||||||
* Exceptions are permitted
|
* Exceptions are permitted
|
||||||
* snake_case should be used instead of UpperCamelCase for function names
|
* snake_case should be used instead of UpperCamelCase for function and type
|
||||||
|
names
|
||||||
|
|
||||||
Thanks for contributing!
|
Thanks for contributing!
|
||||||
|
262
ChangeLog.rst
262
ChangeLog.rst
@ -1,37 +1,86 @@
|
|||||||
4.1.1 - TBD
|
4.1.1 - TBD
|
||||||
------------
|
------------
|
||||||
|
|
||||||
4.1.0 - 2017-12-20
|
4.1.0 - 2017-12-20
|
||||||
------------------
|
------------------
|
||||||
|
|
||||||
* Added ``fmt::to_wstring()`` in addition to ``fmt::to_string()`` (`#559 <https://github.com/fmtlib/fmt/pull/559>`_). Thanks `@alabuzhev (Alex Alabuzhev) <https://github.com/alabuzhev>`_.
|
* Added ``fmt::to_wstring()`` in addition to ``fmt::to_string()``
|
||||||
|
(`#559 <https://github.com/fmtlib/fmt/pull/559>`_).
|
||||||
|
Thanks `@alabuzhev (Alex Alabuzhev) <https://github.com/alabuzhev>`_.
|
||||||
|
|
||||||
* Added support for C++17 ``std::string_view`` (`#571 <https://github.com/fmtlib/fmt/pull/571>`_ and `#578 <https://github.com/fmtlib/fmt/pull/578>`_). Thanks `@thelostt (Mário Feroldi) <https://github.com/thelostt>`_ and `@mwinterb <https://github.com/mwinterb>`_.
|
* Added support for C++17 ``std::string_view``
|
||||||
|
(`#571 <https://github.com/fmtlib/fmt/pull/571>`_ and
|
||||||
|
`#578 <https://github.com/fmtlib/fmt/pull/578>`_).
|
||||||
|
Thanks `@thelostt (Mário Feroldi) <https://github.com/thelostt>`_ and
|
||||||
|
`@mwinterb <https://github.com/mwinterb>`_.
|
||||||
|
|
||||||
* Enabled stream exceptions to catch errors (`#581 <https://github.com/fmtlib/fmt/issues/581>`_). Thanks `@crusader-mike <https://github.com/crusader-mike>`_.
|
* Enabled stream exceptions to catch errors
|
||||||
|
(`#581 <https://github.com/fmtlib/fmt/issues/581>`_).
|
||||||
|
Thanks `@crusader-mike <https://github.com/crusader-mike>`_.
|
||||||
|
|
||||||
* Allowed formatting of class hierarchies with ``fmt::format_arg()`` (`#547 <https://github.com/fmtlib/fmt/pull/547>`_). Thanks `@rollbear (Björn Fahller) <https://github.com/rollbear>`_.
|
* Allowed formatting of class hierarchies with ``fmt::format_arg()``
|
||||||
|
(`#547 <https://github.com/fmtlib/fmt/pull/547>`_).
|
||||||
|
Thanks `@rollbear (Björn Fahller) <https://github.com/rollbear>`_.
|
||||||
|
|
||||||
* Removed limitations on character types
|
* Removed limitations on character types
|
||||||
(`#563 <https://github.com/fmtlib/fmt/pull/563>`_).
|
(`#563 <https://github.com/fmtlib/fmt/pull/563>`_).
|
||||||
Thanks `@Yelnats321 (Elnar Dakeshov) <https://github.com/Yelnats321>`_.
|
Thanks `@Yelnats321 (Elnar Dakeshov) <https://github.com/Yelnats321>`_.
|
||||||
|
|
||||||
* Conditionally enabled use of ``std::allocator_traits`` (`#583 <https://github.com/fmtlib/fmt/pull/583>`_). Thanks `@mwinterb <https://github.com/mwinterb>`_.
|
* Conditionally enabled use of ``std::allocator_traits``
|
||||||
|
(`#583 <https://github.com/fmtlib/fmt/pull/583>`_).
|
||||||
|
Thanks `@mwinterb <https://github.com/mwinterb>`_.
|
||||||
|
|
||||||
* Added support for ``const`` variadic member function emulation with ``FMT_VARIADIC_CONST`` (`#591 <https://github.com/fmtlib/fmt/pull/591>`_). Thanks `@ludekvodicka (Ludek Vodicka) <https://github.com/ludekvodicka>`_.
|
* Added support for ``const`` variadic member function emulation with
|
||||||
|
``FMT_VARIADIC_CONST`` (`#591 <https://github.com/fmtlib/fmt/pull/591>`_).
|
||||||
|
Thanks `@ludekvodicka (Ludek Vodicka) <https://github.com/ludekvodicka>`_.
|
||||||
|
|
||||||
* Various bugfixes: bad overflow check, unsupported implicit type conversion when determining formatting function, test segfaults (`#551 <https://github.com/fmtlib/fmt/issues/551>`_), ill-formed macros (`#542 <https://github.com/fmtlib/fmt/pull/542>`_) and ambiguous overloads (`#580 <https://github.com/fmtlib/fmt/issues/580>`_). Thanks `@xylosper (Byoung-young Lee) <https://github.com/xylosper>`_.
|
* Various bugfixes: bad overflow check, unsupported implicit type conversion
|
||||||
|
when determining formatting function, test segfaults
|
||||||
|
(`#551 <https://github.com/fmtlib/fmt/issues/551>`_), ill-formed macros
|
||||||
|
(`#542 <https://github.com/fmtlib/fmt/pull/542>`_) and ambiguous overloads
|
||||||
|
(`#580 <https://github.com/fmtlib/fmt/issues/580>`_).
|
||||||
|
Thanks `@xylosper (Byoung-young Lee) <https://github.com/xylosper>`_.
|
||||||
|
|
||||||
* Prevented warnings on MSVC (`#605 <https://github.com/fmtlib/fmt/pull/605>`_, `#602 <https://github.com/fmtlib/fmt/pull/602>`_, and `#545 <https://github.com/fmtlib/fmt/pull/545>`_), clang (`#582 <https://github.com/fmtlib/fmt/pull/582>`_), GCC (`#573 <https://github.com/fmtlib/fmt/issues/573>`_), various conversion warnings (`#609 <https://github.com/fmtlib/fmt/pull/609>`_, `#567 <https://github.com/fmtlib/fmt/pull/567>`_, `#553 <https://github.com/fmtlib/fmt/pull/553>`_ and `#553 <https://github.com/fmtlib/fmt/pull/553>`_), and added ``override`` and ``[[noreturn]]`` (`#549 <https://github.com/fmtlib/fmt/pull/549>`_ and `#555 <https://github.com/fmtlib/fmt/issues/555>`_). Thanks `@alabuzhev (Alex Alabuzhev) <https://github.com/alabuzhev>`_, `@virgiliofornazin (Virgilio Alexandre Fornazin) <https://gihtub.com/virgiliofornazin>`_, `@alexanderbock (Alexander Bock) <https://github.com/alexanderbock>`_, `@yumetodo <https://github.com/yumetodo>`_, `@VaderY (Császár Mátyás) <https://github.com/VaderY>`_, `@jpcima (JP Cimalando) <https://github.com/jpcima>`_, `@thelostt (Mário Feroldi) <https://github.com/thelostt>`_, and `@Manu343726 (Manu Sánchez) <https://github.com/Manu343726>`_.
|
* Prevented warnings on MSVC (`#605 <https://github.com/fmtlib/fmt/pull/605>`_,
|
||||||
|
`#602 <https://github.com/fmtlib/fmt/pull/602>`_, and
|
||||||
|
`#545 <https://github.com/fmtlib/fmt/pull/545>`_),
|
||||||
|
clang (`#582 <https://github.com/fmtlib/fmt/pull/582>`_),
|
||||||
|
GCC (`#573 <https://github.com/fmtlib/fmt/issues/573>`_),
|
||||||
|
various conversion warnings (`#609 <https://github.com/fmtlib/fmt/pull/609>`_,
|
||||||
|
`#567 <https://github.com/fmtlib/fmt/pull/567>`_,
|
||||||
|
`#553 <https://github.com/fmtlib/fmt/pull/553>`_ and
|
||||||
|
`#553 <https://github.com/fmtlib/fmt/pull/553>`_), and added ``override`` and
|
||||||
|
``[[noreturn]]`` (`#549 <https://github.com/fmtlib/fmt/pull/549>`_ and
|
||||||
|
`#555 <https://github.com/fmtlib/fmt/issues/555>`_).
|
||||||
|
Thanks `@alabuzhev (Alex Alabuzhev) <https://github.com/alabuzhev>`_,
|
||||||
|
`@virgiliofornazin (Virgilio Alexandre Fornazin)
|
||||||
|
<https://gihtub.com/virgiliofornazin>`_,
|
||||||
|
`@alexanderbock (Alexander Bock) <https://github.com/alexanderbock>`_,
|
||||||
|
`@yumetodo <https://github.com/yumetodo>`_,
|
||||||
|
`@VaderY (Császár Mátyás) <https://github.com/VaderY>`_,
|
||||||
|
`@jpcima (JP Cimalando) <https://github.com/jpcima>`_,
|
||||||
|
`@thelostt (Mário Feroldi) <https://github.com/thelostt>`_, and
|
||||||
|
`@Manu343726 (Manu Sánchez) <https://github.com/Manu343726>`_.
|
||||||
|
|
||||||
* Improved CMake: Used GNUInstallDirs to set installation location (`#610 <https://github.com/fmtlib/fmt/pull/610>`_) and fixed warnings (`#536 <https://github.com/fmtlib/fmt/pull/536>`_ and `#556 <https://github.com/fmtlib/fmt/pull/556>`_). Thanks `@mikecrowe (Mike Crowe) <https://github.com/mikecrowe>`_, `@evgen231 <https://github.com/evgen231>`_ and `@henryiii (Henry Schreiner) <https://github.com/henryiii>`_.
|
* Improved CMake: Used GNUInstallDirs to set installation location
|
||||||
|
(`#610 <https://github.com/fmtlib/fmt/pull/610>`_) and fixed warnings
|
||||||
|
(`#536 <https://github.com/fmtlib/fmt/pull/536>`_ and
|
||||||
|
`#556 <https://github.com/fmtlib/fmt/pull/556>`_).
|
||||||
|
Thanks `@mikecrowe (Mike Crowe) <https://github.com/mikecrowe>`_,
|
||||||
|
`@evgen231 <https://github.com/evgen231>`_ and
|
||||||
|
`@henryiii (Henry Schreiner) <https://github.com/henryiii>`_.
|
||||||
|
|
||||||
4.0.0 - 2017-06-27
|
4.0.0 - 2017-06-27
|
||||||
------------------
|
------------------
|
||||||
|
|
||||||
* Removed old compatibility headers ``cppformat/*.h`` and CMake options (`#527 <https://github.com/fmtlib/fmt/pull/527>`_). Thanks `@maddinat0r (Alex Martin) <https://github.com/maddinat0r>`_.
|
* Removed old compatibility headers ``cppformat/*.h`` and CMake options
|
||||||
|
(`#527 <https://github.com/fmtlib/fmt/pull/527>`_).
|
||||||
|
Thanks `@maddinat0r (Alex Martin) <https://github.com/maddinat0r>`_.
|
||||||
|
|
||||||
* Added ``string.h`` containing ``fmt::to_string()`` as alternative to ``std::to_string()`` as well as other string writer functionality (`#326 <https://github.com/fmtlib/fmt/issues/326>`_ and `#441 <https://github.com/fmtlib/fmt/pull/441>`_):
|
* Added ``string.h`` containing ``fmt::to_string()`` as alternative to
|
||||||
|
``std::to_string()`` as well as other string writer functionality
|
||||||
|
(`#326 <https://github.com/fmtlib/fmt/issues/326>`_ and
|
||||||
|
`#441 <https://github.com/fmtlib/fmt/pull/441>`_):
|
||||||
|
|
||||||
.. code:: c++
|
.. code:: c++
|
||||||
|
|
||||||
@ -39,9 +88,17 @@
|
|||||||
|
|
||||||
std::string answer = fmt::to_string(42);
|
std::string answer = fmt::to_string(42);
|
||||||
|
|
||||||
Thanks to `@glebov-andrey (Andrey Glebov) <https://github.com/glebov-andrey>`_.
|
Thanks to `@glebov-andrey (Andrey Glebov)
|
||||||
|
<https://github.com/glebov-andrey>`_.
|
||||||
|
|
||||||
* Moved ``fmt::printf()`` to new ``printf.h`` header and allowed ``%s`` as generic specifier (`#453 <https://github.com/fmtlib/fmt/pull/453>`_), made ``%.f`` more conformant to regular ``printf()`` (`#490 <https://github.com/fmtlib/fmt/pull/490>`_), added custom writer support (`#476 <https://github.com/fmtlib/fmt/issues/476>`_) and implemented missing custom argument formatting (`#339 <https://github.com/fmtlib/fmt/pull/339>`_ and `#340 <https://github.com/fmtlib/fmt/pull/340>`_):
|
* Moved ``fmt::printf()`` to new ``printf.h`` header and allowed ``%s`` as
|
||||||
|
generic specifier (`#453 <https://github.com/fmtlib/fmt/pull/453>`_),
|
||||||
|
made ``%.f`` more conformant to regular ``printf()``
|
||||||
|
(`#490 <https://github.com/fmtlib/fmt/pull/490>`_), added custom writer
|
||||||
|
support (`#476 <https://github.com/fmtlib/fmt/issues/476>`_) and implemented
|
||||||
|
missing custom argument formatting
|
||||||
|
(`#339 <https://github.com/fmtlib/fmt/pull/339>`_ and
|
||||||
|
`#340 <https://github.com/fmtlib/fmt/pull/340>`_):
|
||||||
|
|
||||||
.. code:: c++
|
.. code:: c++
|
||||||
|
|
||||||
@ -50,11 +107,21 @@
|
|||||||
// %s format specifier can be used with any argument type.
|
// %s format specifier can be used with any argument type.
|
||||||
fmt::printf("%s", 42);
|
fmt::printf("%s", 42);
|
||||||
|
|
||||||
Thanks `@mojoBrendan <https://github.com/mojoBrendan>`_, `@manylegged (Arthur Danskin) <https://github.com/manylegged>`_ and `@spacemoose (Glen Stark) <https://github.com/spacemoose>`_. See also `#360 <https://github.com/fmtlib/fmt/issues/360>`_, `#335 <https://github.com/fmtlib/fmt/issues/335>`_ and `#331 <https://github.com/fmtlib/fmt/issues/331>`_.
|
Thanks `@mojoBrendan <https://github.com/mojoBrendan>`_,
|
||||||
|
`@manylegged (Arthur Danskin) <https://github.com/manylegged>`_ and
|
||||||
|
`@spacemoose (Glen Stark) <https://github.com/spacemoose>`_.
|
||||||
|
See also `#360 <https://github.com/fmtlib/fmt/issues/360>`_,
|
||||||
|
`#335 <https://github.com/fmtlib/fmt/issues/335>`_ and
|
||||||
|
`#331 <https://github.com/fmtlib/fmt/issues/331>`_.
|
||||||
|
|
||||||
* Added ``container.h`` containing a ``BasicContainerWriter`` to write to containers like ``std::vector`` (`#450 <https://github.com/fmtlib/fmt/pull/450>`_). Thanks `@polyvertex (Jean-Charles Lefebvre) <https://github.com/polyvertex>`_.
|
* Added ``container.h`` containing a ``BasicContainerWriter``
|
||||||
|
to write to containers like ``std::vector``
|
||||||
|
(`#450 <https://github.com/fmtlib/fmt/pull/450>`_).
|
||||||
|
Thanks `@polyvertex (Jean-Charles Lefebvre) <https://github.com/polyvertex>`_.
|
||||||
|
|
||||||
* Added ``fmt::join()`` function that takes a range and formats its elements separated by a given string (`#466 <https://github.com/fmtlib/fmt/pull/466>`_):
|
* Added ``fmt::join()`` function that takes a range and formats
|
||||||
|
its elements separated by a given string
|
||||||
|
(`#466 <https://github.com/fmtlib/fmt/pull/466>`_):
|
||||||
|
|
||||||
.. code:: c++
|
.. code:: c++
|
||||||
|
|
||||||
@ -66,75 +133,172 @@
|
|||||||
|
|
||||||
Thanks `@olivier80 <https://github.com/olivier80>`_.
|
Thanks `@olivier80 <https://github.com/olivier80>`_.
|
||||||
|
|
||||||
* Added support for custom formatting specifications to simplify customization of built-in formatting (`#444 <https://github.com/fmtlib/fmt/pull/444>`_). Thanks `@polyvertex (Jean-Charles Lefebvre) <https://github.com/polyvertex>`_. See also `#439 <https://github.com/fmtlib/fmt/issues/439>`_.
|
* Added support for custom formatting specifications to simplify customization
|
||||||
|
of built-in formatting (`#444 <https://github.com/fmtlib/fmt/pull/444>`_).
|
||||||
|
Thanks `@polyvertex (Jean-Charles Lefebvre) <https://github.com/polyvertex>`_.
|
||||||
|
See also `#439 <https://github.com/fmtlib/fmt/issues/439>`_.
|
||||||
|
|
||||||
* Added ``fmt::format_system_error()`` for error code formatting (`#323 <https://github.com/fmtlib/fmt/issues/323>`_ and `#526 <https://github.com/fmtlib/fmt/pull/526>`_). Thanks `@maddinat0r (Alex Martin) <https://github.com/maddinat0r>`_.
|
* Added ``fmt::format_system_error()`` for error code formatting
|
||||||
|
(`#323 <https://github.com/fmtlib/fmt/issues/323>`_ and
|
||||||
|
`#526 <https://github.com/fmtlib/fmt/pull/526>`_).
|
||||||
|
Thanks `@maddinat0r (Alex Martin) <https://github.com/maddinat0r>`_.
|
||||||
|
|
||||||
* Added thread-safe ``fmt::localtime()`` and ``fmt::gmtime()`` as replacement for the standard version to ``time.h`` (`#396 <https://github.com/fmtlib/fmt/pull/396>`_). Thanks `@codicodi <https://github.com/codicodi>`_.
|
* Added thread-safe ``fmt::localtime()`` and ``fmt::gmtime()``
|
||||||
|
as replacement for the standard version to ``time.h``
|
||||||
|
(`#396 <https://github.com/fmtlib/fmt/pull/396>`_).
|
||||||
|
Thanks `@codicodi <https://github.com/codicodi>`_.
|
||||||
|
|
||||||
* Internal improvements to ``NamedArg`` and ``ArgLists`` (`#389 <https://github.com/fmtlib/fmt/pull/389>`_ and `#390 <https://github.com/fmtlib/fmt/pull/390>`_). Thanks `@chronoxor <https://github.com/chronoxor>`_.
|
* Internal improvements to ``NamedArg`` and ``ArgLists``
|
||||||
|
(`#389 <https://github.com/fmtlib/fmt/pull/389>`_ and
|
||||||
|
`#390 <https://github.com/fmtlib/fmt/pull/390>`_).
|
||||||
|
Thanks `@chronoxor <https://github.com/chronoxor>`_.
|
||||||
|
|
||||||
* Fixed crash due to bug in ``FormatBuf`` (`#493 <https://github.com/fmtlib/fmt/pull/493>`_). Thanks `@effzeh <https://github.com/effzeh>`_. See also `#480 <https://github.com/fmtlib/fmt/issues/480>`_ and `#491 <https://github.com/fmtlib/fmt/issues/491>`_.
|
* Fixed crash due to bug in ``FormatBuf``
|
||||||
|
(`#493 <https://github.com/fmtlib/fmt/pull/493>`_).
|
||||||
|
Thanks `@effzeh <https://github.com/effzeh>`_. See also
|
||||||
|
`#480 <https://github.com/fmtlib/fmt/issues/480>`_ and
|
||||||
|
`#491 <https://github.com/fmtlib/fmt/issues/491>`_.
|
||||||
|
|
||||||
* Fixed handling of wide strings in ``fmt::StringWriter``.
|
* Fixed handling of wide strings in ``fmt::StringWriter``.
|
||||||
|
|
||||||
* Improved compiler error messages (`#357 <https://github.com/fmtlib/fmt/issues/357>`_).
|
* Improved compiler error messages
|
||||||
|
(`#357 <https://github.com/fmtlib/fmt/issues/357>`_).
|
||||||
|
|
||||||
* Fixed various warnings and issues with various compilers (`#494 <https://github.com/fmtlib/fmt/pull/494>`_, `#499 <https://github.com/fmtlib/fmt/pull/499>`_, `#483 <https://github.com/fmtlib/fmt/pull/483>`_, `#519 <https://github.com/fmtlib/fmt/pull/519>`_, `#485 <https://github.com/fmtlib/fmt/pull/485>`_, `#482 <https://github.com/fmtlib/fmt/pull/482>`_, `#475 <https://github.com/fmtlib/fmt/pull/475>`_, `#473 <https://github.com/fmtlib/fmt/pull/473>`_ and `#414 <https://github.com/fmtlib/fmt/pull/414>`_). Thanks `@chronoxor <https://github.com/chronoxor>`_, `@zhaohuaxishi <https://github.com/zhaohuaxishi>`_, `@pkestene (Pierre Kestener) <https://github.com/pkestene>`_, `@dschmidt (Dominik Schmidt) <https://github.com/dschmidt>`_ and `@0x414c (Alexey Gorishny) <https://github.com/0x414c>`_ .
|
* Fixed various warnings and issues with various compilers
|
||||||
|
(`#494 <https://github.com/fmtlib/fmt/pull/494>`_,
|
||||||
|
`#499 <https://github.com/fmtlib/fmt/pull/499>`_,
|
||||||
|
`#483 <https://github.com/fmtlib/fmt/pull/483>`_,
|
||||||
|
`#519 <https://github.com/fmtlib/fmt/pull/519>`_,
|
||||||
|
`#485 <https://github.com/fmtlib/fmt/pull/485>`_,
|
||||||
|
`#482 <https://github.com/fmtlib/fmt/pull/482>`_,
|
||||||
|
`#475 <https://github.com/fmtlib/fmt/pull/475>`_,
|
||||||
|
`#473 <https://github.com/fmtlib/fmt/pull/473>`_ and
|
||||||
|
`#414 <https://github.com/fmtlib/fmt/pull/414>`_).
|
||||||
|
Thanks `@chronoxor <https://github.com/chronoxor>`_,
|
||||||
|
`@zhaohuaxishi <https://github.com/zhaohuaxishi>`_,
|
||||||
|
`@pkestene (Pierre Kestener) <https://github.com/pkestene>`_,
|
||||||
|
`@dschmidt (Dominik Schmidt) <https://github.com/dschmidt>`_ and
|
||||||
|
`@0x414c (Alexey Gorishny) <https://github.com/0x414c>`_ .
|
||||||
|
|
||||||
* Improved CMake: targets are now namespaced (`#511 <https://github.com/fmtlib/fmt/pull/511>`_ and `#513 <https://github.com/fmtlib/fmt/pull/513>`_), supported header-only ``printf.h`` (`#354 <https://github.com/fmtlib/fmt/pull/354>`_), fixed issue with minimal supported library subset (`#418 <https://github.com/fmtlib/fmt/issues/418>`_, `#419 <https://github.com/fmtlib/fmt/pull/419>`_ and `#420 <https://github.com/fmtlib/fmt/pull/420>`_). Thanks `@bjoernthiel (Bjoern Thiel) <https://github.com/bjoernthiel>`_,
|
* Improved CMake: targets are now namespaced
|
||||||
`@niosHD (Mario Werner) <https://github.com/niosHD>`_, `@LogicalKnight (Sean LK) <https://github.com/LogicalKnight>`_ and `@alabuzhev (Alex Alabuzhev) <https://github.com/alabuzhev>`_.
|
(`#511 <https://github.com/fmtlib/fmt/pull/511>`_ and
|
||||||
|
`#513 <https://github.com/fmtlib/fmt/pull/513>`_), supported header-only
|
||||||
|
``printf.h`` (`#354 <https://github.com/fmtlib/fmt/pull/354>`_), fixed issue
|
||||||
|
with minimal supported library subset
|
||||||
|
(`#418 <https://github.com/fmtlib/fmt/issues/418>`_,
|
||||||
|
`#419 <https://github.com/fmtlib/fmt/pull/419>`_ and
|
||||||
|
`#420 <https://github.com/fmtlib/fmt/pull/420>`_).
|
||||||
|
Thanks `@bjoernthiel (Bjoern Thiel) <https://github.com/bjoernthiel>`_,
|
||||||
|
`@niosHD (Mario Werner) <https://github.com/niosHD>`_,
|
||||||
|
`@LogicalKnight (Sean LK) <https://github.com/LogicalKnight>`_ and
|
||||||
|
`@alabuzhev (Alex Alabuzhev) <https://github.com/alabuzhev>`_.
|
||||||
|
|
||||||
* Improved documentation. Thanks to `@pwm1234 (Phil) <https://github.com/pwm1234>`_ for `#393 <https://github.com/fmtlib/fmt/pull/393>`_.
|
* Improved documentation. Thanks to
|
||||||
|
`@pwm1234 (Phil) <https://github.com/pwm1234>`_ for
|
||||||
|
`#393 <https://github.com/fmtlib/fmt/pull/393>`_.
|
||||||
|
|
||||||
3.0.2 - 2017-06-14
|
3.0.2 - 2017-06-14
|
||||||
------------------
|
------------------
|
||||||
|
|
||||||
* Added ``FMT_VERSION`` macro (`#411 <https://github.com/fmtlib/fmt/issues/411>`_).
|
* Added ``FMT_VERSION`` macro
|
||||||
|
(`#411 <https://github.com/fmtlib/fmt/issues/411>`_).
|
||||||
|
|
||||||
* Used ``FMT_NULL`` instead of literal ``0`` (`#409 <https://github.com/fmtlib/fmt/pull/409>`_). Thanks `@alabuzhev (Alex Alabuzhev) <https://github.com/alabuzhev>`_.
|
* Used ``FMT_NULL`` instead of literal ``0``
|
||||||
|
(`#409 <https://github.com/fmtlib/fmt/pull/409>`_).
|
||||||
|
Thanks `@alabuzhev (Alex Alabuzhev) <https://github.com/alabuzhev>`_.
|
||||||
|
|
||||||
* Added extern templates for ``format_float`` (`#413 <https://github.com/fmtlib/fmt/issues/413>`_).
|
* Added extern templates for ``format_float``
|
||||||
|
(`#413 <https://github.com/fmtlib/fmt/issues/413>`_).
|
||||||
|
|
||||||
* Fixed implicit conversion issue (`#507 <https://github.com/fmtlib/fmt/issues/507>`_).
|
* Fixed implicit conversion issue
|
||||||
|
(`#507 <https://github.com/fmtlib/fmt/issues/507>`_).
|
||||||
|
|
||||||
* Fixed signbit detection (`#423 <https://github.com/fmtlib/fmt/issues/423>`_).
|
* Fixed signbit detection (`#423 <https://github.com/fmtlib/fmt/issues/423>`_).
|
||||||
|
|
||||||
* Fixed naming collision (`#425 <https://github.com/fmtlib/fmt/issues/425>`_).
|
* Fixed naming collision (`#425 <https://github.com/fmtlib/fmt/issues/425>`_).
|
||||||
|
|
||||||
* Fixed missing intrinsic for C++/CLI (`#457 <https://github.com/fmtlib/fmt/pull/457>`_). Thanks `@calumr (Calum Robinson) <https://github.com/calumr>`_
|
* Fixed missing intrinsic for C++/CLI
|
||||||
|
(`#457 <https://github.com/fmtlib/fmt/pull/457>`_).
|
||||||
|
Thanks `@calumr (Calum Robinson) <https://github.com/calumr>`_
|
||||||
|
|
||||||
* Fixed Android detection (`#458 <https://github.com/fmtlib/fmt/pull/458>`_). Thanks `@Gachapen (Magnus Bjerke Vik) <https://github.com/Gachapen>`_.
|
* Fixed Android detection (`#458 <https://github.com/fmtlib/fmt/pull/458>`_).
|
||||||
|
Thanks `@Gachapen (Magnus Bjerke Vik) <https://github.com/Gachapen>`_.
|
||||||
|
|
||||||
* Use lean ``windows.h`` if not in header-only mode (`#503 <https://github.com/fmtlib/fmt/pull/503>`_). Thanks `@Quentin01 (Quentin Buathier) <https://github.com/Quentin01>`_.
|
* Use lean ``windows.h`` if not in header-only mode
|
||||||
|
(`#503 <https://github.com/fmtlib/fmt/pull/503>`_).
|
||||||
|
Thanks `@Quentin01 (Quentin Buathier) <https://github.com/Quentin01>`_.
|
||||||
|
|
||||||
* Fixed issue with CMake exporting C++11 flag (`#445 <https://github.com/fmtlib/fmt/pull/455>`_). Thanks `@EricWF (Eric) <https://github.com/EricWF>`_.
|
* Fixed issue with CMake exporting C++11 flag
|
||||||
|
(`#445 <https://github.com/fmtlib/fmt/pull/455>`_).
|
||||||
|
Thanks `@EricWF (Eric) <https://github.com/EricWF>`_.
|
||||||
|
|
||||||
* Fixed issue with nvcc and MSVC compiler bug and MinGW (`#505 <https://github.com/fmtlib/fmt/issues/505>`_).
|
* Fixed issue with nvcc and MSVC compiler bug and MinGW
|
||||||
|
(`#505 <https://github.com/fmtlib/fmt/issues/505>`_).
|
||||||
|
|
||||||
* Fixed DLL issues (`#469 <https://github.com/fmtlib/fmt/pull/469>`_ and `#502 <https://github.com/fmtlib/fmt/pull/502>`_). Thanks `@richardeakin (Richard Eakin) <https://github.com/richardeakin>`_ and `@AndreasSchoenle (Andreas Schönle) <https://github.com/AndreasSchoenle>`_.
|
* Fixed DLL issues (`#469 <https://github.com/fmtlib/fmt/pull/469>`_ and
|
||||||
|
`#502 <https://github.com/fmtlib/fmt/pull/502>`_).
|
||||||
|
Thanks `@richardeakin (Richard Eakin) <https://github.com/richardeakin>`_ and
|
||||||
|
`@AndreasSchoenle (Andreas Schönle) <https://github.com/AndreasSchoenle>`_.
|
||||||
|
|
||||||
* Fixed test compilation under FreeBSD (`#433 <https://github.com/fmtlib/fmt/issues/433>`_).
|
* Fixed test compilation under FreeBSD
|
||||||
|
(`#433 <https://github.com/fmtlib/fmt/issues/433>`_).
|
||||||
|
|
||||||
* Fixed various warnings (`#403 <https://github.com/fmtlib/fmt/pull/403>`_, `#410 <https://github.com/fmtlib/fmt/pull/410>`_ and `#510 <https://github.com/fmtlib/fmt/pull/510>`_). Thanks `@Lecetem <https://github.com/Lectem>`_, `@chenhayat (Chen Hayat) <https://github.com/chenhayat>`_ and `@trozen <https://github.com/trozen>`_.
|
* Fixed various warnings (`#403 <https://github.com/fmtlib/fmt/pull/403>`_,
|
||||||
|
`#410 <https://github.com/fmtlib/fmt/pull/410>`_ and
|
||||||
|
`#510 <https://github.com/fmtlib/fmt/pull/510>`_).
|
||||||
|
Thanks `@Lecetem <https://github.com/Lectem>`_,
|
||||||
|
`@chenhayat (Chen Hayat) <https://github.com/chenhayat>`_ and
|
||||||
|
`@trozen <https://github.com/trozen>`_.
|
||||||
|
|
||||||
* Removed redundant include (`#479 <https://github.com/fmtlib/fmt/issues/479>`_).
|
* Removed redundant include
|
||||||
|
(`#479 <https://github.com/fmtlib/fmt/issues/479>`_).
|
||||||
|
|
||||||
* Fixed documentation issues.
|
* Fixed documentation issues.
|
||||||
|
|
||||||
3.0.1 - 2016-11-01
|
3.0.1 - 2016-11-01
|
||||||
------------------
|
------------------
|
||||||
* Fixed handling of thousands seperator (`#353 <https://github.com/fmtlib/fmt/issues/353>`_)
|
* Fixed handling of thousands seperator
|
||||||
|
(`#353 <https://github.com/fmtlib/fmt/issues/353>`_).
|
||||||
|
|
||||||
* Fixed handling of ``unsigned char`` strings (`#373 <https://github.com/fmtlib/fmt/issues/373>`_)
|
* Fixed handling of ``unsigned char`` strings
|
||||||
|
(`#373 <https://github.com/fmtlib/fmt/issues/373>`_).
|
||||||
|
|
||||||
* Corrected buffer growth when formatting time (`#367 <https://github.com/fmtlib/fmt/issues/367>`_)
|
* Corrected buffer growth when formatting time
|
||||||
|
(`#367 <https://github.com/fmtlib/fmt/issues/367>`_).
|
||||||
|
|
||||||
* Removed warnings under MSVC and clang (`#318 <https://github.com/fmtlib/fmt/issues/318>`_, `#250 <https://github.com/fmtlib/fmt/issues/250>`_, also merged `#385 <https://github.com/fmtlib/fmt/pull/385>`_ and `#361 <https://github.com/fmtlib/fmt/pull/361>`_). Thanks `@jcelerier (Jean-Michaël Celerier) <https://github.com/jcelerier>`_ and `@nmoehrle (Nils Moehrle) <https://github.com/nmoehrle>`_.
|
* Removed warnings under MSVC and clang
|
||||||
|
(`#318 <https://github.com/fmtlib/fmt/issues/318>`_,
|
||||||
|
`#250 <https://github.com/fmtlib/fmt/issues/250>`_, also merged
|
||||||
|
`#385 <https://github.com/fmtlib/fmt/pull/385>`_ and
|
||||||
|
`#361 <https://github.com/fmtlib/fmt/pull/361>`_).
|
||||||
|
Thanks `@jcelerier (Jean-Michaël Celerier) <https://github.com/jcelerier>`_
|
||||||
|
and `@nmoehrle (Nils Moehrle) <https://github.com/nmoehrle>`_.
|
||||||
|
|
||||||
* Fixed compilation issues under Android (`#327 <https://github.com/fmtlib/fmt/pull/327>`_, `#345 <https://github.com/fmtlib/fmt/issues/345>`_ and `#381 <https://github.com/fmtlib/fmt/pull/381>`_), FreeBSD (`#358 <https://github.com/fmtlib/fmt/pull/358>`_), Cygwin (`#388 <https://github.com/fmtlib/fmt/issues/388>`_), MinGW (`#355 <https://github.com/fmtlib/fmt/issues/355>`_) as well as other issues (`#350 <https://github.com/fmtlib/fmt/issues/350>`_, `#366 <https://github.com/fmtlib/fmt/issues/355>`_, `#348 <https://github.com/fmtlib/fmt/pull/348>`_, `#402 <https://github.com/fmtlib/fmt/pull/402>`_, `#405 <https://github.com/fmtlib/fmt/pull/405>`_). Thanks to `@dpantele (Dmitry) <https://github.com/dpantele>`_, `@hghwng (Hugh Wang) <https://github.com/hghwng>`_, `@arvedarved (Tilman Keskinöz) <https://github.com/arvedarved>`_, `@LogicalKnight (Sean) <https://github.com/LogicalKnight>`_ and `@JanHellwig (Jan Hellwig) <https://github.com/janhellwig>`_.
|
* Fixed compilation issues under Android
|
||||||
|
(`#327 <https://github.com/fmtlib/fmt/pull/327>`_,
|
||||||
* Fixed some documentation issues and extended specification (`#320 <https://github.com/fmtlib/fmt/issues/320>`_, `#333 <https://github.com/fmtlib/fmt/pull/333>`_, `#347 <https://github.com/fmtlib/fmt/issues/347>`_, `#362 <https://github.com/fmtlib/fmt/pull/362>`_). Thanks to `@smellman (Taro Matsuzawa aka. btm) <https://github.com/smellman>`_.
|
`#345 <https://github.com/fmtlib/fmt/issues/345>`_ and
|
||||||
|
`#381 <https://github.com/fmtlib/fmt/pull/381>`_),
|
||||||
|
FreeBSD (`#358 <https://github.com/fmtlib/fmt/pull/358>`_),
|
||||||
|
Cygwin (`#388 <https://github.com/fmtlib/fmt/issues/388>`_),
|
||||||
|
MinGW (`#355 <https://github.com/fmtlib/fmt/issues/355>`_) as well as other
|
||||||
|
issues (`#350 <https://github.com/fmtlib/fmt/issues/350>`_,
|
||||||
|
`#366 <https://github.com/fmtlib/fmt/issues/355>`_,
|
||||||
|
`#348 <https://github.com/fmtlib/fmt/pull/348>`_,
|
||||||
|
`#402 <https://github.com/fmtlib/fmt/pull/402>`_,
|
||||||
|
`#405 <https://github.com/fmtlib/fmt/pull/405>`_).
|
||||||
|
Thanks to `@dpantele (Dmitry) <https://github.com/dpantele>`_,
|
||||||
|
`@hghwng (Hugh Wang) <https://github.com/hghwng>`_,
|
||||||
|
`@arvedarved (Tilman Keskinöz) <https://github.com/arvedarved>`_,
|
||||||
|
`@LogicalKnight (Sean) <https://github.com/LogicalKnight>`_ and
|
||||||
|
`@JanHellwig (Jan Hellwig) <https://github.com/janhellwig>`_.
|
||||||
|
|
||||||
|
* Fixed some documentation issues and extended specification
|
||||||
|
(`#320 <https://github.com/fmtlib/fmt/issues/320>`_,
|
||||||
|
`#333 <https://github.com/fmtlib/fmt/pull/333>`_,
|
||||||
|
`#347 <https://github.com/fmtlib/fmt/issues/347>`_,
|
||||||
|
`#362 <https://github.com/fmtlib/fmt/pull/362>`_).
|
||||||
|
Thanks to `@smellman (Taro Matsuzawa aka. btm)
|
||||||
|
<https://github.com/smellman>`_.
|
||||||
|
|
||||||
3.0.0 - 2016-05-07
|
3.0.0 - 2016-05-07
|
||||||
------------------
|
------------------
|
||||||
@ -252,8 +416,8 @@
|
|||||||
`@Gachapen (Magnus Bjerke Vik) <https://github.com/Gachapen>`_ and
|
`@Gachapen (Magnus Bjerke Vik) <https://github.com/Gachapen>`_ and
|
||||||
`@jwilk (Jakub Wilk) <https://github.com/jwilk>`_.
|
`@jwilk (Jakub Wilk) <https://github.com/jwilk>`_.
|
||||||
|
|
||||||
* Fixed compiler and sanitizer warnings (
|
* Fixed compiler and sanitizer warnings
|
||||||
`#244 <https://github.com/fmtlib/fmt/issues/244>`_,
|
(`#244 <https://github.com/fmtlib/fmt/issues/244>`_,
|
||||||
`#256 <https://github.com/fmtlib/fmt/pull/256>`_,
|
`#256 <https://github.com/fmtlib/fmt/pull/256>`_,
|
||||||
`#259 <https://github.com/fmtlib/fmt/pull/259>`_,
|
`#259 <https://github.com/fmtlib/fmt/pull/259>`_,
|
||||||
`#263 <https://github.com/fmtlib/fmt/issues/263>`_,
|
`#263 <https://github.com/fmtlib/fmt/issues/263>`_,
|
||||||
|
181
README.rst
181
README.rst
@ -21,40 +21,39 @@ Features
|
|||||||
--------
|
--------
|
||||||
|
|
||||||
* Two APIs: faster concatenation-based `write API
|
* Two APIs: faster concatenation-based `write API
|
||||||
<http://fmtlib.net/latest/api.html#write-api>`_ and slower,
|
<http://fmtlib.net/latest/api.html#write-api>`_ and slower, but still very
|
||||||
but still very fast, replacement-based `format API
|
fast, replacement-based `format API
|
||||||
<http://fmtlib.net/latest/api.html#format-api>`_ with positional arguments
|
<http://fmtlib.net/latest/api.html#format-api>`_ with positional arguments for
|
||||||
for localization.
|
localization.
|
||||||
* Write API similar to the one used by IOStreams but stateless allowing
|
* Write API similar to the one used by IOStreams but stateless allowing faster
|
||||||
faster implementation.
|
implementation.
|
||||||
* Format API with `format string syntax
|
* Format API with `format string syntax <http://fmtlib.net/latest/syntax.html>`_
|
||||||
<http://fmtlib.net/latest/syntax.html>`_
|
|
||||||
similar to the one used by `str.format
|
similar to the one used by `str.format
|
||||||
<https://docs.python.org/2/library/stdtypes.html#str.format>`_ in Python.
|
<https://docs.python.org/2/library/stdtypes.html#str.format>`_ in Python.
|
||||||
* Safe `printf implementation
|
* Safe `printf implementation
|
||||||
<http://fmtlib.net/latest/api.html#printf-formatting-functions>`_
|
<http://fmtlib.net/latest/api.html#printf-formatting-functions>`_ including
|
||||||
including the POSIX extension for positional arguments.
|
the POSIX extension for positional arguments.
|
||||||
* Support for user-defined types.
|
* Support for user-defined types.
|
||||||
* High speed: performance of the format API is close to that of
|
* High speed: performance of the format API is close to that of glibc's `printf
|
||||||
glibc's `printf <http://en.cppreference.com/w/cpp/io/c/fprintf>`_
|
<http://en.cppreference.com/w/cpp/io/c/fprintf>`_ and better than the
|
||||||
and better than the performance of IOStreams. See `Speed tests`_ and
|
performance of IOStreams. See `Speed tests`_ and
|
||||||
`Fast integer to string conversion in C++
|
`Fast integer to string conversion in C++
|
||||||
<http://zverovich.net/2013/09/07/integer-to-string-conversion-in-cplusplus.html>`_.
|
<http://zverovich.net/2013/09/07/integer-to-string-conversion-in-cplusplus.html>`_.
|
||||||
* Small code size both in terms of source code (the core library consists of a single
|
* Small code size both in terms of source code (the core library consists of a
|
||||||
header file and a single source file) and compiled code.
|
single header file and a single source file) and compiled code.
|
||||||
See `Compile time and code bloat`_.
|
See `Compile time and code bloat`_.
|
||||||
* Reliability: the library has an extensive set of `unit tests
|
* Reliability: the library has an extensive set of `unit tests
|
||||||
<https://github.com/fmtlib/fmt/tree/master/test>`_.
|
<https://github.com/fmtlib/fmt/tree/master/test>`_.
|
||||||
* Safety: the library is fully type safe, errors in format strings are
|
* Safety: the library is fully type safe, errors in format strings can be
|
||||||
reported using exceptions, automatic memory management prevents buffer
|
reported at compile time, automatic memory management prevents buffer overflow
|
||||||
overflow errors.
|
errors.
|
||||||
* Ease of use: small self-contained code base, no external dependencies,
|
* Ease of use: small self-contained code base, no external dependencies,
|
||||||
permissive BSD `license
|
permissive BSD `license
|
||||||
<https://github.com/fmtlib/fmt/blob/master/LICENSE.rst>`_
|
<https://github.com/fmtlib/fmt/blob/master/LICENSE.rst>`_
|
||||||
* `Portability <http://fmtlib.net/latest/index.html#portability>`_ with consistent output
|
* `Portability <http://fmtlib.net/latest/index.html#portability>`_ with
|
||||||
across platforms and support for older compilers.
|
consistent output across platforms and support for older compilers.
|
||||||
* Clean warning-free codebase even on high warning levels
|
* Clean warning-free codebase even on high warning levels
|
||||||
(-Wall -Wextra -pedantic).
|
(``-Wall -Wextra -pedantic``).
|
||||||
* Support for wide strings.
|
* Support for wide strings.
|
||||||
* Optional header-only configuration enabled with the ``FMT_HEADER_ONLY`` macro.
|
* Optional header-only configuration enabled with the ``FMT_HEADER_ONLY`` macro.
|
||||||
|
|
||||||
@ -77,6 +76,28 @@ Arguments can be accessed by position and arguments' indices can be repeated:
|
|||||||
std::string s = fmt::format("{0}{1}{0}", "abra", "cad");
|
std::string s = fmt::format("{0}{1}{0}", "abra", "cad");
|
||||||
// s == "abracadabra"
|
// s == "abracadabra"
|
||||||
|
|
||||||
|
Format strings can be checked at compile time:
|
||||||
|
|
||||||
|
.. code:: c++
|
||||||
|
|
||||||
|
// test.cc
|
||||||
|
using namespace fmt::literals;
|
||||||
|
std::string s = "{2}"_format(42);
|
||||||
|
|
||||||
|
.. code::
|
||||||
|
|
||||||
|
$ g++ -Iinclude test.cc -std=c++14
|
||||||
|
...
|
||||||
|
test.cc:5:31: note: in instantiation of function template specialization
|
||||||
|
'fmt::internal::udl_formatter<char, '{', '2', '}'>::operator()<int>' requested
|
||||||
|
here
|
||||||
|
std::string s = "{2}"_format(42);
|
||||||
|
^
|
||||||
|
include/fmt/format.h:3838:7: note: non-constexpr function 'on_error' cannot be
|
||||||
|
used in a constant expression
|
||||||
|
on_error("argument index out of range");
|
||||||
|
^
|
||||||
|
|
||||||
fmt can be used as a safe portable replacement for ``itoa``:
|
fmt can be used as a safe portable replacement for ``itoa``:
|
||||||
|
|
||||||
.. code:: c++
|
.. code:: c++
|
||||||
@ -106,9 +127,7 @@ An object of any user-defined type for which there is an overloaded
|
|||||||
std::string s = fmt::format("The date is {}", Date(2012, 12, 9));
|
std::string s = fmt::format("The date is {}", Date(2012, 12, 9));
|
||||||
// s == "The date is 2012-12-9"
|
// s == "The date is 2012-12-9"
|
||||||
|
|
||||||
You can use the `FMT_VARIADIC
|
You can create your own functions similar to `format
|
||||||
<http://fmtlib.net/latest/api.html#utilities>`_
|
|
||||||
macro to create your own functions similar to `format
|
|
||||||
<http://fmtlib.net/latest/api.html#format>`_ and
|
<http://fmtlib.net/latest/api.html#format>`_ and
|
||||||
`print <http://fmtlib.net/latest/api.html#print>`_
|
`print <http://fmtlib.net/latest/api.html#print>`_
|
||||||
which take arbitrary arguments:
|
which take arbitrary arguments:
|
||||||
@ -116,17 +135,19 @@ which take arbitrary arguments:
|
|||||||
.. code:: c++
|
.. code:: c++
|
||||||
|
|
||||||
// Prints formatted error message.
|
// Prints formatted error message.
|
||||||
void report_error(const char *format, fmt::ArgList args) {
|
void vreport_error(const char *format, fmt::args args) {
|
||||||
fmt::print("Error: ");
|
fmt::print("Error: ");
|
||||||
fmt::print(format, args);
|
fmt::vprint(format, args);
|
||||||
|
}
|
||||||
|
template <typename... Args>
|
||||||
|
void report_error(const char *format, const Args & ... args) {
|
||||||
|
vreport_error(format, fmt::make_args(args));
|
||||||
}
|
}
|
||||||
FMT_VARIADIC(void, report_error, const char *)
|
|
||||||
|
|
||||||
report_error("file not found: {}", path);
|
report_error("file not found: {}", path);
|
||||||
|
|
||||||
Note that you only need to define one function that takes ``fmt::ArgList``
|
Note that ``vreport_error`` is not parameterized on argument types which can
|
||||||
argument. ``FMT_VARIADIC`` automatically defines necessary wrappers that
|
improve compile times and reduce code size compared to fully parameterized version.
|
||||||
accept variable number of arguments.
|
|
||||||
|
|
||||||
Projects using this library
|
Projects using this library
|
||||||
---------------------------
|
---------------------------
|
||||||
@ -138,12 +159,6 @@ Projects using this library
|
|||||||
|
|
||||||
* `CUAUV <http://cuauv.org/>`_: Cornell University's autonomous underwater vehicle
|
* `CUAUV <http://cuauv.org/>`_: Cornell University's autonomous underwater vehicle
|
||||||
|
|
||||||
* `Drake <http://drake.mit.edu/>`_: A planning, control, and analysis toolbox for nonlinear dynamical systems (MIT)
|
|
||||||
|
|
||||||
* `Envoy <https://lyft.github.io/envoy/>`_: C++ L7 proxy and communication bus (Lyft)
|
|
||||||
|
|
||||||
* `FiveM <https://fivem.net/>`_: a modification framework for GTA V
|
|
||||||
|
|
||||||
* `HarpyWar/pvpgn <https://github.com/pvpgn/pvpgn-server>`_:
|
* `HarpyWar/pvpgn <https://github.com/pvpgn/pvpgn-server>`_:
|
||||||
Player vs Player Gaming Network with tweaks
|
Player vs Player Gaming Network with tweaks
|
||||||
|
|
||||||
@ -155,14 +170,25 @@ Projects using this library
|
|||||||
|
|
||||||
* `Lifeline <https://github.com/peter-clark/lifeline>`_: A 2D game
|
* `Lifeline <https://github.com/peter-clark/lifeline>`_: A 2D game
|
||||||
|
|
||||||
* `MongoDB Smasher <https://github.com/duckie/mongo_smasher>`_: A small tool to generate randomized datasets
|
* `Drake <http://drake.mit.edu/>`_: A planning, control, and analysis toolbox
|
||||||
|
for nonlinear dynamical systems (MIT)
|
||||||
|
|
||||||
* `OpenSpace <http://openspaceproject.com/>`_: An open-source astrovisualization framework
|
* `Envoy <https://lyft.github.io/envoy/>`_: C++ L7 proxy and communication bus
|
||||||
|
(Lyft)
|
||||||
|
|
||||||
|
* `FiveM <https://fivem.net/>`_: a modification framework for GTA V
|
||||||
|
|
||||||
|
* `MongoDB Smasher <https://github.com/duckie/mongo_smasher>`_: A small tool to
|
||||||
|
generate randomized datasets
|
||||||
|
|
||||||
|
* `OpenSpace <http://openspaceproject.com/>`_: An open-source astrovisualization
|
||||||
|
framework
|
||||||
|
|
||||||
* `PenUltima Online (POL) <http://www.polserver.com/>`_:
|
* `PenUltima Online (POL) <http://www.polserver.com/>`_:
|
||||||
An MMO server, compatible with most Ultima Online clients
|
An MMO server, compatible with most Ultima Online clients
|
||||||
|
|
||||||
* `quasardb <https://www.quasardb.net/>`_: A distributed, high-performance, associative database
|
* `quasardb <https://www.quasardb.net/>`_: A distributed, high-performance,
|
||||||
|
associative database
|
||||||
|
|
||||||
* `readpe <https://bitbucket.org/sys_dev/readpe>`_: Read Portable Executable
|
* `readpe <https://bitbucket.org/sys_dev/readpe>`_: Read Portable Executable
|
||||||
|
|
||||||
@ -308,11 +334,12 @@ further details see the `source
|
|||||||
================= ============= ===========
|
================= ============= ===========
|
||||||
Library Method Run Time, s
|
Library Method Run Time, s
|
||||||
================= ============= ===========
|
================= ============= ===========
|
||||||
EGLIBC 2.19 printf 1.30
|
libc printf 1.35
|
||||||
libstdc++ 4.8.2 std::ostream 1.85
|
libc++ std::ostream 3.42
|
||||||
fmt 1.0 fmt::print 1.42
|
fmt 534bff7 fmt::print 1.56
|
||||||
tinyformat 2.0.1 tfm::printf 2.25
|
tinyformat 2.0.1 tfm::printf 3.73
|
||||||
Boost Format 1.54 boost::format 9.94
|
Boost Format 1.54 boost::format 8.44
|
||||||
|
Folly Format folly::format 2.54
|
||||||
================= ============= ===========
|
================= ============= ===========
|
||||||
|
|
||||||
As you can see ``boost::format`` is much slower than the alternative methods; this
|
As you can see ``boost::format`` is much slower than the alternative methods; this
|
||||||
@ -332,38 +359,45 @@ from `format-benchmark <https://github.com/fmtlib/format-benchmark>`_
|
|||||||
tests compile time and code bloat for nontrivial projects.
|
tests compile time and code bloat for nontrivial projects.
|
||||||
It generates 100 translation units and uses ``printf()`` or its alternative
|
It generates 100 translation units and uses ``printf()`` or its alternative
|
||||||
five times in each to simulate a medium sized project. The resulting
|
five times in each to simulate a medium sized project. The resulting
|
||||||
executable size and compile time (g++-4.8.1, Ubuntu GNU/Linux 13.10,
|
executable size and compile time (Apple LLVM version 8.1.0 (clang-802.0.42),
|
||||||
best of three) is shown in the following tables.
|
macOS Sierra, best of three) is shown in the following tables.
|
||||||
|
|
||||||
**Optimized build (-O3)**
|
**Optimized build (-O3)**
|
||||||
|
|
||||||
============ =============== ==================== ==================
|
============= =============== ==================== ==================
|
||||||
Method Compile Time, s Executable size, KiB Stripped size, KiB
|
Method Compile Time, s Executable size, KiB Stripped size, KiB
|
||||||
============ =============== ==================== ==================
|
============= =============== ==================== ==================
|
||||||
printf 2.6 41 30
|
printf 2.7 29 26
|
||||||
IOStreams 19.4 92 70
|
printf+string 18.4 29 26
|
||||||
fmt 46.8 46 34
|
IOStreams 34.6 59 55
|
||||||
tinyformat 64.6 418 386
|
fmt 22.0 37 34
|
||||||
Boost Format 222.8 990 923
|
tinyformat 51.8 103 97
|
||||||
============ =============== ==================== ==================
|
Boost Format 120.5 762 739
|
||||||
|
Folly Format 158.7 102 87
|
||||||
|
============= =============== ==================== ==================
|
||||||
|
|
||||||
As you can see, fmt has two times less overhead in terms of resulting
|
As you can see, fmt has 60% less overhead in terms of resulting binary code
|
||||||
code size compared to IOStreams and comes pretty close to ``printf``.
|
size compared to IOStreams and comes pretty close to ``printf``. Boost Format
|
||||||
Boost Format has by far the largest overheads.
|
has by far the largest overheads.
|
||||||
|
|
||||||
|
``printf+string`` is the same as ``printf`` but with extra ``<string>``
|
||||||
|
include to measure the overhead of the latter.
|
||||||
|
|
||||||
**Non-optimized build**
|
**Non-optimized build**
|
||||||
|
|
||||||
============ =============== ==================== ==================
|
============= =============== ==================== ==================
|
||||||
Method Compile Time, s Executable size, KiB Stripped size, KiB
|
Method Compile Time, s Executable size, KiB Stripped size, KiB
|
||||||
============ =============== ==================== ==================
|
============= =============== ==================== ==================
|
||||||
printf 2.1 41 30
|
printf 2.4 33 30
|
||||||
IOStreams 19.7 86 62
|
printf+string 18.5 33 30
|
||||||
fmt 47.9 108 86
|
IOStreams 31.9 56 52
|
||||||
tinyformat 27.7 234 190
|
fmt 20.9 56 51
|
||||||
Boost Format 122.6 884 763
|
tinyformat 38.9 88 82
|
||||||
============ =============== ==================== ==================
|
Boost Format 64.8 366 304
|
||||||
|
Folly Format 113.5 442 428
|
||||||
|
============= =============== ==================== ==================
|
||||||
|
|
||||||
``libc``, ``libstdc++`` and ``libfmt`` are all linked as shared
|
``libc``, ``lib(std)c++`` and ``libfmt`` are all linked as shared
|
||||||
libraries to compare formatting function overhead only. Boost Format
|
libraries to compare formatting function overhead only. Boost Format
|
||||||
and tinyformat are header-only libraries so they don't provide any
|
and tinyformat are header-only libraries so they don't provide any
|
||||||
linkage options.
|
linkage options.
|
||||||
@ -412,10 +446,13 @@ It only applies if you distribute the documentation of fmt.
|
|||||||
Acknowledgments
|
Acknowledgments
|
||||||
---------------
|
---------------
|
||||||
|
|
||||||
The fmt library is maintained by Victor Zverovich (`vitaut <https://github.com/vitaut>`_)
|
The fmt library is maintained by Victor Zverovich (`vitaut
|
||||||
and Jonathan Müller (`foonathan <https://github.com/foonathan>`_) with contributions from many
|
<https://github.com/vitaut>`_) and Jonathan Müller (`foonathan
|
||||||
other people. See `Contributors <https://github.com/fmtlib/fmt/graphs/contributors>`_ and `Releases <https://github.com/fmtlib/fmt/releases>`_ for some of the names. Let us know if your contribution
|
<https://github.com/foonathan>`_) with contributions from many other people.
|
||||||
is not listed or mentioned incorrectly and we'll make it right.
|
See `Contributors <https://github.com/fmtlib/fmt/graphs/contributors>`_ and
|
||||||
|
`Releases <https://github.com/fmtlib/fmt/releases>`_ for some of the names.
|
||||||
|
Let us know if your contribution is not listed or mentioned incorrectly and
|
||||||
|
we'll make it right.
|
||||||
|
|
||||||
The benchmark section of this readme file and the performance tests are taken
|
The benchmark section of this readme file and the performance tests are taken
|
||||||
from the excellent `tinyformat <https://github.com/c42f/tinyformat>`_ library
|
from the excellent `tinyformat <https://github.com/c42f/tinyformat>`_ library
|
||||||
|
@ -6,7 +6,7 @@ endif ()
|
|||||||
|
|
||||||
add_custom_target(doc
|
add_custom_target(doc
|
||||||
COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/build.py ${FMT_VERSION}
|
COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/build.py ${FMT_VERSION}
|
||||||
SOURCES build.py conf.py _templates/layout.html)
|
SOURCES api.rst syntax.rst build.py conf.py _templates/layout.html)
|
||||||
|
|
||||||
install(DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/html/
|
install(DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/html/
|
||||||
DESTINATION share/doc/fmt OPTIONAL)
|
DESTINATION share/doc/fmt OPTIONAL)
|
||||||
|
76
doc/api.rst
76
doc/api.rst
@ -61,58 +61,35 @@ The format string syntax is described in the documentation of
|
|||||||
Formatting user-defined types
|
Formatting user-defined types
|
||||||
-----------------------------
|
-----------------------------
|
||||||
|
|
||||||
A custom ``format_arg`` function may be implemented and used to format any
|
To make a user-defined type formattable, specialize the ``formatter<T>`` struct
|
||||||
user-defined type. That is how date and time formatting described in the
|
template and implement ``parse`` and ``format`` methods::
|
||||||
previous section is implemented in :file:`fmt/time.h`. The following example
|
|
||||||
shows how to implement custom formatting for a user-defined structure.
|
|
||||||
|
|
||||||
::
|
struct MyStruct { double x, y; };
|
||||||
|
|
||||||
struct MyStruct { double a, b; };
|
namespace fmt {
|
||||||
|
template <>
|
||||||
|
struct formatter<MyStruct> {
|
||||||
|
template <typename ParseContext>
|
||||||
|
auto parse(ParseContext &ctx) { return ctx.begin(); }
|
||||||
|
|
||||||
void format_arg(fmt::BasicFormatter<char> &f,
|
template <typename FormatContext>
|
||||||
const char *&format_str, const MyStruct &s) {
|
auto format(const MyStruct &s, FormatContext &ctx) {
|
||||||
f.writer().write("[MyStruct: a={:.1f}, b={:.2f}]", s.a, s.b);
|
fmt::format_to(ctx.begin(), "[MyStruct: x={:.1f}, y={:.2f}]", s.x, s.y);
|
||||||
}
|
|
||||||
|
|
||||||
MyStruct m = { 1, 2 };
|
|
||||||
std::string s = fmt::format("m={}", n);
|
|
||||||
// s == "m=[MyStruct: a=1.0, b=2.00]"
|
|
||||||
|
|
||||||
Note in the example above the ``format_arg`` function ignores the contents of
|
|
||||||
``format_str`` so the type will always be formatted as specified. See
|
|
||||||
``format_arg`` in :file:`fmt/time.h` for an advanced example of how to use
|
|
||||||
the ``format_str`` argument to customize the formatted output.
|
|
||||||
|
|
||||||
This technique can also be used for formatting class hierarchies::
|
|
||||||
|
|
||||||
namespace local {
|
|
||||||
struct Parent {
|
|
||||||
Parent(int p) : p(p) {}
|
|
||||||
virtual void write(fmt::Writer &w) const {
|
|
||||||
w.write("Parent : p={}", p);
|
|
||||||
}
|
}
|
||||||
int p;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Child : Parent {
|
|
||||||
Child(int c, int p) : Parent(p), c(c) {}
|
|
||||||
virtual void write(fmt::Writer &w) const {
|
|
||||||
w.write("Child c={} : ", c);
|
|
||||||
Parent::write(w);
|
|
||||||
}
|
|
||||||
int c;
|
|
||||||
};
|
|
||||||
|
|
||||||
void format_arg(fmt::BasicFormatter<char> &f,
|
|
||||||
const char *&format_str, const Parent &p) {
|
|
||||||
p.write(f.writer());
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
Local::Child c(1,2);
|
Then you can pass objects of type ``MyStruct`` to any formatting function::
|
||||||
Local::Parent &p = c;
|
|
||||||
fmt::print("via ref to base: {}\n", p);
|
MyStruct m = {1, 2};
|
||||||
fmt::print("direct to child: {}\n", c);
|
std::string s = fmt::format("m={}", m);
|
||||||
|
// s == "m=[MyStruct: x=1.0, y=2.00]"
|
||||||
|
|
||||||
|
In the example above the ``formatter<MyStruct>::parse`` function ignores the
|
||||||
|
contents of the format string referred to by ``ctx.begin()`` so the object will
|
||||||
|
always be formatted as specified. See ``formatter<tm>::parse`` in
|
||||||
|
:file:`fmt/time.h` for an advanced example of how to parse the format string and
|
||||||
|
customize the formatted output.
|
||||||
|
|
||||||
This section shows how to define a custom format function for a user-defined
|
This section shows how to define a custom format function for a user-defined
|
||||||
type. The next section describes how to get ``fmt`` to use a conventional stream
|
type. The next section describes how to get ``fmt`` to use a conventional stream
|
||||||
@ -151,7 +128,7 @@ custom argument formatter class::
|
|||||||
// with the ``x`` format specifier.
|
// with the ``x`` format specifier.
|
||||||
class CustomArgFormatter :
|
class CustomArgFormatter :
|
||||||
public fmt::BasicArgFormatter<CustomArgFormatter, char> {
|
public fmt::BasicArgFormatter<CustomArgFormatter, char> {
|
||||||
public:
|
public:
|
||||||
CustomArgFormatter(fmt::BasicFormatter<char, CustomArgFormatter> &f,
|
CustomArgFormatter(fmt::BasicFormatter<char, CustomArgFormatter> &f,
|
||||||
fmt::FormatSpec &s, const char *fmt)
|
fmt::FormatSpec &s, const char *fmt)
|
||||||
: fmt::BasicArgFormatter<CustomArgFormatter, char>(f, s, fmt) {}
|
: fmt::BasicArgFormatter<CustomArgFormatter, char>(f, s, fmt) {}
|
||||||
@ -233,9 +210,6 @@ store output elsewhere by subclassing `~fmt::BasicWriter`.
|
|||||||
.. doxygenclass:: fmt::BasicStringWriter
|
.. doxygenclass:: fmt::BasicStringWriter
|
||||||
:members:
|
:members:
|
||||||
|
|
||||||
.. doxygenclass:: fmt::BasicContainerWriter
|
|
||||||
:members:
|
|
||||||
|
|
||||||
.. doxygenfunction:: bin(int)
|
.. doxygenfunction:: bin(int)
|
||||||
|
|
||||||
.. doxygenfunction:: oct(int)
|
.. doxygenfunction:: oct(int)
|
||||||
@ -262,8 +236,6 @@ Utilities
|
|||||||
|
|
||||||
.. doxygenfunction:: fmt::to_string(const T&)
|
.. doxygenfunction:: fmt::to_string(const T&)
|
||||||
|
|
||||||
.. doxygenfunction:: fmt::to_wstring(const T&)
|
|
||||||
|
|
||||||
.. doxygenclass:: fmt::BasicStringRef
|
.. doxygenclass:: fmt::BasicStringRef
|
||||||
:members:
|
:members:
|
||||||
|
|
||||||
|
@ -4,8 +4,9 @@
|
|||||||
Format String Syntax
|
Format String Syntax
|
||||||
********************
|
********************
|
||||||
|
|
||||||
Formatting functions such as :ref:`fmt::format() <format>` and :ref:`fmt::print() <print>`
|
Formatting functions such as :ref:`fmt::format() <format>` and
|
||||||
use the same format string syntax described in this section.
|
:ref:`fmt::print() <print>` use the same format string syntax described in this
|
||||||
|
section.
|
||||||
|
|
||||||
Format strings contain "replacement fields" surrounded by curly braces ``{}``.
|
Format strings contain "replacement fields" surrounded by curly braces ``{}``.
|
||||||
Anything that is not contained in braces is considered literal text, which is
|
Anything that is not contained in braces is considered literal text, which is
|
||||||
@ -53,8 +54,8 @@ described in the next section.
|
|||||||
|
|
||||||
A *format_spec* field can also include nested replacement fields in certain
|
A *format_spec* field can also include nested replacement fields in certain
|
||||||
positions within it. These nested replacement fields can contain only an
|
positions within it. These nested replacement fields can contain only an
|
||||||
argument id; format specifications are not allowed. This allows the
|
argument id; format specifications are not allowed. This allows the formatting
|
||||||
formatting of a value to be dynamically specified.
|
of a value to be dynamically specified.
|
||||||
|
|
||||||
See the :ref:`formatexamples` section for some examples.
|
See the :ref:`formatexamples` section for some examples.
|
||||||
|
|
||||||
@ -362,7 +363,7 @@ Replacing ``%x`` and ``%o`` and converting the value to different bases::
|
|||||||
// Result: "int: 42; hex: 2a; oct: 52; bin: 101010"
|
// Result: "int: 42; hex: 2a; oct: 52; bin: 101010"
|
||||||
// with 0x or 0 or 0b as prefix:
|
// with 0x or 0 or 0b as prefix:
|
||||||
format("int: {0:d}; hex: {0:#x}; oct: {0:#o}; bin: {0:#b}", 42);
|
format("int: {0:d}; hex: {0:#x}; oct: {0:#o}; bin: {0:#b}", 42);
|
||||||
// Result: "int: 42; hex: 0x2a; oct: 052; bin: 0b101010"
|
// Result: "int: 42; hex: 0x2a; oct: 052; bin: 0b101010"
|
||||||
|
|
||||||
.. ifconfig:: False
|
.. ifconfig:: False
|
||||||
|
|
||||||
|
@ -61,19 +61,21 @@ __ http://en.wikipedia.org/wiki/Library_%28computing%29#Shared_libraries
|
|||||||
Header-only usage with CMake
|
Header-only usage with CMake
|
||||||
============================
|
============================
|
||||||
|
|
||||||
In order to add ``fmtlib`` into an existing ``CMakeLists.txt`` file, you can add the ``fmt`` library directory into your main project, which will enable the ``fmt`` library::
|
You can add the ``fmt`` library directory into your project and include it in
|
||||||
|
your ``CMakeLists.txt`` file::
|
||||||
|
|
||||||
add_subdirectory(fmt)
|
add_subdirectory(fmt)
|
||||||
|
|
||||||
If you have a project called ``foo`` that you would like to link against the fmt library in a header-only fashion, you can enable with with::
|
|
||||||
|
|
||||||
target_link_libraries(foo PRIVATE fmt::fmt-header-only)
|
or
|
||||||
|
::
|
||||||
And then to ensure that the ``fmt`` library does not always get built, you can modify the call to ``add_subdirectory`` to read ::
|
|
||||||
|
|
||||||
add_subdirectory(fmt EXCLUDE_FROM_ALL)
|
add_subdirectory(fmt EXCLUDE_FROM_ALL)
|
||||||
|
|
||||||
This will ensure that the ``fmt`` library is exluded from calls to ``make``, ``make all``, or ``cmake --build .``.
|
to exclude it from ``make``, ``make all``, or ``cmake --build .``.
|
||||||
|
|
||||||
|
Settting up your target to use a header-only version of ``fmt`` is equaly easy::
|
||||||
|
|
||||||
|
target_link_libraries(<your-target> PRIVATE fmt-header-only)
|
||||||
|
|
||||||
Building the documentation
|
Building the documentation
|
||||||
==========================
|
==========================
|
||||||
|
@ -1,95 +0,0 @@
|
|||||||
# Define the fmt library, its includes and the needed defines.
|
|
||||||
# *.cc are added to FMT_HEADERS for the header-only configuration.
|
|
||||||
set(FMT_HEADERS container.h format.h format.cc ostream.h ostream.cc printf.h
|
|
||||||
printf.cc string.h time.h)
|
|
||||||
if (HAVE_OPEN)
|
|
||||||
set(FMT_HEADERS ${FMT_HEADERS} posix.h)
|
|
||||||
set(FMT_SOURCES ${FMT_SOURCES} posix.cc)
|
|
||||||
endif ()
|
|
||||||
|
|
||||||
add_library(fmt ${FMT_SOURCES} ${FMT_HEADERS} ../README.rst ../ChangeLog.rst)
|
|
||||||
add_library(fmt::fmt ALIAS fmt)
|
|
||||||
|
|
||||||
# Starting with cmake 3.1 the CXX_STANDARD property can be used instead.
|
|
||||||
# Note: Don't make -std=c++11 public or interface, since it breaks projects
|
|
||||||
# that use C++14.
|
|
||||||
target_compile_options(fmt PRIVATE ${CPP11_FLAG})
|
|
||||||
if (FMT_PEDANTIC)
|
|
||||||
target_compile_options(fmt PRIVATE ${PEDANTIC_COMPILE_FLAGS})
|
|
||||||
endif ()
|
|
||||||
|
|
||||||
target_include_directories(fmt PUBLIC
|
|
||||||
$<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}>
|
|
||||||
$<INSTALL_INTERFACE:include>)
|
|
||||||
|
|
||||||
set_target_properties(fmt PROPERTIES
|
|
||||||
VERSION ${FMT_VERSION} SOVERSION ${CPACK_PACKAGE_VERSION_MAJOR})
|
|
||||||
set_target_properties(fmt PROPERTIES DEBUG_POSTFIX d)
|
|
||||||
|
|
||||||
if (BUILD_SHARED_LIBS)
|
|
||||||
if (UNIX AND NOT APPLE)
|
|
||||||
# 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 ()
|
|
||||||
|
|
||||||
#------------------------------------------------------------------------------
|
|
||||||
# additionally define a header only library when cmake is new enough
|
|
||||||
if (CMAKE_VERSION VERSION_GREATER 3.1.0 OR CMAKE_VERSION VERSION_EQUAL 3.1.0)
|
|
||||||
add_library(fmt-header-only INTERFACE)
|
|
||||||
add_library(fmt::fmt-header-only ALIAS fmt-header-only)
|
|
||||||
|
|
||||||
target_compile_definitions(fmt-header-only INTERFACE FMT_HEADER_ONLY=1)
|
|
||||||
|
|
||||||
target_include_directories(fmt-header-only INTERFACE
|
|
||||||
$<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}>
|
|
||||||
$<INSTALL_INTERFACE:include>)
|
|
||||||
endif ()
|
|
||||||
|
|
||||||
# Install targets.
|
|
||||||
if (FMT_INSTALL)
|
|
||||||
include(GNUInstallDirs)
|
|
||||||
include(CMakePackageConfigHelpers)
|
|
||||||
set(FMT_CMAKE_DIR ${CMAKE_INSTALL_LIBDIR}/cmake/fmt CACHE STRING
|
|
||||||
"Installation directory for cmake files, relative to ${CMAKE_INSTALL_PREFIX}.")
|
|
||||||
set(version_config ${PROJECT_BINARY_DIR}/fmt-config-version.cmake)
|
|
||||||
set(project_config ${PROJECT_BINARY_DIR}/fmt-config.cmake)
|
|
||||||
set(targets_export_name fmt-targets)
|
|
||||||
|
|
||||||
set (INSTALL_TARGETS fmt)
|
|
||||||
if (TARGET fmt-header-only)
|
|
||||||
set(INSTALL_TARGETS ${INSTALL_TARGETS} fmt-header-only)
|
|
||||||
endif ()
|
|
||||||
|
|
||||||
set(FMT_LIB_DIR ${CMAKE_INSTALL_LIBDIR} CACHE STRING
|
|
||||||
"Installation directory for libraries, relative to ${CMAKE_INSTALL_PREFIX}.")
|
|
||||||
|
|
||||||
set(FMT_INC_DIR ${CMAKE_INSTALL_INCLUDEDIR}/fmt CACHE STRING
|
|
||||||
"Installation directory for include files, relative to ${CMAKE_INSTALL_PREFIX}.")
|
|
||||||
|
|
||||||
# Generate the version, config and target files into the build directory.
|
|
||||||
write_basic_package_version_file(
|
|
||||||
${version_config}
|
|
||||||
VERSION ${FMT_VERSION}
|
|
||||||
COMPATIBILITY AnyNewerVersion)
|
|
||||||
configure_package_config_file(
|
|
||||||
${PROJECT_SOURCE_DIR}/support/cmake/fmt-config.cmake.in
|
|
||||||
${project_config}
|
|
||||||
INSTALL_DESTINATION ${FMT_CMAKE_DIR})
|
|
||||||
export(TARGETS ${INSTALL_TARGETS} NAMESPACE fmt::
|
|
||||||
FILE ${PROJECT_BINARY_DIR}/${targets_export_name}.cmake)
|
|
||||||
|
|
||||||
# Install version, config and target files.
|
|
||||||
install(
|
|
||||||
FILES ${project_config} ${version_config}
|
|
||||||
DESTINATION ${FMT_CMAKE_DIR})
|
|
||||||
install(EXPORT ${targets_export_name} DESTINATION ${FMT_CMAKE_DIR}
|
|
||||||
NAMESPACE fmt::)
|
|
||||||
|
|
||||||
# Install the library and headers.
|
|
||||||
install(TARGETS ${INSTALL_TARGETS} EXPORT ${targets_export_name}
|
|
||||||
DESTINATION ${FMT_LIB_DIR})
|
|
||||||
install(FILES ${FMT_HEADERS} DESTINATION ${FMT_INC_DIR})
|
|
||||||
endif ()
|
|
@ -1,82 +0,0 @@
|
|||||||
/*
|
|
||||||
Formatting library for C++ - standard container utilities
|
|
||||||
|
|
||||||
Copyright (c) 2012 - 2016, Victor Zverovich
|
|
||||||
All rights reserved.
|
|
||||||
|
|
||||||
For the license information refer to format.h.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef FMT_CONTAINER_H_
|
|
||||||
#define FMT_CONTAINER_H_
|
|
||||||
|
|
||||||
#include "format.h"
|
|
||||||
|
|
||||||
namespace fmt {
|
|
||||||
|
|
||||||
namespace internal {
|
|
||||||
|
|
||||||
/**
|
|
||||||
\rst
|
|
||||||
A "buffer" that appends data to a standard container (e.g. typically a
|
|
||||||
``std::vector`` or ``std::basic_string``).
|
|
||||||
\endrst
|
|
||||||
*/
|
|
||||||
template <typename Container>
|
|
||||||
class ContainerBuffer : public Buffer<typename Container::value_type> {
|
|
||||||
private:
|
|
||||||
Container& container_;
|
|
||||||
|
|
||||||
protected:
|
|
||||||
virtual void grow(std::size_t size) FMT_OVERRIDE {
|
|
||||||
container_.resize(size);
|
|
||||||
this->ptr_ = &container_[0];
|
|
||||||
this->capacity_ = size;
|
|
||||||
}
|
|
||||||
|
|
||||||
public:
|
|
||||||
explicit ContainerBuffer(Container& container) : container_(container) {
|
|
||||||
this->size_ = container_.size();
|
|
||||||
if (this->size_ > 0) {
|
|
||||||
this->ptr_ = &container_[0];
|
|
||||||
this->capacity_ = this->size_;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
} // namespace internal
|
|
||||||
|
|
||||||
/**
|
|
||||||
\rst
|
|
||||||
This class template provides operations for formatting and appending data
|
|
||||||
to a standard *container* like ``std::vector`` or ``std::basic_string``.
|
|
||||||
|
|
||||||
**Example**::
|
|
||||||
|
|
||||||
void vecformat(std::vector<char>& dest, fmt::BasicCStringRef<char> format,
|
|
||||||
fmt::ArgList args) {
|
|
||||||
fmt::BasicContainerWriter<std::vector<char> > appender(dest);
|
|
||||||
appender.write(format, args);
|
|
||||||
}
|
|
||||||
FMT_VARIADIC(void, vecformat, std::vector<char>&,
|
|
||||||
fmt::BasicCStringRef<char>);
|
|
||||||
\endrst
|
|
||||||
*/
|
|
||||||
template <class Container>
|
|
||||||
class BasicContainerWriter
|
|
||||||
: public BasicWriter<typename Container::value_type> {
|
|
||||||
private:
|
|
||||||
internal::ContainerBuffer<Container> buffer_;
|
|
||||||
|
|
||||||
public:
|
|
||||||
/**
|
|
||||||
\rst
|
|
||||||
Constructs a :class:`fmt::BasicContainerWriter` object.
|
|
||||||
\endrst
|
|
||||||
*/
|
|
||||||
explicit BasicContainerWriter(Container& dest)
|
|
||||||
: BasicWriter<typename Container::value_type>(buffer_), buffer_(dest) {}
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace fmt
|
|
||||||
|
|
||||||
#endif // FMT_CONTAINER_H_
|
|
4215
fmt/format.h
4215
fmt/format.h
File diff suppressed because it is too large
Load Diff
@ -1,35 +0,0 @@
|
|||||||
/*
|
|
||||||
Formatting library for C++ - std::ostream support
|
|
||||||
|
|
||||||
Copyright (c) 2012 - 2016, Victor Zverovich
|
|
||||||
All rights reserved.
|
|
||||||
|
|
||||||
For the license information refer to format.h.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "ostream.h"
|
|
||||||
|
|
||||||
namespace fmt {
|
|
||||||
|
|
||||||
namespace internal {
|
|
||||||
FMT_FUNC void write(std::ostream &os, Writer &w) {
|
|
||||||
const char *data = w.data();
|
|
||||||
typedef internal::MakeUnsigned<std::streamsize>::Type UnsignedStreamSize;
|
|
||||||
UnsignedStreamSize size = w.size();
|
|
||||||
UnsignedStreamSize max_size =
|
|
||||||
internal::to_unsigned((std::numeric_limits<std::streamsize>::max)());
|
|
||||||
do {
|
|
||||||
UnsignedStreamSize n = size <= max_size ? size : max_size;
|
|
||||||
os.write(data, static_cast<std::streamsize>(n));
|
|
||||||
data += n;
|
|
||||||
size -= n;
|
|
||||||
} while (size != 0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
FMT_FUNC void print(std::ostream &os, CStringRef format_str, ArgList args) {
|
|
||||||
MemoryWriter w;
|
|
||||||
w.write(format_str, args);
|
|
||||||
internal::write(os, w);
|
|
||||||
}
|
|
||||||
} // namespace fmt
|
|
108
fmt/ostream.h
108
fmt/ostream.h
@ -1,108 +0,0 @@
|
|||||||
/*
|
|
||||||
Formatting library for C++ - std::ostream support
|
|
||||||
|
|
||||||
Copyright (c) 2012 - 2016, Victor Zverovich
|
|
||||||
All rights reserved.
|
|
||||||
|
|
||||||
For the license information refer to format.h.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef FMT_OSTREAM_H_
|
|
||||||
#define FMT_OSTREAM_H_
|
|
||||||
|
|
||||||
#include "format.h"
|
|
||||||
#include <ostream>
|
|
||||||
|
|
||||||
namespace fmt {
|
|
||||||
|
|
||||||
namespace internal {
|
|
||||||
|
|
||||||
template <class Char>
|
|
||||||
class FormatBuf : public std::basic_streambuf<Char> {
|
|
||||||
private:
|
|
||||||
typedef typename std::basic_streambuf<Char>::int_type int_type;
|
|
||||||
typedef typename std::basic_streambuf<Char>::traits_type traits_type;
|
|
||||||
|
|
||||||
Buffer<Char> &buffer_;
|
|
||||||
|
|
||||||
public:
|
|
||||||
FormatBuf(Buffer<Char> &buffer) : buffer_(buffer) {}
|
|
||||||
|
|
||||||
protected:
|
|
||||||
// The put-area is actually always empty. This makes the implementation
|
|
||||||
// simpler and has the advantage that the streambuf and the buffer are always
|
|
||||||
// in sync and sputc never writes into uninitialized memory. The obvious
|
|
||||||
// disadvantage is that each call to sputc always results in a (virtual) call
|
|
||||||
// to overflow. There is no disadvantage here for sputn since this always
|
|
||||||
// results in a call to xsputn.
|
|
||||||
|
|
||||||
int_type overflow(int_type ch = traits_type::eof()) FMT_OVERRIDE {
|
|
||||||
if (!traits_type::eq_int_type(ch, traits_type::eof()))
|
|
||||||
buffer_.push_back(static_cast<Char>(ch));
|
|
||||||
return ch;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::streamsize xsputn(const Char *s, std::streamsize count) FMT_OVERRIDE {
|
|
||||||
buffer_.append(s, s + count);
|
|
||||||
return count;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
Yes &convert(std::ostream &);
|
|
||||||
|
|
||||||
struct DummyStream : std::ostream {
|
|
||||||
DummyStream(); // Suppress a bogus warning in MSVC.
|
|
||||||
|
|
||||||
// Hide all operator<< overloads from std::ostream.
|
|
||||||
template <typename T>
|
|
||||||
typename EnableIf<sizeof(T) == 0>::type operator<<(const T &);
|
|
||||||
};
|
|
||||||
|
|
||||||
No &operator<<(std::ostream &, int);
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
struct ConvertToIntImpl<T, true> {
|
|
||||||
// Convert to int only if T doesn't have an overloaded operator<<.
|
|
||||||
enum {
|
|
||||||
value = sizeof(convert(get<DummyStream>() << get<T>())) == sizeof(No)
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
// Write the content of w to os.
|
|
||||||
FMT_API void write(std::ostream &os, Writer &w);
|
|
||||||
} // namespace internal
|
|
||||||
|
|
||||||
// Formats a value.
|
|
||||||
template <typename Char, typename ArgFormatter_, typename T>
|
|
||||||
void format_arg(BasicFormatter<Char, ArgFormatter_> &f,
|
|
||||||
const Char *&format_str, const T &value) {
|
|
||||||
internal::MemoryBuffer<Char, internal::INLINE_BUFFER_SIZE> buffer;
|
|
||||||
|
|
||||||
internal::FormatBuf<Char> format_buf(buffer);
|
|
||||||
std::basic_ostream<Char> output(&format_buf);
|
|
||||||
output.exceptions(std::ios_base::failbit | std::ios_base::badbit);
|
|
||||||
output << value;
|
|
||||||
|
|
||||||
BasicStringRef<Char> str(&buffer[0], buffer.size());
|
|
||||||
typedef internal::MakeArg< BasicFormatter<Char> > MakeArg;
|
|
||||||
format_str = f.format(format_str, MakeArg(str));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
\rst
|
|
||||||
Prints formatted data to the stream *os*.
|
|
||||||
|
|
||||||
**Example**::
|
|
||||||
|
|
||||||
print(cerr, "Don't {}!", "panic");
|
|
||||||
\endrst
|
|
||||||
*/
|
|
||||||
FMT_API void print(std::ostream &os, CStringRef format_str, ArgList args);
|
|
||||||
FMT_VARIADIC(void, print, std::ostream &, CStringRef)
|
|
||||||
} // namespace fmt
|
|
||||||
|
|
||||||
#ifdef FMT_HEADER_ONLY
|
|
||||||
# include "ostream.cc"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#endif // FMT_OSTREAM_H_
|
|
@ -1,32 +0,0 @@
|
|||||||
/*
|
|
||||||
Formatting library for C++
|
|
||||||
|
|
||||||
Copyright (c) 2012 - 2016, Victor Zverovich
|
|
||||||
All rights reserved.
|
|
||||||
|
|
||||||
For the license information refer to format.h.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "format.h"
|
|
||||||
#include "printf.h"
|
|
||||||
|
|
||||||
namespace fmt {
|
|
||||||
|
|
||||||
template <typename Char>
|
|
||||||
void printf(BasicWriter<Char> &w, BasicCStringRef<Char> format, ArgList args);
|
|
||||||
|
|
||||||
FMT_FUNC int fprintf(std::FILE *f, CStringRef format, ArgList args) {
|
|
||||||
MemoryWriter w;
|
|
||||||
printf(w, format, args);
|
|
||||||
std::size_t size = w.size();
|
|
||||||
return std::fwrite(w.data(), 1, size, f) < size ? -1 : static_cast<int>(size);
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifndef FMT_HEADER_ONLY
|
|
||||||
|
|
||||||
template void PrintfFormatter<char>::format(CStringRef format);
|
|
||||||
template void PrintfFormatter<wchar_t>::format(WCStringRef format);
|
|
||||||
|
|
||||||
#endif // FMT_HEADER_ONLY
|
|
||||||
|
|
||||||
} // namespace fmt
|
|
603
fmt/printf.h
603
fmt/printf.h
@ -1,603 +0,0 @@
|
|||||||
/*
|
|
||||||
Formatting library for C++
|
|
||||||
|
|
||||||
Copyright (c) 2012 - 2016, Victor Zverovich
|
|
||||||
All rights reserved.
|
|
||||||
|
|
||||||
For the license information refer to format.h.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef FMT_PRINTF_H_
|
|
||||||
#define FMT_PRINTF_H_
|
|
||||||
|
|
||||||
#include <algorithm> // std::fill_n
|
|
||||||
#include <limits> // std::numeric_limits
|
|
||||||
|
|
||||||
#include "ostream.h"
|
|
||||||
|
|
||||||
namespace fmt {
|
|
||||||
namespace internal {
|
|
||||||
|
|
||||||
// Checks if a value fits in int - used to avoid warnings about comparing
|
|
||||||
// signed and unsigned integers.
|
|
||||||
template <bool IsSigned>
|
|
||||||
struct IntChecker {
|
|
||||||
template <typename T>
|
|
||||||
static bool fits_in_int(T value) {
|
|
||||||
unsigned max = std::numeric_limits<int>::max();
|
|
||||||
return value <= max;
|
|
||||||
}
|
|
||||||
static bool fits_in_int(bool) { return true; }
|
|
||||||
};
|
|
||||||
|
|
||||||
template <>
|
|
||||||
struct IntChecker<true> {
|
|
||||||
template <typename T>
|
|
||||||
static bool fits_in_int(T value) {
|
|
||||||
return value >= std::numeric_limits<int>::min() &&
|
|
||||||
value <= std::numeric_limits<int>::max();
|
|
||||||
}
|
|
||||||
static bool fits_in_int(int) { return true; }
|
|
||||||
};
|
|
||||||
|
|
||||||
class PrecisionHandler : public ArgVisitor<PrecisionHandler, int> {
|
|
||||||
public:
|
|
||||||
void report_unhandled_arg() {
|
|
||||||
FMT_THROW(FormatError("precision is not integer"));
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
int visit_any_int(T value) {
|
|
||||||
if (!IntChecker<std::numeric_limits<T>::is_signed>::fits_in_int(value))
|
|
||||||
FMT_THROW(FormatError("number is too big"));
|
|
||||||
return static_cast<int>(value);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// IsZeroInt::visit(arg) returns true iff arg is a zero integer.
|
|
||||||
class IsZeroInt : public ArgVisitor<IsZeroInt, bool> {
|
|
||||||
public:
|
|
||||||
template <typename T>
|
|
||||||
bool visit_any_int(T value) { return value == 0; }
|
|
||||||
};
|
|
||||||
|
|
||||||
// returns the default type for format specific "%s"
|
|
||||||
class DefaultType : public ArgVisitor<DefaultType, char> {
|
|
||||||
public:
|
|
||||||
char visit_char(int) { return 'c'; }
|
|
||||||
|
|
||||||
char visit_bool(bool) { return 's'; }
|
|
||||||
|
|
||||||
char visit_pointer(const void *) { return 'p'; }
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
char visit_any_int(T) { return 'd'; }
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
char visit_any_double(T) { return 'g'; }
|
|
||||||
|
|
||||||
char visit_unhandled_arg() { return 's'; }
|
|
||||||
};
|
|
||||||
|
|
||||||
template <typename T, typename U>
|
|
||||||
struct is_same {
|
|
||||||
enum { value = 0 };
|
|
||||||
};
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
struct is_same<T, T> {
|
|
||||||
enum { value = 1 };
|
|
||||||
};
|
|
||||||
|
|
||||||
// An argument visitor that converts an integer argument to T for printf,
|
|
||||||
// if T is an integral type. If T is void, the argument is converted to
|
|
||||||
// corresponding signed or unsigned type depending on the type specifier:
|
|
||||||
// 'd' and 'i' - signed, other - unsigned)
|
|
||||||
template <typename T = void>
|
|
||||||
class ArgConverter : public ArgVisitor<ArgConverter<T>, void> {
|
|
||||||
private:
|
|
||||||
internal::Arg &arg_;
|
|
||||||
wchar_t type_;
|
|
||||||
|
|
||||||
FMT_DISALLOW_COPY_AND_ASSIGN(ArgConverter);
|
|
||||||
|
|
||||||
public:
|
|
||||||
ArgConverter(internal::Arg &arg, wchar_t type)
|
|
||||||
: arg_(arg), type_(type) {}
|
|
||||||
|
|
||||||
void visit_bool(bool value) {
|
|
||||||
if (type_ != 's')
|
|
||||||
visit_any_int(value);
|
|
||||||
}
|
|
||||||
|
|
||||||
void visit_char(int value) {
|
|
||||||
if (type_ != 's')
|
|
||||||
visit_any_int(value);
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename U>
|
|
||||||
void visit_any_int(U value) {
|
|
||||||
bool is_signed = type_ == 'd' || type_ == 'i';
|
|
||||||
if (type_ == 's') {
|
|
||||||
is_signed = std::numeric_limits<U>::is_signed;
|
|
||||||
}
|
|
||||||
|
|
||||||
using internal::Arg;
|
|
||||||
typedef typename internal::Conditional<
|
|
||||||
is_same<T, void>::value, U, T>::type TargetType;
|
|
||||||
if (const_check(sizeof(TargetType) <= sizeof(int))) {
|
|
||||||
// Extra casts are used to silence warnings.
|
|
||||||
if (is_signed) {
|
|
||||||
arg_.type = Arg::INT;
|
|
||||||
arg_.int_value = static_cast<int>(static_cast<TargetType>(value));
|
|
||||||
} else {
|
|
||||||
arg_.type = Arg::UINT;
|
|
||||||
typedef typename internal::MakeUnsigned<TargetType>::Type Unsigned;
|
|
||||||
arg_.uint_value = static_cast<unsigned>(static_cast<Unsigned>(value));
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (is_signed) {
|
|
||||||
arg_.type = Arg::LONG_LONG;
|
|
||||||
// glibc's printf doesn't sign extend arguments of smaller types:
|
|
||||||
// std::printf("%lld", -42); // prints "4294967254"
|
|
||||||
// but we don't have to do the same because it's a UB.
|
|
||||||
arg_.long_long_value = static_cast<LongLong>(value);
|
|
||||||
} else {
|
|
||||||
arg_.type = Arg::ULONG_LONG;
|
|
||||||
arg_.ulong_long_value =
|
|
||||||
static_cast<typename internal::MakeUnsigned<U>::Type>(value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// Converts an integer argument to char for printf.
|
|
||||||
class CharConverter : public ArgVisitor<CharConverter, void> {
|
|
||||||
private:
|
|
||||||
internal::Arg &arg_;
|
|
||||||
|
|
||||||
FMT_DISALLOW_COPY_AND_ASSIGN(CharConverter);
|
|
||||||
|
|
||||||
public:
|
|
||||||
explicit CharConverter(internal::Arg &arg) : arg_(arg) {}
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
void visit_any_int(T value) {
|
|
||||||
arg_.type = internal::Arg::CHAR;
|
|
||||||
arg_.int_value = static_cast<char>(value);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// Checks if an argument is a valid printf width specifier and sets
|
|
||||||
// left alignment if it is negative.
|
|
||||||
class WidthHandler : public ArgVisitor<WidthHandler, unsigned> {
|
|
||||||
private:
|
|
||||||
FormatSpec &spec_;
|
|
||||||
|
|
||||||
FMT_DISALLOW_COPY_AND_ASSIGN(WidthHandler);
|
|
||||||
|
|
||||||
public:
|
|
||||||
explicit WidthHandler(FormatSpec &spec) : spec_(spec) {}
|
|
||||||
|
|
||||||
void report_unhandled_arg() {
|
|
||||||
FMT_THROW(FormatError("width is not integer"));
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
unsigned visit_any_int(T value) {
|
|
||||||
typedef typename internal::IntTraits<T>::MainType UnsignedType;
|
|
||||||
UnsignedType width = static_cast<UnsignedType>(value);
|
|
||||||
if (internal::is_negative(value)) {
|
|
||||||
spec_.align_ = ALIGN_LEFT;
|
|
||||||
width = 0 - width;
|
|
||||||
}
|
|
||||||
unsigned int_max = std::numeric_limits<int>::max();
|
|
||||||
if (width > int_max)
|
|
||||||
FMT_THROW(FormatError("number is too big"));
|
|
||||||
return static_cast<unsigned>(width);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
} // namespace internal
|
|
||||||
|
|
||||||
/**
|
|
||||||
\rst
|
|
||||||
A ``printf`` argument formatter based on the `curiously recurring template
|
|
||||||
pattern <http://en.wikipedia.org/wiki/Curiously_recurring_template_pattern>`_.
|
|
||||||
|
|
||||||
To use `~fmt::BasicPrintfArgFormatter` define a subclass that implements some
|
|
||||||
or all of the visit methods with the same signatures as the methods in
|
|
||||||
`~fmt::ArgVisitor`, for example, `~fmt::ArgVisitor::visit_int()`.
|
|
||||||
Pass the subclass as the *Impl* template parameter. When a formatting
|
|
||||||
function processes an argument, it will dispatch to a visit method
|
|
||||||
specific to the argument type. For example, if the argument type is
|
|
||||||
``double`` then the `~fmt::ArgVisitor::visit_double()` method of a subclass
|
|
||||||
will be called. If the subclass doesn't contain a method with this signature,
|
|
||||||
then a corresponding method of `~fmt::BasicPrintfArgFormatter` or its
|
|
||||||
superclass will be called.
|
|
||||||
\endrst
|
|
||||||
*/
|
|
||||||
template <typename Impl, typename Char, typename Spec>
|
|
||||||
class BasicPrintfArgFormatter :
|
|
||||||
public internal::ArgFormatterBase<Impl, Char, Spec> {
|
|
||||||
private:
|
|
||||||
void write_null_pointer() {
|
|
||||||
this->spec().type_ = 0;
|
|
||||||
this->write("(nil)");
|
|
||||||
}
|
|
||||||
|
|
||||||
typedef internal::ArgFormatterBase<Impl, Char, Spec> Base;
|
|
||||||
|
|
||||||
public:
|
|
||||||
/**
|
|
||||||
\rst
|
|
||||||
Constructs an argument formatter object.
|
|
||||||
*writer* is a reference to the output writer and *spec* contains format
|
|
||||||
specifier information for standard argument types.
|
|
||||||
\endrst
|
|
||||||
*/
|
|
||||||
BasicPrintfArgFormatter(BasicWriter<Char> &w, Spec &s)
|
|
||||||
: internal::ArgFormatterBase<Impl, Char, Spec>(w, s) {}
|
|
||||||
|
|
||||||
/** Formats an argument of type ``bool``. */
|
|
||||||
void visit_bool(bool value) {
|
|
||||||
Spec &fmt_spec = this->spec();
|
|
||||||
if (fmt_spec.type_ != 's')
|
|
||||||
return this->visit_any_int(value);
|
|
||||||
fmt_spec.type_ = 0;
|
|
||||||
this->write(value);
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Formats a character. */
|
|
||||||
void visit_char(int value) {
|
|
||||||
const Spec &fmt_spec = this->spec();
|
|
||||||
BasicWriter<Char> &w = this->writer();
|
|
||||||
if (fmt_spec.type_ && fmt_spec.type_ != 'c')
|
|
||||||
w.write_int(value, fmt_spec);
|
|
||||||
typedef typename BasicWriter<Char>::CharPtr CharPtr;
|
|
||||||
CharPtr out = CharPtr();
|
|
||||||
if (fmt_spec.width_ > 1) {
|
|
||||||
Char fill = ' ';
|
|
||||||
out = w.grow_buffer(fmt_spec.width_);
|
|
||||||
if (fmt_spec.align_ != ALIGN_LEFT) {
|
|
||||||
std::fill_n(out, fmt_spec.width_ - 1, fill);
|
|
||||||
out += fmt_spec.width_ - 1;
|
|
||||||
} else {
|
|
||||||
std::fill_n(out + 1, fmt_spec.width_ - 1, fill);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
out = w.grow_buffer(1);
|
|
||||||
}
|
|
||||||
*out = static_cast<Char>(value);
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Formats a null-terminated C string. */
|
|
||||||
void visit_cstring(const char *value) {
|
|
||||||
if (value)
|
|
||||||
Base::visit_cstring(value);
|
|
||||||
else if (this->spec().type_ == 'p')
|
|
||||||
write_null_pointer();
|
|
||||||
else
|
|
||||||
this->write("(null)");
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Formats a pointer. */
|
|
||||||
void visit_pointer(const void *value) {
|
|
||||||
if (value)
|
|
||||||
return Base::visit_pointer(value);
|
|
||||||
this->spec().type_ = 0;
|
|
||||||
write_null_pointer();
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Formats an argument of a custom (user-defined) type. */
|
|
||||||
void visit_custom(internal::Arg::CustomValue c) {
|
|
||||||
BasicFormatter<Char> formatter(ArgList(), this->writer());
|
|
||||||
const Char format_str[] = {'}', 0};
|
|
||||||
const Char *format = format_str;
|
|
||||||
c.format(&formatter, c.value, &format);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/** The default printf argument formatter. */
|
|
||||||
template <typename Char>
|
|
||||||
class PrintfArgFormatter :
|
|
||||||
public BasicPrintfArgFormatter<PrintfArgFormatter<Char>, Char, FormatSpec> {
|
|
||||||
public:
|
|
||||||
/** Constructs an argument formatter object. */
|
|
||||||
PrintfArgFormatter(BasicWriter<Char> &w, FormatSpec &s)
|
|
||||||
: BasicPrintfArgFormatter<PrintfArgFormatter<Char>, Char, FormatSpec>(w, s) {}
|
|
||||||
};
|
|
||||||
|
|
||||||
/** This template formats data and writes the output to a writer. */
|
|
||||||
template <typename Char, typename ArgFormatter = PrintfArgFormatter<Char> >
|
|
||||||
class PrintfFormatter : private internal::FormatterBase {
|
|
||||||
private:
|
|
||||||
BasicWriter<Char> &writer_;
|
|
||||||
|
|
||||||
void parse_flags(FormatSpec &spec, const Char *&s);
|
|
||||||
|
|
||||||
// Returns the argument with specified index or, if arg_index is equal
|
|
||||||
// to the maximum unsigned value, the next argument.
|
|
||||||
internal::Arg get_arg(
|
|
||||||
const Char *s,
|
|
||||||
unsigned arg_index = (std::numeric_limits<unsigned>::max)());
|
|
||||||
|
|
||||||
// Parses argument index, flags and width and returns the argument index.
|
|
||||||
unsigned parse_header(const Char *&s, FormatSpec &spec);
|
|
||||||
|
|
||||||
public:
|
|
||||||
/**
|
|
||||||
\rst
|
|
||||||
Constructs a ``PrintfFormatter`` object. References to the arguments and
|
|
||||||
the writer are stored in the formatter object so make sure they have
|
|
||||||
appropriate lifetimes.
|
|
||||||
\endrst
|
|
||||||
*/
|
|
||||||
explicit PrintfFormatter(const ArgList &al, BasicWriter<Char> &w)
|
|
||||||
: FormatterBase(al), writer_(w) {}
|
|
||||||
|
|
||||||
/** Formats stored arguments and writes the output to the writer. */
|
|
||||||
void format(BasicCStringRef<Char> format_str);
|
|
||||||
};
|
|
||||||
|
|
||||||
template <typename Char, typename AF>
|
|
||||||
void PrintfFormatter<Char, AF>::parse_flags(FormatSpec &spec, const Char *&s) {
|
|
||||||
for (;;) {
|
|
||||||
switch (*s++) {
|
|
||||||
case '-':
|
|
||||||
spec.align_ = ALIGN_LEFT;
|
|
||||||
break;
|
|
||||||
case '+':
|
|
||||||
spec.flags_ |= SIGN_FLAG | PLUS_FLAG;
|
|
||||||
break;
|
|
||||||
case '0':
|
|
||||||
spec.fill_ = '0';
|
|
||||||
break;
|
|
||||||
case ' ':
|
|
||||||
spec.flags_ |= SIGN_FLAG;
|
|
||||||
break;
|
|
||||||
case '#':
|
|
||||||
spec.flags_ |= HASH_FLAG;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
--s;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename Char, typename AF>
|
|
||||||
internal::Arg PrintfFormatter<Char, AF>::get_arg(const Char *s,
|
|
||||||
unsigned arg_index) {
|
|
||||||
(void)s;
|
|
||||||
const char *error = FMT_NULL;
|
|
||||||
internal::Arg arg = arg_index == std::numeric_limits<unsigned>::max() ?
|
|
||||||
next_arg(error) : FormatterBase::get_arg(arg_index - 1, error);
|
|
||||||
if (error)
|
|
||||||
FMT_THROW(FormatError(!*s ? "invalid format string" : error));
|
|
||||||
return arg;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename Char, typename AF>
|
|
||||||
unsigned PrintfFormatter<Char, AF>::parse_header(
|
|
||||||
const Char *&s, FormatSpec &spec) {
|
|
||||||
unsigned arg_index = std::numeric_limits<unsigned>::max();
|
|
||||||
Char c = *s;
|
|
||||||
if (c >= '0' && c <= '9') {
|
|
||||||
// Parse an argument index (if followed by '$') or a width possibly
|
|
||||||
// preceded with '0' flag(s).
|
|
||||||
unsigned value = internal::parse_nonnegative_int(s);
|
|
||||||
if (*s == '$') { // value is an argument index
|
|
||||||
++s;
|
|
||||||
arg_index = value;
|
|
||||||
} else {
|
|
||||||
if (c == '0')
|
|
||||||
spec.fill_ = '0';
|
|
||||||
if (value != 0) {
|
|
||||||
// Nonzero value means that we parsed width and don't need to
|
|
||||||
// parse it or flags again, so return now.
|
|
||||||
spec.width_ = value;
|
|
||||||
return arg_index;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
parse_flags(spec, s);
|
|
||||||
// Parse width.
|
|
||||||
if (*s >= '0' && *s <= '9') {
|
|
||||||
spec.width_ = internal::parse_nonnegative_int(s);
|
|
||||||
} else if (*s == '*') {
|
|
||||||
++s;
|
|
||||||
spec.width_ = internal::WidthHandler(spec).visit(get_arg(s));
|
|
||||||
}
|
|
||||||
return arg_index;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename Char, typename AF>
|
|
||||||
void PrintfFormatter<Char, AF>::format(BasicCStringRef<Char> format_str) {
|
|
||||||
const Char *start = format_str.c_str();
|
|
||||||
const Char *s = start;
|
|
||||||
while (*s) {
|
|
||||||
Char c = *s++;
|
|
||||||
if (c != '%') continue;
|
|
||||||
if (*s == c) {
|
|
||||||
write(writer_, start, s);
|
|
||||||
start = ++s;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
write(writer_, start, s - 1);
|
|
||||||
|
|
||||||
FormatSpec spec;
|
|
||||||
spec.align_ = ALIGN_RIGHT;
|
|
||||||
|
|
||||||
// Parse argument index, flags and width.
|
|
||||||
unsigned arg_index = parse_header(s, spec);
|
|
||||||
|
|
||||||
// Parse precision.
|
|
||||||
if (*s == '.') {
|
|
||||||
++s;
|
|
||||||
if ('0' <= *s && *s <= '9') {
|
|
||||||
spec.precision_ = static_cast<int>(internal::parse_nonnegative_int(s));
|
|
||||||
} else if (*s == '*') {
|
|
||||||
++s;
|
|
||||||
spec.precision_ = internal::PrecisionHandler().visit(get_arg(s));
|
|
||||||
} else {
|
|
||||||
spec.precision_ = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
using internal::Arg;
|
|
||||||
Arg arg = get_arg(s, arg_index);
|
|
||||||
if (spec.flag(HASH_FLAG) && internal::IsZeroInt().visit(arg))
|
|
||||||
spec.flags_ &= ~internal::to_unsigned<int>(HASH_FLAG);
|
|
||||||
if (spec.fill_ == '0') {
|
|
||||||
if (arg.type <= Arg::LAST_NUMERIC_TYPE)
|
|
||||||
spec.align_ = ALIGN_NUMERIC;
|
|
||||||
else
|
|
||||||
spec.fill_ = ' '; // Ignore '0' flag for non-numeric types.
|
|
||||||
}
|
|
||||||
|
|
||||||
// Parse length and convert the argument to the required type.
|
|
||||||
using internal::ArgConverter;
|
|
||||||
switch (*s++) {
|
|
||||||
case 'h':
|
|
||||||
if (*s == 'h')
|
|
||||||
ArgConverter<signed char>(arg, *++s).visit(arg);
|
|
||||||
else
|
|
||||||
ArgConverter<short>(arg, *s).visit(arg);
|
|
||||||
break;
|
|
||||||
case 'l':
|
|
||||||
if (*s == 'l')
|
|
||||||
ArgConverter<fmt::LongLong>(arg, *++s).visit(arg);
|
|
||||||
else
|
|
||||||
ArgConverter<long>(arg, *s).visit(arg);
|
|
||||||
break;
|
|
||||||
case 'j':
|
|
||||||
ArgConverter<intmax_t>(arg, *s).visit(arg);
|
|
||||||
break;
|
|
||||||
case 'z':
|
|
||||||
ArgConverter<std::size_t>(arg, *s).visit(arg);
|
|
||||||
break;
|
|
||||||
case 't':
|
|
||||||
ArgConverter<std::ptrdiff_t>(arg, *s).visit(arg);
|
|
||||||
break;
|
|
||||||
case 'L':
|
|
||||||
// printf produces garbage when 'L' is omitted for long double, no
|
|
||||||
// need to do the same.
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
--s;
|
|
||||||
ArgConverter<void>(arg, *s).visit(arg);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Parse type.
|
|
||||||
if (!*s)
|
|
||||||
FMT_THROW(FormatError("invalid format string"));
|
|
||||||
spec.type_ = static_cast<char>(*s++);
|
|
||||||
|
|
||||||
if (spec.type_ == 's') {
|
|
||||||
// set the format type to the default if 's' is specified
|
|
||||||
spec.type_ = internal::DefaultType().visit(arg);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (arg.type <= Arg::LAST_INTEGER_TYPE) {
|
|
||||||
// Normalize type.
|
|
||||||
switch (spec.type_) {
|
|
||||||
case 'i': case 'u':
|
|
||||||
spec.type_ = 'd';
|
|
||||||
break;
|
|
||||||
case 'c':
|
|
||||||
// TODO: handle wchar_t
|
|
||||||
internal::CharConverter(arg).visit(arg);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
start = s;
|
|
||||||
|
|
||||||
// Format argument.
|
|
||||||
AF(writer_, spec).visit(arg);
|
|
||||||
}
|
|
||||||
write(writer_, start, s);
|
|
||||||
}
|
|
||||||
|
|
||||||
inline void printf(Writer &w, CStringRef format, ArgList args) {
|
|
||||||
PrintfFormatter<char>(args, w).format(format);
|
|
||||||
}
|
|
||||||
FMT_VARIADIC(void, printf, Writer &, CStringRef)
|
|
||||||
|
|
||||||
inline void printf(WWriter &w, WCStringRef format, ArgList args) {
|
|
||||||
PrintfFormatter<wchar_t>(args, w).format(format);
|
|
||||||
}
|
|
||||||
FMT_VARIADIC(void, printf, WWriter &, WCStringRef)
|
|
||||||
|
|
||||||
/**
|
|
||||||
\rst
|
|
||||||
Formats arguments and returns the result as a string.
|
|
||||||
|
|
||||||
**Example**::
|
|
||||||
|
|
||||||
std::string message = fmt::sprintf("The answer is %d", 42);
|
|
||||||
\endrst
|
|
||||||
*/
|
|
||||||
inline std::string sprintf(CStringRef format, ArgList args) {
|
|
||||||
MemoryWriter w;
|
|
||||||
printf(w, format, args);
|
|
||||||
return w.str();
|
|
||||||
}
|
|
||||||
FMT_VARIADIC(std::string, sprintf, CStringRef)
|
|
||||||
|
|
||||||
inline std::wstring sprintf(WCStringRef format, ArgList args) {
|
|
||||||
WMemoryWriter w;
|
|
||||||
printf(w, format, args);
|
|
||||||
return w.str();
|
|
||||||
}
|
|
||||||
FMT_VARIADIC_W(std::wstring, sprintf, WCStringRef)
|
|
||||||
|
|
||||||
/**
|
|
||||||
\rst
|
|
||||||
Prints formatted data to the file *f*.
|
|
||||||
|
|
||||||
**Example**::
|
|
||||||
|
|
||||||
fmt::fprintf(stderr, "Don't %s!", "panic");
|
|
||||||
\endrst
|
|
||||||
*/
|
|
||||||
FMT_API int fprintf(std::FILE *f, CStringRef format, ArgList args);
|
|
||||||
FMT_VARIADIC(int, fprintf, std::FILE *, CStringRef)
|
|
||||||
|
|
||||||
/**
|
|
||||||
\rst
|
|
||||||
Prints formatted data to ``stdout``.
|
|
||||||
|
|
||||||
**Example**::
|
|
||||||
|
|
||||||
fmt::printf("Elapsed time: %.2f seconds", 1.23);
|
|
||||||
\endrst
|
|
||||||
*/
|
|
||||||
inline int printf(CStringRef format, ArgList args) {
|
|
||||||
return fprintf(stdout, format, args);
|
|
||||||
}
|
|
||||||
FMT_VARIADIC(int, printf, CStringRef)
|
|
||||||
|
|
||||||
/**
|
|
||||||
\rst
|
|
||||||
Prints formatted data to the stream *os*.
|
|
||||||
|
|
||||||
**Example**::
|
|
||||||
|
|
||||||
fprintf(cerr, "Don't %s!", "panic");
|
|
||||||
\endrst
|
|
||||||
*/
|
|
||||||
inline int fprintf(std::ostream &os, CStringRef format_str, ArgList args) {
|
|
||||||
MemoryWriter w;
|
|
||||||
printf(w, format_str, args);
|
|
||||||
internal::write(os, w);
|
|
||||||
return static_cast<int>(w.size());
|
|
||||||
}
|
|
||||||
FMT_VARIADIC(int, fprintf, std::ostream &, CStringRef)
|
|
||||||
} // namespace fmt
|
|
||||||
|
|
||||||
#ifdef FMT_HEADER_ONLY
|
|
||||||
# include "printf.cc"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#endif // FMT_PRINTF_H_
|
|
148
fmt/string.h
148
fmt/string.h
@ -1,148 +0,0 @@
|
|||||||
/*
|
|
||||||
Formatting library for C++ - string utilities
|
|
||||||
|
|
||||||
Copyright (c) 2012 - 2016, Victor Zverovich
|
|
||||||
All rights reserved.
|
|
||||||
|
|
||||||
For the license information refer to format.h.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifdef FMT_INCLUDE
|
|
||||||
# error "Add the fmt's parent directory and not fmt itself to includes."
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef FMT_STRING_H_
|
|
||||||
#define FMT_STRING_H_
|
|
||||||
|
|
||||||
#include "format.h"
|
|
||||||
|
|
||||||
namespace fmt {
|
|
||||||
|
|
||||||
namespace internal {
|
|
||||||
|
|
||||||
// A buffer that stores data in ``std::basic_string``.
|
|
||||||
template <typename Char, typename Allocator = std::allocator<Char> >
|
|
||||||
class StringBuffer : public Buffer<Char> {
|
|
||||||
public:
|
|
||||||
typedef std::basic_string<Char, std::char_traits<Char>, Allocator> StringType;
|
|
||||||
|
|
||||||
private:
|
|
||||||
StringType data_;
|
|
||||||
|
|
||||||
protected:
|
|
||||||
virtual void grow(std::size_t size) FMT_OVERRIDE {
|
|
||||||
data_.resize(size);
|
|
||||||
this->ptr_ = &data_[0];
|
|
||||||
this->capacity_ = size;
|
|
||||||
}
|
|
||||||
|
|
||||||
public:
|
|
||||||
explicit StringBuffer(const Allocator &allocator = Allocator())
|
|
||||||
: data_(allocator) {}
|
|
||||||
|
|
||||||
// Moves the data to ``str`` clearing the buffer.
|
|
||||||
void move_to(StringType &str) {
|
|
||||||
data_.resize(this->size_);
|
|
||||||
str.swap(data_);
|
|
||||||
this->capacity_ = this->size_ = 0;
|
|
||||||
this->ptr_ = FMT_NULL;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
} // namespace internal
|
|
||||||
|
|
||||||
/**
|
|
||||||
\rst
|
|
||||||
This class template provides operations for formatting and writing data
|
|
||||||
into a character stream. The output is stored in a ``std::basic_string``
|
|
||||||
that grows dynamically.
|
|
||||||
|
|
||||||
You can use one of the following typedefs for common character types
|
|
||||||
and the standard allocator:
|
|
||||||
|
|
||||||
+---------------+----------------------------+
|
|
||||||
| Type | Definition |
|
|
||||||
+===============+============================+
|
|
||||||
| StringWriter | BasicStringWriter<char> |
|
|
||||||
+---------------+----------------------------+
|
|
||||||
| WStringWriter | BasicStringWriter<wchar_t> |
|
|
||||||
+---------------+----------------------------+
|
|
||||||
|
|
||||||
**Example**::
|
|
||||||
|
|
||||||
StringWriter out;
|
|
||||||
out << "The answer is " << 42 << "\n";
|
|
||||||
|
|
||||||
This will write the following output to the ``out`` object:
|
|
||||||
|
|
||||||
.. code-block:: none
|
|
||||||
|
|
||||||
The answer is 42
|
|
||||||
|
|
||||||
The output can be moved to a ``std::basic_string`` with ``out.move_to()``.
|
|
||||||
\endrst
|
|
||||||
*/
|
|
||||||
template <typename Char, typename Allocator = std::allocator<Char> >
|
|
||||||
class BasicStringWriter : public BasicWriter<Char> {
|
|
||||||
private:
|
|
||||||
internal::StringBuffer<Char, Allocator> buffer_;
|
|
||||||
|
|
||||||
public:
|
|
||||||
/**
|
|
||||||
\rst
|
|
||||||
Constructs a :class:`fmt::BasicStringWriter` object.
|
|
||||||
\endrst
|
|
||||||
*/
|
|
||||||
explicit BasicStringWriter(const Allocator &allocator = Allocator())
|
|
||||||
: BasicWriter<Char>(buffer_), buffer_(allocator) {}
|
|
||||||
|
|
||||||
/**
|
|
||||||
\rst
|
|
||||||
Moves the buffer content to *str* clearing the buffer.
|
|
||||||
\endrst
|
|
||||||
*/
|
|
||||||
void move_to(std::basic_string<Char, std::char_traits<Char>, Allocator> &str) {
|
|
||||||
buffer_.move_to(str);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
typedef BasicStringWriter<char> StringWriter;
|
|
||||||
typedef BasicStringWriter<wchar_t> WStringWriter;
|
|
||||||
|
|
||||||
/**
|
|
||||||
\rst
|
|
||||||
Converts *value* to ``std::string`` using the default format for type *T*.
|
|
||||||
|
|
||||||
**Example**::
|
|
||||||
|
|
||||||
#include "fmt/string.h"
|
|
||||||
|
|
||||||
std::string answer = fmt::to_string(42);
|
|
||||||
\endrst
|
|
||||||
*/
|
|
||||||
template <typename T>
|
|
||||||
std::string to_string(const T &value) {
|
|
||||||
fmt::MemoryWriter w;
|
|
||||||
w << value;
|
|
||||||
return w.str();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
\rst
|
|
||||||
Converts *value* to ``std::wstring`` using the default format for type *T*.
|
|
||||||
|
|
||||||
**Example**::
|
|
||||||
|
|
||||||
#include "fmt/string.h"
|
|
||||||
|
|
||||||
std::wstring answer = fmt::to_wstring(42);
|
|
||||||
\endrst
|
|
||||||
*/
|
|
||||||
template <typename T>
|
|
||||||
std::wstring to_wstring(const T &value) {
|
|
||||||
fmt::WMemoryWriter w;
|
|
||||||
w << value;
|
|
||||||
return w.str();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif // FMT_STRING_H_
|
|
143
fmt/time.h
143
fmt/time.h
@ -1,143 +0,0 @@
|
|||||||
/*
|
|
||||||
Formatting library for C++ - time formatting
|
|
||||||
|
|
||||||
Copyright (c) 2012 - 2016, Victor Zverovich
|
|
||||||
All rights reserved.
|
|
||||||
|
|
||||||
For the license information refer to format.h.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef FMT_TIME_H_
|
|
||||||
#define FMT_TIME_H_
|
|
||||||
|
|
||||||
#include "format.h"
|
|
||||||
#include <ctime>
|
|
||||||
|
|
||||||
#ifdef _MSC_VER
|
|
||||||
# pragma warning(push)
|
|
||||||
# pragma warning(disable: 4702) // unreachable code
|
|
||||||
# pragma warning(disable: 4996) // "deprecated" functions
|
|
||||||
#endif
|
|
||||||
|
|
||||||
namespace fmt {
|
|
||||||
template <typename ArgFormatter>
|
|
||||||
void format_arg(BasicFormatter<char, ArgFormatter> &f,
|
|
||||||
const char *&format_str, const std::tm &tm) {
|
|
||||||
if (*format_str == ':')
|
|
||||||
++format_str;
|
|
||||||
const char *end = format_str;
|
|
||||||
while (*end && *end != '}')
|
|
||||||
++end;
|
|
||||||
if (*end != '}')
|
|
||||||
FMT_THROW(FormatError("missing '}' in format string"));
|
|
||||||
internal::MemoryBuffer<char, internal::INLINE_BUFFER_SIZE> format;
|
|
||||||
format.append(format_str, end + 1);
|
|
||||||
format[format.size() - 1] = '\0';
|
|
||||||
Buffer<char> &buffer = f.writer().buffer();
|
|
||||||
std::size_t start = buffer.size();
|
|
||||||
for (;;) {
|
|
||||||
std::size_t size = buffer.capacity() - start;
|
|
||||||
std::size_t count = std::strftime(&buffer[start], size, &format[0], &tm);
|
|
||||||
if (count != 0) {
|
|
||||||
buffer.resize(start + count);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (size >= format.size() * 256) {
|
|
||||||
// If the buffer is 256 times larger than the format string, assume
|
|
||||||
// that `strftime` gives an empty result. There doesn't seem to be a
|
|
||||||
// better way to distinguish the two cases:
|
|
||||||
// https://github.com/fmtlib/fmt/issues/367
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
const std::size_t MIN_GROWTH = 10;
|
|
||||||
buffer.reserve(buffer.capacity() + (size > MIN_GROWTH ? size : MIN_GROWTH));
|
|
||||||
}
|
|
||||||
format_str = end + 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
namespace internal{
|
|
||||||
inline Null<> localtime_r(...) { return Null<>(); }
|
|
||||||
inline Null<> localtime_s(...) { return Null<>(); }
|
|
||||||
inline Null<> gmtime_r(...) { return Null<>(); }
|
|
||||||
inline Null<> gmtime_s(...) { return Null<>(); }
|
|
||||||
}
|
|
||||||
|
|
||||||
// Thread-safe replacement for std::localtime
|
|
||||||
inline std::tm localtime(std::time_t time) {
|
|
||||||
struct LocalTime {
|
|
||||||
std::time_t time_;
|
|
||||||
std::tm tm_;
|
|
||||||
|
|
||||||
LocalTime(std::time_t t): time_(t) {}
|
|
||||||
|
|
||||||
bool run() {
|
|
||||||
using namespace fmt::internal;
|
|
||||||
return handle(localtime_r(&time_, &tm_));
|
|
||||||
}
|
|
||||||
|
|
||||||
bool handle(std::tm *tm) { return tm != FMT_NULL; }
|
|
||||||
|
|
||||||
bool handle(internal::Null<>) {
|
|
||||||
using namespace fmt::internal;
|
|
||||||
return fallback(localtime_s(&tm_, &time_));
|
|
||||||
}
|
|
||||||
|
|
||||||
bool fallback(int res) { return res == 0; }
|
|
||||||
|
|
||||||
bool fallback(internal::Null<>) {
|
|
||||||
using namespace fmt::internal;
|
|
||||||
std::tm *tm = std::localtime(&time_);
|
|
||||||
if (tm) tm_ = *tm;
|
|
||||||
return tm != FMT_NULL;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
LocalTime lt(time);
|
|
||||||
if (lt.run())
|
|
||||||
return lt.tm_;
|
|
||||||
// Too big time values may be unsupported.
|
|
||||||
FMT_THROW(fmt::FormatError("time_t value out of range"));
|
|
||||||
return std::tm();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Thread-safe replacement for std::gmtime
|
|
||||||
inline std::tm gmtime(std::time_t time) {
|
|
||||||
struct GMTime {
|
|
||||||
std::time_t time_;
|
|
||||||
std::tm tm_;
|
|
||||||
|
|
||||||
GMTime(std::time_t t): time_(t) {}
|
|
||||||
|
|
||||||
bool run() {
|
|
||||||
using namespace fmt::internal;
|
|
||||||
return handle(gmtime_r(&time_, &tm_));
|
|
||||||
}
|
|
||||||
|
|
||||||
bool handle(std::tm *tm) { return tm != FMT_NULL; }
|
|
||||||
|
|
||||||
bool handle(internal::Null<>) {
|
|
||||||
using namespace fmt::internal;
|
|
||||||
return fallback(gmtime_s(&tm_, &time_));
|
|
||||||
}
|
|
||||||
|
|
||||||
bool fallback(int res) { return res == 0; }
|
|
||||||
|
|
||||||
bool fallback(internal::Null<>) {
|
|
||||||
std::tm *tm = std::gmtime(&time_);
|
|
||||||
if (tm != FMT_NULL) tm_ = *tm;
|
|
||||||
return tm != FMT_NULL;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
GMTime gt(time);
|
|
||||||
if (gt.run())
|
|
||||||
return gt.tm_;
|
|
||||||
// Too big time values may be unsupported.
|
|
||||||
FMT_THROW(fmt::FormatError("time_t value out of range"));
|
|
||||||
return std::tm();
|
|
||||||
}
|
|
||||||
} //namespace fmt
|
|
||||||
|
|
||||||
#ifdef _MSC_VER
|
|
||||||
# pragma warning(pop)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#endif // FMT_TIME_H_
|
|
1180
include/fmt/core.h
Normal file
1180
include/fmt/core.h
Normal file
File diff suppressed because it is too large
Load Diff
@ -1,31 +1,12 @@
|
|||||||
/*
|
// Formatting library for C++
|
||||||
Formatting library for C++
|
//
|
||||||
|
// Copyright (c) 2012 - 2016, Victor Zverovich
|
||||||
Copyright (c) 2012 - 2016, Victor Zverovich
|
// All rights reserved.
|
||||||
All rights reserved.
|
//
|
||||||
|
// For the license information refer to format.h.
|
||||||
Redistribution and use in source and binary forms, with or without
|
|
||||||
modification, are permitted provided that the following conditions are met:
|
|
||||||
|
|
||||||
1. Redistributions of source code must retain the above copyright notice, this
|
|
||||||
list of conditions and the following disclaimer.
|
|
||||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
|
||||||
this list of conditions and the following disclaimer in the documentation
|
|
||||||
and/or other materials provided with the distribution.
|
|
||||||
|
|
||||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
|
||||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
|
||||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
||||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
|
||||||
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
|
||||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
|
||||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
|
||||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
||||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
|
||||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "format.h"
|
#include "format.h"
|
||||||
|
#include "locale.h"
|
||||||
|
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
@ -61,6 +42,12 @@
|
|||||||
# define FMT_CATCH(x) if (false)
|
# define FMT_CATCH(x) if (false)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef __GNUC__
|
||||||
|
// Disable the warning about declaration shadowing because it affects too
|
||||||
|
// many valid cases.
|
||||||
|
# pragma GCC diagnostic ignored "-Wshadow"
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifdef _MSC_VER
|
#ifdef _MSC_VER
|
||||||
# pragma warning(push)
|
# pragma warning(push)
|
||||||
# pragma warning(disable: 4127) // conditional expression is constant
|
# pragma warning(disable: 4127) // conditional expression is constant
|
||||||
@ -72,20 +59,17 @@
|
|||||||
|
|
||||||
// Dummy implementations of strerror_r and strerror_s called if corresponding
|
// Dummy implementations of strerror_r and strerror_s called if corresponding
|
||||||
// system functions are not available.
|
// system functions are not available.
|
||||||
FMT_MAYBE_UNUSED
|
inline fmt::internal::null<> strerror_r(int, char *, ...) {
|
||||||
static inline fmt::internal::Null<> strerror_r(int, char *, ...) {
|
return fmt::internal::null<>();
|
||||||
return fmt::internal::Null<>();
|
|
||||||
}
|
}
|
||||||
FMT_MAYBE_UNUSED
|
inline fmt::internal::null<> strerror_s(char *, std::size_t, ...) {
|
||||||
static inline fmt::internal::Null<> strerror_s(char *, std::size_t, ...) {
|
return fmt::internal::null<>();
|
||||||
return fmt::internal::Null<>();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace fmt {
|
namespace fmt {
|
||||||
|
|
||||||
FMT_FUNC internal::RuntimeError::~RuntimeError() FMT_DTOR_NOEXCEPT {}
|
FMT_FUNC format_error::~format_error() throw() {}
|
||||||
FMT_FUNC FormatError::~FormatError() FMT_DTOR_NOEXCEPT {}
|
FMT_FUNC system_error::~system_error() throw() {}
|
||||||
FMT_FUNC SystemError::~SystemError() FMT_DTOR_NOEXCEPT {}
|
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
@ -110,7 +94,7 @@ inline int fmt_snprintf(char *buffer, size_t size, const char *format, ...) {
|
|||||||
|
|
||||||
const char RESET_COLOR[] = "\x1b[0m";
|
const char RESET_COLOR[] = "\x1b[0m";
|
||||||
|
|
||||||
typedef void (*FormatFunc)(Writer &, int, StringRef);
|
typedef void (*FormatFunc)(internal::buffer &, int, string_view);
|
||||||
|
|
||||||
// Portable thread-safe version of strerror.
|
// Portable thread-safe version of strerror.
|
||||||
// Sets buffer to point to a string describing the error code.
|
// Sets buffer to point to a string describing the error code.
|
||||||
@ -150,7 +134,7 @@ int safe_strerror(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Handle the case when strerror_r is not available.
|
// Handle the case when strerror_r is not available.
|
||||||
int handle(internal::Null<>) {
|
int handle(internal::null<>) {
|
||||||
return fallback(strerror_s(buffer_, buffer_size_, error_code_));
|
return fallback(strerror_s(buffer_, buffer_size_, error_code_));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -161,22 +145,13 @@ int safe_strerror(
|
|||||||
ERANGE : result;
|
ERANGE : result;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef __c2__
|
|
||||||
# pragma clang diagnostic push
|
|
||||||
# pragma clang diagnostic ignored "-Wdeprecated-declarations"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// Fallback to strerror if strerror_r and strerror_s are not available.
|
// Fallback to strerror if strerror_r and strerror_s are not available.
|
||||||
int fallback(internal::Null<>) {
|
int fallback(internal::null<>) {
|
||||||
errno = 0;
|
errno = 0;
|
||||||
buffer_ = strerror(error_code_);
|
buffer_ = strerror(error_code_);
|
||||||
return errno;
|
return errno;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef __c2__
|
|
||||||
# pragma clang diagnostic pop
|
|
||||||
#endif
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
StrError(int err_code, char *&buf, std::size_t buf_size)
|
StrError(int err_code, char *&buf, std::size_t buf_size)
|
||||||
: error_code_(err_code), buffer_(buf), buffer_size_(buf_size) {}
|
: error_code_(err_code), buffer_(buf), buffer_size_(buf_size) {}
|
||||||
@ -188,32 +163,36 @@ int safe_strerror(
|
|||||||
return StrError(error_code, buffer, buffer_size).run();
|
return StrError(error_code, buffer, buffer_size).run();
|
||||||
}
|
}
|
||||||
|
|
||||||
void format_error_code(Writer &out, int error_code,
|
void format_error_code(internal::buffer &out, int error_code,
|
||||||
StringRef message) FMT_NOEXCEPT {
|
string_view message) FMT_NOEXCEPT {
|
||||||
// Report error code making sure that the output fits into
|
// Report error code making sure that the output fits into
|
||||||
// INLINE_BUFFER_SIZE to avoid dynamic memory allocation and potential
|
// INLINE_BUFFER_SIZE to avoid dynamic memory allocation and potential
|
||||||
// bad_alloc.
|
// bad_alloc.
|
||||||
out.clear();
|
out.resize(0);
|
||||||
static const char SEP[] = ": ";
|
static const char SEP[] = ": ";
|
||||||
static const char ERROR_STR[] = "error ";
|
static const char ERROR_STR[] = "error ";
|
||||||
// Subtract 2 to account for terminating null characters in SEP and ERROR_STR.
|
// Subtract 2 to account for terminating null characters in SEP and ERROR_STR.
|
||||||
std::size_t error_code_size = sizeof(SEP) + sizeof(ERROR_STR) - 2;
|
std::size_t error_code_size = sizeof(SEP) + sizeof(ERROR_STR) - 2;
|
||||||
typedef internal::IntTraits<int>::MainType MainType;
|
typedef internal::int_traits<int>::main_type main_type;
|
||||||
MainType abs_value = static_cast<MainType>(error_code);
|
main_type abs_value = static_cast<main_type>(error_code);
|
||||||
if (internal::is_negative(error_code)) {
|
if (internal::is_negative(error_code)) {
|
||||||
abs_value = 0 - abs_value;
|
abs_value = 0 - abs_value;
|
||||||
++error_code_size;
|
++error_code_size;
|
||||||
}
|
}
|
||||||
error_code_size += internal::count_digits(abs_value);
|
error_code_size += internal::count_digits(abs_value);
|
||||||
if (message.size() <= internal::INLINE_BUFFER_SIZE - error_code_size)
|
writer w(out);
|
||||||
out << message << SEP;
|
if (message.size() <= internal::INLINE_BUFFER_SIZE - error_code_size) {
|
||||||
out << ERROR_STR << error_code;
|
w.write(message);
|
||||||
|
w.write(SEP);
|
||||||
|
}
|
||||||
|
w.write(ERROR_STR);
|
||||||
|
w.write(error_code);
|
||||||
assert(out.size() <= internal::INLINE_BUFFER_SIZE);
|
assert(out.size() <= internal::INLINE_BUFFER_SIZE);
|
||||||
}
|
}
|
||||||
|
|
||||||
void report_error(FormatFunc func, int error_code,
|
void report_error(FormatFunc func, int error_code,
|
||||||
StringRef message) FMT_NOEXCEPT {
|
string_view message) FMT_NOEXCEPT {
|
||||||
MemoryWriter full_message;
|
memory_buffer full_message;
|
||||||
func(full_message, error_code, message);
|
func(full_message, error_code, message);
|
||||||
// Use Writer::data instead of Writer::c_str to avoid potential memory
|
// Use Writer::data instead of Writer::c_str to avoid potential memory
|
||||||
// allocation.
|
// allocation.
|
||||||
@ -222,17 +201,23 @@ void report_error(FormatFunc func, int error_code,
|
|||||||
}
|
}
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
FMT_FUNC void SystemError::init(
|
template <typename Char>
|
||||||
int err_code, CStringRef format_str, ArgList args) {
|
FMT_FUNC Char internal::thousands_sep(locale_provider *lp) {
|
||||||
|
std::locale loc = lp ? lp->locale().get() : std::locale();
|
||||||
|
return std::use_facet<std::numpunct<Char>>(loc).thousands_sep();
|
||||||
|
}
|
||||||
|
|
||||||
|
FMT_FUNC void system_error::init(
|
||||||
|
int err_code, string_view format_str, format_args args) {
|
||||||
error_code_ = err_code;
|
error_code_ = err_code;
|
||||||
MemoryWriter w;
|
memory_buffer buffer;
|
||||||
format_system_error(w, err_code, format(format_str, args));
|
format_system_error(buffer, err_code, vformat(format_str, args));
|
||||||
std::runtime_error &base = *this;
|
std::runtime_error &base = *this;
|
||||||
base = std::runtime_error(w.str());
|
base = std::runtime_error(to_string(buffer));
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
int internal::CharTraits<char>::format_float(
|
int internal::char_traits<char>::format_float(
|
||||||
char *buffer, std::size_t size, const char *format,
|
char *buffer, std::size_t size, const char *format,
|
||||||
unsigned width, int precision, T value) {
|
unsigned width, int precision, T value) {
|
||||||
if (width == 0) {
|
if (width == 0) {
|
||||||
@ -246,7 +231,7 @@ int internal::CharTraits<char>::format_float(
|
|||||||
}
|
}
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
int internal::CharTraits<wchar_t>::format_float(
|
int internal::char_traits<wchar_t>::format_float(
|
||||||
wchar_t *buffer, std::size_t size, const wchar_t *format,
|
wchar_t *buffer, std::size_t size, const wchar_t *format,
|
||||||
unsigned width, int precision, T value) {
|
unsigned width, int precision, T value) {
|
||||||
if (width == 0) {
|
if (width == 0) {
|
||||||
@ -260,7 +245,7 @@ int internal::CharTraits<wchar_t>::format_float(
|
|||||||
}
|
}
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
const char internal::BasicData<T>::DIGITS[] =
|
const char internal::basic_data<T>::DIGITS[] =
|
||||||
"0001020304050607080910111213141516171819"
|
"0001020304050607080910111213141516171819"
|
||||||
"2021222324252627282930313233343536373839"
|
"2021222324252627282930313233343536373839"
|
||||||
"4041424344454647484950515253545556575859"
|
"4041424344454647484950515253545556575859"
|
||||||
@ -279,63 +264,50 @@ const char internal::BasicData<T>::DIGITS[] =
|
|||||||
factor * 1000000000
|
factor * 1000000000
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
const uint32_t internal::BasicData<T>::POWERS_OF_10_32[] = {
|
const uint32_t internal::basic_data<T>::POWERS_OF_10_32[] = {
|
||||||
0, FMT_POWERS_OF_10(1)
|
0, FMT_POWERS_OF_10(1)
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
const uint64_t internal::BasicData<T>::POWERS_OF_10_64[] = {
|
const uint64_t internal::basic_data<T>::POWERS_OF_10_64[] = {
|
||||||
0,
|
0,
|
||||||
FMT_POWERS_OF_10(1),
|
FMT_POWERS_OF_10(1),
|
||||||
FMT_POWERS_OF_10(ULongLong(1000000000)),
|
FMT_POWERS_OF_10(1000000000ull),
|
||||||
// Multiply several constants instead of using a single long long constant
|
10000000000000000000ull
|
||||||
// to avoid warnings about C++98 not supporting long long.
|
|
||||||
ULongLong(1000000000) * ULongLong(1000000000) * 10
|
|
||||||
};
|
};
|
||||||
|
|
||||||
FMT_FUNC void internal::report_unknown_type(char code, const char *type) {
|
|
||||||
(void)type;
|
|
||||||
if (std::isprint(static_cast<unsigned char>(code))) {
|
|
||||||
FMT_THROW(FormatError(
|
|
||||||
format("unknown format code '{}' for {}", code, type)));
|
|
||||||
}
|
|
||||||
FMT_THROW(FormatError(
|
|
||||||
format("unknown format code '\\x{:02x}' for {}",
|
|
||||||
static_cast<unsigned>(code), type)));
|
|
||||||
}
|
|
||||||
|
|
||||||
#if FMT_USE_WINDOWS_H
|
#if FMT_USE_WINDOWS_H
|
||||||
|
|
||||||
FMT_FUNC internal::UTF8ToUTF16::UTF8ToUTF16(StringRef s) {
|
FMT_FUNC internal::utf8_to_utf16::utf8_to_utf16(string_view s) {
|
||||||
static const char ERROR_MSG[] = "cannot convert string from UTF-8 to UTF-16";
|
static const char ERROR_MSG[] = "cannot convert string from UTF-8 to UTF-16";
|
||||||
if (s.size() > INT_MAX)
|
if (s.size() > INT_MAX)
|
||||||
FMT_THROW(WindowsError(ERROR_INVALID_PARAMETER, ERROR_MSG));
|
FMT_THROW(windows_error(ERROR_INVALID_PARAMETER, ERROR_MSG));
|
||||||
int s_size = static_cast<int>(s.size());
|
int s_size = static_cast<int>(s.size());
|
||||||
int length = MultiByteToWideChar(
|
int length = MultiByteToWideChar(
|
||||||
CP_UTF8, MB_ERR_INVALID_CHARS, s.data(), s_size, FMT_NULL, 0);
|
CP_UTF8, MB_ERR_INVALID_CHARS, s.data(), s_size, FMT_NULL, 0);
|
||||||
if (length == 0)
|
if (length == 0)
|
||||||
FMT_THROW(WindowsError(GetLastError(), ERROR_MSG));
|
FMT_THROW(windows_error(GetLastError(), ERROR_MSG));
|
||||||
buffer_.resize(length + 1);
|
buffer_.resize(length + 1);
|
||||||
length = MultiByteToWideChar(
|
length = MultiByteToWideChar(
|
||||||
CP_UTF8, MB_ERR_INVALID_CHARS, s.data(), s_size, &buffer_[0], length);
|
CP_UTF8, MB_ERR_INVALID_CHARS, s.data(), s_size, &buffer_[0], length);
|
||||||
if (length == 0)
|
if (length == 0)
|
||||||
FMT_THROW(WindowsError(GetLastError(), ERROR_MSG));
|
FMT_THROW(windows_error(GetLastError(), ERROR_MSG));
|
||||||
buffer_[length] = 0;
|
buffer_[length] = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
FMT_FUNC internal::UTF16ToUTF8::UTF16ToUTF8(WStringRef s) {
|
FMT_FUNC internal::utf16_to_utf8::utf16_to_utf8(wstring_view s) {
|
||||||
if (int error_code = convert(s)) {
|
if (int error_code = convert(s)) {
|
||||||
FMT_THROW(WindowsError(error_code,
|
FMT_THROW(windows_error(error_code,
|
||||||
"cannot convert string from UTF-16 to UTF-8"));
|
"cannot convert string from UTF-16 to UTF-8"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
FMT_FUNC int internal::UTF16ToUTF8::convert(WStringRef s) {
|
FMT_FUNC int internal::utf16_to_utf8::convert(wstring_view s) {
|
||||||
if (s.size() > INT_MAX)
|
if (s.size() > INT_MAX)
|
||||||
return ERROR_INVALID_PARAMETER;
|
return ERROR_INVALID_PARAMETER;
|
||||||
int s_size = static_cast<int>(s.size());
|
int s_size = static_cast<int>(s.size());
|
||||||
int length = WideCharToMultiByte(
|
int length = WideCharToMultiByte(
|
||||||
CP_UTF8, 0, s.data(), s_size, FMT_NULL, 0, FMT_NULL, FMT_NULL);
|
CP_UTF8, 0, s.data(), s_size, FMT_NULL, 0, FMT_NULL, FMT_NULL);
|
||||||
if (length == 0)
|
if (length == 0)
|
||||||
return GetLastError();
|
return GetLastError();
|
||||||
buffer_.resize(length + 1);
|
buffer_.resize(length + 1);
|
||||||
@ -347,142 +319,144 @@ FMT_FUNC int internal::UTF16ToUTF8::convert(WStringRef s) {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
FMT_FUNC void WindowsError::init(
|
FMT_FUNC void windows_error::init(
|
||||||
int err_code, CStringRef format_str, ArgList args) {
|
int err_code, string_view format_str, format_args args) {
|
||||||
error_code_ = err_code;
|
error_code_ = err_code;
|
||||||
MemoryWriter w;
|
memory_buffer buffer;
|
||||||
internal::format_windows_error(w, err_code, format(format_str, args));
|
internal::format_windows_error(buffer, err_code, vformat(format_str, args));
|
||||||
std::runtime_error &base = *this;
|
std::runtime_error &base = *this;
|
||||||
base = std::runtime_error(w.str());
|
base = std::runtime_error(to_string(buffer));
|
||||||
}
|
}
|
||||||
|
|
||||||
FMT_FUNC void internal::format_windows_error(
|
FMT_FUNC void internal::format_windows_error(
|
||||||
Writer &out, int error_code, StringRef message) FMT_NOEXCEPT {
|
internal::buffer &out, int error_code, string_view message) FMT_NOEXCEPT {
|
||||||
FMT_TRY {
|
FMT_TRY {
|
||||||
MemoryBuffer<wchar_t, INLINE_BUFFER_SIZE> buffer;
|
wmemory_buffer buf;
|
||||||
buffer.resize(INLINE_BUFFER_SIZE);
|
buf.resize(INLINE_BUFFER_SIZE);
|
||||||
for (;;) {
|
for (;;) {
|
||||||
wchar_t *system_message = &buffer[0];
|
wchar_t *system_message = &buf[0];
|
||||||
int result = FormatMessageW(
|
int result = FormatMessageW(
|
||||||
FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
|
FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
|
||||||
FMT_NULL, error_code, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
|
FMT_NULL, error_code, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
|
||||||
system_message, static_cast<uint32_t>(buffer.size()), FMT_NULL);
|
system_message, static_cast<uint32_t>(buf.size()), FMT_NULL);
|
||||||
if (result != 0) {
|
if (result != 0) {
|
||||||
UTF16ToUTF8 utf8_message;
|
utf16_to_utf8 utf8_message;
|
||||||
if (utf8_message.convert(system_message) == ERROR_SUCCESS) {
|
if (utf8_message.convert(system_message) == ERROR_SUCCESS) {
|
||||||
out << message << ": " << utf8_message;
|
writer w(out);
|
||||||
|
w.write(message);
|
||||||
|
w.write(": ");
|
||||||
|
w.write(utf8_message);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (GetLastError() != ERROR_INSUFFICIENT_BUFFER)
|
if (GetLastError() != ERROR_INSUFFICIENT_BUFFER)
|
||||||
break; // Can't get error message, report error code instead.
|
break; // Can't get error message, report error code instead.
|
||||||
buffer.resize(buffer.size() * 2);
|
buf.resize(buf.size() * 2);
|
||||||
}
|
}
|
||||||
} FMT_CATCH(...) {}
|
} FMT_CATCH(...) {}
|
||||||
fmt::format_error_code(out, error_code, message); // 'fmt::' is for bcc32.
|
format_error_code(out, error_code, message);
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif // FMT_USE_WINDOWS_H
|
#endif // FMT_USE_WINDOWS_H
|
||||||
|
|
||||||
FMT_FUNC void format_system_error(
|
FMT_FUNC void format_system_error(
|
||||||
Writer &out, int error_code, StringRef message) FMT_NOEXCEPT {
|
internal::buffer &out, int error_code, string_view message) FMT_NOEXCEPT {
|
||||||
FMT_TRY {
|
FMT_TRY {
|
||||||
internal::MemoryBuffer<char, internal::INLINE_BUFFER_SIZE> buffer;
|
memory_buffer buf;
|
||||||
buffer.resize(internal::INLINE_BUFFER_SIZE);
|
buf.resize(internal::INLINE_BUFFER_SIZE);
|
||||||
for (;;) {
|
for (;;) {
|
||||||
char *system_message = &buffer[0];
|
char *system_message = &buf[0];
|
||||||
int result = safe_strerror(error_code, system_message, buffer.size());
|
int result = safe_strerror(error_code, system_message, buf.size());
|
||||||
if (result == 0) {
|
if (result == 0) {
|
||||||
out << message << ": " << system_message;
|
writer w(out);
|
||||||
|
w.write(message);
|
||||||
|
w.write(": ");
|
||||||
|
w.write(system_message);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (result != ERANGE)
|
if (result != ERANGE)
|
||||||
break; // Can't get error message, report error code instead.
|
break; // Can't get error message, report error code instead.
|
||||||
buffer.resize(buffer.size() * 2);
|
buf.resize(buf.size() * 2);
|
||||||
}
|
}
|
||||||
} FMT_CATCH(...) {}
|
} FMT_CATCH(...) {}
|
||||||
fmt::format_error_code(out, error_code, message); // 'fmt::' is for bcc32.
|
format_error_code(out, error_code, message);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename Char>
|
template <typename Char>
|
||||||
void internal::FixedBuffer<Char>::grow(std::size_t) {
|
void basic_fixed_buffer<Char>::grow(std::size_t) {
|
||||||
FMT_THROW(std::runtime_error("buffer overflow"));
|
FMT_THROW(std::runtime_error("buffer overflow"));
|
||||||
}
|
}
|
||||||
|
|
||||||
FMT_FUNC internal::Arg internal::FormatterBase::do_get_arg(
|
FMT_FUNC void internal::error_handler::on_error(const char *message) {
|
||||||
unsigned arg_index, const char *&error) {
|
FMT_THROW(format_error(message));
|
||||||
internal::Arg arg = args_[arg_index];
|
|
||||||
switch (arg.type) {
|
|
||||||
case internal::Arg::NONE:
|
|
||||||
error = "argument index out of range";
|
|
||||||
break;
|
|
||||||
case internal::Arg::NAMED_ARG:
|
|
||||||
arg = *static_cast<const internal::Arg*>(arg.pointer);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
/*nothing*/;
|
|
||||||
}
|
|
||||||
return arg;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
FMT_FUNC void report_system_error(
|
FMT_FUNC void report_system_error(
|
||||||
int error_code, fmt::StringRef message) FMT_NOEXCEPT {
|
int error_code, fmt::string_view message) FMT_NOEXCEPT {
|
||||||
// 'fmt::' is for bcc32.
|
|
||||||
report_error(format_system_error, error_code, message);
|
report_error(format_system_error, error_code, message);
|
||||||
}
|
}
|
||||||
|
|
||||||
#if FMT_USE_WINDOWS_H
|
#if FMT_USE_WINDOWS_H
|
||||||
FMT_FUNC void report_windows_error(
|
FMT_FUNC void report_windows_error(
|
||||||
int error_code, fmt::StringRef message) FMT_NOEXCEPT {
|
int error_code, fmt::string_view message) FMT_NOEXCEPT {
|
||||||
// 'fmt::' is for bcc32.
|
|
||||||
report_error(internal::format_windows_error, error_code, message);
|
report_error(internal::format_windows_error, error_code, message);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
FMT_FUNC void print(std::FILE *f, CStringRef format_str, ArgList args) {
|
FMT_FUNC void vprint(std::FILE *f, string_view format_str, format_args args) {
|
||||||
MemoryWriter w;
|
memory_buffer buffer;
|
||||||
w.write(format_str, args);
|
vformat_to(buffer, format_str, args);
|
||||||
std::fwrite(w.data(), 1, w.size(), f);
|
std::fwrite(buffer.data(), 1, buffer.size(), f);
|
||||||
}
|
}
|
||||||
|
|
||||||
FMT_FUNC void print(CStringRef format_str, ArgList args) {
|
FMT_FUNC void vprint(string_view format_str, format_args args) {
|
||||||
print(stdout, format_str, args);
|
vprint(stdout, format_str, args);
|
||||||
}
|
}
|
||||||
|
|
||||||
FMT_FUNC void print_colored(Color c, CStringRef format, ArgList args) {
|
FMT_FUNC void vprint_colored(Color c, string_view format, format_args args) {
|
||||||
char escape[] = "\x1b[30m";
|
char escape[] = "\x1b[30m";
|
||||||
escape[3] = static_cast<char>('0' + c);
|
escape[3] = static_cast<char>('0' + c);
|
||||||
std::fputs(escape, stdout);
|
std::fputs(escape, stdout);
|
||||||
print(format, args);
|
vprint(format, args);
|
||||||
std::fputs(RESET_COLOR, stdout);
|
std::fputs(RESET_COLOR, stdout);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
FMT_FUNC locale locale_provider::locale() { return fmt::locale(); }
|
||||||
|
|
||||||
#ifndef FMT_HEADER_ONLY
|
#ifndef FMT_HEADER_ONLY
|
||||||
|
|
||||||
template struct internal::BasicData<void>;
|
template struct internal::basic_data<void>;
|
||||||
|
|
||||||
// Explicit instantiations for char.
|
// Explicit instantiations for char.
|
||||||
|
|
||||||
template void internal::FixedBuffer<char>::grow(std::size_t);
|
template char internal::thousands_sep(locale_provider *lp);
|
||||||
|
|
||||||
template FMT_API int internal::CharTraits<char>::format_float(
|
template void basic_fixed_buffer<char>::grow(std::size_t);
|
||||||
|
|
||||||
|
template void internal::arg_map<context>::init(const format_args &args);
|
||||||
|
|
||||||
|
template FMT_API int internal::char_traits<char>::format_float(
|
||||||
char *buffer, std::size_t size, const char *format,
|
char *buffer, std::size_t size, const char *format,
|
||||||
unsigned width, int precision, double value);
|
unsigned width, int precision, double value);
|
||||||
|
|
||||||
template FMT_API int internal::CharTraits<char>::format_float(
|
template FMT_API int internal::char_traits<char>::format_float(
|
||||||
char *buffer, std::size_t size, const char *format,
|
char *buffer, std::size_t size, const char *format,
|
||||||
unsigned width, int precision, long double value);
|
unsigned width, int precision, long double value);
|
||||||
|
|
||||||
// Explicit instantiations for wchar_t.
|
// Explicit instantiations for wchar_t.
|
||||||
|
|
||||||
template void internal::FixedBuffer<wchar_t>::grow(std::size_t);
|
template wchar_t internal::thousands_sep(locale_provider *lp);
|
||||||
|
|
||||||
template FMT_API int internal::CharTraits<wchar_t>::format_float(
|
template void basic_fixed_buffer<wchar_t>::grow(std::size_t);
|
||||||
|
|
||||||
|
template void internal::arg_map<wcontext>::init(const wformat_args &args);
|
||||||
|
|
||||||
|
template FMT_API int internal::char_traits<wchar_t>::format_float(
|
||||||
wchar_t *buffer, std::size_t size, const wchar_t *format,
|
wchar_t *buffer, std::size_t size, const wchar_t *format,
|
||||||
unsigned width, int precision, double value);
|
unsigned width, int precision, double value);
|
||||||
|
|
||||||
template FMT_API int internal::CharTraits<wchar_t>::format_float(
|
template FMT_API int internal::char_traits<wchar_t>::format_float(
|
||||||
wchar_t *buffer, std::size_t size, const wchar_t *format,
|
wchar_t *buffer, std::size_t size, const wchar_t *format,
|
||||||
unsigned width, int precision, long double value);
|
unsigned width, int precision, long double value);
|
||||||
|
|
3365
include/fmt/format.h
Normal file
3365
include/fmt/format.h
Normal file
File diff suppressed because it is too large
Load Diff
20
include/fmt/locale.h
Normal file
20
include/fmt/locale.h
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
// Formatting library for C++ - locale support
|
||||||
|
//
|
||||||
|
// Copyright (c) 2012 - 2016, Victor Zverovich
|
||||||
|
// All rights reserved.
|
||||||
|
//
|
||||||
|
// For the license information refer to format.h.
|
||||||
|
|
||||||
|
#include "format.h"
|
||||||
|
#include <locale>
|
||||||
|
|
||||||
|
namespace fmt {
|
||||||
|
class locale {
|
||||||
|
private:
|
||||||
|
std::locale locale_;
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit locale(std::locale loc = std::locale()) : locale_(loc) {}
|
||||||
|
std::locale get() { return locale_; }
|
||||||
|
};
|
||||||
|
}
|
143
include/fmt/ostream.h
Normal file
143
include/fmt/ostream.h
Normal file
@ -0,0 +1,143 @@
|
|||||||
|
// Formatting library for C++ - std::ostream support
|
||||||
|
//
|
||||||
|
// Copyright (c) 2012 - 2016, Victor Zverovich
|
||||||
|
// All rights reserved.
|
||||||
|
//
|
||||||
|
// For the license information refer to format.h.
|
||||||
|
|
||||||
|
#ifndef FMT_OSTREAM_H_
|
||||||
|
#define FMT_OSTREAM_H_
|
||||||
|
|
||||||
|
#include "format.h"
|
||||||
|
#include <ostream>
|
||||||
|
|
||||||
|
namespace fmt {
|
||||||
|
|
||||||
|
namespace internal {
|
||||||
|
|
||||||
|
template <class Char>
|
||||||
|
class FormatBuf : public std::basic_streambuf<Char> {
|
||||||
|
private:
|
||||||
|
typedef typename std::basic_streambuf<Char>::int_type int_type;
|
||||||
|
typedef typename std::basic_streambuf<Char>::traits_type traits_type;
|
||||||
|
|
||||||
|
basic_buffer<Char> &buffer_;
|
||||||
|
|
||||||
|
public:
|
||||||
|
FormatBuf(basic_buffer<Char> &buffer) : buffer_(buffer) {}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
// The put-area is actually always empty. This makes the implementation
|
||||||
|
// simpler and has the advantage that the streambuf and the buffer are always
|
||||||
|
// in sync and sputc never writes into uninitialized memory. The obvious
|
||||||
|
// disadvantage is that each call to sputc always results in a (virtual) call
|
||||||
|
// to overflow. There is no disadvantage here for sputn since this always
|
||||||
|
// results in a call to xsputn.
|
||||||
|
|
||||||
|
int_type overflow(int_type ch = traits_type::eof()) FMT_OVERRIDE {
|
||||||
|
if (!traits_type::eq_int_type(ch, traits_type::eof()))
|
||||||
|
buffer_.push_back(static_cast<Char>(ch));
|
||||||
|
return ch;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::streamsize xsputn(const Char *s, std::streamsize count) FMT_OVERRIDE {
|
||||||
|
buffer_.append(s, s + count);
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct test_stream : std::ostream {
|
||||||
|
private:
|
||||||
|
struct null;
|
||||||
|
// Hide all operator<< from std::ostream.
|
||||||
|
void operator<<(null);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Disable conversion to int if T has an overloaded operator<< which is a free
|
||||||
|
// function (not a member of std::ostream).
|
||||||
|
template <typename T>
|
||||||
|
class convert_to_int<T, true> {
|
||||||
|
private:
|
||||||
|
template <typename U>
|
||||||
|
static decltype(
|
||||||
|
std::declval<test_stream&>() << std::declval<U>(), std::true_type())
|
||||||
|
test(int);
|
||||||
|
|
||||||
|
template <typename>
|
||||||
|
static std::false_type test(...);
|
||||||
|
|
||||||
|
public:
|
||||||
|
static const bool value = !decltype(test<T>(0))::value;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Write the content of buf to os.
|
||||||
|
template <typename Char>
|
||||||
|
void write(std::basic_ostream<Char> &os, basic_buffer<Char> &buf) {
|
||||||
|
const Char *data = buf.data();
|
||||||
|
typedef std::make_unsigned<std::streamsize>::type UnsignedStreamSize;
|
||||||
|
UnsignedStreamSize size = buf.size();
|
||||||
|
UnsignedStreamSize max_size =
|
||||||
|
internal::to_unsigned((std::numeric_limits<std::streamsize>::max)());
|
||||||
|
do {
|
||||||
|
UnsignedStreamSize n = size <= max_size ? size : max_size;
|
||||||
|
os.write(data, static_cast<std::streamsize>(n));
|
||||||
|
data += n;
|
||||||
|
size -= n;
|
||||||
|
} while (size != 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Char, typename T>
|
||||||
|
void format_value(basic_buffer<Char> &buffer, const T &value) {
|
||||||
|
internal::FormatBuf<Char> format_buf(buffer);
|
||||||
|
std::basic_ostream<Char> output(&format_buf);
|
||||||
|
output.exceptions(std::ios_base::failbit | std::ios_base::badbit);
|
||||||
|
output << value;
|
||||||
|
buffer.resize(buffer.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Disable builtin formatting of enums and use operator<< instead.
|
||||||
|
template <typename T>
|
||||||
|
struct format_enum<T,
|
||||||
|
typename std::enable_if<std::is_enum<T>::value>::type> : std::false_type {};
|
||||||
|
} // namespace internal
|
||||||
|
|
||||||
|
// Formats an object of type T that has an overloaded ostream operator<<.
|
||||||
|
template <typename T, typename Char>
|
||||||
|
struct formatter<T, Char,
|
||||||
|
typename std::enable_if<
|
||||||
|
!internal::format_type<buffer_context_t<Char>, T>::value>::type>
|
||||||
|
: formatter<basic_string_view<Char>, Char> {
|
||||||
|
|
||||||
|
template <typename Context>
|
||||||
|
auto format(const T &value, Context &ctx) -> decltype(ctx.begin()) {
|
||||||
|
basic_memory_buffer<Char> buffer;
|
||||||
|
internal::format_value(buffer, value);
|
||||||
|
basic_string_view<Char> str(buffer.data(), buffer.size());
|
||||||
|
formatter<basic_string_view<Char>, Char>::format(str, ctx);
|
||||||
|
return ctx.begin();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
inline void vprint(std::ostream &os, string_view format_str, format_args args) {
|
||||||
|
memory_buffer buffer;
|
||||||
|
vformat_to(buffer, format_str, args);
|
||||||
|
internal::write(os, buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
\rst
|
||||||
|
Prints formatted data to the stream *os*.
|
||||||
|
|
||||||
|
**Example**::
|
||||||
|
|
||||||
|
print(cerr, "Don't {}!", "panic");
|
||||||
|
\endrst
|
||||||
|
*/
|
||||||
|
template <typename... Args>
|
||||||
|
inline void print(std::ostream &os, string_view format_str,
|
||||||
|
const Args & ... args) {
|
||||||
|
vprint(os, format_str, make_args(args...));
|
||||||
|
}
|
||||||
|
} // namespace fmt
|
||||||
|
|
||||||
|
#endif // FMT_OSTREAM_H_
|
@ -1,11 +1,9 @@
|
|||||||
/*
|
// A C++ interface to POSIX functions.
|
||||||
A C++ interface to POSIX functions.
|
//
|
||||||
|
// Copyright (c) 2012 - 2016, Victor Zverovich
|
||||||
Copyright (c) 2012 - 2016, Victor Zverovich
|
// All rights reserved.
|
||||||
All rights reserved.
|
//
|
||||||
|
// For the license information refer to format.h.
|
||||||
For the license information refer to format.h.
|
|
||||||
*/
|
|
||||||
|
|
||||||
// Disable bogus MSVC warnings.
|
// Disable bogus MSVC warnings.
|
||||||
#ifndef _CRT_SECURE_NO_WARNINGS
|
#ifndef _CRT_SECURE_NO_WARNINGS
|
||||||
@ -72,10 +70,10 @@ fmt::BufferedFile::~BufferedFile() FMT_NOEXCEPT {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fmt::BufferedFile::BufferedFile(
|
fmt::BufferedFile::BufferedFile(
|
||||||
fmt::CStringRef filename, fmt::CStringRef mode) {
|
fmt::cstring_view filename, fmt::cstring_view mode) {
|
||||||
FMT_RETRY_VAL(file_, FMT_SYSTEM(fopen(filename.c_str(), mode.c_str())), 0);
|
FMT_RETRY_VAL(file_, FMT_SYSTEM(fopen(filename.c_str(), mode.c_str())), 0);
|
||||||
if (!file_)
|
if (!file_)
|
||||||
FMT_THROW(SystemError(errno, "cannot open file {}", filename));
|
FMT_THROW(system_error(errno, "cannot open file {}", filename.c_str()));
|
||||||
}
|
}
|
||||||
|
|
||||||
void fmt::BufferedFile::close() {
|
void fmt::BufferedFile::close() {
|
||||||
@ -84,7 +82,7 @@ void fmt::BufferedFile::close() {
|
|||||||
int result = FMT_SYSTEM(fclose(file_));
|
int result = FMT_SYSTEM(fclose(file_));
|
||||||
file_ = FMT_NULL;
|
file_ = FMT_NULL;
|
||||||
if (result != 0)
|
if (result != 0)
|
||||||
FMT_THROW(SystemError(errno, "cannot close file"));
|
FMT_THROW(system_error(errno, "cannot close file"));
|
||||||
}
|
}
|
||||||
|
|
||||||
// A macro used to prevent expansion of fileno on broken versions of MinGW.
|
// A macro used to prevent expansion of fileno on broken versions of MinGW.
|
||||||
@ -93,11 +91,11 @@ void fmt::BufferedFile::close() {
|
|||||||
int fmt::BufferedFile::fileno() const {
|
int fmt::BufferedFile::fileno() const {
|
||||||
int fd = FMT_POSIX_CALL(fileno FMT_ARGS(file_));
|
int fd = FMT_POSIX_CALL(fileno FMT_ARGS(file_));
|
||||||
if (fd == -1)
|
if (fd == -1)
|
||||||
FMT_THROW(SystemError(errno, "cannot get file descriptor"));
|
FMT_THROW(system_error(errno, "cannot get file descriptor"));
|
||||||
return fd;
|
return fd;
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt::File::File(fmt::CStringRef path, int oflag) {
|
fmt::File::File(fmt::cstring_view path, int oflag) {
|
||||||
int mode = S_IRUSR | S_IWUSR;
|
int mode = S_IRUSR | S_IWUSR;
|
||||||
#if defined(_WIN32) && !defined(__MINGW32__)
|
#if defined(_WIN32) && !defined(__MINGW32__)
|
||||||
fd_ = -1;
|
fd_ = -1;
|
||||||
@ -106,7 +104,7 @@ fmt::File::File(fmt::CStringRef path, int oflag) {
|
|||||||
FMT_RETRY(fd_, FMT_POSIX_CALL(open(path.c_str(), oflag, mode)));
|
FMT_RETRY(fd_, FMT_POSIX_CALL(open(path.c_str(), oflag, mode)));
|
||||||
#endif
|
#endif
|
||||||
if (fd_ == -1)
|
if (fd_ == -1)
|
||||||
FMT_THROW(SystemError(errno, "cannot open file {}", path));
|
FMT_THROW(system_error(errno, "cannot open file {}", path.c_str()));
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt::File::~File() FMT_NOEXCEPT {
|
fmt::File::~File() FMT_NOEXCEPT {
|
||||||
@ -124,10 +122,10 @@ void fmt::File::close() {
|
|||||||
int result = FMT_POSIX_CALL(close(fd_));
|
int result = FMT_POSIX_CALL(close(fd_));
|
||||||
fd_ = -1;
|
fd_ = -1;
|
||||||
if (result != 0)
|
if (result != 0)
|
||||||
FMT_THROW(SystemError(errno, "cannot close file"));
|
FMT_THROW(system_error(errno, "cannot close file"));
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt::LongLong fmt::File::size() const {
|
long long fmt::File::size() const {
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
// Use GetFileSize instead of GetFileSizeEx for the case when _WIN32_WINNT
|
// Use GetFileSize instead of GetFileSizeEx for the case when _WIN32_WINNT
|
||||||
// is less than 0x0500 as is the case with some default MinGW builds.
|
// is less than 0x0500 as is the case with some default MinGW builds.
|
||||||
@ -138,16 +136,16 @@ fmt::LongLong fmt::File::size() const {
|
|||||||
if (size_lower == INVALID_FILE_SIZE) {
|
if (size_lower == INVALID_FILE_SIZE) {
|
||||||
DWORD error = GetLastError();
|
DWORD error = GetLastError();
|
||||||
if (error != NO_ERROR)
|
if (error != NO_ERROR)
|
||||||
FMT_THROW(WindowsError(GetLastError(), "cannot get file size"));
|
FMT_THROW(windows_error(GetLastError(), "cannot get file size"));
|
||||||
}
|
}
|
||||||
fmt::ULongLong long_size = size_upper;
|
unsigned long long long_size = size_upper;
|
||||||
return (long_size << sizeof(DWORD) * CHAR_BIT) | size_lower;
|
return (long_size << sizeof(DWORD) * CHAR_BIT) | size_lower;
|
||||||
#else
|
#else
|
||||||
typedef struct stat Stat;
|
typedef struct stat 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(SystemError(errno, "cannot get file attributes"));
|
FMT_THROW(system_error(errno, "cannot get file attributes"));
|
||||||
FMT_STATIC_ASSERT(sizeof(fmt::LongLong) >= 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;
|
||||||
#endif
|
#endif
|
||||||
@ -157,7 +155,7 @@ std::size_t fmt::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)
|
if (result < 0)
|
||||||
FMT_THROW(SystemError(errno, "cannot read from file"));
|
FMT_THROW(system_error(errno, "cannot read from file"));
|
||||||
return internal::to_unsigned(result);
|
return internal::to_unsigned(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -165,7 +163,7 @@ std::size_t fmt::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)
|
if (result < 0)
|
||||||
FMT_THROW(SystemError(errno, "cannot write to file"));
|
FMT_THROW(system_error(errno, "cannot write to file"));
|
||||||
return internal::to_unsigned(result);
|
return internal::to_unsigned(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -174,7 +172,7 @@ fmt::File fmt::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(SystemError(errno, "cannot duplicate file descriptor {}", fd));
|
FMT_THROW(system_error(errno, "cannot duplicate file descriptor {}", fd));
|
||||||
return File(new_fd);
|
return File(new_fd);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -182,8 +180,8 @@ void fmt::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(SystemError(errno,
|
throw system_error(errno,
|
||||||
"cannot duplicate file descriptor {} to {}", fd_, fd));
|
"cannot duplicate file descriptor {} to {}", fd_, fd);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -210,7 +208,7 @@ void fmt::File::pipe(File &read_end, File &write_end) {
|
|||||||
int result = FMT_POSIX_CALL(pipe(fds));
|
int result = FMT_POSIX_CALL(pipe(fds));
|
||||||
#endif
|
#endif
|
||||||
if (result != 0)
|
if (result != 0)
|
||||||
FMT_THROW(SystemError(errno, "cannot create pipe"));
|
FMT_THROW(system_error(errno, "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]);
|
||||||
@ -221,7 +219,8 @@ fmt::BufferedFile fmt::File::fdopen(const char *mode) {
|
|||||||
// Don't retry as fdopen doesn't return EINTR.
|
// Don't retry as fdopen doesn't return EINTR.
|
||||||
FILE *f = FMT_POSIX_CALL(fdopen(fd_, mode));
|
FILE *f = FMT_POSIX_CALL(fdopen(fd_, mode));
|
||||||
if (!f)
|
if (!f)
|
||||||
FMT_THROW(SystemError(errno, "cannot associate stream with file descriptor"));
|
FMT_THROW(system_error(errno,
|
||||||
|
"cannot associate stream with file descriptor"));
|
||||||
BufferedFile file(f);
|
BufferedFile file(f);
|
||||||
fd_ = -1;
|
fd_ = -1;
|
||||||
return file;
|
return file;
|
||||||
@ -235,7 +234,7 @@ long fmt::getpagesize() {
|
|||||||
#else
|
#else
|
||||||
long size = FMT_POSIX_CALL(sysconf(_SC_PAGESIZE));
|
long size = FMT_POSIX_CALL(sysconf(_SC_PAGESIZE));
|
||||||
if (size < 0)
|
if (size < 0)
|
||||||
FMT_THROW(SystemError(errno, "cannot get memory page size"));
|
FMT_THROW(system_error(errno, "cannot get memory page size"));
|
||||||
return size;
|
return size;
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
@ -1,11 +1,9 @@
|
|||||||
/*
|
// A C++ interface to POSIX functions.
|
||||||
A C++ interface to POSIX functions.
|
//
|
||||||
|
// Copyright (c) 2012 - 2016, Victor Zverovich
|
||||||
Copyright (c) 2012 - 2016, Victor Zverovich
|
// All rights reserved.
|
||||||
All rights reserved.
|
//
|
||||||
|
// For the license information refer to format.h.
|
||||||
For the license information refer to format.h.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef FMT_POSIX_H_
|
#ifndef FMT_POSIX_H_
|
||||||
#define FMT_POSIX_H_
|
#define FMT_POSIX_H_
|
||||||
@ -66,6 +64,54 @@
|
|||||||
|
|
||||||
namespace fmt {
|
namespace fmt {
|
||||||
|
|
||||||
|
/**
|
||||||
|
\rst
|
||||||
|
A reference to a null-terminated string. It can be constructed from a C
|
||||||
|
string or ``std::string``.
|
||||||
|
|
||||||
|
You can use one of the following typedefs for common character types:
|
||||||
|
|
||||||
|
+---------------+-----------------------------+
|
||||||
|
| Type | Definition |
|
||||||
|
+===============+=============================+
|
||||||
|
| cstring_view | basic_cstring_view<char> |
|
||||||
|
+---------------+-----------------------------+
|
||||||
|
| wcstring_view | basic_cstring_view<wchar_t> |
|
||||||
|
+---------------+-----------------------------+
|
||||||
|
|
||||||
|
This class is most useful as a parameter type to allow passing
|
||||||
|
different types of strings to a function, for example::
|
||||||
|
|
||||||
|
template <typename... Args>
|
||||||
|
std::string format(cstring_view format_str, const Args & ... args);
|
||||||
|
|
||||||
|
format("{}", 42);
|
||||||
|
format(std::string("{}"), 42);
|
||||||
|
\endrst
|
||||||
|
*/
|
||||||
|
template <typename Char>
|
||||||
|
class basic_cstring_view {
|
||||||
|
private:
|
||||||
|
const Char *data_;
|
||||||
|
|
||||||
|
public:
|
||||||
|
/** Constructs a string reference object from a C string. */
|
||||||
|
basic_cstring_view(const Char *s) : data_(s) {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
\rst
|
||||||
|
Constructs a string reference from an ``std::string`` object.
|
||||||
|
\endrst
|
||||||
|
*/
|
||||||
|
basic_cstring_view(const std::basic_string<Char> &s) : data_(s.c_str()) {}
|
||||||
|
|
||||||
|
/** Returns the pointer to a C string. */
|
||||||
|
const Char *c_str() const { return data_; }
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef basic_cstring_view<char> cstring_view;
|
||||||
|
typedef basic_cstring_view<wchar_t> wcstring_view;
|
||||||
|
|
||||||
// An error code.
|
// An error code.
|
||||||
class ErrorCode {
|
class ErrorCode {
|
||||||
private:
|
private:
|
||||||
@ -88,10 +134,10 @@ class BufferedFile {
|
|||||||
|
|
||||||
public:
|
public:
|
||||||
// Constructs a BufferedFile object which doesn't represent any file.
|
// Constructs a BufferedFile object which doesn't represent any file.
|
||||||
BufferedFile() FMT_NOEXCEPT : file_(FMT_NULL) {}
|
BufferedFile() FMT_NOEXCEPT : file_(0) {}
|
||||||
|
|
||||||
// Destroys the object closing the file it represents if any.
|
// Destroys the object closing the file it represents if any.
|
||||||
FMT_API ~BufferedFile() FMT_NOEXCEPT;
|
FMT_API ~BufferedFile() FMT_DTOR_NOEXCEPT;
|
||||||
|
|
||||||
#if !FMT_USE_RVALUE_REFERENCES
|
#if !FMT_USE_RVALUE_REFERENCES
|
||||||
// Emulate a move constructor and a move assignment operator if rvalue
|
// Emulate a move constructor and a move assignment operator if rvalue
|
||||||
@ -154,7 +200,7 @@ public:
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Opens a file.
|
// Opens a file.
|
||||||
FMT_API BufferedFile(CStringRef filename, CStringRef mode);
|
FMT_API BufferedFile(cstring_view filename, cstring_view mode);
|
||||||
|
|
||||||
// Closes the file.
|
// Closes the file.
|
||||||
FMT_API void close();
|
FMT_API void close();
|
||||||
@ -166,15 +212,19 @@ public:
|
|||||||
// of MinGW that define fileno as a macro.
|
// of MinGW that define fileno as a macro.
|
||||||
FMT_API int (fileno)() const;
|
FMT_API int (fileno)() const;
|
||||||
|
|
||||||
void print(CStringRef format_str, const ArgList &args) {
|
void vprint(string_view format_str, format_args args) {
|
||||||
fmt::print(file_, format_str, args);
|
fmt::vprint(file_, format_str, args);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename... Args>
|
||||||
|
inline void print(string_view format_str, const Args & ... args) {
|
||||||
|
vprint(format_str, make_args(args...));
|
||||||
}
|
}
|
||||||
FMT_VARIADIC(void, print, CStringRef)
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// A file. Closed file is represented by a File object with descriptor -1.
|
// A file. Closed file is represented by a File object with descriptor -1.
|
||||||
// Methods that are not declared with FMT_NOEXCEPT may throw
|
// Methods that are not declared with FMT_NOEXCEPT may throw
|
||||||
// fmt::SystemError in case of failure. Note that some errors such as
|
// fmt::system_error in case of failure. Note that some errors such as
|
||||||
// closing the file multiple times will cause a crash on Windows rather
|
// closing the file multiple times will cause a crash on Windows rather
|
||||||
// than an exception. You can get standard behavior by overriding the
|
// than an exception. You can get standard behavior by overriding the
|
||||||
// invalid parameter handler with _set_invalid_parameter_handler.
|
// invalid parameter handler with _set_invalid_parameter_handler.
|
||||||
@ -197,7 +247,7 @@ class File {
|
|||||||
File() FMT_NOEXCEPT : fd_(-1) {}
|
File() FMT_NOEXCEPT : fd_(-1) {}
|
||||||
|
|
||||||
// Opens a file and constructs a File object representing this file.
|
// Opens a file and constructs a File object representing this file.
|
||||||
FMT_API File(CStringRef path, int oflag);
|
FMT_API File(cstring_view path, int oflag);
|
||||||
|
|
||||||
#if !FMT_USE_RVALUE_REFERENCES
|
#if !FMT_USE_RVALUE_REFERENCES
|
||||||
// Emulate a move constructor and a move assignment operator if rvalue
|
// Emulate a move constructor and a move assignment operator if rvalue
|
||||||
@ -260,7 +310,7 @@ class File {
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Destroys the object closing the file it represents if any.
|
// Destroys the object closing the file it represents if any.
|
||||||
FMT_API ~File() FMT_NOEXCEPT;
|
FMT_API ~File() FMT_DTOR_NOEXCEPT;
|
||||||
|
|
||||||
// Returns the file descriptor.
|
// Returns the file descriptor.
|
||||||
int descriptor() const FMT_NOEXCEPT { return fd_; }
|
int descriptor() const FMT_NOEXCEPT { return fd_; }
|
||||||
@ -270,7 +320,7 @@ class File {
|
|||||||
|
|
||||||
// Returns the file size. The size has signed type for consistency with
|
// Returns the file size. The size has signed type for consistency with
|
||||||
// stat::st_size.
|
// stat::st_size.
|
||||||
FMT_API LongLong size() const;
|
FMT_API long long size() const;
|
||||||
|
|
||||||
// Attempts to read count bytes from the file into the specified buffer.
|
// Attempts to read count bytes from the file into the specified buffer.
|
||||||
FMT_API std::size_t read(void *buffer, std::size_t count);
|
FMT_API std::size_t read(void *buffer, std::size_t count);
|
||||||
@ -338,7 +388,7 @@ class Locale {
|
|||||||
|
|
||||||
Locale() : locale_(newlocale(LC_NUMERIC_MASK, "C", FMT_NULL)) {
|
Locale() : locale_(newlocale(LC_NUMERIC_MASK, "C", FMT_NULL)) {
|
||||||
if (!locale_)
|
if (!locale_)
|
||||||
FMT_THROW(fmt::SystemError(errno, "cannot create locale"));
|
FMT_THROW(fmt::system_error(errno, "cannot create locale"));
|
||||||
}
|
}
|
||||||
~Locale() { freelocale(locale_); }
|
~Locale() { freelocale(locale_); }
|
||||||
|
|
644
include/fmt/printf.h
Normal file
644
include/fmt/printf.h
Normal file
@ -0,0 +1,644 @@
|
|||||||
|
// Formatting library for C++
|
||||||
|
//
|
||||||
|
// Copyright (c) 2012 - 2016, Victor Zverovich
|
||||||
|
// All rights reserved.
|
||||||
|
//
|
||||||
|
// For the license information refer to format.h.
|
||||||
|
|
||||||
|
#ifndef FMT_PRINTF_H_
|
||||||
|
#define FMT_PRINTF_H_
|
||||||
|
|
||||||
|
#include <algorithm> // std::fill_n
|
||||||
|
#include <limits> // std::numeric_limits
|
||||||
|
|
||||||
|
#include "ostream.h"
|
||||||
|
|
||||||
|
namespace fmt {
|
||||||
|
namespace internal {
|
||||||
|
|
||||||
|
// Checks if a value fits in int - used to avoid warnings about comparing
|
||||||
|
// signed and unsigned integers.
|
||||||
|
template <bool IsSigned>
|
||||||
|
struct IntChecker {
|
||||||
|
template <typename T>
|
||||||
|
static bool fits_in_int(T value) {
|
||||||
|
unsigned max = std::numeric_limits<int>::max();
|
||||||
|
return value <= max;
|
||||||
|
}
|
||||||
|
static bool fits_in_int(bool) { return true; }
|
||||||
|
};
|
||||||
|
|
||||||
|
template <>
|
||||||
|
struct IntChecker<true> {
|
||||||
|
template <typename T>
|
||||||
|
static bool fits_in_int(T value) {
|
||||||
|
return value >= std::numeric_limits<int>::min() &&
|
||||||
|
value <= std::numeric_limits<int>::max();
|
||||||
|
}
|
||||||
|
static bool fits_in_int(int) { return true; }
|
||||||
|
};
|
||||||
|
|
||||||
|
class PrintfPrecisionHandler {
|
||||||
|
public:
|
||||||
|
template <typename T>
|
||||||
|
typename std::enable_if<std::is_integral<T>::value, int>::type
|
||||||
|
operator()(T value) {
|
||||||
|
if (!IntChecker<std::numeric_limits<T>::is_signed>::fits_in_int(value))
|
||||||
|
FMT_THROW(format_error("number is too big"));
|
||||||
|
return static_cast<int>(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
typename std::enable_if<!std::is_integral<T>::value, int>::type
|
||||||
|
operator()(T) {
|
||||||
|
FMT_THROW(format_error("precision is not integer"));
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// An argument visitor that returns true iff arg is a zero integer.
|
||||||
|
class IsZeroInt {
|
||||||
|
public:
|
||||||
|
template <typename T>
|
||||||
|
typename std::enable_if<std::is_integral<T>::value, bool>::type
|
||||||
|
operator()(T value) { return value == 0; }
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
typename std::enable_if<!std::is_integral<T>::value, bool>::type
|
||||||
|
operator()(T) { return false; }
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
struct make_unsigned_or_bool : std::make_unsigned<T> {};
|
||||||
|
|
||||||
|
template <>
|
||||||
|
struct make_unsigned_or_bool<bool> {
|
||||||
|
using type = bool;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T, typename Context>
|
||||||
|
class ArgConverter {
|
||||||
|
private:
|
||||||
|
typedef typename Context::char_type Char;
|
||||||
|
|
||||||
|
basic_arg<Context> &arg_;
|
||||||
|
typename Context::char_type type_;
|
||||||
|
|
||||||
|
public:
|
||||||
|
ArgConverter(basic_arg<Context> &arg, Char type)
|
||||||
|
: arg_(arg), type_(type) {}
|
||||||
|
|
||||||
|
void operator()(bool value) {
|
||||||
|
if (type_ != 's')
|
||||||
|
operator()<bool>(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename U>
|
||||||
|
typename std::enable_if<std::is_integral<U>::value>::type
|
||||||
|
operator()(U value) {
|
||||||
|
bool is_signed = type_ == 'd' || type_ == 'i';
|
||||||
|
typedef typename std::conditional<
|
||||||
|
std::is_same<T, void>::value, U, T>::type TargetType;
|
||||||
|
if (const_check(sizeof(TargetType) <= sizeof(int))) {
|
||||||
|
// Extra casts are used to silence warnings.
|
||||||
|
if (is_signed) {
|
||||||
|
arg_ = internal::make_arg<Context>(
|
||||||
|
static_cast<int>(static_cast<TargetType>(value)));
|
||||||
|
} else {
|
||||||
|
typedef typename make_unsigned_or_bool<TargetType>::type Unsigned;
|
||||||
|
arg_ = internal::make_arg<Context>(
|
||||||
|
static_cast<unsigned>(static_cast<Unsigned>(value)));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (is_signed) {
|
||||||
|
// glibc's printf doesn't sign extend arguments of smaller types:
|
||||||
|
// std::printf("%lld", -42); // prints "4294967254"
|
||||||
|
// but we don't have to do the same because it's a UB.
|
||||||
|
arg_ = internal::make_arg<Context>(static_cast<long long>(value));
|
||||||
|
} else {
|
||||||
|
arg_ = internal::make_arg<Context>(
|
||||||
|
static_cast<typename make_unsigned_or_bool<U>::type>(value));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename U>
|
||||||
|
typename std::enable_if<!std::is_integral<U>::value>::type operator()(U) {
|
||||||
|
// No coversion needed for non-integral types.
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Converts an integer argument to T for printf, if T is an integral type.
|
||||||
|
// If T is void, the argument is converted to corresponding signed or unsigned
|
||||||
|
// type depending on the type specifier: 'd' and 'i' - signed, other -
|
||||||
|
// unsigned).
|
||||||
|
template <typename T, typename Context, typename Char>
|
||||||
|
void convert_arg(basic_arg<Context> &arg, Char type) {
|
||||||
|
visit(ArgConverter<T, Context>(arg, type), arg);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Converts an integer argument to char for printf.
|
||||||
|
template <typename Context>
|
||||||
|
class CharConverter {
|
||||||
|
private:
|
||||||
|
basic_arg<Context> &arg_;
|
||||||
|
|
||||||
|
FMT_DISALLOW_COPY_AND_ASSIGN(CharConverter);
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit CharConverter(basic_arg<Context> &arg) : arg_(arg) {}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
typename std::enable_if<std::is_integral<T>::value>::type
|
||||||
|
operator()(T value) {
|
||||||
|
arg_ = internal::make_arg<Context>(static_cast<char>(value));
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
typename std::enable_if<!std::is_integral<T>::value>::type operator()(T) {
|
||||||
|
// No coversion needed for non-integral types.
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Checks if an argument is a valid printf width specifier and sets
|
||||||
|
// left alignment if it is negative.
|
||||||
|
template <typename Char>
|
||||||
|
class PrintfWidthHandler {
|
||||||
|
private:
|
||||||
|
typedef basic_format_specs<Char> format_specs;
|
||||||
|
|
||||||
|
format_specs &spec_;
|
||||||
|
|
||||||
|
FMT_DISALLOW_COPY_AND_ASSIGN(PrintfWidthHandler);
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit PrintfWidthHandler(format_specs &spec) : spec_(spec) {}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
typename std::enable_if<std::is_integral<T>::value, unsigned>::type
|
||||||
|
operator()(T value) {
|
||||||
|
typedef typename internal::int_traits<T>::main_type UnsignedType;
|
||||||
|
UnsignedType width = static_cast<UnsignedType>(value);
|
||||||
|
if (internal::is_negative(value)) {
|
||||||
|
spec_.align_ = ALIGN_LEFT;
|
||||||
|
width = 0 - width;
|
||||||
|
}
|
||||||
|
unsigned int_max = std::numeric_limits<int>::max();
|
||||||
|
if (width > int_max)
|
||||||
|
FMT_THROW(format_error("number is too big"));
|
||||||
|
return static_cast<unsigned>(width);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
typename std::enable_if<!std::is_integral<T>::value, unsigned>::type
|
||||||
|
operator()(T) {
|
||||||
|
FMT_THROW(format_error("width is not integer"));
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
} // namespace internal
|
||||||
|
|
||||||
|
template <typename Range>
|
||||||
|
class printf_arg_formatter;
|
||||||
|
|
||||||
|
template <
|
||||||
|
typename OutputIt, typename Char,
|
||||||
|
typename ArgFormatter =
|
||||||
|
printf_arg_formatter<back_insert_range<internal::basic_buffer<Char>>>>
|
||||||
|
class basic_printf_context;
|
||||||
|
|
||||||
|
/**
|
||||||
|
\rst
|
||||||
|
The ``printf`` argument formatter.
|
||||||
|
\endrst
|
||||||
|
*/
|
||||||
|
template <typename Range>
|
||||||
|
class printf_arg_formatter : public internal::arg_formatter_base<Range> {
|
||||||
|
private:
|
||||||
|
using char_type = typename Range::value_type;
|
||||||
|
using iterator = decltype(std::declval<Range>().begin());
|
||||||
|
using base = internal::arg_formatter_base<Range>;
|
||||||
|
using context_type = basic_printf_context<iterator, char_type>;
|
||||||
|
|
||||||
|
context_type &context_;
|
||||||
|
|
||||||
|
void write_null_pointer() {
|
||||||
|
this->spec().type_ = 0;
|
||||||
|
this->write("(nil)");
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
using format_specs = typename base::format_specs;
|
||||||
|
|
||||||
|
/**
|
||||||
|
\rst
|
||||||
|
Constructs an argument formatter object.
|
||||||
|
*buffer* is a reference to the output buffer and *spec* contains format
|
||||||
|
specifier information for standard argument types.
|
||||||
|
\endrst
|
||||||
|
*/
|
||||||
|
printf_arg_formatter(internal::basic_buffer<char_type> &buffer,
|
||||||
|
format_specs &spec, context_type &ctx)
|
||||||
|
: base(back_insert_range<internal::basic_buffer<char_type>>(buffer), spec),
|
||||||
|
context_(ctx) {}
|
||||||
|
|
||||||
|
using base::operator();
|
||||||
|
|
||||||
|
/** Formats an argument of type ``bool``. */
|
||||||
|
void operator()(bool value) {
|
||||||
|
format_specs &fmt_spec = this->spec();
|
||||||
|
if (fmt_spec.type_ != 's')
|
||||||
|
return (*this)(value ? 1 : 0);
|
||||||
|
fmt_spec.type_ = 0;
|
||||||
|
this->write(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Formats a character. */
|
||||||
|
void operator()(char_type value) {
|
||||||
|
format_specs &fmt_spec = this->spec();
|
||||||
|
if (fmt_spec.type_ && fmt_spec.type_ != 'c')
|
||||||
|
return (*this)(static_cast<int>(value));
|
||||||
|
fmt_spec.flags_ = 0;
|
||||||
|
fmt_spec.align_ = ALIGN_RIGHT;
|
||||||
|
base::operator()(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Formats a null-terminated C string. */
|
||||||
|
void operator()(const char *value) {
|
||||||
|
if (value)
|
||||||
|
base::operator()(value);
|
||||||
|
else if (this->spec().type_ == 'p')
|
||||||
|
write_null_pointer();
|
||||||
|
else
|
||||||
|
this->write("(null)");
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Formats a pointer. */
|
||||||
|
void operator()(const void *value) {
|
||||||
|
if (value)
|
||||||
|
return base::operator()(value);
|
||||||
|
this->spec().type_ = 0;
|
||||||
|
write_null_pointer();
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Formats an argument of a custom (user-defined) type. */
|
||||||
|
void operator()(typename basic_arg<context_type>::handle handle) {
|
||||||
|
handle.format(context_);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
struct printf_formatter {
|
||||||
|
template <typename ParseContext>
|
||||||
|
auto parse(ParseContext &ctx) { return ctx.begin(); }
|
||||||
|
|
||||||
|
template <typename FormatContext>
|
||||||
|
auto format(const T &value, FormatContext &ctx) -> decltype(ctx.begin()) {
|
||||||
|
internal::format_value(internal::get_container(ctx.begin()), value);
|
||||||
|
return ctx.begin();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/** This template formats data and writes the output to a writer. */
|
||||||
|
template <typename OutputIt, typename Char, typename ArgFormatter>
|
||||||
|
class basic_printf_context :
|
||||||
|
private internal::context_base<
|
||||||
|
OutputIt, basic_printf_context<OutputIt, Char, ArgFormatter>, Char> {
|
||||||
|
public:
|
||||||
|
/** The character type for the output. */
|
||||||
|
using char_type = Char;
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
using formatter_type = printf_formatter<T>;
|
||||||
|
|
||||||
|
private:
|
||||||
|
using base = internal::context_base<OutputIt, basic_printf_context, Char>;
|
||||||
|
using format_arg = typename base::format_arg;
|
||||||
|
using format_specs = basic_format_specs<char_type>;
|
||||||
|
using iterator = internal::null_terminating_iterator<char_type>;
|
||||||
|
|
||||||
|
void parse_flags(format_specs &spec, iterator &it);
|
||||||
|
|
||||||
|
// Returns the argument with specified index or, if arg_index is equal
|
||||||
|
// to the maximum unsigned value, the next argument.
|
||||||
|
format_arg get_arg(
|
||||||
|
iterator it,
|
||||||
|
unsigned arg_index = (std::numeric_limits<unsigned>::max)());
|
||||||
|
|
||||||
|
// Parses argument index, flags and width and returns the argument index.
|
||||||
|
unsigned parse_header(iterator &it, format_specs &spec);
|
||||||
|
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
\rst
|
||||||
|
Constructs a ``printf_context`` object. References to the arguments and
|
||||||
|
the writer are stored in the context object so make sure they have
|
||||||
|
appropriate lifetimes.
|
||||||
|
\endrst
|
||||||
|
*/
|
||||||
|
basic_printf_context(OutputIt out, basic_string_view<char_type> format_str,
|
||||||
|
basic_format_args<basic_printf_context> args)
|
||||||
|
: base(out, format_str, args) {}
|
||||||
|
|
||||||
|
using base::parse_context;
|
||||||
|
using base::begin;
|
||||||
|
using base::advance_to;
|
||||||
|
|
||||||
|
/** Formats stored arguments and writes the output to the range. */
|
||||||
|
void format();
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename OutputIt, typename Char, typename AF>
|
||||||
|
void basic_printf_context<OutputIt, Char, AF>::parse_flags(
|
||||||
|
format_specs &spec, iterator &it) {
|
||||||
|
for (;;) {
|
||||||
|
switch (*it++) {
|
||||||
|
case '-':
|
||||||
|
spec.align_ = ALIGN_LEFT;
|
||||||
|
break;
|
||||||
|
case '+':
|
||||||
|
spec.flags_ |= SIGN_FLAG | PLUS_FLAG;
|
||||||
|
break;
|
||||||
|
case '0':
|
||||||
|
spec.fill_ = '0';
|
||||||
|
break;
|
||||||
|
case ' ':
|
||||||
|
spec.flags_ |= SIGN_FLAG;
|
||||||
|
break;
|
||||||
|
case '#':
|
||||||
|
spec.flags_ |= HASH_FLAG;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
--it;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename OutputIt, typename Char, typename AF>
|
||||||
|
typename basic_printf_context<OutputIt, Char, AF>::format_arg
|
||||||
|
basic_printf_context<OutputIt, Char, AF>::get_arg(
|
||||||
|
iterator it, unsigned arg_index) {
|
||||||
|
(void)it;
|
||||||
|
if (arg_index == std::numeric_limits<unsigned>::max())
|
||||||
|
return this->do_get_arg(this->parse_context().next_arg_id());
|
||||||
|
return base::get_arg(arg_index - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename OutputIt, typename Char, typename AF>
|
||||||
|
unsigned basic_printf_context<OutputIt, Char, AF>::parse_header(
|
||||||
|
iterator &it, format_specs &spec) {
|
||||||
|
unsigned arg_index = std::numeric_limits<unsigned>::max();
|
||||||
|
char_type c = *it;
|
||||||
|
if (c >= '0' && c <= '9') {
|
||||||
|
// Parse an argument index (if followed by '$') or a width possibly
|
||||||
|
// preceded with '0' flag(s).
|
||||||
|
internal::error_handler eh;
|
||||||
|
unsigned value = parse_nonnegative_int(it, eh);
|
||||||
|
if (*it == '$') { // value is an argument index
|
||||||
|
++it;
|
||||||
|
arg_index = value;
|
||||||
|
} else {
|
||||||
|
if (c == '0')
|
||||||
|
spec.fill_ = '0';
|
||||||
|
if (value != 0) {
|
||||||
|
// Nonzero value means that we parsed width and don't need to
|
||||||
|
// parse it or flags again, so return now.
|
||||||
|
spec.width_ = value;
|
||||||
|
return arg_index;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
parse_flags(spec, it);
|
||||||
|
// Parse width.
|
||||||
|
if (*it >= '0' && *it <= '9') {
|
||||||
|
internal::error_handler eh;
|
||||||
|
spec.width_ = parse_nonnegative_int(it, eh);
|
||||||
|
} else if (*it == '*') {
|
||||||
|
++it;
|
||||||
|
spec.width_ =
|
||||||
|
visit(internal::PrintfWidthHandler<char_type>(spec), get_arg(it));
|
||||||
|
}
|
||||||
|
return arg_index;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename OutputIt, typename Char, typename AF>
|
||||||
|
void basic_printf_context<OutputIt, Char, AF>::format() {
|
||||||
|
auto &buffer = internal::get_container(this->begin());
|
||||||
|
auto start = iterator(this->parse_context());
|
||||||
|
auto it = start;
|
||||||
|
using internal::pointer_from;
|
||||||
|
while (*it) {
|
||||||
|
char_type c = *it++;
|
||||||
|
if (c != '%') continue;
|
||||||
|
if (*it == c) {
|
||||||
|
buffer.append(pointer_from(start), pointer_from(it));
|
||||||
|
start = ++it;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
buffer.append(pointer_from(start), pointer_from(it) - 1);
|
||||||
|
|
||||||
|
format_specs spec;
|
||||||
|
spec.align_ = ALIGN_RIGHT;
|
||||||
|
|
||||||
|
// Parse argument index, flags and width.
|
||||||
|
unsigned arg_index = parse_header(it, spec);
|
||||||
|
|
||||||
|
// Parse precision.
|
||||||
|
if (*it == '.') {
|
||||||
|
++it;
|
||||||
|
if ('0' <= *it && *it <= '9') {
|
||||||
|
internal::error_handler eh;
|
||||||
|
spec.precision_ = static_cast<int>(parse_nonnegative_int(it, eh));
|
||||||
|
} else if (*it == '*') {
|
||||||
|
++it;
|
||||||
|
spec.precision_ =
|
||||||
|
visit(internal::PrintfPrecisionHandler(), get_arg(it));
|
||||||
|
} else {
|
||||||
|
spec.precision_ = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
format_arg arg = get_arg(it, arg_index);
|
||||||
|
if (spec.flag(HASH_FLAG) && visit(internal::IsZeroInt(), arg))
|
||||||
|
spec.flags_ &= ~internal::to_unsigned<int>(HASH_FLAG);
|
||||||
|
if (spec.fill_ == '0') {
|
||||||
|
if (arg.is_arithmetic())
|
||||||
|
spec.align_ = ALIGN_NUMERIC;
|
||||||
|
else
|
||||||
|
spec.fill_ = ' '; // Ignore '0' flag for non-numeric types.
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse length and convert the argument to the required type.
|
||||||
|
using internal::convert_arg;
|
||||||
|
switch (*it++) {
|
||||||
|
case 'h':
|
||||||
|
if (*it == 'h')
|
||||||
|
convert_arg<signed char>(arg, *++it);
|
||||||
|
else
|
||||||
|
convert_arg<short>(arg, *it);
|
||||||
|
break;
|
||||||
|
case 'l':
|
||||||
|
if (*it == 'l')
|
||||||
|
convert_arg<long long>(arg, *++it);
|
||||||
|
else
|
||||||
|
convert_arg<long>(arg, *it);
|
||||||
|
break;
|
||||||
|
case 'j':
|
||||||
|
convert_arg<intmax_t>(arg, *it);
|
||||||
|
break;
|
||||||
|
case 'z':
|
||||||
|
convert_arg<std::size_t>(arg, *it);
|
||||||
|
break;
|
||||||
|
case 't':
|
||||||
|
convert_arg<std::ptrdiff_t>(arg, *it);
|
||||||
|
break;
|
||||||
|
case 'L':
|
||||||
|
// printf produces garbage when 'L' is omitted for long double, no
|
||||||
|
// need to do the same.
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
--it;
|
||||||
|
convert_arg<void>(arg, *it);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse type.
|
||||||
|
if (!*it)
|
||||||
|
FMT_THROW(format_error("invalid format string"));
|
||||||
|
spec.type_ = static_cast<char>(*it++);
|
||||||
|
if (arg.is_integral()) {
|
||||||
|
// Normalize type.
|
||||||
|
switch (spec.type_) {
|
||||||
|
case 'i': case 'u':
|
||||||
|
spec.type_ = 'd';
|
||||||
|
break;
|
||||||
|
case 'c':
|
||||||
|
// TODO: handle wchar_t
|
||||||
|
visit(internal::CharConverter<basic_printf_context>(arg), arg);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
start = it;
|
||||||
|
|
||||||
|
// Format argument.
|
||||||
|
visit(AF(buffer, spec, *this), arg);
|
||||||
|
}
|
||||||
|
buffer.append(pointer_from(start), pointer_from(it));
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Char, typename Context>
|
||||||
|
void printf(internal::basic_buffer<Char> &buf, basic_string_view<Char> format,
|
||||||
|
basic_format_args<Context> args) {
|
||||||
|
Context(std::back_inserter(buf), format, args).format();
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Buffer>
|
||||||
|
using printf_context = basic_printf_context<
|
||||||
|
std::back_insert_iterator<Buffer>, typename Buffer::value_type>;
|
||||||
|
|
||||||
|
using printf_args = basic_format_args<printf_context<internal::buffer>>;
|
||||||
|
|
||||||
|
inline std::string vsprintf(string_view format, printf_args args) {
|
||||||
|
memory_buffer buffer;
|
||||||
|
printf(buffer, format, args);
|
||||||
|
return to_string(buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
\rst
|
||||||
|
Formats arguments and returns the result as a string.
|
||||||
|
|
||||||
|
**Example**::
|
||||||
|
|
||||||
|
std::string message = fmt::sprintf("The answer is %d", 42);
|
||||||
|
\endrst
|
||||||
|
*/
|
||||||
|
template <typename... Args>
|
||||||
|
inline std::string sprintf(string_view format_str, const Args & ... args) {
|
||||||
|
return vsprintf(format_str,
|
||||||
|
make_args<printf_context<internal::buffer>>(args...));
|
||||||
|
}
|
||||||
|
|
||||||
|
inline std::wstring vsprintf(
|
||||||
|
wstring_view format,
|
||||||
|
basic_format_args<printf_context<internal::wbuffer>> args) {
|
||||||
|
wmemory_buffer buffer;
|
||||||
|
printf(buffer, format, args);
|
||||||
|
return to_string(buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename... Args>
|
||||||
|
inline std::wstring sprintf(wstring_view format_str, const Args & ... args) {
|
||||||
|
auto vargs = make_args<printf_context<internal::wbuffer>>(args...);
|
||||||
|
return vsprintf(format_str, vargs);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline int vfprintf(std::FILE *f, string_view format, printf_args args) {
|
||||||
|
memory_buffer buffer;
|
||||||
|
printf(buffer, format, args);
|
||||||
|
std::size_t size = buffer.size();
|
||||||
|
return std::fwrite(
|
||||||
|
buffer.data(), 1, size, f) < size ? -1 : static_cast<int>(size);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
\rst
|
||||||
|
Prints formatted data to the file *f*.
|
||||||
|
|
||||||
|
**Example**::
|
||||||
|
|
||||||
|
fmt::fprintf(stderr, "Don't %s!", "panic");
|
||||||
|
\endrst
|
||||||
|
*/
|
||||||
|
template <typename... Args>
|
||||||
|
inline int fprintf(std::FILE *f, string_view format_str, const Args & ... args) {
|
||||||
|
auto vargs = make_args<printf_context<internal::buffer>>(args...);
|
||||||
|
return vfprintf(f, format_str, vargs);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline int vprintf(string_view format, printf_args args) {
|
||||||
|
return vfprintf(stdout, format, args);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
\rst
|
||||||
|
Prints formatted data to ``stdout``.
|
||||||
|
|
||||||
|
**Example**::
|
||||||
|
|
||||||
|
fmt::printf("Elapsed time: %.2f seconds", 1.23);
|
||||||
|
\endrst
|
||||||
|
*/
|
||||||
|
template <typename... Args>
|
||||||
|
inline int printf(string_view format_str, const Args & ... args) {
|
||||||
|
return vprintf(format_str,
|
||||||
|
make_args<printf_context<internal::buffer>>(args...));
|
||||||
|
}
|
||||||
|
|
||||||
|
inline int vfprintf(std::ostream &os, string_view format_str,
|
||||||
|
printf_args args) {
|
||||||
|
memory_buffer buffer;
|
||||||
|
printf(buffer, format_str, args);
|
||||||
|
internal::write(os, buffer);
|
||||||
|
return static_cast<int>(buffer.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
\rst
|
||||||
|
Prints formatted data to the stream *os*.
|
||||||
|
|
||||||
|
**Example**::
|
||||||
|
|
||||||
|
fprintf(cerr, "Don't %s!", "panic");
|
||||||
|
\endrst
|
||||||
|
*/
|
||||||
|
template <typename... Args>
|
||||||
|
inline int fprintf(std::ostream &os, string_view format_str,
|
||||||
|
const Args & ... args) {
|
||||||
|
auto vargs = make_args<printf_context<internal::buffer>>(args...);
|
||||||
|
return vfprintf(os, format_str, vargs);
|
||||||
|
}
|
||||||
|
} // namespace fmt
|
||||||
|
|
||||||
|
#endif // FMT_PRINTF_H_
|
140
include/fmt/time.h
Normal file
140
include/fmt/time.h
Normal file
@ -0,0 +1,140 @@
|
|||||||
|
// Formatting library for C++ - time formatting
|
||||||
|
//
|
||||||
|
// Copyright (c) 2012 - 2016, Victor Zverovich
|
||||||
|
// All rights reserved.
|
||||||
|
//
|
||||||
|
// For the license information refer to format.h.
|
||||||
|
|
||||||
|
#ifndef FMT_TIME_H_
|
||||||
|
#define FMT_TIME_H_
|
||||||
|
|
||||||
|
#include "format.h"
|
||||||
|
#include <ctime>
|
||||||
|
|
||||||
|
namespace fmt {
|
||||||
|
|
||||||
|
namespace internal{
|
||||||
|
inline null<> localtime_r(...) { return null<>(); }
|
||||||
|
inline null<> localtime_s(...) { return null<>(); }
|
||||||
|
inline null<> gmtime_r(...) { return null<>(); }
|
||||||
|
inline null<> gmtime_s(...) { return null<>(); }
|
||||||
|
}
|
||||||
|
|
||||||
|
// Thread-safe replacement for std::localtime
|
||||||
|
inline std::tm localtime(std::time_t time) {
|
||||||
|
struct LocalTime {
|
||||||
|
std::time_t time_;
|
||||||
|
std::tm tm_;
|
||||||
|
|
||||||
|
LocalTime(std::time_t t): time_(t) {}
|
||||||
|
|
||||||
|
bool run() {
|
||||||
|
using namespace fmt::internal;
|
||||||
|
return handle(localtime_r(&time_, &tm_));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool handle(std::tm *tm) { return tm != FMT_NULL; }
|
||||||
|
|
||||||
|
bool handle(internal::null<>) {
|
||||||
|
using namespace fmt::internal;
|
||||||
|
return fallback(localtime_s(&tm_, &time_));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool fallback(int res) { return res == 0; }
|
||||||
|
|
||||||
|
bool fallback(internal::null<>) {
|
||||||
|
using namespace fmt::internal;
|
||||||
|
std::tm *tm = std::localtime(&time_);
|
||||||
|
if (tm) tm_ = *tm;
|
||||||
|
return tm != FMT_NULL;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
LocalTime lt(time);
|
||||||
|
if (lt.run())
|
||||||
|
return lt.tm_;
|
||||||
|
// Too big time values may be unsupported.
|
||||||
|
FMT_THROW(format_error("time_t value out of range"));
|
||||||
|
return std::tm();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Thread-safe replacement for std::gmtime
|
||||||
|
inline std::tm gmtime(std::time_t time) {
|
||||||
|
struct GMTime {
|
||||||
|
std::time_t time_;
|
||||||
|
std::tm tm_;
|
||||||
|
|
||||||
|
GMTime(std::time_t t): time_(t) {}
|
||||||
|
|
||||||
|
bool run() {
|
||||||
|
using namespace fmt::internal;
|
||||||
|
return handle(gmtime_r(&time_, &tm_));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool handle(std::tm *tm) { return tm != FMT_NULL; }
|
||||||
|
|
||||||
|
bool handle(internal::null<>) {
|
||||||
|
using namespace fmt::internal;
|
||||||
|
return fallback(gmtime_s(&tm_, &time_));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool fallback(int res) { return res == 0; }
|
||||||
|
|
||||||
|
bool fallback(internal::null<>) {
|
||||||
|
std::tm *tm = std::gmtime(&time_);
|
||||||
|
if (tm) tm_ = *tm;
|
||||||
|
return tm != FMT_NULL;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
GMTime gt(time);
|
||||||
|
if (gt.run())
|
||||||
|
return gt.tm_;
|
||||||
|
// Too big time values may be unsupported.
|
||||||
|
FMT_THROW(format_error("time_t value out of range"));
|
||||||
|
return std::tm();
|
||||||
|
}
|
||||||
|
|
||||||
|
template <>
|
||||||
|
struct formatter<std::tm> {
|
||||||
|
template <typename ParseContext>
|
||||||
|
auto parse(ParseContext &ctx) -> decltype(ctx.begin()) {
|
||||||
|
auto it = internal::null_terminating_iterator<char>(ctx);
|
||||||
|
if (*it == ':')
|
||||||
|
++it;
|
||||||
|
auto end = it;
|
||||||
|
while (*end && *end != '}')
|
||||||
|
++end;
|
||||||
|
tm_format.reserve(end - it + 1);
|
||||||
|
using internal::pointer_from;
|
||||||
|
tm_format.append(pointer_from(it), pointer_from(end));
|
||||||
|
tm_format.push_back('\0');
|
||||||
|
return pointer_from(end);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto format(const std::tm &tm, context &ctx) -> decltype(ctx.begin()) {
|
||||||
|
internal::buffer &buf = internal::get_container(ctx.begin());
|
||||||
|
std::size_t start = buf.size();
|
||||||
|
for (;;) {
|
||||||
|
std::size_t size = buf.capacity() - start;
|
||||||
|
std::size_t count = std::strftime(&buf[start], size, &tm_format[0], &tm);
|
||||||
|
if (count != 0) {
|
||||||
|
buf.resize(start + count);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (size >= tm_format.size() * 256) {
|
||||||
|
// If the buffer is 256 times larger than the format string, assume
|
||||||
|
// that `strftime` gives an empty result. There doesn't seem to be a
|
||||||
|
// better way to distinguish the two cases:
|
||||||
|
// https://github.com/fmtlib/fmt/issues/367
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
const std::size_t MIN_GROWTH = 10;
|
||||||
|
buf.reserve(buf.capacity() + (size > MIN_GROWTH ? size : MIN_GROWTH));
|
||||||
|
}
|
||||||
|
return ctx.begin();
|
||||||
|
}
|
||||||
|
|
||||||
|
memory_buffer tm_format;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // FMT_TIME_H_
|
@ -20,8 +20,8 @@ if build == 'mingw':
|
|||||||
else:
|
else:
|
||||||
# Add MSBuild 14.0 to PATH as described in
|
# Add MSBuild 14.0 to PATH as described in
|
||||||
# http://help.appveyor.com/discussions/problems/2229-v140-not-found-on-vs2105rc.
|
# http://help.appveyor.com/discussions/problems/2229-v140-not-found-on-vs2105rc.
|
||||||
os.environ['PATH'] = r'C:\Program Files (x86)\MSBuild\14.0\Bin;' + path
|
os.environ['PATH'] = r'C:\Program Files (x86)\MSBuild\15.0\Bin;' + path
|
||||||
generator = 'Visual Studio 14 2015'
|
generator = 'Visual Studio 15 2017'
|
||||||
if platform == 'x64':
|
if platform == 'x64':
|
||||||
generator += ' Win64'
|
generator += ' Win64'
|
||||||
cmake_command.append('-G' + generator)
|
cmake_command.append('-G' + generator)
|
||||||
|
@ -2,13 +2,14 @@ configuration:
|
|||||||
- Debug
|
- Debug
|
||||||
- Release
|
- Release
|
||||||
|
|
||||||
|
image: Visual Studio 2017
|
||||||
|
|
||||||
environment:
|
environment:
|
||||||
CTEST_OUTPUT_ON_FAILURE: 1
|
CTEST_OUTPUT_ON_FAILURE: 1
|
||||||
matrix:
|
matrix:
|
||||||
- BUILD: msvc
|
- BUILD: msvc
|
||||||
- BUILD: msvc
|
- BUILD: msvc
|
||||||
PLATFORM: x64
|
PLATFORM: x64
|
||||||
- BUILD: mingw
|
|
||||||
|
|
||||||
before_build:
|
before_build:
|
||||||
# Workaround for CMake not wanting sh.exe on PATH for MinGW.
|
# Workaround for CMake not wanting sh.exe on PATH for MinGW.
|
||||||
|
@ -1,55 +1,56 @@
|
|||||||
# C++11 feature support detection
|
# C++14 feature support detection
|
||||||
|
|
||||||
if (NOT FMT_USE_CPP11)
|
if (NOT FMT_USE_CPP14)
|
||||||
return()
|
return()
|
||||||
endif ()
|
endif ()
|
||||||
|
|
||||||
include(CheckCXXCompilerFlag)
|
include(CheckCXXCompilerFlag)
|
||||||
|
|
||||||
if (FMT_USE_CPP11)
|
if (FMT_USE_CPP14)
|
||||||
check_cxx_compiler_flag(-std=c++11 HAVE_STD_CPP11_FLAG)
|
check_cxx_compiler_flag(-std=c++14 HAVE_STD_CPP14_FLAG)
|
||||||
if (HAVE_STD_CPP11_FLAG)
|
if (HAVE_STD_CPP14_FLAG)
|
||||||
# Check if including cmath works with -std=c++11 and -O3.
|
# Check if including cmath works with -std=c++14 and -O3.
|
||||||
# It may not in MinGW due to bug http://ehc.ac/p/mingw/bugs/2250/.
|
# It may not in MinGW due to bug http://ehc.ac/p/mingw/bugs/2250/.
|
||||||
set(CMAKE_REQUIRED_FLAGS "-std=c++11 -O3")
|
set(CMAKE_REQUIRED_FLAGS "-std=c++14 -O3")
|
||||||
check_cxx_source_compiles("
|
check_cxx_source_compiles("
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
int main() {}" FMT_CPP11_CMATH)
|
int main() {}" FMT_CPP14_CMATH)
|
||||||
# Check if including <unistd.h> works with -std=c++11.
|
# Check if including <unistd.h> works with -std=c++14.
|
||||||
# It may not in MinGW due to bug http://sourceforge.net/p/mingw/bugs/2024/.
|
# It may not in MinGW due to bug http://sourceforge.net/p/mingw/bugs/2024/.
|
||||||
check_cxx_source_compiles("
|
check_cxx_source_compiles("
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
int main() {}" FMT_CPP11_UNISTD_H)
|
int main() {}" FMT_CPP14_UNISTD_H)
|
||||||
# Check if snprintf works with -std=c++11. It may not in MinGW.
|
# Check if snprintf works with -std=c++14. It may not in MinGW.
|
||||||
check_cxx_source_compiles("
|
check_cxx_source_compiles("
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
int main() {
|
int main() {
|
||||||
char buffer[10];
|
char buffer[10];
|
||||||
snprintf(buffer, 10, \"foo\");
|
snprintf(buffer, 10, \"foo\");
|
||||||
}" FMT_CPP11_SNPRINTF)
|
}" FMT_CPP14_SNPRINTF)
|
||||||
if (FMT_CPP11_CMATH AND FMT_CPP11_UNISTD_H AND FMT_CPP11_SNPRINTF)
|
if (FMT_CPP14_CMATH AND FMT_CPP14_UNISTD_H AND FMT_CPP14_SNPRINTF)
|
||||||
set(CPP11_FLAG -std=c++11)
|
set(CPP14_FLAG -std=c++14)
|
||||||
else ()
|
else ()
|
||||||
check_cxx_compiler_flag(-std=gnu++11 HAVE_STD_GNUPP11_FLAG)
|
check_cxx_compiler_flag(-std=gnu++14 HAVE_STD_GNUPP14_FLAG)
|
||||||
if (HAVE_STD_CPP11_FLAG)
|
if (HAVE_STD_CPP14_FLAG)
|
||||||
set(CPP11_FLAG -std=gnu++11)
|
set(CPP14_FLAG -std=gnu++14)
|
||||||
endif ()
|
endif ()
|
||||||
endif ()
|
endif ()
|
||||||
set(CMAKE_REQUIRED_FLAGS )
|
set(CMAKE_REQUIRED_FLAGS )
|
||||||
else ()
|
else ()
|
||||||
check_cxx_compiler_flag(-std=c++0x HAVE_STD_CPP0X_FLAG)
|
check_cxx_compiler_flag(-std=c++1y HAVE_STD_CPP1Y_FLAG)
|
||||||
if (HAVE_STD_CPP0X_FLAG)
|
if (HAVE_STD_CPP1Y_FLAG)
|
||||||
set(CPP11_FLAG -std=c++0x)
|
set(CPP14_FLAG -std=c++1y)
|
||||||
endif ()
|
endif ()
|
||||||
endif ()
|
endif ()
|
||||||
endif ()
|
endif ()
|
||||||
|
|
||||||
if (CMAKE_CXX_STANDARD)
|
if (CMAKE_CXX_STANDARD)
|
||||||
# Don't use -std compiler flag if CMAKE_CXX_STANDARD is specified.
|
# Don't use -std compiler flag if CMAKE_CXX_STANDARD is specified.
|
||||||
set(CPP11_FLAG )
|
set(CPP14_FLAG )
|
||||||
endif ()
|
endif ()
|
||||||
|
|
||||||
set(CMAKE_REQUIRED_FLAGS ${CPP11_FLAG})
|
message(STATUS "CPP14_FLAG: ${CPP14_FLAG}")
|
||||||
|
set(CMAKE_REQUIRED_FLAGS ${CPP14_FLAG})
|
||||||
|
|
||||||
# Check if variadic templates are working and not affected by GCC bug 39653:
|
# Check if variadic templates are working and not affected by GCC bug 39653:
|
||||||
# https://gcc.gnu.org/bugzilla/show_bug.cgi?id=39653
|
# https://gcc.gnu.org/bugzilla/show_bug.cgi?id=39653
|
@ -89,10 +89,8 @@ common_cmake_flags = [
|
|||||||
'-DCMAKE_INSTALL_PREFIX=' + install_dir, '-DCMAKE_BUILD_TYPE=' + build
|
'-DCMAKE_INSTALL_PREFIX=' + install_dir, '-DCMAKE_BUILD_TYPE=' + build
|
||||||
]
|
]
|
||||||
extra_cmake_flags = []
|
extra_cmake_flags = []
|
||||||
if standard != '0x':
|
if standard != '14':
|
||||||
extra_cmake_flags = [
|
extra_cmake_flags = ['-DCMAKE_CXX_FLAGS=-std=c++' + standard]
|
||||||
'-DCMAKE_CXX_FLAGS=-std=c++' + standard, '-DFMT_USE_CPP11=OFF'
|
|
||||||
]
|
|
||||||
check_call(['cmake', '-DFMT_DOC=OFF', '-DFMT_PEDANTIC=ON', fmt_dir] +
|
check_call(['cmake', '-DFMT_DOC=OFF', '-DFMT_PEDANTIC=ON', fmt_dir] +
|
||||||
common_cmake_flags + extra_cmake_flags, cwd=build_dir)
|
common_cmake_flags + extra_cmake_flags, cwd=build_dir)
|
||||||
|
|
||||||
|
@ -7,16 +7,10 @@
|
|||||||
# at http://code.google.com/p/googletest/wiki/FAQ for more details.
|
# at http://code.google.com/p/googletest/wiki/FAQ for more details.
|
||||||
add_library(gmock STATIC
|
add_library(gmock STATIC
|
||||||
gmock-gtest-all.cc gmock/gmock.h gtest/gtest.h gtest/gtest-spi.h)
|
gmock-gtest-all.cc gmock/gmock.h gtest/gtest.h gtest/gtest-spi.h)
|
||||||
target_compile_options(gmock PUBLIC ${CPP11_FLAG})
|
target_compile_options(gmock PUBLIC ${CPP14_FLAG})
|
||||||
target_compile_definitions(gmock PUBLIC GTEST_HAS_STD_WSTRING=1)
|
target_compile_definitions(gmock PUBLIC GTEST_HAS_STD_WSTRING=1)
|
||||||
target_include_directories(gmock PUBLIC .)
|
target_include_directories(gmock PUBLIC .)
|
||||||
|
|
||||||
# Workaround for Cygwin to make google-tests compile and run because the macro
|
|
||||||
# _POSIX_C_SOURCE must be defined to allow fileno(), strdup(), fdopen() calls.
|
|
||||||
if (CYGWIN)
|
|
||||||
target_compile_definitions(gmock PUBLIC _POSIX_C_SOURCE=200809)
|
|
||||||
endif ()
|
|
||||||
|
|
||||||
find_package(Threads)
|
find_package(Threads)
|
||||||
if (Threads_FOUND)
|
if (Threads_FOUND)
|
||||||
target_link_libraries(gmock ${CMAKE_THREAD_LIBS_INIT})
|
target_link_libraries(gmock ${CMAKE_THREAD_LIBS_INIT})
|
||||||
@ -38,6 +32,10 @@ if (CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
|
|||||||
target_compile_definitions(gmock PUBLIC GTEST_USE_OWN_TR1_TUPLE=1)
|
target_compile_definitions(gmock PUBLIC GTEST_USE_OWN_TR1_TUPLE=1)
|
||||||
endif ()
|
endif ()
|
||||||
|
|
||||||
|
# Silence MSVC tr1 deprecation warning in gmock.
|
||||||
|
target_compile_definitions(gmock
|
||||||
|
PUBLIC _SILENCE_TR1_NAMESPACE_DEPRECATION_WARNING=0)
|
||||||
|
|
||||||
#------------------------------------------------------------------------------
|
#------------------------------------------------------------------------------
|
||||||
# Build the actual library tests
|
# Build the actual library tests
|
||||||
|
|
||||||
@ -86,16 +84,13 @@ function(add_fmt_test name)
|
|||||||
endfunction()
|
endfunction()
|
||||||
|
|
||||||
add_fmt_test(assert-test)
|
add_fmt_test(assert-test)
|
||||||
add_fmt_test(container-test)
|
|
||||||
add_fmt_test(gtest-extra-test)
|
add_fmt_test(gtest-extra-test)
|
||||||
add_fmt_test(format-test)
|
add_fmt_test(format-test)
|
||||||
add_fmt_test(format-impl-test)
|
add_fmt_test(format-impl-test)
|
||||||
add_fmt_test(ostream-test)
|
add_fmt_test(ostream-test)
|
||||||
add_fmt_test(printf-test)
|
add_fmt_test(printf-test)
|
||||||
add_fmt_test(string-test)
|
|
||||||
add_fmt_test(time-test)
|
add_fmt_test(time-test)
|
||||||
add_fmt_test(util-test mock-allocator.h)
|
add_fmt_test(util-test mock-allocator.h)
|
||||||
add_fmt_test(macro-test)
|
|
||||||
add_fmt_test(custom-formatter-test)
|
add_fmt_test(custom-formatter-test)
|
||||||
|
|
||||||
# Enable stricter options for one test to make sure that the header is free of
|
# Enable stricter options for one test to make sure that the header is free of
|
||||||
@ -106,8 +101,9 @@ endif ()
|
|||||||
|
|
||||||
if (HAVE_OPEN)
|
if (HAVE_OPEN)
|
||||||
add_fmt_executable(posix-mock-test
|
add_fmt_executable(posix-mock-test
|
||||||
posix-mock-test.cc ../fmt/format.cc ../fmt/printf.cc ${TEST_MAIN_SRC})
|
posix-mock-test.cc ../include/fmt/format.cc ${TEST_MAIN_SRC})
|
||||||
target_include_directories(posix-mock-test PRIVATE ${PROJECT_SOURCE_DIR})
|
target_include_directories(
|
||||||
|
posix-mock-test PRIVATE ${PROJECT_SOURCE_DIR}/include)
|
||||||
target_compile_definitions(posix-mock-test PRIVATE FMT_USE_FILE_DESCRIPTORS=1)
|
target_compile_definitions(posix-mock-test PRIVATE FMT_USE_FILE_DESCRIPTORS=1)
|
||||||
target_link_libraries(posix-mock-test gmock)
|
target_link_libraries(posix-mock-test gmock)
|
||||||
add_test(NAME posix-mock-test COMMAND posix-mock-test)
|
add_test(NAME posix-mock-test COMMAND posix-mock-test)
|
||||||
@ -120,23 +116,28 @@ target_link_libraries(header-only-test gmock)
|
|||||||
if (TARGET fmt-header-only)
|
if (TARGET fmt-header-only)
|
||||||
target_link_libraries(header-only-test fmt-header-only)
|
target_link_libraries(header-only-test fmt-header-only)
|
||||||
else ()
|
else ()
|
||||||
target_include_directories(header-only-test PRIVATE ${PROJECT_SOURCE_DIR})
|
target_include_directories(
|
||||||
|
header-only-test PRIVATE ${PROJECT_SOURCE_DIR}/include)
|
||||||
target_compile_definitions(header-only-test PRIVATE FMT_HEADER_ONLY=1)
|
target_compile_definitions(header-only-test PRIVATE FMT_HEADER_ONLY=1)
|
||||||
endif ()
|
endif ()
|
||||||
|
|
||||||
# Test that the library can be compiled with exceptions disabled.
|
# Test that the library can be compiled with exceptions disabled.
|
||||||
check_cxx_compiler_flag(-fno-exceptions HAVE_FNO_EXCEPTIONS_FLAG)
|
check_cxx_compiler_flag(-fno-exceptions HAVE_FNO_EXCEPTIONS_FLAG)
|
||||||
if (HAVE_FNO_EXCEPTIONS_FLAG)
|
if (HAVE_FNO_EXCEPTIONS_FLAG)
|
||||||
add_library(noexception-test ../fmt/format.cc)
|
add_library(noexception-test ../include/fmt/format.cc)
|
||||||
target_include_directories(noexception-test PRIVATE ${PROJECT_SOURCE_DIR})
|
target_compile_options(noexception-test PUBLIC ${CPP14_FLAG})
|
||||||
|
target_include_directories(
|
||||||
|
noexception-test PRIVATE ${PROJECT_SOURCE_DIR}/include)
|
||||||
target_compile_options(noexception-test PRIVATE -fno-exceptions)
|
target_compile_options(noexception-test PRIVATE -fno-exceptions)
|
||||||
endif ()
|
endif ()
|
||||||
|
|
||||||
if (FMT_PEDANTIC)
|
if (FMT_PEDANTIC)
|
||||||
# Test that the library compiles without windows.h.
|
# Test that the library compiles without windows.h.
|
||||||
if (CMAKE_SYSTEM_NAME STREQUAL "Windows")
|
if (CMAKE_SYSTEM_NAME STREQUAL "Windows")
|
||||||
add_library(no-windows-h-test ../fmt/format.cc)
|
add_library(no-windows-h-test ../include/fmt/format.cc)
|
||||||
target_include_directories(no-windows-h-test PRIVATE ${PROJECT_SOURCE_DIR})
|
target_compile_options(no-windows-h-test PUBLIC ${CPP14_FLAG})
|
||||||
|
target_include_directories(
|
||||||
|
no-windows-h-test PRIVATE ${PROJECT_SOURCE_DIR}/include)
|
||||||
target_compile_definitions(no-windows-h-test PRIVATE FMT_USE_WINDOWS_H=0)
|
target_compile_definitions(no-windows-h-test PRIVATE FMT_USE_WINDOWS_H=0)
|
||||||
endif ()
|
endif ()
|
||||||
|
|
||||||
@ -146,9 +147,9 @@ if (FMT_PEDANTIC)
|
|||||||
"${CMAKE_CURRENT_BINARY_DIR}/compile-test"
|
"${CMAKE_CURRENT_BINARY_DIR}/compile-test"
|
||||||
--build-generator ${CMAKE_GENERATOR}
|
--build-generator ${CMAKE_GENERATOR}
|
||||||
--build-makeprogram ${CMAKE_MAKE_PROGRAM}
|
--build-makeprogram ${CMAKE_MAKE_PROGRAM}
|
||||||
--build-options
|
--build-options
|
||||||
"-DCMAKE_CXX_COMPILER=${CMAKE_CXX_COMPILER}"
|
"-DCMAKE_CXX_COMPILER=${CMAKE_CXX_COMPILER}"
|
||||||
"-DCPP11_FLAG=${CPP11_FLAG}"
|
"-DCPP14_FLAG=${CPP14_FLAG}"
|
||||||
"-DSUPPORTS_USER_DEFINED_LITERALS=${SUPPORTS_USER_DEFINED_LITERALS}")
|
"-DSUPPORTS_USER_DEFINED_LITERALS=${SUPPORTS_USER_DEFINED_LITERALS}")
|
||||||
|
|
||||||
# test if the targets are findable from the build directory
|
# test if the targets are findable from the build directory
|
||||||
@ -161,6 +162,7 @@ if (FMT_PEDANTIC)
|
|||||||
--build-makeprogram ${CMAKE_MAKE_PROGRAM}
|
--build-makeprogram ${CMAKE_MAKE_PROGRAM}
|
||||||
--build-options
|
--build-options
|
||||||
"-DCMAKE_CXX_COMPILER=${CMAKE_CXX_COMPILER}"
|
"-DCMAKE_CXX_COMPILER=${CMAKE_CXX_COMPILER}"
|
||||||
|
"-DCPP14_FLAG=${CPP14_FLAG}"
|
||||||
"-DFMT_DIR=${PROJECT_BINARY_DIR}"
|
"-DFMT_DIR=${PROJECT_BINARY_DIR}"
|
||||||
"-DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE}")
|
"-DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE}")
|
||||||
|
|
||||||
@ -174,5 +176,6 @@ if (FMT_PEDANTIC)
|
|||||||
--build-makeprogram ${CMAKE_MAKE_PROGRAM}
|
--build-makeprogram ${CMAKE_MAKE_PROGRAM}
|
||||||
--build-options
|
--build-options
|
||||||
"-DCMAKE_CXX_COMPILER=${CMAKE_CXX_COMPILER}"
|
"-DCMAKE_CXX_COMPILER=${CMAKE_CXX_COMPILER}"
|
||||||
|
"-DCPP14_FLAG=${CPP14_FLAG}"
|
||||||
"-DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE}")
|
"-DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE}")
|
||||||
endif ()
|
endif ()
|
||||||
|
@ -5,9 +5,11 @@ project(fmt-test)
|
|||||||
add_subdirectory(../.. fmt)
|
add_subdirectory(../.. fmt)
|
||||||
|
|
||||||
add_executable(library-test "main.cc")
|
add_executable(library-test "main.cc")
|
||||||
target_link_libraries(library-test fmt::fmt)
|
target_compile_options(library-test PUBLIC ${CPP14_FLAG})
|
||||||
|
target_link_libraries(library-test fmt)
|
||||||
|
|
||||||
if (TARGET fmt::fmt-header-only)
|
if (TARGET fmt-header-only)
|
||||||
add_executable(header-only-test "main.cc")
|
add_executable(header-only-test "main.cc")
|
||||||
target_link_libraries(header-only-test fmt::fmt-header-only)
|
target_compile_options(header-only-test PUBLIC ${CPP14_FLAG})
|
||||||
|
target_link_libraries(header-only-test fmt-header-only)
|
||||||
endif ()
|
endif ()
|
||||||
|
@ -25,7 +25,7 @@
|
|||||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "fmt/format.h"
|
#include "fmt/core.h"
|
||||||
#include "gtest/gtest.h"
|
#include "gtest/gtest.h"
|
||||||
|
|
||||||
#if GTEST_HAS_DEATH_TEST
|
#if GTEST_HAS_DEATH_TEST
|
||||||
|
@ -3,8 +3,8 @@
|
|||||||
cmake_minimum_required(VERSION 2.8)
|
cmake_minimum_required(VERSION 2.8)
|
||||||
|
|
||||||
include(CheckCXXSourceCompiles)
|
include(CheckCXXSourceCompiles)
|
||||||
set(CMAKE_REQUIRED_INCLUDES ${CMAKE_CURRENT_SOURCE_DIR}/../..)
|
set(CMAKE_REQUIRED_INCLUDES ${CMAKE_CURRENT_SOURCE_DIR}/../../include)
|
||||||
set(CMAKE_REQUIRED_FLAGS ${CPP11_FLAG})
|
set(CMAKE_REQUIRED_FLAGS ${CPP14_FLAG})
|
||||||
|
|
||||||
function (generate_source result fragment)
|
function (generate_source result fragment)
|
||||||
set(${result} "
|
set(${result} "
|
||||||
@ -63,9 +63,6 @@ expect_compile_error("fmt::MemoryWriter() << fmt::pad(42, 5, L' ');")
|
|||||||
# Formatting a wide character with a narrow format string is forbidden.
|
# Formatting a wide character with a narrow format string is forbidden.
|
||||||
expect_compile_error("fmt::format(\"{}\", L'a';")
|
expect_compile_error("fmt::format(\"{}\", L'a';")
|
||||||
|
|
||||||
expect_compile("FMT_STATIC_ASSERT(true, \"this should never happen\");")
|
|
||||||
expect_compile_error("FMT_STATIC_ASSERT(0 > 1, \"oops\");")
|
|
||||||
|
|
||||||
# Make sure that compiler features detected in the header
|
# Make sure that compiler features detected in the header
|
||||||
# match the features detected in CMake.
|
# match the features detected in CMake.
|
||||||
if (SUPPORTS_USER_DEFINED_LITERALS)
|
if (SUPPORTS_USER_DEFINED_LITERALS)
|
||||||
|
@ -10,59 +10,43 @@
|
|||||||
#include "fmt/printf.h"
|
#include "fmt/printf.h"
|
||||||
#include "gtest-extra.h"
|
#include "gtest-extra.h"
|
||||||
|
|
||||||
using fmt::BasicPrintfArgFormatter;
|
using fmt::printf_arg_formatter;
|
||||||
|
|
||||||
// A custom argument formatter that doesn't print `-` for floating-point values
|
// A custom argument formatter that doesn't print `-` for floating-point values
|
||||||
// rounded to 0.
|
// rounded to 0.
|
||||||
class CustomArgFormatter
|
class CustomArgFormatter :
|
||||||
: public fmt::BasicArgFormatter<CustomArgFormatter, char> {
|
public fmt::arg_formatter<fmt::back_insert_range<fmt::internal::buffer>> {
|
||||||
public:
|
public:
|
||||||
CustomArgFormatter(fmt::BasicFormatter<char, CustomArgFormatter> &f,
|
using range = fmt::back_insert_range<fmt::internal::buffer>;
|
||||||
fmt::FormatSpec &s, const char *fmt)
|
using iterator = decltype(std::declval<range>().begin());
|
||||||
: fmt::BasicArgFormatter<CustomArgFormatter, char>(f, s, fmt) {}
|
using base = fmt::arg_formatter<range>;
|
||||||
|
|
||||||
void visit_double(double value) {
|
CustomArgFormatter(fmt::basic_context<iterator, char> &ctx,
|
||||||
|
fmt::format_specs &s)
|
||||||
|
: base(ctx, s) {}
|
||||||
|
|
||||||
|
using base::operator();
|
||||||
|
|
||||||
|
void operator()(double value) {
|
||||||
if (round(value * pow(10, spec().precision())) == 0)
|
if (round(value * pow(10, spec().precision())) == 0)
|
||||||
value = 0;
|
value = 0;
|
||||||
fmt::BasicArgFormatter<CustomArgFormatter, char>::visit_double(value);
|
base::operator()(value);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// A custom argument formatter that doesn't print `-` for floating-point values
|
std::string custom_vformat(fmt::string_view format_str, fmt::format_args args) {
|
||||||
// rounded to 0.
|
fmt::memory_buffer buffer;
|
||||||
class CustomPrintfArgFormatter :
|
// Pass custom argument formatter as a template arg to vwrite.
|
||||||
public BasicPrintfArgFormatter<CustomPrintfArgFormatter, char> {
|
fmt::do_vformat_to<CustomArgFormatter>(buffer, format_str, args);
|
||||||
public:
|
return std::string(buffer.data(), buffer.size());
|
||||||
typedef BasicPrintfArgFormatter<CustomPrintfArgFormatter, char> Base;
|
|
||||||
|
|
||||||
CustomPrintfArgFormatter(fmt::BasicWriter<char> &w, fmt::FormatSpec &spec)
|
|
||||||
: Base(w, spec) {}
|
|
||||||
|
|
||||||
void visit_double(double value) {
|
|
||||||
if (round(value * pow(10, spec().precision())) == 0)
|
|
||||||
value = 0;
|
|
||||||
Base::visit_double(value);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
std::string custom_format(const char *format_str, fmt::ArgList args) {
|
|
||||||
fmt::MemoryWriter writer;
|
|
||||||
// Pass custom argument formatter as a template arg to BasicFormatter.
|
|
||||||
fmt::BasicFormatter<char, CustomArgFormatter> formatter(args, writer);
|
|
||||||
formatter.format(format_str);
|
|
||||||
return writer.str();
|
|
||||||
}
|
}
|
||||||
FMT_VARIADIC(std::string, custom_format, const char *)
|
|
||||||
|
|
||||||
std::string custom_sprintf(const char* format_str, fmt::ArgList args){
|
template <typename... Args>
|
||||||
fmt::MemoryWriter writer;
|
std::string custom_format(const char *format_str, const Args & ... args) {
|
||||||
fmt::PrintfFormatter<char, CustomPrintfArgFormatter> formatter(args, writer);
|
auto va = fmt::make_args(args...);
|
||||||
formatter.format(format_str);
|
return custom_vformat(format_str, va);
|
||||||
return writer.str();
|
|
||||||
}
|
}
|
||||||
FMT_VARIADIC(std::string, custom_sprintf, const char*);
|
|
||||||
|
|
||||||
TEST(CustomFormatterTest, Format) {
|
TEST(CustomFormatterTest, Format) {
|
||||||
EXPECT_EQ("0.00", custom_format("{:.2f}", -.00001));
|
EXPECT_EQ("0.00", custom_format("{:.2f}", -.00001));
|
||||||
EXPECT_EQ("0.00", custom_sprintf("%.2f", -.00001));
|
|
||||||
}
|
}
|
||||||
|
@ -5,9 +5,11 @@ project(fmt-test)
|
|||||||
find_package(FMT REQUIRED)
|
find_package(FMT REQUIRED)
|
||||||
|
|
||||||
add_executable(library-test main.cc)
|
add_executable(library-test main.cc)
|
||||||
target_link_libraries(library-test fmt::fmt)
|
target_compile_options(library-test PUBLIC ${CPP14_FLAG})
|
||||||
|
target_link_libraries(library-test fmt)
|
||||||
|
|
||||||
if (TARGET fmt::fmt-header-only)
|
if (TARGET fmt-header-only)
|
||||||
add_executable(header-only-test main.cc)
|
add_executable(header-only-test main.cc)
|
||||||
target_link_libraries(header-only-test fmt::fmt-header-only)
|
target_compile_options(header-only-test PUBLIC ${CPP14_FLAG})
|
||||||
|
target_link_libraries(header-only-test fmt-header-only)
|
||||||
endif ()
|
endif ()
|
||||||
|
@ -29,9 +29,9 @@
|
|||||||
#undef FMT_SHARED
|
#undef FMT_SHARED
|
||||||
#include "test-assert.h"
|
#include "test-assert.h"
|
||||||
|
|
||||||
// Include *.cc instead of *.h to test implementation-specific stuff.
|
// Include format.cc instead of format.h to test implementation-specific stuff.
|
||||||
#include "fmt/format.cc"
|
#include "fmt/format.cc"
|
||||||
#include "fmt/printf.cc"
|
#include "fmt/printf.h"
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
@ -43,18 +43,29 @@
|
|||||||
#undef min
|
#undef min
|
||||||
#undef max
|
#undef max
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
struct ValueExtractor {
|
||||||
|
T operator()(T value) {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename U>
|
||||||
|
T operator()(U) {
|
||||||
|
throw std::runtime_error(fmt::format("invalid type {}", typeid(U).name()));
|
||||||
|
return T();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
TEST(FormatTest, ArgConverter) {
|
TEST(FormatTest, ArgConverter) {
|
||||||
using fmt::internal::Arg;
|
long long value = std::numeric_limits<long long>::max();
|
||||||
Arg arg = Arg();
|
auto arg = fmt::internal::make_arg<fmt::context>(value);
|
||||||
arg.type = Arg::LONG_LONG;
|
visit(fmt::internal::ArgConverter<long long, fmt::context>(arg, 'd'), arg);
|
||||||
arg.long_long_value = std::numeric_limits<fmt::LongLong>::max();
|
EXPECT_EQ(value, visit(ValueExtractor<long long>(), arg));
|
||||||
fmt::internal::ArgConverter<fmt::LongLong>(arg, 'd').visit(arg);
|
|
||||||
EXPECT_EQ(Arg::LONG_LONG, arg.type);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(FormatTest, FormatNegativeNaN) {
|
TEST(FormatTest, FormatNegativeNaN) {
|
||||||
double nan = std::numeric_limits<double>::quiet_NaN();
|
double nan = std::numeric_limits<double>::quiet_NaN();
|
||||||
if (fmt::internal::FPUtil::isnegative(-nan))
|
if (fmt::internal::fputil::isnegative(-nan))
|
||||||
EXPECT_EQ("-nan", fmt::format("{}", -nan));
|
EXPECT_EQ("-nan", fmt::format("{}", -nan));
|
||||||
else
|
else
|
||||||
fmt::print("Warning: compiler doesn't handle negative NaN correctly");
|
fmt::print("Warning: compiler doesn't handle negative NaN correctly");
|
||||||
@ -95,33 +106,33 @@ TEST(FormatTest, StrError) {
|
|||||||
TEST(FormatTest, FormatErrorCode) {
|
TEST(FormatTest, FormatErrorCode) {
|
||||||
std::string msg = "error 42", sep = ": ";
|
std::string msg = "error 42", sep = ": ";
|
||||||
{
|
{
|
||||||
fmt::MemoryWriter w;
|
fmt::memory_buffer buffer;
|
||||||
w << "garbage";
|
format_to(buffer, "garbage");
|
||||||
fmt::format_error_code(w, 42, "test");
|
fmt::format_error_code(buffer, 42, "test");
|
||||||
EXPECT_EQ("test: " + msg, w.str());
|
EXPECT_EQ("test: " + msg, to_string(buffer));
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
fmt::MemoryWriter w;
|
fmt::memory_buffer buffer;
|
||||||
std::string prefix(
|
std::string prefix(
|
||||||
fmt::internal::INLINE_BUFFER_SIZE - msg.size() - sep.size() + 1, 'x');
|
fmt::internal::INLINE_BUFFER_SIZE - msg.size() - sep.size() + 1, 'x');
|
||||||
fmt::format_error_code(w, 42, prefix);
|
fmt::format_error_code(buffer, 42, prefix);
|
||||||
EXPECT_EQ(msg, w.str());
|
EXPECT_EQ(msg, to_string(buffer));
|
||||||
}
|
}
|
||||||
int codes[] = {42, -1};
|
int codes[] = {42, -1};
|
||||||
for (std::size_t i = 0, n = sizeof(codes) / sizeof(*codes); i < n; ++i) {
|
for (std::size_t i = 0, n = sizeof(codes) / sizeof(*codes); i < n; ++i) {
|
||||||
// Test maximum buffer size.
|
// Test maximum buffer size.
|
||||||
msg = fmt::format("error {}", codes[i]);
|
msg = fmt::format("error {}", codes[i]);
|
||||||
fmt::MemoryWriter w;
|
fmt::memory_buffer buffer;
|
||||||
std::string prefix(
|
std::string prefix(
|
||||||
fmt::internal::INLINE_BUFFER_SIZE - msg.size() - sep.size(), 'x');
|
fmt::internal::INLINE_BUFFER_SIZE - msg.size() - sep.size(), 'x');
|
||||||
fmt::format_error_code(w, codes[i], prefix);
|
fmt::format_error_code(buffer, codes[i], prefix);
|
||||||
EXPECT_EQ(prefix + sep + msg, w.str());
|
EXPECT_EQ(prefix + sep + msg, to_string(buffer));
|
||||||
std::size_t size = fmt::internal::INLINE_BUFFER_SIZE;
|
std::size_t size = fmt::internal::INLINE_BUFFER_SIZE;
|
||||||
EXPECT_EQ(size, w.size());
|
EXPECT_EQ(size, buffer.size());
|
||||||
w.clear();
|
buffer.resize(0);
|
||||||
// Test with a message that doesn't fit into the buffer.
|
// Test with a message that doesn't fit into the buffer.
|
||||||
prefix += 'x';
|
prefix += 'x';
|
||||||
fmt::format_error_code(w, codes[i], prefix);
|
fmt::format_error_code(buffer, codes[i], prefix);
|
||||||
EXPECT_EQ(msg, w.str());
|
EXPECT_EQ(msg, to_string(buffer));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
1325
test/format-test.cc
1325
test/format-test.cc
File diff suppressed because it is too large
Load Diff
@ -825,8 +825,9 @@ template <typename T> struct DecayArray<T[]> {
|
|||||||
// crashes).
|
// crashes).
|
||||||
template <typename T>
|
template <typename T>
|
||||||
inline T Invalid() {
|
inline T Invalid() {
|
||||||
|
void *p = NULL;
|
||||||
return const_cast<typename remove_reference<T>::type&>(
|
return const_cast<typename remove_reference<T>::type&>(
|
||||||
*static_cast<volatile typename remove_reference<T>::type*>(NULL));
|
*static_cast<volatile typename remove_reference<T>::type*>(p));
|
||||||
}
|
}
|
||||||
template <>
|
template <>
|
||||||
inline void Invalid<void>() {}
|
inline void Invalid<void>() {}
|
||||||
|
@ -78,7 +78,7 @@ void throw_exception() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void throw_system_error() {
|
void throw_system_error() {
|
||||||
throw fmt::SystemError(EDOM, "test");
|
throw fmt::system_error(EDOM, "test");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Tests that when EXPECT_THROW_MSG fails, it evaluates its message argument
|
// Tests that when EXPECT_THROW_MSG fails, it evaluates its message argument
|
||||||
@ -207,11 +207,11 @@ TEST(ExpectThrowTest, DoesNotGenerateUnreachableCodeWarning) {
|
|||||||
// EXPECT_SYSTEM_ERROR macro.
|
// EXPECT_SYSTEM_ERROR macro.
|
||||||
TEST(ExpectSystemErrorTest, DoesNotGenerateUnreachableCodeWarning) {
|
TEST(ExpectSystemErrorTest, DoesNotGenerateUnreachableCodeWarning) {
|
||||||
int n = 0;
|
int n = 0;
|
||||||
EXPECT_SYSTEM_ERROR(throw fmt::SystemError(EDOM, "test"), EDOM, "test");
|
EXPECT_SYSTEM_ERROR(throw fmt::system_error(EDOM, "test"), EDOM, "test");
|
||||||
EXPECT_NONFATAL_FAILURE(EXPECT_SYSTEM_ERROR(n++, EDOM, ""), "");
|
EXPECT_NONFATAL_FAILURE(EXPECT_SYSTEM_ERROR(n++, EDOM, ""), "");
|
||||||
EXPECT_NONFATAL_FAILURE(EXPECT_SYSTEM_ERROR(throw 1, EDOM, ""), "");
|
EXPECT_NONFATAL_FAILURE(EXPECT_SYSTEM_ERROR(throw 1, EDOM, ""), "");
|
||||||
EXPECT_NONFATAL_FAILURE(EXPECT_SYSTEM_ERROR(
|
EXPECT_NONFATAL_FAILURE(EXPECT_SYSTEM_ERROR(
|
||||||
throw fmt::SystemError(EDOM, "aaa"), EDOM, "bbb"), "");
|
throw fmt::system_error(EDOM, "aaa"), EDOM, "bbb"), "");
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(AssertionSyntaxTest, ExceptionAssertionBehavesLikeSingleStatement) {
|
TEST(AssertionSyntaxTest, ExceptionAssertionBehavesLikeSingleStatement) {
|
||||||
@ -268,10 +268,10 @@ TEST(ExpectTest, EXPECT_SYSTEM_ERROR) {
|
|||||||
EXPECT_NONFATAL_FAILURE(
|
EXPECT_NONFATAL_FAILURE(
|
||||||
EXPECT_SYSTEM_ERROR(throw_exception(), EDOM, "test"),
|
EXPECT_SYSTEM_ERROR(throw_exception(), EDOM, "test"),
|
||||||
"Expected: throw_exception() throws an exception of "
|
"Expected: throw_exception() throws an exception of "
|
||||||
"type fmt::SystemError.\n Actual: it throws a different type.");
|
"type fmt::system_error.\n Actual: it throws a different type.");
|
||||||
EXPECT_NONFATAL_FAILURE(
|
EXPECT_NONFATAL_FAILURE(
|
||||||
EXPECT_SYSTEM_ERROR(do_nothing(), EDOM, "test"),
|
EXPECT_SYSTEM_ERROR(do_nothing(), EDOM, "test"),
|
||||||
"Expected: do_nothing() throws an exception of type fmt::SystemError.\n"
|
"Expected: do_nothing() throws an exception of type fmt::system_error.\n"
|
||||||
" Actual: it throws nothing.");
|
" Actual: it throws nothing.");
|
||||||
EXPECT_NONFATAL_FAILURE(
|
EXPECT_NONFATAL_FAILURE(
|
||||||
EXPECT_SYSTEM_ERROR(throw_system_error(), EDOM, "other"),
|
EXPECT_SYSTEM_ERROR(throw_system_error(), EDOM, "other"),
|
||||||
@ -319,9 +319,9 @@ TEST(StreamingAssertionsTest, EXPECT_WRITE) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
TEST(UtilTest, FormatSystemError) {
|
TEST(UtilTest, FormatSystemError) {
|
||||||
fmt::MemoryWriter out;
|
fmt::memory_buffer out;
|
||||||
fmt::format_system_error(out, EDOM, "test message");
|
fmt::format_system_error(out, EDOM, "test message");
|
||||||
EXPECT_EQ(out.str(), format_system_error(EDOM, "test message"));
|
EXPECT_EQ(to_string(out), format_system_error(EDOM, "test message"));
|
||||||
}
|
}
|
||||||
|
|
||||||
#if FMT_USE_FILE_DESCRIPTORS
|
#if FMT_USE_FILE_DESCRIPTORS
|
||||||
|
@ -38,7 +38,7 @@ void OutputRedirect::flush() {
|
|||||||
int result = 0;
|
int result = 0;
|
||||||
FMT_RETRY(result, fflush(file_));
|
FMT_RETRY(result, fflush(file_));
|
||||||
if (result != 0)
|
if (result != 0)
|
||||||
throw fmt::SystemError(errno, "cannot flush stream");
|
throw fmt::system_error(errno, "cannot flush stream");
|
||||||
}
|
}
|
||||||
|
|
||||||
void OutputRedirect::restore() {
|
void OutputRedirect::restore() {
|
||||||
@ -103,8 +103,8 @@ std::string read(File &f, std::size_t count) {
|
|||||||
|
|
||||||
#endif // FMT_USE_FILE_DESCRIPTORS
|
#endif // FMT_USE_FILE_DESCRIPTORS
|
||||||
|
|
||||||
std::string format_system_error(int error_code, fmt::StringRef message) {
|
std::string format_system_error(int error_code, fmt::string_view message) {
|
||||||
fmt::MemoryWriter out;
|
fmt::memory_buffer out;
|
||||||
fmt::format_system_error(out, error_code, message);
|
format_system_error(out, error_code, message);
|
||||||
return out.str();
|
return to_string(out);
|
||||||
}
|
}
|
||||||
|
@ -31,7 +31,7 @@
|
|||||||
#include <string>
|
#include <string>
|
||||||
#include <gmock/gmock.h>
|
#include <gmock/gmock.h>
|
||||||
|
|
||||||
#include "fmt/format.h"
|
#include "fmt/core.h"
|
||||||
|
|
||||||
#ifndef FMT_USE_FILE_DESCRIPTORS
|
#ifndef FMT_USE_FILE_DESCRIPTORS
|
||||||
# define FMT_USE_FILE_DESCRIPTORS 0
|
# define FMT_USE_FILE_DESCRIPTORS 0
|
||||||
@ -81,10 +81,10 @@
|
|||||||
FMT_TEST_THROW_(statement, expected_exception, \
|
FMT_TEST_THROW_(statement, expected_exception, \
|
||||||
expected_message, GTEST_NONFATAL_FAILURE_)
|
expected_message, GTEST_NONFATAL_FAILURE_)
|
||||||
|
|
||||||
std::string format_system_error(int error_code, fmt::StringRef message);
|
std::string format_system_error(int error_code, fmt::string_view message);
|
||||||
|
|
||||||
#define EXPECT_SYSTEM_ERROR(statement, error_code, message) \
|
#define EXPECT_SYSTEM_ERROR(statement, error_code, message) \
|
||||||
EXPECT_THROW_MSG(statement, fmt::SystemError, \
|
EXPECT_THROW_MSG(statement, fmt::system_error, \
|
||||||
format_system_error(error_code, message))
|
format_system_error(error_code, message))
|
||||||
|
|
||||||
#if FMT_USE_FILE_DESCRIPTORS
|
#if FMT_USE_FILE_DESCRIPTORS
|
||||||
|
@ -1,3 +1,3 @@
|
|||||||
// Header-only configuration test
|
// Header-only configuration test
|
||||||
|
|
||||||
#include "fmt/format.h"
|
#include "fmt/core.h"
|
||||||
|
@ -1,3 +1,3 @@
|
|||||||
// Additional translation unit for the header-only configuration test
|
// Additional translation unit for the header-only configuration test
|
||||||
|
|
||||||
#include "fmt/format.h"
|
#include "fmt/core.h"
|
||||||
|
@ -1,115 +0,0 @@
|
|||||||
/*
|
|
||||||
Tests of variadic function emulation macros.
|
|
||||||
|
|
||||||
Copyright (c) 2012-2014, Victor Zverovich
|
|
||||||
All rights reserved.
|
|
||||||
|
|
||||||
Redistribution and use in source and binary forms, with or without
|
|
||||||
modification, are permitted provided that the following conditions are met:
|
|
||||||
|
|
||||||
1. Redistributions of source code must retain the above copyright notice, this
|
|
||||||
list of conditions and the following disclaimer.
|
|
||||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
|
||||||
this list of conditions and the following disclaimer in the documentation
|
|
||||||
and/or other materials provided with the distribution.
|
|
||||||
|
|
||||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
|
||||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
|
||||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
||||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
|
||||||
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
|
||||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
|
||||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
|
||||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
||||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
|
||||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <utility>
|
|
||||||
#include <gtest/gtest.h>
|
|
||||||
|
|
||||||
#define FMT_USE_VARIADIC_TEMPLATES 0
|
|
||||||
#include "fmt/format.h"
|
|
||||||
|
|
||||||
#define IDENTITY(x) x
|
|
||||||
|
|
||||||
TEST(UtilTest, Gen) {
|
|
||||||
int values[] = {FMT_GEN(10, IDENTITY)};
|
|
||||||
for (int i = 0; i < 10; ++i)
|
|
||||||
EXPECT_EQ(i, values[i]);
|
|
||||||
}
|
|
||||||
|
|
||||||
#define MAKE_PAIR(x, y) std::make_pair(x, y)
|
|
||||||
|
|
||||||
TEST(UtilTest, ForEach) {
|
|
||||||
std::pair<char, int> values[] = {
|
|
||||||
FMT_FOR_EACH10(MAKE_PAIR, 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j')
|
|
||||||
};
|
|
||||||
for (int i = 0; i < 10; ++i) {
|
|
||||||
EXPECT_EQ('a' + i, values[i].first);
|
|
||||||
EXPECT_EQ(i, values[i].second);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(UtilTest, NArg) {
|
|
||||||
EXPECT_EQ(1, FMT_NARG(a));
|
|
||||||
EXPECT_EQ(2, FMT_NARG(a, b));
|
|
||||||
EXPECT_EQ(3, FMT_NARG(a, b, c));
|
|
||||||
EXPECT_EQ(4, FMT_NARG(a, b, c, d));
|
|
||||||
EXPECT_EQ(5, FMT_NARG(a, b, c, d, e));
|
|
||||||
EXPECT_EQ(6, FMT_NARG(a, b, c, d, e, f));
|
|
||||||
EXPECT_EQ(7, FMT_NARG(a, b, c, d, e, f, g));
|
|
||||||
EXPECT_EQ(8, FMT_NARG(a, b, c, d, e, f, g, h));
|
|
||||||
EXPECT_EQ(9, FMT_NARG(a, b, c, d, e, f, g, h, i));
|
|
||||||
EXPECT_EQ(10, FMT_NARG(a, b, c, d, e, f, g, h, i, j));
|
|
||||||
}
|
|
||||||
|
|
||||||
int result;
|
|
||||||
|
|
||||||
#define MAKE_TEST(func) \
|
|
||||||
void func(const char *, const fmt::ArgList &args) { \
|
|
||||||
result = 0; \
|
|
||||||
for (unsigned i = 0; args[i].type; ++i) \
|
|
||||||
result += args[i].int_value; \
|
|
||||||
}
|
|
||||||
|
|
||||||
MAKE_TEST(test_func)
|
|
||||||
|
|
||||||
typedef char Char;
|
|
||||||
FMT_WRAP1(test_func, const char *, 1)
|
|
||||||
|
|
||||||
TEST(UtilTest, Wrap1) {
|
|
||||||
result = 0;
|
|
||||||
test_func("", 42);
|
|
||||||
EXPECT_EQ(42, result);
|
|
||||||
}
|
|
||||||
|
|
||||||
MAKE_TEST(test_variadic_void)
|
|
||||||
FMT_VARIADIC_VOID(test_variadic_void, const char *)
|
|
||||||
|
|
||||||
TEST(UtilTest, VariadicVoid) {
|
|
||||||
result = 0;
|
|
||||||
test_variadic_void("", 10, 20, 30, 40, 50, 60, 70, 80, 90, 100);
|
|
||||||
EXPECT_EQ(550, result);
|
|
||||||
}
|
|
||||||
|
|
||||||
template <int>
|
|
||||||
struct S {};
|
|
||||||
|
|
||||||
#define GET_TYPE(n) S<n>
|
|
||||||
|
|
||||||
int test_variadic(FMT_GEN(10, GET_TYPE), const fmt::ArgList &args) { \
|
|
||||||
int result = 0; \
|
|
||||||
for (unsigned i = 0; args[i].type; ++i) \
|
|
||||||
result += args[i].int_value; \
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
FMT_VARIADIC(int, test_variadic,
|
|
||||||
S<0>, S<1>, S<2>, S<3>, S<4>, S<5>, S<6>, S<7>, S<8>, S<9>)
|
|
||||||
|
|
||||||
#define MAKE_ARG(n) S<n>()
|
|
||||||
|
|
||||||
TEST(UtilTest, Variadic) {
|
|
||||||
EXPECT_EQ(550, test_variadic(FMT_GEN(10, MAKE_ARG),
|
|
||||||
10, 20, 30, 40, 50, 60, 70, 80, 90, 100));
|
|
||||||
}
|
|
@ -36,8 +36,8 @@ class MockAllocator {
|
|||||||
MockAllocator() {}
|
MockAllocator() {}
|
||||||
MockAllocator(const MockAllocator &) {}
|
MockAllocator(const MockAllocator &) {}
|
||||||
typedef T value_type;
|
typedef T value_type;
|
||||||
MOCK_METHOD2_T(allocate, T *(std::size_t n, const void *h));
|
MOCK_METHOD1_T(allocate, T* (std::size_t n));
|
||||||
MOCK_METHOD2_T(deallocate, void (T *p, std::size_t n));
|
MOCK_METHOD2_T(deallocate, void (T* p, std::size_t n));
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename Allocator>
|
template <typename Allocator>
|
||||||
@ -78,14 +78,10 @@ class AllocatorRef {
|
|||||||
|
|
||||||
Allocator *get() const { return alloc_; }
|
Allocator *get() const { return alloc_; }
|
||||||
|
|
||||||
value_type *allocate(std::size_t n, const void *h) {
|
value_type* allocate(std::size_t n) {
|
||||||
#if FMT_USE_ALLOCATOR_TRAITS
|
return fmt::internal::allocate(*alloc_, n);
|
||||||
return std::allocator_traits<Allocator>::allocate(*alloc_, n, h);
|
|
||||||
#else
|
|
||||||
return alloc_->allocate(n, h);
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
void deallocate(value_type *p, std::size_t n) { alloc_->deallocate(p, n); }
|
void deallocate(value_type* p, std::size_t n) { alloc_->deallocate(p, n); }
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // FMT_MOCK_ALLOCATOR_H_
|
#endif // FMT_MOCK_ALLOCATOR_H_
|
||||||
|
@ -33,7 +33,7 @@
|
|||||||
#include "util.h"
|
#include "util.h"
|
||||||
|
|
||||||
using fmt::format;
|
using fmt::format;
|
||||||
using fmt::FormatError;
|
using fmt::format_error;
|
||||||
|
|
||||||
std::ostream &operator<<(std::ostream &os, const Date &d) {
|
std::ostream &operator<<(std::ostream &os, const Date &d) {
|
||||||
os << d.year() << '-' << d.month() << '-' << d.day();
|
os << d.year() << '-' << d.month() << '-' << d.day();
|
||||||
@ -53,25 +53,26 @@ std::ostream &operator<<(std::ostream &os, TestEnum) {
|
|||||||
enum TestEnum2 {A};
|
enum TestEnum2 {A};
|
||||||
|
|
||||||
TEST(OStreamTest, Enum) {
|
TEST(OStreamTest, Enum) {
|
||||||
EXPECT_FALSE(fmt::internal::ConvertToInt<TestEnum>::value);
|
EXPECT_FALSE(fmt::internal::convert_to_int<TestEnum>::value);
|
||||||
EXPECT_EQ("TestEnum", fmt::format("{}", TestEnum()));
|
EXPECT_EQ("TestEnum", fmt::format("{}", TestEnum()));
|
||||||
EXPECT_EQ("0", fmt::format("{}", A));
|
EXPECT_EQ("0", fmt::format("{}", A));
|
||||||
}
|
}
|
||||||
|
|
||||||
struct TestArgFormatter : fmt::BasicArgFormatter<TestArgFormatter, char> {
|
using range = fmt::back_insert_range<fmt::internal::buffer>;
|
||||||
TestArgFormatter(fmt::BasicFormatter<char, TestArgFormatter> &f,
|
|
||||||
fmt::FormatSpec &s, const char *fmt)
|
struct test_arg_formatter: fmt::arg_formatter<range> {
|
||||||
: fmt::BasicArgFormatter<TestArgFormatter, char>(f, s, fmt) {}
|
test_arg_formatter(fmt::context &ctx, fmt::format_specs &s)
|
||||||
|
: fmt::arg_formatter<range>(ctx, s) {}
|
||||||
};
|
};
|
||||||
|
|
||||||
TEST(OStreamTest, CustomArg) {
|
TEST(OStreamTest, CustomArg) {
|
||||||
fmt::MemoryWriter writer;
|
fmt::memory_buffer buffer;
|
||||||
typedef fmt::BasicFormatter<char, TestArgFormatter> Formatter;
|
fmt::internal::buffer &base = buffer;
|
||||||
Formatter formatter(fmt::ArgList(), writer);
|
fmt::context ctx(std::back_inserter(base), "", fmt::format_args());
|
||||||
fmt::FormatSpec spec;
|
fmt::format_specs spec;
|
||||||
TestArgFormatter af(formatter, spec, "}");
|
test_arg_formatter af(ctx, spec);
|
||||||
af.visit(fmt::internal::MakeArg<Formatter>(TestEnum()));
|
visit(af, fmt::internal::make_arg<fmt::context>(TestEnum()));
|
||||||
EXPECT_EQ("TestEnum", writer.str());
|
EXPECT_EQ("TestEnum", std::string(buffer.data(), buffer.size()));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(OStreamTest, Format) {
|
TEST(OStreamTest, Format) {
|
||||||
@ -87,19 +88,19 @@ TEST(OStreamTest, FormatSpecs) {
|
|||||||
EXPECT_EQ("def ", format("{0:<5}", TestString("def")));
|
EXPECT_EQ("def ", format("{0:<5}", TestString("def")));
|
||||||
EXPECT_EQ(" def", format("{0:>5}", TestString("def")));
|
EXPECT_EQ(" def", format("{0:>5}", TestString("def")));
|
||||||
EXPECT_THROW_MSG(format("{0:=5}", TestString("def")),
|
EXPECT_THROW_MSG(format("{0:=5}", TestString("def")),
|
||||||
FormatError, "format specifier '=' requires numeric argument");
|
format_error, "format specifier requires numeric argument");
|
||||||
EXPECT_EQ(" def ", format("{0:^5}", TestString("def")));
|
EXPECT_EQ(" def ", format("{0:^5}", TestString("def")));
|
||||||
EXPECT_EQ("def**", format("{0:*<5}", TestString("def")));
|
EXPECT_EQ("def**", format("{0:*<5}", TestString("def")));
|
||||||
EXPECT_THROW_MSG(format("{0:+}", TestString()),
|
EXPECT_THROW_MSG(format("{0:+}", TestString()),
|
||||||
FormatError, "format specifier '+' requires numeric argument");
|
format_error, "format specifier requires numeric argument");
|
||||||
EXPECT_THROW_MSG(format("{0:-}", TestString()),
|
EXPECT_THROW_MSG(format("{0:-}", TestString()),
|
||||||
FormatError, "format specifier '-' requires numeric argument");
|
format_error, "format specifier requires numeric argument");
|
||||||
EXPECT_THROW_MSG(format("{0: }", TestString()),
|
EXPECT_THROW_MSG(format("{0: }", TestString()),
|
||||||
FormatError, "format specifier ' ' requires numeric argument");
|
format_error, "format specifier requires numeric argument");
|
||||||
EXPECT_THROW_MSG(format("{0:#}", TestString()),
|
EXPECT_THROW_MSG(format("{0:#}", TestString()),
|
||||||
FormatError, "format specifier '#' requires numeric argument");
|
format_error, "format specifier requires numeric argument");
|
||||||
EXPECT_THROW_MSG(format("{0:05}", TestString()),
|
EXPECT_THROW_MSG(format("{0:05}", TestString()),
|
||||||
FormatError, "format specifier '0' requires numeric argument");
|
format_error, "format specifier requires numeric argument");
|
||||||
EXPECT_EQ("test ", format("{0:13}", TestString("test")));
|
EXPECT_EQ("test ", format("{0:13}", TestString("test")));
|
||||||
EXPECT_EQ("test ", format("{0:{1}}", TestString("test"), 13));
|
EXPECT_EQ("test ", format("{0:{1}}", TestString("test"), 13));
|
||||||
EXPECT_EQ("te", format("{0:.2}", TestString("test")));
|
EXPECT_EQ("te", format("{0:.2}", TestString("test")));
|
||||||
@ -123,9 +124,10 @@ TEST(OStreamTest, Print) {
|
|||||||
|
|
||||||
TEST(OStreamTest, WriteToOStream) {
|
TEST(OStreamTest, WriteToOStream) {
|
||||||
std::ostringstream os;
|
std::ostringstream os;
|
||||||
fmt::MemoryWriter w;
|
fmt::memory_buffer buffer;
|
||||||
w << "foo";
|
const char *foo = "foo";
|
||||||
fmt::internal::write(os, w);
|
buffer.append(foo, foo + std::strlen(foo));
|
||||||
|
fmt::internal::write(os, buffer);
|
||||||
EXPECT_EQ("foo", os.str());
|
EXPECT_EQ("foo", os.str());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -135,55 +137,34 @@ TEST(OStreamTest, WriteToOStreamMaxSize) {
|
|||||||
if (max_size <= fmt::internal::to_unsigned(max_streamsize))
|
if (max_size <= fmt::internal::to_unsigned(max_streamsize))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
class TestWriter : public fmt::BasicWriter<char> {
|
struct test_buffer : fmt::internal::buffer {
|
||||||
private:
|
explicit test_buffer(std::size_t size) { resize(size); }
|
||||||
struct TestBuffer : fmt::Buffer<char> {
|
void grow(std::size_t) {}
|
||||||
explicit TestBuffer(std::size_t size) { size_ = size; }
|
} buffer(max_size);
|
||||||
void grow(std::size_t) {}
|
|
||||||
} buffer_;
|
|
||||||
public:
|
|
||||||
explicit TestWriter(std::size_t size)
|
|
||||||
: fmt::BasicWriter<char>(buffer_), buffer_(size) {}
|
|
||||||
} w(max_size);
|
|
||||||
|
|
||||||
struct MockStreamBuf : std::streambuf {
|
struct mock_streambuf : std::streambuf {
|
||||||
MOCK_METHOD2(xsputn, std::streamsize (const void *s, std::streamsize n));
|
MOCK_METHOD2(xsputn, std::streamsize (const void *s, std::streamsize n));
|
||||||
std::streamsize xsputn(const char *s, std::streamsize n) {
|
std::streamsize xsputn(const char *s, std::streamsize n) {
|
||||||
const void *v = s;
|
const void *v = s;
|
||||||
return xsputn(v, n);
|
return xsputn(v, n);
|
||||||
}
|
}
|
||||||
} buffer;
|
} streambuf;
|
||||||
|
|
||||||
struct TestOStream : std::ostream {
|
struct test_ostream : std::ostream {
|
||||||
explicit TestOStream(MockStreamBuf &buffer) : std::ostream(&buffer) {}
|
explicit test_ostream(mock_streambuf &buffer) : std::ostream(&buffer) {}
|
||||||
} os(buffer);
|
} os(streambuf);
|
||||||
|
|
||||||
testing::InSequence sequence;
|
testing::InSequence sequence;
|
||||||
const char *data = 0;
|
const char *data = 0;
|
||||||
std::size_t size = max_size;
|
std::size_t size = max_size;
|
||||||
do {
|
do {
|
||||||
typedef fmt::internal::MakeUnsigned<std::streamsize>::Type UStreamSize;
|
typedef std::make_unsigned<std::streamsize>::type ustreamsize;
|
||||||
UStreamSize n = std::min<UStreamSize>(
|
ustreamsize n = std::min<ustreamsize>(
|
||||||
size, fmt::internal::to_unsigned(max_streamsize));
|
size, fmt::internal::to_unsigned(max_streamsize));
|
||||||
EXPECT_CALL(buffer, xsputn(data, static_cast<std::streamsize>(n)))
|
EXPECT_CALL(streambuf, xsputn(data, static_cast<std::streamsize>(n)))
|
||||||
.WillOnce(testing::Return(max_streamsize));
|
.WillOnce(testing::Return(max_streamsize));
|
||||||
data += n;
|
data += n;
|
||||||
size -= static_cast<std::size_t>(n);
|
size -= static_cast<std::size_t>(n);
|
||||||
} while (size != 0);
|
} while (size != 0);
|
||||||
fmt::internal::write(os, w);
|
fmt::internal::write(os, buffer);
|
||||||
}
|
|
||||||
|
|
||||||
struct ConvertibleToInt {
|
|
||||||
template <typename ValueType>
|
|
||||||
operator ValueType() const {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
friend std::ostream &operator<<(std::ostream &o, ConvertibleToInt) {
|
|
||||||
return o << "foo";
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
TEST(FormatTest, FormatConvertibleToInt) {
|
|
||||||
EXPECT_EQ("foo", fmt::format("{}", ConvertibleToInt()));
|
|
||||||
}
|
}
|
||||||
|
@ -213,17 +213,11 @@ int (test::fileno)(FILE *stream) {
|
|||||||
# define EXPECT_EQ_POSIX(expected, actual)
|
# define EXPECT_EQ_POSIX(expected, actual)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
void write_file(fmt::CStringRef filename, fmt::StringRef content) {
|
void write_file(fmt::cstring_view filename, fmt::string_view content) {
|
||||||
fmt::BufferedFile f(filename, "w");
|
fmt::BufferedFile f(filename, "w");
|
||||||
f.print("{}", content);
|
f.print("{}", content);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(UtilTest, StaticAssert) {
|
|
||||||
FMT_STATIC_ASSERT(true, "success");
|
|
||||||
// Static assertion failure is tested in compile-test because it causes
|
|
||||||
// a compile-time error.
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(UtilTest, GetPageSize) {
|
TEST(UtilTest, GetPageSize) {
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
SYSTEM_INFO si = {};
|
SYSTEM_INFO si = {};
|
||||||
@ -277,13 +271,13 @@ TEST(FileTest, Size) {
|
|||||||
write_file("test", content);
|
write_file("test", content);
|
||||||
File f("test", File::RDONLY);
|
File f("test", File::RDONLY);
|
||||||
EXPECT_GE(f.size(), 0);
|
EXPECT_GE(f.size(), 0);
|
||||||
EXPECT_EQ(content.size(), static_cast<fmt::ULongLong>(f.size()));
|
EXPECT_EQ(content.size(), static_cast<unsigned long long>(f.size()));
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
fmt::MemoryWriter message;
|
fmt::memory_buffer message;
|
||||||
fmt::internal::format_windows_error(
|
fmt::internal::format_windows_error(
|
||||||
message, ERROR_ACCESS_DENIED, "cannot get file size");
|
message, ERROR_ACCESS_DENIED, "cannot get file size");
|
||||||
fstat_sim = ERROR;
|
fstat_sim = ERROR;
|
||||||
EXPECT_THROW_MSG(f.size(), fmt::WindowsError, message.str());
|
EXPECT_THROW_MSG(f.size(), fmt::windows_error, fmt::to_string(message));
|
||||||
fstat_sim = NONE;
|
fstat_sim = NONE;
|
||||||
#else
|
#else
|
||||||
f.close();
|
f.close();
|
||||||
@ -340,7 +334,7 @@ TEST(FileTest, ConvertReadCount) {
|
|||||||
++size;
|
++size;
|
||||||
read_count = 1;
|
read_count = 1;
|
||||||
read_nbyte = 0;
|
read_nbyte = 0;
|
||||||
EXPECT_THROW(read_end.read(&c, size), fmt::SystemError);
|
EXPECT_THROW(read_end.read(&c, size), fmt::system_error);
|
||||||
read_count = 0;
|
read_count = 0;
|
||||||
EXPECT_EQ(UINT_MAX, read_nbyte);
|
EXPECT_EQ(UINT_MAX, read_nbyte);
|
||||||
}
|
}
|
||||||
@ -354,7 +348,7 @@ TEST(FileTest, ConvertWriteCount) {
|
|||||||
++size;
|
++size;
|
||||||
write_count = 1;
|
write_count = 1;
|
||||||
write_nbyte = 0;
|
write_nbyte = 0;
|
||||||
EXPECT_THROW(write_end.write(&c, size), fmt::SystemError);
|
EXPECT_THROW(write_end.write(&c, size), fmt::system_error);
|
||||||
write_count = 0;
|
write_count = 0;
|
||||||
EXPECT_EQ(UINT_MAX, write_nbyte);
|
EXPECT_EQ(UINT_MAX, write_nbyte);
|
||||||
}
|
}
|
||||||
@ -415,7 +409,7 @@ TEST(BufferedFileTest, OpenRetry) {
|
|||||||
#ifndef _WIN32
|
#ifndef _WIN32
|
||||||
char c = 0;
|
char c = 0;
|
||||||
if (fread(&c, 1, 1, f->get()) < 1)
|
if (fread(&c, 1, 1, f->get()) < 1)
|
||||||
throw fmt::SystemError(errno, "fread failed");
|
throw fmt::system_error(errno, "fread failed");
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -65,7 +65,7 @@ File open_file() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Attempts to write a string to a file.
|
// Attempts to write a string to a file.
|
||||||
void write(File &f, fmt::StringRef s) {
|
void write(File &f, fmt::string_view s) {
|
||||||
std::size_t num_chars_left = s.size();
|
std::size_t num_chars_left = s.size();
|
||||||
const char *ptr = s.data();
|
const char *ptr = s.data();
|
||||||
do {
|
do {
|
||||||
@ -173,7 +173,7 @@ TEST(BufferedFileTest, Fileno) {
|
|||||||
EXPECT_DEATH_IF_SUPPORTED({
|
EXPECT_DEATH_IF_SUPPORTED({
|
||||||
try {
|
try {
|
||||||
f.fileno();
|
f.fileno();
|
||||||
} catch (fmt::SystemError) {
|
} catch (fmt::system_error) {
|
||||||
std::exit(1);
|
std::exit(1);
|
||||||
}
|
}
|
||||||
}, "");
|
}, "");
|
||||||
|
@ -29,19 +29,19 @@
|
|||||||
#include <climits>
|
#include <climits>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
|
|
||||||
|
#include "fmt/core.h"
|
||||||
#include "fmt/printf.h"
|
#include "fmt/printf.h"
|
||||||
#include "fmt/format.h"
|
|
||||||
#include "gtest-extra.h"
|
#include "gtest-extra.h"
|
||||||
#include "util.h"
|
#include "util.h"
|
||||||
|
|
||||||
using fmt::format;
|
using fmt::format;
|
||||||
using fmt::FormatError;
|
using fmt::format_error;
|
||||||
|
|
||||||
const unsigned BIG_NUM = INT_MAX + 1u;
|
const unsigned BIG_NUM = INT_MAX + 1u;
|
||||||
|
|
||||||
// Makes format string argument positional.
|
// Makes format string argument positional.
|
||||||
std::string make_positional(fmt::StringRef format) {
|
std::string make_positional(fmt::string_view format) {
|
||||||
std::string s(format.to_string());
|
std::string s(format.data(), format.size());
|
||||||
s.replace(s.find('%'), 1, "%1$");
|
s.replace(s.find('%'), 1, "%1$");
|
||||||
return s;
|
return s;
|
||||||
}
|
}
|
||||||
@ -80,45 +80,45 @@ TEST(PrintfTest, AutomaticArgIndexing) {
|
|||||||
|
|
||||||
TEST(PrintfTest, NumberIsTooBigInArgIndex) {
|
TEST(PrintfTest, NumberIsTooBigInArgIndex) {
|
||||||
EXPECT_THROW_MSG(fmt::sprintf(format("%{}$", BIG_NUM)),
|
EXPECT_THROW_MSG(fmt::sprintf(format("%{}$", BIG_NUM)),
|
||||||
FormatError, "number is too big");
|
format_error, "number is too big");
|
||||||
EXPECT_THROW_MSG(fmt::sprintf(format("%{}$d", BIG_NUM)),
|
EXPECT_THROW_MSG(fmt::sprintf(format("%{}$d", BIG_NUM)),
|
||||||
FormatError, "number is too big");
|
format_error, "number is too big");
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(PrintfTest, SwitchArgIndexing) {
|
TEST(PrintfTest, SwitchArgIndexing) {
|
||||||
EXPECT_THROW_MSG(fmt::sprintf("%1$d%", 1, 2),
|
EXPECT_THROW_MSG(fmt::sprintf("%1$d%", 1, 2),
|
||||||
FormatError, "invalid format string");
|
format_error, "cannot switch from manual to automatic argument indexing");
|
||||||
EXPECT_THROW_MSG(fmt::sprintf(format("%1$d%{}d", BIG_NUM), 1, 2),
|
EXPECT_THROW_MSG(fmt::sprintf(format("%1$d%{}d", BIG_NUM), 1, 2),
|
||||||
FormatError, "number is too big");
|
format_error, "number is too big");
|
||||||
EXPECT_THROW_MSG(fmt::sprintf("%1$d%d", 1, 2),
|
EXPECT_THROW_MSG(fmt::sprintf("%1$d%d", 1, 2),
|
||||||
FormatError, "cannot switch from manual to automatic argument indexing");
|
format_error, "cannot switch from manual to automatic argument indexing");
|
||||||
|
|
||||||
EXPECT_THROW_MSG(fmt::sprintf("%d%1$", 1, 2),
|
EXPECT_THROW_MSG(fmt::sprintf("%d%1$", 1, 2),
|
||||||
FormatError, "invalid format string");
|
format_error, "cannot switch from automatic to manual argument indexing");
|
||||||
EXPECT_THROW_MSG(fmt::sprintf(format("%d%{}$d", BIG_NUM), 1, 2),
|
EXPECT_THROW_MSG(fmt::sprintf(format("%d%{}$d", BIG_NUM), 1, 2),
|
||||||
FormatError, "number is too big");
|
format_error, "number is too big");
|
||||||
EXPECT_THROW_MSG(fmt::sprintf("%d%1$d", 1, 2),
|
EXPECT_THROW_MSG(fmt::sprintf("%d%1$d", 1, 2),
|
||||||
FormatError, "cannot switch from automatic to manual argument indexing");
|
format_error, "cannot switch from automatic to manual argument indexing");
|
||||||
|
|
||||||
// Indexing errors override width errors.
|
// Indexing errors override width errors.
|
||||||
EXPECT_THROW_MSG(fmt::sprintf(format("%d%1${}d", BIG_NUM), 1, 2),
|
EXPECT_THROW_MSG(fmt::sprintf(format("%d%1${}d", BIG_NUM), 1, 2),
|
||||||
FormatError, "number is too big");
|
format_error, "number is too big");
|
||||||
EXPECT_THROW_MSG(fmt::sprintf(format("%1$d%{}d", BIG_NUM), 1, 2),
|
EXPECT_THROW_MSG(fmt::sprintf(format("%1$d%{}d", BIG_NUM), 1, 2),
|
||||||
FormatError, "number is too big");
|
format_error, "number is too big");
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(PrintfTest, InvalidArgIndex) {
|
TEST(PrintfTest, InvalidArgIndex) {
|
||||||
EXPECT_THROW_MSG(fmt::sprintf("%0$d", 42), FormatError,
|
EXPECT_THROW_MSG(fmt::sprintf("%0$d", 42), format_error,
|
||||||
"argument index out of range");
|
"argument index out of range");
|
||||||
EXPECT_THROW_MSG(fmt::sprintf("%2$d", 42), FormatError,
|
EXPECT_THROW_MSG(fmt::sprintf("%2$d", 42), format_error,
|
||||||
"argument index out of range");
|
"argument index out of range");
|
||||||
EXPECT_THROW_MSG(fmt::sprintf(format("%{}$d", INT_MAX), 42),
|
EXPECT_THROW_MSG(fmt::sprintf(format("%{}$d", INT_MAX), 42),
|
||||||
FormatError, "argument index out of range");
|
format_error, "argument index out of range");
|
||||||
|
|
||||||
EXPECT_THROW_MSG(fmt::sprintf("%2$", 42),
|
EXPECT_THROW_MSG(fmt::sprintf("%2$", 42),
|
||||||
FormatError, "invalid format string");
|
format_error, "argument index out of range");
|
||||||
EXPECT_THROW_MSG(fmt::sprintf(format("%{}$d", BIG_NUM), 42),
|
EXPECT_THROW_MSG(fmt::sprintf(format("%{}$d", BIG_NUM), 42),
|
||||||
FormatError, "number is too big");
|
format_error, "number is too big");
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(PrintfTest, DefaultAlignRight) {
|
TEST(PrintfTest, DefaultAlignRight) {
|
||||||
@ -137,7 +137,7 @@ TEST(PrintfTest, ZeroFlag) {
|
|||||||
EXPECT_PRINTF("+00042", "%00+6d", 42);
|
EXPECT_PRINTF("+00042", "%00+6d", 42);
|
||||||
|
|
||||||
// '0' flag is ignored for non-numeric types.
|
// '0' flag is ignored for non-numeric types.
|
||||||
EXPECT_PRINTF(" x", "%05c", 'x');
|
EXPECT_PRINTF("0000x", "%05c", 'x');
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(PrintfTest, PlusFlag) {
|
TEST(PrintfTest, PlusFlag) {
|
||||||
@ -202,27 +202,25 @@ TEST(PrintfTest, HashFlag) {
|
|||||||
|
|
||||||
TEST(PrintfTest, Width) {
|
TEST(PrintfTest, Width) {
|
||||||
EXPECT_PRINTF(" abc", "%5s", "abc");
|
EXPECT_PRINTF(" abc", "%5s", "abc");
|
||||||
EXPECT_PRINTF(" -42", "%5s", "-42");
|
|
||||||
EXPECT_PRINTF(" 0.123456", "%10s", 0.123456);
|
|
||||||
|
|
||||||
// Width cannot be specified twice.
|
// Width cannot be specified twice.
|
||||||
EXPECT_THROW_MSG(fmt::sprintf("%5-5d", 42), FormatError,
|
EXPECT_THROW_MSG(fmt::sprintf("%5-5d", 42), format_error,
|
||||||
"unknown format code '-' for integer");
|
"invalid type specifier");
|
||||||
|
|
||||||
EXPECT_THROW_MSG(fmt::sprintf(format("%{}d", BIG_NUM), 42),
|
EXPECT_THROW_MSG(fmt::sprintf(format("%{}d", BIG_NUM), 42),
|
||||||
FormatError, "number is too big");
|
format_error, "number is too big");
|
||||||
EXPECT_THROW_MSG(fmt::sprintf(format("%1${}d", BIG_NUM), 42),
|
EXPECT_THROW_MSG(fmt::sprintf(format("%1${}d", BIG_NUM), 42),
|
||||||
FormatError, "number is too big");
|
format_error, "number is too big");
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(PrintfTest, DynamicWidth) {
|
TEST(PrintfTest, DynamicWidth) {
|
||||||
EXPECT_EQ(" 42", fmt::sprintf("%*d", 5, 42));
|
EXPECT_EQ(" 42", fmt::sprintf("%*d", 5, 42));
|
||||||
EXPECT_EQ("42 ", fmt::sprintf("%*d", -5, 42));
|
EXPECT_EQ("42 ", fmt::sprintf("%*d", -5, 42));
|
||||||
EXPECT_THROW_MSG(fmt::sprintf("%*d", 5.0, 42), FormatError,
|
EXPECT_THROW_MSG(fmt::sprintf("%*d", 5.0, 42), format_error,
|
||||||
"width is not integer");
|
"width is not integer");
|
||||||
EXPECT_THROW_MSG(fmt::sprintf("%*d"), FormatError,
|
EXPECT_THROW_MSG(fmt::sprintf("%*d"), format_error,
|
||||||
"argument index out of range");
|
"argument index out of range");
|
||||||
EXPECT_THROW_MSG(fmt::sprintf("%*d", BIG_NUM, 42), FormatError,
|
EXPECT_THROW_MSG(fmt::sprintf("%*d", BIG_NUM, 42), format_error,
|
||||||
"number is too big");
|
"number is too big");
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -265,41 +263,41 @@ TEST(PrintfTest, IgnorePrecisionForNonNumericArg) {
|
|||||||
TEST(PrintfTest, DynamicPrecision) {
|
TEST(PrintfTest, DynamicPrecision) {
|
||||||
EXPECT_EQ("00042", fmt::sprintf("%.*d", 5, 42));
|
EXPECT_EQ("00042", fmt::sprintf("%.*d", 5, 42));
|
||||||
EXPECT_EQ("42", fmt::sprintf("%.*d", -5, 42));
|
EXPECT_EQ("42", fmt::sprintf("%.*d", -5, 42));
|
||||||
EXPECT_THROW_MSG(fmt::sprintf("%.*d", 5.0, 42), FormatError,
|
EXPECT_THROW_MSG(fmt::sprintf("%.*d", 5.0, 42), format_error,
|
||||||
"precision is not integer");
|
"precision is not integer");
|
||||||
EXPECT_THROW_MSG(fmt::sprintf("%.*d"), FormatError,
|
EXPECT_THROW_MSG(fmt::sprintf("%.*d"), format_error,
|
||||||
"argument index out of range");
|
"argument index out of range");
|
||||||
EXPECT_THROW_MSG(fmt::sprintf("%.*d", BIG_NUM, 42), FormatError,
|
EXPECT_THROW_MSG(fmt::sprintf("%.*d", BIG_NUM, 42), format_error,
|
||||||
"number is too big");
|
"number is too big");
|
||||||
if (sizeof(fmt::LongLong) != sizeof(int)) {
|
if (sizeof(long long) != sizeof(int)) {
|
||||||
fmt::LongLong prec = static_cast<fmt::LongLong>(INT_MIN) - 1;
|
long long prec = static_cast<long long>(INT_MIN) - 1;
|
||||||
EXPECT_THROW_MSG(fmt::sprintf("%.*d", prec, 42), FormatError,
|
EXPECT_THROW_MSG(fmt::sprintf("%.*d", prec, 42), format_error,
|
||||||
"number is too big");
|
"number is too big");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
struct MakeSigned { typedef T Type; };
|
struct make_signed { typedef T type; };
|
||||||
|
|
||||||
#define SPECIALIZE_MAKE_SIGNED(T, S) \
|
#define SPECIALIZE_MAKE_SIGNED(T, S) \
|
||||||
template <> \
|
template <> \
|
||||||
struct MakeSigned<T> { typedef S Type; }
|
struct make_signed<T> { typedef S type; }
|
||||||
|
|
||||||
SPECIALIZE_MAKE_SIGNED(char, signed char);
|
SPECIALIZE_MAKE_SIGNED(char, signed char);
|
||||||
SPECIALIZE_MAKE_SIGNED(unsigned char, signed char);
|
SPECIALIZE_MAKE_SIGNED(unsigned char, signed char);
|
||||||
SPECIALIZE_MAKE_SIGNED(unsigned short, short);
|
SPECIALIZE_MAKE_SIGNED(unsigned short, short);
|
||||||
SPECIALIZE_MAKE_SIGNED(unsigned, int);
|
SPECIALIZE_MAKE_SIGNED(unsigned, int);
|
||||||
SPECIALIZE_MAKE_SIGNED(unsigned long, long);
|
SPECIALIZE_MAKE_SIGNED(unsigned long, long);
|
||||||
SPECIALIZE_MAKE_SIGNED(fmt::ULongLong, fmt::LongLong);
|
SPECIALIZE_MAKE_SIGNED(unsigned long long, long long);
|
||||||
|
|
||||||
// Test length format specifier ``length_spec``.
|
// Test length format specifier ``length_spec``.
|
||||||
template <typename T, typename U>
|
template <typename T, typename U>
|
||||||
void TestLength(const char *length_spec, U value) {
|
void TestLength(const char *length_spec, U value) {
|
||||||
fmt::LongLong signed_value = 0;
|
long long signed_value = 0;
|
||||||
fmt::ULongLong unsigned_value = 0;
|
unsigned long long unsigned_value = 0;
|
||||||
// Apply integer promotion to the argument.
|
// Apply integer promotion to the argument.
|
||||||
using std::numeric_limits;
|
using std::numeric_limits;
|
||||||
fmt::ULongLong max = numeric_limits<U>::max();
|
unsigned long long max = numeric_limits<U>::max();
|
||||||
using fmt::internal::const_check;
|
using fmt::internal::const_check;
|
||||||
if (const_check(max <= static_cast<unsigned>(numeric_limits<int>::max()))) {
|
if (const_check(max <= static_cast<unsigned>(numeric_limits<int>::max()))) {
|
||||||
signed_value = static_cast<int>(value);
|
signed_value = static_cast<int>(value);
|
||||||
@ -308,13 +306,13 @@ void TestLength(const char *length_spec, U value) {
|
|||||||
signed_value = static_cast<unsigned>(value);
|
signed_value = static_cast<unsigned>(value);
|
||||||
unsigned_value = static_cast<unsigned>(value);
|
unsigned_value = static_cast<unsigned>(value);
|
||||||
}
|
}
|
||||||
using fmt::internal::MakeUnsigned;
|
|
||||||
if (sizeof(U) <= sizeof(int) && sizeof(int) < sizeof(T)) {
|
if (sizeof(U) <= sizeof(int) && sizeof(int) < sizeof(T)) {
|
||||||
signed_value = static_cast<fmt::LongLong>(value);
|
signed_value = static_cast<long long>(value);
|
||||||
unsigned_value = static_cast<typename MakeUnsigned<unsigned>::Type>(value);
|
unsigned_value =
|
||||||
|
static_cast<typename std::make_unsigned<unsigned>::type>(value);
|
||||||
} else {
|
} else {
|
||||||
signed_value = static_cast<typename MakeSigned<T>::Type>(value);
|
signed_value = static_cast<typename make_signed<T>::type>(value);
|
||||||
unsigned_value = static_cast<typename MakeUnsigned<T>::Type>(value);
|
unsigned_value = static_cast<typename std::make_unsigned<T>::type>(value);
|
||||||
}
|
}
|
||||||
std::ostringstream os;
|
std::ostringstream os;
|
||||||
os << signed_value;
|
os << signed_value;
|
||||||
@ -341,20 +339,20 @@ void TestLength(const char *length_spec) {
|
|||||||
TestLength<T>(length_spec, -42);
|
TestLength<T>(length_spec, -42);
|
||||||
TestLength<T>(length_spec, min);
|
TestLength<T>(length_spec, min);
|
||||||
TestLength<T>(length_spec, max);
|
TestLength<T>(length_spec, max);
|
||||||
TestLength<T>(length_spec, fmt::LongLong(min) - 1);
|
TestLength<T>(length_spec, static_cast<long long>(min) - 1);
|
||||||
fmt::ULongLong long_long_max = std::numeric_limits<fmt::LongLong>::max();
|
unsigned long long long_long_max = std::numeric_limits<long long>::max();
|
||||||
if (static_cast<fmt::ULongLong>(max) < long_long_max)
|
if (static_cast<unsigned long long>(max) < long_long_max)
|
||||||
TestLength<T>(length_spec, fmt::LongLong(max) + 1);
|
TestLength<T>(length_spec, static_cast<long long>(max) + 1);
|
||||||
TestLength<T>(length_spec, std::numeric_limits<short>::min());
|
TestLength<T>(length_spec, std::numeric_limits<short>::min());
|
||||||
TestLength<T>(length_spec, std::numeric_limits<unsigned short>::max());
|
TestLength<T>(length_spec, std::numeric_limits<unsigned short>::max());
|
||||||
TestLength<T>(length_spec, std::numeric_limits<int>::min());
|
TestLength<T>(length_spec, std::numeric_limits<int>::min());
|
||||||
TestLength<T>(length_spec, std::numeric_limits<int>::max());
|
TestLength<T>(length_spec, std::numeric_limits<int>::max());
|
||||||
TestLength<T>(length_spec, std::numeric_limits<unsigned>::min());
|
TestLength<T>(length_spec, std::numeric_limits<unsigned>::min());
|
||||||
TestLength<T>(length_spec, std::numeric_limits<unsigned>::max());
|
TestLength<T>(length_spec, std::numeric_limits<unsigned>::max());
|
||||||
TestLength<T>(length_spec, std::numeric_limits<fmt::LongLong>::min());
|
TestLength<T>(length_spec, std::numeric_limits<long long>::min());
|
||||||
TestLength<T>(length_spec, std::numeric_limits<fmt::LongLong>::max());
|
TestLength<T>(length_spec, std::numeric_limits<long long>::max());
|
||||||
TestLength<T>(length_spec, std::numeric_limits<fmt::ULongLong>::min());
|
TestLength<T>(length_spec, std::numeric_limits<unsigned long long>::min());
|
||||||
TestLength<T>(length_spec, std::numeric_limits<fmt::ULongLong>::max());
|
TestLength<T>(length_spec, std::numeric_limits<unsigned long long>::max());
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(PrintfTest, Length) {
|
TEST(PrintfTest, Length) {
|
||||||
@ -365,8 +363,8 @@ TEST(PrintfTest, Length) {
|
|||||||
TestLength<unsigned short>("h");
|
TestLength<unsigned short>("h");
|
||||||
TestLength<long>("l");
|
TestLength<long>("l");
|
||||||
TestLength<unsigned long>("l");
|
TestLength<unsigned long>("l");
|
||||||
TestLength<fmt::LongLong>("ll");
|
TestLength<long long>("ll");
|
||||||
TestLength<fmt::ULongLong>("ll");
|
TestLength<unsigned long long>("ll");
|
||||||
TestLength<intmax_t>("j");
|
TestLength<intmax_t>("j");
|
||||||
TestLength<std::size_t>("z");
|
TestLength<std::size_t>("z");
|
||||||
TestLength<std::ptrdiff_t>("t");
|
TestLength<std::ptrdiff_t>("t");
|
||||||
@ -383,19 +381,17 @@ TEST(PrintfTest, Bool) {
|
|||||||
TEST(PrintfTest, Int) {
|
TEST(PrintfTest, Int) {
|
||||||
EXPECT_PRINTF("-42", "%d", -42);
|
EXPECT_PRINTF("-42", "%d", -42);
|
||||||
EXPECT_PRINTF("-42", "%i", -42);
|
EXPECT_PRINTF("-42", "%i", -42);
|
||||||
EXPECT_PRINTF("-42", "%s", -42);
|
|
||||||
unsigned u = 0 - 42u;
|
unsigned u = 0 - 42u;
|
||||||
EXPECT_PRINTF(fmt::format("{}", u), "%u", -42);
|
EXPECT_PRINTF(fmt::format("{}", u), "%u", -42);
|
||||||
EXPECT_PRINTF(fmt::format("{:o}", u), "%o", -42);
|
EXPECT_PRINTF(fmt::format("{:o}", u), "%o", -42);
|
||||||
EXPECT_PRINTF(fmt::format("{:x}", u), "%x", -42);
|
EXPECT_PRINTF(fmt::format("{:x}", u), "%x", -42);
|
||||||
EXPECT_PRINTF(fmt::format("{:X}", u), "%X", -42);
|
EXPECT_PRINTF(fmt::format("{:X}", u), "%X", -42);
|
||||||
EXPECT_PRINTF(fmt::format("{}", u), "%s", u);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(PrintfTest, LongLong) {
|
TEST(PrintfTest, long_long) {
|
||||||
// fmt::printf allows passing long long arguments to %d without length
|
// fmt::printf allows passing long long arguments to %d without length
|
||||||
// specifiers.
|
// specifiers.
|
||||||
fmt::LongLong max = std::numeric_limits<fmt::LongLong>::max();
|
long long max = std::numeric_limits<long long>::max();
|
||||||
EXPECT_PRINTF(fmt::format("{}", max), "%d", max);
|
EXPECT_PRINTF(fmt::format("{}", max), "%d", max);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -405,7 +401,6 @@ TEST(PrintfTest, Float) {
|
|||||||
EXPECT_PRINTF("392.6", "%.1f", 392.65);
|
EXPECT_PRINTF("392.6", "%.1f", 392.65);
|
||||||
EXPECT_PRINTF("393", "%.f", 392.65);
|
EXPECT_PRINTF("393", "%.f", 392.65);
|
||||||
EXPECT_PRINTF("392.650000", "%F", 392.65);
|
EXPECT_PRINTF("392.650000", "%F", 392.65);
|
||||||
EXPECT_PRINTF("392.65", "%s", 392.65);
|
|
||||||
char buffer[BUFFER_SIZE];
|
char buffer[BUFFER_SIZE];
|
||||||
safe_sprintf(buffer, "%e", 392.65);
|
safe_sprintf(buffer, "%e", 392.65);
|
||||||
EXPECT_PRINTF(buffer, "%e", 392.65);
|
EXPECT_PRINTF(buffer, "%e", 392.65);
|
||||||
@ -430,7 +425,6 @@ TEST(PrintfTest, Inf) {
|
|||||||
|
|
||||||
TEST(PrintfTest, Char) {
|
TEST(PrintfTest, Char) {
|
||||||
EXPECT_PRINTF("x", "%c", 'x');
|
EXPECT_PRINTF("x", "%c", 'x');
|
||||||
EXPECT_PRINTF("x", "%s", 'x');
|
|
||||||
int max = std::numeric_limits<int>::max();
|
int max = std::numeric_limits<int>::max();
|
||||||
EXPECT_PRINTF(fmt::format("{}", static_cast<char>(max)), "%c", max);
|
EXPECT_PRINTF(fmt::format("{}", static_cast<char>(max)), "%c", max);
|
||||||
//EXPECT_PRINTF("x", "%lc", L'x');
|
//EXPECT_PRINTF("x", "%lc", L'x');
|
||||||
@ -449,17 +443,13 @@ TEST(PrintfTest, Pointer) {
|
|||||||
int n;
|
int n;
|
||||||
void *p = &n;
|
void *p = &n;
|
||||||
EXPECT_PRINTF(fmt::format("{}", p), "%p", p);
|
EXPECT_PRINTF(fmt::format("{}", p), "%p", p);
|
||||||
EXPECT_PRINTF(fmt::format("{}", p), "%s", p);
|
|
||||||
p = 0;
|
p = 0;
|
||||||
EXPECT_PRINTF("(nil)", "%p", p);
|
EXPECT_PRINTF("(nil)", "%p", p);
|
||||||
EXPECT_PRINTF(" (nil)", "%10p", p);
|
EXPECT_PRINTF(" (nil)", "%10p", p);
|
||||||
EXPECT_PRINTF("(nil)", "%s", p);
|
|
||||||
EXPECT_PRINTF(" (nil)", "%10s", p);
|
|
||||||
const char *s = "test";
|
const char *s = "test";
|
||||||
EXPECT_PRINTF(fmt::format("{:p}", s), "%p", s);
|
EXPECT_PRINTF(fmt::format("{:p}", s), "%p", s);
|
||||||
const char *null_str = 0;
|
const char *null_str = 0;
|
||||||
EXPECT_PRINTF("(nil)", "%p", null_str);
|
EXPECT_PRINTF("(nil)", "%p", null_str);
|
||||||
EXPECT_PRINTF("(null)", "%s", null_str);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(PrintfTest, Location) {
|
TEST(PrintfTest, Location) {
|
||||||
@ -503,9 +493,3 @@ TEST(PrintfTest, OStream) {
|
|||||||
EXPECT_EQ("Don't panic!", os.str());
|
EXPECT_EQ("Don't panic!", os.str());
|
||||||
EXPECT_EQ(12, ret);
|
EXPECT_EQ(12, ret);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(PrintfTest, Writer) {
|
|
||||||
fmt::MemoryWriter writer;
|
|
||||||
printf(writer, "%d", 42);
|
|
||||||
EXPECT_EQ("42", writer.str());
|
|
||||||
}
|
|
||||||
|
@ -1,84 +0,0 @@
|
|||||||
/*
|
|
||||||
Tests of string utilities
|
|
||||||
|
|
||||||
Copyright (c) 2012 - 2016, Victor Zverovich
|
|
||||||
All rights reserved.
|
|
||||||
|
|
||||||
For the license information refer to format.h.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "fmt/string.h"
|
|
||||||
#include "gtest/gtest.h"
|
|
||||||
|
|
||||||
using fmt::internal::StringBuffer;
|
|
||||||
|
|
||||||
TEST(StringBufferTest, Empty) {
|
|
||||||
StringBuffer<char> buffer;
|
|
||||||
EXPECT_EQ(0u, buffer.size());
|
|
||||||
EXPECT_EQ(0u, buffer.capacity());
|
|
||||||
std::string data;
|
|
||||||
// std::string may have initial capacity.
|
|
||||||
std::size_t capacity = data.capacity();
|
|
||||||
buffer.move_to(data);
|
|
||||||
EXPECT_EQ("", data);
|
|
||||||
EXPECT_EQ(capacity, data.capacity());
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(StringBufferTest, Reserve) {
|
|
||||||
StringBuffer<char> buffer;
|
|
||||||
std::size_t capacity = std::string().capacity() + 10;
|
|
||||||
buffer.reserve(capacity);
|
|
||||||
EXPECT_EQ(0u, buffer.size());
|
|
||||||
EXPECT_EQ(capacity, buffer.capacity());
|
|
||||||
std::string data;
|
|
||||||
buffer.move_to(data);
|
|
||||||
EXPECT_EQ("", data);
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(StringBufferTest, Resize) {
|
|
||||||
StringBuffer<char> buffer;
|
|
||||||
std::size_t size = std::string().capacity() + 10;
|
|
||||||
buffer.resize(size);
|
|
||||||
EXPECT_EQ(size, buffer.size());
|
|
||||||
EXPECT_EQ(size, buffer.capacity());
|
|
||||||
std::string data;
|
|
||||||
buffer.move_to(data);
|
|
||||||
EXPECT_EQ(size, data.size());
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(StringBufferTest, MoveTo) {
|
|
||||||
StringBuffer<char> buffer;
|
|
||||||
std::size_t size = std::string().capacity() + 10;
|
|
||||||
buffer.resize(size);
|
|
||||||
const char *p = &buffer[0];
|
|
||||||
std::string data;
|
|
||||||
buffer.move_to(data);
|
|
||||||
EXPECT_EQ(p, &data[0]);
|
|
||||||
EXPECT_EQ(0u, buffer.size());
|
|
||||||
EXPECT_EQ(0u, buffer.capacity());
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(StringWriterTest, MoveTo) {
|
|
||||||
fmt::StringWriter out;
|
|
||||||
out << "The answer is " << 42 << "\n";
|
|
||||||
std::string s;
|
|
||||||
out.move_to(s);
|
|
||||||
EXPECT_EQ("The answer is 42\n", s);
|
|
||||||
EXPECT_EQ(0u, out.size());
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(StringWriterTest, WString) {
|
|
||||||
fmt::WStringWriter out;
|
|
||||||
out << "The answer is " << 42 << "\n";
|
|
||||||
std::wstring s;
|
|
||||||
out.move_to(s);
|
|
||||||
EXPECT_EQ(L"The answer is 42\n", s);
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(StringTest, ToString) {
|
|
||||||
EXPECT_EQ("42", fmt::to_string(42));
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(StringTest, ToWString) {
|
|
||||||
EXPECT_EQ(L"42", fmt::to_wstring(42));
|
|
||||||
}
|
|
@ -6,6 +6,7 @@
|
|||||||
|
|
||||||
For the license information refer to format.h.
|
For the license information refer to format.h.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifdef WIN32
|
#ifdef WIN32
|
||||||
#define _CRT_SECURE_NO_WARNINGS
|
#define _CRT_SECURE_NO_WARNINGS
|
||||||
#endif
|
#endif
|
||||||
|
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user