Externals: Add libfmt 5.3.0 to externals
Allows us to migrate off of printf specifiers and have more type-safe formatting facilities. It also allows for custom type support as well. fmt is also on track to have part of it standardized within C++2a, so this will also lessen the transitional work necessary later on by allowing new code to use it. This simply adds the library but doesn't do anything with it yet.
This commit is contained in:
parent
cea1b58919
commit
d5d60c6e64
|
@ -530,6 +530,7 @@ if (_M_X86)
|
||||||
add_subdirectory(Externals/Bochs_disasm)
|
add_subdirectory(Externals/Bochs_disasm)
|
||||||
endif()
|
endif()
|
||||||
add_subdirectory(Externals/cpp-optparse)
|
add_subdirectory(Externals/cpp-optparse)
|
||||||
|
add_subdirectory(Externals/fmt EXCLUDE_FROM_ALL)
|
||||||
add_subdirectory(Externals/glslang)
|
add_subdirectory(Externals/glslang)
|
||||||
add_subdirectory(Externals/imgui)
|
add_subdirectory(Externals/imgui)
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,34 @@
|
||||||
|
.vscode/
|
||||||
|
|
||||||
|
*.iml
|
||||||
|
.idea/
|
||||||
|
.externalNativeBuild/
|
||||||
|
.gradle/
|
||||||
|
gradle/
|
||||||
|
gradlew*
|
||||||
|
local.properties
|
||||||
|
build/
|
||||||
|
|
||||||
|
bin/
|
||||||
|
/_CPack_Packages
|
||||||
|
/CMakeScripts
|
||||||
|
/doc/doxyxml
|
||||||
|
/doc/html
|
||||||
|
virtualenv
|
||||||
|
/Testing
|
||||||
|
/install_manifest.txt
|
||||||
|
*~
|
||||||
|
*.a
|
||||||
|
*.so*
|
||||||
|
*.xcodeproj
|
||||||
|
*.zip
|
||||||
|
cmake_install.cmake
|
||||||
|
CPack*.cmake
|
||||||
|
fmt-*.cmake
|
||||||
|
CTestTestfile.cmake
|
||||||
|
CMakeCache.txt
|
||||||
|
CMakeFiles
|
||||||
|
FMT.build
|
||||||
|
Makefile
|
||||||
|
run-msbuild.bat
|
||||||
|
fmt.pc
|
|
@ -0,0 +1,273 @@
|
||||||
|
cmake_minimum_required(VERSION 3.1.0)
|
||||||
|
|
||||||
|
# Use newer policies if available, up to most recent tested version of CMake.
|
||||||
|
if(${CMAKE_VERSION} VERSION_LESS 3.11)
|
||||||
|
cmake_policy(VERSION ${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION})
|
||||||
|
else()
|
||||||
|
cmake_policy(VERSION 3.11)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# Determine if fmt is built as a subproject (using add_subdirectory)
|
||||||
|
# or if it is the master project.
|
||||||
|
set(MASTER_PROJECT OFF)
|
||||||
|
if (CMAKE_CURRENT_SOURCE_DIR STREQUAL CMAKE_SOURCE_DIR)
|
||||||
|
set(MASTER_PROJECT ON)
|
||||||
|
message(STATUS "CMake version: ${CMAKE_VERSION}")
|
||||||
|
endif ()
|
||||||
|
|
||||||
|
# Joins arguments and places the results in ${result_var}.
|
||||||
|
function(join result_var)
|
||||||
|
set(result )
|
||||||
|
foreach (arg ${ARGN})
|
||||||
|
set(result "${result}${arg}")
|
||||||
|
endforeach ()
|
||||||
|
set(${result_var} "${result}" PARENT_SCOPE)
|
||||||
|
endfunction()
|
||||||
|
|
||||||
|
# Set the default CMAKE_BUILD_TYPE to Release.
|
||||||
|
# This should be done before the project command since the latter can set
|
||||||
|
# CMAKE_BUILD_TYPE itself (it does so for nmake).
|
||||||
|
if (NOT CMAKE_BUILD_TYPE)
|
||||||
|
join(doc "Choose the type of build, options are: None(CMAKE_CXX_FLAGS or "
|
||||||
|
"CMAKE_C_FLAGS used) Debug Release RelWithDebInfo MinSizeRel.")
|
||||||
|
set(CMAKE_BUILD_TYPE Release CACHE STRING ${doc})
|
||||||
|
endif ()
|
||||||
|
|
||||||
|
option(FMT_PEDANTIC "Enable extra warnings and expensive tests." OFF)
|
||||||
|
option(FMT_WERROR "Halt the compilation with an error on compiler warnings." OFF)
|
||||||
|
|
||||||
|
# Options that control generation of various targets.
|
||||||
|
option(FMT_DOC "Generate the doc target." ${MASTER_PROJECT})
|
||||||
|
option(FMT_INSTALL "Generate the install target." ${MASTER_PROJECT})
|
||||||
|
option(FMT_TEST "Generate the test target." ${MASTER_PROJECT})
|
||||||
|
|
||||||
|
project(FMT)
|
||||||
|
|
||||||
|
# Get version from core.h
|
||||||
|
file(READ include/fmt/core.h core_h)
|
||||||
|
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 core.h.")
|
||||||
|
endif ()
|
||||||
|
# Use math to skip leading zeros if any.
|
||||||
|
math(EXPR CPACK_PACKAGE_VERSION_MAJOR ${CMAKE_MATCH_1})
|
||||||
|
math(EXPR CPACK_PACKAGE_VERSION_MINOR ${CMAKE_MATCH_2})
|
||||||
|
math(EXPR CPACK_PACKAGE_VERSION_PATCH ${CMAKE_MATCH_3})
|
||||||
|
join(FMT_VERSION ${CPACK_PACKAGE_VERSION_MAJOR}.${CPACK_PACKAGE_VERSION_MINOR}.
|
||||||
|
${CPACK_PACKAGE_VERSION_PATCH})
|
||||||
|
message(STATUS "Version: ${FMT_VERSION}")
|
||||||
|
|
||||||
|
message(STATUS "Build type: ${CMAKE_BUILD_TYPE}")
|
||||||
|
|
||||||
|
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)
|
||||||
|
|
||||||
|
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH}
|
||||||
|
"${CMAKE_CURRENT_SOURCE_DIR}/support/cmake")
|
||||||
|
|
||||||
|
include(cxx14)
|
||||||
|
include(CheckCXXCompilerFlag)
|
||||||
|
|
||||||
|
if (CMAKE_CXX_COMPILER_ID MATCHES "GNU")
|
||||||
|
set(PEDANTIC_COMPILE_FLAGS -pedantic-errors -Wall -Wextra -pedantic
|
||||||
|
-Wold-style-cast -Wundef
|
||||||
|
-Wredundant-decls -Wwrite-strings -Wpointer-arith
|
||||||
|
-Wcast-qual -Wformat=2 -Wmissing-include-dirs
|
||||||
|
-Wcast-align -Wnon-virtual-dtor
|
||||||
|
-Wctor-dtor-privacy -Wdisabled-optimization
|
||||||
|
-Winvalid-pch -Woverloaded-virtual
|
||||||
|
-Wconversion
|
||||||
|
-Wno-ctor-dtor-privacy -Wno-format-nonliteral -Wno-shadow)
|
||||||
|
if (NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 4.6)
|
||||||
|
set(PEDANTIC_COMPILE_FLAGS ${PEDANTIC_COMPILE_FLAGS} -Wnoexcept
|
||||||
|
-Wno-dangling-else -Wno-unused-local-typedefs)
|
||||||
|
endif ()
|
||||||
|
if (NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 5.0)
|
||||||
|
set(PEDANTIC_COMPILE_FLAGS ${PEDANTIC_COMPILE_FLAGS} -Wdouble-promotion
|
||||||
|
-Wtrampolines -Wzero-as-null-pointer-constant -Wuseless-cast
|
||||||
|
-Wvector-operation-performance -Wsized-deallocation)
|
||||||
|
endif ()
|
||||||
|
if (NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 6.0)
|
||||||
|
set(PEDANTIC_COMPILE_FLAGS ${PEDANTIC_COMPILE_FLAGS} -Wshift-overflow=2
|
||||||
|
-Wnull-dereference -Wduplicated-cond)
|
||||||
|
endif ()
|
||||||
|
set(WERROR_FLAG -Werror)
|
||||||
|
endif ()
|
||||||
|
|
||||||
|
if (CMAKE_CXX_COMPILER_ID MATCHES "Clang")
|
||||||
|
set(PEDANTIC_COMPILE_FLAGS -Wall -Wextra -pedantic -Wconversion -Wno-sign-conversion)
|
||||||
|
check_cxx_compiler_flag(-Wzero-as-null-pointer-constant HAS_NULLPTR_WARNING)
|
||||||
|
if (HAS_NULLPTR_WARNING)
|
||||||
|
set(PEDANTIC_COMPILE_FLAGS ${PEDANTIC_COMPILE_FLAGS}
|
||||||
|
-Wzero-as-null-pointer-constant)
|
||||||
|
endif ()
|
||||||
|
set(WERROR_FLAG -Werror)
|
||||||
|
endif ()
|
||||||
|
|
||||||
|
if (MSVC)
|
||||||
|
set(PEDANTIC_COMPILE_FLAGS /W3)
|
||||||
|
set(WERROR_FLAG /WX)
|
||||||
|
endif ()
|
||||||
|
|
||||||
|
if (MASTER_PROJECT AND CMAKE_GENERATOR MATCHES "Visual Studio")
|
||||||
|
# If Microsoft SDK is installed create script run-msbuild.bat that
|
||||||
|
# calls SetEnv.cmd to set up build environment and runs msbuild.
|
||||||
|
# It is useful when building Visual Studio projects with the SDK
|
||||||
|
# toolchain rather than Visual Studio.
|
||||||
|
include(FindSetEnv)
|
||||||
|
if (WINSDK_SETENV)
|
||||||
|
set(MSBUILD_SETUP "call \"${WINSDK_SETENV}\"")
|
||||||
|
endif ()
|
||||||
|
# Set FrameworkPathOverride to get rid of MSB3644 warnings.
|
||||||
|
set(netfxpath "C:\\Program Files\\Reference Assemblies\\Microsoft\\Framework\\.NETFramework\\v4.0")
|
||||||
|
file(WRITE run-msbuild.bat "
|
||||||
|
${MSBUILD_SETUP}
|
||||||
|
${CMAKE_MAKE_PROGRAM} -p:FrameworkPathOverride=\"${netfxpath}\" %*")
|
||||||
|
endif ()
|
||||||
|
|
||||||
|
include(CheckSymbolExists)
|
||||||
|
if (WIN32)
|
||||||
|
check_symbol_exists(open io.h HAVE_OPEN)
|
||||||
|
else ()
|
||||||
|
check_symbol_exists(open fcntl.h HAVE_OPEN)
|
||||||
|
endif ()
|
||||||
|
|
||||||
|
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.
|
||||||
|
add_headers(FMT_HEADERS chrono.h color.h core.h format.h format-inl.h locale.h
|
||||||
|
ostream.h printf.h time.h ranges.h)
|
||||||
|
set(FMT_SOURCES src/format.cc)
|
||||||
|
if (HAVE_OPEN)
|
||||||
|
add_headers(FMT_HEADERS posix.h)
|
||||||
|
set(FMT_SOURCES ${FMT_SOURCES} src/posix.cc)
|
||||||
|
endif ()
|
||||||
|
|
||||||
|
add_library(fmt ${FMT_SOURCES} ${FMT_HEADERS} README.rst ChangeLog.rst)
|
||||||
|
add_library(fmt::fmt ALIAS fmt)
|
||||||
|
|
||||||
|
if (FMT_WERROR)
|
||||||
|
target_compile_options(fmt PRIVATE ${WERROR_FLAG})
|
||||||
|
endif ()
|
||||||
|
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 ()
|
||||||
|
|
||||||
|
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}/include>
|
||||||
|
$<INSTALL_INTERFACE:include>)
|
||||||
|
|
||||||
|
# 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(pkgconfig ${PROJECT_BINARY_DIR}/fmt.pc)
|
||||||
|
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}.")
|
||||||
|
|
||||||
|
set(FMT_PKGCONFIG_DIR "${CMAKE_INSTALL_PREFIX}/share/pkgconfig" CACHE PATH
|
||||||
|
"Installation directory for pkgconfig (.pc) 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_file(
|
||||||
|
"${PROJECT_SOURCE_DIR}/support/cmake/fmt.pc.in"
|
||||||
|
"${pkgconfig}"
|
||||||
|
@ONLY)
|
||||||
|
configure_package_config_file(
|
||||||
|
${PROJECT_SOURCE_DIR}/support/cmake/fmt-config.cmake.in
|
||||||
|
${project_config}
|
||||||
|
INSTALL_DESTINATION ${FMT_CMAKE_DIR})
|
||||||
|
# Use a namespace because CMake provides better diagnostics for namespaced
|
||||||
|
# imported targets.
|
||||||
|
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 $<TARGET_PDB_FILE:${INSTALL_TARGETS}> DESTINATION ${FMT_LIB_DIR} OPTIONAL)
|
||||||
|
install(FILES ${FMT_HEADERS} DESTINATION ${FMT_INC_DIR})
|
||||||
|
install(FILES "${pkgconfig}" DESTINATION "${FMT_PKGCONFIG_DIR}")
|
||||||
|
endif ()
|
||||||
|
|
||||||
|
if (FMT_DOC)
|
||||||
|
add_subdirectory(doc)
|
||||||
|
endif ()
|
||||||
|
|
||||||
|
if (FMT_TEST)
|
||||||
|
enable_testing()
|
||||||
|
add_subdirectory(test)
|
||||||
|
endif ()
|
||||||
|
|
||||||
|
set(gitignore ${PROJECT_SOURCE_DIR}/.gitignore)
|
||||||
|
if (MASTER_PROJECT AND EXISTS ${gitignore})
|
||||||
|
# Get the list of ignored files from .gitignore.
|
||||||
|
file (STRINGS ${gitignore} lines)
|
||||||
|
LIST(REMOVE_ITEM lines /doc/html)
|
||||||
|
foreach (line ${lines})
|
||||||
|
string(REPLACE "." "[.]" line "${line}")
|
||||||
|
string(REPLACE "*" ".*" line "${line}")
|
||||||
|
set(ignored_files ${ignored_files} "${line}$" "${line}/")
|
||||||
|
endforeach ()
|
||||||
|
set(ignored_files ${ignored_files}
|
||||||
|
/.git /breathe /format-benchmark sphinx/ .buildinfo .doctrees)
|
||||||
|
|
||||||
|
set(CPACK_SOURCE_GENERATOR ZIP)
|
||||||
|
set(CPACK_SOURCE_IGNORE_FILES ${ignored_files})
|
||||||
|
set(CPACK_SOURCE_PACKAGE_FILE_NAME fmt-${FMT_VERSION})
|
||||||
|
set(CPACK_PACKAGE_NAME fmt)
|
||||||
|
set(CPACK_RESOURCE_FILE_README ${PROJECT_SOURCE_DIR}/README.rst)
|
||||||
|
include(CPack)
|
||||||
|
endif ()
|
|
@ -0,0 +1,12 @@
|
||||||
|
Contributing to fmt
|
||||||
|
===================
|
||||||
|
|
||||||
|
All C++ code must adhere to `Google C++ Style Guide
|
||||||
|
<https://google.github.io/styleguide/cppguide.html>`_ with the following
|
||||||
|
exceptions:
|
||||||
|
|
||||||
|
* Exceptions are permitted
|
||||||
|
* snake_case should be used instead of UpperCamelCase for function and type
|
||||||
|
names
|
||||||
|
|
||||||
|
Thanks for contributing!
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,23 @@
|
||||||
|
Copyright (c) 2012 - 2016, 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.
|
|
@ -0,0 +1,519 @@
|
||||||
|
{fmt}
|
||||||
|
=====
|
||||||
|
|
||||||
|
.. image:: https://travis-ci.org/fmtlib/fmt.png?branch=master
|
||||||
|
:target: https://travis-ci.org/fmtlib/fmt
|
||||||
|
|
||||||
|
.. image:: https://ci.appveyor.com/api/projects/status/ehjkiefde6gucy1v
|
||||||
|
:target: https://ci.appveyor.com/project/vitaut/fmt
|
||||||
|
|
||||||
|
.. image:: https://badges.gitter.im/Join%20Chat.svg
|
||||||
|
:alt: Join the chat at https://gitter.im/fmtlib/fmt
|
||||||
|
:target: https://gitter.im/fmtlib/fmt
|
||||||
|
|
||||||
|
**{fmt}** is an open-source formatting library for C++.
|
||||||
|
It can be used as a safe and fast alternative to (s)printf and IOStreams.
|
||||||
|
|
||||||
|
`Documentation <http://fmtlib.net/latest/>`__
|
||||||
|
|
||||||
|
This is a development branch that implements the C++ standards proposal `P0645
|
||||||
|
Text Formatting <http://fmtlib.net/Text%20Formatting.html>`__.
|
||||||
|
Released versions are available from the `Releases page
|
||||||
|
<https://github.com/fmtlib/fmt/releases>`__.
|
||||||
|
|
||||||
|
Features
|
||||||
|
--------
|
||||||
|
|
||||||
|
* Replacement-based `format API <http://fmtlib.net/dev/api.html>`_ with
|
||||||
|
positional arguments for localization.
|
||||||
|
* `Format string syntax <http://fmtlib.net/dev/syntax.html>`_ similar to the one
|
||||||
|
of `str.format <https://docs.python.org/2/library/stdtypes.html#str.format>`_
|
||||||
|
in Python.
|
||||||
|
* Safe `printf implementation
|
||||||
|
<http://fmtlib.net/latest/api.html#printf-formatting>`_ including
|
||||||
|
the POSIX extension for positional arguments.
|
||||||
|
* Support for user-defined types.
|
||||||
|
* High speed: performance of the format API is close to that of glibc's `printf
|
||||||
|
<http://en.cppreference.com/w/cpp/io/c/fprintf>`_ and better than the
|
||||||
|
performance of IOStreams. See `Speed tests`_ and
|
||||||
|
`Fast integer to string conversion in C++
|
||||||
|
<http://zverovich.net/2013/09/07/integer-to-string-conversion-in-cplusplus.html>`_.
|
||||||
|
* Small code size both in terms of source code (the minimum configuration
|
||||||
|
consists of just three header files, ``core.h``, ``format.h`` and
|
||||||
|
``format-inl.h``) and compiled code. See `Compile time and code bloat`_.
|
||||||
|
* Reliability: the library has an extensive set of `unit tests
|
||||||
|
<https://github.com/fmtlib/fmt/tree/master/test>`_.
|
||||||
|
* Safety: the library is fully type safe, errors in format strings can be
|
||||||
|
reported at compile time, automatic memory management prevents buffer overflow
|
||||||
|
errors.
|
||||||
|
* Ease of use: small self-contained code base, no external dependencies,
|
||||||
|
permissive BSD `license
|
||||||
|
<https://github.com/fmtlib/fmt/blob/master/LICENSE.rst>`_
|
||||||
|
* `Portability <http://fmtlib.net/latest/index.html#portability>`_ with
|
||||||
|
consistent output across platforms and support for older compilers.
|
||||||
|
* Clean warning-free codebase even on high warning levels
|
||||||
|
(``-Wall -Wextra -pedantic``).
|
||||||
|
* Support for wide strings.
|
||||||
|
* Optional header-only configuration enabled with the ``FMT_HEADER_ONLY`` macro.
|
||||||
|
|
||||||
|
See the `documentation <http://fmtlib.net/latest/>`_ for more details.
|
||||||
|
|
||||||
|
Examples
|
||||||
|
--------
|
||||||
|
|
||||||
|
This prints ``Hello, world!`` to stdout:
|
||||||
|
|
||||||
|
.. code:: c++
|
||||||
|
|
||||||
|
fmt::print("Hello, {}!", "world"); // uses Python-like format string syntax
|
||||||
|
fmt::printf("Hello, %s!", "world"); // uses printf format string syntax
|
||||||
|
|
||||||
|
Arguments can be accessed by position and arguments' indices can be repeated:
|
||||||
|
|
||||||
|
.. code:: c++
|
||||||
|
|
||||||
|
std::string s = fmt::format("{0}{1}{0}", "abra", "cad");
|
||||||
|
// s == "abracadabra"
|
||||||
|
|
||||||
|
Format strings can be checked at compile time:
|
||||||
|
|
||||||
|
.. code:: c++
|
||||||
|
|
||||||
|
// test.cc
|
||||||
|
#define FMT_STRING_ALIAS 1
|
||||||
|
#include <fmt/format.h>
|
||||||
|
std::string s = format(fmt("{2}"), 42);
|
||||||
|
|
||||||
|
.. code::
|
||||||
|
|
||||||
|
$ c++ -Iinclude -std=c++14 test.cc
|
||||||
|
...
|
||||||
|
test.cc:4:17: note: in instantiation of function template specialization 'fmt::v5::format<S, int>' requested here
|
||||||
|
std::string s = format(fmt("{2}"), 42);
|
||||||
|
^
|
||||||
|
include/fmt/core.h:778:19: note: non-constexpr function 'on_error' cannot be used in a constant expression
|
||||||
|
ErrorHandler::on_error(message);
|
||||||
|
^
|
||||||
|
include/fmt/format.h:2226:16: note: in call to '&checker.context_->on_error(&"argument index out of range"[0])'
|
||||||
|
context_.on_error("argument index out of range");
|
||||||
|
^
|
||||||
|
|
||||||
|
{fmt} can be used as a safe portable replacement for ``itoa``
|
||||||
|
(`godbolt <https://godbolt.org/g/NXmpU4>`_):
|
||||||
|
|
||||||
|
.. code:: c++
|
||||||
|
|
||||||
|
fmt::memory_buffer buf;
|
||||||
|
format_to(buf, "{}", 42); // replaces itoa(42, buffer, 10)
|
||||||
|
format_to(buf, "{:x}", 42); // replaces itoa(42, buffer, 16)
|
||||||
|
// access the string using to_string(buf) or buf.data()
|
||||||
|
|
||||||
|
Formatting of user-defined types is supported via a simple
|
||||||
|
`extension API <http://fmtlib.net/latest/api.html#formatting-user-defined-types>`_:
|
||||||
|
|
||||||
|
.. code:: c++
|
||||||
|
|
||||||
|
#include "fmt/format.h"
|
||||||
|
|
||||||
|
struct date {
|
||||||
|
int year, month, day;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <>
|
||||||
|
struct fmt::formatter<date> {
|
||||||
|
template <typename ParseContext>
|
||||||
|
constexpr auto parse(ParseContext &ctx) { return ctx.begin(); }
|
||||||
|
|
||||||
|
template <typename FormatContext>
|
||||||
|
auto format(const date &d, FormatContext &ctx) {
|
||||||
|
return format_to(ctx.out(), "{}-{}-{}", d.year, d.month, d.day);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
std::string s = fmt::format("The date is {}", date{2012, 12, 9});
|
||||||
|
// s == "The date is 2012-12-9"
|
||||||
|
|
||||||
|
You can create your own functions similar to `format
|
||||||
|
<http://fmtlib.net/latest/api.html#format>`_ and
|
||||||
|
`print <http://fmtlib.net/latest/api.html#print>`_
|
||||||
|
which take arbitrary arguments (`godbolt <https://godbolt.org/g/MHjHVf>`_):
|
||||||
|
|
||||||
|
.. code:: c++
|
||||||
|
|
||||||
|
// Prints formatted error message.
|
||||||
|
void vreport_error(const char *format, fmt::format_args args) {
|
||||||
|
fmt::print("Error: ");
|
||||||
|
fmt::vprint(format, args);
|
||||||
|
}
|
||||||
|
template <typename... Args>
|
||||||
|
void report_error(const char *format, const Args & ... args) {
|
||||||
|
vreport_error(format, fmt::make_format_args(args...));
|
||||||
|
}
|
||||||
|
|
||||||
|
report_error("file not found: {}", path);
|
||||||
|
|
||||||
|
Note that ``vreport_error`` is not parameterized on argument types which can
|
||||||
|
improve compile times and reduce code size compared to fully parameterized
|
||||||
|
version.
|
||||||
|
|
||||||
|
Projects using this library
|
||||||
|
---------------------------
|
||||||
|
|
||||||
|
* `0 A.D. <http://play0ad.com/>`_: A free, open-source, cross-platform real-time
|
||||||
|
strategy game
|
||||||
|
|
||||||
|
* `AMPL/MP <https://github.com/ampl/mp>`_:
|
||||||
|
An open-source library for mathematical programming
|
||||||
|
|
||||||
|
* `AvioBook <https://www.aviobook.aero/en>`_: A comprehensive aircraft
|
||||||
|
operations suite
|
||||||
|
|
||||||
|
* `Celestia <https://celestia.space/>`_: Real-time 3D visualization of space
|
||||||
|
|
||||||
|
* `Ceph <https://ceph.com/>`_: A scalable distributed storage system
|
||||||
|
|
||||||
|
* `CUAUV <http://cuauv.org/>`_: Cornell University's autonomous underwater
|
||||||
|
vehicle
|
||||||
|
|
||||||
|
* `HarpyWar/pvpgn <https://github.com/pvpgn/pvpgn-server>`_:
|
||||||
|
Player vs Player Gaming Network with tweaks
|
||||||
|
|
||||||
|
* `KBEngine <http://kbengine.org/>`_: An open-source MMOG server engine
|
||||||
|
|
||||||
|
* `Keypirinha <http://keypirinha.com/>`_: A semantic launcher for Windows
|
||||||
|
|
||||||
|
* `Kodi <https://kodi.tv/>`_ (formerly xbmc): Home theater software
|
||||||
|
|
||||||
|
* `Lifeline <https://github.com/peter-clark/lifeline>`_: A 2D game
|
||||||
|
|
||||||
|
* `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
|
||||||
|
|
||||||
|
* `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/>`_:
|
||||||
|
An MMO server, compatible with most Ultima Online clients
|
||||||
|
|
||||||
|
* `quasardb <https://www.quasardb.net/>`_: A distributed, high-performance,
|
||||||
|
associative database
|
||||||
|
|
||||||
|
* `readpe <https://bitbucket.org/sys_dev/readpe>`_: Read Portable Executable
|
||||||
|
|
||||||
|
* `redis-cerberus <https://github.com/HunanTV/redis-cerberus>`_: A Redis cluster
|
||||||
|
proxy
|
||||||
|
|
||||||
|
* `rpclib <http://rpclib.net/>`_: A modern C++ msgpack-RPC server and client
|
||||||
|
library
|
||||||
|
|
||||||
|
* `Saddy <https://github.com/mamontov-cpp/saddy-graphics-engine-2d>`_:
|
||||||
|
Small crossplatform 2D graphic engine
|
||||||
|
|
||||||
|
* `Salesforce Analytics Cloud <http://www.salesforce.com/analytics-cloud/overview/>`_:
|
||||||
|
Business intelligence software
|
||||||
|
|
||||||
|
* `Scylla <http://www.scylladb.com/>`_: A Cassandra-compatible NoSQL data store
|
||||||
|
that can handle 1 million transactions per second on a single server
|
||||||
|
|
||||||
|
* `Seastar <http://www.seastar-project.org/>`_: An advanced, open-source C++
|
||||||
|
framework for high-performance server applications on modern hardware
|
||||||
|
|
||||||
|
* `spdlog <https://github.com/gabime/spdlog>`_: Super fast C++ logging library
|
||||||
|
|
||||||
|
* `Stellar <https://www.stellar.org/>`_: Financial platform
|
||||||
|
|
||||||
|
* `Touch Surgery <https://www.touchsurgery.com/>`_: Surgery simulator
|
||||||
|
|
||||||
|
* `TrinityCore <https://github.com/TrinityCore/TrinityCore>`_: Open-source
|
||||||
|
MMORPG framework
|
||||||
|
|
||||||
|
`More... <https://github.com/search?q=cppformat&type=Code>`_
|
||||||
|
|
||||||
|
If you are aware of other projects using this library, please let me know
|
||||||
|
by `email <mailto:victor.zverovich@gmail.com>`_ or by submitting an
|
||||||
|
`issue <https://github.com/fmtlib/fmt/issues>`_.
|
||||||
|
|
||||||
|
Motivation
|
||||||
|
----------
|
||||||
|
|
||||||
|
So why yet another formatting library?
|
||||||
|
|
||||||
|
There are plenty of methods for doing this task, from standard ones like
|
||||||
|
the printf family of function and IOStreams to Boost Format library and
|
||||||
|
FastFormat. The reason for creating a new library is that every existing
|
||||||
|
solution that I found either had serious issues or didn't provide
|
||||||
|
all the features I needed.
|
||||||
|
|
||||||
|
Printf
|
||||||
|
~~~~~~
|
||||||
|
|
||||||
|
The good thing about printf is that it is pretty fast and readily available
|
||||||
|
being a part of the C standard library. The main drawback is that it
|
||||||
|
doesn't support user-defined types. Printf also has safety issues although
|
||||||
|
they are mostly solved with `__attribute__ ((format (printf, ...))
|
||||||
|
<http://gcc.gnu.org/onlinedocs/gcc/Function-Attributes.html>`_ in GCC.
|
||||||
|
There is a POSIX extension that adds positional arguments required for
|
||||||
|
`i18n <https://en.wikipedia.org/wiki/Internationalization_and_localization>`_
|
||||||
|
to printf but it is not a part of C99 and may not be available on some
|
||||||
|
platforms.
|
||||||
|
|
||||||
|
IOStreams
|
||||||
|
~~~~~~~~~
|
||||||
|
|
||||||
|
The main issue with IOStreams is best illustrated with an example:
|
||||||
|
|
||||||
|
.. code:: c++
|
||||||
|
|
||||||
|
std::cout << std::setprecision(2) << std::fixed << 1.23456 << "\n";
|
||||||
|
|
||||||
|
which is a lot of typing compared to printf:
|
||||||
|
|
||||||
|
.. code:: c++
|
||||||
|
|
||||||
|
printf("%.2f\n", 1.23456);
|
||||||
|
|
||||||
|
Matthew Wilson, the author of FastFormat, referred to this situation with
|
||||||
|
IOStreams as "chevron hell". IOStreams doesn't support positional arguments
|
||||||
|
by design.
|
||||||
|
|
||||||
|
The good part is that IOStreams supports user-defined types and is safe
|
||||||
|
although error reporting is awkward.
|
||||||
|
|
||||||
|
Boost Format library
|
||||||
|
~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
This is a very powerful library which supports both printf-like format
|
||||||
|
strings and positional arguments. Its main drawback is performance.
|
||||||
|
According to various benchmarks it is much slower than other methods
|
||||||
|
considered here. Boost Format also has excessive build times and severe
|
||||||
|
code bloat issues (see `Benchmarks`_).
|
||||||
|
|
||||||
|
FastFormat
|
||||||
|
~~~~~~~~~~
|
||||||
|
|
||||||
|
This is an interesting library which is fast, safe and has positional
|
||||||
|
arguments. However it has significant limitations, citing its author:
|
||||||
|
|
||||||
|
Three features that have no hope of being accommodated within the
|
||||||
|
current design are:
|
||||||
|
|
||||||
|
* Leading zeros (or any other non-space padding)
|
||||||
|
* Octal/hexadecimal encoding
|
||||||
|
* Runtime width/alignment specification
|
||||||
|
|
||||||
|
It is also quite big and has a heavy dependency, STLSoft, which might be
|
||||||
|
too restrictive for using it in some projects.
|
||||||
|
|
||||||
|
Loki SafeFormat
|
||||||
|
~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
SafeFormat is a formatting library which uses printf-like format strings
|
||||||
|
and is type safe. It doesn't support user-defined types or positional
|
||||||
|
arguments. It makes unconventional use of ``operator()`` for passing
|
||||||
|
format arguments.
|
||||||
|
|
||||||
|
Tinyformat
|
||||||
|
~~~~~~~~~~
|
||||||
|
|
||||||
|
This library supports printf-like format strings and is very small and
|
||||||
|
fast. Unfortunately it doesn't support positional arguments and wrapping
|
||||||
|
it in C++98 is somewhat difficult. Also its performance and code compactness
|
||||||
|
are limited by IOStreams.
|
||||||
|
|
||||||
|
Boost Spirit.Karma
|
||||||
|
~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
This is not really a formatting library but I decided to include it here
|
||||||
|
for completeness. As IOStreams it suffers from the problem of mixing
|
||||||
|
verbatim text with arguments. The library is pretty fast, but slower
|
||||||
|
on integer formatting than ``fmt::Writer`` on Karma's own benchmark,
|
||||||
|
see `Fast integer to string conversion in C++
|
||||||
|
<http://zverovich.net/2013/09/07/integer-to-string-conversion-in-cplusplus.html>`_.
|
||||||
|
|
||||||
|
Benchmarks
|
||||||
|
----------
|
||||||
|
|
||||||
|
Speed tests
|
||||||
|
~~~~~~~~~~~
|
||||||
|
|
||||||
|
The following speed tests results were generated by building
|
||||||
|
``tinyformat_test.cpp`` on Ubuntu GNU/Linux 14.04.1 with
|
||||||
|
``g++-4.8.2 -O3 -DSPEED_TEST -DHAVE_FORMAT``, and taking the best of three
|
||||||
|
runs. In the test, the format string ``"%0.10f:%04d:%+g:%s:%p:%c:%%\n"`` or
|
||||||
|
equivalent is filled 2000000 times with output sent to ``/dev/null``; for
|
||||||
|
further details see the `source
|
||||||
|
<https://github.com/fmtlib/format-benchmark/blob/master/tinyformat_test.cpp>`_.
|
||||||
|
|
||||||
|
================= ============= ===========
|
||||||
|
Library Method Run Time, s
|
||||||
|
================= ============= ===========
|
||||||
|
libc printf 1.35
|
||||||
|
libc++ std::ostream 3.42
|
||||||
|
fmt 534bff7 fmt::print 1.56
|
||||||
|
tinyformat 2.0.1 tfm::printf 3.73
|
||||||
|
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
|
||||||
|
is confirmed by `other tests <http://accu.org/index.php/journals/1539>`_.
|
||||||
|
Tinyformat is quite good coming close to IOStreams. Unfortunately tinyformat
|
||||||
|
cannot be faster than the IOStreams because it uses them internally.
|
||||||
|
Performance of fmt is close to that of printf, being `faster than printf on integer
|
||||||
|
formatting <http://zverovich.net/2013/09/07/integer-to-string-conversion-in-cplusplus.html>`_,
|
||||||
|
but slower on floating-point formatting which dominates this benchmark.
|
||||||
|
|
||||||
|
Compile time and code bloat
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
The script `bloat-test.py
|
||||||
|
<https://github.com/fmtlib/format-benchmark/blob/master/bloat-test.py>`_
|
||||||
|
from `format-benchmark <https://github.com/fmtlib/format-benchmark>`_
|
||||||
|
tests compile time and code bloat for nontrivial projects.
|
||||||
|
It generates 100 translation units and uses ``printf()`` or its alternative
|
||||||
|
five times in each to simulate a medium sized project. The resulting
|
||||||
|
executable size and compile time (Apple LLVM version 8.1.0 (clang-802.0.42),
|
||||||
|
macOS Sierra, best of three) is shown in the following tables.
|
||||||
|
|
||||||
|
**Optimized build (-O3)**
|
||||||
|
|
||||||
|
============= =============== ==================== ==================
|
||||||
|
Method Compile Time, s Executable size, KiB Stripped size, KiB
|
||||||
|
============= =============== ==================== ==================
|
||||||
|
printf 2.6 29 26
|
||||||
|
printf+string 16.4 29 26
|
||||||
|
IOStreams 31.1 59 55
|
||||||
|
fmt 19.0 37 34
|
||||||
|
tinyformat 44.0 103 97
|
||||||
|
Boost Format 91.9 226 203
|
||||||
|
Folly Format 115.7 101 88
|
||||||
|
============= =============== ==================== ==================
|
||||||
|
|
||||||
|
As you can see, fmt has 60% less overhead in terms of resulting binary code
|
||||||
|
size compared to IOStreams and comes pretty close to ``printf``. Boost Format
|
||||||
|
and Folly Format have 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**
|
||||||
|
|
||||||
|
============= =============== ==================== ==================
|
||||||
|
Method Compile Time, s Executable size, KiB Stripped size, KiB
|
||||||
|
============= =============== ==================== ==================
|
||||||
|
printf 2.2 33 30
|
||||||
|
printf+string 16.0 33 30
|
||||||
|
IOStreams 28.3 56 52
|
||||||
|
fmt 18.2 59 50
|
||||||
|
tinyformat 32.6 88 82
|
||||||
|
Boost Format 54.1 365 303
|
||||||
|
Folly Format 79.9 445 430
|
||||||
|
============= =============== ==================== ==================
|
||||||
|
|
||||||
|
``libc``, ``lib(std)c++`` and ``libfmt`` are all linked as shared
|
||||||
|
libraries to compare formatting function overhead only. Boost Format
|
||||||
|
and tinyformat are header-only libraries so they don't provide any
|
||||||
|
linkage options.
|
||||||
|
|
||||||
|
Running the tests
|
||||||
|
~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
Please refer to `Building the library`__ for the instructions on how to build
|
||||||
|
the library and run the unit tests.
|
||||||
|
|
||||||
|
__ http://fmtlib.net/latest/usage.html#building-the-library
|
||||||
|
|
||||||
|
Benchmarks reside in a separate repository,
|
||||||
|
`format-benchmarks <https://github.com/fmtlib/format-benchmark>`_,
|
||||||
|
so to run the benchmarks you first need to clone this repository and
|
||||||
|
generate Makefiles with CMake::
|
||||||
|
|
||||||
|
$ git clone --recursive https://github.com/fmtlib/format-benchmark.git
|
||||||
|
$ cd format-benchmark
|
||||||
|
$ cmake .
|
||||||
|
|
||||||
|
Then you can run the speed test::
|
||||||
|
|
||||||
|
$ make speed-test
|
||||||
|
|
||||||
|
or the bloat test::
|
||||||
|
|
||||||
|
$ make bloat-test
|
||||||
|
|
||||||
|
FAQ
|
||||||
|
---
|
||||||
|
|
||||||
|
Q: how can I capture formatting arguments and format them later?
|
||||||
|
|
||||||
|
A: use ``std::tuple``:
|
||||||
|
|
||||||
|
.. code:: c++
|
||||||
|
|
||||||
|
template <typename... Args>
|
||||||
|
auto capture(const Args&... args) {
|
||||||
|
return std::make_tuple(args...);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto print_message = [](const auto&... args) {
|
||||||
|
fmt::print(args...);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Capture and store arguments:
|
||||||
|
auto args = capture("{} {}", 42, "foo");
|
||||||
|
// Do formatting:
|
||||||
|
std::apply(print_message, args);
|
||||||
|
|
||||||
|
License
|
||||||
|
-------
|
||||||
|
|
||||||
|
fmt is distributed under the BSD `license
|
||||||
|
<https://github.com/fmtlib/fmt/blob/master/LICENSE.rst>`_.
|
||||||
|
|
||||||
|
The `Format String Syntax
|
||||||
|
<http://fmtlib.net/latest/syntax.html>`_
|
||||||
|
section in the documentation is based on the one from Python `string module
|
||||||
|
documentation <https://docs.python.org/3/library/string.html#module-string>`_
|
||||||
|
adapted for the current library. For this reason the documentation is
|
||||||
|
distributed under the Python Software Foundation license available in
|
||||||
|
`doc/python-license.txt
|
||||||
|
<https://raw.github.com/fmtlib/fmt/master/doc/python-license.txt>`_.
|
||||||
|
It only applies if you distribute the documentation of fmt.
|
||||||
|
|
||||||
|
Acknowledgments
|
||||||
|
---------------
|
||||||
|
|
||||||
|
The fmt library is maintained by Victor Zverovich (`vitaut
|
||||||
|
<https://github.com/vitaut>`_) and Jonathan Müller (`foonathan
|
||||||
|
<https://github.com/foonathan>`_) with contributions from many 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 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
|
||||||
|
from the excellent `tinyformat <https://github.com/c42f/tinyformat>`_ library
|
||||||
|
written by Chris Foster. Boost Format library is acknowledged transitively
|
||||||
|
since it had some influence on tinyformat.
|
||||||
|
Some ideas used in the implementation are borrowed from `Loki
|
||||||
|
<http://loki-lib.sourceforge.net/>`_ SafeFormat and `Diagnostic API
|
||||||
|
<http://clang.llvm.org/doxygen/classclang_1_1Diagnostic.html>`_ in
|
||||||
|
`Clang <http://clang.llvm.org/>`_.
|
||||||
|
Format string syntax and the documentation are based on Python's `str.format
|
||||||
|
<http://docs.python.org/2/library/stdtypes.html#str.format>`_.
|
||||||
|
Thanks `Doug Turnbull <https://github.com/softwaredoug>`_ for his valuable
|
||||||
|
comments and contribution to the design of the type-safe API and
|
||||||
|
`Gregory Czajkowski <https://github.com/gcflymoto>`_ for implementing binary
|
||||||
|
formatting. Thanks `Ruslan Baratov <https://github.com/ruslo>`_ for comprehensive
|
||||||
|
`comparison of integer formatting algorithms <https://github.com/ruslo/int-dec-format-tests>`_
|
||||||
|
and useful comments regarding performance, `Boris Kaul <https://github.com/localvoid>`_ for
|
||||||
|
`C++ counting digits benchmark <https://github.com/localvoid/cxx-benchmark-count-digits>`_.
|
||||||
|
Thanks to `CarterLi <https://github.com/CarterLi>`_ for contributing various
|
||||||
|
improvements to the code.
|
|
@ -0,0 +1,452 @@
|
||||||
|
// Formatting library for C++ - chrono support
|
||||||
|
//
|
||||||
|
// Copyright (c) 2012 - present, Victor Zverovich
|
||||||
|
// All rights reserved.
|
||||||
|
//
|
||||||
|
// For the license information refer to format.h.
|
||||||
|
|
||||||
|
#ifndef FMT_CHRONO_H_
|
||||||
|
#define FMT_CHRONO_H_
|
||||||
|
|
||||||
|
#include "format.h"
|
||||||
|
#include "locale.h"
|
||||||
|
|
||||||
|
#include <chrono>
|
||||||
|
#include <ctime>
|
||||||
|
#include <locale>
|
||||||
|
#include <sstream>
|
||||||
|
|
||||||
|
FMT_BEGIN_NAMESPACE
|
||||||
|
|
||||||
|
namespace internal{
|
||||||
|
|
||||||
|
enum class numeric_system {
|
||||||
|
standard,
|
||||||
|
// Alternative numeric system, e.g. 十二 instead of 12 in ja_JP locale.
|
||||||
|
alternative
|
||||||
|
};
|
||||||
|
|
||||||
|
// Parses a put_time-like format string and invokes handler actions.
|
||||||
|
template <typename Char, typename Handler>
|
||||||
|
FMT_CONSTEXPR const Char *parse_chrono_format(
|
||||||
|
const Char *begin, const Char *end, Handler &&handler) {
|
||||||
|
auto ptr = begin;
|
||||||
|
while (ptr != end) {
|
||||||
|
auto c = *ptr;
|
||||||
|
if (c == '}') break;
|
||||||
|
if (c != '%') {
|
||||||
|
++ptr;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (begin != ptr)
|
||||||
|
handler.on_text(begin, ptr);
|
||||||
|
++ptr; // consume '%'
|
||||||
|
if (ptr == end)
|
||||||
|
throw format_error("invalid format");
|
||||||
|
c = *ptr++;
|
||||||
|
switch (c) {
|
||||||
|
case '%':
|
||||||
|
handler.on_text(ptr - 1, ptr);
|
||||||
|
break;
|
||||||
|
case 'n': {
|
||||||
|
const char newline[] = "\n";
|
||||||
|
handler.on_text(newline, newline + 1);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 't': {
|
||||||
|
const char tab[] = "\t";
|
||||||
|
handler.on_text(tab, tab + 1);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
// Day of the week:
|
||||||
|
case 'a':
|
||||||
|
handler.on_abbr_weekday();
|
||||||
|
break;
|
||||||
|
case 'A':
|
||||||
|
handler.on_full_weekday();
|
||||||
|
break;
|
||||||
|
case 'w':
|
||||||
|
handler.on_dec0_weekday(numeric_system::standard);
|
||||||
|
break;
|
||||||
|
case 'u':
|
||||||
|
handler.on_dec1_weekday(numeric_system::standard);
|
||||||
|
break;
|
||||||
|
// Month:
|
||||||
|
case 'b':
|
||||||
|
handler.on_abbr_month();
|
||||||
|
break;
|
||||||
|
case 'B':
|
||||||
|
handler.on_full_month();
|
||||||
|
break;
|
||||||
|
// Hour, minute, second:
|
||||||
|
case 'H':
|
||||||
|
handler.on_24_hour(numeric_system::standard);
|
||||||
|
break;
|
||||||
|
case 'I':
|
||||||
|
handler.on_12_hour(numeric_system::standard);
|
||||||
|
break;
|
||||||
|
case 'M':
|
||||||
|
handler.on_minute(numeric_system::standard);
|
||||||
|
break;
|
||||||
|
case 'S':
|
||||||
|
handler.on_second(numeric_system::standard);
|
||||||
|
break;
|
||||||
|
// Other:
|
||||||
|
case 'c':
|
||||||
|
handler.on_datetime(numeric_system::standard);
|
||||||
|
break;
|
||||||
|
case 'x':
|
||||||
|
handler.on_loc_date(numeric_system::standard);
|
||||||
|
break;
|
||||||
|
case 'X':
|
||||||
|
handler.on_loc_time(numeric_system::standard);
|
||||||
|
break;
|
||||||
|
case 'D':
|
||||||
|
handler.on_us_date();
|
||||||
|
break;
|
||||||
|
case 'F':
|
||||||
|
handler.on_iso_date();
|
||||||
|
break;
|
||||||
|
case 'r':
|
||||||
|
handler.on_12_hour_time();
|
||||||
|
break;
|
||||||
|
case 'R':
|
||||||
|
handler.on_24_hour_time();
|
||||||
|
break;
|
||||||
|
case 'T':
|
||||||
|
handler.on_iso_time();
|
||||||
|
break;
|
||||||
|
case 'p':
|
||||||
|
handler.on_am_pm();
|
||||||
|
break;
|
||||||
|
case 'z':
|
||||||
|
handler.on_utc_offset();
|
||||||
|
break;
|
||||||
|
case 'Z':
|
||||||
|
handler.on_tz_name();
|
||||||
|
break;
|
||||||
|
// Alternative representation:
|
||||||
|
case 'E': {
|
||||||
|
if (ptr == end)
|
||||||
|
throw format_error("invalid format");
|
||||||
|
c = *ptr++;
|
||||||
|
switch (c) {
|
||||||
|
case 'c':
|
||||||
|
handler.on_datetime(numeric_system::alternative);
|
||||||
|
break;
|
||||||
|
case 'x':
|
||||||
|
handler.on_loc_date(numeric_system::alternative);
|
||||||
|
break;
|
||||||
|
case 'X':
|
||||||
|
handler.on_loc_time(numeric_system::alternative);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw format_error("invalid format");
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 'O':
|
||||||
|
if (ptr == end)
|
||||||
|
throw format_error("invalid format");
|
||||||
|
c = *ptr++;
|
||||||
|
switch (c) {
|
||||||
|
case 'w':
|
||||||
|
handler.on_dec0_weekday(numeric_system::alternative);
|
||||||
|
break;
|
||||||
|
case 'u':
|
||||||
|
handler.on_dec1_weekday(numeric_system::alternative);
|
||||||
|
break;
|
||||||
|
case 'H':
|
||||||
|
handler.on_24_hour(numeric_system::alternative);
|
||||||
|
break;
|
||||||
|
case 'I':
|
||||||
|
handler.on_12_hour(numeric_system::alternative);
|
||||||
|
break;
|
||||||
|
case 'M':
|
||||||
|
handler.on_minute(numeric_system::alternative);
|
||||||
|
break;
|
||||||
|
case 'S':
|
||||||
|
handler.on_second(numeric_system::alternative);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw format_error("invalid format");
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw format_error("invalid format");
|
||||||
|
}
|
||||||
|
begin = ptr;
|
||||||
|
}
|
||||||
|
if (begin != ptr)
|
||||||
|
handler.on_text(begin, ptr);
|
||||||
|
return ptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct chrono_format_checker {
|
||||||
|
void report_no_date() { throw format_error("no date"); }
|
||||||
|
|
||||||
|
template <typename Char>
|
||||||
|
void on_text(const Char *, const Char *) {}
|
||||||
|
void on_abbr_weekday() { report_no_date(); }
|
||||||
|
void on_full_weekday() { report_no_date(); }
|
||||||
|
void on_dec0_weekday(numeric_system) { report_no_date(); }
|
||||||
|
void on_dec1_weekday(numeric_system) { report_no_date(); }
|
||||||
|
void on_abbr_month() { report_no_date(); }
|
||||||
|
void on_full_month() { report_no_date(); }
|
||||||
|
void on_24_hour(numeric_system) {}
|
||||||
|
void on_12_hour(numeric_system) {}
|
||||||
|
void on_minute(numeric_system) {}
|
||||||
|
void on_second(numeric_system) {}
|
||||||
|
void on_datetime(numeric_system) { report_no_date(); }
|
||||||
|
void on_loc_date(numeric_system) { report_no_date(); }
|
||||||
|
void on_loc_time(numeric_system) { report_no_date(); }
|
||||||
|
void on_us_date() { report_no_date(); }
|
||||||
|
void on_iso_date() { report_no_date(); }
|
||||||
|
void on_12_hour_time() {}
|
||||||
|
void on_24_hour_time() {}
|
||||||
|
void on_iso_time() {}
|
||||||
|
void on_am_pm() {}
|
||||||
|
void on_utc_offset() { report_no_date(); }
|
||||||
|
void on_tz_name() { report_no_date(); }
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename Int>
|
||||||
|
inline int to_int(Int value) {
|
||||||
|
FMT_ASSERT(value >= (std::numeric_limits<int>::min)() &&
|
||||||
|
value <= (std::numeric_limits<int>::max)(), "invalid value");
|
||||||
|
return static_cast<int>(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename FormatContext, typename OutputIt>
|
||||||
|
struct chrono_formatter {
|
||||||
|
FormatContext &context;
|
||||||
|
OutputIt out;
|
||||||
|
std::chrono::seconds s;
|
||||||
|
std::chrono::milliseconds ms;
|
||||||
|
|
||||||
|
typedef typename FormatContext::char_type char_type;
|
||||||
|
|
||||||
|
explicit chrono_formatter(FormatContext &ctx, OutputIt o)
|
||||||
|
: context(ctx), out(o) {}
|
||||||
|
|
||||||
|
int hour() const { return to_int((s.count() / 3600) % 24); }
|
||||||
|
|
||||||
|
int hour12() const {
|
||||||
|
auto hour = to_int((s.count() / 3600) % 12);
|
||||||
|
return hour > 0 ? hour : 12;
|
||||||
|
}
|
||||||
|
|
||||||
|
int minute() const { return to_int((s.count() / 60) % 60); }
|
||||||
|
int second() const { return to_int(s.count() % 60); }
|
||||||
|
|
||||||
|
std::tm time() const {
|
||||||
|
auto time = std::tm();
|
||||||
|
time.tm_hour = hour();
|
||||||
|
time.tm_min = minute();
|
||||||
|
time.tm_sec = second();
|
||||||
|
return time;
|
||||||
|
}
|
||||||
|
|
||||||
|
void write(int value, int width) {
|
||||||
|
typedef typename int_traits<int>::main_type main_type;
|
||||||
|
main_type n = to_unsigned(value);
|
||||||
|
int num_digits = internal::count_digits(n);
|
||||||
|
if (width > num_digits)
|
||||||
|
out = std::fill_n(out, width - num_digits, '0');
|
||||||
|
out = format_decimal<char_type>(out, n, num_digits);
|
||||||
|
}
|
||||||
|
|
||||||
|
void format_localized(const tm &time, const char *format) {
|
||||||
|
auto locale = context.locale().template get<std::locale>();
|
||||||
|
auto &facet = std::use_facet<std::time_put<char_type>>(locale);
|
||||||
|
std::basic_ostringstream<char_type> os;
|
||||||
|
os.imbue(locale);
|
||||||
|
facet.put(os, os, ' ', &time, format, format + std::strlen(format));
|
||||||
|
auto str = os.str();
|
||||||
|
std::copy(str.begin(), str.end(), out);
|
||||||
|
}
|
||||||
|
|
||||||
|
void on_text(const char_type *begin, const char_type *end) {
|
||||||
|
std::copy(begin, end, out);
|
||||||
|
}
|
||||||
|
|
||||||
|
// These are not implemented because durations don't have date information.
|
||||||
|
void on_abbr_weekday() {}
|
||||||
|
void on_full_weekday() {}
|
||||||
|
void on_dec0_weekday(numeric_system) {}
|
||||||
|
void on_dec1_weekday(numeric_system) {}
|
||||||
|
void on_abbr_month() {}
|
||||||
|
void on_full_month() {}
|
||||||
|
void on_datetime(numeric_system) {}
|
||||||
|
void on_loc_date(numeric_system) {}
|
||||||
|
void on_loc_time(numeric_system) {}
|
||||||
|
void on_us_date() {}
|
||||||
|
void on_iso_date() {}
|
||||||
|
void on_utc_offset() {}
|
||||||
|
void on_tz_name() {}
|
||||||
|
|
||||||
|
void on_24_hour(numeric_system ns) {
|
||||||
|
if (ns == numeric_system::standard)
|
||||||
|
return write(hour(), 2);
|
||||||
|
auto time = tm();
|
||||||
|
time.tm_hour = hour();
|
||||||
|
format_localized(time, "%OH");
|
||||||
|
}
|
||||||
|
|
||||||
|
void on_12_hour(numeric_system ns) {
|
||||||
|
if (ns == numeric_system::standard)
|
||||||
|
return write(hour12(), 2);
|
||||||
|
auto time = tm();
|
||||||
|
time.tm_hour = hour();
|
||||||
|
format_localized(time, "%OI");
|
||||||
|
}
|
||||||
|
|
||||||
|
void on_minute(numeric_system ns) {
|
||||||
|
if (ns == numeric_system::standard)
|
||||||
|
return write(minute(), 2);
|
||||||
|
auto time = tm();
|
||||||
|
time.tm_min = minute();
|
||||||
|
format_localized(time, "%OM");
|
||||||
|
}
|
||||||
|
|
||||||
|
void on_second(numeric_system ns) {
|
||||||
|
if (ns == numeric_system::standard) {
|
||||||
|
write(second(), 2);
|
||||||
|
if (ms != std::chrono::milliseconds(0)) {
|
||||||
|
*out++ = '.';
|
||||||
|
write(to_int(ms.count()), 3);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
auto time = tm();
|
||||||
|
time.tm_sec = second();
|
||||||
|
format_localized(time, "%OS");
|
||||||
|
}
|
||||||
|
|
||||||
|
void on_12_hour_time() { format_localized(time(), "%r"); }
|
||||||
|
|
||||||
|
void on_24_hour_time() {
|
||||||
|
write(hour(), 2);
|
||||||
|
*out++ = ':';
|
||||||
|
write(minute(), 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
void on_iso_time() {
|
||||||
|
on_24_hour_time();
|
||||||
|
*out++ = ':';
|
||||||
|
write(second(), 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
void on_am_pm() { format_localized(time(), "%p"); }
|
||||||
|
};
|
||||||
|
} // namespace internal
|
||||||
|
|
||||||
|
template <typename Period> FMT_CONSTEXPR const char *get_units() {
|
||||||
|
return FMT_NULL;
|
||||||
|
}
|
||||||
|
template <> FMT_CONSTEXPR const char *get_units<std::atto>() { return "as"; }
|
||||||
|
template <> FMT_CONSTEXPR const char *get_units<std::femto>() { return "fs"; }
|
||||||
|
template <> FMT_CONSTEXPR const char *get_units<std::pico>() { return "ps"; }
|
||||||
|
template <> FMT_CONSTEXPR const char *get_units<std::nano>() { return "ns"; }
|
||||||
|
template <> FMT_CONSTEXPR const char *get_units<std::micro>() { return "µs"; }
|
||||||
|
template <> FMT_CONSTEXPR const char *get_units<std::milli>() { return "ms"; }
|
||||||
|
template <> FMT_CONSTEXPR const char *get_units<std::centi>() { return "cs"; }
|
||||||
|
template <> FMT_CONSTEXPR const char *get_units<std::deci>() { return "ds"; }
|
||||||
|
template <> FMT_CONSTEXPR const char *get_units<std::ratio<1>>() { return "s"; }
|
||||||
|
template <> FMT_CONSTEXPR const char *get_units<std::deca>() { return "das"; }
|
||||||
|
template <> FMT_CONSTEXPR const char *get_units<std::hecto>() { return "hs"; }
|
||||||
|
template <> FMT_CONSTEXPR const char *get_units<std::kilo>() { return "ks"; }
|
||||||
|
template <> FMT_CONSTEXPR const char *get_units<std::mega>() { return "Ms"; }
|
||||||
|
template <> FMT_CONSTEXPR const char *get_units<std::giga>() { return "Gs"; }
|
||||||
|
template <> FMT_CONSTEXPR const char *get_units<std::tera>() { return "Ts"; }
|
||||||
|
template <> FMT_CONSTEXPR const char *get_units<std::peta>() { return "Ps"; }
|
||||||
|
template <> FMT_CONSTEXPR const char *get_units<std::exa>() { return "Es"; }
|
||||||
|
template <> FMT_CONSTEXPR const char *get_units<std::ratio<60>>() {
|
||||||
|
return "m";
|
||||||
|
}
|
||||||
|
template <> FMT_CONSTEXPR const char *get_units<std::ratio<3600>>() {
|
||||||
|
return "h";
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Rep, typename Period, typename Char>
|
||||||
|
struct formatter<std::chrono::duration<Rep, Period>, Char> {
|
||||||
|
private:
|
||||||
|
align_spec spec;
|
||||||
|
internal::arg_ref<Char> width_ref;
|
||||||
|
mutable basic_string_view<Char> format_str;
|
||||||
|
typedef std::chrono::duration<Rep, Period> duration;
|
||||||
|
|
||||||
|
struct spec_handler {
|
||||||
|
formatter &f;
|
||||||
|
basic_parse_context<Char> &context;
|
||||||
|
|
||||||
|
typedef internal::arg_ref<Char> arg_ref_type;
|
||||||
|
|
||||||
|
template <typename Id>
|
||||||
|
FMT_CONSTEXPR arg_ref_type make_arg_ref(Id arg_id) {
|
||||||
|
context.check_arg_id(arg_id);
|
||||||
|
return arg_ref_type(arg_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
FMT_CONSTEXPR arg_ref_type make_arg_ref(internal::auto_id) {
|
||||||
|
return arg_ref_type(context.next_arg_id());
|
||||||
|
}
|
||||||
|
|
||||||
|
void on_error(const char *msg) { throw format_error(msg); }
|
||||||
|
void on_fill(Char fill) { f.spec.fill_ = fill; }
|
||||||
|
void on_align(alignment align) { f.spec.align_ = align; }
|
||||||
|
void on_width(unsigned width) { f.spec.width_ = width; }
|
||||||
|
|
||||||
|
template <typename Id>
|
||||||
|
void on_dynamic_width(Id arg_id) {
|
||||||
|
f.width_ref = make_arg_ref(arg_id);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
public:
|
||||||
|
formatter() : spec() {}
|
||||||
|
|
||||||
|
FMT_CONSTEXPR auto parse(basic_parse_context<Char> &ctx)
|
||||||
|
-> decltype(ctx.begin()) {
|
||||||
|
auto begin = ctx.begin(), end = ctx.end();
|
||||||
|
if (begin == end) return begin;
|
||||||
|
spec_handler handler{*this, ctx};
|
||||||
|
begin = internal::parse_align(begin, end, handler);
|
||||||
|
if (begin == end) return begin;
|
||||||
|
begin = internal::parse_width(begin, end, handler);
|
||||||
|
end = parse_chrono_format(begin, end, internal::chrono_format_checker());
|
||||||
|
format_str = basic_string_view<Char>(&*begin, internal::to_unsigned(end - begin));
|
||||||
|
return end;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename FormatContext>
|
||||||
|
auto format(const duration &d, FormatContext &ctx)
|
||||||
|
-> decltype(ctx.out()) {
|
||||||
|
auto begin = format_str.begin(), end = format_str.end();
|
||||||
|
memory_buffer buf;
|
||||||
|
typedef output_range<decltype(ctx.out()), Char> range;
|
||||||
|
basic_writer<range> w(range(ctx.out()));
|
||||||
|
if (begin == end || *begin == '}') {
|
||||||
|
if (const char *unit = get_units<Period>())
|
||||||
|
format_to(buf, "{}{}", d.count(), unit);
|
||||||
|
else if (Period::den == 1)
|
||||||
|
format_to(buf, "{}[{}]s", d.count(), Period::num);
|
||||||
|
else
|
||||||
|
format_to(buf, "{}[{}/{}]s", d.count(), Period::num, Period::den);
|
||||||
|
internal::handle_dynamic_spec<internal::width_checker>(
|
||||||
|
spec.width_, width_ref, ctx);
|
||||||
|
} else {
|
||||||
|
auto out = std::back_inserter(buf);
|
||||||
|
internal::chrono_formatter<FormatContext, decltype(out)> f(ctx, out);
|
||||||
|
f.s = std::chrono::duration_cast<std::chrono::seconds>(d);
|
||||||
|
f.ms = std::chrono::duration_cast<std::chrono::milliseconds>(d - f.s);
|
||||||
|
parse_chrono_format(begin, end, f);
|
||||||
|
}
|
||||||
|
w.write(buf.data(), buf.size(), spec);
|
||||||
|
return w.out();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
FMT_END_NAMESPACE
|
||||||
|
|
||||||
|
#endif // FMT_CHRONO_H_
|
|
@ -0,0 +1,577 @@
|
||||||
|
// Formatting library for C++ - color support
|
||||||
|
//
|
||||||
|
// Copyright (c) 2018 - present, Victor Zverovich and fmt contributors
|
||||||
|
// All rights reserved.
|
||||||
|
//
|
||||||
|
// For the license information refer to format.h.
|
||||||
|
|
||||||
|
#ifndef FMT_COLOR_H_
|
||||||
|
#define FMT_COLOR_H_
|
||||||
|
|
||||||
|
#include "format.h"
|
||||||
|
|
||||||
|
FMT_BEGIN_NAMESPACE
|
||||||
|
|
||||||
|
#ifdef FMT_DEPRECATED_COLORS
|
||||||
|
|
||||||
|
// color and (v)print_colored are deprecated.
|
||||||
|
enum color { black, red, green, yellow, blue, magenta, cyan, white };
|
||||||
|
FMT_API void vprint_colored(color c, string_view format, format_args args);
|
||||||
|
FMT_API void vprint_colored(color c, wstring_view format, wformat_args args);
|
||||||
|
template <typename... Args>
|
||||||
|
inline void print_colored(color c, string_view format_str,
|
||||||
|
const Args & ... args) {
|
||||||
|
vprint_colored(c, format_str, make_format_args(args...));
|
||||||
|
}
|
||||||
|
template <typename... Args>
|
||||||
|
inline void print_colored(color c, wstring_view format_str,
|
||||||
|
const Args & ... args) {
|
||||||
|
vprint_colored(c, format_str, make_format_args<wformat_context>(args...));
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void vprint_colored(color c, string_view format, format_args args) {
|
||||||
|
char escape[] = "\x1b[30m";
|
||||||
|
escape[3] = static_cast<char>('0' + c);
|
||||||
|
std::fputs(escape, stdout);
|
||||||
|
vprint(format, args);
|
||||||
|
std::fputs(internal::data::RESET_COLOR, stdout);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void vprint_colored(color c, wstring_view format, wformat_args args) {
|
||||||
|
wchar_t escape[] = L"\x1b[30m";
|
||||||
|
escape[3] = static_cast<wchar_t>('0' + c);
|
||||||
|
std::fputws(escape, stdout);
|
||||||
|
vprint(format, args);
|
||||||
|
std::fputws(internal::data::WRESET_COLOR, stdout);
|
||||||
|
}
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
|
enum class color : uint32_t {
|
||||||
|
alice_blue = 0xF0F8FF, // rgb(240,248,255)
|
||||||
|
antique_white = 0xFAEBD7, // rgb(250,235,215)
|
||||||
|
aqua = 0x00FFFF, // rgb(0,255,255)
|
||||||
|
aquamarine = 0x7FFFD4, // rgb(127,255,212)
|
||||||
|
azure = 0xF0FFFF, // rgb(240,255,255)
|
||||||
|
beige = 0xF5F5DC, // rgb(245,245,220)
|
||||||
|
bisque = 0xFFE4C4, // rgb(255,228,196)
|
||||||
|
black = 0x000000, // rgb(0,0,0)
|
||||||
|
blanched_almond = 0xFFEBCD, // rgb(255,235,205)
|
||||||
|
blue = 0x0000FF, // rgb(0,0,255)
|
||||||
|
blue_violet = 0x8A2BE2, // rgb(138,43,226)
|
||||||
|
brown = 0xA52A2A, // rgb(165,42,42)
|
||||||
|
burly_wood = 0xDEB887, // rgb(222,184,135)
|
||||||
|
cadet_blue = 0x5F9EA0, // rgb(95,158,160)
|
||||||
|
chartreuse = 0x7FFF00, // rgb(127,255,0)
|
||||||
|
chocolate = 0xD2691E, // rgb(210,105,30)
|
||||||
|
coral = 0xFF7F50, // rgb(255,127,80)
|
||||||
|
cornflower_blue = 0x6495ED, // rgb(100,149,237)
|
||||||
|
cornsilk = 0xFFF8DC, // rgb(255,248,220)
|
||||||
|
crimson = 0xDC143C, // rgb(220,20,60)
|
||||||
|
cyan = 0x00FFFF, // rgb(0,255,255)
|
||||||
|
dark_blue = 0x00008B, // rgb(0,0,139)
|
||||||
|
dark_cyan = 0x008B8B, // rgb(0,139,139)
|
||||||
|
dark_golden_rod = 0xB8860B, // rgb(184,134,11)
|
||||||
|
dark_gray = 0xA9A9A9, // rgb(169,169,169)
|
||||||
|
dark_green = 0x006400, // rgb(0,100,0)
|
||||||
|
dark_khaki = 0xBDB76B, // rgb(189,183,107)
|
||||||
|
dark_magenta = 0x8B008B, // rgb(139,0,139)
|
||||||
|
dark_olive_green = 0x556B2F, // rgb(85,107,47)
|
||||||
|
dark_orange = 0xFF8C00, // rgb(255,140,0)
|
||||||
|
dark_orchid = 0x9932CC, // rgb(153,50,204)
|
||||||
|
dark_red = 0x8B0000, // rgb(139,0,0)
|
||||||
|
dark_salmon = 0xE9967A, // rgb(233,150,122)
|
||||||
|
dark_sea_green = 0x8FBC8F, // rgb(143,188,143)
|
||||||
|
dark_slate_blue = 0x483D8B, // rgb(72,61,139)
|
||||||
|
dark_slate_gray = 0x2F4F4F, // rgb(47,79,79)
|
||||||
|
dark_turquoise = 0x00CED1, // rgb(0,206,209)
|
||||||
|
dark_violet = 0x9400D3, // rgb(148,0,211)
|
||||||
|
deep_pink = 0xFF1493, // rgb(255,20,147)
|
||||||
|
deep_sky_blue = 0x00BFFF, // rgb(0,191,255)
|
||||||
|
dim_gray = 0x696969, // rgb(105,105,105)
|
||||||
|
dodger_blue = 0x1E90FF, // rgb(30,144,255)
|
||||||
|
fire_brick = 0xB22222, // rgb(178,34,34)
|
||||||
|
floral_white = 0xFFFAF0, // rgb(255,250,240)
|
||||||
|
forest_green = 0x228B22, // rgb(34,139,34)
|
||||||
|
fuchsia = 0xFF00FF, // rgb(255,0,255)
|
||||||
|
gainsboro = 0xDCDCDC, // rgb(220,220,220)
|
||||||
|
ghost_white = 0xF8F8FF, // rgb(248,248,255)
|
||||||
|
gold = 0xFFD700, // rgb(255,215,0)
|
||||||
|
golden_rod = 0xDAA520, // rgb(218,165,32)
|
||||||
|
gray = 0x808080, // rgb(128,128,128)
|
||||||
|
green = 0x008000, // rgb(0,128,0)
|
||||||
|
green_yellow = 0xADFF2F, // rgb(173,255,47)
|
||||||
|
honey_dew = 0xF0FFF0, // rgb(240,255,240)
|
||||||
|
hot_pink = 0xFF69B4, // rgb(255,105,180)
|
||||||
|
indian_red = 0xCD5C5C, // rgb(205,92,92)
|
||||||
|
indigo = 0x4B0082, // rgb(75,0,130)
|
||||||
|
ivory = 0xFFFFF0, // rgb(255,255,240)
|
||||||
|
khaki = 0xF0E68C, // rgb(240,230,140)
|
||||||
|
lavender = 0xE6E6FA, // rgb(230,230,250)
|
||||||
|
lavender_blush = 0xFFF0F5, // rgb(255,240,245)
|
||||||
|
lawn_green = 0x7CFC00, // rgb(124,252,0)
|
||||||
|
lemon_chiffon = 0xFFFACD, // rgb(255,250,205)
|
||||||
|
light_blue = 0xADD8E6, // rgb(173,216,230)
|
||||||
|
light_coral = 0xF08080, // rgb(240,128,128)
|
||||||
|
light_cyan = 0xE0FFFF, // rgb(224,255,255)
|
||||||
|
light_golden_rod_yellow = 0xFAFAD2, // rgb(250,250,210)
|
||||||
|
light_gray = 0xD3D3D3, // rgb(211,211,211)
|
||||||
|
light_green = 0x90EE90, // rgb(144,238,144)
|
||||||
|
light_pink = 0xFFB6C1, // rgb(255,182,193)
|
||||||
|
light_salmon = 0xFFA07A, // rgb(255,160,122)
|
||||||
|
light_sea_green = 0x20B2AA, // rgb(32,178,170)
|
||||||
|
light_sky_blue = 0x87CEFA, // rgb(135,206,250)
|
||||||
|
light_slate_gray = 0x778899, // rgb(119,136,153)
|
||||||
|
light_steel_blue = 0xB0C4DE, // rgb(176,196,222)
|
||||||
|
light_yellow = 0xFFFFE0, // rgb(255,255,224)
|
||||||
|
lime = 0x00FF00, // rgb(0,255,0)
|
||||||
|
lime_green = 0x32CD32, // rgb(50,205,50)
|
||||||
|
linen = 0xFAF0E6, // rgb(250,240,230)
|
||||||
|
magenta = 0xFF00FF, // rgb(255,0,255)
|
||||||
|
maroon = 0x800000, // rgb(128,0,0)
|
||||||
|
medium_aquamarine = 0x66CDAA, // rgb(102,205,170)
|
||||||
|
medium_blue = 0x0000CD, // rgb(0,0,205)
|
||||||
|
medium_orchid = 0xBA55D3, // rgb(186,85,211)
|
||||||
|
medium_purple = 0x9370DB, // rgb(147,112,219)
|
||||||
|
medium_sea_green = 0x3CB371, // rgb(60,179,113)
|
||||||
|
medium_slate_blue = 0x7B68EE, // rgb(123,104,238)
|
||||||
|
medium_spring_green = 0x00FA9A, // rgb(0,250,154)
|
||||||
|
medium_turquoise = 0x48D1CC, // rgb(72,209,204)
|
||||||
|
medium_violet_red = 0xC71585, // rgb(199,21,133)
|
||||||
|
midnight_blue = 0x191970, // rgb(25,25,112)
|
||||||
|
mint_cream = 0xF5FFFA, // rgb(245,255,250)
|
||||||
|
misty_rose = 0xFFE4E1, // rgb(255,228,225)
|
||||||
|
moccasin = 0xFFE4B5, // rgb(255,228,181)
|
||||||
|
navajo_white = 0xFFDEAD, // rgb(255,222,173)
|
||||||
|
navy = 0x000080, // rgb(0,0,128)
|
||||||
|
old_lace = 0xFDF5E6, // rgb(253,245,230)
|
||||||
|
olive = 0x808000, // rgb(128,128,0)
|
||||||
|
olive_drab = 0x6B8E23, // rgb(107,142,35)
|
||||||
|
orange = 0xFFA500, // rgb(255,165,0)
|
||||||
|
orange_red = 0xFF4500, // rgb(255,69,0)
|
||||||
|
orchid = 0xDA70D6, // rgb(218,112,214)
|
||||||
|
pale_golden_rod = 0xEEE8AA, // rgb(238,232,170)
|
||||||
|
pale_green = 0x98FB98, // rgb(152,251,152)
|
||||||
|
pale_turquoise = 0xAFEEEE, // rgb(175,238,238)
|
||||||
|
pale_violet_red = 0xDB7093, // rgb(219,112,147)
|
||||||
|
papaya_whip = 0xFFEFD5, // rgb(255,239,213)
|
||||||
|
peach_puff = 0xFFDAB9, // rgb(255,218,185)
|
||||||
|
peru = 0xCD853F, // rgb(205,133,63)
|
||||||
|
pink = 0xFFC0CB, // rgb(255,192,203)
|
||||||
|
plum = 0xDDA0DD, // rgb(221,160,221)
|
||||||
|
powder_blue = 0xB0E0E6, // rgb(176,224,230)
|
||||||
|
purple = 0x800080, // rgb(128,0,128)
|
||||||
|
rebecca_purple = 0x663399, // rgb(102,51,153)
|
||||||
|
red = 0xFF0000, // rgb(255,0,0)
|
||||||
|
rosy_brown = 0xBC8F8F, // rgb(188,143,143)
|
||||||
|
royal_blue = 0x4169E1, // rgb(65,105,225)
|
||||||
|
saddle_brown = 0x8B4513, // rgb(139,69,19)
|
||||||
|
salmon = 0xFA8072, // rgb(250,128,114)
|
||||||
|
sandy_brown = 0xF4A460, // rgb(244,164,96)
|
||||||
|
sea_green = 0x2E8B57, // rgb(46,139,87)
|
||||||
|
sea_shell = 0xFFF5EE, // rgb(255,245,238)
|
||||||
|
sienna = 0xA0522D, // rgb(160,82,45)
|
||||||
|
silver = 0xC0C0C0, // rgb(192,192,192)
|
||||||
|
sky_blue = 0x87CEEB, // rgb(135,206,235)
|
||||||
|
slate_blue = 0x6A5ACD, // rgb(106,90,205)
|
||||||
|
slate_gray = 0x708090, // rgb(112,128,144)
|
||||||
|
snow = 0xFFFAFA, // rgb(255,250,250)
|
||||||
|
spring_green = 0x00FF7F, // rgb(0,255,127)
|
||||||
|
steel_blue = 0x4682B4, // rgb(70,130,180)
|
||||||
|
tan = 0xD2B48C, // rgb(210,180,140)
|
||||||
|
teal = 0x008080, // rgb(0,128,128)
|
||||||
|
thistle = 0xD8BFD8, // rgb(216,191,216)
|
||||||
|
tomato = 0xFF6347, // rgb(255,99,71)
|
||||||
|
turquoise = 0x40E0D0, // rgb(64,224,208)
|
||||||
|
violet = 0xEE82EE, // rgb(238,130,238)
|
||||||
|
wheat = 0xF5DEB3, // rgb(245,222,179)
|
||||||
|
white = 0xFFFFFF, // rgb(255,255,255)
|
||||||
|
white_smoke = 0xF5F5F5, // rgb(245,245,245)
|
||||||
|
yellow = 0xFFFF00, // rgb(255,255,0)
|
||||||
|
yellow_green = 0x9ACD32 // rgb(154,205,50)
|
||||||
|
}; // enum class color
|
||||||
|
|
||||||
|
enum class terminal_color : uint8_t {
|
||||||
|
black = 30,
|
||||||
|
red,
|
||||||
|
green,
|
||||||
|
yellow,
|
||||||
|
blue,
|
||||||
|
magenta,
|
||||||
|
cyan,
|
||||||
|
white,
|
||||||
|
bright_black = 90,
|
||||||
|
bright_red,
|
||||||
|
bright_green,
|
||||||
|
bright_yellow,
|
||||||
|
bright_blue,
|
||||||
|
bright_magenta,
|
||||||
|
bright_cyan,
|
||||||
|
bright_white
|
||||||
|
}; // enum class terminal_color
|
||||||
|
|
||||||
|
enum class emphasis : uint8_t {
|
||||||
|
bold = 1,
|
||||||
|
italic = 1 << 1,
|
||||||
|
underline = 1 << 2,
|
||||||
|
strikethrough = 1 << 3
|
||||||
|
}; // enum class emphasis
|
||||||
|
|
||||||
|
// rgb is a struct for red, green and blue colors.
|
||||||
|
// We use rgb as name because some editors will show it as color direct in the
|
||||||
|
// editor.
|
||||||
|
struct rgb {
|
||||||
|
FMT_CONSTEXPR_DECL rgb() : r(0), g(0), b(0) {}
|
||||||
|
FMT_CONSTEXPR_DECL rgb(uint8_t r_, uint8_t g_, uint8_t b_)
|
||||||
|
: r(r_), g(g_), b(b_) {}
|
||||||
|
FMT_CONSTEXPR_DECL rgb(uint32_t hex)
|
||||||
|
: r((hex >> 16) & 0xFF), g((hex >> 8) & 0xFF), b((hex) & 0xFF) {}
|
||||||
|
FMT_CONSTEXPR_DECL rgb(color hex)
|
||||||
|
: r((uint32_t(hex) >> 16) & 0xFF), g((uint32_t(hex) >> 8) & 0xFF),
|
||||||
|
b(uint32_t(hex) & 0xFF) {}
|
||||||
|
uint8_t r;
|
||||||
|
uint8_t g;
|
||||||
|
uint8_t b;
|
||||||
|
};
|
||||||
|
|
||||||
|
namespace internal {
|
||||||
|
|
||||||
|
// color is a struct of either a rgb color or a terminal color.
|
||||||
|
struct color_type {
|
||||||
|
FMT_CONSTEXPR color_type() FMT_NOEXCEPT
|
||||||
|
: is_rgb(), value{} {}
|
||||||
|
FMT_CONSTEXPR color_type(color rgb_color) FMT_NOEXCEPT
|
||||||
|
: is_rgb(true), value{} {
|
||||||
|
value.rgb_color = static_cast<uint32_t>(rgb_color);
|
||||||
|
}
|
||||||
|
FMT_CONSTEXPR color_type(rgb rgb_color) FMT_NOEXCEPT
|
||||||
|
: is_rgb(true), value{} {
|
||||||
|
value.rgb_color = (static_cast<uint32_t>(rgb_color.r) << 16)
|
||||||
|
| (static_cast<uint32_t>(rgb_color.g) << 8) | rgb_color.b;
|
||||||
|
}
|
||||||
|
FMT_CONSTEXPR color_type(terminal_color term_color) FMT_NOEXCEPT
|
||||||
|
: is_rgb(), value{} {
|
||||||
|
value.term_color = static_cast<uint8_t>(term_color);
|
||||||
|
}
|
||||||
|
bool is_rgb;
|
||||||
|
union color_union {
|
||||||
|
uint8_t term_color;
|
||||||
|
uint32_t rgb_color;
|
||||||
|
} value;
|
||||||
|
};
|
||||||
|
} // namespace internal
|
||||||
|
|
||||||
|
// Experimental text formatting support.
|
||||||
|
class text_style {
|
||||||
|
public:
|
||||||
|
FMT_CONSTEXPR text_style(emphasis em = emphasis()) FMT_NOEXCEPT
|
||||||
|
: set_foreground_color(), set_background_color(), ems(em) {}
|
||||||
|
|
||||||
|
FMT_CONSTEXPR text_style &operator|=(const text_style &rhs) {
|
||||||
|
if (!set_foreground_color) {
|
||||||
|
set_foreground_color = rhs.set_foreground_color;
|
||||||
|
foreground_color = rhs.foreground_color;
|
||||||
|
} else if (rhs.set_foreground_color) {
|
||||||
|
if (!foreground_color.is_rgb || !rhs.foreground_color.is_rgb)
|
||||||
|
throw format_error("can't OR a terminal color");
|
||||||
|
foreground_color.value.rgb_color |= rhs.foreground_color.value.rgb_color;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!set_background_color) {
|
||||||
|
set_background_color = rhs.set_background_color;
|
||||||
|
background_color = rhs.background_color;
|
||||||
|
} else if (rhs.set_background_color) {
|
||||||
|
if (!background_color.is_rgb || !rhs.background_color.is_rgb)
|
||||||
|
throw format_error("can't OR a terminal color");
|
||||||
|
background_color.value.rgb_color |= rhs.background_color.value.rgb_color;
|
||||||
|
}
|
||||||
|
|
||||||
|
ems = static_cast<emphasis>(static_cast<uint8_t>(ems) |
|
||||||
|
static_cast<uint8_t>(rhs.ems));
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
friend FMT_CONSTEXPR
|
||||||
|
text_style operator|(text_style lhs, const text_style &rhs) {
|
||||||
|
return lhs |= rhs;
|
||||||
|
}
|
||||||
|
|
||||||
|
FMT_CONSTEXPR text_style &operator&=(const text_style &rhs) {
|
||||||
|
if (!set_foreground_color) {
|
||||||
|
set_foreground_color = rhs.set_foreground_color;
|
||||||
|
foreground_color = rhs.foreground_color;
|
||||||
|
} else if (rhs.set_foreground_color) {
|
||||||
|
if (!foreground_color.is_rgb || !rhs.foreground_color.is_rgb)
|
||||||
|
throw format_error("can't AND a terminal color");
|
||||||
|
foreground_color.value.rgb_color &= rhs.foreground_color.value.rgb_color;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!set_background_color) {
|
||||||
|
set_background_color = rhs.set_background_color;
|
||||||
|
background_color = rhs.background_color;
|
||||||
|
} else if (rhs.set_background_color) {
|
||||||
|
if (!background_color.is_rgb || !rhs.background_color.is_rgb)
|
||||||
|
throw format_error("can't AND a terminal color");
|
||||||
|
background_color.value.rgb_color &= rhs.background_color.value.rgb_color;
|
||||||
|
}
|
||||||
|
|
||||||
|
ems = static_cast<emphasis>(static_cast<uint8_t>(ems) &
|
||||||
|
static_cast<uint8_t>(rhs.ems));
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
friend FMT_CONSTEXPR
|
||||||
|
text_style operator&(text_style lhs, const text_style &rhs) {
|
||||||
|
return lhs &= rhs;
|
||||||
|
}
|
||||||
|
|
||||||
|
FMT_CONSTEXPR bool has_foreground() const FMT_NOEXCEPT {
|
||||||
|
return set_foreground_color;
|
||||||
|
}
|
||||||
|
FMT_CONSTEXPR bool has_background() const FMT_NOEXCEPT {
|
||||||
|
return set_background_color;
|
||||||
|
}
|
||||||
|
FMT_CONSTEXPR bool has_emphasis() const FMT_NOEXCEPT {
|
||||||
|
return static_cast<uint8_t>(ems) != 0;
|
||||||
|
}
|
||||||
|
FMT_CONSTEXPR internal::color_type get_foreground() const FMT_NOEXCEPT {
|
||||||
|
assert(has_foreground() && "no foreground specified for this style");
|
||||||
|
return foreground_color;
|
||||||
|
}
|
||||||
|
FMT_CONSTEXPR internal::color_type get_background() const FMT_NOEXCEPT {
|
||||||
|
assert(has_background() && "no background specified for this style");
|
||||||
|
return background_color;
|
||||||
|
}
|
||||||
|
FMT_CONSTEXPR emphasis get_emphasis() const FMT_NOEXCEPT {
|
||||||
|
assert(has_emphasis() && "no emphasis specified for this style");
|
||||||
|
return ems;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
FMT_CONSTEXPR text_style(bool is_foreground,
|
||||||
|
internal::color_type text_color) FMT_NOEXCEPT
|
||||||
|
: set_foreground_color(),
|
||||||
|
set_background_color(),
|
||||||
|
ems() {
|
||||||
|
if (is_foreground) {
|
||||||
|
foreground_color = text_color;
|
||||||
|
set_foreground_color = true;
|
||||||
|
} else {
|
||||||
|
background_color = text_color;
|
||||||
|
set_background_color = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
friend FMT_CONSTEXPR_DECL text_style fg(internal::color_type foreground)
|
||||||
|
FMT_NOEXCEPT;
|
||||||
|
friend FMT_CONSTEXPR_DECL text_style bg(internal::color_type background)
|
||||||
|
FMT_NOEXCEPT;
|
||||||
|
|
||||||
|
internal::color_type foreground_color;
|
||||||
|
internal::color_type background_color;
|
||||||
|
bool set_foreground_color;
|
||||||
|
bool set_background_color;
|
||||||
|
emphasis ems;
|
||||||
|
};
|
||||||
|
|
||||||
|
FMT_CONSTEXPR text_style fg(internal::color_type foreground) FMT_NOEXCEPT {
|
||||||
|
return text_style(/*is_foreground=*/true, foreground);
|
||||||
|
}
|
||||||
|
|
||||||
|
FMT_CONSTEXPR text_style bg(internal::color_type background) FMT_NOEXCEPT {
|
||||||
|
return text_style(/*is_foreground=*/false, background);
|
||||||
|
}
|
||||||
|
|
||||||
|
FMT_CONSTEXPR text_style operator|(emphasis lhs, emphasis rhs) FMT_NOEXCEPT {
|
||||||
|
return text_style(lhs) | rhs;
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace internal {
|
||||||
|
|
||||||
|
template <typename Char>
|
||||||
|
struct ansi_color_escape {
|
||||||
|
FMT_CONSTEXPR ansi_color_escape(internal::color_type text_color,
|
||||||
|
const char * esc) FMT_NOEXCEPT {
|
||||||
|
// If we have a terminal color, we need to output another escape code
|
||||||
|
// sequence.
|
||||||
|
if (!text_color.is_rgb) {
|
||||||
|
bool is_background = esc == internal::data::BACKGROUND_COLOR;
|
||||||
|
uint32_t value = text_color.value.term_color;
|
||||||
|
// Background ASCII codes are the same as the foreground ones but with
|
||||||
|
// 10 more.
|
||||||
|
if (is_background)
|
||||||
|
value += 10u;
|
||||||
|
|
||||||
|
std::size_t index = 0;
|
||||||
|
buffer[index++] = static_cast<Char>('\x1b');
|
||||||
|
buffer[index++] = static_cast<Char>('[');
|
||||||
|
|
||||||
|
if (value >= 100u) {
|
||||||
|
buffer[index++] = static_cast<Char>('1');
|
||||||
|
value %= 100u;
|
||||||
|
}
|
||||||
|
buffer[index++] = static_cast<Char>('0' + value / 10u);
|
||||||
|
buffer[index++] = static_cast<Char>('0' + value % 10u);
|
||||||
|
|
||||||
|
buffer[index++] = static_cast<Char>('m');
|
||||||
|
buffer[index++] = static_cast<Char>('\0');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < 7; i++) {
|
||||||
|
buffer[i] = static_cast<Char>(esc[i]);
|
||||||
|
}
|
||||||
|
rgb color(text_color.value.rgb_color);
|
||||||
|
to_esc(color.r, buffer + 7, ';');
|
||||||
|
to_esc(color.g, buffer + 11, ';');
|
||||||
|
to_esc(color.b, buffer + 15, 'm');
|
||||||
|
buffer[19] = static_cast<Char>(0);
|
||||||
|
}
|
||||||
|
FMT_CONSTEXPR ansi_color_escape(emphasis em) FMT_NOEXCEPT {
|
||||||
|
uint8_t em_codes[4] = {};
|
||||||
|
uint8_t em_bits = static_cast<uint8_t>(em);
|
||||||
|
if (em_bits & static_cast<uint8_t>(emphasis::bold))
|
||||||
|
em_codes[0] = 1;
|
||||||
|
if (em_bits & static_cast<uint8_t>(emphasis::italic))
|
||||||
|
em_codes[1] = 3;
|
||||||
|
if (em_bits & static_cast<uint8_t>(emphasis::underline))
|
||||||
|
em_codes[2] = 4;
|
||||||
|
if (em_bits & static_cast<uint8_t>(emphasis::strikethrough))
|
||||||
|
em_codes[3] = 9;
|
||||||
|
|
||||||
|
std::size_t index = 0;
|
||||||
|
for (int i = 0; i < 4; ++i) {
|
||||||
|
if (!em_codes[i])
|
||||||
|
continue;
|
||||||
|
buffer[index++] = static_cast<Char>('\x1b');
|
||||||
|
buffer[index++] = static_cast<Char>('[');
|
||||||
|
buffer[index++] = static_cast<Char>('0' + em_codes[i]);
|
||||||
|
buffer[index++] = static_cast<Char>('m');
|
||||||
|
}
|
||||||
|
buffer[index++] = static_cast<Char>(0);
|
||||||
|
}
|
||||||
|
FMT_CONSTEXPR operator const Char *() const FMT_NOEXCEPT { return buffer; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
Char buffer[7u + 3u * 4u + 1u];
|
||||||
|
|
||||||
|
static FMT_CONSTEXPR void to_esc(uint8_t c, Char *out,
|
||||||
|
char delimiter) FMT_NOEXCEPT {
|
||||||
|
out[0] = static_cast<Char>('0' + c / 100);
|
||||||
|
out[1] = static_cast<Char>('0' + c / 10 % 10);
|
||||||
|
out[2] = static_cast<Char>('0' + c % 10);
|
||||||
|
out[3] = static_cast<Char>(delimiter);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename Char>
|
||||||
|
FMT_CONSTEXPR ansi_color_escape<Char>
|
||||||
|
make_foreground_color(internal::color_type foreground) FMT_NOEXCEPT {
|
||||||
|
return ansi_color_escape<Char>(foreground, internal::data::FOREGROUND_COLOR);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Char>
|
||||||
|
FMT_CONSTEXPR ansi_color_escape<Char>
|
||||||
|
make_background_color(internal::color_type background) FMT_NOEXCEPT {
|
||||||
|
return ansi_color_escape<Char>(background, internal::data::BACKGROUND_COLOR);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Char>
|
||||||
|
FMT_CONSTEXPR ansi_color_escape<Char>
|
||||||
|
make_emphasis(emphasis em) FMT_NOEXCEPT {
|
||||||
|
return ansi_color_escape<Char>(em);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Char>
|
||||||
|
inline void fputs(const Char *chars, FILE *stream) FMT_NOEXCEPT {
|
||||||
|
std::fputs(chars, stream);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <>
|
||||||
|
inline void fputs<wchar_t>(const wchar_t *chars, FILE *stream) FMT_NOEXCEPT {
|
||||||
|
std::fputws(chars, stream);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Char>
|
||||||
|
inline void reset_color(FILE *stream) FMT_NOEXCEPT {
|
||||||
|
fputs(internal::data::RESET_COLOR, stream);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <>
|
||||||
|
inline void reset_color<wchar_t>(FILE *stream) FMT_NOEXCEPT {
|
||||||
|
fputs(internal::data::WRESET_COLOR, stream);
|
||||||
|
}
|
||||||
|
|
||||||
|
// The following specialiazation disables using std::FILE as a character type,
|
||||||
|
// which is needed because or else
|
||||||
|
// fmt::print(stderr, fmt::emphasis::bold, "");
|
||||||
|
// would take stderr (a std::FILE *) as the format string.
|
||||||
|
template <>
|
||||||
|
struct is_string<std::FILE *> : std::false_type {};
|
||||||
|
template <>
|
||||||
|
struct is_string<const std::FILE *> : std::false_type {};
|
||||||
|
} // namespace internal
|
||||||
|
|
||||||
|
template <
|
||||||
|
typename S, typename Char = typename internal::char_t<S>::type>
|
||||||
|
void vprint(std::FILE *f, const text_style &ts, const S &format,
|
||||||
|
basic_format_args<typename buffer_context<Char>::type> args) {
|
||||||
|
bool has_style = false;
|
||||||
|
if (ts.has_emphasis()) {
|
||||||
|
has_style = true;
|
||||||
|
internal::fputs<Char>(
|
||||||
|
internal::make_emphasis<Char>(ts.get_emphasis()), f);
|
||||||
|
}
|
||||||
|
if (ts.has_foreground()) {
|
||||||
|
has_style = true;
|
||||||
|
internal::fputs<Char>(
|
||||||
|
internal::make_foreground_color<Char>(ts.get_foreground()), f);
|
||||||
|
}
|
||||||
|
if (ts.has_background()) {
|
||||||
|
has_style = true;
|
||||||
|
internal::fputs<Char>(
|
||||||
|
internal::make_background_color<Char>(ts.get_background()), f);
|
||||||
|
}
|
||||||
|
vprint(f, format, args);
|
||||||
|
if (has_style) {
|
||||||
|
internal::reset_color<Char>(f);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Formats a string and prints it to the specified file stream using ANSI
|
||||||
|
escape sequences to specify text formatting.
|
||||||
|
Example:
|
||||||
|
fmt::print(fmt::emphasis::bold | fg(fmt::color::red),
|
||||||
|
"Elapsed time: {0:.2f} seconds", 1.23);
|
||||||
|
*/
|
||||||
|
template <typename String, typename... Args>
|
||||||
|
typename std::enable_if<internal::is_string<String>::value>::type print(
|
||||||
|
std::FILE *f, const text_style &ts, const String &format_str,
|
||||||
|
const Args &... args) {
|
||||||
|
internal::check_format_string<Args...>(format_str);
|
||||||
|
typedef typename internal::char_t<String>::type char_t;
|
||||||
|
typedef typename buffer_context<char_t>::type context_t;
|
||||||
|
format_arg_store<context_t, Args...> as{args...};
|
||||||
|
vprint(f, ts, format_str, basic_format_args<context_t>(as));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Formats a string and prints it to stdout using ANSI escape sequences to
|
||||||
|
specify text formatting.
|
||||||
|
Example:
|
||||||
|
fmt::print(fmt::emphasis::bold | fg(fmt::color::red),
|
||||||
|
"Elapsed time: {0:.2f} seconds", 1.23);
|
||||||
|
*/
|
||||||
|
template <typename String, typename... Args>
|
||||||
|
typename std::enable_if<internal::is_string<String>::value>::type print(
|
||||||
|
const text_style &ts, const String &format_str,
|
||||||
|
const Args &... args) {
|
||||||
|
return print(stdout, ts, format_str, args...);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
FMT_END_NAMESPACE
|
||||||
|
|
||||||
|
#endif // FMT_COLOR_H_
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,972 @@
|
||||||
|
// Formatting library for C++
|
||||||
|
//
|
||||||
|
// Copyright (c) 2012 - 2016, Victor Zverovich
|
||||||
|
// All rights reserved.
|
||||||
|
//
|
||||||
|
// For the license information refer to format.h.
|
||||||
|
|
||||||
|
#ifndef FMT_FORMAT_INL_H_
|
||||||
|
#define FMT_FORMAT_INL_H_
|
||||||
|
|
||||||
|
#include "format.h"
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include <cctype>
|
||||||
|
#include <cerrno>
|
||||||
|
#include <climits>
|
||||||
|
#include <cmath>
|
||||||
|
#include <cstdarg>
|
||||||
|
#include <cstddef> // for std::ptrdiff_t
|
||||||
|
#include <cstring> // for std::memmove
|
||||||
|
#if !defined(FMT_STATIC_THOUSANDS_SEPARATOR)
|
||||||
|
# include <locale>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if FMT_USE_WINDOWS_H
|
||||||
|
# if !defined(FMT_HEADER_ONLY) && !defined(WIN32_LEAN_AND_MEAN)
|
||||||
|
# define WIN32_LEAN_AND_MEAN
|
||||||
|
# endif
|
||||||
|
# if defined(NOMINMAX) || defined(FMT_WIN_MINMAX)
|
||||||
|
# include <windows.h>
|
||||||
|
# else
|
||||||
|
# define NOMINMAX
|
||||||
|
# include <windows.h>
|
||||||
|
# undef NOMINMAX
|
||||||
|
# endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if FMT_EXCEPTIONS
|
||||||
|
# define FMT_TRY try
|
||||||
|
# define FMT_CATCH(x) catch (x)
|
||||||
|
#else
|
||||||
|
# define FMT_TRY if (true)
|
||||||
|
# define FMT_CATCH(x) if (false)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef _MSC_VER
|
||||||
|
# pragma warning(push)
|
||||||
|
# pragma warning(disable: 4127) // conditional expression is constant
|
||||||
|
# pragma warning(disable: 4702) // unreachable code
|
||||||
|
// Disable deprecation warning for strerror. The latter is not called but
|
||||||
|
// MSVC fails to detect it.
|
||||||
|
# pragma warning(disable: 4996)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Dummy implementations of strerror_r and strerror_s called if corresponding
|
||||||
|
// system functions are not available.
|
||||||
|
inline fmt::internal::null<> strerror_r(int, char *, ...) {
|
||||||
|
return fmt::internal::null<>();
|
||||||
|
}
|
||||||
|
inline fmt::internal::null<> strerror_s(char *, std::size_t, ...) {
|
||||||
|
return fmt::internal::null<>();
|
||||||
|
}
|
||||||
|
|
||||||
|
FMT_BEGIN_NAMESPACE
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
#ifndef _MSC_VER
|
||||||
|
# define FMT_SNPRINTF snprintf
|
||||||
|
#else // _MSC_VER
|
||||||
|
inline int fmt_snprintf(char *buffer, size_t size, const char *format, ...) {
|
||||||
|
va_list args;
|
||||||
|
va_start(args, format);
|
||||||
|
int result = vsnprintf_s(buffer, size, _TRUNCATE, format, args);
|
||||||
|
va_end(args);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
# define FMT_SNPRINTF fmt_snprintf
|
||||||
|
#endif // _MSC_VER
|
||||||
|
|
||||||
|
#if defined(_WIN32) && defined(__MINGW32__) && !defined(__NO_ISOCEXT)
|
||||||
|
# define FMT_SWPRINTF snwprintf
|
||||||
|
#else
|
||||||
|
# define FMT_SWPRINTF swprintf
|
||||||
|
#endif // defined(_WIN32) && defined(__MINGW32__) && !defined(__NO_ISOCEXT)
|
||||||
|
|
||||||
|
typedef void (*FormatFunc)(internal::buffer &, int, string_view);
|
||||||
|
|
||||||
|
// Portable thread-safe version of strerror.
|
||||||
|
// Sets buffer to point to a string describing the error code.
|
||||||
|
// This can be either a pointer to a string stored in buffer,
|
||||||
|
// or a pointer to some static immutable string.
|
||||||
|
// Returns one of the following values:
|
||||||
|
// 0 - success
|
||||||
|
// ERANGE - buffer is not large enough to store the error message
|
||||||
|
// other - failure
|
||||||
|
// Buffer should be at least of size 1.
|
||||||
|
int safe_strerror(
|
||||||
|
int error_code, char *&buffer, std::size_t buffer_size) FMT_NOEXCEPT {
|
||||||
|
FMT_ASSERT(buffer != FMT_NULL && buffer_size != 0, "invalid buffer");
|
||||||
|
|
||||||
|
class dispatcher {
|
||||||
|
private:
|
||||||
|
int error_code_;
|
||||||
|
char *&buffer_;
|
||||||
|
std::size_t buffer_size_;
|
||||||
|
|
||||||
|
// A noop assignment operator to avoid bogus warnings.
|
||||||
|
void operator=(const dispatcher &) {}
|
||||||
|
|
||||||
|
// Handle the result of XSI-compliant version of strerror_r.
|
||||||
|
int handle(int result) {
|
||||||
|
// glibc versions before 2.13 return result in errno.
|
||||||
|
return result == -1 ? errno : result;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle the result of GNU-specific version of strerror_r.
|
||||||
|
int handle(char *message) {
|
||||||
|
// If the buffer is full then the message is probably truncated.
|
||||||
|
if (message == buffer_ && strlen(buffer_) == buffer_size_ - 1)
|
||||||
|
return ERANGE;
|
||||||
|
buffer_ = message;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle the case when strerror_r is not available.
|
||||||
|
int handle(internal::null<>) {
|
||||||
|
return fallback(strerror_s(buffer_, buffer_size_, error_code_));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fallback to strerror_s when strerror_r is not available.
|
||||||
|
int fallback(int result) {
|
||||||
|
// If the buffer is full then the message is probably truncated.
|
||||||
|
return result == 0 && strlen(buffer_) == buffer_size_ - 1 ?
|
||||||
|
ERANGE : result;
|
||||||
|
}
|
||||||
|
|
||||||
|
#if !FMT_MSC_VER
|
||||||
|
// Fallback to strerror if strerror_r and strerror_s are not available.
|
||||||
|
int fallback(internal::null<>) {
|
||||||
|
errno = 0;
|
||||||
|
buffer_ = strerror(error_code_);
|
||||||
|
return errno;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
public:
|
||||||
|
dispatcher(int err_code, char *&buf, std::size_t buf_size)
|
||||||
|
: error_code_(err_code), buffer_(buf), buffer_size_(buf_size) {}
|
||||||
|
|
||||||
|
int run() {
|
||||||
|
return handle(strerror_r(error_code_, buffer_, buffer_size_));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
return dispatcher(error_code, buffer, buffer_size).run();
|
||||||
|
}
|
||||||
|
|
||||||
|
void format_error_code(internal::buffer &out, int error_code,
|
||||||
|
string_view message) FMT_NOEXCEPT {
|
||||||
|
// Report error code making sure that the output fits into
|
||||||
|
// inline_buffer_size to avoid dynamic memory allocation and potential
|
||||||
|
// bad_alloc.
|
||||||
|
out.resize(0);
|
||||||
|
static const char SEP[] = ": ";
|
||||||
|
static const char ERROR_STR[] = "error ";
|
||||||
|
// 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;
|
||||||
|
typedef internal::int_traits<int>::main_type main_type;
|
||||||
|
main_type abs_value = static_cast<main_type>(error_code);
|
||||||
|
if (internal::is_negative(error_code)) {
|
||||||
|
abs_value = 0 - abs_value;
|
||||||
|
++error_code_size;
|
||||||
|
}
|
||||||
|
error_code_size += internal::to_unsigned(internal::count_digits(abs_value));
|
||||||
|
writer w(out);
|
||||||
|
if (message.size() <= inline_buffer_size - error_code_size) {
|
||||||
|
w.write(message);
|
||||||
|
w.write(SEP);
|
||||||
|
}
|
||||||
|
w.write(ERROR_STR);
|
||||||
|
w.write(error_code);
|
||||||
|
assert(out.size() <= inline_buffer_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
void report_error(FormatFunc func, int error_code,
|
||||||
|
string_view message) FMT_NOEXCEPT {
|
||||||
|
memory_buffer full_message;
|
||||||
|
func(full_message, error_code, message);
|
||||||
|
// Use Writer::data instead of Writer::c_str to avoid potential memory
|
||||||
|
// allocation.
|
||||||
|
std::fwrite(full_message.data(), full_message.size(), 1, stderr);
|
||||||
|
std::fputc('\n', stderr);
|
||||||
|
}
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
FMT_FUNC size_t internal::count_code_points(basic_string_view<char8_t> s) {
|
||||||
|
const char8_t *data = s.data();
|
||||||
|
size_t num_code_points = 0;
|
||||||
|
for (size_t i = 0, size = s.size(); i != size; ++i) {
|
||||||
|
if ((data[i] & 0xc0) != 0x80)
|
||||||
|
++num_code_points;
|
||||||
|
}
|
||||||
|
return num_code_points;
|
||||||
|
}
|
||||||
|
|
||||||
|
#if !defined(FMT_STATIC_THOUSANDS_SEPARATOR)
|
||||||
|
namespace internal {
|
||||||
|
|
||||||
|
template <typename Locale>
|
||||||
|
locale_ref::locale_ref(const Locale &loc) : locale_(&loc) {
|
||||||
|
static_assert(std::is_same<Locale, std::locale>::value, "");
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Locale>
|
||||||
|
Locale locale_ref::get() const {
|
||||||
|
static_assert(std::is_same<Locale, std::locale>::value, "");
|
||||||
|
return locale_ ? *static_cast<const std::locale*>(locale_) : std::locale();
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Char>
|
||||||
|
FMT_FUNC Char thousands_sep_impl(locale_ref loc) {
|
||||||
|
return std::use_facet<std::numpunct<Char> >(
|
||||||
|
loc.get<std::locale>()).thousands_sep();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
template <typename Char>
|
||||||
|
FMT_FUNC Char internal::thousands_sep_impl(locale_ref) {
|
||||||
|
return FMT_STATIC_THOUSANDS_SEPARATOR;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
FMT_FUNC void system_error::init(
|
||||||
|
int err_code, string_view format_str, format_args args) {
|
||||||
|
error_code_ = err_code;
|
||||||
|
memory_buffer buffer;
|
||||||
|
format_system_error(buffer, err_code, vformat(format_str, args));
|
||||||
|
std::runtime_error &base = *this;
|
||||||
|
base = std::runtime_error(to_string(buffer));
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace internal {
|
||||||
|
template <typename T>
|
||||||
|
int char_traits<char>::format_float(
|
||||||
|
char *buf, std::size_t size, const char *format, int precision, T value) {
|
||||||
|
return precision < 0 ?
|
||||||
|
FMT_SNPRINTF(buf, size, format, value) :
|
||||||
|
FMT_SNPRINTF(buf, size, format, precision, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
int char_traits<wchar_t>::format_float(
|
||||||
|
wchar_t *buf, std::size_t size, const wchar_t *format, int precision,
|
||||||
|
T value) {
|
||||||
|
return precision < 0 ?
|
||||||
|
FMT_SWPRINTF(buf, size, format, value) :
|
||||||
|
FMT_SWPRINTF(buf, size, format, precision, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
const char basic_data<T>::DIGITS[] =
|
||||||
|
"0001020304050607080910111213141516171819"
|
||||||
|
"2021222324252627282930313233343536373839"
|
||||||
|
"4041424344454647484950515253545556575859"
|
||||||
|
"6061626364656667686970717273747576777879"
|
||||||
|
"8081828384858687888990919293949596979899";
|
||||||
|
|
||||||
|
#define FMT_POWERS_OF_10(factor) \
|
||||||
|
factor * 10, \
|
||||||
|
factor * 100, \
|
||||||
|
factor * 1000, \
|
||||||
|
factor * 10000, \
|
||||||
|
factor * 100000, \
|
||||||
|
factor * 1000000, \
|
||||||
|
factor * 10000000, \
|
||||||
|
factor * 100000000, \
|
||||||
|
factor * 1000000000
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
const uint32_t basic_data<T>::POWERS_OF_10_32[] = {
|
||||||
|
1, FMT_POWERS_OF_10(1)
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
const uint32_t basic_data<T>::ZERO_OR_POWERS_OF_10_32[] = {
|
||||||
|
0, FMT_POWERS_OF_10(1)
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
const uint64_t basic_data<T>::ZERO_OR_POWERS_OF_10_64[] = {
|
||||||
|
0,
|
||||||
|
FMT_POWERS_OF_10(1),
|
||||||
|
FMT_POWERS_OF_10(1000000000ull),
|
||||||
|
10000000000000000000ull
|
||||||
|
};
|
||||||
|
|
||||||
|
// Normalized 64-bit significands of pow(10, k), for k = -348, -340, ..., 340.
|
||||||
|
// These are generated by support/compute-powers.py.
|
||||||
|
template <typename T>
|
||||||
|
const uint64_t basic_data<T>::POW10_SIGNIFICANDS[] = {
|
||||||
|
0xfa8fd5a0081c0288, 0xbaaee17fa23ebf76, 0x8b16fb203055ac76,
|
||||||
|
0xcf42894a5dce35ea, 0x9a6bb0aa55653b2d, 0xe61acf033d1a45df,
|
||||||
|
0xab70fe17c79ac6ca, 0xff77b1fcbebcdc4f, 0xbe5691ef416bd60c,
|
||||||
|
0x8dd01fad907ffc3c, 0xd3515c2831559a83, 0x9d71ac8fada6c9b5,
|
||||||
|
0xea9c227723ee8bcb, 0xaecc49914078536d, 0x823c12795db6ce57,
|
||||||
|
0xc21094364dfb5637, 0x9096ea6f3848984f, 0xd77485cb25823ac7,
|
||||||
|
0xa086cfcd97bf97f4, 0xef340a98172aace5, 0xb23867fb2a35b28e,
|
||||||
|
0x84c8d4dfd2c63f3b, 0xc5dd44271ad3cdba, 0x936b9fcebb25c996,
|
||||||
|
0xdbac6c247d62a584, 0xa3ab66580d5fdaf6, 0xf3e2f893dec3f126,
|
||||||
|
0xb5b5ada8aaff80b8, 0x87625f056c7c4a8b, 0xc9bcff6034c13053,
|
||||||
|
0x964e858c91ba2655, 0xdff9772470297ebd, 0xa6dfbd9fb8e5b88f,
|
||||||
|
0xf8a95fcf88747d94, 0xb94470938fa89bcf, 0x8a08f0f8bf0f156b,
|
||||||
|
0xcdb02555653131b6, 0x993fe2c6d07b7fac, 0xe45c10c42a2b3b06,
|
||||||
|
0xaa242499697392d3, 0xfd87b5f28300ca0e, 0xbce5086492111aeb,
|
||||||
|
0x8cbccc096f5088cc, 0xd1b71758e219652c, 0x9c40000000000000,
|
||||||
|
0xe8d4a51000000000, 0xad78ebc5ac620000, 0x813f3978f8940984,
|
||||||
|
0xc097ce7bc90715b3, 0x8f7e32ce7bea5c70, 0xd5d238a4abe98068,
|
||||||
|
0x9f4f2726179a2245, 0xed63a231d4c4fb27, 0xb0de65388cc8ada8,
|
||||||
|
0x83c7088e1aab65db, 0xc45d1df942711d9a, 0x924d692ca61be758,
|
||||||
|
0xda01ee641a708dea, 0xa26da3999aef774a, 0xf209787bb47d6b85,
|
||||||
|
0xb454e4a179dd1877, 0x865b86925b9bc5c2, 0xc83553c5c8965d3d,
|
||||||
|
0x952ab45cfa97a0b3, 0xde469fbd99a05fe3, 0xa59bc234db398c25,
|
||||||
|
0xf6c69a72a3989f5c, 0xb7dcbf5354e9bece, 0x88fcf317f22241e2,
|
||||||
|
0xcc20ce9bd35c78a5, 0x98165af37b2153df, 0xe2a0b5dc971f303a,
|
||||||
|
0xa8d9d1535ce3b396, 0xfb9b7cd9a4a7443c, 0xbb764c4ca7a44410,
|
||||||
|
0x8bab8eefb6409c1a, 0xd01fef10a657842c, 0x9b10a4e5e9913129,
|
||||||
|
0xe7109bfba19c0c9d, 0xac2820d9623bf429, 0x80444b5e7aa7cf85,
|
||||||
|
0xbf21e44003acdd2d, 0x8e679c2f5e44ff8f, 0xd433179d9c8cb841,
|
||||||
|
0x9e19db92b4e31ba9, 0xeb96bf6ebadf77d9, 0xaf87023b9bf0ee6b,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Binary exponents of pow(10, k), for k = -348, -340, ..., 340, corresponding
|
||||||
|
// to significands above.
|
||||||
|
template <typename T>
|
||||||
|
const int16_t basic_data<T>::POW10_EXPONENTS[] = {
|
||||||
|
-1220, -1193, -1166, -1140, -1113, -1087, -1060, -1034, -1007, -980, -954,
|
||||||
|
-927, -901, -874, -847, -821, -794, -768, -741, -715, -688, -661,
|
||||||
|
-635, -608, -582, -555, -529, -502, -475, -449, -422, -396, -369,
|
||||||
|
-343, -316, -289, -263, -236, -210, -183, -157, -130, -103, -77,
|
||||||
|
-50, -24, 3, 30, 56, 83, 109, 136, 162, 189, 216,
|
||||||
|
242, 269, 295, 322, 348, 375, 402, 428, 455, 481, 508,
|
||||||
|
534, 561, 588, 614, 641, 667, 694, 720, 747, 774, 800,
|
||||||
|
827, 853, 880, 907, 933, 960, 986, 1013, 1039, 1066
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T> const char basic_data<T>::FOREGROUND_COLOR[] = "\x1b[38;2;";
|
||||||
|
template <typename T> const char basic_data<T>::BACKGROUND_COLOR[] = "\x1b[48;2;";
|
||||||
|
template <typename T> const char basic_data<T>::RESET_COLOR[] = "\x1b[0m";
|
||||||
|
template <typename T> const wchar_t basic_data<T>::WRESET_COLOR[] = L"\x1b[0m";
|
||||||
|
|
||||||
|
// A handmade floating-point number f * pow(2, e).
|
||||||
|
class fp {
|
||||||
|
private:
|
||||||
|
typedef uint64_t significand_type;
|
||||||
|
|
||||||
|
// All sizes are in bits.
|
||||||
|
static FMT_CONSTEXPR_DECL const int char_size =
|
||||||
|
std::numeric_limits<unsigned char>::digits;
|
||||||
|
// Subtract 1 to account for an implicit most significant bit in the
|
||||||
|
// normalized form.
|
||||||
|
static FMT_CONSTEXPR_DECL const int double_significand_size =
|
||||||
|
std::numeric_limits<double>::digits - 1;
|
||||||
|
static FMT_CONSTEXPR_DECL const uint64_t implicit_bit =
|
||||||
|
1ull << double_significand_size;
|
||||||
|
|
||||||
|
public:
|
||||||
|
significand_type f;
|
||||||
|
int e;
|
||||||
|
|
||||||
|
static FMT_CONSTEXPR_DECL const int significand_size =
|
||||||
|
sizeof(significand_type) * char_size;
|
||||||
|
|
||||||
|
fp(): f(0), e(0) {}
|
||||||
|
fp(uint64_t f_val, int e_val): f(f_val), e(e_val) {}
|
||||||
|
|
||||||
|
// Constructs fp from an IEEE754 double. It is a template to prevent compile
|
||||||
|
// errors on platforms where double is not IEEE754.
|
||||||
|
template <typename Double>
|
||||||
|
explicit fp(Double d) {
|
||||||
|
// Assume double is in the format [sign][exponent][significand].
|
||||||
|
typedef std::numeric_limits<Double> limits;
|
||||||
|
const int double_size = static_cast<int>(sizeof(Double) * char_size);
|
||||||
|
const int exponent_size =
|
||||||
|
double_size - double_significand_size - 1; // -1 for sign
|
||||||
|
const uint64_t significand_mask = implicit_bit - 1;
|
||||||
|
const uint64_t exponent_mask = (~0ull >> 1) & ~significand_mask;
|
||||||
|
const int exponent_bias = (1 << exponent_size) - limits::max_exponent - 1;
|
||||||
|
auto u = bit_cast<uint64_t>(d);
|
||||||
|
auto biased_e = (u & exponent_mask) >> double_significand_size;
|
||||||
|
f = u & significand_mask;
|
||||||
|
if (biased_e != 0)
|
||||||
|
f += implicit_bit;
|
||||||
|
else
|
||||||
|
biased_e = 1; // Subnormals use biased exponent 1 (min exponent).
|
||||||
|
e = static_cast<int>(biased_e - exponent_bias - double_significand_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Normalizes the value converted from double and multiplied by (1 << SHIFT).
|
||||||
|
template <int SHIFT = 0>
|
||||||
|
void normalize() {
|
||||||
|
// Handle subnormals.
|
||||||
|
auto shifted_implicit_bit = implicit_bit << SHIFT;
|
||||||
|
while ((f & shifted_implicit_bit) == 0) {
|
||||||
|
f <<= 1;
|
||||||
|
--e;
|
||||||
|
}
|
||||||
|
// Subtract 1 to account for hidden bit.
|
||||||
|
auto offset = significand_size - double_significand_size - SHIFT - 1;
|
||||||
|
f <<= offset;
|
||||||
|
e -= offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Compute lower and upper boundaries (m^- and m^+ in the Grisu paper), where
|
||||||
|
// a boundary is a value half way between the number and its predecessor
|
||||||
|
// (lower) or successor (upper). The upper boundary is normalized and lower
|
||||||
|
// has the same exponent but may be not normalized.
|
||||||
|
void compute_boundaries(fp &lower, fp &upper) const {
|
||||||
|
lower = f == implicit_bit ?
|
||||||
|
fp((f << 2) - 1, e - 2) : fp((f << 1) - 1, e - 1);
|
||||||
|
upper = fp((f << 1) + 1, e - 1);
|
||||||
|
upper.normalize<1>(); // 1 is to account for the exponent shift above.
|
||||||
|
lower.f <<= lower.e - upper.e;
|
||||||
|
lower.e = upper.e;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Returns an fp number representing x - y. Result may not be normalized.
|
||||||
|
inline fp operator-(fp x, fp y) {
|
||||||
|
FMT_ASSERT(x.f >= y.f && x.e == y.e, "invalid operands");
|
||||||
|
return fp(x.f - y.f, x.e);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Computes an fp number r with r.f = x.f * y.f / pow(2, 64) rounded to nearest
|
||||||
|
// with half-up tie breaking, r.e = x.e + y.e + 64. Result may not be normalized.
|
||||||
|
FMT_API fp operator*(fp x, fp y);
|
||||||
|
|
||||||
|
// Returns cached power (of 10) c_k = c_k.f * pow(2, c_k.e) such that its
|
||||||
|
// (binary) exponent satisfies min_exponent <= c_k.e <= min_exponent + 3.
|
||||||
|
FMT_API fp get_cached_power(int min_exponent, int &pow10_exponent);
|
||||||
|
|
||||||
|
FMT_FUNC fp operator*(fp x, fp y) {
|
||||||
|
// Multiply 32-bit parts of significands.
|
||||||
|
uint64_t mask = (1ULL << 32) - 1;
|
||||||
|
uint64_t a = x.f >> 32, b = x.f & mask;
|
||||||
|
uint64_t c = y.f >> 32, d = y.f & mask;
|
||||||
|
uint64_t ac = a * c, bc = b * c, ad = a * d, bd = b * d;
|
||||||
|
// Compute mid 64-bit of result and round.
|
||||||
|
uint64_t mid = (bd >> 32) + (ad & mask) + (bc & mask) + (1U << 31);
|
||||||
|
return fp(ac + (ad >> 32) + (bc >> 32) + (mid >> 32), x.e + y.e + 64);
|
||||||
|
}
|
||||||
|
|
||||||
|
FMT_FUNC fp get_cached_power(int min_exponent, int &pow10_exponent) {
|
||||||
|
const double one_over_log2_10 = 0.30102999566398114; // 1 / log2(10)
|
||||||
|
int index = static_cast<int>(std::ceil(
|
||||||
|
(min_exponent + fp::significand_size - 1) * one_over_log2_10));
|
||||||
|
// Decimal exponent of the first (smallest) cached power of 10.
|
||||||
|
const int first_dec_exp = -348;
|
||||||
|
// Difference between 2 consecutive decimal exponents in cached powers of 10.
|
||||||
|
const int dec_exp_step = 8;
|
||||||
|
index = (index - first_dec_exp - 1) / dec_exp_step + 1;
|
||||||
|
pow10_exponent = first_dec_exp + index * dec_exp_step;
|
||||||
|
return fp(data::POW10_SIGNIFICANDS[index], data::POW10_EXPONENTS[index]);
|
||||||
|
}
|
||||||
|
|
||||||
|
FMT_FUNC bool grisu2_round(
|
||||||
|
char *buf, int &size, int max_digits, uint64_t delta,
|
||||||
|
uint64_t remainder, uint64_t exp, uint64_t diff, int &exp10) {
|
||||||
|
while (remainder < diff && delta - remainder >= exp &&
|
||||||
|
(remainder + exp < diff || diff - remainder > remainder + exp - diff)) {
|
||||||
|
--buf[size - 1];
|
||||||
|
remainder += exp;
|
||||||
|
}
|
||||||
|
if (size > max_digits) {
|
||||||
|
--size;
|
||||||
|
++exp10;
|
||||||
|
if (buf[size] >= '5')
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generates output using Grisu2 digit-gen algorithm.
|
||||||
|
FMT_FUNC bool grisu2_gen_digits(
|
||||||
|
char *buf, int &size, uint32_t hi, uint64_t lo, int &exp,
|
||||||
|
uint64_t delta, const fp &one, const fp &diff, int max_digits) {
|
||||||
|
// Generate digits for the most significant part (hi).
|
||||||
|
while (exp > 0) {
|
||||||
|
uint32_t digit = 0;
|
||||||
|
// This optimization by miloyip reduces the number of integer divisions by
|
||||||
|
// one per iteration.
|
||||||
|
switch (exp) {
|
||||||
|
case 10: digit = hi / 1000000000; hi %= 1000000000; break;
|
||||||
|
case 9: digit = hi / 100000000; hi %= 100000000; break;
|
||||||
|
case 8: digit = hi / 10000000; hi %= 10000000; break;
|
||||||
|
case 7: digit = hi / 1000000; hi %= 1000000; break;
|
||||||
|
case 6: digit = hi / 100000; hi %= 100000; break;
|
||||||
|
case 5: digit = hi / 10000; hi %= 10000; break;
|
||||||
|
case 4: digit = hi / 1000; hi %= 1000; break;
|
||||||
|
case 3: digit = hi / 100; hi %= 100; break;
|
||||||
|
case 2: digit = hi / 10; hi %= 10; break;
|
||||||
|
case 1: digit = hi; hi = 0; break;
|
||||||
|
default:
|
||||||
|
FMT_ASSERT(false, "invalid number of digits");
|
||||||
|
}
|
||||||
|
if (digit != 0 || size != 0)
|
||||||
|
buf[size++] = static_cast<char>('0' + digit);
|
||||||
|
--exp;
|
||||||
|
uint64_t remainder = (static_cast<uint64_t>(hi) << -one.e) + lo;
|
||||||
|
if (remainder <= delta || size > max_digits) {
|
||||||
|
return grisu2_round(
|
||||||
|
buf, size, max_digits, delta, remainder,
|
||||||
|
static_cast<uint64_t>(data::POWERS_OF_10_32[exp]) << -one.e,
|
||||||
|
diff.f, exp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Generate digits for the least significant part (lo).
|
||||||
|
for (;;) {
|
||||||
|
lo *= 10;
|
||||||
|
delta *= 10;
|
||||||
|
char digit = static_cast<char>(lo >> -one.e);
|
||||||
|
if (digit != 0 || size != 0)
|
||||||
|
buf[size++] = static_cast<char>('0' + digit);
|
||||||
|
lo &= one.f - 1;
|
||||||
|
--exp;
|
||||||
|
if (lo < delta || size > max_digits) {
|
||||||
|
return grisu2_round(buf, size, max_digits, delta, lo, one.f,
|
||||||
|
diff.f * data::POWERS_OF_10_32[-exp], exp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#if FMT_CLANG_VERSION
|
||||||
|
# define FMT_FALLTHROUGH [[clang::fallthrough]];
|
||||||
|
#elif FMT_GCC_VERSION >= 700
|
||||||
|
# define FMT_FALLTHROUGH [[gnu::fallthrough]];
|
||||||
|
#else
|
||||||
|
# define FMT_FALLTHROUGH
|
||||||
|
#endif
|
||||||
|
|
||||||
|
struct gen_digits_params {
|
||||||
|
int num_digits;
|
||||||
|
bool fixed;
|
||||||
|
bool upper;
|
||||||
|
bool trailing_zeros;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct prettify_handler {
|
||||||
|
char *data;
|
||||||
|
ptrdiff_t size;
|
||||||
|
buffer &buf;
|
||||||
|
|
||||||
|
explicit prettify_handler(buffer &b, ptrdiff_t n)
|
||||||
|
: data(b.data()), size(n), buf(b) {}
|
||||||
|
~prettify_handler() {
|
||||||
|
assert(buf.size() >= to_unsigned(size));
|
||||||
|
buf.resize(to_unsigned(size));
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename F>
|
||||||
|
void insert(ptrdiff_t pos, ptrdiff_t n, F f) {
|
||||||
|
std::memmove(data + pos + n, data + pos, to_unsigned(size - pos));
|
||||||
|
f(data + pos);
|
||||||
|
size += n;
|
||||||
|
}
|
||||||
|
|
||||||
|
void insert(ptrdiff_t pos, char c) {
|
||||||
|
std::memmove(data + pos + 1, data + pos, to_unsigned(size - pos));
|
||||||
|
data[pos] = c;
|
||||||
|
++size;
|
||||||
|
}
|
||||||
|
|
||||||
|
void append(ptrdiff_t n, char c) {
|
||||||
|
std::uninitialized_fill_n(data + size, n, c);
|
||||||
|
size += n;
|
||||||
|
}
|
||||||
|
|
||||||
|
void append(char c) { data[size++] = c; }
|
||||||
|
|
||||||
|
void remove_trailing(char c) {
|
||||||
|
while (data[size - 1] == c) --size;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Writes the exponent exp in the form "[+-]d{2,3}" to buffer.
|
||||||
|
template <typename Handler>
|
||||||
|
FMT_FUNC void write_exponent(int exp, Handler &&h) {
|
||||||
|
FMT_ASSERT(-1000 < exp && exp < 1000, "exponent out of range");
|
||||||
|
if (exp < 0) {
|
||||||
|
h.append('-');
|
||||||
|
exp = -exp;
|
||||||
|
} else {
|
||||||
|
h.append('+');
|
||||||
|
}
|
||||||
|
if (exp >= 100) {
|
||||||
|
h.append(static_cast<char>('0' + exp / 100));
|
||||||
|
exp %= 100;
|
||||||
|
const char *d = data::DIGITS + exp * 2;
|
||||||
|
h.append(d[0]);
|
||||||
|
h.append(d[1]);
|
||||||
|
} else {
|
||||||
|
const char *d = data::DIGITS + exp * 2;
|
||||||
|
h.append(d[0]);
|
||||||
|
h.append(d[1]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct fill {
|
||||||
|
size_t n;
|
||||||
|
void operator()(char *buf) const {
|
||||||
|
buf[0] = '0';
|
||||||
|
buf[1] = '.';
|
||||||
|
std::uninitialized_fill_n(buf + 2, n, '0');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// The number is given as v = f * pow(10, exp), where f has size digits.
|
||||||
|
template <typename Handler>
|
||||||
|
FMT_FUNC void grisu2_prettify(const gen_digits_params ¶ms,
|
||||||
|
int size, int exp, Handler &&handler) {
|
||||||
|
if (!params.fixed) {
|
||||||
|
// Insert a decimal point after the first digit and add an exponent.
|
||||||
|
handler.insert(1, '.');
|
||||||
|
exp += size - 1;
|
||||||
|
if (size < params.num_digits)
|
||||||
|
handler.append(params.num_digits - size, '0');
|
||||||
|
handler.append(params.upper ? 'E' : 'e');
|
||||||
|
write_exponent(exp, handler);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// pow(10, full_exp - 1) <= v <= pow(10, full_exp).
|
||||||
|
int full_exp = size + exp;
|
||||||
|
const int exp_threshold = 21;
|
||||||
|
if (size <= full_exp && full_exp <= exp_threshold) {
|
||||||
|
// 1234e7 -> 12340000000[.0+]
|
||||||
|
handler.append(full_exp - size, '0');
|
||||||
|
int num_zeros = params.num_digits - full_exp;
|
||||||
|
if (num_zeros > 0 && params.trailing_zeros) {
|
||||||
|
handler.append('.');
|
||||||
|
handler.append(num_zeros, '0');
|
||||||
|
}
|
||||||
|
} else if (full_exp > 0) {
|
||||||
|
// 1234e-2 -> 12.34[0+]
|
||||||
|
handler.insert(full_exp, '.');
|
||||||
|
if (!params.trailing_zeros) {
|
||||||
|
// Remove trailing zeros.
|
||||||
|
handler.remove_trailing('0');
|
||||||
|
} else if (params.num_digits > size) {
|
||||||
|
// Add trailing zeros.
|
||||||
|
ptrdiff_t num_zeros = params.num_digits - size;
|
||||||
|
handler.append(num_zeros, '0');
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// 1234e-6 -> 0.001234
|
||||||
|
handler.insert(0, 2 - full_exp, fill{to_unsigned(-full_exp)});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct char_counter {
|
||||||
|
ptrdiff_t size;
|
||||||
|
|
||||||
|
template <typename F>
|
||||||
|
void insert(ptrdiff_t, ptrdiff_t n, F) { size += n; }
|
||||||
|
void insert(ptrdiff_t, char) { ++size; }
|
||||||
|
void append(ptrdiff_t n, char) { size += n; }
|
||||||
|
void append(char) { ++size; }
|
||||||
|
void remove_trailing(char) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Converts format specifiers into parameters for digit generation and computes
|
||||||
|
// output buffer size for a number in the range [pow(10, exp - 1), pow(10, exp)
|
||||||
|
// or 0 if exp == 1.
|
||||||
|
FMT_FUNC gen_digits_params process_specs(const core_format_specs &specs,
|
||||||
|
int exp, buffer &buf) {
|
||||||
|
auto params = gen_digits_params();
|
||||||
|
int num_digits = specs.precision >= 0 ? specs.precision : 6;
|
||||||
|
switch (specs.type) {
|
||||||
|
case 'G':
|
||||||
|
params.upper = true;
|
||||||
|
FMT_FALLTHROUGH
|
||||||
|
case '\0': case 'g':
|
||||||
|
params.trailing_zeros = (specs.flags & HASH_FLAG) != 0;
|
||||||
|
if (-4 <= exp && exp < num_digits + 1) {
|
||||||
|
params.fixed = true;
|
||||||
|
if (!specs.type && params.trailing_zeros && exp >= 0)
|
||||||
|
num_digits = exp + 1;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'F':
|
||||||
|
params.upper = true;
|
||||||
|
FMT_FALLTHROUGH
|
||||||
|
case 'f': {
|
||||||
|
params.fixed = true;
|
||||||
|
params.trailing_zeros = true;
|
||||||
|
int adjusted_min_digits = num_digits + exp;
|
||||||
|
if (adjusted_min_digits > 0)
|
||||||
|
num_digits = adjusted_min_digits;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 'E':
|
||||||
|
params.upper = true;
|
||||||
|
FMT_FALLTHROUGH
|
||||||
|
case 'e':
|
||||||
|
++num_digits;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
params.num_digits = num_digits;
|
||||||
|
char_counter counter{num_digits};
|
||||||
|
grisu2_prettify(params, params.num_digits, exp - num_digits, counter);
|
||||||
|
buf.resize(to_unsigned(counter.size));
|
||||||
|
return params;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Double>
|
||||||
|
FMT_FUNC typename std::enable_if<sizeof(Double) == sizeof(uint64_t), bool>::type
|
||||||
|
grisu2_format(Double value, buffer &buf, core_format_specs specs) {
|
||||||
|
FMT_ASSERT(value >= 0, "value is negative");
|
||||||
|
if (value == 0) {
|
||||||
|
gen_digits_params params = process_specs(specs, 1, buf);
|
||||||
|
const size_t size = 1;
|
||||||
|
buf[0] = '0';
|
||||||
|
grisu2_prettify(params, size, 0, prettify_handler(buf, size));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
fp fp_value(value);
|
||||||
|
fp lower, upper; // w^- and w^+ in the Grisu paper.
|
||||||
|
fp_value.compute_boundaries(lower, upper);
|
||||||
|
|
||||||
|
// Find a cached power of 10 close to 1 / upper and use it to scale upper.
|
||||||
|
const int min_exp = -60; // alpha in Grisu.
|
||||||
|
int cached_exp = 0; // K in Grisu.
|
||||||
|
auto cached_pow = get_cached_power( // \tilde{c}_{-k} in Grisu.
|
||||||
|
min_exp - (upper.e + fp::significand_size), cached_exp);
|
||||||
|
cached_exp = -cached_exp;
|
||||||
|
upper = upper * cached_pow; // \tilde{M}^+ in Grisu.
|
||||||
|
--upper.f; // \tilde{M}^+ - 1 ulp -> M^+_{\downarrow}.
|
||||||
|
fp one(1ull << -upper.e, upper.e);
|
||||||
|
// hi (p1 in Grisu) contains the most significant digits of scaled_upper.
|
||||||
|
// hi = floor(upper / one).
|
||||||
|
uint32_t hi = static_cast<uint32_t>(upper.f >> -one.e);
|
||||||
|
int exp = count_digits(hi); // kappa in Grisu.
|
||||||
|
gen_digits_params params = process_specs(specs, cached_exp + exp, buf);
|
||||||
|
fp_value.normalize();
|
||||||
|
fp scaled_value = fp_value * cached_pow;
|
||||||
|
lower = lower * cached_pow; // \tilde{M}^- in Grisu.
|
||||||
|
++lower.f; // \tilde{M}^- + 1 ulp -> M^-_{\uparrow}.
|
||||||
|
uint64_t delta = upper.f - lower.f;
|
||||||
|
fp diff = upper - scaled_value; // wp_w in Grisu.
|
||||||
|
// lo (p2 in Grisu) contains the least significants digits of scaled_upper.
|
||||||
|
// lo = supper % one.
|
||||||
|
uint64_t lo = upper.f & (one.f - 1);
|
||||||
|
int size = 0;
|
||||||
|
if (!grisu2_gen_digits(buf.data(), size, hi, lo, exp, delta, one, diff,
|
||||||
|
params.num_digits)) {
|
||||||
|
buf.clear();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
grisu2_prettify(params, size, cached_exp + exp, prettify_handler(buf, size));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Double>
|
||||||
|
void sprintf_format(Double value, internal::buffer &buf,
|
||||||
|
core_format_specs spec) {
|
||||||
|
// Buffer capacity must be non-zero, otherwise MSVC's vsnprintf_s will fail.
|
||||||
|
FMT_ASSERT(buf.capacity() != 0, "empty buffer");
|
||||||
|
|
||||||
|
// Build format string.
|
||||||
|
enum { MAX_FORMAT_SIZE = 10}; // longest format: %#-*.*Lg
|
||||||
|
char format[MAX_FORMAT_SIZE];
|
||||||
|
char *format_ptr = format;
|
||||||
|
*format_ptr++ = '%';
|
||||||
|
if (spec.has(HASH_FLAG))
|
||||||
|
*format_ptr++ = '#';
|
||||||
|
if (spec.precision >= 0) {
|
||||||
|
*format_ptr++ = '.';
|
||||||
|
*format_ptr++ = '*';
|
||||||
|
}
|
||||||
|
if (std::is_same<Double, long double>::value)
|
||||||
|
*format_ptr++ = 'L';
|
||||||
|
*format_ptr++ = spec.type;
|
||||||
|
*format_ptr = '\0';
|
||||||
|
|
||||||
|
// Format using snprintf.
|
||||||
|
char *start = FMT_NULL;
|
||||||
|
for (;;) {
|
||||||
|
std::size_t buffer_size = buf.capacity();
|
||||||
|
start = &buf[0];
|
||||||
|
int result = internal::char_traits<char>::format_float(
|
||||||
|
start, buffer_size, format, spec.precision, value);
|
||||||
|
if (result >= 0) {
|
||||||
|
unsigned n = internal::to_unsigned(result);
|
||||||
|
if (n < buf.capacity()) {
|
||||||
|
buf.resize(n);
|
||||||
|
break; // The buffer is large enough - continue with formatting.
|
||||||
|
}
|
||||||
|
buf.reserve(n + 1);
|
||||||
|
} else {
|
||||||
|
// If result is negative we ask to increase the capacity by at least 1,
|
||||||
|
// but as std::vector, the buffer grows exponentially.
|
||||||
|
buf.reserve(buf.capacity() + 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} // namespace internal
|
||||||
|
|
||||||
|
#if FMT_USE_WINDOWS_H
|
||||||
|
|
||||||
|
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";
|
||||||
|
if (s.size() > INT_MAX)
|
||||||
|
FMT_THROW(windows_error(ERROR_INVALID_PARAMETER, ERROR_MSG));
|
||||||
|
int s_size = static_cast<int>(s.size());
|
||||||
|
if (s_size == 0) {
|
||||||
|
// MultiByteToWideChar does not support zero length, handle separately.
|
||||||
|
buffer_.resize(1);
|
||||||
|
buffer_[0] = 0;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int length = MultiByteToWideChar(
|
||||||
|
CP_UTF8, MB_ERR_INVALID_CHARS, s.data(), s_size, FMT_NULL, 0);
|
||||||
|
if (length == 0)
|
||||||
|
FMT_THROW(windows_error(GetLastError(), ERROR_MSG));
|
||||||
|
buffer_.resize(length + 1);
|
||||||
|
length = MultiByteToWideChar(
|
||||||
|
CP_UTF8, MB_ERR_INVALID_CHARS, s.data(), s_size, &buffer_[0], length);
|
||||||
|
if (length == 0)
|
||||||
|
FMT_THROW(windows_error(GetLastError(), ERROR_MSG));
|
||||||
|
buffer_[length] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
FMT_FUNC internal::utf16_to_utf8::utf16_to_utf8(wstring_view s) {
|
||||||
|
if (int error_code = convert(s)) {
|
||||||
|
FMT_THROW(windows_error(error_code,
|
||||||
|
"cannot convert string from UTF-16 to UTF-8"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
FMT_FUNC int internal::utf16_to_utf8::convert(wstring_view s) {
|
||||||
|
if (s.size() > INT_MAX)
|
||||||
|
return ERROR_INVALID_PARAMETER;
|
||||||
|
int s_size = static_cast<int>(s.size());
|
||||||
|
if (s_size == 0) {
|
||||||
|
// WideCharToMultiByte does not support zero length, handle separately.
|
||||||
|
buffer_.resize(1);
|
||||||
|
buffer_[0] = 0;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int length = WideCharToMultiByte(
|
||||||
|
CP_UTF8, 0, s.data(), s_size, FMT_NULL, 0, FMT_NULL, FMT_NULL);
|
||||||
|
if (length == 0)
|
||||||
|
return GetLastError();
|
||||||
|
buffer_.resize(length + 1);
|
||||||
|
length = WideCharToMultiByte(
|
||||||
|
CP_UTF8, 0, s.data(), s_size, &buffer_[0], length, FMT_NULL, FMT_NULL);
|
||||||
|
if (length == 0)
|
||||||
|
return GetLastError();
|
||||||
|
buffer_[length] = 0;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
FMT_FUNC void windows_error::init(
|
||||||
|
int err_code, string_view format_str, format_args args) {
|
||||||
|
error_code_ = err_code;
|
||||||
|
memory_buffer buffer;
|
||||||
|
internal::format_windows_error(buffer, err_code, vformat(format_str, args));
|
||||||
|
std::runtime_error &base = *this;
|
||||||
|
base = std::runtime_error(to_string(buffer));
|
||||||
|
}
|
||||||
|
|
||||||
|
FMT_FUNC void internal::format_windows_error(
|
||||||
|
internal::buffer &out, int error_code, string_view message) FMT_NOEXCEPT {
|
||||||
|
FMT_TRY {
|
||||||
|
wmemory_buffer buf;
|
||||||
|
buf.resize(inline_buffer_size);
|
||||||
|
for (;;) {
|
||||||
|
wchar_t *system_message = &buf[0];
|
||||||
|
int result = FormatMessageW(
|
||||||
|
FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
|
||||||
|
FMT_NULL, error_code, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
|
||||||
|
system_message, static_cast<uint32_t>(buf.size()), FMT_NULL);
|
||||||
|
if (result != 0) {
|
||||||
|
utf16_to_utf8 utf8_message;
|
||||||
|
if (utf8_message.convert(system_message) == ERROR_SUCCESS) {
|
||||||
|
writer w(out);
|
||||||
|
w.write(message);
|
||||||
|
w.write(": ");
|
||||||
|
w.write(utf8_message);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (GetLastError() != ERROR_INSUFFICIENT_BUFFER)
|
||||||
|
break; // Can't get error message, report error code instead.
|
||||||
|
buf.resize(buf.size() * 2);
|
||||||
|
}
|
||||||
|
} FMT_CATCH(...) {}
|
||||||
|
format_error_code(out, error_code, message);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // FMT_USE_WINDOWS_H
|
||||||
|
|
||||||
|
FMT_FUNC void format_system_error(
|
||||||
|
internal::buffer &out, int error_code, string_view message) FMT_NOEXCEPT {
|
||||||
|
FMT_TRY {
|
||||||
|
memory_buffer buf;
|
||||||
|
buf.resize(inline_buffer_size);
|
||||||
|
for (;;) {
|
||||||
|
char *system_message = &buf[0];
|
||||||
|
int result = safe_strerror(error_code, system_message, buf.size());
|
||||||
|
if (result == 0) {
|
||||||
|
writer w(out);
|
||||||
|
w.write(message);
|
||||||
|
w.write(": ");
|
||||||
|
w.write(system_message);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (result != ERANGE)
|
||||||
|
break; // Can't get error message, report error code instead.
|
||||||
|
buf.resize(buf.size() * 2);
|
||||||
|
}
|
||||||
|
} FMT_CATCH(...) {}
|
||||||
|
format_error_code(out, error_code, message);
|
||||||
|
}
|
||||||
|
|
||||||
|
FMT_FUNC void internal::error_handler::on_error(const char *message) {
|
||||||
|
FMT_THROW(format_error(message));
|
||||||
|
}
|
||||||
|
|
||||||
|
FMT_FUNC void report_system_error(
|
||||||
|
int error_code, fmt::string_view message) FMT_NOEXCEPT {
|
||||||
|
report_error(format_system_error, error_code, message);
|
||||||
|
}
|
||||||
|
|
||||||
|
#if FMT_USE_WINDOWS_H
|
||||||
|
FMT_FUNC void report_windows_error(
|
||||||
|
int error_code, fmt::string_view message) FMT_NOEXCEPT {
|
||||||
|
report_error(internal::format_windows_error, error_code, message);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
FMT_FUNC void vprint(std::FILE *f, string_view format_str, format_args args) {
|
||||||
|
memory_buffer buffer;
|
||||||
|
internal::vformat_to(buffer, format_str,
|
||||||
|
basic_format_args<buffer_context<char>::type>(args));
|
||||||
|
std::fwrite(buffer.data(), 1, buffer.size(), f);
|
||||||
|
}
|
||||||
|
|
||||||
|
FMT_FUNC void vprint(std::FILE *f, wstring_view format_str, wformat_args args) {
|
||||||
|
wmemory_buffer buffer;
|
||||||
|
internal::vformat_to(buffer, format_str, args);
|
||||||
|
std::fwrite(buffer.data(), sizeof(wchar_t), buffer.size(), f);
|
||||||
|
}
|
||||||
|
|
||||||
|
FMT_FUNC void vprint(string_view format_str, format_args args) {
|
||||||
|
vprint(stdout, format_str, args);
|
||||||
|
}
|
||||||
|
|
||||||
|
FMT_FUNC void vprint(wstring_view format_str, wformat_args args) {
|
||||||
|
vprint(stdout, format_str, args);
|
||||||
|
}
|
||||||
|
|
||||||
|
FMT_END_NAMESPACE
|
||||||
|
|
||||||
|
#ifdef _MSC_VER
|
||||||
|
# pragma warning(pop)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif // FMT_FORMAT_INL_H_
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,77 @@
|
||||||
|
// Formatting library for C++ - std::locale support
|
||||||
|
//
|
||||||
|
// Copyright (c) 2012 - present, Victor Zverovich
|
||||||
|
// All rights reserved.
|
||||||
|
//
|
||||||
|
// For the license information refer to format.h.
|
||||||
|
|
||||||
|
#ifndef FMT_LOCALE_H_
|
||||||
|
#define FMT_LOCALE_H_
|
||||||
|
|
||||||
|
#include "format.h"
|
||||||
|
#include <locale>
|
||||||
|
|
||||||
|
FMT_BEGIN_NAMESPACE
|
||||||
|
|
||||||
|
namespace internal {
|
||||||
|
template <typename Char>
|
||||||
|
typename buffer_context<Char>::type::iterator vformat_to(
|
||||||
|
const std::locale &loc, basic_buffer<Char> &buf,
|
||||||
|
basic_string_view<Char> format_str,
|
||||||
|
basic_format_args<typename buffer_context<Char>::type> args) {
|
||||||
|
typedef back_insert_range<basic_buffer<Char> > range;
|
||||||
|
return vformat_to<arg_formatter<range>>(
|
||||||
|
buf, to_string_view(format_str), args, internal::locale_ref(loc));
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Char>
|
||||||
|
std::basic_string<Char> vformat(
|
||||||
|
const std::locale &loc, basic_string_view<Char> format_str,
|
||||||
|
basic_format_args<typename buffer_context<Char>::type> args) {
|
||||||
|
basic_memory_buffer<Char> buffer;
|
||||||
|
internal::vformat_to(loc, buffer, format_str, args);
|
||||||
|
return fmt::to_string(buffer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename S, typename Char = FMT_CHAR(S)>
|
||||||
|
inline std::basic_string<Char> vformat(
|
||||||
|
const std::locale &loc, const S &format_str,
|
||||||
|
basic_format_args<typename buffer_context<Char>::type> args) {
|
||||||
|
return internal::vformat(loc, to_string_view(format_str), args);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename S, typename... Args>
|
||||||
|
inline std::basic_string<FMT_CHAR(S)> format(
|
||||||
|
const std::locale &loc, const S &format_str, const Args &... args) {
|
||||||
|
return internal::vformat(
|
||||||
|
loc, to_string_view(format_str),
|
||||||
|
*internal::checked_args<S, Args...>(format_str, args...));
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename String, typename OutputIt, typename... Args>
|
||||||
|
inline typename std::enable_if<internal::is_output_iterator<OutputIt>::value,
|
||||||
|
OutputIt>::type
|
||||||
|
vformat_to(OutputIt out, const std::locale &loc, const String &format_str,
|
||||||
|
typename format_args_t<OutputIt, FMT_CHAR(String)>::type args) {
|
||||||
|
typedef output_range<OutputIt, FMT_CHAR(String)> range;
|
||||||
|
return vformat_to<arg_formatter<range>>(
|
||||||
|
range(out), to_string_view(format_str), args, internal::locale_ref(loc));
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename OutputIt, typename S, typename... Args>
|
||||||
|
inline typename std::enable_if<
|
||||||
|
internal::is_string<S>::value &&
|
||||||
|
internal::is_output_iterator<OutputIt>::value, OutputIt>::type
|
||||||
|
format_to(OutputIt out, const std::locale &loc, const S &format_str,
|
||||||
|
const Args &... args) {
|
||||||
|
internal::check_format_string<Args...>(format_str);
|
||||||
|
typedef typename format_context_t<OutputIt, FMT_CHAR(S)>::type context;
|
||||||
|
format_arg_store<context, Args...> as{args...};
|
||||||
|
return vformat_to(out, loc, to_string_view(format_str),
|
||||||
|
basic_format_args<context>(as));
|
||||||
|
}
|
||||||
|
|
||||||
|
FMT_END_NAMESPACE
|
||||||
|
|
||||||
|
#endif // FMT_LOCALE_H_
|
|
@ -0,0 +1,153 @@
|
||||||
|
// Formatting library for C++ - std::ostream support
|
||||||
|
//
|
||||||
|
// Copyright (c) 2012 - present, 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>
|
||||||
|
|
||||||
|
FMT_BEGIN_NAMESPACE
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename Char>
|
||||||
|
struct test_stream : std::basic_ostream<Char> {
|
||||||
|
private:
|
||||||
|
struct null;
|
||||||
|
// Hide all operator<< from std::basic_ostream<Char>.
|
||||||
|
void operator<<(null);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Checks if T has a user-defined operator<< (e.g. not a member of std::ostream).
|
||||||
|
template <typename T, typename Char>
|
||||||
|
class is_streamable {
|
||||||
|
private:
|
||||||
|
template <typename U>
|
||||||
|
static decltype(
|
||||||
|
internal::declval<test_stream<Char>&>()
|
||||||
|
<< internal::declval<U>(), std::true_type()) test(int);
|
||||||
|
|
||||||
|
template <typename>
|
||||||
|
static std::false_type test(...);
|
||||||
|
|
||||||
|
typedef decltype(test<T>(0)) result;
|
||||||
|
|
||||||
|
public:
|
||||||
|
static const bool value = result::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());
|
||||||
|
}
|
||||||
|
} // namespace internal
|
||||||
|
|
||||||
|
// Disable conversion to int if T has an overloaded operator<< which is a free
|
||||||
|
// function (not a member of std::ostream).
|
||||||
|
template <typename T, typename Char>
|
||||||
|
struct convert_to_int<T, Char, void> {
|
||||||
|
static const bool value =
|
||||||
|
convert_to_int<T, Char, int>::value &&
|
||||||
|
!internal::is_streamable<T, Char>::value;
|
||||||
|
};
|
||||||
|
|
||||||
|
// 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::is_streamable<T, Char>::value &&
|
||||||
|
!internal::format_type<
|
||||||
|
typename buffer_context<Char>::type, T>::value>::type>
|
||||||
|
: formatter<basic_string_view<Char>, Char> {
|
||||||
|
|
||||||
|
template <typename Context>
|
||||||
|
auto format(const T &value, Context &ctx) -> decltype(ctx.out()) {
|
||||||
|
basic_memory_buffer<Char> buffer;
|
||||||
|
internal::format_value(buffer, value);
|
||||||
|
basic_string_view<Char> str(buffer.data(), buffer.size());
|
||||||
|
return formatter<basic_string_view<Char>, Char>::format(str, ctx);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename Char>
|
||||||
|
inline void vprint(std::basic_ostream<Char> &os,
|
||||||
|
basic_string_view<Char> format_str,
|
||||||
|
basic_format_args<typename buffer_context<Char>::type> args) {
|
||||||
|
basic_memory_buffer<Char> buffer;
|
||||||
|
internal::vformat_to(buffer, format_str, args);
|
||||||
|
internal::write(os, buffer);
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
\rst
|
||||||
|
Prints formatted data to the stream *os*.
|
||||||
|
|
||||||
|
**Example**::
|
||||||
|
|
||||||
|
fmt::print(cerr, "Don't {}!", "panic");
|
||||||
|
\endrst
|
||||||
|
*/
|
||||||
|
template <typename S, typename... Args>
|
||||||
|
inline typename std::enable_if<internal::is_string<S>::value>::type
|
||||||
|
print(std::basic_ostream<FMT_CHAR(S)> &os, const S &format_str,
|
||||||
|
const Args & ... args) {
|
||||||
|
internal::checked_args<S, Args...> ca(format_str, args...);
|
||||||
|
vprint(os, to_string_view(format_str), *ca);
|
||||||
|
}
|
||||||
|
FMT_END_NAMESPACE
|
||||||
|
|
||||||
|
#endif // FMT_OSTREAM_H_
|
|
@ -0,0 +1,324 @@
|
||||||
|
// A C++ interface to POSIX functions.
|
||||||
|
//
|
||||||
|
// Copyright (c) 2012 - 2016, Victor Zverovich
|
||||||
|
// All rights reserved.
|
||||||
|
//
|
||||||
|
// For the license information refer to format.h.
|
||||||
|
|
||||||
|
#ifndef FMT_POSIX_H_
|
||||||
|
#define FMT_POSIX_H_
|
||||||
|
|
||||||
|
#if defined(__MINGW32__) || defined(__CYGWIN__)
|
||||||
|
// Workaround MinGW bug https://sourceforge.net/p/mingw/bugs/2024/.
|
||||||
|
# undef __STRICT_ANSI__
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <errno.h>
|
||||||
|
#include <fcntl.h> // for O_RDONLY
|
||||||
|
#include <locale.h> // for locale_t
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h> // for strtod_l
|
||||||
|
|
||||||
|
#include <cstddef>
|
||||||
|
|
||||||
|
#if defined __APPLE__ || defined(__FreeBSD__)
|
||||||
|
# include <xlocale.h> // for LC_NUMERIC_MASK on OS X
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "format.h"
|
||||||
|
|
||||||
|
#ifndef FMT_POSIX
|
||||||
|
# if defined(_WIN32) && !defined(__MINGW32__)
|
||||||
|
// Fix warnings about deprecated symbols.
|
||||||
|
# define FMT_POSIX(call) _##call
|
||||||
|
# else
|
||||||
|
# define FMT_POSIX(call) call
|
||||||
|
# endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Calls to system functions are wrapped in FMT_SYSTEM for testability.
|
||||||
|
#ifdef FMT_SYSTEM
|
||||||
|
# define FMT_POSIX_CALL(call) FMT_SYSTEM(call)
|
||||||
|
#else
|
||||||
|
# define FMT_SYSTEM(call) call
|
||||||
|
# ifdef _WIN32
|
||||||
|
// Fix warnings about deprecated symbols.
|
||||||
|
# define FMT_POSIX_CALL(call) ::_##call
|
||||||
|
# else
|
||||||
|
# define FMT_POSIX_CALL(call) ::call
|
||||||
|
# endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Retries the expression while it evaluates to error_result and errno
|
||||||
|
// equals to EINTR.
|
||||||
|
#ifndef _WIN32
|
||||||
|
# define FMT_RETRY_VAL(result, expression, error_result) \
|
||||||
|
do { \
|
||||||
|
result = (expression); \
|
||||||
|
} while (result == error_result && errno == EINTR)
|
||||||
|
#else
|
||||||
|
# define FMT_RETRY_VAL(result, expression, error_result) result = (expression)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define FMT_RETRY(result, expression) FMT_RETRY_VAL(result, expression, -1)
|
||||||
|
|
||||||
|
FMT_BEGIN_NAMESPACE
|
||||||
|
|
||||||
|
/**
|
||||||
|
\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.
|
||||||
|
class error_code {
|
||||||
|
private:
|
||||||
|
int value_;
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit error_code(int value = 0) FMT_NOEXCEPT : value_(value) {}
|
||||||
|
|
||||||
|
int get() const FMT_NOEXCEPT { return value_; }
|
||||||
|
};
|
||||||
|
|
||||||
|
// A buffered file.
|
||||||
|
class buffered_file {
|
||||||
|
private:
|
||||||
|
FILE *file_;
|
||||||
|
|
||||||
|
friend class file;
|
||||||
|
|
||||||
|
explicit buffered_file(FILE *f) : file_(f) {}
|
||||||
|
|
||||||
|
public:
|
||||||
|
// Constructs a buffered_file object which doesn't represent any file.
|
||||||
|
buffered_file() FMT_NOEXCEPT : file_(FMT_NULL) {}
|
||||||
|
|
||||||
|
// Destroys the object closing the file it represents if any.
|
||||||
|
FMT_API ~buffered_file() FMT_NOEXCEPT;
|
||||||
|
|
||||||
|
private:
|
||||||
|
buffered_file(const buffered_file &) = delete;
|
||||||
|
void operator=(const buffered_file &) = delete;
|
||||||
|
|
||||||
|
|
||||||
|
public:
|
||||||
|
buffered_file(buffered_file &&other) FMT_NOEXCEPT : file_(other.file_) {
|
||||||
|
other.file_ = FMT_NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
buffered_file& operator=(buffered_file &&other) {
|
||||||
|
close();
|
||||||
|
file_ = other.file_;
|
||||||
|
other.file_ = FMT_NULL;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Opens a file.
|
||||||
|
FMT_API buffered_file(cstring_view filename, cstring_view mode);
|
||||||
|
|
||||||
|
// Closes the file.
|
||||||
|
FMT_API void close();
|
||||||
|
|
||||||
|
// Returns the pointer to a FILE object representing this file.
|
||||||
|
FILE *get() const FMT_NOEXCEPT { return file_; }
|
||||||
|
|
||||||
|
// We place parentheses around fileno to workaround a bug in some versions
|
||||||
|
// of MinGW that define fileno as a macro.
|
||||||
|
FMT_API int (fileno)() const;
|
||||||
|
|
||||||
|
void vprint(string_view format_str, format_args args) {
|
||||||
|
fmt::vprint(file_, format_str, args);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename... Args>
|
||||||
|
inline void print(string_view format_str, const Args & ... args) {
|
||||||
|
vprint(format_str, make_format_args(args...));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// A file. Closed file is represented by a file object with descriptor -1.
|
||||||
|
// Methods that are not declared with FMT_NOEXCEPT may throw
|
||||||
|
// 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
|
||||||
|
// than an exception. You can get standard behavior by overriding the
|
||||||
|
// invalid parameter handler with _set_invalid_parameter_handler.
|
||||||
|
class file {
|
||||||
|
private:
|
||||||
|
int fd_; // File descriptor.
|
||||||
|
|
||||||
|
// Constructs a file object with a given descriptor.
|
||||||
|
explicit file(int fd) : fd_(fd) {}
|
||||||
|
|
||||||
|
public:
|
||||||
|
// Possible values for the oflag argument to the constructor.
|
||||||
|
enum {
|
||||||
|
RDONLY = FMT_POSIX(O_RDONLY), // Open for reading only.
|
||||||
|
WRONLY = FMT_POSIX(O_WRONLY), // Open for writing only.
|
||||||
|
RDWR = FMT_POSIX(O_RDWR) // Open for reading and writing.
|
||||||
|
};
|
||||||
|
|
||||||
|
// Constructs a file object which doesn't represent any file.
|
||||||
|
file() FMT_NOEXCEPT : fd_(-1) {}
|
||||||
|
|
||||||
|
// Opens a file and constructs a file object representing this file.
|
||||||
|
FMT_API file(cstring_view path, int oflag);
|
||||||
|
|
||||||
|
private:
|
||||||
|
file(const file &) = delete;
|
||||||
|
void operator=(const file &) = delete;
|
||||||
|
|
||||||
|
public:
|
||||||
|
file(file &&other) FMT_NOEXCEPT : fd_(other.fd_) {
|
||||||
|
other.fd_ = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
file& operator=(file &&other) {
|
||||||
|
close();
|
||||||
|
fd_ = other.fd_;
|
||||||
|
other.fd_ = -1;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Destroys the object closing the file it represents if any.
|
||||||
|
FMT_API ~file() FMT_NOEXCEPT;
|
||||||
|
|
||||||
|
// Returns the file descriptor.
|
||||||
|
int descriptor() const FMT_NOEXCEPT { return fd_; }
|
||||||
|
|
||||||
|
// Closes the file.
|
||||||
|
FMT_API void close();
|
||||||
|
|
||||||
|
// Returns the file size. The size has signed type for consistency with
|
||||||
|
// stat::st_size.
|
||||||
|
FMT_API long long size() const;
|
||||||
|
|
||||||
|
// Attempts to read count bytes from the file into the specified buffer.
|
||||||
|
FMT_API std::size_t read(void *buffer, std::size_t count);
|
||||||
|
|
||||||
|
// Attempts to write count bytes from the specified buffer to the file.
|
||||||
|
FMT_API std::size_t write(const void *buffer, std::size_t count);
|
||||||
|
|
||||||
|
// Duplicates a file descriptor with the dup function and returns
|
||||||
|
// the duplicate as a file object.
|
||||||
|
FMT_API static file dup(int fd);
|
||||||
|
|
||||||
|
// Makes fd be the copy of this file descriptor, closing fd first if
|
||||||
|
// necessary.
|
||||||
|
FMT_API void dup2(int fd);
|
||||||
|
|
||||||
|
// Makes fd be the copy of this file descriptor, closing fd first if
|
||||||
|
// necessary.
|
||||||
|
FMT_API void dup2(int fd, error_code &ec) FMT_NOEXCEPT;
|
||||||
|
|
||||||
|
// Creates a pipe setting up read_end and write_end file objects for reading
|
||||||
|
// and writing respectively.
|
||||||
|
FMT_API static void pipe(file &read_end, file &write_end);
|
||||||
|
|
||||||
|
// Creates a buffered_file object associated with this file and detaches
|
||||||
|
// this file object from the file.
|
||||||
|
FMT_API buffered_file fdopen(const char *mode);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Returns the memory page size.
|
||||||
|
long getpagesize();
|
||||||
|
|
||||||
|
#if (defined(LC_NUMERIC_MASK) || defined(_MSC_VER)) && \
|
||||||
|
!defined(__ANDROID__) && !defined(__CYGWIN__) && !defined(__OpenBSD__) && \
|
||||||
|
!defined(__NEWLIB_H__)
|
||||||
|
# define FMT_LOCALE
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef FMT_LOCALE
|
||||||
|
// A "C" numeric locale.
|
||||||
|
class Locale {
|
||||||
|
private:
|
||||||
|
# ifdef _MSC_VER
|
||||||
|
typedef _locale_t locale_t;
|
||||||
|
|
||||||
|
enum { LC_NUMERIC_MASK = LC_NUMERIC };
|
||||||
|
|
||||||
|
static locale_t newlocale(int category_mask, const char *locale, locale_t) {
|
||||||
|
return _create_locale(category_mask, locale);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void freelocale(locale_t locale) {
|
||||||
|
_free_locale(locale);
|
||||||
|
}
|
||||||
|
|
||||||
|
static double strtod_l(const char *nptr, char **endptr, _locale_t locale) {
|
||||||
|
return _strtod_l(nptr, endptr, locale);
|
||||||
|
}
|
||||||
|
# endif
|
||||||
|
|
||||||
|
locale_t locale_;
|
||||||
|
|
||||||
|
Locale(const Locale &) = delete;
|
||||||
|
void operator=(const Locale &) = delete;
|
||||||
|
|
||||||
|
public:
|
||||||
|
typedef locale_t Type;
|
||||||
|
|
||||||
|
Locale() : locale_(newlocale(LC_NUMERIC_MASK, "C", FMT_NULL)) {
|
||||||
|
if (!locale_)
|
||||||
|
FMT_THROW(system_error(errno, "cannot create locale"));
|
||||||
|
}
|
||||||
|
~Locale() { freelocale(locale_); }
|
||||||
|
|
||||||
|
Type get() const { return locale_; }
|
||||||
|
|
||||||
|
// Converts string to floating-point number and advances str past the end
|
||||||
|
// of the parsed input.
|
||||||
|
double strtod(const char *&str) const {
|
||||||
|
char *end = FMT_NULL;
|
||||||
|
double result = strtod_l(str, &end, locale_);
|
||||||
|
str = end;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
#endif // FMT_LOCALE
|
||||||
|
FMT_END_NAMESPACE
|
||||||
|
|
||||||
|
#endif // FMT_POSIX_H_
|
|
@ -0,0 +1,855 @@
|
||||||
|
// 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"
|
||||||
|
|
||||||
|
FMT_BEGIN_NAMESPACE
|
||||||
|
namespace internal {
|
||||||
|
|
||||||
|
// An iterator that produces a null terminator on *end. This simplifies parsing
|
||||||
|
// and allows comparing the performance of processing a null-terminated string
|
||||||
|
// vs string_view.
|
||||||
|
template <typename Char>
|
||||||
|
class null_terminating_iterator {
|
||||||
|
public:
|
||||||
|
typedef std::ptrdiff_t difference_type;
|
||||||
|
typedef Char value_type;
|
||||||
|
typedef const Char* pointer;
|
||||||
|
typedef const Char& reference;
|
||||||
|
typedef std::random_access_iterator_tag iterator_category;
|
||||||
|
|
||||||
|
null_terminating_iterator() : ptr_(0), end_(0) {}
|
||||||
|
|
||||||
|
FMT_CONSTEXPR null_terminating_iterator(const Char *ptr, const Char *end)
|
||||||
|
: ptr_(ptr), end_(end) {}
|
||||||
|
|
||||||
|
template <typename Range>
|
||||||
|
FMT_CONSTEXPR explicit null_terminating_iterator(const Range &r)
|
||||||
|
: ptr_(r.begin()), end_(r.end()) {}
|
||||||
|
|
||||||
|
FMT_CONSTEXPR null_terminating_iterator &operator=(const Char *ptr) {
|
||||||
|
assert(ptr <= end_);
|
||||||
|
ptr_ = ptr;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
FMT_CONSTEXPR Char operator*() const {
|
||||||
|
return ptr_ != end_ ? *ptr_ : Char();
|
||||||
|
}
|
||||||
|
|
||||||
|
FMT_CONSTEXPR null_terminating_iterator operator++() {
|
||||||
|
++ptr_;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
FMT_CONSTEXPR null_terminating_iterator operator++(int) {
|
||||||
|
null_terminating_iterator result(*this);
|
||||||
|
++ptr_;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
FMT_CONSTEXPR null_terminating_iterator operator--() {
|
||||||
|
--ptr_;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
FMT_CONSTEXPR null_terminating_iterator operator+(difference_type n) {
|
||||||
|
return null_terminating_iterator(ptr_ + n, end_);
|
||||||
|
}
|
||||||
|
|
||||||
|
FMT_CONSTEXPR null_terminating_iterator operator-(difference_type n) {
|
||||||
|
return null_terminating_iterator(ptr_ - n, end_);
|
||||||
|
}
|
||||||
|
|
||||||
|
FMT_CONSTEXPR null_terminating_iterator operator+=(difference_type n) {
|
||||||
|
ptr_ += n;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
FMT_CONSTEXPR difference_type operator-(
|
||||||
|
null_terminating_iterator other) const {
|
||||||
|
return ptr_ - other.ptr_;
|
||||||
|
}
|
||||||
|
|
||||||
|
FMT_CONSTEXPR bool operator!=(null_terminating_iterator other) const {
|
||||||
|
return ptr_ != other.ptr_;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator>=(null_terminating_iterator other) const {
|
||||||
|
return ptr_ >= other.ptr_;
|
||||||
|
}
|
||||||
|
|
||||||
|
// This should be a friend specialization pointer_from<Char> but the latter
|
||||||
|
// doesn't compile by gcc 5.1 due to a compiler bug.
|
||||||
|
template <typename CharT>
|
||||||
|
friend FMT_CONSTEXPR_DECL const CharT *pointer_from(
|
||||||
|
null_terminating_iterator<CharT> it);
|
||||||
|
|
||||||
|
private:
|
||||||
|
const Char *ptr_;
|
||||||
|
const Char *end_;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
FMT_CONSTEXPR const T *pointer_from(const T *p) { return p; }
|
||||||
|
|
||||||
|
template <typename Char>
|
||||||
|
FMT_CONSTEXPR const Char *pointer_from(null_terminating_iterator<Char> it) {
|
||||||
|
return it.ptr_;
|
||||||
|
}
|
||||||
|
|
||||||
|
// DEPRECATED: Parses the input as an unsigned integer. This function assumes
|
||||||
|
// that the first character is a digit and presence of a non-digit character at
|
||||||
|
// the end.
|
||||||
|
// it: an iterator pointing to the beginning of the input range.
|
||||||
|
template <typename Iterator, typename ErrorHandler>
|
||||||
|
FMT_CONSTEXPR unsigned parse_nonnegative_int(Iterator &it, ErrorHandler &&eh) {
|
||||||
|
assert('0' <= *it && *it <= '9');
|
||||||
|
if (*it == '0') {
|
||||||
|
++it;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
unsigned value = 0;
|
||||||
|
// Convert to unsigned to prevent a warning.
|
||||||
|
unsigned max_int = (std::numeric_limits<int>::max)();
|
||||||
|
unsigned big = max_int / 10;
|
||||||
|
do {
|
||||||
|
// Check for overflow.
|
||||||
|
if (value > big) {
|
||||||
|
value = max_int + 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
value = value * 10 + unsigned(*it - '0');
|
||||||
|
// Workaround for MSVC "setup_exception stack overflow" error:
|
||||||
|
auto next = it;
|
||||||
|
++next;
|
||||||
|
it = next;
|
||||||
|
} while ('0' <= *it && *it <= '9');
|
||||||
|
if (value > max_int)
|
||||||
|
eh.on_error("number is too big");
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Checks if a value fits in int - used to avoid warnings about comparing
|
||||||
|
// signed and unsigned integers.
|
||||||
|
template <bool IsSigned>
|
||||||
|
struct int_checker {
|
||||||
|
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 int_checker<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 printf_precision_handler: public function<int> {
|
||||||
|
public:
|
||||||
|
template <typename T>
|
||||||
|
typename std::enable_if<std::is_integral<T>::value, int>::type
|
||||||
|
operator()(T value) {
|
||||||
|
if (!int_checker<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 is_zero_int: public function<bool> {
|
||||||
|
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> {
|
||||||
|
typedef bool type;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T, typename Context>
|
||||||
|
class arg_converter: public function<void> {
|
||||||
|
private:
|
||||||
|
typedef typename Context::char_type Char;
|
||||||
|
|
||||||
|
basic_format_arg<Context> &arg_;
|
||||||
|
typename Context::char_type type_;
|
||||||
|
|
||||||
|
public:
|
||||||
|
arg_converter(basic_format_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_format_arg<Context> &arg, Char type) {
|
||||||
|
visit_format_arg(arg_converter<T, Context>(arg, type), arg);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Converts an integer argument to char for printf.
|
||||||
|
template <typename Context>
|
||||||
|
class char_converter: public function<void> {
|
||||||
|
private:
|
||||||
|
basic_format_arg<Context> &arg_;
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit char_converter(basic_format_arg<Context> &arg) : arg_(arg) {}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
typename std::enable_if<std::is_integral<T>::value>::type
|
||||||
|
operator()(T value) {
|
||||||
|
typedef typename Context::char_type Char;
|
||||||
|
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 printf_width_handler: public function<unsigned> {
|
||||||
|
private:
|
||||||
|
typedef basic_format_specs<Char> format_specs;
|
||||||
|
|
||||||
|
format_specs &spec_;
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit printf_width_handler(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;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename Char, typename Context>
|
||||||
|
void printf(basic_buffer<Char> &buf, basic_string_view<Char> format,
|
||||||
|
basic_format_args<Context> args) {
|
||||||
|
Context(std::back_inserter(buf), format, args).format();
|
||||||
|
}
|
||||||
|
} // namespace internal
|
||||||
|
|
||||||
|
using internal::printf; // For printing into memory_buffer.
|
||||||
|
|
||||||
|
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::function<
|
||||||
|
typename internal::arg_formatter_base<Range>::iterator>,
|
||||||
|
public internal::arg_formatter_base<Range> {
|
||||||
|
private:
|
||||||
|
typedef typename Range::value_type char_type;
|
||||||
|
typedef decltype(internal::declval<Range>().begin()) iterator;
|
||||||
|
typedef internal::arg_formatter_base<Range> base;
|
||||||
|
typedef basic_printf_context<iterator, char_type> context_type;
|
||||||
|
|
||||||
|
context_type &context_;
|
||||||
|
|
||||||
|
void write_null_pointer(char) {
|
||||||
|
this->spec()->type = 0;
|
||||||
|
this->write("(nil)");
|
||||||
|
}
|
||||||
|
|
||||||
|
void write_null_pointer(wchar_t) {
|
||||||
|
this->spec()->type = 0;
|
||||||
|
this->write(L"(nil)");
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
typedef typename base::format_specs 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,
|
||||||
|
ctx.locale()),
|
||||||
|
context_(ctx) {}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
typename std::enable_if<std::is_integral<T>::value, iterator>::type
|
||||||
|
operator()(T value) {
|
||||||
|
// MSVC2013 fails to compile separate overloads for bool and char_type so
|
||||||
|
// use std::is_same instead.
|
||||||
|
if (std::is_same<T, bool>::value) {
|
||||||
|
format_specs &fmt_spec = *this->spec();
|
||||||
|
if (fmt_spec.type != 's')
|
||||||
|
return base::operator()(value ? 1 : 0);
|
||||||
|
fmt_spec.type = 0;
|
||||||
|
this->write(value != 0);
|
||||||
|
} else if (std::is_same<T, 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;
|
||||||
|
return base::operator()(value);
|
||||||
|
} else {
|
||||||
|
return base::operator()(value);
|
||||||
|
}
|
||||||
|
return this->out();
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
typename std::enable_if<std::is_floating_point<T>::value, iterator>::type
|
||||||
|
operator()(T value) {
|
||||||
|
return base::operator()(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Formats a null-terminated C string. */
|
||||||
|
iterator operator()(const char *value) {
|
||||||
|
if (value)
|
||||||
|
base::operator()(value);
|
||||||
|
else if (this->spec()->type == 'p')
|
||||||
|
write_null_pointer(char_type());
|
||||||
|
else
|
||||||
|
this->write("(null)");
|
||||||
|
return this->out();
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Formats a null-terminated wide C string. */
|
||||||
|
iterator operator()(const wchar_t *value) {
|
||||||
|
if (value)
|
||||||
|
base::operator()(value);
|
||||||
|
else if (this->spec()->type == 'p')
|
||||||
|
write_null_pointer(char_type());
|
||||||
|
else
|
||||||
|
this->write(L"(null)");
|
||||||
|
return this->out();
|
||||||
|
}
|
||||||
|
|
||||||
|
iterator operator()(basic_string_view<char_type> value) {
|
||||||
|
return base::operator()(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
iterator operator()(monostate value) {
|
||||||
|
return base::operator()(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Formats a pointer. */
|
||||||
|
iterator operator()(const void *value) {
|
||||||
|
if (value)
|
||||||
|
return base::operator()(value);
|
||||||
|
this->spec()->type = 0;
|
||||||
|
write_null_pointer(char_type());
|
||||||
|
return this->out();
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Formats an argument of a custom (user-defined) type. */
|
||||||
|
iterator operator()(typename basic_format_arg<context_type>::handle handle) {
|
||||||
|
handle.format(context_);
|
||||||
|
return this->out();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
struct printf_formatter {
|
||||||
|
template <typename ParseContext>
|
||||||
|
auto parse(ParseContext &ctx) -> decltype(ctx.begin()) { return ctx.begin(); }
|
||||||
|
|
||||||
|
template <typename FormatContext>
|
||||||
|
auto format(const T &value, FormatContext &ctx) -> decltype(ctx.out()) {
|
||||||
|
internal::format_value(internal::get_container(ctx.out()), value);
|
||||||
|
return ctx.out();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/** This template formats data and writes the output to a writer. */
|
||||||
|
template <typename OutputIt, typename Char, typename ArgFormatter>
|
||||||
|
class basic_printf_context :
|
||||||
|
// Inherit publicly as a workaround for the icc bug
|
||||||
|
// https://software.intel.com/en-us/forums/intel-c-compiler/topic/783476.
|
||||||
|
public internal::context_base<
|
||||||
|
OutputIt, basic_printf_context<OutputIt, Char, ArgFormatter>, Char> {
|
||||||
|
public:
|
||||||
|
/** The character type for the output. */
|
||||||
|
typedef Char char_type;
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
struct formatter_type { typedef printf_formatter<T> type; };
|
||||||
|
|
||||||
|
private:
|
||||||
|
typedef internal::context_base<OutputIt, basic_printf_context, Char> base;
|
||||||
|
typedef typename base::format_arg format_arg;
|
||||||
|
typedef basic_format_specs<char_type> format_specs;
|
||||||
|
typedef internal::null_terminating_iterator<char_type> iterator;
|
||||||
|
|
||||||
|
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::out;
|
||||||
|
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_format_arg(
|
||||||
|
internal::printf_width_handler<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->out());
|
||||||
|
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_format_arg(internal::printf_precision_handler(), get_arg(it));
|
||||||
|
} else {
|
||||||
|
spec.precision = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
format_arg arg = get_arg(it, arg_index);
|
||||||
|
if (spec.has(HASH_FLAG) && visit_format_arg(internal::is_zero_int(), arg))
|
||||||
|
spec.flags = static_cast<uint_least8_t>(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 better?
|
||||||
|
visit_format_arg(
|
||||||
|
internal::char_converter<basic_printf_context>(arg), arg);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
start = it;
|
||||||
|
|
||||||
|
// Format argument.
|
||||||
|
visit_format_arg(AF(buffer, spec, *this), arg);
|
||||||
|
}
|
||||||
|
buffer.append(pointer_from(start), pointer_from(it));
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Buffer>
|
||||||
|
struct basic_printf_context_t {
|
||||||
|
typedef basic_printf_context<
|
||||||
|
std::back_insert_iterator<Buffer>, typename Buffer::value_type> type;
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef basic_printf_context_t<internal::buffer>::type printf_context;
|
||||||
|
typedef basic_printf_context_t<internal::wbuffer>::type wprintf_context;
|
||||||
|
|
||||||
|
typedef basic_format_args<printf_context> printf_args;
|
||||||
|
typedef basic_format_args<wprintf_context> wprintf_args;
|
||||||
|
|
||||||
|
/**
|
||||||
|
\rst
|
||||||
|
Constructs an `~fmt::format_arg_store` object that contains references to
|
||||||
|
arguments and can be implicitly converted to `~fmt::printf_args`.
|
||||||
|
\endrst
|
||||||
|
*/
|
||||||
|
template<typename... Args>
|
||||||
|
inline format_arg_store<printf_context, Args...>
|
||||||
|
make_printf_args(const Args &... args) { return {args...}; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
\rst
|
||||||
|
Constructs an `~fmt::format_arg_store` object that contains references to
|
||||||
|
arguments and can be implicitly converted to `~fmt::wprintf_args`.
|
||||||
|
\endrst
|
||||||
|
*/
|
||||||
|
template<typename... Args>
|
||||||
|
inline format_arg_store<wprintf_context, Args...>
|
||||||
|
make_wprintf_args(const Args &... args) { return {args...}; }
|
||||||
|
|
||||||
|
template <typename S, typename Char = FMT_CHAR(S)>
|
||||||
|
inline std::basic_string<Char>
|
||||||
|
vsprintf(const S &format,
|
||||||
|
basic_format_args<typename basic_printf_context_t<
|
||||||
|
internal::basic_buffer<Char>>::type> args) {
|
||||||
|
basic_memory_buffer<Char> buffer;
|
||||||
|
printf(buffer, to_string_view(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 S, typename... Args>
|
||||||
|
inline FMT_ENABLE_IF_T(
|
||||||
|
internal::is_string<S>::value, std::basic_string<FMT_CHAR(S)>)
|
||||||
|
sprintf(const S &format, const Args & ... args) {
|
||||||
|
internal::check_format_string<Args...>(format);
|
||||||
|
typedef internal::basic_buffer<FMT_CHAR(S)> buffer;
|
||||||
|
typedef typename basic_printf_context_t<buffer>::type context;
|
||||||
|
format_arg_store<context, Args...> as{ args... };
|
||||||
|
return vsprintf(to_string_view(format),
|
||||||
|
basic_format_args<context>(as));
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename S, typename Char = FMT_CHAR(S)>
|
||||||
|
inline int vfprintf(std::FILE *f, const S &format,
|
||||||
|
basic_format_args<typename basic_printf_context_t<
|
||||||
|
internal::basic_buffer<Char>>::type> args) {
|
||||||
|
basic_memory_buffer<Char> buffer;
|
||||||
|
printf(buffer, to_string_view(format), args);
|
||||||
|
std::size_t size = buffer.size();
|
||||||
|
return std::fwrite(
|
||||||
|
buffer.data(), sizeof(Char), 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 S, typename... Args>
|
||||||
|
inline FMT_ENABLE_IF_T(internal::is_string<S>::value, int)
|
||||||
|
fprintf(std::FILE *f, const S &format, const Args & ... args) {
|
||||||
|
internal::check_format_string<Args...>(format);
|
||||||
|
typedef internal::basic_buffer<FMT_CHAR(S)> buffer;
|
||||||
|
typedef typename basic_printf_context_t<buffer>::type context;
|
||||||
|
format_arg_store<context, Args...> as{ args... };
|
||||||
|
return vfprintf(f, to_string_view(format),
|
||||||
|
basic_format_args<context>(as));
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename S, typename Char = FMT_CHAR(S)>
|
||||||
|
inline int vprintf(const S &format,
|
||||||
|
basic_format_args<typename basic_printf_context_t<
|
||||||
|
internal::basic_buffer<Char>>::type> args) {
|
||||||
|
return vfprintf(stdout, to_string_view(format), args);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
\rst
|
||||||
|
Prints formatted data to ``stdout``.
|
||||||
|
|
||||||
|
**Example**::
|
||||||
|
|
||||||
|
fmt::printf("Elapsed time: %.2f seconds", 1.23);
|
||||||
|
\endrst
|
||||||
|
*/
|
||||||
|
template <typename S, typename... Args>
|
||||||
|
inline FMT_ENABLE_IF_T(internal::is_string<S>::value, int)
|
||||||
|
printf(const S &format_str, const Args & ... args) {
|
||||||
|
internal::check_format_string<Args...>(format_str);
|
||||||
|
typedef internal::basic_buffer<FMT_CHAR(S)> buffer;
|
||||||
|
typedef typename basic_printf_context_t<buffer>::type context;
|
||||||
|
format_arg_store<context, Args...> as{ args... };
|
||||||
|
return vprintf(to_string_view(format_str),
|
||||||
|
basic_format_args<context>(as));
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename S, typename Char = FMT_CHAR(S)>
|
||||||
|
inline int vfprintf(std::basic_ostream<Char> &os,
|
||||||
|
const S &format,
|
||||||
|
basic_format_args<typename basic_printf_context_t<
|
||||||
|
internal::basic_buffer<Char>>::type> args) {
|
||||||
|
basic_memory_buffer<Char> buffer;
|
||||||
|
printf(buffer, to_string_view(format), args);
|
||||||
|
internal::write(os, buffer);
|
||||||
|
return static_cast<int>(buffer.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
\rst
|
||||||
|
Prints formatted data to the stream *os*.
|
||||||
|
|
||||||
|
**Example**::
|
||||||
|
|
||||||
|
fmt::fprintf(cerr, "Don't %s!", "panic");
|
||||||
|
\endrst
|
||||||
|
*/
|
||||||
|
template <typename S, typename... Args>
|
||||||
|
inline FMT_ENABLE_IF_T(internal::is_string<S>::value, int)
|
||||||
|
fprintf(std::basic_ostream<FMT_CHAR(S)> &os,
|
||||||
|
const S &format_str, const Args & ... args) {
|
||||||
|
internal::check_format_string<Args...>(format_str);
|
||||||
|
typedef internal::basic_buffer<FMT_CHAR(S)> buffer;
|
||||||
|
typedef typename basic_printf_context_t<buffer>::type context;
|
||||||
|
format_arg_store<context, Args...> as{ args... };
|
||||||
|
return vfprintf(os, to_string_view(format_str),
|
||||||
|
basic_format_args<context>(as));
|
||||||
|
}
|
||||||
|
FMT_END_NAMESPACE
|
||||||
|
|
||||||
|
#endif // FMT_PRINTF_H_
|
|
@ -0,0 +1,308 @@
|
||||||
|
// Formatting library for C++ - the core API
|
||||||
|
//
|
||||||
|
// Copyright (c) 2012 - present, Victor Zverovich
|
||||||
|
// All rights reserved.
|
||||||
|
//
|
||||||
|
// For the license information refer to format.h.
|
||||||
|
//
|
||||||
|
// Copyright (c) 2018 - present, Remotion (Igor Schulz)
|
||||||
|
// All Rights Reserved
|
||||||
|
// {fmt} support for ranges, containers and types tuple interface.
|
||||||
|
|
||||||
|
#ifndef FMT_RANGES_H_
|
||||||
|
#define FMT_RANGES_H_
|
||||||
|
|
||||||
|
#include "format.h"
|
||||||
|
#include <type_traits>
|
||||||
|
|
||||||
|
// output only up to N items from the range.
|
||||||
|
#ifndef FMT_RANGE_OUTPUT_LENGTH_LIMIT
|
||||||
|
# define FMT_RANGE_OUTPUT_LENGTH_LIMIT 256
|
||||||
|
#endif
|
||||||
|
|
||||||
|
FMT_BEGIN_NAMESPACE
|
||||||
|
|
||||||
|
template <typename Char>
|
||||||
|
struct formatting_base {
|
||||||
|
template <typename ParseContext>
|
||||||
|
FMT_CONSTEXPR auto parse(ParseContext &ctx) -> decltype(ctx.begin()) {
|
||||||
|
return ctx.begin();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename Char, typename Enable = void>
|
||||||
|
struct formatting_range : formatting_base<Char> {
|
||||||
|
static FMT_CONSTEXPR_DECL const std::size_t range_length_limit =
|
||||||
|
FMT_RANGE_OUTPUT_LENGTH_LIMIT; // output only up to N items from the range.
|
||||||
|
Char prefix;
|
||||||
|
Char delimiter;
|
||||||
|
Char postfix;
|
||||||
|
formatting_range() : prefix('{'), delimiter(','), postfix('}') {}
|
||||||
|
static FMT_CONSTEXPR_DECL const bool add_delimiter_spaces = true;
|
||||||
|
static FMT_CONSTEXPR_DECL const bool add_prepostfix_space = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename Char, typename Enable = void>
|
||||||
|
struct formatting_tuple : formatting_base<Char> {
|
||||||
|
Char prefix;
|
||||||
|
Char delimiter;
|
||||||
|
Char postfix;
|
||||||
|
formatting_tuple() : prefix('('), delimiter(','), postfix(')') {}
|
||||||
|
static FMT_CONSTEXPR_DECL const bool add_delimiter_spaces = true;
|
||||||
|
static FMT_CONSTEXPR_DECL const bool add_prepostfix_space = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
namespace internal {
|
||||||
|
|
||||||
|
template <typename RangeT, typename OutputIterator>
|
||||||
|
void copy(const RangeT &range, OutputIterator out) {
|
||||||
|
for (auto it = range.begin(), end = range.end(); it != end; ++it)
|
||||||
|
*out++ = *it;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename OutputIterator>
|
||||||
|
void copy(const char *str, OutputIterator out) {
|
||||||
|
const char *p_curr = str;
|
||||||
|
while (*p_curr) {
|
||||||
|
*out++ = *p_curr++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename OutputIterator>
|
||||||
|
void copy(char ch, OutputIterator out) {
|
||||||
|
*out++ = ch;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Return true value if T has std::string interface, like std::string_view.
|
||||||
|
template <typename T>
|
||||||
|
class is_like_std_string {
|
||||||
|
template <typename U>
|
||||||
|
static auto check(U *p) ->
|
||||||
|
decltype(p->find('a'), p->length(), p->data(), int());
|
||||||
|
template <typename>
|
||||||
|
static void check(...);
|
||||||
|
|
||||||
|
public:
|
||||||
|
static FMT_CONSTEXPR_DECL const bool value =
|
||||||
|
!std::is_void<decltype(check<T>(FMT_NULL))>::value;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename Char>
|
||||||
|
struct is_like_std_string<fmt::basic_string_view<Char>> : std::true_type {};
|
||||||
|
|
||||||
|
template <typename... Ts>
|
||||||
|
struct conditional_helper {};
|
||||||
|
|
||||||
|
template <typename T, typename _ = void>
|
||||||
|
struct is_range_ : std::false_type {};
|
||||||
|
|
||||||
|
#if !FMT_MSC_VER || FMT_MSC_VER > 1800
|
||||||
|
template <typename T>
|
||||||
|
struct is_range_<T, typename std::conditional<
|
||||||
|
false,
|
||||||
|
conditional_helper<decltype(internal::declval<T>().begin()),
|
||||||
|
decltype(internal::declval<T>().end())>,
|
||||||
|
void>::type> : std::true_type {};
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/// tuple_size and tuple_element check.
|
||||||
|
template <typename T>
|
||||||
|
class is_tuple_like_ {
|
||||||
|
template <typename U>
|
||||||
|
static auto check(U *p) ->
|
||||||
|
decltype(std::tuple_size<U>::value,
|
||||||
|
internal::declval<typename std::tuple_element<0, U>::type>(), int());
|
||||||
|
template <typename>
|
||||||
|
static void check(...);
|
||||||
|
|
||||||
|
public:
|
||||||
|
static FMT_CONSTEXPR_DECL const bool value =
|
||||||
|
!std::is_void<decltype(check<T>(FMT_NULL))>::value;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Check for integer_sequence
|
||||||
|
#if defined(__cpp_lib_integer_sequence) || FMT_MSC_VER >= 1900
|
||||||
|
template <typename T, T... N>
|
||||||
|
using integer_sequence = std::integer_sequence<T, N...>;
|
||||||
|
template <std::size_t... N>
|
||||||
|
using index_sequence = std::index_sequence<N...>;
|
||||||
|
template <std::size_t N>
|
||||||
|
using make_index_sequence = std::make_index_sequence<N>;
|
||||||
|
#else
|
||||||
|
template <typename T, T... N>
|
||||||
|
struct integer_sequence {
|
||||||
|
typedef T value_type;
|
||||||
|
|
||||||
|
static FMT_CONSTEXPR std::size_t size() {
|
||||||
|
return sizeof...(N);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <std::size_t... N>
|
||||||
|
using index_sequence = integer_sequence<std::size_t, N...>;
|
||||||
|
|
||||||
|
template <typename T, std::size_t N, T... Ns>
|
||||||
|
struct make_integer_sequence : make_integer_sequence<T, N - 1, N - 1, Ns...> {};
|
||||||
|
template <typename T, T... Ns>
|
||||||
|
struct make_integer_sequence<T, 0, Ns...> : integer_sequence<T, Ns...> {};
|
||||||
|
|
||||||
|
template <std::size_t N>
|
||||||
|
using make_index_sequence = make_integer_sequence<std::size_t, N>;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
template <class Tuple, class F, size_t... Is>
|
||||||
|
void for_each(index_sequence<Is...>, Tuple &&tup, F &&f) FMT_NOEXCEPT {
|
||||||
|
using std::get;
|
||||||
|
// using free function get<I>(T) now.
|
||||||
|
const int _[] = {0, ((void)f(get<Is>(tup)), 0)...};
|
||||||
|
(void)_; // blocks warnings
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class T>
|
||||||
|
FMT_CONSTEXPR make_index_sequence<std::tuple_size<T>::value>
|
||||||
|
get_indexes(T const &) { return {}; }
|
||||||
|
|
||||||
|
template <class Tuple, class F>
|
||||||
|
void for_each(Tuple &&tup, F &&f) {
|
||||||
|
const auto indexes = get_indexes(tup);
|
||||||
|
for_each(indexes, std::forward<Tuple>(tup), std::forward<F>(f));
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename Arg>
|
||||||
|
FMT_CONSTEXPR const char* format_str_quoted(bool add_space, const Arg&,
|
||||||
|
typename std::enable_if<
|
||||||
|
!is_like_std_string<typename std::decay<Arg>::type>::value>::type* = nullptr) {
|
||||||
|
return add_space ? " {}" : "{}";
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename Arg>
|
||||||
|
FMT_CONSTEXPR const char* format_str_quoted(bool add_space, const Arg&,
|
||||||
|
typename std::enable_if<
|
||||||
|
is_like_std_string<typename std::decay<Arg>::type>::value>::type* = nullptr) {
|
||||||
|
return add_space ? " \"{}\"" : "\"{}\"";
|
||||||
|
}
|
||||||
|
|
||||||
|
FMT_CONSTEXPR const char* format_str_quoted(bool add_space, const char*) {
|
||||||
|
return add_space ? " \"{}\"" : "\"{}\"";
|
||||||
|
}
|
||||||
|
FMT_CONSTEXPR const wchar_t* format_str_quoted(bool add_space, const wchar_t*) {
|
||||||
|
return add_space ? L" \"{}\"" : L"\"{}\"";
|
||||||
|
}
|
||||||
|
|
||||||
|
FMT_CONSTEXPR const char* format_str_quoted(bool add_space, const char) {
|
||||||
|
return add_space ? " '{}'" : "'{}'";
|
||||||
|
}
|
||||||
|
FMT_CONSTEXPR const wchar_t* format_str_quoted(bool add_space, const wchar_t) {
|
||||||
|
return add_space ? L" '{}'" : L"'{}'";
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace internal
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
struct is_tuple_like {
|
||||||
|
static FMT_CONSTEXPR_DECL const bool value =
|
||||||
|
internal::is_tuple_like_<T>::value && !internal::is_range_<T>::value;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename TupleT, typename Char>
|
||||||
|
struct formatter<TupleT, Char,
|
||||||
|
typename std::enable_if<fmt::is_tuple_like<TupleT>::value>::type> {
|
||||||
|
private:
|
||||||
|
// C++11 generic lambda for format()
|
||||||
|
template <typename FormatContext>
|
||||||
|
struct format_each {
|
||||||
|
template <typename T>
|
||||||
|
void operator()(const T& v) {
|
||||||
|
if (i > 0) {
|
||||||
|
if (formatting.add_prepostfix_space) {
|
||||||
|
*out++ = ' ';
|
||||||
|
}
|
||||||
|
internal::copy(formatting.delimiter, out);
|
||||||
|
}
|
||||||
|
format_to(out,
|
||||||
|
internal::format_str_quoted(
|
||||||
|
(formatting.add_delimiter_spaces && i > 0), v),
|
||||||
|
v);
|
||||||
|
++i;
|
||||||
|
}
|
||||||
|
|
||||||
|
formatting_tuple<Char>& formatting;
|
||||||
|
std::size_t& i;
|
||||||
|
typename std::add_lvalue_reference<decltype(std::declval<FormatContext>().out())>::type out;
|
||||||
|
};
|
||||||
|
|
||||||
|
public:
|
||||||
|
formatting_tuple<Char> formatting;
|
||||||
|
|
||||||
|
template <typename ParseContext>
|
||||||
|
FMT_CONSTEXPR auto parse(ParseContext &ctx) -> decltype(ctx.begin()) {
|
||||||
|
return formatting.parse(ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename FormatContext = format_context>
|
||||||
|
auto format(const TupleT &values, FormatContext &ctx) -> decltype(ctx.out()) {
|
||||||
|
auto out = ctx.out();
|
||||||
|
std::size_t i = 0;
|
||||||
|
internal::copy(formatting.prefix, out);
|
||||||
|
|
||||||
|
internal::for_each(values, format_each<FormatContext>{formatting, i, out});
|
||||||
|
if (formatting.add_prepostfix_space) {
|
||||||
|
*out++ = ' ';
|
||||||
|
}
|
||||||
|
internal::copy(formatting.postfix, out);
|
||||||
|
|
||||||
|
return ctx.out();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
struct is_range {
|
||||||
|
static FMT_CONSTEXPR_DECL const bool value =
|
||||||
|
internal::is_range_<T>::value && !internal::is_like_std_string<T>::value;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename RangeT, typename Char>
|
||||||
|
struct formatter<RangeT, Char,
|
||||||
|
typename std::enable_if<fmt::is_range<RangeT>::value>::type> {
|
||||||
|
|
||||||
|
formatting_range<Char> formatting;
|
||||||
|
|
||||||
|
template <typename ParseContext>
|
||||||
|
FMT_CONSTEXPR auto parse(ParseContext &ctx) -> decltype(ctx.begin()) {
|
||||||
|
return formatting.parse(ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename FormatContext>
|
||||||
|
typename FormatContext::iterator format(
|
||||||
|
const RangeT &values, FormatContext &ctx) {
|
||||||
|
auto out = ctx.out();
|
||||||
|
internal::copy(formatting.prefix, out);
|
||||||
|
std::size_t i = 0;
|
||||||
|
for (auto it = values.begin(), end = values.end(); it != end; ++it) {
|
||||||
|
if (i > 0) {
|
||||||
|
if (formatting.add_prepostfix_space) {
|
||||||
|
*out++ = ' ';
|
||||||
|
}
|
||||||
|
internal::copy(formatting.delimiter, out);
|
||||||
|
}
|
||||||
|
format_to(out,
|
||||||
|
internal::format_str_quoted(
|
||||||
|
(formatting.add_delimiter_spaces && i > 0), *it),
|
||||||
|
*it);
|
||||||
|
if (++i > formatting.range_length_limit) {
|
||||||
|
format_to(out, " ... <other elements>");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (formatting.add_prepostfix_space) {
|
||||||
|
*out++ = ' ';
|
||||||
|
}
|
||||||
|
internal::copy(formatting.postfix, out);
|
||||||
|
return ctx.out();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
FMT_END_NAMESPACE
|
||||||
|
|
||||||
|
#endif // FMT_RANGES_H_
|
||||||
|
|
|
@ -0,0 +1,160 @@
|
||||||
|
// Formatting library for C++ - time formatting
|
||||||
|
//
|
||||||
|
// Copyright (c) 2012 - present, 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>
|
||||||
|
#include <locale>
|
||||||
|
|
||||||
|
FMT_BEGIN_NAMESPACE
|
||||||
|
|
||||||
|
// Prevents expansion of a preceding token as a function-style macro.
|
||||||
|
// Usage: f FMT_NOMACRO()
|
||||||
|
#define FMT_NOMACRO
|
||||||
|
|
||||||
|
namespace internal{
|
||||||
|
inline null<> localtime_r FMT_NOMACRO(...) { return null<>(); }
|
||||||
|
inline null<> localtime_s(...) { return null<>(); }
|
||||||
|
inline null<> gmtime_r(...) { return null<>(); }
|
||||||
|
inline null<> gmtime_s(...) { return null<>(); }
|
||||||
|
} // namespace internal
|
||||||
|
|
||||||
|
// Thread-safe replacement for std::localtime
|
||||||
|
inline std::tm localtime(std::time_t time) {
|
||||||
|
struct dispatcher {
|
||||||
|
std::time_t time_;
|
||||||
|
std::tm tm_;
|
||||||
|
|
||||||
|
dispatcher(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; }
|
||||||
|
|
||||||
|
#if !FMT_MSC_VER
|
||||||
|
bool fallback(internal::null<>) {
|
||||||
|
using namespace fmt::internal;
|
||||||
|
std::tm *tm = std::localtime(&time_);
|
||||||
|
if (tm) tm_ = *tm;
|
||||||
|
return tm != FMT_NULL;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
dispatcher lt(time);
|
||||||
|
// Too big time values may be unsupported.
|
||||||
|
if (!lt.run())
|
||||||
|
FMT_THROW(format_error("time_t value out of range"));
|
||||||
|
return lt.tm_;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Thread-safe replacement for std::gmtime
|
||||||
|
inline std::tm gmtime(std::time_t time) {
|
||||||
|
struct dispatcher {
|
||||||
|
std::time_t time_;
|
||||||
|
std::tm tm_;
|
||||||
|
|
||||||
|
dispatcher(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; }
|
||||||
|
|
||||||
|
#if !FMT_MSC_VER
|
||||||
|
bool fallback(internal::null<>) {
|
||||||
|
std::tm *tm = std::gmtime(&time_);
|
||||||
|
if (tm) tm_ = *tm;
|
||||||
|
return tm != FMT_NULL;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
dispatcher gt(time);
|
||||||
|
// Too big time values may be unsupported.
|
||||||
|
if (!gt.run())
|
||||||
|
FMT_THROW(format_error("time_t value out of range"));
|
||||||
|
return gt.tm_;
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace internal {
|
||||||
|
inline std::size_t strftime(char *str, std::size_t count, const char *format,
|
||||||
|
const std::tm *time) {
|
||||||
|
return std::strftime(str, count, format, time);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline std::size_t strftime(wchar_t *str, std::size_t count,
|
||||||
|
const wchar_t *format, const std::tm *time) {
|
||||||
|
return std::wcsftime(str, count, format, time);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Char>
|
||||||
|
struct formatter<std::tm, Char> {
|
||||||
|
template <typename ParseContext>
|
||||||
|
auto parse(ParseContext &ctx) -> decltype(ctx.begin()) {
|
||||||
|
auto it = ctx.begin();
|
||||||
|
if (it != ctx.end() && *it == ':')
|
||||||
|
++it;
|
||||||
|
auto end = it;
|
||||||
|
while (end != ctx.end() && *end != '}')
|
||||||
|
++end;
|
||||||
|
tm_format.reserve(internal::to_unsigned(end - it + 1));
|
||||||
|
tm_format.append(it, end);
|
||||||
|
tm_format.push_back('\0');
|
||||||
|
return end;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename FormatContext>
|
||||||
|
auto format(const std::tm &tm, FormatContext &ctx) -> decltype(ctx.out()) {
|
||||||
|
basic_memory_buffer<Char> buf;
|
||||||
|
std::size_t start = buf.size();
|
||||||
|
for (;;) {
|
||||||
|
std::size_t size = buf.capacity() - start;
|
||||||
|
std::size_t count =
|
||||||
|
internal::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 std::copy(buf.begin(), buf.end(), ctx.out());
|
||||||
|
}
|
||||||
|
|
||||||
|
basic_memory_buffer<Char> tm_format;
|
||||||
|
};
|
||||||
|
FMT_END_NAMESPACE
|
||||||
|
|
||||||
|
#endif // FMT_TIME_H_
|
|
@ -0,0 +1,59 @@
|
||||||
|
// Formatting library for C++
|
||||||
|
//
|
||||||
|
// Copyright (c) 2012 - 2016, Victor Zverovich
|
||||||
|
// All rights reserved.
|
||||||
|
//
|
||||||
|
// For the license information refer to format.h.
|
||||||
|
|
||||||
|
#include "fmt/format-inl.h"
|
||||||
|
|
||||||
|
FMT_BEGIN_NAMESPACE
|
||||||
|
template struct internal::basic_data<void>;
|
||||||
|
template FMT_API internal::locale_ref::locale_ref(const std::locale &loc);
|
||||||
|
template FMT_API std::locale internal::locale_ref::get<std::locale>() const;
|
||||||
|
|
||||||
|
// Explicit instantiations for char.
|
||||||
|
|
||||||
|
template FMT_API char internal::thousands_sep_impl(locale_ref);
|
||||||
|
|
||||||
|
template FMT_API void internal::basic_buffer<char>::append(const char *, const char *);
|
||||||
|
|
||||||
|
template FMT_API void internal::arg_map<format_context>::init(
|
||||||
|
const basic_format_args<format_context> &args);
|
||||||
|
|
||||||
|
template FMT_API int internal::char_traits<char>::format_float(
|
||||||
|
char *, std::size_t, const char *, int, double);
|
||||||
|
|
||||||
|
template FMT_API int internal::char_traits<char>::format_float(
|
||||||
|
char *, std::size_t, const char *, int, long double);
|
||||||
|
|
||||||
|
template FMT_API std::string internal::vformat<char>(
|
||||||
|
string_view, basic_format_args<format_context>);
|
||||||
|
|
||||||
|
template FMT_API format_context::iterator internal::vformat_to(
|
||||||
|
internal::buffer &, string_view, basic_format_args<format_context>);
|
||||||
|
|
||||||
|
template FMT_API void internal::sprintf_format(
|
||||||
|
double, internal::buffer &, core_format_specs);
|
||||||
|
template FMT_API void internal::sprintf_format(
|
||||||
|
long double, internal::buffer &, core_format_specs);
|
||||||
|
|
||||||
|
// Explicit instantiations for wchar_t.
|
||||||
|
|
||||||
|
template FMT_API wchar_t internal::thousands_sep_impl(locale_ref);
|
||||||
|
|
||||||
|
template FMT_API void internal::basic_buffer<wchar_t>::append(
|
||||||
|
const wchar_t *, const wchar_t *);
|
||||||
|
|
||||||
|
template FMT_API void internal::arg_map<wformat_context>::init(
|
||||||
|
const basic_format_args<wformat_context> &);
|
||||||
|
|
||||||
|
template FMT_API int internal::char_traits<wchar_t>::format_float(
|
||||||
|
wchar_t *, std::size_t, const wchar_t *, int, double);
|
||||||
|
|
||||||
|
template FMT_API int internal::char_traits<wchar_t>::format_float(
|
||||||
|
wchar_t *, std::size_t, const wchar_t *, int, long double);
|
||||||
|
|
||||||
|
template FMT_API std::wstring internal::vformat<wchar_t>(
|
||||||
|
wstring_view, basic_format_args<wformat_context>);
|
||||||
|
FMT_END_NAMESPACE
|
|
@ -0,0 +1,244 @@
|
||||||
|
// A C++ interface to POSIX functions.
|
||||||
|
//
|
||||||
|
// Copyright (c) 2012 - 2016, Victor Zverovich
|
||||||
|
// All rights reserved.
|
||||||
|
//
|
||||||
|
// For the license information refer to format.h.
|
||||||
|
|
||||||
|
// Disable bogus MSVC warnings.
|
||||||
|
#if !defined(_CRT_SECURE_NO_WARNINGS) && defined(_MSC_VER)
|
||||||
|
# define _CRT_SECURE_NO_WARNINGS
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "fmt/posix.h"
|
||||||
|
|
||||||
|
#include <limits.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
|
||||||
|
#ifndef _WIN32
|
||||||
|
# include <unistd.h>
|
||||||
|
#else
|
||||||
|
# ifndef WIN32_LEAN_AND_MEAN
|
||||||
|
# define WIN32_LEAN_AND_MEAN
|
||||||
|
# endif
|
||||||
|
# include <windows.h>
|
||||||
|
# include <io.h>
|
||||||
|
|
||||||
|
# define O_CREAT _O_CREAT
|
||||||
|
# define O_TRUNC _O_TRUNC
|
||||||
|
|
||||||
|
# ifndef S_IRUSR
|
||||||
|
# define S_IRUSR _S_IREAD
|
||||||
|
# endif
|
||||||
|
|
||||||
|
# ifndef S_IWUSR
|
||||||
|
# define S_IWUSR _S_IWRITE
|
||||||
|
# endif
|
||||||
|
|
||||||
|
# ifdef __MINGW32__
|
||||||
|
# define _SH_DENYNO 0x40
|
||||||
|
# endif
|
||||||
|
|
||||||
|
#endif // _WIN32
|
||||||
|
|
||||||
|
#ifdef fileno
|
||||||
|
# undef fileno
|
||||||
|
#endif
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
#ifdef _WIN32
|
||||||
|
// Return type of read and write functions.
|
||||||
|
typedef int RWResult;
|
||||||
|
|
||||||
|
// On Windows the count argument to read and write is unsigned, so convert
|
||||||
|
// it from size_t preventing integer overflow.
|
||||||
|
inline unsigned convert_rwcount(std::size_t count) {
|
||||||
|
return count <= UINT_MAX ? static_cast<unsigned>(count) : UINT_MAX;
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
// Return type of read and write functions.
|
||||||
|
typedef ssize_t RWResult;
|
||||||
|
|
||||||
|
inline std::size_t convert_rwcount(std::size_t count) { return count; }
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
FMT_BEGIN_NAMESPACE
|
||||||
|
|
||||||
|
buffered_file::~buffered_file() FMT_NOEXCEPT {
|
||||||
|
if (file_ && FMT_SYSTEM(fclose(file_)) != 0)
|
||||||
|
report_system_error(errno, "cannot close file");
|
||||||
|
}
|
||||||
|
|
||||||
|
buffered_file::buffered_file(cstring_view filename, cstring_view mode) {
|
||||||
|
FMT_RETRY_VAL(file_,
|
||||||
|
FMT_SYSTEM(fopen(filename.c_str(), mode.c_str())), FMT_NULL);
|
||||||
|
if (!file_)
|
||||||
|
FMT_THROW(system_error(errno, "cannot open file {}", filename.c_str()));
|
||||||
|
}
|
||||||
|
|
||||||
|
void buffered_file::close() {
|
||||||
|
if (!file_)
|
||||||
|
return;
|
||||||
|
int result = FMT_SYSTEM(fclose(file_));
|
||||||
|
file_ = FMT_NULL;
|
||||||
|
if (result != 0)
|
||||||
|
FMT_THROW(system_error(errno, "cannot close file"));
|
||||||
|
}
|
||||||
|
|
||||||
|
// A macro used to prevent expansion of fileno on broken versions of MinGW.
|
||||||
|
#define FMT_ARGS
|
||||||
|
|
||||||
|
int buffered_file::fileno() const {
|
||||||
|
int fd = FMT_POSIX_CALL(fileno FMT_ARGS(file_));
|
||||||
|
if (fd == -1)
|
||||||
|
FMT_THROW(system_error(errno, "cannot get file descriptor"));
|
||||||
|
return fd;
|
||||||
|
}
|
||||||
|
|
||||||
|
file::file(cstring_view path, int oflag) {
|
||||||
|
int mode = S_IRUSR | S_IWUSR;
|
||||||
|
#if defined(_WIN32) && !defined(__MINGW32__)
|
||||||
|
fd_ = -1;
|
||||||
|
FMT_POSIX_CALL(sopen_s(&fd_, path.c_str(), oflag, _SH_DENYNO, mode));
|
||||||
|
#else
|
||||||
|
FMT_RETRY(fd_, FMT_POSIX_CALL(open(path.c_str(), oflag, mode)));
|
||||||
|
#endif
|
||||||
|
if (fd_ == -1)
|
||||||
|
FMT_THROW(system_error(errno, "cannot open file {}", path.c_str()));
|
||||||
|
}
|
||||||
|
|
||||||
|
file::~file() FMT_NOEXCEPT {
|
||||||
|
// Don't retry close in case of EINTR!
|
||||||
|
// See http://linux.derkeiler.com/Mailing-Lists/Kernel/2005-09/3000.html
|
||||||
|
if (fd_ != -1 && FMT_POSIX_CALL(close(fd_)) != 0)
|
||||||
|
report_system_error(errno, "cannot close file");
|
||||||
|
}
|
||||||
|
|
||||||
|
void file::close() {
|
||||||
|
if (fd_ == -1)
|
||||||
|
return;
|
||||||
|
// Don't retry close in case of EINTR!
|
||||||
|
// See http://linux.derkeiler.com/Mailing-Lists/Kernel/2005-09/3000.html
|
||||||
|
int result = FMT_POSIX_CALL(close(fd_));
|
||||||
|
fd_ = -1;
|
||||||
|
if (result != 0)
|
||||||
|
FMT_THROW(system_error(errno, "cannot close file"));
|
||||||
|
}
|
||||||
|
|
||||||
|
long long file::size() const {
|
||||||
|
#ifdef _WIN32
|
||||||
|
// Use GetFileSize instead of GetFileSizeEx for the case when _WIN32_WINNT
|
||||||
|
// is less than 0x0500 as is the case with some default MinGW builds.
|
||||||
|
// Both functions support large file sizes.
|
||||||
|
DWORD size_upper = 0;
|
||||||
|
HANDLE handle = reinterpret_cast<HANDLE>(_get_osfhandle(fd_));
|
||||||
|
DWORD size_lower = FMT_SYSTEM(GetFileSize(handle, &size_upper));
|
||||||
|
if (size_lower == INVALID_FILE_SIZE) {
|
||||||
|
DWORD error = GetLastError();
|
||||||
|
if (error != NO_ERROR)
|
||||||
|
FMT_THROW(windows_error(GetLastError(), "cannot get file size"));
|
||||||
|
}
|
||||||
|
unsigned long long long_size = size_upper;
|
||||||
|
return (long_size << sizeof(DWORD) * CHAR_BIT) | size_lower;
|
||||||
|
#else
|
||||||
|
typedef struct stat Stat;
|
||||||
|
Stat file_stat = Stat();
|
||||||
|
if (FMT_POSIX_CALL(fstat(fd_, &file_stat)) == -1)
|
||||||
|
FMT_THROW(system_error(errno, "cannot get file attributes"));
|
||||||
|
static_assert(sizeof(long long) >= sizeof(file_stat.st_size),
|
||||||
|
"return type of file::size is not large enough");
|
||||||
|
return file_stat.st_size;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
std::size_t file::read(void *buffer, std::size_t count) {
|
||||||
|
RWResult result = 0;
|
||||||
|
FMT_RETRY(result, FMT_POSIX_CALL(read(fd_, buffer, convert_rwcount(count))));
|
||||||
|
if (result < 0)
|
||||||
|
FMT_THROW(system_error(errno, "cannot read from file"));
|
||||||
|
return internal::to_unsigned(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::size_t file::write(const void *buffer, std::size_t count) {
|
||||||
|
RWResult result = 0;
|
||||||
|
FMT_RETRY(result, FMT_POSIX_CALL(write(fd_, buffer, convert_rwcount(count))));
|
||||||
|
if (result < 0)
|
||||||
|
FMT_THROW(system_error(errno, "cannot write to file"));
|
||||||
|
return internal::to_unsigned(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
file file::dup(int fd) {
|
||||||
|
// Don't retry as dup doesn't return EINTR.
|
||||||
|
// http://pubs.opengroup.org/onlinepubs/009695399/functions/dup.html
|
||||||
|
int new_fd = FMT_POSIX_CALL(dup(fd));
|
||||||
|
if (new_fd == -1)
|
||||||
|
FMT_THROW(system_error(errno, "cannot duplicate file descriptor {}", fd));
|
||||||
|
return file(new_fd);
|
||||||
|
}
|
||||||
|
|
||||||
|
void file::dup2(int fd) {
|
||||||
|
int result = 0;
|
||||||
|
FMT_RETRY(result, FMT_POSIX_CALL(dup2(fd_, fd)));
|
||||||
|
if (result == -1) {
|
||||||
|
FMT_THROW(system_error(errno,
|
||||||
|
"cannot duplicate file descriptor {} to {}", fd_, fd));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void file::dup2(int fd, error_code &ec) FMT_NOEXCEPT {
|
||||||
|
int result = 0;
|
||||||
|
FMT_RETRY(result, FMT_POSIX_CALL(dup2(fd_, fd)));
|
||||||
|
if (result == -1)
|
||||||
|
ec = error_code(errno);
|
||||||
|
}
|
||||||
|
|
||||||
|
void file::pipe(file &read_end, file &write_end) {
|
||||||
|
// Close the descriptors first to make sure that assignments don't throw
|
||||||
|
// and there are no leaks.
|
||||||
|
read_end.close();
|
||||||
|
write_end.close();
|
||||||
|
int fds[2] = {};
|
||||||
|
#ifdef _WIN32
|
||||||
|
// Make the default pipe capacity same as on Linux 2.6.11+.
|
||||||
|
enum { DEFAULT_CAPACITY = 65536 };
|
||||||
|
int result = FMT_POSIX_CALL(pipe(fds, DEFAULT_CAPACITY, _O_BINARY));
|
||||||
|
#else
|
||||||
|
// Don't retry as the pipe function doesn't return EINTR.
|
||||||
|
// http://pubs.opengroup.org/onlinepubs/009696799/functions/pipe.html
|
||||||
|
int result = FMT_POSIX_CALL(pipe(fds));
|
||||||
|
#endif
|
||||||
|
if (result != 0)
|
||||||
|
FMT_THROW(system_error(errno, "cannot create pipe"));
|
||||||
|
// The following assignments don't throw because read_fd and write_fd
|
||||||
|
// are closed.
|
||||||
|
read_end = file(fds[0]);
|
||||||
|
write_end = file(fds[1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
buffered_file file::fdopen(const char *mode) {
|
||||||
|
// Don't retry as fdopen doesn't return EINTR.
|
||||||
|
FILE *f = FMT_POSIX_CALL(fdopen(fd_, mode));
|
||||||
|
if (!f)
|
||||||
|
FMT_THROW(system_error(errno,
|
||||||
|
"cannot associate stream with file descriptor"));
|
||||||
|
buffered_file bf(f);
|
||||||
|
fd_ = -1;
|
||||||
|
return bf;
|
||||||
|
}
|
||||||
|
|
||||||
|
long getpagesize() {
|
||||||
|
#ifdef _WIN32
|
||||||
|
SYSTEM_INFO si;
|
||||||
|
GetSystemInfo(&si);
|
||||||
|
return si.dwPageSize;
|
||||||
|
#else
|
||||||
|
long size = FMT_POSIX_CALL(sysconf(_SC_PAGESIZE));
|
||||||
|
if (size < 0)
|
||||||
|
FMT_THROW(system_error(errno, "cannot get memory page size"));
|
||||||
|
return size;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
FMT_END_NAMESPACE
|
||||||
|
|
|
@ -0,0 +1,15 @@
|
||||||
|
LOCAL_PATH := $(call my-dir)
|
||||||
|
include $(CLEAR_VARS)
|
||||||
|
|
||||||
|
LOCAL_MODULE := fmt_static
|
||||||
|
LOCAL_MODULE_FILENAME := libfmt
|
||||||
|
|
||||||
|
LOCAL_SRC_FILES := ../src/format.cc
|
||||||
|
|
||||||
|
LOCAL_C_INCLUDES := $(LOCAL_PATH)
|
||||||
|
LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH)
|
||||||
|
|
||||||
|
LOCAL_CFLAGS += -std=c++11 -fexceptions
|
||||||
|
|
||||||
|
include $(BUILD_STATIC_LIBRARY)
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
<manifest package="net.fmtlib" />
|
|
@ -0,0 +1,6 @@
|
||||||
|
This directory contains build support files such as
|
||||||
|
|
||||||
|
* CMake modules
|
||||||
|
* Build scripts
|
||||||
|
* qmake (static build with dynamic libc only)
|
||||||
|
|
|
@ -0,0 +1,40 @@
|
||||||
|
#!/usr/bin/env python
|
||||||
|
# Build the project on AppVeyor.
|
||||||
|
|
||||||
|
import os
|
||||||
|
from subprocess import check_call
|
||||||
|
|
||||||
|
build = os.environ['BUILD']
|
||||||
|
config = os.environ['CONFIGURATION']
|
||||||
|
platform = os.environ['PLATFORM']
|
||||||
|
path = os.environ['PATH']
|
||||||
|
image = os.environ['APPVEYOR_BUILD_WORKER_IMAGE']
|
||||||
|
jobid = os.environ['APPVEYOR_JOB_ID']
|
||||||
|
cmake_command = ['cmake', '-DFMT_PEDANTIC=ON', '-DCMAKE_BUILD_TYPE=' + config, '..']
|
||||||
|
if build == 'mingw':
|
||||||
|
cmake_command.append('-GMinGW Makefiles')
|
||||||
|
build_command = ['mingw32-make', '-j4']
|
||||||
|
test_command = ['mingw32-make', 'test']
|
||||||
|
# Remove the path to Git bin directory from $PATH because it breaks
|
||||||
|
# MinGW config.
|
||||||
|
path = path.replace(r'C:\Program Files (x86)\Git\bin', '')
|
||||||
|
os.environ['PATH'] = r'C:\MinGW\bin;' + path
|
||||||
|
else:
|
||||||
|
# Add MSBuild 14.0 to PATH as described in
|
||||||
|
# http://help.appveyor.com/discussions/problems/2229-v140-not-found-on-vs2105rc.
|
||||||
|
os.environ['PATH'] = r'C:\Program Files (x86)\MSBuild\15.0\Bin;' + path
|
||||||
|
if image == 'Visual Studio 2013':
|
||||||
|
generator = 'Visual Studio 12 2013'
|
||||||
|
elif image == 'Visual Studio 2015':
|
||||||
|
generator = 'Visual Studio 14 2015'
|
||||||
|
elif image == 'Visual Studio 2017':
|
||||||
|
generator = 'Visual Studio 15 2017'
|
||||||
|
if platform == 'x64':
|
||||||
|
generator += ' Win64'
|
||||||
|
cmake_command.append('-G' + generator)
|
||||||
|
build_command = ['cmake', '--build', '.', '--config', config, '--', '/m:4']
|
||||||
|
test_command = ['ctest', '-C', config]
|
||||||
|
|
||||||
|
check_call(cmake_command)
|
||||||
|
check_call(build_command)
|
||||||
|
check_call(test_command)
|
|
@ -0,0 +1,34 @@
|
||||||
|
configuration:
|
||||||
|
- Debug
|
||||||
|
- Release
|
||||||
|
|
||||||
|
clone_depth: 1
|
||||||
|
|
||||||
|
platform:
|
||||||
|
- Win32
|
||||||
|
- x64
|
||||||
|
|
||||||
|
image:
|
||||||
|
- Visual Studio 2013
|
||||||
|
- Visual Studio 2015
|
||||||
|
- Visual Studio 2017
|
||||||
|
|
||||||
|
environment:
|
||||||
|
CTEST_OUTPUT_ON_FAILURE: 1
|
||||||
|
MSVC_DEFAULT_OPTIONS: ON
|
||||||
|
BUILD: msvc
|
||||||
|
|
||||||
|
before_build:
|
||||||
|
- mkdir build
|
||||||
|
- cd build
|
||||||
|
|
||||||
|
build_script:
|
||||||
|
- python ../support/appveyor-build.py
|
||||||
|
|
||||||
|
on_failure:
|
||||||
|
- appveyor PushArtifact Testing/Temporary/LastTest.log
|
||||||
|
- appveyor AddTest test
|
||||||
|
|
||||||
|
# Uncomment this to debug AppVeyor failures.
|
||||||
|
#on_finish:
|
||||||
|
# - ps: $blockRdp = $true; iex ((new-object net.webclient).DownloadString('https://raw.githubusercontent.com/appveyor/ci/master/scripts/enable-rdp.ps1'))
|
|
@ -0,0 +1,105 @@
|
||||||
|
|
||||||
|
// General gradle arguments for root project
|
||||||
|
buildscript {
|
||||||
|
repositories {
|
||||||
|
google()
|
||||||
|
jcenter()
|
||||||
|
}
|
||||||
|
dependencies {
|
||||||
|
//
|
||||||
|
// https://developer.android.com/studio/releases/gradle-plugin
|
||||||
|
//
|
||||||
|
// Notice that 3.1.3 here is the version of [Android Gradle Plugin]
|
||||||
|
// Accroding to URL above you will need Gradle 4.4 or higher
|
||||||
|
//
|
||||||
|
classpath 'com.android.tools.build:gradle:3.1.3'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
repositories {
|
||||||
|
google()
|
||||||
|
jcenter()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Output: Shared library (.so) for Android
|
||||||
|
apply plugin: 'com.android.library'
|
||||||
|
|
||||||
|
android {
|
||||||
|
compileSdkVersion 25 // Android 7.0
|
||||||
|
|
||||||
|
// Target ABI
|
||||||
|
// - This option controls target platform of module
|
||||||
|
// - The platform might be limited by compiler's support
|
||||||
|
// some can work with Clang(default), but some can work only with GCC...
|
||||||
|
// if bad, both toolchains might not support it
|
||||||
|
splits {
|
||||||
|
abi {
|
||||||
|
enable true
|
||||||
|
// Specify platforms for Application
|
||||||
|
reset()
|
||||||
|
include "arm64-v8a", "armeabi-v7a", "x86_64"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
defaultConfig {
|
||||||
|
minSdkVersion 21 // Android 5.0+
|
||||||
|
targetSdkVersion 25 // Follow Compile SDK
|
||||||
|
versionCode 20 // Follow release count
|
||||||
|
versionName "5.2.1" // Follow Official version
|
||||||
|
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
|
||||||
|
|
||||||
|
externalNativeBuild {
|
||||||
|
cmake {
|
||||||
|
arguments "-DANDROID_STL=c++_shared" // Specify Android STL
|
||||||
|
arguments "-DBUILD_SHARED_LIBS=true" // Build shared object
|
||||||
|
arguments "-DFMT_TEST=false" // Skip test
|
||||||
|
arguments "-DFMT_DOC=false" // Skip document
|
||||||
|
cppFlags "-std=c++17"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
println("Gradle CMake Plugin: ")
|
||||||
|
println(externalNativeBuild.cmake.cppFlags)
|
||||||
|
println(externalNativeBuild.cmake.arguments)
|
||||||
|
}
|
||||||
|
|
||||||
|
// External Native build
|
||||||
|
// - Use existing CMakeList.txt
|
||||||
|
// - Give path to CMake. This gradle file should be
|
||||||
|
// neighbor of the top level cmake
|
||||||
|
externalNativeBuild {
|
||||||
|
cmake {
|
||||||
|
path "../CMakeLists.txt"
|
||||||
|
// buildStagingDirectory "./build" // Custom path for cmake output
|
||||||
|
}
|
||||||
|
//println(cmake.path)
|
||||||
|
}
|
||||||
|
|
||||||
|
sourceSets{
|
||||||
|
// Android Manifest for Gradle
|
||||||
|
main {
|
||||||
|
manifest.srcFile 'AndroidManifest.xml'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
assemble.doLast
|
||||||
|
{
|
||||||
|
// Instead of `ninja install`, Gradle will deploy the files.
|
||||||
|
// We are doing this since FMT is dependent to the ANDROID_STL after build
|
||||||
|
copy {
|
||||||
|
from 'build/intermediates/cmake'
|
||||||
|
into '../libs'
|
||||||
|
}
|
||||||
|
// Copy debug binaries
|
||||||
|
copy {
|
||||||
|
from '../libs/debug/obj'
|
||||||
|
into '../libs/debug'
|
||||||
|
}
|
||||||
|
// Copy Release binaries
|
||||||
|
copy {
|
||||||
|
from '../libs/release/obj'
|
||||||
|
into '../libs/release'
|
||||||
|
}
|
||||||
|
// Remove empty directory
|
||||||
|
delete '../libs/debug/obj'
|
||||||
|
delete '../libs/release/obj'
|
||||||
|
}
|
|
@ -0,0 +1,7 @@
|
||||||
|
# A CMake script to find SetEnv.cmd.
|
||||||
|
|
||||||
|
find_program(WINSDK_SETENV NAMES SetEnv.cmd
|
||||||
|
PATHS "[HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Microsoft SDKs\\Windows;CurrentInstallFolder]/bin")
|
||||||
|
if (WINSDK_SETENV AND PRINT_PATH)
|
||||||
|
execute_process(COMMAND ${CMAKE_COMMAND} -E echo "${WINSDK_SETENV}")
|
||||||
|
endif ()
|
|
@ -0,0 +1,97 @@
|
||||||
|
# C++14 feature support detection
|
||||||
|
|
||||||
|
include(CheckCXXSourceCompiles)
|
||||||
|
include(CheckCXXCompilerFlag)
|
||||||
|
|
||||||
|
if (NOT CMAKE_CXX_STANDARD)
|
||||||
|
set(CMAKE_CXX_STANDARD 11)
|
||||||
|
endif()
|
||||||
|
message(STATUS "CXX_STANDARD: ${CMAKE_CXX_STANDARD}")
|
||||||
|
|
||||||
|
if (CMAKE_CXX_STANDARD EQUAL 20)
|
||||||
|
check_cxx_compiler_flag(-std=c++20 has_std_20_flag)
|
||||||
|
check_cxx_compiler_flag(-std=c++2a has_std_2a_flag)
|
||||||
|
|
||||||
|
if (has_std_20_flag)
|
||||||
|
set(CXX_STANDARD_FLAG -std=c++20)
|
||||||
|
elseif (has_std_2a_flag)
|
||||||
|
set(CXX_STANDARD_FLAG -std=c++2a)
|
||||||
|
endif ()
|
||||||
|
elseif (CMAKE_CXX_STANDARD EQUAL 17)
|
||||||
|
check_cxx_compiler_flag(-std=c++17 has_std_17_flag)
|
||||||
|
check_cxx_compiler_flag(-std=c++1z has_std_1z_flag)
|
||||||
|
|
||||||
|
if (has_std_17_flag)
|
||||||
|
set(CXX_STANDARD_FLAG -std=c++17)
|
||||||
|
elseif (has_std_1z_flag)
|
||||||
|
set(CXX_STANDARD_FLAG -std=c++1z)
|
||||||
|
endif ()
|
||||||
|
elseif (CMAKE_CXX_STANDARD EQUAL 14)
|
||||||
|
check_cxx_compiler_flag(-std=c++14 has_std_14_flag)
|
||||||
|
check_cxx_compiler_flag(-std=c++1y has_std_1y_flag)
|
||||||
|
|
||||||
|
if (has_std_14_flag)
|
||||||
|
set(CXX_STANDARD_FLAG -std=c++14)
|
||||||
|
elseif (has_std_1y_flag)
|
||||||
|
set(CXX_STANDARD_FLAG -std=c++1y)
|
||||||
|
endif ()
|
||||||
|
elseif (CMAKE_CXX_STANDARD EQUAL 11)
|
||||||
|
check_cxx_compiler_flag(-std=c++11 has_std_11_flag)
|
||||||
|
check_cxx_compiler_flag(-std=c++0x has_std_0x_flag)
|
||||||
|
|
||||||
|
if (has_std_11_flag)
|
||||||
|
set(CXX_STANDARD_FLAG -std=c++11)
|
||||||
|
elseif (has_std_0x_flag)
|
||||||
|
set(CXX_STANDARD_FLAG -std=c++0x)
|
||||||
|
endif ()
|
||||||
|
endif ()
|
||||||
|
|
||||||
|
set(CMAKE_REQUIRED_FLAGS ${CXX_STANDARD_FLAG})
|
||||||
|
|
||||||
|
# Check if variadic templates are working and not affected by GCC bug 39653:
|
||||||
|
# https://gcc.gnu.org/bugzilla/show_bug.cgi?id=39653
|
||||||
|
check_cxx_source_compiles("
|
||||||
|
template <class T, class ...Types>
|
||||||
|
struct S { typedef typename S<Types...>::type type; };
|
||||||
|
int main() {}" SUPPORTS_VARIADIC_TEMPLATES)
|
||||||
|
if (NOT SUPPORTS_VARIADIC_TEMPLATES)
|
||||||
|
set (SUPPORTS_VARIADIC_TEMPLATES OFF)
|
||||||
|
endif ()
|
||||||
|
|
||||||
|
# Check if initializer lists are supported.
|
||||||
|
check_cxx_source_compiles("
|
||||||
|
#include <initializer_list>
|
||||||
|
int main() {}" SUPPORTS_INITIALIZER_LIST)
|
||||||
|
if (NOT SUPPORTS_INITIALIZER_LIST)
|
||||||
|
set (SUPPORTS_INITIALIZER_LIST OFF)
|
||||||
|
endif ()
|
||||||
|
|
||||||
|
# Check if enum bases are available
|
||||||
|
check_cxx_source_compiles("
|
||||||
|
enum C : char {A};
|
||||||
|
int main() {}"
|
||||||
|
SUPPORTS_ENUM_BASE)
|
||||||
|
if (NOT SUPPORTS_ENUM_BASE)
|
||||||
|
set (SUPPORTS_ENUM_BASE OFF)
|
||||||
|
endif ()
|
||||||
|
|
||||||
|
# Check if type traits are available
|
||||||
|
check_cxx_source_compiles("
|
||||||
|
#include <type_traits>
|
||||||
|
class C { void operator=(const C&); };
|
||||||
|
int main() { static_assert(!std::is_copy_assignable<C>::value, \"\"); }"
|
||||||
|
SUPPORTS_TYPE_TRAITS)
|
||||||
|
if (NOT SUPPORTS_TYPE_TRAITS)
|
||||||
|
set (SUPPORTS_TYPE_TRAITS OFF)
|
||||||
|
endif ()
|
||||||
|
|
||||||
|
# Check if user-defined literals are available
|
||||||
|
check_cxx_source_compiles("
|
||||||
|
void operator\"\" _udl(long double);
|
||||||
|
int main() {}"
|
||||||
|
SUPPORTS_USER_DEFINED_LITERALS)
|
||||||
|
if (NOT SUPPORTS_USER_DEFINED_LITERALS)
|
||||||
|
set (SUPPORTS_USER_DEFINED_LITERALS OFF)
|
||||||
|
endif ()
|
||||||
|
|
||||||
|
set(CMAKE_REQUIRED_FLAGS )
|
|
@ -0,0 +1,4 @@
|
||||||
|
@PACKAGE_INIT@
|
||||||
|
|
||||||
|
include(${CMAKE_CURRENT_LIST_DIR}/@targets_export_name@.cmake)
|
||||||
|
check_required_components(fmt)
|
|
@ -0,0 +1,11 @@
|
||||||
|
prefix=@CMAKE_INSTALL_PREFIX@
|
||||||
|
exec_prefix=@CMAKE_INSTALL_PREFIX@
|
||||||
|
libdir=@CMAKE_INSTALL_FULL_LIBDIR@
|
||||||
|
includedir=@CMAKE_INSTALL_FULL_INCLUDEDIR@
|
||||||
|
|
||||||
|
Name: fmt
|
||||||
|
Description: A modern formatting library
|
||||||
|
Version: @FMT_VERSION@
|
||||||
|
Libs: -L${libdir} -lfmt
|
||||||
|
Cflags: -I${includedir}
|
||||||
|
|
|
@ -0,0 +1,11 @@
|
||||||
|
@echo on
|
||||||
|
rem This scripts configures build environment and runs CMake.
|
||||||
|
rem Use it instead of running CMake directly when building with
|
||||||
|
rem the Microsoft SDK toolchain rather than Visual Studio.
|
||||||
|
rem It is used in the same way as cmake, for example:
|
||||||
|
rem
|
||||||
|
rem run-cmake -G "Visual Studio 10 Win64" .
|
||||||
|
|
||||||
|
for /F "delims=" %%i IN ('cmake "-DPRINT_PATH=1" -P %~dp0/FindSetEnv.cmake') DO set setenv=%%i
|
||||||
|
if NOT "%setenv%" == "" call "%setenv%"
|
||||||
|
cmake %*
|
|
@ -0,0 +1,53 @@
|
||||||
|
#!/usr/bin/env python
|
||||||
|
# Compute 10 ** exp with exp in the range [min_exponent, max_exponent] and print
|
||||||
|
# normalized (with most-significant bit equal to 1) significands in hexadecimal.
|
||||||
|
|
||||||
|
from __future__ import print_function
|
||||||
|
|
||||||
|
min_exponent = -348
|
||||||
|
max_exponent = 340
|
||||||
|
step = 8
|
||||||
|
significand_size = 64
|
||||||
|
exp_offset = 2000
|
||||||
|
|
||||||
|
class fp:
|
||||||
|
pass
|
||||||
|
|
||||||
|
powers = []
|
||||||
|
for i, exp in enumerate(range(min_exponent, max_exponent + 1, step)):
|
||||||
|
result = fp()
|
||||||
|
n = 10 ** exp if exp >= 0 else 2 ** exp_offset / 10 ** -exp
|
||||||
|
k = significand_size + 1
|
||||||
|
# Convert to binary and round.
|
||||||
|
binary = '{:b}'.format(n)
|
||||||
|
result.f = (int('{:0<{}}'.format(binary[:k], k), 2) + 1) / 2
|
||||||
|
result.e = len(binary) - (exp_offset if exp < 0 else 0) - significand_size
|
||||||
|
powers.append(result)
|
||||||
|
# Sanity check.
|
||||||
|
exp_offset10 = 400
|
||||||
|
actual = result.f * 10 ** exp_offset10
|
||||||
|
if result.e > 0:
|
||||||
|
actual *= 2 ** result.e
|
||||||
|
else:
|
||||||
|
for j in range(-result.e):
|
||||||
|
actual /= 2
|
||||||
|
expected = 10 ** (exp_offset10 + exp)
|
||||||
|
precision = len('{}'.format(expected)) - len('{}'.format(actual - expected))
|
||||||
|
if precision < 19:
|
||||||
|
print('low precision:', precision)
|
||||||
|
exit(1)
|
||||||
|
|
||||||
|
print('Significands:', end='')
|
||||||
|
for i, fp in enumerate(powers):
|
||||||
|
if i % 3 == 0:
|
||||||
|
print(end='\n ')
|
||||||
|
print(' {:0<#16x}'.format(fp.f, ), end=',')
|
||||||
|
|
||||||
|
print('\n\nExponents:', end='')
|
||||||
|
for i, fp in enumerate(powers):
|
||||||
|
if i % 11 == 0:
|
||||||
|
print(end='\n ')
|
||||||
|
print(' {:5}'.format(fp.e), end=',')
|
||||||
|
|
||||||
|
print('\n\nMax exponent difference:',
|
||||||
|
max([x.e - powers[i - 1].e for i, x in enumerate(powers)][1:]))
|
|
@ -0,0 +1,581 @@
|
||||||
|
"""Pythonic command-line interface parser that will make you smile.
|
||||||
|
|
||||||
|
* http://docopt.org
|
||||||
|
* Repository and issue-tracker: https://github.com/docopt/docopt
|
||||||
|
* Licensed under terms of MIT license (see LICENSE-MIT)
|
||||||
|
* Copyright (c) 2013 Vladimir Keleshev, vladimir@keleshev.com
|
||||||
|
|
||||||
|
"""
|
||||||
|
import sys
|
||||||
|
import re
|
||||||
|
|
||||||
|
|
||||||
|
__all__ = ['docopt']
|
||||||
|
__version__ = '0.6.1'
|
||||||
|
|
||||||
|
|
||||||
|
class DocoptLanguageError(Exception):
|
||||||
|
|
||||||
|
"""Error in construction of usage-message by developer."""
|
||||||
|
|
||||||
|
|
||||||
|
class DocoptExit(SystemExit):
|
||||||
|
|
||||||
|
"""Exit in case user invoked program with incorrect arguments."""
|
||||||
|
|
||||||
|
usage = ''
|
||||||
|
|
||||||
|
def __init__(self, message=''):
|
||||||
|
SystemExit.__init__(self, (message + '\n' + self.usage).strip())
|
||||||
|
|
||||||
|
|
||||||
|
class Pattern(object):
|
||||||
|
|
||||||
|
def __eq__(self, other):
|
||||||
|
return repr(self) == repr(other)
|
||||||
|
|
||||||
|
def __hash__(self):
|
||||||
|
return hash(repr(self))
|
||||||
|
|
||||||
|
def fix(self):
|
||||||
|
self.fix_identities()
|
||||||
|
self.fix_repeating_arguments()
|
||||||
|
return self
|
||||||
|
|
||||||
|
def fix_identities(self, uniq=None):
|
||||||
|
"""Make pattern-tree tips point to same object if they are equal."""
|
||||||
|
if not hasattr(self, 'children'):
|
||||||
|
return self
|
||||||
|
uniq = list(set(self.flat())) if uniq is None else uniq
|
||||||
|
for i, child in enumerate(self.children):
|
||||||
|
if not hasattr(child, 'children'):
|
||||||
|
assert child in uniq
|
||||||
|
self.children[i] = uniq[uniq.index(child)]
|
||||||
|
else:
|
||||||
|
child.fix_identities(uniq)
|
||||||
|
|
||||||
|
def fix_repeating_arguments(self):
|
||||||
|
"""Fix elements that should accumulate/increment values."""
|
||||||
|
either = [list(child.children) for child in transform(self).children]
|
||||||
|
for case in either:
|
||||||
|
for e in [child for child in case if case.count(child) > 1]:
|
||||||
|
if type(e) is Argument or type(e) is Option and e.argcount:
|
||||||
|
if e.value is None:
|
||||||
|
e.value = []
|
||||||
|
elif type(e.value) is not list:
|
||||||
|
e.value = e.value.split()
|
||||||
|
if type(e) is Command or type(e) is Option and e.argcount == 0:
|
||||||
|
e.value = 0
|
||||||
|
return self
|
||||||
|
|
||||||
|
|
||||||
|
def transform(pattern):
|
||||||
|
"""Expand pattern into an (almost) equivalent one, but with single Either.
|
||||||
|
|
||||||
|
Example: ((-a | -b) (-c | -d)) => (-a -c | -a -d | -b -c | -b -d)
|
||||||
|
Quirks: [-a] => (-a), (-a...) => (-a -a)
|
||||||
|
|
||||||
|
"""
|
||||||
|
result = []
|
||||||
|
groups = [[pattern]]
|
||||||
|
while groups:
|
||||||
|
children = groups.pop(0)
|
||||||
|
parents = [Required, Optional, OptionsShortcut, Either, OneOrMore]
|
||||||
|
if any(t in map(type, children) for t in parents):
|
||||||
|
child = [c for c in children if type(c) in parents][0]
|
||||||
|
children.remove(child)
|
||||||
|
if type(child) is Either:
|
||||||
|
for c in child.children:
|
||||||
|
groups.append([c] + children)
|
||||||
|
elif type(child) is OneOrMore:
|
||||||
|
groups.append(child.children * 2 + children)
|
||||||
|
else:
|
||||||
|
groups.append(child.children + children)
|
||||||
|
else:
|
||||||
|
result.append(children)
|
||||||
|
return Either(*[Required(*e) for e in result])
|
||||||
|
|
||||||
|
|
||||||
|
class LeafPattern(Pattern):
|
||||||
|
|
||||||
|
"""Leaf/terminal node of a pattern tree."""
|
||||||
|
|
||||||
|
def __init__(self, name, value=None):
|
||||||
|
self.name, self.value = name, value
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return '%s(%r, %r)' % (self.__class__.__name__, self.name, self.value)
|
||||||
|
|
||||||
|
def flat(self, *types):
|
||||||
|
return [self] if not types or type(self) in types else []
|
||||||
|
|
||||||
|
def match(self, left, collected=None):
|
||||||
|
collected = [] if collected is None else collected
|
||||||
|
pos, match = self.single_match(left)
|
||||||
|
if match is None:
|
||||||
|
return False, left, collected
|
||||||
|
left_ = left[:pos] + left[pos + 1:]
|
||||||
|
same_name = [a for a in collected if a.name == self.name]
|
||||||
|
if type(self.value) in (int, list):
|
||||||
|
if type(self.value) is int:
|
||||||
|
increment = 1
|
||||||
|
else:
|
||||||
|
increment = ([match.value] if type(match.value) is str
|
||||||
|
else match.value)
|
||||||
|
if not same_name:
|
||||||
|
match.value = increment
|
||||||
|
return True, left_, collected + [match]
|
||||||
|
same_name[0].value += increment
|
||||||
|
return True, left_, collected
|
||||||
|
return True, left_, collected + [match]
|
||||||
|
|
||||||
|
|
||||||
|
class BranchPattern(Pattern):
|
||||||
|
|
||||||
|
"""Branch/inner node of a pattern tree."""
|
||||||
|
|
||||||
|
def __init__(self, *children):
|
||||||
|
self.children = list(children)
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return '%s(%s)' % (self.__class__.__name__,
|
||||||
|
', '.join(repr(a) for a in self.children))
|
||||||
|
|
||||||
|
def flat(self, *types):
|
||||||
|
if type(self) in types:
|
||||||
|
return [self]
|
||||||
|
return sum([child.flat(*types) for child in self.children], [])
|
||||||
|
|
||||||
|
|
||||||
|
class Argument(LeafPattern):
|
||||||
|
|
||||||
|
def single_match(self, left):
|
||||||
|
for n, pattern in enumerate(left):
|
||||||
|
if type(pattern) is Argument:
|
||||||
|
return n, Argument(self.name, pattern.value)
|
||||||
|
return None, None
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def parse(class_, source):
|
||||||
|
name = re.findall('(<\S*?>)', source)[0]
|
||||||
|
value = re.findall('\[default: (.*)\]', source, flags=re.I)
|
||||||
|
return class_(name, value[0] if value else None)
|
||||||
|
|
||||||
|
|
||||||
|
class Command(Argument):
|
||||||
|
|
||||||
|
def __init__(self, name, value=False):
|
||||||
|
self.name, self.value = name, value
|
||||||
|
|
||||||
|
def single_match(self, left):
|
||||||
|
for n, pattern in enumerate(left):
|
||||||
|
if type(pattern) is Argument:
|
||||||
|
if pattern.value == self.name:
|
||||||
|
return n, Command(self.name, True)
|
||||||
|
else:
|
||||||
|
break
|
||||||
|
return None, None
|
||||||
|
|
||||||
|
|
||||||
|
class Option(LeafPattern):
|
||||||
|
|
||||||
|
def __init__(self, short=None, long=None, argcount=0, value=False):
|
||||||
|
assert argcount in (0, 1)
|
||||||
|
self.short, self.long, self.argcount = short, long, argcount
|
||||||
|
self.value = None if value is False and argcount else value
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def parse(class_, option_description):
|
||||||
|
short, long, argcount, value = None, None, 0, False
|
||||||
|
options, _, description = option_description.strip().partition(' ')
|
||||||
|
options = options.replace(',', ' ').replace('=', ' ')
|
||||||
|
for s in options.split():
|
||||||
|
if s.startswith('--'):
|
||||||
|
long = s
|
||||||
|
elif s.startswith('-'):
|
||||||
|
short = s
|
||||||
|
else:
|
||||||
|
argcount = 1
|
||||||
|
if argcount:
|
||||||
|
matched = re.findall('\[default: (.*)\]', description, flags=re.I)
|
||||||
|
value = matched[0] if matched else None
|
||||||
|
return class_(short, long, argcount, value)
|
||||||
|
|
||||||
|
def single_match(self, left):
|
||||||
|
for n, pattern in enumerate(left):
|
||||||
|
if self.name == pattern.name:
|
||||||
|
return n, pattern
|
||||||
|
return None, None
|
||||||
|
|
||||||
|
@property
|
||||||
|
def name(self):
|
||||||
|
return self.long or self.short
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return 'Option(%r, %r, %r, %r)' % (self.short, self.long,
|
||||||
|
self.argcount, self.value)
|
||||||
|
|
||||||
|
|
||||||
|
class Required(BranchPattern):
|
||||||
|
|
||||||
|
def match(self, left, collected=None):
|
||||||
|
collected = [] if collected is None else collected
|
||||||
|
l = left
|
||||||
|
c = collected
|
||||||
|
for pattern in self.children:
|
||||||
|
matched, l, c = pattern.match(l, c)
|
||||||
|
if not matched:
|
||||||
|
return False, left, collected
|
||||||
|
return True, l, c
|
||||||
|
|
||||||
|
|
||||||
|
class Optional(BranchPattern):
|
||||||
|
|
||||||
|
def match(self, left, collected=None):
|
||||||
|
collected = [] if collected is None else collected
|
||||||
|
for pattern in self.children:
|
||||||
|
m, left, collected = pattern.match(left, collected)
|
||||||
|
return True, left, collected
|
||||||
|
|
||||||
|
|
||||||
|
class OptionsShortcut(Optional):
|
||||||
|
|
||||||
|
"""Marker/placeholder for [options] shortcut."""
|
||||||
|
|
||||||
|
|
||||||
|
class OneOrMore(BranchPattern):
|
||||||
|
|
||||||
|
def match(self, left, collected=None):
|
||||||
|
assert len(self.children) == 1
|
||||||
|
collected = [] if collected is None else collected
|
||||||
|
l = left
|
||||||
|
c = collected
|
||||||
|
l_ = None
|
||||||
|
matched = True
|
||||||
|
times = 0
|
||||||
|
while matched:
|
||||||
|
# could it be that something didn't match but changed l or c?
|
||||||
|
matched, l, c = self.children[0].match(l, c)
|
||||||
|
times += 1 if matched else 0
|
||||||
|
if l_ == l:
|
||||||
|
break
|
||||||
|
l_ = l
|
||||||
|
if times >= 1:
|
||||||
|
return True, l, c
|
||||||
|
return False, left, collected
|
||||||
|
|
||||||
|
|
||||||
|
class Either(BranchPattern):
|
||||||
|
|
||||||
|
def match(self, left, collected=None):
|
||||||
|
collected = [] if collected is None else collected
|
||||||
|
outcomes = []
|
||||||
|
for pattern in self.children:
|
||||||
|
matched, _, _ = outcome = pattern.match(left, collected)
|
||||||
|
if matched:
|
||||||
|
outcomes.append(outcome)
|
||||||
|
if outcomes:
|
||||||
|
return min(outcomes, key=lambda outcome: len(outcome[1]))
|
||||||
|
return False, left, collected
|
||||||
|
|
||||||
|
|
||||||
|
class Tokens(list):
|
||||||
|
|
||||||
|
def __init__(self, source, error=DocoptExit):
|
||||||
|
self += source.split() if hasattr(source, 'split') else source
|
||||||
|
self.error = error
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def from_pattern(source):
|
||||||
|
source = re.sub(r'([\[\]\(\)\|]|\.\.\.)', r' \1 ', source)
|
||||||
|
source = [s for s in re.split('\s+|(\S*<.*?>)', source) if s]
|
||||||
|
return Tokens(source, error=DocoptLanguageError)
|
||||||
|
|
||||||
|
def move(self):
|
||||||
|
return self.pop(0) if len(self) else None
|
||||||
|
|
||||||
|
def current(self):
|
||||||
|
return self[0] if len(self) else None
|
||||||
|
|
||||||
|
|
||||||
|
def parse_long(tokens, options):
|
||||||
|
"""long ::= '--' chars [ ( ' ' | '=' ) chars ] ;"""
|
||||||
|
long, eq, value = tokens.move().partition('=')
|
||||||
|
assert long.startswith('--')
|
||||||
|
value = None if eq == value == '' else value
|
||||||
|
similar = [o for o in options if o.long == long]
|
||||||
|
if tokens.error is DocoptExit and similar == []: # if no exact match
|
||||||
|
similar = [o for o in options if o.long and o.long.startswith(long)]
|
||||||
|
if len(similar) > 1: # might be simply specified ambiguously 2+ times?
|
||||||
|
raise tokens.error('%s is not a unique prefix: %s?' %
|
||||||
|
(long, ', '.join(o.long for o in similar)))
|
||||||
|
elif len(similar) < 1:
|
||||||
|
argcount = 1 if eq == '=' else 0
|
||||||
|
o = Option(None, long, argcount)
|
||||||
|
options.append(o)
|
||||||
|
if tokens.error is DocoptExit:
|
||||||
|
o = Option(None, long, argcount, value if argcount else True)
|
||||||
|
else:
|
||||||
|
o = Option(similar[0].short, similar[0].long,
|
||||||
|
similar[0].argcount, similar[0].value)
|
||||||
|
if o.argcount == 0:
|
||||||
|
if value is not None:
|
||||||
|
raise tokens.error('%s must not have an argument' % o.long)
|
||||||
|
else:
|
||||||
|
if value is None:
|
||||||
|
if tokens.current() in [None, '--']:
|
||||||
|
raise tokens.error('%s requires argument' % o.long)
|
||||||
|
value = tokens.move()
|
||||||
|
if tokens.error is DocoptExit:
|
||||||
|
o.value = value if value is not None else True
|
||||||
|
return [o]
|
||||||
|
|
||||||
|
|
||||||
|
def parse_shorts(tokens, options):
|
||||||
|
"""shorts ::= '-' ( chars )* [ [ ' ' ] chars ] ;"""
|
||||||
|
token = tokens.move()
|
||||||
|
assert token.startswith('-') and not token.startswith('--')
|
||||||
|
left = token.lstrip('-')
|
||||||
|
parsed = []
|
||||||
|
while left != '':
|
||||||
|
short, left = '-' + left[0], left[1:]
|
||||||
|
similar = [o for o in options if o.short == short]
|
||||||
|
if len(similar) > 1:
|
||||||
|
raise tokens.error('%s is specified ambiguously %d times' %
|
||||||
|
(short, len(similar)))
|
||||||
|
elif len(similar) < 1:
|
||||||
|
o = Option(short, None, 0)
|
||||||
|
options.append(o)
|
||||||
|
if tokens.error is DocoptExit:
|
||||||
|
o = Option(short, None, 0, True)
|
||||||
|
else: # why copying is necessary here?
|
||||||
|
o = Option(short, similar[0].long,
|
||||||
|
similar[0].argcount, similar[0].value)
|
||||||
|
value = None
|
||||||
|
if o.argcount != 0:
|
||||||
|
if left == '':
|
||||||
|
if tokens.current() in [None, '--']:
|
||||||
|
raise tokens.error('%s requires argument' % short)
|
||||||
|
value = tokens.move()
|
||||||
|
else:
|
||||||
|
value = left
|
||||||
|
left = ''
|
||||||
|
if tokens.error is DocoptExit:
|
||||||
|
o.value = value if value is not None else True
|
||||||
|
parsed.append(o)
|
||||||
|
return parsed
|
||||||
|
|
||||||
|
|
||||||
|
def parse_pattern(source, options):
|
||||||
|
tokens = Tokens.from_pattern(source)
|
||||||
|
result = parse_expr(tokens, options)
|
||||||
|
if tokens.current() is not None:
|
||||||
|
raise tokens.error('unexpected ending: %r' % ' '.join(tokens))
|
||||||
|
return Required(*result)
|
||||||
|
|
||||||
|
|
||||||
|
def parse_expr(tokens, options):
|
||||||
|
"""expr ::= seq ( '|' seq )* ;"""
|
||||||
|
seq = parse_seq(tokens, options)
|
||||||
|
if tokens.current() != '|':
|
||||||
|
return seq
|
||||||
|
result = [Required(*seq)] if len(seq) > 1 else seq
|
||||||
|
while tokens.current() == '|':
|
||||||
|
tokens.move()
|
||||||
|
seq = parse_seq(tokens, options)
|
||||||
|
result += [Required(*seq)] if len(seq) > 1 else seq
|
||||||
|
return [Either(*result)] if len(result) > 1 else result
|
||||||
|
|
||||||
|
|
||||||
|
def parse_seq(tokens, options):
|
||||||
|
"""seq ::= ( atom [ '...' ] )* ;"""
|
||||||
|
result = []
|
||||||
|
while tokens.current() not in [None, ']', ')', '|']:
|
||||||
|
atom = parse_atom(tokens, options)
|
||||||
|
if tokens.current() == '...':
|
||||||
|
atom = [OneOrMore(*atom)]
|
||||||
|
tokens.move()
|
||||||
|
result += atom
|
||||||
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
def parse_atom(tokens, options):
|
||||||
|
"""atom ::= '(' expr ')' | '[' expr ']' | 'options'
|
||||||
|
| long | shorts | argument | command ;
|
||||||
|
"""
|
||||||
|
token = tokens.current()
|
||||||
|
result = []
|
||||||
|
if token in '([':
|
||||||
|
tokens.move()
|
||||||
|
matching, pattern = {'(': [')', Required], '[': [']', Optional]}[token]
|
||||||
|
result = pattern(*parse_expr(tokens, options))
|
||||||
|
if tokens.move() != matching:
|
||||||
|
raise tokens.error("unmatched '%s'" % token)
|
||||||
|
return [result]
|
||||||
|
elif token == 'options':
|
||||||
|
tokens.move()
|
||||||
|
return [OptionsShortcut()]
|
||||||
|
elif token.startswith('--') and token != '--':
|
||||||
|
return parse_long(tokens, options)
|
||||||
|
elif token.startswith('-') and token not in ('-', '--'):
|
||||||
|
return parse_shorts(tokens, options)
|
||||||
|
elif token.startswith('<') and token.endswith('>') or token.isupper():
|
||||||
|
return [Argument(tokens.move())]
|
||||||
|
else:
|
||||||
|
return [Command(tokens.move())]
|
||||||
|
|
||||||
|
|
||||||
|
def parse_argv(tokens, options, options_first=False):
|
||||||
|
"""Parse command-line argument vector.
|
||||||
|
|
||||||
|
If options_first:
|
||||||
|
argv ::= [ long | shorts ]* [ argument ]* [ '--' [ argument ]* ] ;
|
||||||
|
else:
|
||||||
|
argv ::= [ long | shorts | argument ]* [ '--' [ argument ]* ] ;
|
||||||
|
|
||||||
|
"""
|
||||||
|
parsed = []
|
||||||
|
while tokens.current() is not None:
|
||||||
|
if tokens.current() == '--':
|
||||||
|
return parsed + [Argument(None, v) for v in tokens]
|
||||||
|
elif tokens.current().startswith('--'):
|
||||||
|
parsed += parse_long(tokens, options)
|
||||||
|
elif tokens.current().startswith('-') and tokens.current() != '-':
|
||||||
|
parsed += parse_shorts(tokens, options)
|
||||||
|
elif options_first:
|
||||||
|
return parsed + [Argument(None, v) for v in tokens]
|
||||||
|
else:
|
||||||
|
parsed.append(Argument(None, tokens.move()))
|
||||||
|
return parsed
|
||||||
|
|
||||||
|
|
||||||
|
def parse_defaults(doc):
|
||||||
|
defaults = []
|
||||||
|
for s in parse_section('options:', doc):
|
||||||
|
# FIXME corner case "bla: options: --foo"
|
||||||
|
_, _, s = s.partition(':') # get rid of "options:"
|
||||||
|
split = re.split('\n[ \t]*(-\S+?)', '\n' + s)[1:]
|
||||||
|
split = [s1 + s2 for s1, s2 in zip(split[::2], split[1::2])]
|
||||||
|
options = [Option.parse(s) for s in split if s.startswith('-')]
|
||||||
|
defaults += options
|
||||||
|
return defaults
|
||||||
|
|
||||||
|
|
||||||
|
def parse_section(name, source):
|
||||||
|
pattern = re.compile('^([^\n]*' + name + '[^\n]*\n?(?:[ \t].*?(?:\n|$))*)',
|
||||||
|
re.IGNORECASE | re.MULTILINE)
|
||||||
|
return [s.strip() for s in pattern.findall(source)]
|
||||||
|
|
||||||
|
|
||||||
|
def formal_usage(section):
|
||||||
|
_, _, section = section.partition(':') # drop "usage:"
|
||||||
|
pu = section.split()
|
||||||
|
return '( ' + ' '.join(') | (' if s == pu[0] else s for s in pu[1:]) + ' )'
|
||||||
|
|
||||||
|
|
||||||
|
def extras(help, version, options, doc):
|
||||||
|
if help and any((o.name in ('-h', '--help')) and o.value for o in options):
|
||||||
|
print(doc.strip("\n"))
|
||||||
|
sys.exit()
|
||||||
|
if version and any(o.name == '--version' and o.value for o in options):
|
||||||
|
print(version)
|
||||||
|
sys.exit()
|
||||||
|
|
||||||
|
|
||||||
|
class Dict(dict):
|
||||||
|
def __repr__(self):
|
||||||
|
return '{%s}' % ',\n '.join('%r: %r' % i for i in sorted(self.items()))
|
||||||
|
|
||||||
|
|
||||||
|
def docopt(doc, argv=None, help=True, version=None, options_first=False):
|
||||||
|
"""Parse `argv` based on command-line interface described in `doc`.
|
||||||
|
|
||||||
|
`docopt` creates your command-line interface based on its
|
||||||
|
description that you pass as `doc`. Such description can contain
|
||||||
|
--options, <positional-argument>, commands, which could be
|
||||||
|
[optional], (required), (mutually | exclusive) or repeated...
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
doc : str
|
||||||
|
Description of your command-line interface.
|
||||||
|
argv : list of str, optional
|
||||||
|
Argument vector to be parsed. sys.argv[1:] is used if not
|
||||||
|
provided.
|
||||||
|
help : bool (default: True)
|
||||||
|
Set to False to disable automatic help on -h or --help
|
||||||
|
options.
|
||||||
|
version : any object
|
||||||
|
If passed, the object will be printed if --version is in
|
||||||
|
`argv`.
|
||||||
|
options_first : bool (default: False)
|
||||||
|
Set to True to require options precede positional arguments,
|
||||||
|
i.e. to forbid options and positional arguments intermix.
|
||||||
|
|
||||||
|
Returns
|
||||||
|
-------
|
||||||
|
args : dict
|
||||||
|
A dictionary, where keys are names of command-line elements
|
||||||
|
such as e.g. "--verbose" and "<path>", and values are the
|
||||||
|
parsed values of those elements.
|
||||||
|
|
||||||
|
Example
|
||||||
|
-------
|
||||||
|
>>> from docopt import docopt
|
||||||
|
>>> doc = '''
|
||||||
|
... Usage:
|
||||||
|
... my_program tcp <host> <port> [--timeout=<seconds>]
|
||||||
|
... my_program serial <port> [--baud=<n>] [--timeout=<seconds>]
|
||||||
|
... my_program (-h | --help | --version)
|
||||||
|
...
|
||||||
|
... Options:
|
||||||
|
... -h, --help Show this screen and exit.
|
||||||
|
... --baud=<n> Baudrate [default: 9600]
|
||||||
|
... '''
|
||||||
|
>>> argv = ['tcp', '127.0.0.1', '80', '--timeout', '30']
|
||||||
|
>>> docopt(doc, argv)
|
||||||
|
{'--baud': '9600',
|
||||||
|
'--help': False,
|
||||||
|
'--timeout': '30',
|
||||||
|
'--version': False,
|
||||||
|
'<host>': '127.0.0.1',
|
||||||
|
'<port>': '80',
|
||||||
|
'serial': False,
|
||||||
|
'tcp': True}
|
||||||
|
|
||||||
|
See also
|
||||||
|
--------
|
||||||
|
* For video introduction see http://docopt.org
|
||||||
|
* Full documentation is available in README.rst as well as online
|
||||||
|
at https://github.com/docopt/docopt#readme
|
||||||
|
|
||||||
|
"""
|
||||||
|
argv = sys.argv[1:] if argv is None else argv
|
||||||
|
|
||||||
|
usage_sections = parse_section('usage:', doc)
|
||||||
|
if len(usage_sections) == 0:
|
||||||
|
raise DocoptLanguageError('"usage:" (case-insensitive) not found.')
|
||||||
|
if len(usage_sections) > 1:
|
||||||
|
raise DocoptLanguageError('More than one "usage:" (case-insensitive).')
|
||||||
|
DocoptExit.usage = usage_sections[0]
|
||||||
|
|
||||||
|
options = parse_defaults(doc)
|
||||||
|
pattern = parse_pattern(formal_usage(DocoptExit.usage), options)
|
||||||
|
# [default] syntax for argument is disabled
|
||||||
|
#for a in pattern.flat(Argument):
|
||||||
|
# same_name = [d for d in arguments if d.name == a.name]
|
||||||
|
# if same_name:
|
||||||
|
# a.value = same_name[0].value
|
||||||
|
argv = parse_argv(Tokens(argv), list(options), options_first)
|
||||||
|
pattern_options = set(pattern.flat(Option))
|
||||||
|
for options_shortcut in pattern.flat(OptionsShortcut):
|
||||||
|
doc_options = parse_defaults(doc)
|
||||||
|
options_shortcut.children = list(set(doc_options) - pattern_options)
|
||||||
|
#if any_options:
|
||||||
|
# options_shortcut.children += [Option(o.short, o.long, o.argcount)
|
||||||
|
# for o in argv if type(o) is Option]
|
||||||
|
extras(help, version, argv, doc)
|
||||||
|
matched, left, collected = pattern.fix().match(argv)
|
||||||
|
if matched and left == []: # better error message if left?
|
||||||
|
return Dict((a.name, a.value) for a in (pattern.flat() + collected))
|
||||||
|
raise DocoptExit()
|
|
@ -0,0 +1,27 @@
|
||||||
|
# Staticlib configuration for qmake builds
|
||||||
|
# For some reason qmake 3.1 fails to identify source dependencies and excludes format.cc and printf.cc
|
||||||
|
# from compilation so it _MUST_ be called as qmake -nodepend
|
||||||
|
# A workaround is implemented below: a custom compiler is defined which does not track dependencies
|
||||||
|
|
||||||
|
TEMPLATE = lib
|
||||||
|
|
||||||
|
TARGET = fmt
|
||||||
|
|
||||||
|
QMAKE_EXT_CPP = .cc
|
||||||
|
|
||||||
|
CONFIG = staticlib warn_on c++11
|
||||||
|
|
||||||
|
FMT_SOURCES = \
|
||||||
|
../src/format.cc \
|
||||||
|
../src/posix.cc
|
||||||
|
|
||||||
|
fmt.name = libfmt
|
||||||
|
fmt.input = FMT_SOURCES
|
||||||
|
fmt.output = ${QMAKE_FILE_BASE}$$QMAKE_EXT_OBJ
|
||||||
|
fmt.clean = ${QMAKE_FILE_BASE}$$QMAKE_EXT_OBJ
|
||||||
|
fmt.depends = ${QMAKE_FILE_IN}
|
||||||
|
# QMAKE_RUN_CXX will not be expanded
|
||||||
|
fmt.commands = $$QMAKE_CXX -c $$QMAKE_CXXFLAGS $$QMAKE_CXXFLAGS_WARN_ON $$QMAKE_CXXFLAGS_RELEASE_WITH_DEBUGINFO $$QMAKE_CXXFLAGS_CXX11 ${QMAKE_FILE_IN}
|
||||||
|
fmt.variable_out = OBJECTS
|
||||||
|
fmt.CONFIG = no_dependencies no_link
|
||||||
|
QMAKE_EXTRA_COMPILERS += fmt
|
|
@ -0,0 +1,261 @@
|
||||||
|
#!/usr/bin/env python
|
||||||
|
|
||||||
|
"""Manage site and releases.
|
||||||
|
|
||||||
|
Usage:
|
||||||
|
manage.py release [<branch>]
|
||||||
|
manage.py site
|
||||||
|
"""
|
||||||
|
|
||||||
|
from __future__ import print_function
|
||||||
|
import datetime, docopt, errno, fileinput, json, os
|
||||||
|
import re, requests, shutil, sys, tempfile
|
||||||
|
from contextlib import contextmanager
|
||||||
|
from distutils.version import LooseVersion
|
||||||
|
from subprocess import check_call
|
||||||
|
|
||||||
|
|
||||||
|
class Git:
|
||||||
|
def __init__(self, dir):
|
||||||
|
self.dir = dir
|
||||||
|
|
||||||
|
def call(self, method, args, **kwargs):
|
||||||
|
return check_call(['git', method] + list(args), **kwargs)
|
||||||
|
|
||||||
|
def add(self, *args):
|
||||||
|
return self.call('add', args, cwd=self.dir)
|
||||||
|
|
||||||
|
def checkout(self, *args):
|
||||||
|
return self.call('checkout', args, cwd=self.dir)
|
||||||
|
|
||||||
|
def clean(self, *args):
|
||||||
|
return self.call('clean', args, cwd=self.dir)
|
||||||
|
|
||||||
|
def clone(self, *args):
|
||||||
|
return self.call('clone', list(args) + [self.dir])
|
||||||
|
|
||||||
|
def commit(self, *args):
|
||||||
|
return self.call('commit', args, cwd=self.dir)
|
||||||
|
|
||||||
|
def pull(self, *args):
|
||||||
|
return self.call('pull', args, cwd=self.dir)
|
||||||
|
|
||||||
|
def push(self, *args):
|
||||||
|
return self.call('push', args, cwd=self.dir)
|
||||||
|
|
||||||
|
def reset(self, *args):
|
||||||
|
return self.call('reset', args, cwd=self.dir)
|
||||||
|
|
||||||
|
def update(self, *args):
|
||||||
|
clone = not os.path.exists(self.dir)
|
||||||
|
if clone:
|
||||||
|
self.clone(*args)
|
||||||
|
return clone
|
||||||
|
|
||||||
|
|
||||||
|
def clean_checkout(repo, branch):
|
||||||
|
repo.clean('-f', '-d')
|
||||||
|
repo.reset('--hard')
|
||||||
|
repo.checkout(branch)
|
||||||
|
|
||||||
|
|
||||||
|
class Runner:
|
||||||
|
def __init__(self, cwd):
|
||||||
|
self.cwd = cwd
|
||||||
|
|
||||||
|
def __call__(self, *args, **kwargs):
|
||||||
|
kwargs['cwd'] = kwargs.get('cwd', self.cwd)
|
||||||
|
check_call(args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
def create_build_env():
|
||||||
|
"""Create a build environment."""
|
||||||
|
class Env:
|
||||||
|
pass
|
||||||
|
env = Env()
|
||||||
|
|
||||||
|
# Import the documentation build module.
|
||||||
|
env.fmt_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
||||||
|
sys.path.insert(0, os.path.join(env.fmt_dir, 'doc'))
|
||||||
|
import build
|
||||||
|
|
||||||
|
env.build_dir = 'build'
|
||||||
|
env.versions = build.versions
|
||||||
|
|
||||||
|
# Virtualenv and repos are cached to speed up builds.
|
||||||
|
build.create_build_env(os.path.join(env.build_dir, 'virtualenv'))
|
||||||
|
|
||||||
|
env.fmt_repo = Git(os.path.join(env.build_dir, 'fmt'))
|
||||||
|
return env
|
||||||
|
|
||||||
|
|
||||||
|
@contextmanager
|
||||||
|
def rewrite(filename):
|
||||||
|
class Buffer:
|
||||||
|
pass
|
||||||
|
buffer = Buffer()
|
||||||
|
if not os.path.exists(filename):
|
||||||
|
buffer.data = ''
|
||||||
|
yield buffer
|
||||||
|
return
|
||||||
|
with open(filename) as f:
|
||||||
|
buffer.data = f.read()
|
||||||
|
yield buffer
|
||||||
|
with open(filename, 'w') as f:
|
||||||
|
f.write(buffer.data)
|
||||||
|
|
||||||
|
|
||||||
|
fmt_repo_url = 'git@github.com:fmtlib/fmt'
|
||||||
|
|
||||||
|
|
||||||
|
def update_site(env):
|
||||||
|
env.fmt_repo.update(fmt_repo_url)
|
||||||
|
|
||||||
|
doc_repo = Git(os.path.join(env.build_dir, 'fmtlib.github.io'))
|
||||||
|
doc_repo.update('git@github.com:fmtlib/fmtlib.github.io')
|
||||||
|
|
||||||
|
for version in env.versions:
|
||||||
|
clean_checkout(env.fmt_repo, version)
|
||||||
|
target_doc_dir = os.path.join(env.fmt_repo.dir, 'doc')
|
||||||
|
# Remove the old theme.
|
||||||
|
for entry in os.listdir(target_doc_dir):
|
||||||
|
path = os.path.join(target_doc_dir, entry)
|
||||||
|
if os.path.isdir(path):
|
||||||
|
shutil.rmtree(path)
|
||||||
|
# Copy the new theme.
|
||||||
|
for entry in ['_static', '_templates', 'basic-bootstrap', 'bootstrap',
|
||||||
|
'conf.py', 'fmt.less']:
|
||||||
|
src = os.path.join(env.fmt_dir, 'doc', entry)
|
||||||
|
dst = os.path.join(target_doc_dir, entry)
|
||||||
|
copy = shutil.copytree if os.path.isdir(src) else shutil.copyfile
|
||||||
|
copy(src, dst)
|
||||||
|
# Rename index to contents.
|
||||||
|
contents = os.path.join(target_doc_dir, 'contents.rst')
|
||||||
|
if not os.path.exists(contents):
|
||||||
|
os.rename(os.path.join(target_doc_dir, 'index.rst'), contents)
|
||||||
|
# Fix issues in reference.rst/api.rst.
|
||||||
|
for filename in ['reference.rst', 'api.rst']:
|
||||||
|
pattern = re.compile('doxygenfunction.. (bin|oct|hexu|hex)$', re.M)
|
||||||
|
with rewrite(os.path.join(target_doc_dir, filename)) as b:
|
||||||
|
b.data = b.data.replace('std::ostream &', 'std::ostream&')
|
||||||
|
b.data = re.sub(pattern, r'doxygenfunction:: \1(int)', b.data)
|
||||||
|
b.data = b.data.replace('std::FILE*', 'std::FILE *')
|
||||||
|
b.data = b.data.replace('unsigned int', 'unsigned')
|
||||||
|
b.data = b.data.replace('operator""_', 'operator"" _')
|
||||||
|
# Fix a broken link in index.rst.
|
||||||
|
index = os.path.join(target_doc_dir, 'index.rst')
|
||||||
|
with rewrite(index) as b:
|
||||||
|
b.data = b.data.replace(
|
||||||
|
'doc/latest/index.html#format-string-syntax', 'syntax.html')
|
||||||
|
# Build the docs.
|
||||||
|
html_dir = os.path.join(env.build_dir, 'html')
|
||||||
|
if os.path.exists(html_dir):
|
||||||
|
shutil.rmtree(html_dir)
|
||||||
|
include_dir = env.fmt_repo.dir
|
||||||
|
if LooseVersion(version) >= LooseVersion('5.0.0'):
|
||||||
|
include_dir = os.path.join(include_dir, 'include', 'fmt')
|
||||||
|
elif LooseVersion(version) >= LooseVersion('3.0.0'):
|
||||||
|
include_dir = os.path.join(include_dir, 'fmt')
|
||||||
|
import build
|
||||||
|
build.build_docs(version, doc_dir=target_doc_dir,
|
||||||
|
include_dir=include_dir, work_dir=env.build_dir)
|
||||||
|
shutil.rmtree(os.path.join(html_dir, '.doctrees'))
|
||||||
|
# Create symlinks for older versions.
|
||||||
|
for link, target in {'index': 'contents', 'api': 'reference'}.items():
|
||||||
|
link = os.path.join(html_dir, link) + '.html'
|
||||||
|
target += '.html'
|
||||||
|
if os.path.exists(os.path.join(html_dir, target)) and \
|
||||||
|
not os.path.exists(link):
|
||||||
|
os.symlink(target, link)
|
||||||
|
# Copy docs to the website.
|
||||||
|
version_doc_dir = os.path.join(doc_repo.dir, version)
|
||||||
|
try:
|
||||||
|
shutil.rmtree(version_doc_dir)
|
||||||
|
except OSError as e:
|
||||||
|
if e.errno != errno.ENOENT:
|
||||||
|
raise
|
||||||
|
shutil.move(html_dir, version_doc_dir)
|
||||||
|
|
||||||
|
|
||||||
|
def release(args):
|
||||||
|
env = create_build_env()
|
||||||
|
fmt_repo = env.fmt_repo
|
||||||
|
|
||||||
|
branch = args.get('<branch>')
|
||||||
|
if branch is None:
|
||||||
|
branch = 'master'
|
||||||
|
if not fmt_repo.update('-b', branch, fmt_repo_url):
|
||||||
|
clean_checkout(fmt_repo, branch)
|
||||||
|
|
||||||
|
# Convert changelog from RST to GitHub-flavored Markdown and get the
|
||||||
|
# version.
|
||||||
|
changelog = 'ChangeLog.rst'
|
||||||
|
changelog_path = os.path.join(fmt_repo.dir, changelog)
|
||||||
|
import rst2md
|
||||||
|
changes, version = rst2md.convert(changelog_path)
|
||||||
|
cmakelists = 'CMakeLists.txt'
|
||||||
|
for line in fileinput.input(os.path.join(fmt_repo.dir, cmakelists),
|
||||||
|
inplace=True):
|
||||||
|
prefix = 'set(FMT_VERSION '
|
||||||
|
if line.startswith(prefix):
|
||||||
|
line = prefix + version + ')\n'
|
||||||
|
sys.stdout.write(line)
|
||||||
|
|
||||||
|
# Update the version in the changelog.
|
||||||
|
title_len = 0
|
||||||
|
for line in fileinput.input(changelog_path, inplace=True):
|
||||||
|
if line.decode('utf-8').startswith(version + ' - TBD'):
|
||||||
|
line = version + ' - ' + datetime.date.today().isoformat()
|
||||||
|
title_len = len(line)
|
||||||
|
line += '\n'
|
||||||
|
elif title_len:
|
||||||
|
line = '-' * title_len + '\n'
|
||||||
|
title_len = 0
|
||||||
|
sys.stdout.write(line)
|
||||||
|
|
||||||
|
# Add the version to the build script.
|
||||||
|
script = os.path.join('doc', 'build.py')
|
||||||
|
script_path = os.path.join(fmt_repo.dir, script)
|
||||||
|
for line in fileinput.input(script_path, inplace=True):
|
||||||
|
m = re.match(r'( *versions = )\[(.+)\]', line)
|
||||||
|
if m:
|
||||||
|
line = '{}[{}, \'{}\']\n'.format(m.group(1), m.group(2), version)
|
||||||
|
sys.stdout.write(line)
|
||||||
|
|
||||||
|
fmt_repo.checkout('-B', 'release')
|
||||||
|
fmt_repo.add(changelog, cmakelists, script)
|
||||||
|
fmt_repo.commit('-m', 'Update version')
|
||||||
|
|
||||||
|
# Build the docs and package.
|
||||||
|
run = Runner(fmt_repo.dir)
|
||||||
|
run('cmake', '.')
|
||||||
|
run('make', 'doc', 'package_source')
|
||||||
|
update_site(env)
|
||||||
|
|
||||||
|
# Create a release on GitHub.
|
||||||
|
fmt_repo.push('origin', 'release')
|
||||||
|
params = {'access_token': os.getenv('FMT_TOKEN')}
|
||||||
|
r = requests.post('https://api.github.com/repos/fmtlib/fmt/releases',
|
||||||
|
params=params,
|
||||||
|
data=json.dumps({'tag_name': version,
|
||||||
|
'target_commitish': 'release',
|
||||||
|
'body': changes, 'draft': True}))
|
||||||
|
if r.status_code != 201:
|
||||||
|
raise Exception('Failed to create a release ' + str(r))
|
||||||
|
id = r.json()['id']
|
||||||
|
uploads_url = 'https://uploads.github.com/repos/fmtlib/fmt/releases'
|
||||||
|
package = 'fmt-{}.zip'.format(version)
|
||||||
|
r = requests.post(
|
||||||
|
'{}/{}/assets?name={}'.format(uploads_url, id, package),
|
||||||
|
headers={'Content-Type': 'application/zip'},
|
||||||
|
params=params, data=open('build/fmt/' + package, 'rb'))
|
||||||
|
if r.status_code != 201:
|
||||||
|
raise Exception('Failed to upload an asset ' + str(r))
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
args = docopt.docopt(__doc__)
|
||||||
|
if args.get('release'):
|
||||||
|
release(args)
|
||||||
|
elif args.get('site'):
|
||||||
|
update_site(create_build_env())
|
|
@ -0,0 +1,159 @@
|
||||||
|
#!/usr/bin/env python
|
||||||
|
# reStructuredText (RST) to GitHub-flavored Markdown converter
|
||||||
|
|
||||||
|
import re, sys
|
||||||
|
from docutils import core, nodes, writers
|
||||||
|
|
||||||
|
|
||||||
|
def is_github_ref(node):
|
||||||
|
return re.match('https://github.com/.*/(issues|pull)/.*', node['refuri'])
|
||||||
|
|
||||||
|
|
||||||
|
class Translator(nodes.NodeVisitor):
|
||||||
|
def __init__(self, document):
|
||||||
|
nodes.NodeVisitor.__init__(self, document)
|
||||||
|
self.output = ''
|
||||||
|
self.indent = 0
|
||||||
|
self.preserve_newlines = False
|
||||||
|
|
||||||
|
def write(self, text):
|
||||||
|
self.output += text.replace('\n', '\n' + ' ' * self.indent)
|
||||||
|
|
||||||
|
def visit_document(self, node):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def depart_document(self, node):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def visit_section(self, node):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def depart_section(self, node):
|
||||||
|
# Skip all sections except the first one.
|
||||||
|
raise nodes.StopTraversal
|
||||||
|
|
||||||
|
def visit_title(self, node):
|
||||||
|
self.version = re.match(r'(\d+\.\d+\.\d+).*', node.children[0]).group(1)
|
||||||
|
raise nodes.SkipChildren
|
||||||
|
|
||||||
|
def visit_title_reference(self, node):
|
||||||
|
raise Exception(node)
|
||||||
|
|
||||||
|
def depart_title(self, node):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def visit_Text(self, node):
|
||||||
|
if not self.preserve_newlines:
|
||||||
|
node = node.replace('\n', ' ')
|
||||||
|
self.write(node)
|
||||||
|
|
||||||
|
def depart_Text(self, node):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def visit_bullet_list(self, node):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def depart_bullet_list(self, node):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def visit_list_item(self, node):
|
||||||
|
self.write('* ')
|
||||||
|
self.indent += 2
|
||||||
|
|
||||||
|
def depart_list_item(self, node):
|
||||||
|
self.indent -= 2
|
||||||
|
self.write('\n\n')
|
||||||
|
|
||||||
|
def visit_paragraph(self, node):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def depart_paragraph(self, node):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def visit_reference(self, node):
|
||||||
|
if not is_github_ref(node):
|
||||||
|
self.write('[')
|
||||||
|
|
||||||
|
def depart_reference(self, node):
|
||||||
|
if not is_github_ref(node):
|
||||||
|
self.write('](' + node['refuri'] + ')')
|
||||||
|
|
||||||
|
def visit_target(self, node):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def depart_target(self, node):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def visit_literal(self, node):
|
||||||
|
self.write('`')
|
||||||
|
|
||||||
|
def depart_literal(self, node):
|
||||||
|
self.write('`')
|
||||||
|
|
||||||
|
def visit_literal_block(self, node):
|
||||||
|
self.write('\n\n```')
|
||||||
|
if 'c++' in node['classes']:
|
||||||
|
self.write('c++')
|
||||||
|
self.write('\n')
|
||||||
|
self.preserve_newlines = True
|
||||||
|
|
||||||
|
def depart_literal_block(self, node):
|
||||||
|
self.write('\n```\n')
|
||||||
|
self.preserve_newlines = False
|
||||||
|
|
||||||
|
def visit_inline(self, node):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def depart_inline(self, node):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def visit_image(self, node):
|
||||||
|
self.write('![](' + node['uri'] + ')')
|
||||||
|
|
||||||
|
def depart_image(self, node):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def write_row(self, row, widths):
|
||||||
|
for i, entry in enumerate(row):
|
||||||
|
text = entry[0][0] if len(entry) > 0 else ''
|
||||||
|
if i != 0:
|
||||||
|
self.write('|')
|
||||||
|
self.write('{:{}}'.format(text, widths[i]))
|
||||||
|
self.write('\n')
|
||||||
|
|
||||||
|
def visit_table(self, node):
|
||||||
|
table = node.children[0]
|
||||||
|
colspecs = table[:-2]
|
||||||
|
thead = table[-2]
|
||||||
|
tbody = table[-1]
|
||||||
|
widths = [int(cs['colwidth']) for cs in colspecs]
|
||||||
|
sep = '|'.join(['-' * w for w in widths]) + '\n'
|
||||||
|
self.write('\n\n')
|
||||||
|
self.write_row(thead[0], widths)
|
||||||
|
self.write(sep)
|
||||||
|
for row in tbody:
|
||||||
|
self.write_row(row, widths)
|
||||||
|
raise nodes.SkipChildren
|
||||||
|
|
||||||
|
def depart_table(self, node):
|
||||||
|
pass
|
||||||
|
|
||||||
|
class MDWriter(writers.Writer):
|
||||||
|
"""GitHub-flavored markdown writer"""
|
||||||
|
|
||||||
|
supported = ('md',)
|
||||||
|
"""Formats this writer supports."""
|
||||||
|
|
||||||
|
def translate(self):
|
||||||
|
translator = Translator(self.document)
|
||||||
|
self.document.walkabout(translator)
|
||||||
|
self.output = (translator.output, translator.version)
|
||||||
|
|
||||||
|
|
||||||
|
def convert(rst_path):
|
||||||
|
"""Converts RST file to Markdown."""
|
||||||
|
return core.publish_file(source_path=rst_path, writer=MDWriter())
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
convert(sys.argv[1])
|
|
@ -0,0 +1,7 @@
|
||||||
|
# Sphinx configuration for readthedocs.
|
||||||
|
|
||||||
|
import os, sys
|
||||||
|
|
||||||
|
master_doc = 'index'
|
||||||
|
html_theme = 'theme'
|
||||||
|
html_theme_path = ["."]
|
|
@ -0,0 +1,2 @@
|
||||||
|
If you are not redirected automatically, follow the
|
||||||
|
`link to the fmt documentation <http://fmtlib.net/latest/>`_.
|
|
@ -0,0 +1,17 @@
|
||||||
|
{% extends "basic/layout.html" %}
|
||||||
|
|
||||||
|
{% block extrahead %}
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta http-equiv="refresh" content="1;url=http://fmtlib.net/latest/">
|
||||||
|
<script type="text/javascript">
|
||||||
|
window.location.href = "http://fmtlib.net/latest/"
|
||||||
|
</script>
|
||||||
|
<title>Page Redirection</title>
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block document %}
|
||||||
|
If you are not redirected automatically, follow the <a href='http://fmtlib.net/latest/'>link to the fmt documentation</a>.
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block footer %}
|
||||||
|
{% endblock %}
|
|
@ -0,0 +1,2 @@
|
||||||
|
[theme]
|
||||||
|
inherit = basic
|
|
@ -0,0 +1,113 @@
|
||||||
|
#!/usr/bin/env python
|
||||||
|
# Build the project on Travis CI.
|
||||||
|
|
||||||
|
from __future__ import print_function
|
||||||
|
import errno, os, shutil, subprocess, sys, urllib
|
||||||
|
from subprocess import call, check_call, Popen, PIPE, STDOUT
|
||||||
|
|
||||||
|
def rmtree_if_exists(dir):
|
||||||
|
try:
|
||||||
|
shutil.rmtree(dir)
|
||||||
|
except OSError as e:
|
||||||
|
if e.errno == errno.ENOENT:
|
||||||
|
pass
|
||||||
|
|
||||||
|
def makedirs_if_not_exist(dir):
|
||||||
|
try:
|
||||||
|
os.makedirs(dir)
|
||||||
|
except OSError as e:
|
||||||
|
if e.errno != errno.EEXIST:
|
||||||
|
raise
|
||||||
|
|
||||||
|
def install_dependencies():
|
||||||
|
branch = os.environ['TRAVIS_BRANCH']
|
||||||
|
if branch != 'master':
|
||||||
|
print('Branch: ' + branch)
|
||||||
|
exit(0) # Ignore non-master branches
|
||||||
|
check_call('curl -s https://deb.nodesource.com/gpgkey/nodesource.gpg.key ' +
|
||||||
|
'| sudo apt-key add -', shell=True)
|
||||||
|
check_call('echo "deb https://deb.nodesource.com/node_0.10 precise main" ' +
|
||||||
|
'| sudo tee /etc/apt/sources.list.d/nodesource.list', shell=True)
|
||||||
|
check_call(['sudo', 'apt-get', 'update'])
|
||||||
|
check_call(['sudo', 'apt-get', 'install', 'python-virtualenv', 'nodejs'])
|
||||||
|
check_call(['sudo', 'npm', 'install', '-g', 'less@2.6.1', 'less-plugin-clean-css'])
|
||||||
|
deb_file = 'doxygen_1.8.6-2_amd64.deb'
|
||||||
|
urllib.urlretrieve('http://mirrors.kernel.org/ubuntu/pool/main/d/doxygen/' +
|
||||||
|
deb_file, deb_file)
|
||||||
|
check_call(['sudo', 'dpkg', '-i', deb_file])
|
||||||
|
|
||||||
|
fmt_dir = os.path.dirname(os.path.dirname(os.path.realpath(__file__)))
|
||||||
|
|
||||||
|
build = os.environ['BUILD']
|
||||||
|
if build == 'Doc':
|
||||||
|
travis = 'TRAVIS' in os.environ
|
||||||
|
if travis:
|
||||||
|
install_dependencies()
|
||||||
|
sys.path.insert(0, os.path.join(fmt_dir, 'doc'))
|
||||||
|
import build
|
||||||
|
build.create_build_env()
|
||||||
|
html_dir = build.build_docs()
|
||||||
|
repo = 'fmtlib.github.io'
|
||||||
|
if travis and 'KEY' not in os.environ:
|
||||||
|
# Don't update the repo if building on Travis from an account that
|
||||||
|
# doesn't have push access.
|
||||||
|
print('Skipping update of ' + repo)
|
||||||
|
exit(0)
|
||||||
|
# Clone the fmtlib.github.io repo.
|
||||||
|
rmtree_if_exists(repo)
|
||||||
|
git_url = 'https://github.com/' if travis else 'git@github.com:'
|
||||||
|
check_call(['git', 'clone', git_url + 'fmtlib/{}.git'.format(repo)])
|
||||||
|
# Copy docs to the repo.
|
||||||
|
target_dir = os.path.join(repo, 'dev')
|
||||||
|
rmtree_if_exists(target_dir)
|
||||||
|
shutil.copytree(html_dir, target_dir, ignore=shutil.ignore_patterns('.*'))
|
||||||
|
if travis:
|
||||||
|
check_call(['git', 'config', '--global', 'user.name', 'amplbot'])
|
||||||
|
check_call(['git', 'config', '--global', 'user.email', 'viz@ampl.com'])
|
||||||
|
# Push docs to GitHub pages.
|
||||||
|
check_call(['git', 'add', '--all'], cwd=repo)
|
||||||
|
if call(['git', 'diff-index', '--quiet', 'HEAD'], cwd=repo):
|
||||||
|
check_call(['git', 'commit', '-m', 'Update documentation'], cwd=repo)
|
||||||
|
cmd = 'git push'
|
||||||
|
if travis:
|
||||||
|
cmd += ' https://$KEY@github.com/fmtlib/fmtlib.github.io.git master'
|
||||||
|
p = Popen(cmd, shell=True, stdout=PIPE, stderr=STDOUT, cwd=repo)
|
||||||
|
# Print the output without the key.
|
||||||
|
print(p.communicate()[0].replace(os.environ['KEY'], '$KEY'))
|
||||||
|
if p.returncode != 0:
|
||||||
|
raise subprocess.CalledProcessError(p.returncode, cmd)
|
||||||
|
exit(0)
|
||||||
|
|
||||||
|
standard = os.environ['STANDARD']
|
||||||
|
install_dir = os.path.join(fmt_dir, "_install")
|
||||||
|
build_dir = os.path.join(fmt_dir, "_build")
|
||||||
|
test_build_dir = os.path.join(fmt_dir, "_build_test")
|
||||||
|
|
||||||
|
# Configure library.
|
||||||
|
makedirs_if_not_exist(build_dir)
|
||||||
|
cmake_flags = [
|
||||||
|
'-DCMAKE_INSTALL_PREFIX=' + install_dir, '-DCMAKE_BUILD_TYPE=' + build,
|
||||||
|
'-DCMAKE_CXX_STANDARD=' + standard
|
||||||
|
]
|
||||||
|
check_call(['cmake', '-DFMT_DOC=OFF', '-DFMT_PEDANTIC=ON', '-DFMT_WERROR=ON', fmt_dir] +
|
||||||
|
cmake_flags, cwd=build_dir)
|
||||||
|
|
||||||
|
# Build library.
|
||||||
|
check_call(['make', '-j4'], cwd=build_dir)
|
||||||
|
|
||||||
|
# Test library.
|
||||||
|
env = os.environ.copy()
|
||||||
|
env['CTEST_OUTPUT_ON_FAILURE'] = '1'
|
||||||
|
if call(['make', 'test'], env=env, cwd=build_dir):
|
||||||
|
with open(os.path.join(build_dir, 'Testing', 'Temporary', 'LastTest.log'), 'r') as f:
|
||||||
|
print(f.read())
|
||||||
|
sys.exit(-1)
|
||||||
|
|
||||||
|
# Install library.
|
||||||
|
check_call(['make', 'install'], cwd=build_dir)
|
||||||
|
|
||||||
|
# Test installation.
|
||||||
|
makedirs_if_not_exist(test_build_dir)
|
||||||
|
check_call(['cmake', os.path.join(fmt_dir, "test", "find-package-test")] +
|
||||||
|
cmake_flags, cwd=test_build_dir)
|
||||||
|
check_call(['make', '-j4'], cwd=test_build_dir)
|
|
@ -0,0 +1,30 @@
|
||||||
|
#!/usr/bin/env python
|
||||||
|
# Update the coverity branch from the master branch.
|
||||||
|
# It is not done automatically because Coverity Scan limits
|
||||||
|
# the number of submissions per day.
|
||||||
|
|
||||||
|
from __future__ import print_function
|
||||||
|
import shutil, tempfile
|
||||||
|
from subprocess import check_output, STDOUT
|
||||||
|
|
||||||
|
class Git:
|
||||||
|
def __init__(self, dir):
|
||||||
|
self.dir = dir
|
||||||
|
|
||||||
|
def __call__(self, *args):
|
||||||
|
output = check_output(['git'] + list(args), cwd=self.dir, stderr=STDOUT)
|
||||||
|
print(output)
|
||||||
|
return output
|
||||||
|
|
||||||
|
dir = tempfile.mkdtemp()
|
||||||
|
try:
|
||||||
|
git = Git(dir)
|
||||||
|
git('clone', '-b', 'coverity', 'git@github.com:fmtlib/fmt.git', dir)
|
||||||
|
output = git('merge', '-X', 'theirs', '--no-commit', 'origin/master')
|
||||||
|
if 'Fast-forward' not in output:
|
||||||
|
git('reset', 'HEAD', '.travis.yml')
|
||||||
|
git('checkout', '--', '.travis.yml')
|
||||||
|
git('commit', '-m', 'Update coverity branch')
|
||||||
|
git('push')
|
||||||
|
finally:
|
||||||
|
shutil.rmtree(dir)
|
Loading…
Reference in New Issue