Compare commits

..

146 Commits

Author SHA1 Message Date
Gabi Melman
5b4c4f3f77 Merge pull request #2011 from sjanel/bugfix/fixclangcompilation
Fix compilation error in clang 13 in C++20 mode - ambiguous call to log function
2021-07-27 12:26:34 +03:00
Stephane Janel
aecdfc60a0 Fix comment and clang-format 2021-07-27 09:19:02 +02:00
Gabi Melman
816ede3a17 Revert e93115f436 because won't compile under mscv 2017 2021-07-27 02:43:54 +03:00
Gabi Melman
353c79ca71 Update .travis.yml 2021-07-27 01:49:36 +03:00
Gabi Melman
e93115f436 Fixed compile under c++20 and clang 12 2021-07-27 01:44:52 +03:00
Gabi Melman
197c9639bb Fixed compile under c++20 and clang 12 2021-07-27 01:37:15 +03:00
Gabi Melman
a358a38b84 Update .travis.yml 2021-07-27 01:31:34 +03:00
Gabi Melman
16d76e2293 Update .travis.yml 2021-07-27 01:29:36 +03:00
Gabi Melman
536e583cbe Merge pull request #2013 from gabime/pr-2011
Fix ambiguous error in clang13 and c++20
2021-07-27 01:12:48 +03:00
gabime
9049f9aeb9 Fix ambiguous error in clang13 and c++20 2021-07-27 00:26:32 +03:00
Gabi Melman
bee3e63e1b Merge pull request #2008 from dkavolis/v1.x
Enable format string compile time validation
2021-07-22 19:54:14 +03:00
dkavolis
d8f13cbd5b replace FormatString template argument with fmt::basic_format_string 2021-07-22 16:23:56 +01:00
gabime
0f39da5490 Updated example 2021-07-22 12:44:41 +03:00
Gabi Melman
28fef35a12 Merge pull request #2010 from dkavolis/fmt_string
Check args on formatting
2021-07-22 02:32:28 +03:00
dkavolis
1344d44a5a check args on formatting 2021-07-21 23:54:11 +01:00
gabime
61ed2a670e bump version number to 1.9.0 2021-07-20 23:01:34 +03:00
gabime
db1bc035f7 clang-format 2021-07-20 22:55:47 +03:00
gabime
8de6cdaa82 bump version number to 1.8.6 2021-07-20 22:52:37 +03:00
gabime
fe1a4f5fb6 Added SPDLOG_FMT_RUNTIME macro for compatibilty with fmt prior 8 2021-07-20 17:18:40 +03:00
gabime
d38f89cae8 Fixed daily_file_sink compilation under c++20 2021-07-20 15:02:39 +03:00
gabime
9c90fe8264 Fixed wchar support 2021-07-20 12:53:50 +03:00
gabime
b85a666f72 Enabled parallel build under msvc 2021-07-19 16:33:31 +03:00
Gabi Melman
5ba95f6816 Update logger.h 2021-07-19 03:46:01 +03:00
Gabi Melman
dc38b7c3c4 Update logger.h 2021-07-19 03:20:34 +03:00
Gabi Melman
6484b03dd9 Update logger.h 2021-07-19 03:09:37 +03:00
gabime
29235d9b4b minor string_view change and comment 2021-07-19 01:15:53 +03:00
gabime
4b3687f1a6 Removed unneeded macro definition 2021-07-19 01:11:45 +03:00
gabime
e7e8b75a4c clang-format 2021-07-19 00:50:51 +03:00
gabime
e98265a49b cosmetic reorder of logger funcs definitions 2021-07-19 00:48:01 +03:00
gabime
e87f69bdb6 Removed check if format string can be converted to fmt::is_compile_string 2021-07-19 00:12:17 +03:00
Gabi Melman
70d2832c0d Update README.md 2021-07-14 16:30:54 +03:00
gabime
7636f1f659 revert some changes made by mistake 2021-07-14 14:33:45 +03:00
gabime
1523c83650 Added fmt/compile.h bundled file 2021-07-14 14:28:34 +03:00
Gabi Melman
a6987efaec Update README.md 2021-07-14 13:51:23 +03:00
Gabi Melman
6491abb519 Update .travis.yml 2021-07-10 19:14:47 +03:00
gabime
8faabb4e3a Fix msvc compile 2021-07-10 17:33:08 +03:00
gabime
2838c2c8a5 use vformat_to instead for format_to for better performance 2021-07-10 17:00:13 +03:00
gabime
3315bad009 Treat wall warnings as errors if SPDLOG_BUILD_WARNINGS is ON 2021-07-10 15:22:44 +03:00
gabime
3eeced78b5 Removed some cmake wdev warnings 2021-07-10 15:15:39 +03:00
gabime
c23430b438 Fixed cast warning 2021-07-10 14:31:56 +03:00
Gabi Melman
0e49bfff51 Update .travis.yml 2021-07-10 14:20:06 +03:00
Gabi Melman
3ed40d04a9 Update .travis.yml 2021-07-10 14:14:09 +03:00
gabime
70b36aa55d Remove fmt::runtime() wrapper in logger.h 2021-07-10 14:07:32 +03:00
gabime
0f83b33d4f backward compatibility with fmt version < 8 2021-07-10 13:48:06 +03:00
gabime
b83106bed4 Update bundled fmt to v8.0.1 2021-07-03 23:10:57 +03:00
Gabi Melman
21413e599a Update qt_sinks.h 2021-06-29 02:20:48 +03:00
Gabi Melman
5f4cc7b036 Merge pull request #1986 from mguludag/v1.x
Added QTextEdit and QPlainTextEdit sink
2021-06-29 02:18:00 +03:00
Muhammed Galib Uludag
9aa26fb969 Added Qt sinks 2021-06-29 01:04:25 +03:00
Muhammed Galib Uludag
7f74012a0d Delete qtextedit_sink.h 2021-06-29 01:02:16 +03:00
Muhammed Galib Uludag
96ebef093f Delete qtextedit_sink_p.h 2021-06-29 01:01:48 +03:00
Muhammed Galib Uludag
a19f4bba0c Delete qplaintextedit_sink_p.h 2021-06-29 01:01:40 +03:00
Muhammed Galib Uludag
c24b957e17 Delete qplaintextedit_sink.h 2021-06-29 01:01:18 +03:00
Muhammed Galib Uludag
5ba2f77230 Added QPlainTextEdit sink
QPlainTextEdit performs better than QTextEdit and its derivatives and also it has rich features
2021-06-28 23:16:23 +03:00
Muhammed Galib Uludag
a09f490804 Implemented QTextEdit and, QTextBrowser sink 2021-06-28 23:13:37 +03:00
Gabi Melman
082d6fbea9 Merge pull request #1984 from hctym1995/v1.x
Add a color-terminal type
2021-06-28 15:10:01 +03:00
zyw1995ted@163.com
37372960a8 add a color-terminal type 2021-06-28 18:12:12 +08:00
gabime
0035a0c98d Fixed dup sink compile warnings in older compilers with back_inserter 2021-06-28 12:09:39 +03:00
Gabi Melman
036cc5d575 Merge pull request #1982 from mguludag/v1.x
ignore pattern formatting
2021-06-27 23:11:39 +03:00
Muhammed Galib Uludag
14950926ed ignore pattern formatting for message section in mongodb 2021-06-27 22:50:31 +03:00
Muhammed Galib Uludag
baa3b1a07e Merge branch 'gabime:v1.x' into v1.x 2021-06-27 20:32:52 +03:00
Gabi Melman
2a09f66a44 Remove un needed functions and added override keyword 2021-06-27 20:32:01 +03:00
Muhammed Galib Uludag
e50b62c770 suppressed unused var 2021-06-27 20:30:41 +03:00
Gabi Melman
13d8b0f17f Merge pull request #1981 from mguludag/v1.x
added mongodb sink
2021-06-27 20:30:08 +03:00
Muhammed Galib Uludag
9e0c658b29 factory functions namespace fix 2021-06-27 20:17:19 +03:00
Muhammed Galib Uludag
74fec56927 Changed base class to base_sink and added factory functions 2021-06-27 19:59:07 +03:00
Muhammed Galib Uludag
514f304a47 changed license template for compability to other spdlog headers 2021-06-27 18:06:44 +03:00
Muhammed Galib Uludag
7f85a5c988 change license to mit 2021-06-27 17:53:19 +03:00
Muhammed Galib Uludag
14d626d961 added mongodb sink 2021-06-27 16:16:39 +03:00
Gabi Melman
7560cacb3f Update .travis.yml 2021-06-27 00:31:34 +03:00
Gabi Melman
3cd9bcdab9 Update dup_filter_sink.h 2021-06-27 00:21:03 +03:00
Gabi Melman
32f1efdc99 Update dup_filter_sink.h 2021-06-27 00:11:31 +03:00
Gabi Melman
bcc9f03457 Update .travis.yml 2021-06-27 00:01:44 +03:00
Gabi Melman
4c845bf02b Update dup_filter_sink.h 2021-06-26 23:40:11 +03:00
Gabi Melman
c727864393 Update .travis.yml 2021-06-26 23:25:27 +03:00
Gabi Melman
4548573a75 Update .travis.yml 2021-06-26 23:18:22 +03:00
Gabi Melman
385246730d Update .travis.yml 2021-06-26 23:14:26 +03:00
Gabi Melman
e06d21a4c0 Update .travis.yml 2021-06-26 22:53:48 +03:00
Gabi Melman
c132d2ae8c Update .travis.yml 2021-06-26 22:51:28 +03:00
Gabi Melman
ece31100e0 Update appveyor.yml 2021-06-26 22:47:32 +03:00
Gabi Melman
ce4e1ac54b Update appveyor.yml 2021-06-26 22:44:02 +03:00
Gabi Melman
ef61fb11f0 Update README.md 2021-06-26 22:21:55 +03:00
Gabi Melman
42f2b11ec8 Update .travis.yml 2021-06-26 21:46:49 +03:00
Gabi Melman
ac87cbb0d1 Update appveyor.yml 2021-06-26 21:44:03 +03:00
Gabi Melman
c65de3d689 Update appveyor.yml 2021-06-26 21:41:15 +03:00
Gabi Melman
1433fa4209 Update appveyor.yml 2021-06-26 21:40:42 +03:00
Gabi Melman
d51149e5ac Update appveyor.yml 2021-06-26 20:45:29 +03:00
Gabi Melman
ffd813435a fix compile error again with wchar formatting 2021-06-26 20:10:21 +03:00
gabime
d75fd2c7f9 Fixed wchar support under msvc 2021-06-26 19:43:37 +03:00
gabime
cdad84aa46 merge 2021-06-26 18:02:09 +03:00
gabime
0fdb545d8c Fixed clang c++20 compile 2021-06-26 17:59:08 +03:00
gabime
a5f5ff70e0 Fixed clang c++20 compile 2021-06-26 17:58:45 +03:00
gabime
4f0e320236 Fixed format string in bench 2021-06-26 17:52:55 +03:00
gabime
68aed6a5eb Fixed building under c++17 2021-06-26 17:36:57 +03:00
Gabi Melman
6811112208 Update logger.h 2021-06-24 19:42:12 +03:00
gabime
9ebc4b24d9 Added missing args.h file 2021-06-24 17:26:02 +03:00
gabime
b990080a52 Fixed fmt locale.h deprecation warning 2021-06-24 17:22:06 +03:00
gabime
efbe3e4d57 Added missing fmt 8 headers 2021-06-24 17:17:27 +03:00
gabime
7b14a65b2b Fixed format_to deprecated warning by wrapping the buffer with std::back_inserter 2021-06-24 17:07:14 +03:00
gabime
5887744d8b Fixed bin2hex to work with fmt v8 2021-06-24 15:58:25 +03:00
gabime
8bf718671a Update fmt version 8.0 2021-06-24 13:22:02 +03:00
Gabi Melman
c858b14c03 Update clang format to format cpp macros 2021-06-20 23:20:36 +03:00
Gabi Melman
12df172575 Merge pull request #1971 from SpriteOvO/v1.x
Use std::function for the global error handler
2021-06-16 01:25:09 +03:00
Sprite
7fa751d36e Use std::function for the global error handler 2021-06-16 05:04:17 +08:00
Gabi Melman
7a7611e977 Merge pull request #1970 from bansan85/v1.x
Fix signed/unsigned mismatch in VS
2021-06-13 21:49:08 +03:00
LE GARREC Vincent
ec8763adf2 Fix signed/unsigned mismatch in VS 2021-06-13 19:35:02 +02:00
Gabi Melman
f2d1d573f5 Fix #1967 2021-06-07 22:39:42 +03:00
Gabi Melman
a530b87fd0 Merge pull request #1961 from jafj/FixCxx20Wdeprecated-copy
Fix C++20 build resulting in deprecated implicit copy assignment operator warning
2021-06-02 12:15:38 +03:00
JB_12
6c21789aed Fix C++20 build resulting in deprecated implicit copy assignment operator warning 2021-06-02 08:20:29 +01:00
Gabi Melman
616866fcf4 Merge pull request #1958 from mlund/v1.x
Allow compilation with nvc++ (and possibly PGI)
2021-05-30 22:14:02 +03:00
Mikael Lund
faf06bcfe5 Add newlin to color_sinks.cpp 2021-05-30 13:07:16 +01:00
Mikael Lund
cd376a5c43 Allow compilation with nvc++ 2021-05-30 13:02:56 +01:00
Gabi Melman
6ba5ab6d67 Merge pull request #1948 from stevenlunt/v1.x
add macros for overriding the individual level names
2021-05-20 01:09:09 +03:00
steven lunt
1bee3218b4 cleanup thanks to gabime 2021-05-19 17:51:03 -04:00
steven lunt
802eaadd2d add macros for overriding the individual level names 2021-05-19 10:45:33 -04:00
steven lunt
ee22eed23d add macros for overriding the individual level names 2021-05-18 19:36:45 -04:00
steven lunt
ab72de5f7a Revert "added spdlog::level::set_string_view to enable alternate log level names without changing the build via SPDLOG_LEVEL_NAMES"
This reverts commit 2a16d1d230.
2021-05-18 19:25:07 -04:00
steven lunt
a32cea24fd Revert "remove constexpr on level_string_views to fix compilation on C++17 from addition of set_string_view"
This reverts commit ac3e26b0ff.
2021-05-18 19:24:44 -04:00
Gabi Melman
af0d805be4 Merge pull request #1946 from jafj/EnableCMakePolicyCMP0077
Add support for CMake policy CMP0077
2021-05-17 01:54:23 +03:00
JB_12
181c22f798 Add support for CMake policy CMP0077 2021-05-16 22:49:02 +01:00
Gabi Melman
87133ef6b7 Merge pull request #1933 from neheb/s
small std::find conversion
2021-05-11 02:12:46 +03:00
Rosen Penev
1ef2f014ee small std::find conversion
Signed-off-by: Rosen Penev <rosenp@gmail.com>
2021-05-10 14:42:28 -07:00
Gabi Melman
0a92d1d684 Merge pull request #1931 from vadz/msvc-undef-warning-fix
Fix warning about testing _WIN64 which might be undefined
2021-05-07 01:05:41 +03:00
Vadim Zeitlin
ff5221b693 Fix warning about testing _WIN64 which might be undefined
This warning is disabled by default, but is pretty useful and worth
enabling for MSVC, just as -Wundef for gcc, so fix it in Win32 build.
2021-05-06 22:33:41 +01:00
Gabi Melman
db484cc4b8 Merge pull request #1930 from vadz/msvc-unreachable-warning-fix-alt
Avoid harmless warning about unreachable statement in MSVS build
2021-05-07 00:27:45 +03:00
Vadim Zeitlin
6442963f49 Avoid harmless warning about unreachable statement in MSVS build
All MSVS versions >= 2015 warn about "return 0" after throw_spdlog_ex()
being unreachable in filesize(), so disable this warning in this
function (note that it can't be disabled inside it).
2021-05-06 22:02:25 +01:00
Gabi Melman
0f7b95ce47 Merge pull request #1924 from lnovey/remove-c-style-casts
Change c-style casts to reinterpret casts in tcp_client
2021-05-03 20:12:03 +03:00
Luke Novey
632a2e0894 Change c-style casts to reinterpret casts 2021-05-03 12:33:58 -04:00
gabime
e9635c7b2d rethrnow non std exceptions to fix #533 2021-05-01 00:29:36 +03:00
Gabi Melman
8e3b1338a5 Merge pull request #1918 from matt77hias/v1.x
Made mutex member variable mutable
2021-04-22 22:48:29 +03:00
Matthias Moulin
9d3dde0900 Made mutex member variable mutable
Classes inheriting from `base_sink` can now lock the base mutex inside their `const` member methods (e.g., basic accessors).
2021-04-22 21:19:54 +02:00
Gabi Melman
c5abaeddca Merge pull request #1916 from haifengkao/FixNoNewLineAtEndOfFile
fix Xcode compiler warning "no new line at the end of file"
2021-04-20 22:31:56 +03:00
Hai Feng Kao
ca2cd6f3e7 fix Xcode compiler warning "no new line at the end of file" 2021-04-20 12:43:56 +08:00
Gabi Melman
7d07e0312a Update README.md 2021-04-10 13:07:36 +03:00
Gabi Melman
e1c73fd8f4 Update README.md 2021-04-10 13:05:39 +03:00
Gabi Melman
b83ab21283 Update README.md 2021-04-10 13:02:16 +03:00
Gabi Melman
8001156ca8 Update stdout_sinks-inl.h 2021-04-08 23:20:36 +03:00
Gabi Melman
57e31f0a58 Merge pull request #1906 from LonghronShen/v1.x
fix bug #1790
2021-04-08 23:18:20 +03:00
Gabi Melman
51fadf6b7e Merge pull request #1912 from SUPERustam/v1.x
Minor update, PEP8 format and f-string instead of format method
2021-04-08 23:13:33 +03:00
Gabi Melman
2a6a8aa0a0 Merge pull request #1913 from bsergean/patch-1
Update pattern_formatter-inl.h full_formatter comment to describe the default logging pattern expression accurately
2021-04-08 21:11:53 +03:00
Benjamin Sergeant
aa264a7fb2 Update pattern_formatter-inl.h
Comment describing the default logging pattern is missing [%s:%#], which is the abbreviated source file + the line number.

I tried to customize our own logger by copy pasting this info, and then I noticed we had lost that information.
2021-04-08 09:05:55 -07:00
SUPERustam
5e35c2b6ab Update extract_version.py 2021-04-08 09:56:01 +03:00
Steven Hangger
0385372314 fix bug #1790 2021-04-05 20:00:22 +08:00
Gabi Melman
efbff95ec7 Merge pull request #1900 from nandanvasudevan/patch-1
Minor typo
2021-04-03 16:16:04 +03:00
Nandan V
2a9edb2153 Minor typo 2021-04-03 11:42:35 +00:00
Gabi Melman
be14e60d9e Merge pull request #1891 from Hugoto69/patch-1
Update .clang-tidy
2021-03-25 22:15:12 +02:00
Hugo Bonnet
ef4641cad7 Update .clang-tidy 2021-03-25 20:43:51 +01:00
114 changed files with 6657 additions and 6064 deletions

View File

@@ -104,5 +104,6 @@ SpacesInSquareBrackets: false
Standard: Cpp11 Standard: Cpp11
TabWidth: 8 TabWidth: 8
UseTab: Never UseTab: Never
IndentPPDirectives: AfterHash
... ...

View File

@@ -2,7 +2,7 @@ Checks: 'cppcoreguidelines-*,
performance-*, performance-*,
modernize-*, modernize-*,
google-*, google-*,
misc-*, misc-*
cert-*, cert-*,
readability-*, readability-*,
clang-analyzer-*, clang-analyzer-*,

View File

@@ -5,7 +5,7 @@
sudo: required sudo: required
language: cpp language: cpp
# gcc 4.8 # gcc t
addons: &gcc48 addons: &gcc48
apt: apt:
packages: packages:
@@ -21,6 +21,16 @@ addons: &gcc7
sources: sources:
- ubuntu-toolchain-r-test - ubuntu-toolchain-r-test
# gcc 9.0
addons: &gcc9
apt:
packages:
- g++-9
sources:
- ubuntu-toolchain-r-test
# Clang 3.5 # Clang 3.5
addons: &clang35 addons: &clang35
apt: apt:
@@ -40,47 +50,63 @@ addons: &clang10
sources: sources:
- sourceline: "deb http://apt.llvm.org/bionic/ llvm-toolchain-bionic-10 main" - sourceline: "deb http://apt.llvm.org/bionic/ llvm-toolchain-bionic-10 main"
key_url: "https://apt.llvm.org/llvm-snapshot.gpg.key" key_url: "https://apt.llvm.org/llvm-snapshot.gpg.key"
addons: &clang12
apt:
packages:
- clang-12
- lldb-12
- lld-12
sources:
- sourceline: "deb http://apt.llvm.org/bionic/ llvm-toolchain-bionic-12 main"
key_url: "https://apt.llvm.org/llvm-snapshot.gpg.key"
matrix: matrix:
include: include:
# Test gcc-4.8: C++11, Build=Debug/Release # Test gcc-4.8: C++11, Build=Release
- env: GCC_VERSION=4.8 BUILD_TYPE=Debug CPP=11
os: linux
addons: *gcc48
- env: GCC_VERSION=4.8 BUILD_TYPE=Release CPP=11 - env: GCC_VERSION=4.8 BUILD_TYPE=Release CPP=11
os: linux os: linux
addons: *gcc48 addons: *gcc48
# Test gcc-7: C++11, Build=Release
- env: GCC_VERSION=7 BUILD_TYPE=Release CPP=11 - env: GCC_VERSION=7 BUILD_TYPE=Release CPP=11
os: linux os: linux
addons: *gcc7 addons: *gcc7
# Test clang-3.5: C++11, Build=Debug/Release # Test gcc-9: C++17, Build=Release
- env: CLANG_VERSION=3.5 BUILD_TYPE=Debug CPP=11 - env: GCC_VERSION=9 BUILD_TYPE=Release CPP=17
os: linux os: linux
addons: *clang35 addons: *gcc9
# Test clang-3.5: C++11, Build=Release
- env: CLANG_VERSION=3.5 BUILD_TYPE=Release CPP=11 - env: CLANG_VERSION=3.5 BUILD_TYPE=Release CPP=11
os: linux os: linux
addons: *clang35 addons: *clang35
# osx # Text osx
- env: BUILD_TYPE=Release CPP=11 ASAN=Off TSAN=Off - env: BUILD_TYPE=Release CPP=11 ASAN=Off TSAN=Off
os: osx os: osx
# Test clang-10.0: C++11, Build=Debug/Release # Test clang-10.0: C++11, Build=Release
- env: CLANG_VERSION=10 BUILD_TYPE=Debug CPP=11
os: linux
dist: bionic
addons: *clang10
- env: CLANG_VERSION=10 BUILD_TYPE=Release CPP=11 ASAN=On - env: CLANG_VERSION=10 BUILD_TYPE=Release CPP=11 ASAN=On
os: linux os: linux
dist: bionic dist: bionic
addons: *clang10 addons: *clang10
# Test clang-10.0: C++17, Build=Debug
- env: CLANG_VERSION=10 BUILD_TYPE=Debug CPP=17 ASAN=Off
os: linux
dist: bionic
addons: *clang10
# Test clang-12.0: C++17, Build=Debug
- env: CLANG_VERSION=12 BUILD_TYPE=Debug CPP=17 ASAN=Off
os: linux
dist: bionic
addons: *clang12
before_script: before_script:
- if [ -n "$GCC_VERSION" ]; then export CXX="g++-${GCC_VERSION}" CC="gcc-${GCC_VERSION}"; fi - if [ -n "$GCC_VERSION" ]; then export CXX="g++-${GCC_VERSION}" CC="gcc-${GCC_VERSION}"; fi

View File

@@ -15,6 +15,13 @@ message(STATUS "Build spdlog: ${SPDLOG_VERSION}")
include(GNUInstallDirs) include(GNUInstallDirs)
# ---------------------------------------------------------------------------------------
# Set CMake policies to support later version behaviour
# ---------------------------------------------------------------------------------------
if(POLICY CMP0077)
cmake_policy(SET CMP0077 NEW) # option() honors variables already set
endif()
# --------------------------------------------------------------------------------------- # ---------------------------------------------------------------------------------------
# Set default build to release # Set default build to release
# --------------------------------------------------------------------------------------- # ---------------------------------------------------------------------------------------
@@ -30,9 +37,9 @@ if(NOT CMAKE_CXX_STANDARD)
set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_CXX_STANDARD_REQUIRED ON)
endif() endif()
# make sure __cplusplus is defined when using msvc # make sure __cplusplus is defined when using msvc and enable parallel build
if(MSVC) if(MSVC)
string(APPEND CMAKE_CXX_FLAGS " /Zc:__cplusplus") string(APPEND CMAKE_CXX_FLAGS " /Zc:__cplusplus /MP")
endif() endif()
set(CMAKE_CXX_EXTENSIONS OFF) set(CMAKE_CXX_EXTENSIONS OFF)
@@ -92,10 +99,15 @@ endif()
if(WIN32) if(WIN32)
option(SPDLOG_WCHAR_SUPPORT "Support wchar api" OFF) option(SPDLOG_WCHAR_SUPPORT "Support wchar api" OFF)
option(SPDLOG_WCHAR_FILENAMES "Support wchar filenames" OFF) option(SPDLOG_WCHAR_FILENAMES "Support wchar filenames" OFF)
else()
set(SPDLOG_WCHAR_SUPPORT OFF CACHE BOOL "non supported option" FORCE)
set(SPDLOG_WCHAR_FILENAMES OFF CACHE BOOL "non supported option" FORCE)
endif() endif()
if(${CMAKE_SYSTEM_NAME} STREQUAL "Linux") if(${CMAKE_SYSTEM_NAME} STREQUAL "Linux")
option(SPDLOG_CLOCK_COARSE option(SPDLOG_CLOCK_COARSE "Use CLOCK_REALTIME_COARSE instead of the regular clock," OFF)
"Use the much faster (but much less accurate) CLOCK_REALTIME_COARSE instead of the regular clock," OFF) else()
set(SPDLOG_CLOCK_COARSE OFF CACHE BOOL "non supported option" FORCE)
endif() endif()
option(SPDLOG_PREVENT_CHILD_FD "Prevent from child processes to inherit log file descriptors" OFF) option(SPDLOG_PREVENT_CHILD_FD "Prevent from child processes to inherit log file descriptors" OFF)
@@ -137,8 +149,8 @@ if(SPDLOG_BUILD_SHARED OR BUILD_SHARED_LIBS)
add_library(spdlog SHARED ${SPDLOG_SRCS} ${SPDLOG_ALL_HEADERS}) add_library(spdlog SHARED ${SPDLOG_SRCS} ${SPDLOG_ALL_HEADERS})
target_compile_definitions(spdlog PUBLIC SPDLOG_SHARED_LIB) target_compile_definitions(spdlog PUBLIC SPDLOG_SHARED_LIB)
if(MSVC) if(MSVC)
target_compile_options(spdlog PUBLIC target_compile_options(spdlog PUBLIC $<$<AND:$<CXX_COMPILER_ID:MSVC>,$<NOT:$<COMPILE_LANGUAGE:CUDA>>>:/wd4251
$<$<AND:$<CXX_COMPILER_ID:MSVC>,$<NOT:$<COMPILE_LANGUAGE:CUDA>>>:/wd4251 /wd4275>) /wd4275>)
endif() endif()
if(NOT SPDLOG_FMT_EXTERNAL AND NOT SPDLOG_FMT_EXTERNAL_HO) if(NOT SPDLOG_FMT_EXTERNAL AND NOT SPDLOG_FMT_EXTERNAL_HO)
target_compile_definitions(spdlog PRIVATE FMT_EXPORT PUBLIC FMT_SHARED) target_compile_definitions(spdlog PRIVATE FMT_EXPORT PUBLIC FMT_SHARED)
@@ -198,10 +210,10 @@ endif()
# --------------------------------------------------------------------------------------- # ---------------------------------------------------------------------------------------
# Add required libraries for Android CMake build # Add required libraries for Android CMake build
# --------------------------------------------------------------------------------------- # ---------------------------------------------------------------------------------------
if (ANDROID) if(ANDROID)
target_link_libraries(spdlog PUBLIC log) target_link_libraries(spdlog PUBLIC log)
target_link_libraries(spdlog_header_only INTERFACE log) target_link_libraries(spdlog_header_only INTERFACE log)
endif () endif()
# --------------------------------------------------------------------------------------- # ---------------------------------------------------------------------------------------
# Misc definitions according to tweak options # Misc definitions according to tweak options
@@ -305,4 +317,3 @@ if(SPDLOG_INSTALL)
# --------------------------------------------------------------------------------------- # ---------------------------------------------------------------------------------------
include(cmake/spdlogCPack.cmake) include(cmake/spdlogCPack.cmake)
endif() endif()

View File

@@ -1,6 +1,6 @@
# spdlog # spdlog
Very fast, header-only/compiled, C++ logging library. [![Build Status](https://travis-ci.org/gabime/spdlog.svg?branch=v1.x)](https://travis-ci.org/gabime/spdlog)&nbsp; [![Build status](https://ci.appveyor.com/api/projects/status/d2jnxclg20vd0o50?svg=true)](https://ci.appveyor.com/project/gabime/spdlog) [![Release](https://img.shields.io/github/release/gabime/spdlog.svg)](https://github.com/gabime/spdlog/releases/latest) Very fast, header-only/compiled, C++ logging library. [![Build Status](https://travis-ci.com/gabime/spdlog.svg?branch=v1.x)](https://travis-ci.com/gabime/spdlog)&nbsp; [![Build status](https://ci.appveyor.com/api/projects/status/d2jnxclg20vd0o50?svg=true)](https://ci.appveyor.com/project/gabime/spdlog) [![Release](https://img.shields.io/github/release/gabime/spdlog.svg)](https://github.com/gabime/spdlog/releases/latest)
## Install ## Install
#### Header only version #### Header only version
@@ -49,7 +49,7 @@ $ cmake .. && make -j
* syslog. * syslog.
* Windows event log. * Windows event log.
* Windows debugger (```OutputDebugString(..)```). * Windows debugger (```OutputDebugString(..)```).
* Easily extendable with custom log targets (just implement a single function in the [sink](include/spdlog/sinks/sink.h) interface). * Easily [extendable](https://github.com/gabime/spdlog/wiki/4.-Sinks#implementing-your-own-sink) with custom log targets.
* Log filtering - log levels can be modified in runtime as well as in compile time. * Log filtering - log levels can be modified in runtime as well as in compile time.
* Support for loading log levels from argv or from environment var. * Support for loading log levels from argv or from environment var.
* [Backtrace](#backtrace-support) support - store debug messages in a ring buffer and display later on demand. * [Backtrace](#backtrace-support) support - store debug messages in a ring buffer and display later on demand.
@@ -143,8 +143,9 @@ void daily_example()
--- ---
#### Backtrace support #### Backtrace support
```c++ ```c++
// Loggers can store in a ring buffer all messages (including debug/trace) and display later on demand. // Debug messages can be stored in a ring buffer instead of being logged immediately.
// When needed, call dump_backtrace() to see them // This is useful in order to display debug logs only when really nededed (e.g. when error happens).
// When needed, call dump_backtrace() to see them.
spdlog::enable_backtrace(32); // Store the latest 32 messages in a buffer. Older messages will be dropped. spdlog::enable_backtrace(32); // Store the latest 32 messages in a buffer. Older messages will be dropped.
// or my_logger->enable_backtrace(32).. // or my_logger->enable_backtrace(32)..

View File

@@ -50,6 +50,13 @@ environment:
WCHAR: 'ON' WCHAR: 'ON'
WCHAR_FILES: 'ON' WCHAR_FILES: 'ON'
BUILD_EXAMPLE: 'OFF' BUILD_EXAMPLE: 'OFF'
- GENERATOR: '"Visual Studio 16 2019" -A x64'
BUILD_TYPE: Release
BUILD_SHARED: 'ON'
WCHAR: 'OFF'
WCHAR_FILES: 'OFF'
BUILD_EXAMPLE: 'OFF'
APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2019
build_script: build_script:
- cmd: >- - cmd: >-
set set

View File

@@ -10,17 +10,15 @@ endif()
find_package(Threads REQUIRED) find_package(Threads REQUIRED)
find_package(benchmark CONFIG) find_package(benchmark CONFIG)
if (NOT benchmark_FOUND) if(NOT benchmark_FOUND)
message(STATUS "Using CMake Version ${CMAKE_VERSION}") message(STATUS "Using CMake Version ${CMAKE_VERSION}")
if (${CMAKE_VERSION} VERSION_GREATER_EQUAL "3.11.0") if(${CMAKE_VERSION} VERSION_GREATER_EQUAL "3.11.0")
# User can fetch googlebenchmark # User can fetch googlebenchmark
message(STATUS "Downloading GoogleBenchmark") message(STATUS "Downloading GoogleBenchmark")
include(FetchContent) include(FetchContent)
set(BENCHMARK_ENABLE_GTEST_TESTS OFF CACHE INTERNAL "") set(BENCHMARK_ENABLE_GTEST_TESTS OFF CACHE INTERNAL "")
# Do not build and run googlebenchmark tests # Do not build and run googlebenchmark tests
FetchContent_Declare(googlebenchmark FetchContent_Declare(googlebenchmark GIT_REPOSITORY https://github.com/google/benchmark.git GIT_TAG v1.5.2)
GIT_REPOSITORY https://github.com/google/benchmark.git
GIT_TAG v1.5.2)
FetchContent_MakeAvailable(googlebenchmark) FetchContent_MakeAvailable(googlebenchmark)
else() else()

View File

@@ -11,9 +11,9 @@
#include "spdlog/sinks/basic_file_sink.h" #include "spdlog/sinks/basic_file_sink.h"
#ifdef SPDLOG_FMT_EXTERNAL #ifdef SPDLOG_FMT_EXTERNAL
#include <fmt/locale.h> # include <fmt/format.h>
#else #else
#include "spdlog/fmt/bundled/locale.h" # include "spdlog/fmt/bundled/format.h"
#endif #endif
#include "utils.h" #include "utils.h"
@@ -32,9 +32,9 @@ using namespace utils;
void bench_mt(int howmany, std::shared_ptr<spdlog::logger> log, int thread_count); void bench_mt(int howmany, std::shared_ptr<spdlog::logger> log, int thread_count);
#ifdef _MSC_VER #ifdef _MSC_VER
#pragma warning(push) # pragma warning(push)
#pragma warning(disable : 4996) // disable fopen warning under msvc # pragma warning(disable : 4996) // disable fopen warning under msvc
#endif // _MSC_VER #endif // _MSC_VER
int count_lines(const char *filename) int count_lines(const char *filename)
{ {
@@ -64,7 +64,7 @@ void verify_file(const char *filename, int expected_count)
} }
#ifdef _MSC_VER #ifdef _MSC_VER
#pragma warning(pop) # pragma warning(pop)
#endif #endif
int main(int argc, char *argv[]) int main(int argc, char *argv[])
@@ -103,12 +103,11 @@ int main(int argc, char *argv[])
auto slot_size = sizeof(spdlog::details::async_msg); auto slot_size = sizeof(spdlog::details::async_msg);
spdlog::info("-------------------------------------------------"); spdlog::info("-------------------------------------------------");
spdlog::info(fmt::format(std::locale("en_US.UTF-8"), "Messages : {:L}", howmany)); spdlog::info("Messages : {:L}", howmany);
spdlog::info(fmt::format(std::locale("en_US.UTF-8"), "Threads : {:L}", threads)); spdlog::info("Threads : {:L}", threads);
spdlog::info(fmt::format(std::locale("en_US.UTF-8"), "Queue : {:L} slots", queue_size)); spdlog::info("Queue : {:L} slots", queue_size);
spdlog::info(fmt::format( spdlog::info("Queue memory : {:L} x {:L} = {:L} KB ", queue_size, slot_size, (queue_size * slot_size) / 1024);
std::locale("en_US.UTF-8"), "Queue memory : {:L} x {:L} = {:L} KB ", queue_size, slot_size, (queue_size * slot_size) / 1024)); spdlog::info("Total iters : {:L}", iters);
spdlog::info(fmt::format(std::locale("en_US.UTF-8"), "Total iters : {:L}", iters));
spdlog::info("-------------------------------------------------"); spdlog::info("-------------------------------------------------");
const char *filename = "logs/basic_async.log"; const char *filename = "logs/basic_async.log";
@@ -181,5 +180,5 @@ void bench_mt(int howmany, std::shared_ptr<spdlog::logger> logger, int thread_co
auto delta = high_resolution_clock::now() - start; auto delta = high_resolution_clock::now() - start;
auto delta_d = duration_cast<duration<double>>(delta).count(); auto delta_d = duration_cast<duration<double>>(delta).count();
spdlog::info(fmt::format(std::locale("en_US.UTF-8"), "Elapsed: {} secs\t {:L}/sec", delta_d, int(howmany / delta_d))); spdlog::info("Elapsed: {} secs\t {:L}/sec", delta_d, int(howmany / delta_d));
} }

View File

@@ -13,9 +13,9 @@
#include "spdlog/sinks/rotating_file_sink.h" #include "spdlog/sinks/rotating_file_sink.h"
#ifdef SPDLOG_FMT_EXTERNAL #ifdef SPDLOG_FMT_EXTERNAL
#include <fmt/locale.h> # include <fmt/locale.h>
#else #else
#include "spdlog/fmt/bundled/locale.h" # include "spdlog/fmt/bundled/format.h"
#endif #endif
#include "utils.h" #include "utils.h"
@@ -128,7 +128,7 @@ int main(int argc, char *argv[])
if (threads > max_threads) if (threads > max_threads)
{ {
throw std::runtime_error(fmt::format("Number of threads exceeds maximum({}})", max_threads)); throw std::runtime_error(fmt::format("Number of threads exceeds maximum({})", max_threads));
} }
bench_single_threaded(iters); bench_single_threaded(iters);

View File

@@ -42,6 +42,7 @@ function(spdlog_enable_warnings target_name)
-Wextra -Wextra
-Wconversion -Wconversion
-pedantic -pedantic
-Werror
-Wfatal-errors> -Wfatal-errors>
$<$<CXX_COMPILER_ID:MSVC>:${MSVC_OPTIONS}>) $<$<CXX_COMPILER_ID:MSVC>:${MSVC_OPTIONS}>)
endif() endif()
@@ -59,4 +60,3 @@ function(spdlog_enable_sanitizer target_name)
target_compile_options(${target_name} PRIVATE -fno-omit-frame-pointer) target_compile_options(${target_name} PRIVATE -fno-omit-frame-pointer)
target_link_libraries(${target_name} PRIVATE -fsanitize=address,undefined -fuse-ld=gold) target_link_libraries(${target_name} PRIVATE -fsanitize=address,undefined -fuse-ld=gold)
endfunction() endfunction()

View File

@@ -22,7 +22,8 @@ void syslog_example();
void custom_flags_example(); void custom_flags_example();
#include "spdlog/spdlog.h" #include "spdlog/spdlog.h"
#include "spdlog/cfg/env.h" // support for loading levels from the environment variable #include "spdlog/cfg/env.h" // support for loading levels from the environment variable
#include "spdlog/fmt/ostr.h" // support for user defined types
int main(int, char *[]) int main(int, char *[])
{ {
@@ -129,7 +130,7 @@ void daily_example()
#include "spdlog/cfg/env.h" #include "spdlog/cfg/env.h"
void load_levels_example() void load_levels_example()
{ {
// Set the log level to "info" and mylogger to to "trace": // Set the log level to "info" and mylogger to "trace":
// SPDLOG_LEVEL=info,mylogger=trace && ./example // SPDLOG_LEVEL=info,mylogger=trace && ./example
spdlog::cfg::load_env_levels(); spdlog::cfg::load_env_levels();
// or from command line: // or from command line:
@@ -221,7 +222,6 @@ void multi_sink_example()
} }
// User defined types logging by implementing operator<< // User defined types logging by implementing operator<<
#include "spdlog/fmt/ostr.h" // must be included
struct my_type struct my_type
{ {
int i; int i;
@@ -246,7 +246,7 @@ void err_handler_example()
// syslog example (linux/osx/freebsd) // syslog example (linux/osx/freebsd)
#ifndef _WIN32 #ifndef _WIN32
#include "spdlog/sinks/syslog_sink.h" # include "spdlog/sinks/syslog_sink.h"
void syslog_example() void syslog_example()
{ {
std::string ident = "spdlog-example"; std::string ident = "spdlog-example";
@@ -257,7 +257,7 @@ void syslog_example()
// Android example. // Android example.
#if defined(__ANDROID__) #if defined(__ANDROID__)
#include "spdlog/sinks/android_sink.h" # include "spdlog/sinks/android_sink.h"
void android_example() void android_example()
{ {
std::string tag = "spdlog-android"; std::string tag = "spdlog-android";

View File

@@ -46,7 +46,7 @@ struct async_factory_impl
auto tp = registry_inst.get_tp(); auto tp = registry_inst.get_tp();
if (tp == nullptr) if (tp == nullptr)
{ {
tp = std::make_shared<details::thread_pool>(details::default_async_q_size, 1); tp = std::make_shared<details::thread_pool>(details::default_async_q_size, 1U);
registry_inst.set_tp(tp); registry_inst.set_tp(tp);
} }

View File

@@ -4,7 +4,7 @@
#pragma once #pragma once
#ifndef SPDLOG_HEADER_ONLY #ifndef SPDLOG_HEADER_ONLY
#include <spdlog/async_logger.h> # include <spdlog/async_logger.h>
#endif #endif
#include <spdlog/sinks/sink.h> #include <spdlog/sinks/sink.h>

View File

@@ -64,5 +64,5 @@ private:
} // namespace spdlog } // namespace spdlog
#ifdef SPDLOG_HEADER_ONLY #ifdef SPDLOG_HEADER_ONLY
#include "async_logger-inl.h" # include "async_logger-inl.h"
#endif #endif

View File

@@ -4,7 +4,7 @@
#pragma once #pragma once
#ifndef SPDLOG_HEADER_ONLY #ifndef SPDLOG_HEADER_ONLY
#include <spdlog/cfg/helpers.h> # include <spdlog/cfg/helpers.h>
#endif #endif
#include <spdlog/spdlog.h> #include <spdlog/spdlog.h>

View File

@@ -25,5 +25,5 @@ SPDLOG_API void load_levels(const std::string &txt);
} // namespace spdlog } // namespace spdlog
#ifdef SPDLOG_HEADER_ONLY #ifdef SPDLOG_HEADER_ONLY
#include "helpers-inl.h" # include "helpers-inl.h"
#endif // SPDLOG_HEADER_ONLY #endif // SPDLOG_HEADER_ONLY

View File

@@ -4,13 +4,19 @@
#pragma once #pragma once
#ifndef SPDLOG_HEADER_ONLY #ifndef SPDLOG_HEADER_ONLY
#include <spdlog/common.h> # include <spdlog/common.h>
#endif #endif
#include <algorithm>
#include <iterator>
namespace spdlog { namespace spdlog {
namespace level { namespace level {
static string_view_t level_string_views[] SPDLOG_LEVEL_NAMES; #if __cplusplus >= 201703L
constexpr
#endif
static string_view_t level_string_views[] SPDLOG_LEVEL_NAMES;
static const char *short_level_names[] SPDLOG_SHORT_LEVEL_NAMES; static const char *short_level_names[] SPDLOG_SHORT_LEVEL_NAMES;
@@ -19,11 +25,6 @@ SPDLOG_INLINE const string_view_t &to_string_view(spdlog::level::level_enum l) S
return level_string_views[l]; return level_string_views[l];
} }
SPDLOG_INLINE void set_string_view(spdlog::level::level_enum l, const string_view_t &s) SPDLOG_NOEXCEPT
{
level_string_views[l] = s;
}
SPDLOG_INLINE const char *to_short_c_str(spdlog::level::level_enum l) SPDLOG_NOEXCEPT SPDLOG_INLINE const char *to_short_c_str(spdlog::level::level_enum l) SPDLOG_NOEXCEPT
{ {
return short_level_names[l]; return short_level_names[l];
@@ -31,15 +32,10 @@ SPDLOG_INLINE const char *to_short_c_str(spdlog::level::level_enum l) SPDLOG_NOE
SPDLOG_INLINE spdlog::level::level_enum from_str(const std::string &name) SPDLOG_NOEXCEPT SPDLOG_INLINE spdlog::level::level_enum from_str(const std::string &name) SPDLOG_NOEXCEPT
{ {
int level = 0; auto it = std::find(std::begin(level_string_views), std::end(level_string_views), name);
for (const auto &level_str : level_string_views) if (it != std::end(level_string_views))
{ return static_cast<level::level_enum>(std::distance(std::begin(level_string_views), it));
if (level_str == name)
{
return static_cast<level::level_enum>(level);
}
level++;
}
// check also for "warn" and "err" before giving up.. // check also for "warn" and "err" before giving up..
if (name == "warn") if (name == "warn")
{ {
@@ -60,7 +56,7 @@ SPDLOG_INLINE spdlog_ex::spdlog_ex(std::string msg)
SPDLOG_INLINE spdlog_ex::spdlog_ex(const std::string &msg, int last_errno) SPDLOG_INLINE spdlog_ex::spdlog_ex(const std::string &msg, int last_errno)
{ {
memory_buf_t outbuf; memory_buf_t outbuf;
fmt::format_system_error(outbuf, last_errno, msg); fmt::format_system_error(outbuf, last_errno, msg.c_str());
msg_ = fmt::to_string(outbuf); msg_ = fmt::to_string(outbuf);
} }

View File

@@ -16,66 +16,77 @@
#include <functional> #include <functional>
#ifdef SPDLOG_COMPILED_LIB #ifdef SPDLOG_COMPILED_LIB
#undef SPDLOG_HEADER_ONLY # undef SPDLOG_HEADER_ONLY
#if defined(_WIN32) && defined(SPDLOG_SHARED_LIB) # if defined(_WIN32) && defined(SPDLOG_SHARED_LIB)
#ifdef spdlog_EXPORTS # ifdef spdlog_EXPORTS
#define SPDLOG_API __declspec(dllexport) # define SPDLOG_API __declspec(dllexport)
#else # else
#define SPDLOG_API __declspec(dllimport) # define SPDLOG_API __declspec(dllimport)
#endif # endif
#else // !defined(_WIN32) || !defined(SPDLOG_SHARED_LIB) # else // !defined(_WIN32) || !defined(SPDLOG_SHARED_LIB)
#define SPDLOG_API # define SPDLOG_API
#endif # endif
#define SPDLOG_INLINE # define SPDLOG_INLINE
#else // !defined(SPDLOG_COMPILED_LIB) #else // !defined(SPDLOG_COMPILED_LIB)
#define SPDLOG_API # define SPDLOG_API
#define SPDLOG_HEADER_ONLY # define SPDLOG_HEADER_ONLY
#define SPDLOG_INLINE inline # define SPDLOG_INLINE inline
#endif // #ifdef SPDLOG_COMPILED_LIB #endif // #ifdef SPDLOG_COMPILED_LIB
#include <spdlog/fmt/fmt.h> #include <spdlog/fmt/fmt.h>
// backward compatibility with fmt versions older than 8
#if FMT_VERSION >= 80000
# define SPDLOG_FMT_RUNTIME(format_string) fmt::runtime(format_string)
# if defined(SPDLOG_WCHAR_FILENAMES) || defined(SPDLOG_WCHAR_TO_UTF8_SUPPORT)
# include <spdlog/fmt/xchar.h>
# endif
#else
# define SPDLOG_FMT_RUNTIME(format_string) format_string
#endif
// visual studio upto 2013 does not support noexcept nor constexpr // visual studio upto 2013 does not support noexcept nor constexpr
#if defined(_MSC_VER) && (_MSC_VER < 1900) #if defined(_MSC_VER) && (_MSC_VER < 1900)
#define SPDLOG_NOEXCEPT _NOEXCEPT # define SPDLOG_NOEXCEPT _NOEXCEPT
#define SPDLOG_CONSTEXPR # define SPDLOG_CONSTEXPR
#else #else
#define SPDLOG_NOEXCEPT noexcept # define SPDLOG_NOEXCEPT noexcept
#define SPDLOG_CONSTEXPR constexpr # define SPDLOG_CONSTEXPR constexpr
#endif #endif
#if defined(__GNUC__) || defined(__clang__) #if defined(__GNUC__) || defined(__clang__)
#define SPDLOG_DEPRECATED __attribute__((deprecated)) # define SPDLOG_DEPRECATED __attribute__((deprecated))
#elif defined(_MSC_VER) #elif defined(_MSC_VER)
#define SPDLOG_DEPRECATED __declspec(deprecated) # define SPDLOG_DEPRECATED __declspec(deprecated)
#else #else
#define SPDLOG_DEPRECATED # define SPDLOG_DEPRECATED
#endif #endif
// disable thread local on msvc 2013 // disable thread local on msvc 2013
#ifndef SPDLOG_NO_TLS #ifndef SPDLOG_NO_TLS
#if (defined(_MSC_VER) && (_MSC_VER < 1900)) || defined(__cplusplus_winrt) # if (defined(_MSC_VER) && (_MSC_VER < 1900)) || defined(__cplusplus_winrt)
#define SPDLOG_NO_TLS 1 # define SPDLOG_NO_TLS 1
#endif # endif
#endif #endif
#ifndef SPDLOG_FUNCTION #ifndef SPDLOG_FUNCTION
#define SPDLOG_FUNCTION static_cast<const char *>(__FUNCTION__) # define SPDLOG_FUNCTION static_cast<const char *>(__FUNCTION__)
#endif #endif
#ifdef SPDLOG_NO_EXCEPTIONS #ifdef SPDLOG_NO_EXCEPTIONS
#define SPDLOG_TRY # define SPDLOG_TRY
#define SPDLOG_THROW(ex) \ # define SPDLOG_THROW(ex) \
do \ do \
{ \ { \
printf("spdlog fatal error: %s\n", ex.what()); \ printf("spdlog fatal error: %s\n", ex.what()); \
std::abort(); \ std::abort(); \
} while (0) } while (0)
#define SPDLOG_CATCH_ALL() # define SPDLOG_CATCH_STD
#else #else
#define SPDLOG_TRY try # define SPDLOG_TRY try
#define SPDLOG_THROW(ex) throw(ex) # define SPDLOG_THROW(ex) throw(ex)
#define SPDLOG_CATCH_ALL() catch (...) # define SPDLOG_CATCH_STD \
catch (const std::exception &) {}
#endif #endif
namespace spdlog { namespace spdlog {
@@ -89,11 +100,11 @@ class sink;
#if defined(_WIN32) && defined(SPDLOG_WCHAR_FILENAMES) #if defined(_WIN32) && defined(SPDLOG_WCHAR_FILENAMES)
using filename_t = std::wstring; using filename_t = std::wstring;
// allow macro expansion to occur in SPDLOG_FILENAME_T // allow macro expansion to occur in SPDLOG_FILENAME_T
#define SPDLOG_FILENAME_T_INNER(s) L##s # define SPDLOG_FILENAME_T_INNER(s) L##s
#define SPDLOG_FILENAME_T(s) SPDLOG_FILENAME_T_INNER(s) # define SPDLOG_FILENAME_T(s) SPDLOG_FILENAME_T_INNER(s)
#else #else
using filename_t = std::string; using filename_t = std::string;
#define SPDLOG_FILENAME_T(s) s # define SPDLOG_FILENAME_T(s) s
#endif #endif
using log_clock = std::chrono::system_clock; using log_clock = std::chrono::system_clock;
@@ -106,19 +117,30 @@ using memory_buf_t = fmt::basic_memory_buffer<char, 250>;
using wmemory_buf_t = fmt::basic_memory_buffer<wchar_t, 250>; using wmemory_buf_t = fmt::basic_memory_buffer<wchar_t, 250>;
#ifdef SPDLOG_WCHAR_TO_UTF8_SUPPORT #ifdef SPDLOG_WCHAR_TO_UTF8_SUPPORT
#ifndef _WIN32 # ifndef _WIN32
#error SPDLOG_WCHAR_TO_UTF8_SUPPORT only supported on windows # error SPDLOG_WCHAR_TO_UTF8_SUPPORT only supported on windows
#else # else
template<typename T> template<typename T>
struct is_convertible_to_wstring_view : std::is_convertible<T, wstring_view_t> struct is_convertible_to_wstring_view : std::is_convertible<T, wstring_view_t>
{}; {};
#endif // _WIN32 template<class T>
struct is_convertible_to_wformat_string : std::is_convertible<T, fmt::wformat_string<>>
{};
# endif // _WIN32
#else #else
template<typename> template<typename>
struct is_convertible_to_wstring_view : std::false_type struct is_convertible_to_wstring_view : std::false_type
{}; {};
template<class>
struct is_convertible_to_wformat_string : std::false_type
{};
#endif // SPDLOG_WCHAR_TO_UTF8_SUPPORT #endif // SPDLOG_WCHAR_TO_UTF8_SUPPORT
template<class T>
struct is_convertible_to_basic_format_string
: std::integral_constant<bool, std::is_convertible<const T &, fmt::format_string<>>::value || is_convertible_to_wformat_string<T>::value>
{};
#if defined(SPDLOG_NO_ATOMIC_LEVELS) #if defined(SPDLOG_NO_ATOMIC_LEVELS)
using level_t = details::null_atomic_int; using level_t = details::null_atomic_int;
#else #else
@@ -134,7 +156,7 @@ using level_t = std::atomic<int>;
#define SPDLOG_LEVEL_OFF 6 #define SPDLOG_LEVEL_OFF 6
#if !defined(SPDLOG_ACTIVE_LEVEL) #if !defined(SPDLOG_ACTIVE_LEVEL)
#define SPDLOG_ACTIVE_LEVEL SPDLOG_LEVEL_INFO # define SPDLOG_ACTIVE_LEVEL SPDLOG_LEVEL_INFO
#endif #endif
// Log level enum // Log level enum
@@ -151,23 +173,31 @@ enum level_enum
n_levels n_levels
}; };
#define SPDLOG_LEVEL_NAME_TRACE string_view_t("trace", 5)
#define SPDLOG_LEVEL_NAME_DEBUG string_view_t("debug", 5)
#define SPDLOG_LEVEL_NAME_INFO string_view_t("info", 4)
#define SPDLOG_LEVEL_NAME_WARNING string_view_t("warning", 7)
#define SPDLOG_LEVEL_NAME_ERROR string_view_t("error", 5)
#define SPDLOG_LEVEL_NAME_CRITICAL string_view_t("critical", 8)
#define SPDLOG_LEVEL_NAME_OFF string_view_t("off", 3)
#if !defined(SPDLOG_LEVEL_NAMES) #if !defined(SPDLOG_LEVEL_NAMES)
#define SPDLOG_LEVEL_NAMES \ # define SPDLOG_LEVEL_NAMES \
{ \ { \
"trace", "debug", "info", "warning", "error", "critical", "off" \ SPDLOG_LEVEL_NAME_TRACE, SPDLOG_LEVEL_NAME_DEBUG, SPDLOG_LEVEL_NAME_INFO, SPDLOG_LEVEL_NAME_WARNING, SPDLOG_LEVEL_NAME_ERROR, \
} SPDLOG_LEVEL_NAME_CRITICAL, SPDLOG_LEVEL_NAME_OFF \
}
#endif #endif
#if !defined(SPDLOG_SHORT_LEVEL_NAMES) #if !defined(SPDLOG_SHORT_LEVEL_NAMES)
#define SPDLOG_SHORT_LEVEL_NAMES \ # define SPDLOG_SHORT_LEVEL_NAMES \
{ \ { \
"T", "D", "I", "W", "E", "C", "O" \ "T", "D", "I", "W", "E", "C", "O" \
} }
#endif #endif
SPDLOG_API const string_view_t &to_string_view(spdlog::level::level_enum l) SPDLOG_NOEXCEPT; SPDLOG_API const string_view_t &to_string_view(spdlog::level::level_enum l) SPDLOG_NOEXCEPT;
SPDLOG_API void set_string_view(spdlog::level::level_enum l, const string_view_t &s) SPDLOG_NOEXCEPT;
SPDLOG_API const char *to_short_c_str(spdlog::level::level_enum l) SPDLOG_NOEXCEPT; SPDLOG_API const char *to_short_c_str(spdlog::level::level_enum l) SPDLOG_NOEXCEPT;
SPDLOG_API spdlog::level::level_enum from_str(const std::string &name) SPDLOG_NOEXCEPT; SPDLOG_API spdlog::level::level_enum from_str(const std::string &name) SPDLOG_NOEXCEPT;
@@ -245,5 +275,5 @@ std::unique_ptr<T> make_unique(Args &&...args)
} // namespace spdlog } // namespace spdlog
#ifdef SPDLOG_HEADER_ONLY #ifdef SPDLOG_HEADER_ONLY
#include "common-inl.h" # include "common-inl.h"
#endif #endif

View File

@@ -4,7 +4,7 @@
#pragma once #pragma once
#ifndef SPDLOG_HEADER_ONLY #ifndef SPDLOG_HEADER_ONLY
#include <spdlog/details/backtracer.h> # include <spdlog/details/backtracer.h>
#endif #endif
namespace spdlog { namespace spdlog {
namespace details { namespace details {

View File

@@ -41,5 +41,5 @@ public:
} // namespace spdlog } // namespace spdlog
#ifdef SPDLOG_HEADER_ONLY #ifdef SPDLOG_HEADER_ONLY
#include "backtracer-inl.h" # include "backtracer-inl.h"
#endif #endif

View File

@@ -4,7 +4,7 @@
#pragma once #pragma once
#ifndef SPDLOG_HEADER_ONLY #ifndef SPDLOG_HEADER_ONLY
#include <spdlog/details/file_helper.h> # include <spdlog/details/file_helper.h>
#endif #endif
#include <spdlog/details/os.h> #include <spdlog/details/os.h>

View File

@@ -47,7 +47,7 @@ public:
private: private:
const int open_tries_ = 5; const int open_tries_ = 5;
const int open_interval_ = 10; const unsigned int open_interval_ = 10;
std::FILE *fd_{nullptr}; std::FILE *fd_{nullptr};
filename_t filename_; filename_t filename_;
}; };
@@ -55,5 +55,5 @@ private:
} // namespace spdlog } // namespace spdlog
#ifdef SPDLOG_HEADER_ONLY #ifdef SPDLOG_HEADER_ONLY
#include "file_helper-inl.h" # include "file_helper-inl.h"
#endif #endif

View File

@@ -4,6 +4,7 @@
#include <chrono> #include <chrono>
#include <type_traits> #include <type_traits>
#include <iterator>
#include <spdlog/fmt/fmt.h> #include <spdlog/fmt/fmt.h>
#include <spdlog/common.h> #include <spdlog/common.h>
@@ -54,7 +55,7 @@ inline void pad2(int n, memory_buf_t &dest)
} }
else // unlikely, but just in case, let fmt deal with it else // unlikely, but just in case, let fmt deal with it
{ {
fmt::format_to(dest, "{:02}", n); fmt::format_to(std::back_inserter(dest), SPDLOG_FMT_RUNTIME("{:02}"), n);
} }
} }

View File

@@ -4,7 +4,7 @@
#pragma once #pragma once
#ifndef SPDLOG_HEADER_ONLY #ifndef SPDLOG_HEADER_ONLY
#include <spdlog/details/log_msg.h> # include <spdlog/details/log_msg.h>
#endif #endif
#include <spdlog/details/os.h> #include <spdlog/details/os.h>

View File

@@ -15,6 +15,7 @@ struct SPDLOG_API log_msg
log_msg(source_loc loc, string_view_t logger_name, level::level_enum lvl, string_view_t msg); log_msg(source_loc loc, string_view_t logger_name, level::level_enum lvl, string_view_t msg);
log_msg(string_view_t logger_name, level::level_enum lvl, string_view_t msg); log_msg(string_view_t logger_name, level::level_enum lvl, string_view_t msg);
log_msg(const log_msg &other) = default; log_msg(const log_msg &other) = default;
log_msg &operator=(const log_msg &other) = default;
string_view_t logger_name; string_view_t logger_name;
level::level_enum level{level::off}; level::level_enum level{level::off};
@@ -32,5 +33,5 @@ struct SPDLOG_API log_msg
} // namespace spdlog } // namespace spdlog
#ifdef SPDLOG_HEADER_ONLY #ifdef SPDLOG_HEADER_ONLY
#include "log_msg-inl.h" # include "log_msg-inl.h"
#endif #endif

View File

@@ -4,7 +4,7 @@
#pragma once #pragma once
#ifndef SPDLOG_HEADER_ONLY #ifndef SPDLOG_HEADER_ONLY
#include <spdlog/details/log_msg_buffer.h> # include <spdlog/details/log_msg_buffer.h>
#endif #endif
namespace spdlog { namespace spdlog {
@@ -26,9 +26,7 @@ SPDLOG_INLINE log_msg_buffer::log_msg_buffer(const log_msg_buffer &other)
update_string_views(); update_string_views();
} }
SPDLOG_INLINE log_msg_buffer::log_msg_buffer(log_msg_buffer &&other) SPDLOG_NOEXCEPT SPDLOG_INLINE log_msg_buffer::log_msg_buffer(log_msg_buffer &&other) SPDLOG_NOEXCEPT : log_msg{other}, buffer{std::move(other.buffer)}
: log_msg{other}
, buffer{std::move(other.buffer)}
{ {
update_string_views(); update_string_views();
} }

View File

@@ -29,5 +29,5 @@ public:
} // namespace spdlog } // namespace spdlog
#ifdef SPDLOG_HEADER_ONLY #ifdef SPDLOG_HEADER_ONLY
#include "log_msg_buffer-inl.h" # include "log_msg_buffer-inl.h"
#endif #endif

View File

@@ -4,7 +4,7 @@
#pragma once #pragma once
#ifndef SPDLOG_HEADER_ONLY #ifndef SPDLOG_HEADER_ONLY
#include <spdlog/details/os.h> # include <spdlog/details/os.h>
#endif #endif
#include <spdlog/common.h> #include <spdlog/common.h>
@@ -23,45 +23,45 @@
#ifdef _WIN32 #ifdef _WIN32
#include <io.h> // _get_osfhandle and _isatty support # include <io.h> // _get_osfhandle and _isatty support
#include <process.h> // _get_pid support # include <process.h> // _get_pid support
#include <spdlog/details/windows_include.h> # include <spdlog/details/windows_include.h>
#ifdef __MINGW32__ # ifdef __MINGW32__
#include <share.h> # include <share.h>
#endif # endif
#if defined(SPDLOG_WCHAR_TO_UTF8_SUPPORT) || defined(SPDLOG_WCHAR_FILENAMES) # if defined(SPDLOG_WCHAR_TO_UTF8_SUPPORT) || defined(SPDLOG_WCHAR_FILENAMES)
#include <limits> # include <limits>
#endif # endif
#include <direct.h> // for _mkdir/_wmkdir # include <direct.h> // for _mkdir/_wmkdir
#else // unix #else // unix
#include <fcntl.h> # include <fcntl.h>
#include <unistd.h> # include <unistd.h>
#ifdef __linux__ # ifdef __linux__
#include <sys/syscall.h> //Use gettid() syscall under linux to get thread id # include <sys/syscall.h> //Use gettid() syscall under linux to get thread id
#elif defined(_AIX) # elif defined(_AIX)
#include <pthread.h> // for pthread_getthreadid_np # include <pthread.h> // for pthread_getthreadid_np
#elif defined(__DragonFly__) || defined(__FreeBSD__) # elif defined(__DragonFly__) || defined(__FreeBSD__)
#include <pthread_np.h> // for pthread_getthreadid_np # include <pthread_np.h> // for pthread_getthreadid_np
#elif defined(__NetBSD__) # elif defined(__NetBSD__)
#include <lwp.h> // for _lwp_self # include <lwp.h> // for _lwp_self
#elif defined(__sun) # elif defined(__sun)
#include <thread.h> // for thr_self # include <thread.h> // for thr_self
#endif # endif
#endif // unix #endif // unix
#ifndef __has_feature // Clang - feature checking macros. #ifndef __has_feature // Clang - feature checking macros.
#define __has_feature(x) 0 // Compatibility with non-clang compilers. # define __has_feature(x) 0 // Compatibility with non-clang compilers.
#endif #endif
namespace spdlog { namespace spdlog {
@@ -123,12 +123,12 @@ SPDLOG_INLINE std::tm gmtime() SPDLOG_NOEXCEPT
SPDLOG_INLINE bool fopen_s(FILE **fp, const filename_t &filename, const filename_t &mode) SPDLOG_INLINE bool fopen_s(FILE **fp, const filename_t &filename, const filename_t &mode)
{ {
#ifdef _WIN32 #ifdef _WIN32
#ifdef SPDLOG_WCHAR_FILENAMES # ifdef SPDLOG_WCHAR_FILENAMES
*fp = ::_wfsopen((filename.c_str()), mode.c_str(), _SH_DENYNO); *fp = ::_wfsopen((filename.c_str()), mode.c_str(), _SH_DENYNO);
#else # else
*fp = ::_fsopen((filename.c_str()), mode.c_str(), _SH_DENYNO); *fp = ::_fsopen((filename.c_str()), mode.c_str(), _SH_DENYNO);
#endif # endif
#if defined(SPDLOG_PREVENT_CHILD_FD) # if defined(SPDLOG_PREVENT_CHILD_FD)
if (*fp != nullptr) if (*fp != nullptr)
{ {
auto file_handle = reinterpret_cast<HANDLE>(_get_osfhandle(::_fileno(*fp))); auto file_handle = reinterpret_cast<HANDLE>(_get_osfhandle(::_fileno(*fp)));
@@ -138,9 +138,9 @@ SPDLOG_INLINE bool fopen_s(FILE **fp, const filename_t &filename, const filename
*fp = nullptr; *fp = nullptr;
} }
} }
#endif # endif
#else // unix #else // unix
#if defined(SPDLOG_PREVENT_CHILD_FD) # if defined(SPDLOG_PREVENT_CHILD_FD)
const int mode_flag = mode == SPDLOG_FILENAME_T("ab") ? O_APPEND : O_TRUNC; const int mode_flag = mode == SPDLOG_FILENAME_T("ab") ? O_APPEND : O_TRUNC;
const int fd = ::open((filename.c_str()), O_CREAT | O_WRONLY | O_CLOEXEC | mode_flag, mode_t(0644)); const int fd = ::open((filename.c_str()), O_CREAT | O_WRONLY | O_CLOEXEC | mode_flag, mode_t(0644));
if (fd == -1) if (fd == -1)
@@ -152,9 +152,9 @@ SPDLOG_INLINE bool fopen_s(FILE **fp, const filename_t &filename, const filename
{ {
::close(fd); ::close(fd);
} }
#else # else
*fp = ::fopen((filename.c_str()), mode.c_str()); *fp = ::fopen((filename.c_str()), mode.c_str());
#endif # endif
#endif #endif
return *fp == nullptr; return *fp == nullptr;
@@ -187,11 +187,11 @@ SPDLOG_INLINE int rename(const filename_t &filename1, const filename_t &filename
SPDLOG_INLINE bool path_exists(const filename_t &filename) SPDLOG_NOEXCEPT SPDLOG_INLINE bool path_exists(const filename_t &filename) SPDLOG_NOEXCEPT
{ {
#ifdef _WIN32 #ifdef _WIN32
#ifdef SPDLOG_WCHAR_FILENAMES # ifdef SPDLOG_WCHAR_FILENAMES
auto attribs = ::GetFileAttributesW(filename.c_str()); auto attribs = ::GetFileAttributesW(filename.c_str());
#else # else
auto attribs = ::GetFileAttributesA(filename.c_str()); auto attribs = ::GetFileAttributesA(filename.c_str());
#endif # endif
return attribs != INVALID_FILE_ATTRIBUTES; return attribs != INVALID_FILE_ATTRIBUTES;
#else // common linux/unix all have the stat system call #else // common linux/unix all have the stat system call
struct stat buffer; struct stat buffer;
@@ -199,6 +199,12 @@ SPDLOG_INLINE bool path_exists(const filename_t &filename) SPDLOG_NOEXCEPT
#endif #endif
} }
#ifdef _MSC_VER
// avoid warning about unreachable statement at the end of filesize()
# pragma warning(push)
# pragma warning(disable : 4702)
#endif
// Return file size according to open FILE* object // Return file size according to open FILE* object
SPDLOG_INLINE size_t filesize(FILE *f) SPDLOG_INLINE size_t filesize(FILE *f)
{ {
@@ -208,59 +214,63 @@ SPDLOG_INLINE size_t filesize(FILE *f)
} }
#if defined(_WIN32) && !defined(__CYGWIN__) #if defined(_WIN32) && !defined(__CYGWIN__)
int fd = ::_fileno(f); int fd = ::_fileno(f);
#if _WIN64 // 64 bits # if defined(_WIN64) // 64 bits
__int64 ret = ::_filelengthi64(fd); __int64 ret = ::_filelengthi64(fd);
if (ret >= 0) if (ret >= 0)
{ {
return static_cast<size_t>(ret); return static_cast<size_t>(ret);
} }
#else // windows 32 bits # else // windows 32 bits
long ret = ::_filelength(fd); long ret = ::_filelength(fd);
if (ret >= 0) if (ret >= 0)
{ {
return static_cast<size_t>(ret); return static_cast<size_t>(ret);
} }
#endif # endif
#else // unix #else // unix
// OpenBSD doesn't compile with :: before the fileno(..) // OpenBSD doesn't compile with :: before the fileno(..)
#if defined(__OpenBSD__) # if defined(__OpenBSD__)
int fd = fileno(f); int fd = fileno(f);
#else # else
int fd = ::fileno(f); int fd = ::fileno(f);
#endif # endif
// 64 bits(but not in osx or cygwin, where fstat64 is deprecated) // 64 bits(but not in osx or cygwin, where fstat64 is deprecated)
#if (defined(__linux__) || defined(__sun) || defined(_AIX)) && (defined(__LP64__) || defined(_LP64)) # if (defined(__linux__) || defined(__sun) || defined(_AIX)) && (defined(__LP64__) || defined(_LP64))
struct stat64 st; struct stat64 st;
if (::fstat64(fd, &st) == 0) if (::fstat64(fd, &st) == 0)
{ {
return static_cast<size_t>(st.st_size); return static_cast<size_t>(st.st_size);
} }
#else // other unix or linux 32 bits or cygwin # else // other unix or linux 32 bits or cygwin
struct stat st; struct stat st;
if (::fstat(fd, &st) == 0) if (::fstat(fd, &st) == 0)
{ {
return static_cast<size_t>(st.st_size); return static_cast<size_t>(st.st_size);
} }
#endif # endif
#endif #endif
throw_spdlog_ex("Failed getting file size from fd", errno); throw_spdlog_ex("Failed getting file size from fd", errno);
return 0; // will not be reached. return 0; // will not be reached.
} }
#ifdef _MSC_VER
# pragma warning(pop)
#endif
// Return utc offset in minutes or throw spdlog_ex on failure // Return utc offset in minutes or throw spdlog_ex on failure
SPDLOG_INLINE int utc_minutes_offset(const std::tm &tm) SPDLOG_INLINE int utc_minutes_offset(const std::tm &tm)
{ {
#ifdef _WIN32 #ifdef _WIN32
#if _WIN32_WINNT < _WIN32_WINNT_WS08 # if _WIN32_WINNT < _WIN32_WINNT_WS08
TIME_ZONE_INFORMATION tzinfo; TIME_ZONE_INFORMATION tzinfo;
auto rv = ::GetTimeZoneInformation(&tzinfo); auto rv = ::GetTimeZoneInformation(&tzinfo);
#else # else
DYNAMIC_TIME_ZONE_INFORMATION tzinfo; DYNAMIC_TIME_ZONE_INFORMATION tzinfo;
auto rv = ::GetDynamicTimeZoneInformation(&tzinfo); auto rv = ::GetDynamicTimeZoneInformation(&tzinfo);
#endif # endif
if (rv == TIME_ZONE_ID_INVALID) if (rv == TIME_ZONE_ID_INVALID)
throw_spdlog_ex("Failed getting timezone info. ", errno); throw_spdlog_ex("Failed getting timezone info. ", errno);
@@ -276,7 +286,7 @@ SPDLOG_INLINE int utc_minutes_offset(const std::tm &tm)
return offset; return offset;
#else #else
#if defined(sun) || defined(__sun) || defined(_AIX) || (!defined(_BSD_SOURCE) && !defined(_GNU_SOURCE)) # if defined(sun) || defined(__sun) || defined(_AIX) || (!defined(_BSD_SOURCE) && !defined(_GNU_SOURCE))
// 'tm_gmtoff' field is BSD extension and it's missing on SunOS/Solaris // 'tm_gmtoff' field is BSD extension and it's missing on SunOS/Solaris
struct helper struct helper
{ {
@@ -306,9 +316,9 @@ SPDLOG_INLINE int utc_minutes_offset(const std::tm &tm)
}; };
auto offset_seconds = helper::calculate_gmt_offset(tm); auto offset_seconds = helper::calculate_gmt_offset(tm);
#else # else
auto offset_seconds = tm.tm_gmtoff; auto offset_seconds = tm.tm_gmtoff;
#endif # endif
return static_cast<int>(offset_seconds / 60); return static_cast<int>(offset_seconds / 60);
#endif #endif
@@ -322,9 +332,9 @@ SPDLOG_INLINE size_t _thread_id() SPDLOG_NOEXCEPT
#ifdef _WIN32 #ifdef _WIN32
return static_cast<size_t>(::GetCurrentThreadId()); return static_cast<size_t>(::GetCurrentThreadId());
#elif defined(__linux__) #elif defined(__linux__)
#if defined(__ANDROID__) && defined(__ANDROID_API__) && (__ANDROID_API__ < 21) # if defined(__ANDROID__) && defined(__ANDROID_API__) && (__ANDROID_API__ < 21)
#define SYS_gettid __NR_gettid # define SYS_gettid __NR_gettid
#endif # endif
return static_cast<size_t>(::syscall(SYS_gettid)); return static_cast<size_t>(::syscall(SYS_gettid));
#elif defined(_AIX) || defined(__DragonFly__) || defined(__FreeBSD__) #elif defined(_AIX) || defined(__DragonFly__) || defined(__FreeBSD__)
return static_cast<size_t>(::pthread_getthreadid_np()); return static_cast<size_t>(::pthread_getthreadid_np());
@@ -356,7 +366,7 @@ SPDLOG_INLINE size_t thread_id() SPDLOG_NOEXCEPT
// This is avoid msvc issue in sleep_for that happens if the clock changes. // This is avoid msvc issue in sleep_for that happens if the clock changes.
// See https://github.com/gabime/spdlog/issues/609 // See https://github.com/gabime/spdlog/issues/609
SPDLOG_INLINE void sleep_for_millis(int milliseconds) SPDLOG_NOEXCEPT SPDLOG_INLINE void sleep_for_millis(unsigned int milliseconds) SPDLOG_NOEXCEPT
{ {
#if defined(_WIN32) #if defined(_WIN32)
::Sleep(milliseconds); ::Sleep(milliseconds);
@@ -405,8 +415,8 @@ SPDLOG_INLINE bool is_color_terminal() SPDLOG_NOEXCEPT
return true; return true;
} }
static constexpr std::array<const char *, 15> terms = {{"ansi", "color", "console", "cygwin", "gnome", "konsole", "kterm", "linux", static constexpr std::array<const char *, 16> terms = {{"ansi", "color", "console", "cygwin", "gnome", "konsole", "kterm", "linux",
"msys", "putty", "rxvt", "screen", "vt100", "xterm", "alacritty"}}; "msys", "putty", "rxvt", "screen", "vt100", "xterm", "alacritty", "vt102"}};
const char *env_term_p = std::getenv("TERM"); const char *env_term_p = std::getenv("TERM");
if (env_term_p == nullptr) if (env_term_p == nullptr)
@@ -509,11 +519,11 @@ SPDLOG_INLINE void utf8_to_wstrbuf(string_view_t str, wmemory_buf_t &target)
static SPDLOG_INLINE bool mkdir_(const filename_t &path) static SPDLOG_INLINE bool mkdir_(const filename_t &path)
{ {
#ifdef _WIN32 #ifdef _WIN32
#ifdef SPDLOG_WCHAR_FILENAMES # ifdef SPDLOG_WCHAR_FILENAMES
return ::_wmkdir(path.c_str()) == 0; return ::_wmkdir(path.c_str()) == 0;
#else # else
return ::_mkdir(path.c_str()) == 0; return ::_mkdir(path.c_str()) == 0;
#endif # endif
#else #else
return ::mkdir(path.c_str(), mode_t(0755)) == 0; return ::mkdir(path.c_str(), mode_t(0755)) == 0;
#endif #endif
@@ -570,14 +580,14 @@ std::string SPDLOG_INLINE getenv(const char *field)
{ {
#if defined(_MSC_VER) #if defined(_MSC_VER)
#if defined(__cplusplus_winrt) # if defined(__cplusplus_winrt)
return std::string{}; // not supported under uwp return std::string{}; // not supported under uwp
#else # else
size_t len = 0; size_t len = 0;
char buf[128]; char buf[128];
bool ok = ::getenv_s(&len, buf, sizeof(buf), field) == 0; bool ok = ::getenv_s(&len, buf, sizeof(buf), field) == 0;
return ok ? buf : std::string{}; return ok ? buf : std::string{};
#endif # endif
#else // revert to getenv #else // revert to getenv
char *buf = ::getenv(field); char *buf = ::getenv(field);
return buf ? buf : std::string{}; return buf ? buf : std::string{};

View File

@@ -22,22 +22,22 @@ SPDLOG_API std::tm gmtime() SPDLOG_NOEXCEPT;
// eol definition // eol definition
#if !defined(SPDLOG_EOL) #if !defined(SPDLOG_EOL)
#ifdef _WIN32 # ifdef _WIN32
#define SPDLOG_EOL "\r\n" # define SPDLOG_EOL "\r\n"
#else # else
#define SPDLOG_EOL "\n" # define SPDLOG_EOL "\n"
#endif # endif
#endif #endif
SPDLOG_CONSTEXPR static const char *default_eol = SPDLOG_EOL; SPDLOG_CONSTEXPR static const char *default_eol = SPDLOG_EOL;
// folder separator // folder separator
#if !defined(SPDLOG_FOLDER_SEPS) #if !defined(SPDLOG_FOLDER_SEPS)
#ifdef _WIN32 # ifdef _WIN32
#define SPDLOG_FOLDER_SEPS "\\/" # define SPDLOG_FOLDER_SEPS "\\/"
#else # else
#define SPDLOG_FOLDER_SEPS "/" # define SPDLOG_FOLDER_SEPS "/"
#endif # endif
#endif #endif
SPDLOG_CONSTEXPR static const char folder_seps[] = SPDLOG_FOLDER_SEPS; SPDLOG_CONSTEXPR static const char folder_seps[] = SPDLOG_FOLDER_SEPS;
@@ -74,7 +74,7 @@ SPDLOG_API size_t thread_id() SPDLOG_NOEXCEPT;
// This is avoid msvc issue in sleep_for that happens if the clock changes. // This is avoid msvc issue in sleep_for that happens if the clock changes.
// See https://github.com/gabime/spdlog/issues/609 // See https://github.com/gabime/spdlog/issues/609
SPDLOG_API void sleep_for_millis(int milliseconds) SPDLOG_NOEXCEPT; SPDLOG_API void sleep_for_millis(unsigned int milliseconds) SPDLOG_NOEXCEPT;
SPDLOG_API std::string filename_to_str(const filename_t &filename); SPDLOG_API std::string filename_to_str(const filename_t &filename);
@@ -114,5 +114,5 @@ SPDLOG_API std::string getenv(const char *field);
} // namespace spdlog } // namespace spdlog
#ifdef SPDLOG_HEADER_ONLY #ifdef SPDLOG_HEADER_ONLY
#include "os-inl.h" # include "os-inl.h"
#endif #endif

View File

@@ -4,7 +4,7 @@
#pragma once #pragma once
#ifndef SPDLOG_HEADER_ONLY #ifndef SPDLOG_HEADER_ONLY
#include <spdlog/details/periodic_worker.h> # include <spdlog/details/periodic_worker.h>
#endif #endif
namespace spdlog { namespace spdlog {

View File

@@ -36,5 +36,5 @@ private:
} // namespace spdlog } // namespace spdlog
#ifdef SPDLOG_HEADER_ONLY #ifdef SPDLOG_HEADER_ONLY
#include "periodic_worker-inl.h" # include "periodic_worker-inl.h"
#endif #endif

View File

@@ -4,7 +4,7 @@
#pragma once #pragma once
#ifndef SPDLOG_HEADER_ONLY #ifndef SPDLOG_HEADER_ONLY
#include <spdlog/details/registry.h> # include <spdlog/details/registry.h>
#endif #endif
#include <spdlog/common.h> #include <spdlog/common.h>
@@ -14,11 +14,11 @@
#ifndef SPDLOG_DISABLE_DEFAULT_LOGGER #ifndef SPDLOG_DISABLE_DEFAULT_LOGGER
// support for the default stdout color logger // support for the default stdout color logger
#ifdef _WIN32 # ifdef _WIN32
#include <spdlog/sinks/wincolor_sink.h> # include <spdlog/sinks/wincolor_sink.h>
#else # else
#include <spdlog/sinks/ansicolor_sink.h> # include <spdlog/sinks/ansicolor_sink.h>
#endif # endif
#endif // SPDLOG_DISABLE_DEFAULT_LOGGER #endif // SPDLOG_DISABLE_DEFAULT_LOGGER
#include <chrono> #include <chrono>
@@ -36,11 +36,11 @@ SPDLOG_INLINE registry::registry()
#ifndef SPDLOG_DISABLE_DEFAULT_LOGGER #ifndef SPDLOG_DISABLE_DEFAULT_LOGGER
// create default logger (ansicolor_stdout_sink_mt or wincolor_stdout_sink_mt in windows). // create default logger (ansicolor_stdout_sink_mt or wincolor_stdout_sink_mt in windows).
#ifdef _WIN32 # ifdef _WIN32
auto color_sink = std::make_shared<sinks::wincolor_stdout_sink_mt>(); auto color_sink = std::make_shared<sinks::wincolor_stdout_sink_mt>();
#else # else
auto color_sink = std::make_shared<sinks::ansicolor_stdout_sink_mt>(); auto color_sink = std::make_shared<sinks::ansicolor_stdout_sink_mt>();
#endif # endif
const char *default_logger_name = ""; const char *default_logger_name = "";
default_logger_ = std::make_shared<spdlog::logger>(default_logger_name, std::move(color_sink)); default_logger_ = std::make_shared<spdlog::logger>(default_logger_name, std::move(color_sink));
@@ -195,14 +195,14 @@ SPDLOG_INLINE void registry::flush_every(std::chrono::seconds interval)
periodic_flusher_ = details::make_unique<periodic_worker>(clbk, interval); periodic_flusher_ = details::make_unique<periodic_worker>(clbk, interval);
} }
SPDLOG_INLINE void registry::set_error_handler(void (*handler)(const std::string &msg)) SPDLOG_INLINE void registry::set_error_handler(err_handler handler)
{ {
std::lock_guard<std::mutex> lock(logger_map_mutex_); std::lock_guard<std::mutex> lock(logger_map_mutex_);
for (auto &l : loggers_) for (auto &l : loggers_)
{ {
l.second->set_error_handler(handler); l.second->set_error_handler(handler);
} }
err_handler_ = handler; err_handler_ = std::move(handler);
} }
SPDLOG_INLINE void registry::apply_all(const std::function<void(const std::shared_ptr<logger>)> &fun) SPDLOG_INLINE void registry::apply_all(const std::function<void(const std::shared_ptr<logger>)> &fun)

View File

@@ -63,7 +63,7 @@ public:
void flush_every(std::chrono::seconds interval); void flush_every(std::chrono::seconds interval);
void set_error_handler(void (*handler)(const std::string &msg)); void set_error_handler(err_handler handler);
void apply_all(const std::function<void(const std::shared_ptr<logger>)> &fun); void apply_all(const std::function<void(const std::shared_ptr<logger>)> &fun);
@@ -99,7 +99,7 @@ private:
std::unique_ptr<formatter> formatter_; std::unique_ptr<formatter> formatter_;
spdlog::level::level_enum global_log_level_ = level::info; spdlog::level::level_enum global_log_level_ = level::info;
level::level_enum flush_level_ = level::off; level::level_enum flush_level_ = level::off;
void (*err_handler_)(const std::string &msg) = nullptr; err_handler err_handler_;
std::shared_ptr<thread_pool> tp_; std::shared_ptr<thread_pool> tp_;
std::unique_ptr<periodic_worker> periodic_flusher_; std::unique_ptr<periodic_worker> periodic_flusher_;
std::shared_ptr<logger> default_logger_; std::shared_ptr<logger> default_logger_;
@@ -111,5 +111,5 @@ private:
} // namespace spdlog } // namespace spdlog
#ifdef SPDLOG_HEADER_ONLY #ifdef SPDLOG_HEADER_ONLY
#include "registry-inl.h" # include "registry-inl.h"
#endif #endif

View File

@@ -21,4 +21,4 @@ struct synchronous_factory
return new_logger; return new_logger;
} }
}; };
} // namespace spdlog } // namespace spdlog

View File

@@ -144,7 +144,7 @@ public:
// set TCP_NODELAY // set TCP_NODELAY
int enable_flag = 1; int enable_flag = 1;
::setsockopt(socket_, IPPROTO_TCP, TCP_NODELAY, (char *)&enable_flag, sizeof(enable_flag)); ::setsockopt(socket_, IPPROTO_TCP, TCP_NODELAY, reinterpret_cast<char *>(&enable_flag), sizeof(enable_flag));
} }
// Send exactly n_bytes of the given data. // Send exactly n_bytes of the given data.

View File

@@ -4,7 +4,7 @@
#pragma once #pragma once
#ifdef _WIN32 #ifdef _WIN32
#error include tcp_client-windows.h instead # error include tcp_client-windows.h instead
#endif #endif
// tcp client helper // tcp client helper
@@ -103,15 +103,15 @@ public:
// set TCP_NODELAY // set TCP_NODELAY
int enable_flag = 1; int enable_flag = 1;
::setsockopt(socket_, IPPROTO_TCP, TCP_NODELAY, (char *)&enable_flag, sizeof(enable_flag)); ::setsockopt(socket_, IPPROTO_TCP, TCP_NODELAY, reinterpret_cast<char *>(&enable_flag), sizeof(enable_flag));
// prevent sigpipe on systems where MSG_NOSIGNAL is not available // prevent sigpipe on systems where MSG_NOSIGNAL is not available
#if defined(SO_NOSIGPIPE) && !defined(MSG_NOSIGNAL) #if defined(SO_NOSIGPIPE) && !defined(MSG_NOSIGNAL)
::setsockopt(socket_, SOL_SOCKET, SO_NOSIGPIPE, (char *)&enable_flag, sizeof(enable_flag)); ::setsockopt(socket_, SOL_SOCKET, SO_NOSIGPIPE, reinterpret_cast<char *>(&enable_flag), sizeof(enable_flag));
#endif #endif
#if !defined(SO_NOSIGPIPE) && !defined(MSG_NOSIGNAL) #if !defined(SO_NOSIGPIPE) && !defined(MSG_NOSIGNAL)
#error "tcp_sink would raise SIGPIPE since niether SO_NOSIGPIPE nor MSG_NOSIGNAL are available" # error "tcp_sink would raise SIGPIPE since niether SO_NOSIGPIPE nor MSG_NOSIGNAL are available"
#endif #endif
} }

View File

@@ -4,7 +4,7 @@
#pragma once #pragma once
#ifndef SPDLOG_HEADER_ONLY #ifndef SPDLOG_HEADER_ONLY
#include <spdlog/details/thread_pool.h> # include <spdlog/details/thread_pool.h>
#endif #endif
#include <spdlog/common.h> #include <spdlog/common.h>
@@ -49,7 +49,7 @@ SPDLOG_INLINE thread_pool::~thread_pool()
t.join(); t.join();
} }
} }
SPDLOG_CATCH_ALL() {} SPDLOG_CATCH_STD
} }
void SPDLOG_INLINE thread_pool::post_log(async_logger_ptr &&worker_ptr, const details::log_msg &msg, async_overflow_policy overflow_policy) void SPDLOG_INLINE thread_pool::post_log(async_logger_ptr &&worker_ptr, const details::log_msg &msg, async_overflow_policy overflow_policy)

View File

@@ -117,5 +117,5 @@ private:
} // namespace spdlog } // namespace spdlog
#ifdef SPDLOG_HEADER_ONLY #ifdef SPDLOG_HEADER_ONLY
#include "thread_pool-inl.h" # include "thread_pool-inl.h"
#endif #endif

View File

@@ -1,11 +1,11 @@
#pragma once #pragma once
#ifndef NOMINMAX #ifndef NOMINMAX
#define NOMINMAX // prevent windows redefining min/max # define NOMINMAX // prevent windows redefining min/max
#endif #endif
#ifndef WIN32_LEAN_AND_MEAN #ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN # define WIN32_LEAN_AND_MEAN
#endif #endif
#include <windows.h> #include <windows.h>

View File

@@ -89,7 +89,7 @@ struct formatter<spdlog::details::dump_info<T>>
// parse the format string flags // parse the format string flags
template<typename ParseContext> template<typename ParseContext>
auto parse(ParseContext &ctx) -> decltype(ctx.begin()) FMT_CONSTEXPR auto parse(ParseContext &ctx) -> decltype(ctx.begin())
{ {
auto it = ctx.begin(); auto it = ctx.begin();
while (it != ctx.end() && *it != '}') while (it != ctx.end() && *it != '}')
@@ -209,7 +209,7 @@ struct formatter<spdlog::details::dump_info<T>>
if (put_positions) if (put_positions)
{ {
fmt::format_to(inserter, "{:<04X}: ", pos); fmt::format_to(inserter, "{:04X}: ", pos);
} }
} }
}; };

View File

@@ -1,27 +0,0 @@
Copyright (c) 2012 - present, Victor Zverovich
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
--- Optional exception to the license ---
As an exception, if, as a result of your compiling your source code, portions
of this Software are embedded into a machine-executable object form of such
source code, you may redistribute such embedded portions in such object form
without including the above copyright and permission notices.

View File

@@ -0,0 +1,232 @@
// Formatting library for C++ - dynamic format arguments
//
// Copyright (c) 2012 - present, Victor Zverovich
// All rights reserved.
//
// For the license information refer to format.h.
#ifndef FMT_ARGS_H_
#define FMT_ARGS_H_
#include <functional> // std::reference_wrapper
#include <memory> // std::unique_ptr
#include <vector>
#include "core.h"
FMT_BEGIN_NAMESPACE
namespace detail {
template <typename T> struct is_reference_wrapper : std::false_type {};
template <typename T>
struct is_reference_wrapper<std::reference_wrapper<T>> : std::true_type {};
template <typename T> const T& unwrap(const T& v) { return v; }
template <typename T> const T& unwrap(const std::reference_wrapper<T>& v) {
return static_cast<const T&>(v);
}
class dynamic_arg_list {
// Workaround for clang's -Wweak-vtables. Unlike for regular classes, for
// templates it doesn't complain about inability to deduce single translation
// unit for placing vtable. So storage_node_base is made a fake template.
template <typename = void> struct node {
virtual ~node() = default;
std::unique_ptr<node<>> next;
};
template <typename T> struct typed_node : node<> {
T value;
template <typename Arg>
FMT_CONSTEXPR typed_node(const Arg& arg) : value(arg) {}
template <typename Char>
FMT_CONSTEXPR typed_node(const basic_string_view<Char>& arg)
: value(arg.data(), arg.size()) {}
};
std::unique_ptr<node<>> head_;
public:
template <typename T, typename Arg> const T& push(const Arg& arg) {
auto new_node = std::unique_ptr<typed_node<T>>(new typed_node<T>(arg));
auto& value = new_node->value;
new_node->next = std::move(head_);
head_ = std::move(new_node);
return value;
}
};
} // namespace detail
/**
\rst
A dynamic version of `fmt::format_arg_store`.
It's equipped with a storage to potentially temporary objects which lifetimes
could be shorter than the format arguments object.
It can be implicitly converted into `~fmt::basic_format_args` for passing
into type-erased formatting functions such as `~fmt::vformat`.
\endrst
*/
template <typename Context>
class dynamic_format_arg_store
#if FMT_GCC_VERSION && FMT_GCC_VERSION < 409
// Workaround a GCC template argument substitution bug.
: public basic_format_args<Context>
#endif
{
private:
using char_type = typename Context::char_type;
template <typename T> struct need_copy {
static constexpr detail::type mapped_type =
detail::mapped_type_constant<T, Context>::value;
enum {
value = !(detail::is_reference_wrapper<T>::value ||
std::is_same<T, basic_string_view<char_type>>::value ||
std::is_same<T, detail::std_string_view<char_type>>::value ||
(mapped_type != detail::type::cstring_type &&
mapped_type != detail::type::string_type &&
mapped_type != detail::type::custom_type))
};
};
template <typename T>
using stored_type = conditional_t<detail::is_string<T>::value &&
!has_formatter<T, Context>::value &&
!detail::is_reference_wrapper<T>::value,
std::basic_string<char_type>, T>;
// Storage of basic_format_arg must be contiguous.
std::vector<basic_format_arg<Context>> data_;
std::vector<detail::named_arg_info<char_type>> named_info_;
// Storage of arguments not fitting into basic_format_arg must grow
// without relocation because items in data_ refer to it.
detail::dynamic_arg_list dynamic_args_;
friend class basic_format_args<Context>;
unsigned long long get_types() const {
return detail::is_unpacked_bit | data_.size() |
(named_info_.empty()
? 0ULL
: static_cast<unsigned long long>(detail::has_named_args_bit));
}
const basic_format_arg<Context>* data() const {
return named_info_.empty() ? data_.data() : data_.data() + 1;
}
template <typename T> void emplace_arg(const T& arg) {
data_.emplace_back(detail::make_arg<Context>(arg));
}
template <typename T>
void emplace_arg(const detail::named_arg<char_type, T>& arg) {
if (named_info_.empty()) {
constexpr const detail::named_arg_info<char_type>* zero_ptr{nullptr};
data_.insert(data_.begin(), {zero_ptr, 0});
}
data_.emplace_back(detail::make_arg<Context>(detail::unwrap(arg.value)));
auto pop_one = [](std::vector<basic_format_arg<Context>>* data) {
data->pop_back();
};
std::unique_ptr<std::vector<basic_format_arg<Context>>, decltype(pop_one)>
guard{&data_, pop_one};
named_info_.push_back({arg.name, static_cast<int>(data_.size() - 2u)});
data_[0].value_.named_args = {named_info_.data(), named_info_.size()};
guard.release();
}
public:
/**
\rst
Adds an argument into the dynamic store for later passing to a formatting
function.
Note that custom types and string types (but not string views) are copied
into the store dynamically allocating memory if necessary.
**Example**::
fmt::dynamic_format_arg_store<fmt::format_context> store;
store.push_back(42);
store.push_back("abc");
store.push_back(1.5f);
std::string result = fmt::vformat("{} and {} and {}", store);
\endrst
*/
template <typename T> void push_back(const T& arg) {
if (detail::const_check(need_copy<T>::value))
emplace_arg(dynamic_args_.push<stored_type<T>>(arg));
else
emplace_arg(detail::unwrap(arg));
}
/**
\rst
Adds a reference to the argument into the dynamic store for later passing to
a formatting function.
**Example**::
fmt::dynamic_format_arg_store<fmt::format_context> store;
char band[] = "Rolling Stones";
store.push_back(std::cref(band));
band[9] = 'c'; // Changing str affects the output.
std::string result = fmt::vformat("{}", store);
// result == "Rolling Scones"
\endrst
*/
template <typename T> void push_back(std::reference_wrapper<T> arg) {
static_assert(
need_copy<T>::value,
"objects of built-in types and string views are always copied");
emplace_arg(arg.get());
}
/**
Adds named argument into the dynamic store for later passing to a formatting
function. ``std::reference_wrapper`` is supported to avoid copying of the
argument. The name is always copied into the store.
*/
template <typename T>
void push_back(const detail::named_arg<char_type, T>& arg) {
const char_type* arg_name =
dynamic_args_.push<std::basic_string<char_type>>(arg.name).c_str();
if (detail::const_check(need_copy<T>::value)) {
emplace_arg(
fmt::arg(arg_name, dynamic_args_.push<stored_type<T>>(arg.value)));
} else {
emplace_arg(fmt::arg(arg_name, arg.value));
}
}
/** Erase all elements from the store */
void clear() {
data_.clear();
named_info_.clear();
dynamic_args_ = detail::dynamic_arg_list();
}
/**
\rst
Reserves space to store at least *new_cap* arguments including
*new_cap_named* named arguments.
\endrst
*/
void reserve(size_t new_cap, size_t new_cap_named) {
FMT_ASSERT(new_cap >= new_cap_named,
"Set of arguments includes set of named arguments");
data_.reserve(new_cap);
named_info_.reserve(new_cap_named);
}
};
FMT_END_NAMESPACE
#endif // FMT_ARGS_H_

View File

@@ -8,13 +8,13 @@
#ifndef FMT_CHRONO_H_ #ifndef FMT_CHRONO_H_
#define FMT_CHRONO_H_ #define FMT_CHRONO_H_
#include <algorithm>
#include <chrono> #include <chrono>
#include <ctime> #include <ctime>
#include <locale> #include <locale>
#include <sstream> #include <sstream>
#include "format.h" #include "format.h"
#include "locale.h"
FMT_BEGIN_NAMESPACE FMT_BEGIN_NAMESPACE
@@ -282,13 +282,89 @@ To safe_duration_cast(std::chrono::duration<FromRep, FromPeriod> from,
#define FMT_NOMACRO #define FMT_NOMACRO
namespace detail { namespace detail {
template <typename T = void> struct null {};
inline null<> localtime_r FMT_NOMACRO(...) { return null<>(); } inline null<> localtime_r FMT_NOMACRO(...) { return null<>(); }
inline null<> localtime_s(...) { return null<>(); } inline null<> localtime_s(...) { return null<>(); }
inline null<> gmtime_r(...) { return null<>(); } inline null<> gmtime_r(...) { return null<>(); }
inline null<> gmtime_s(...) { return null<>(); } inline null<> gmtime_s(...) { return null<>(); }
inline auto do_write(const std::tm& time, const std::locale& loc, char format,
char modifier) -> std::string {
auto&& os = std::ostringstream();
os.imbue(loc);
using iterator = std::ostreambuf_iterator<char>;
const auto& facet = std::use_facet<std::time_put<char, iterator>>(loc);
auto end = facet.put(os, os, ' ', &time, format, modifier);
if (end.failed()) FMT_THROW(format_error("failed to format time"));
auto str = os.str();
if (!detail::is_utf8() || loc == std::locale::classic()) return str;
// char16_t and char32_t codecvts are broken in MSVC (linkage errors) and
// gcc-4.
#if FMT_MSC_VER != 0 || \
(defined(__GLIBCXX__) && !defined(_GLIBCXX_USE_DUAL_ABI))
// The _GLIBCXX_USE_DUAL_ABI macro is always defined in libstdc++ from gcc-5
// and newer.
using code_unit = wchar_t;
#else
using code_unit = char32_t;
#endif
auto& f = std::use_facet<std::codecvt<code_unit, char, std::mbstate_t>>(loc);
auto mb = std::mbstate_t();
const char* from_next = nullptr;
code_unit* to_next = nullptr;
constexpr size_t buf_size = 32;
code_unit buf[buf_size] = {};
auto result = f.in(mb, str.data(), str.data() + str.size(), from_next, buf,
buf + buf_size, to_next);
if (result != std::codecvt_base::ok)
FMT_THROW(format_error("failed to format time"));
str.clear();
for (code_unit* p = buf; p != to_next; ++p) {
uint32_t c = static_cast<uint32_t>(*p);
if (sizeof(code_unit) == 2 && c >= 0xd800 && c <= 0xdfff) {
// surrogate pair
++p;
if (p == to_next || (c & 0xfc00) != 0xd800 || (*p & 0xfc00) != 0xdc00) {
FMT_THROW(format_error("failed to format time"));
}
c = (c << 10) + static_cast<uint32_t>(*p) - 0x35fdc00;
}
if (c < 0x80) {
str.push_back(static_cast<char>(c));
} else if (c < 0x800) {
str.push_back(static_cast<char>(0xc0 | (c >> 6)));
str.push_back(static_cast<char>(0x80 | (c & 0x3f)));
} else if ((c >= 0x800 && c <= 0xd7ff) || (c >= 0xe000 && c <= 0xffff)) {
str.push_back(static_cast<char>(0xe0 | (c >> 12)));
str.push_back(static_cast<char>(0x80 | ((c & 0xfff) >> 6)));
str.push_back(static_cast<char>(0x80 | (c & 0x3f)));
} else if (c >= 0x10000 && c <= 0x10ffff) {
str.push_back(static_cast<char>(0xf0 | (c >> 18)));
str.push_back(static_cast<char>(0x80 | ((c & 0x3ffff) >> 12)));
str.push_back(static_cast<char>(0x80 | ((c & 0xfff) >> 6)));
str.push_back(static_cast<char>(0x80 | (c & 0x3f)));
} else {
FMT_THROW(format_error("failed to format time"));
}
}
return str;
}
template <typename OutputIt>
auto write(OutputIt out, const std::tm& time, const std::locale& loc,
char format, char modifier = 0) -> OutputIt {
auto str = do_write(time, loc, format, modifier);
return std::copy(str.begin(), str.end(), out);
}
} // namespace detail } // namespace detail
// Thread-safe replacement for std::localtime FMT_MODULE_EXPORT_BEGIN
/**
Converts given time since epoch as ``std::time_t`` value into calendar time,
expressed in local time. Unlike ``std::localtime``, this function is
thread-safe on most platforms.
*/
inline std::tm localtime(std::time_t time) { inline std::tm localtime(std::time_t time) {
struct dispatcher { struct dispatcher {
std::time_t time_; std::time_t time_;
@@ -330,7 +406,11 @@ inline std::tm localtime(
return localtime(std::chrono::system_clock::to_time_t(time_point)); return localtime(std::chrono::system_clock::to_time_t(time_point));
} }
// Thread-safe replacement for std::gmtime /**
Converts given time since epoch as ``std::time_t`` value into calendar time,
expressed in Coordinated Universal Time (UTC). Unlike ``std::gmtime``, this
function is thread-safe on most platforms.
*/
inline std::tm gmtime(std::time_t time) { inline std::tm gmtime(std::time_t time) {
struct dispatcher { struct dispatcher {
std::time_t time_; std::time_t time_;
@@ -371,44 +451,84 @@ inline std::tm gmtime(
return gmtime(std::chrono::system_clock::to_time_t(time_point)); return gmtime(std::chrono::system_clock::to_time_t(time_point));
} }
namespace detail { FMT_BEGIN_DETAIL_NAMESPACE
inline size_t strftime(char* str, size_t count, const char* format, inline size_t strftime(char* str, size_t count, const char* format,
const std::tm* time) { const std::tm* time) {
return std::strftime(str, count, format, time); // Assign to a pointer to suppress GCCs -Wformat-nonliteral
// First assign the nullptr to suppress -Wsuggest-attribute=format
std::size_t (*strftime)(char*, std::size_t, const char*, const std::tm*) =
nullptr;
strftime = std::strftime;
return strftime(str, count, format, time);
} }
inline size_t strftime(wchar_t* str, size_t count, const wchar_t* format, inline size_t strftime(wchar_t* str, size_t count, const wchar_t* format,
const std::tm* time) { const std::tm* time) {
return std::wcsftime(str, count, format, time); // See above
std::size_t (*wcsftime)(wchar_t*, std::size_t, const wchar_t*,
const std::tm*) = nullptr;
wcsftime = std::wcsftime;
return wcsftime(str, count, format, time);
} }
} // namespace detail
template <typename Char> FMT_END_DETAIL_NAMESPACE
struct formatter<std::chrono::time_point<std::chrono::system_clock>, Char>
: formatter<std::tm, Char> { template <typename Char, typename Duration>
struct formatter<std::chrono::time_point<std::chrono::system_clock, Duration>,
Char> : formatter<std::tm, Char> {
FMT_CONSTEXPR formatter() {
this->specs = {default_specs, sizeof(default_specs) / sizeof(Char)};
}
template <typename ParseContext>
FMT_CONSTEXPR 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;
if (end != it) this->specs = {it, detail::to_unsigned(end - it)};
return end;
}
template <typename FormatContext> template <typename FormatContext>
auto format(std::chrono::time_point<std::chrono::system_clock> val, auto format(std::chrono::time_point<std::chrono::system_clock> val,
FormatContext& ctx) -> decltype(ctx.out()) { FormatContext& ctx) -> decltype(ctx.out()) {
std::tm time = localtime(val); std::tm time = localtime(val);
return formatter<std::tm, Char>::format(time, ctx); return formatter<std::tm, Char>::format(time, ctx);
} }
static constexpr Char default_specs[] = {'%', 'Y', '-', '%', 'm', '-',
'%', 'd', ' ', '%', 'H', ':',
'%', 'M', ':', '%', 'S'};
}; };
template <typename Char, typename Duration>
constexpr Char
formatter<std::chrono::time_point<std::chrono::system_clock, Duration>,
Char>::default_specs[];
template <typename Char> struct formatter<std::tm, Char> { template <typename Char> struct formatter<std::tm, Char> {
template <typename ParseContext> template <typename ParseContext>
auto parse(ParseContext& ctx) -> decltype(ctx.begin()) { FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
auto it = ctx.begin(); auto it = ctx.begin();
if (it != ctx.end() && *it == ':') ++it; if (it != ctx.end() && *it == ':') ++it;
auto end = it; auto end = it;
while (end != ctx.end() && *end != '}') ++end; while (end != ctx.end() && *end != '}') ++end;
tm_format.reserve(detail::to_unsigned(end - it + 1)); specs = {it, detail::to_unsigned(end - it)};
tm_format.append(it, end);
tm_format.push_back('\0');
return end; return end;
} }
template <typename FormatContext> template <typename FormatContext>
auto format(const std::tm& tm, FormatContext& ctx) -> decltype(ctx.out()) { auto format(const std::tm& tm, FormatContext& ctx) const
-> decltype(ctx.out()) {
basic_memory_buffer<Char> tm_format;
tm_format.append(specs.begin(), specs.end());
// By appending an extra space we can distinguish an empty result that
// indicates insufficient buffer size from a guaranteed non-empty result
// https://github.com/fmtlib/fmt/issues/2238
tm_format.push_back(' ');
tm_format.push_back('\0');
basic_memory_buffer<Char> buf; basic_memory_buffer<Char> buf;
size_t start = buf.size(); size_t start = buf.size();
for (;;) { for (;;) {
@@ -418,49 +538,40 @@ template <typename Char> struct formatter<std::tm, Char> {
buf.resize(start + count); buf.resize(start + count);
break; 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 size_t MIN_GROWTH = 10; const size_t MIN_GROWTH = 10;
buf.reserve(buf.capacity() + (size > MIN_GROWTH ? size : MIN_GROWTH)); buf.reserve(buf.capacity() + (size > MIN_GROWTH ? size : MIN_GROWTH));
} }
return std::copy(buf.begin(), buf.end(), ctx.out()); // Remove the extra space.
return std::copy(buf.begin(), buf.end() - 1, ctx.out());
} }
basic_memory_buffer<Char> tm_format; basic_string_view<Char> specs;
}; };
namespace detail { FMT_BEGIN_DETAIL_NAMESPACE
template <typename Period> FMT_CONSTEXPR const char* get_units() {
template <typename Period> FMT_CONSTEXPR inline const char* get_units() {
if (std::is_same<Period, std::atto>::value) return "as";
if (std::is_same<Period, std::femto>::value) return "fs";
if (std::is_same<Period, std::pico>::value) return "ps";
if (std::is_same<Period, std::nano>::value) return "ns";
if (std::is_same<Period, std::micro>::value) return "µs";
if (std::is_same<Period, std::milli>::value) return "ms";
if (std::is_same<Period, std::centi>::value) return "cs";
if (std::is_same<Period, std::deci>::value) return "ds";
if (std::is_same<Period, std::ratio<1>>::value) return "s";
if (std::is_same<Period, std::deca>::value) return "das";
if (std::is_same<Period, std::hecto>::value) return "hs";
if (std::is_same<Period, std::kilo>::value) return "ks";
if (std::is_same<Period, std::mega>::value) return "Ms";
if (std::is_same<Period, std::giga>::value) return "Gs";
if (std::is_same<Period, std::tera>::value) return "Ts";
if (std::is_same<Period, std::peta>::value) return "Ps";
if (std::is_same<Period, std::exa>::value) return "Es";
if (std::is_same<Period, std::ratio<60>>::value) return "m";
if (std::is_same<Period, std::ratio<3600>>::value) return "h";
return nullptr; return nullptr;
} }
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";
}
enum class numeric_system { enum class numeric_system {
standard, standard,
@@ -626,33 +737,50 @@ FMT_CONSTEXPR const Char* parse_chrono_format(const Char* begin,
return ptr; return ptr;
} }
struct chrono_format_checker { template <typename Derived> struct null_chrono_spec_handler {
FMT_NORETURN void report_no_date() { FMT_THROW(format_error("no date")); } FMT_CONSTEXPR void unsupported() {
static_cast<Derived*>(this)->unsupported();
}
FMT_CONSTEXPR void on_abbr_weekday() { unsupported(); }
FMT_CONSTEXPR void on_full_weekday() { unsupported(); }
FMT_CONSTEXPR void on_dec0_weekday(numeric_system) { unsupported(); }
FMT_CONSTEXPR void on_dec1_weekday(numeric_system) { unsupported(); }
FMT_CONSTEXPR void on_abbr_month() { unsupported(); }
FMT_CONSTEXPR void on_full_month() { unsupported(); }
FMT_CONSTEXPR void on_24_hour(numeric_system) { unsupported(); }
FMT_CONSTEXPR void on_12_hour(numeric_system) { unsupported(); }
FMT_CONSTEXPR void on_minute(numeric_system) { unsupported(); }
FMT_CONSTEXPR void on_second(numeric_system) { unsupported(); }
FMT_CONSTEXPR void on_datetime(numeric_system) { unsupported(); }
FMT_CONSTEXPR void on_loc_date(numeric_system) { unsupported(); }
FMT_CONSTEXPR void on_loc_time(numeric_system) { unsupported(); }
FMT_CONSTEXPR void on_us_date() { unsupported(); }
FMT_CONSTEXPR void on_iso_date() { unsupported(); }
FMT_CONSTEXPR void on_12_hour_time() { unsupported(); }
FMT_CONSTEXPR void on_24_hour_time() { unsupported(); }
FMT_CONSTEXPR void on_iso_time() { unsupported(); }
FMT_CONSTEXPR void on_am_pm() { unsupported(); }
FMT_CONSTEXPR void on_duration_value() { unsupported(); }
FMT_CONSTEXPR void on_duration_unit() { unsupported(); }
FMT_CONSTEXPR void on_utc_offset() { unsupported(); }
FMT_CONSTEXPR void on_tz_name() { unsupported(); }
};
template <typename Char> void on_text(const Char*, const Char*) {} struct chrono_format_checker : null_chrono_spec_handler<chrono_format_checker> {
FMT_NORETURN void on_abbr_weekday() { report_no_date(); } FMT_NORETURN void unsupported() { FMT_THROW(format_error("no date")); }
FMT_NORETURN void on_full_weekday() { report_no_date(); }
FMT_NORETURN void on_dec0_weekday(numeric_system) { report_no_date(); } template <typename Char>
FMT_NORETURN void on_dec1_weekday(numeric_system) { report_no_date(); } FMT_CONSTEXPR void on_text(const Char*, const Char*) {}
FMT_NORETURN void on_abbr_month() { report_no_date(); } FMT_CONSTEXPR void on_24_hour(numeric_system) {}
FMT_NORETURN void on_full_month() { report_no_date(); } FMT_CONSTEXPR void on_12_hour(numeric_system) {}
void on_24_hour(numeric_system) {} FMT_CONSTEXPR void on_minute(numeric_system) {}
void on_12_hour(numeric_system) {} FMT_CONSTEXPR void on_second(numeric_system) {}
void on_minute(numeric_system) {} FMT_CONSTEXPR void on_12_hour_time() {}
void on_second(numeric_system) {} FMT_CONSTEXPR void on_24_hour_time() {}
FMT_NORETURN void on_datetime(numeric_system) { report_no_date(); } FMT_CONSTEXPR void on_iso_time() {}
FMT_NORETURN void on_loc_date(numeric_system) { report_no_date(); } FMT_CONSTEXPR void on_am_pm() {}
FMT_NORETURN void on_loc_time(numeric_system) { report_no_date(); } FMT_CONSTEXPR void on_duration_value() {}
FMT_NORETURN void on_us_date() { report_no_date(); } FMT_CONSTEXPR void on_duration_unit() {}
FMT_NORETURN 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_duration_value() {}
void on_duration_unit() {}
FMT_NORETURN void on_utc_offset() { report_no_date(); }
FMT_NORETURN void on_tz_name() { report_no_date(); }
}; };
template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)> template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)>
@@ -676,7 +804,8 @@ inline bool isfinite(T value) {
// Converts value to int and checks that it's in the range [0, upper). // Converts value to int and checks that it's in the range [0, upper).
template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)> template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)>
inline int to_nonnegative_int(T value, int upper) { inline int to_nonnegative_int(T value, int upper) {
FMT_ASSERT(value >= 0 && value <= upper, "invalid value"); FMT_ASSERT(value >= 0 && to_unsigned(value) <= to_unsigned(upper),
"invalid value");
(void)upper; (void)upper;
return static_cast<int>(value); return static_cast<int>(value);
} }
@@ -754,15 +883,21 @@ inline std::chrono::duration<Rep, std::milli> get_milliseconds(
return std::chrono::duration<Rep, std::milli>(static_cast<Rep>(ms)); return std::chrono::duration<Rep, std::milli>(static_cast<Rep>(ms));
} }
template <typename Char, typename Rep, typename OutputIt> template <typename Char, typename Rep, typename OutputIt,
OutputIt format_duration_value(OutputIt out, Rep val, int precision) { FMT_ENABLE_IF(std::is_integral<Rep>::value)>
const Char pr_f[] = {'{', ':', '.', '{', '}', 'f', '}', 0}; OutputIt format_duration_value(OutputIt out, Rep val, int) {
if (precision >= 0) return format_to(out, pr_f, val, precision); return write<Char>(out, val);
const Char fp_f[] = {'{', ':', 'g', '}', 0};
const Char format[] = {'{', '}', 0};
return format_to(out, std::is_floating_point<Rep>::value ? fp_f : format,
val);
} }
template <typename Char, typename Rep, typename OutputIt,
FMT_ENABLE_IF(std::is_floating_point<Rep>::value)>
OutputIt format_duration_value(OutputIt out, Rep val, int precision) {
auto specs = basic_format_specs<Char>();
specs.precision = precision;
specs.type = precision > 0 ? 'f' : 'g';
return write<Char>(out, val, specs);
}
template <typename Char, typename OutputIt> template <typename Char, typename OutputIt>
OutputIt copy_unit(string_view unit, OutputIt out, Char) { OutputIt copy_unit(string_view unit, OutputIt out, Char) {
return std::copy(unit.begin(), unit.end(), out); return std::copy(unit.begin(), unit.end(), out);
@@ -780,10 +915,15 @@ template <typename Char, typename Period, typename OutputIt>
OutputIt format_duration_unit(OutputIt out) { OutputIt format_duration_unit(OutputIt out) {
if (const char* unit = get_units<Period>()) if (const char* unit = get_units<Period>())
return copy_unit(string_view(unit), out, Char()); return copy_unit(string_view(unit), out, Char());
const Char num_f[] = {'[', '{', '}', ']', 's', 0}; *out++ = '[';
if (const_check(Period::den == 1)) return format_to(out, num_f, Period::num); out = write<Char>(out, Period::num);
const Char num_def_f[] = {'[', '{', '}', '/', '{', '}', ']', 's', 0}; if (const_check(Period::den != 1)) {
return format_to(out, num_def_f, Period::num, Period::den); *out++ = '/';
out = write<Char>(out, Period::den);
}
*out++ = ']';
*out++ = 's';
return out;
} }
template <typename FormatContext, typename OutputIt, typename Rep, template <typename FormatContext, typename OutputIt, typename Rep,
@@ -792,6 +932,7 @@ struct chrono_formatter {
FormatContext& context; FormatContext& context;
OutputIt out; OutputIt out;
int precision; int precision;
bool localized = false;
// rep is unsigned to avoid overflow. // rep is unsigned to avoid overflow.
using rep = using rep =
conditional_t<std::is_integral<Rep>::value && sizeof(Rep) < sizeof(int), conditional_t<std::is_integral<Rep>::value && sizeof(Rep) < sizeof(int),
@@ -886,13 +1027,9 @@ struct chrono_formatter {
void format_localized(const tm& time, char format, char modifier = 0) { void format_localized(const tm& time, char format, char modifier = 0) {
if (isnan(val)) return write_nan(); if (isnan(val)) return write_nan();
auto locale = context.locale().template get<std::locale>(); const auto& loc = localized ? context.locale().template get<std::locale>()
auto& facet = std::use_facet<std::time_put<char_type>>(locale); : std::locale::classic();
std::basic_ostringstream<char_type> os; out = detail::write(out, time, loc, format, modifier);
os.imbue(locale);
facet.put(os, os, ' ', &time, format, modifier);
auto str = os.str();
std::copy(str.begin(), str.end(), out);
} }
void on_text(const char_type* begin, const char_type* end) { void on_text(const char_type* begin, const char_type* end) {
@@ -1005,17 +1142,59 @@ struct chrono_formatter {
out = format_duration_unit<char_type, Period>(out); out = format_duration_unit<char_type, Period>(out);
} }
}; };
} // namespace detail
FMT_END_DETAIL_NAMESPACE
#if defined(__cpp_lib_chrono) && __cpp_lib_chrono >= 201907
using weekday = std::chrono::weekday;
#else
// A fallback version of weekday.
class weekday {
private:
unsigned char value;
public:
weekday() = default;
explicit constexpr weekday(unsigned wd) noexcept
: value(static_cast<unsigned char>(wd != 7 ? wd : 0)) {}
constexpr unsigned c_encoding() const noexcept { return value; }
};
#endif
// A rudimentary weekday formatter.
template <> struct formatter<weekday> {
private:
bool localized = false;
public:
FMT_CONSTEXPR auto parse(format_parse_context& ctx) -> decltype(ctx.begin()) {
auto begin = ctx.begin(), end = ctx.end();
if (begin != end && *begin == 'L') {
++begin;
localized = true;
}
return begin;
}
auto format(weekday wd, format_context& ctx) -> decltype(ctx.out()) {
auto time = std::tm();
time.tm_wday = static_cast<int>(wd.c_encoding());
const auto& loc = localized ? ctx.locale().template get<std::locale>()
: std::locale::classic();
return detail::write(ctx.out(), time, loc, 'a');
}
};
template <typename Rep, typename Period, typename Char> template <typename Rep, typename Period, typename Char>
struct formatter<std::chrono::duration<Rep, Period>, Char> { struct formatter<std::chrono::duration<Rep, Period>, Char> {
private: private:
basic_format_specs<Char> specs; basic_format_specs<Char> specs;
int precision; int precision = -1;
using arg_ref_type = detail::arg_ref<Char>; using arg_ref_type = detail::arg_ref<Char>;
arg_ref_type width_ref; arg_ref_type width_ref;
arg_ref_type precision_ref; arg_ref_type precision_ref;
mutable basic_string_view<Char> format_str; bool localized = false;
basic_string_view<Char> format_str;
using duration = std::chrono::duration<Rep, Period>; using duration = std::chrono::duration<Rep, Period>;
struct spec_handler { struct spec_handler {
@@ -1038,17 +1217,21 @@ struct formatter<std::chrono::duration<Rep, Period>, Char> {
} }
void on_error(const char* msg) { FMT_THROW(format_error(msg)); } void on_error(const char* msg) { FMT_THROW(format_error(msg)); }
void on_fill(basic_string_view<Char> fill) { f.specs.fill = fill; } FMT_CONSTEXPR void on_fill(basic_string_view<Char> fill) {
void on_align(align_t align) { f.specs.align = align; } f.specs.fill = fill;
void on_width(int width) { f.specs.width = width; } }
void on_precision(int _precision) { f.precision = _precision; } FMT_CONSTEXPR void on_align(align_t align) { f.specs.align = align; }
void end_precision() {} FMT_CONSTEXPR void on_width(int width) { f.specs.width = width; }
FMT_CONSTEXPR void on_precision(int _precision) {
f.precision = _precision;
}
FMT_CONSTEXPR void end_precision() {}
template <typename Id> void on_dynamic_width(Id arg_id) { template <typename Id> FMT_CONSTEXPR void on_dynamic_width(Id arg_id) {
f.width_ref = make_arg_ref(arg_id); f.width_ref = make_arg_ref(arg_id);
} }
template <typename Id> void on_dynamic_precision(Id arg_id) { template <typename Id> FMT_CONSTEXPR void on_dynamic_precision(Id arg_id) {
f.precision_ref = make_arg_ref(arg_id); f.precision_ref = make_arg_ref(arg_id);
} }
}; };
@@ -1073,13 +1256,15 @@ struct formatter<std::chrono::duration<Rep, Period>, Char> {
else else
handler.on_error("precision not allowed for this argument type"); handler.on_error("precision not allowed for this argument type");
} }
if (begin != end && *begin == 'L') {
++begin;
localized = true;
}
end = parse_chrono_format(begin, end, detail::chrono_format_checker()); end = parse_chrono_format(begin, end, detail::chrono_format_checker());
return {begin, end}; return {begin, end};
} }
public: public:
formatter() : precision(-1) {}
FMT_CONSTEXPR auto parse(basic_format_parse_context<Char>& ctx) FMT_CONSTEXPR auto parse(basic_format_parse_context<Char>& ctx)
-> decltype(ctx.begin()) { -> decltype(ctx.begin()) {
auto range = do_parse(ctx); auto range = do_parse(ctx);
@@ -1089,30 +1274,35 @@ struct formatter<std::chrono::duration<Rep, Period>, Char> {
} }
template <typename FormatContext> template <typename FormatContext>
auto format(const duration& d, FormatContext& ctx) -> decltype(ctx.out()) { auto format(const duration& d, FormatContext& ctx) const
-> decltype(ctx.out()) {
auto specs_copy = specs;
auto precision_copy = precision;
auto begin = format_str.begin(), end = format_str.end(); auto begin = format_str.begin(), end = format_str.end();
// As a possible future optimization, we could avoid extra copying if width // As a possible future optimization, we could avoid extra copying if width
// is not specified. // is not specified.
basic_memory_buffer<Char> buf; basic_memory_buffer<Char> buf;
auto out = std::back_inserter(buf); auto out = std::back_inserter(buf);
detail::handle_dynamic_spec<detail::width_checker>(specs.width, width_ref, detail::handle_dynamic_spec<detail::width_checker>(specs_copy.width,
ctx); width_ref, ctx);
detail::handle_dynamic_spec<detail::precision_checker>(precision, detail::handle_dynamic_spec<detail::precision_checker>(precision_copy,
precision_ref, ctx); precision_ref, ctx);
if (begin == end || *begin == '}') { if (begin == end || *begin == '}') {
out = detail::format_duration_value<Char>(out, d.count(), precision); out = detail::format_duration_value<Char>(out, d.count(), precision_copy);
detail::format_duration_unit<Char, Period>(out); detail::format_duration_unit<Char, Period>(out);
} else { } else {
detail::chrono_formatter<FormatContext, decltype(out), Rep, Period> f( detail::chrono_formatter<FormatContext, decltype(out), Rep, Period> f(
ctx, out, d); ctx, out, d);
f.precision = precision; f.precision = precision_copy;
parse_chrono_format(begin, end, f); f.localized = localized;
detail::parse_chrono_format(begin, end, f);
} }
return detail::write( return detail::write(
ctx.out(), basic_string_view<Char>(buf.data(), buf.size()), specs); ctx.out(), basic_string_view<Char>(buf.data(), buf.size()), specs_copy);
} }
}; };
FMT_MODULE_EXPORT_END
FMT_END_NAMESPACE FMT_END_NAMESPACE
#endif // FMT_CHRONO_H_ #endif // FMT_CHRONO_H_

View File

@@ -10,7 +10,15 @@
#include "format.h" #include "format.h"
// __declspec(deprecated) is broken in some MSVC versions.
#if FMT_MSC_VER
# define FMT_DEPRECATED_NONMSVC
#else
# define FMT_DEPRECATED_NONMSVC FMT_DEPRECATED
#endif
FMT_BEGIN_NAMESPACE FMT_BEGIN_NAMESPACE
FMT_MODULE_EXPORT_BEGIN
enum class color : uint32_t { enum class color : uint32_t {
alice_blue = 0xF0F8FF, // rgb(240,248,255) alice_blue = 0xF0F8FF, // rgb(240,248,255)
@@ -198,7 +206,7 @@ struct rgb {
uint8_t b; uint8_t b;
}; };
namespace detail { FMT_BEGIN_DETAIL_NAMESPACE
// color is a struct of either a rgb color or a terminal color. // color is a struct of either a rgb color or a terminal color.
struct color_type { struct color_type {
@@ -221,9 +229,10 @@ struct color_type {
uint32_t rgb_color; uint32_t rgb_color;
} value; } value;
}; };
} // namespace detail
// Experimental text formatting support. FMT_END_DETAIL_NAMESPACE
/** A text style consisting of foreground and background colors and emphasis. */
class text_style { class text_style {
public: public:
FMT_CONSTEXPR text_style(emphasis em = emphasis()) FMT_NOEXCEPT FMT_CONSTEXPR text_style(emphasis em = emphasis()) FMT_NOEXCEPT
@@ -260,33 +269,14 @@ class text_style {
return lhs |= rhs; return lhs |= rhs;
} }
FMT_CONSTEXPR text_style& operator&=(const text_style& rhs) { FMT_DEPRECATED_NONMSVC FMT_CONSTEXPR text_style& operator&=(
if (!set_foreground_color) { const text_style& rhs) {
set_foreground_color = rhs.set_foreground_color; return and_assign(rhs);
foreground_color = rhs.foreground_color;
} else if (rhs.set_foreground_color) {
if (!foreground_color.is_rgb || !rhs.foreground_color.is_rgb)
FMT_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)
FMT_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, FMT_DEPRECATED_NONMSVC friend FMT_CONSTEXPR text_style
const text_style& rhs) { operator&(text_style lhs, const text_style& rhs) {
return lhs &= rhs; return lhs.and_assign(rhs);
} }
FMT_CONSTEXPR bool has_foreground() const FMT_NOEXCEPT { FMT_CONSTEXPR bool has_foreground() const FMT_NOEXCEPT {
@@ -326,8 +316,34 @@ class text_style {
} }
} }
// DEPRECATED!
FMT_CONSTEXPR text_style& and_assign(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)
FMT_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)
FMT_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_DECL text_style fg(detail::color_type foreground) friend FMT_CONSTEXPR_DECL text_style fg(detail::color_type foreground)
FMT_NOEXCEPT; FMT_NOEXCEPT;
friend FMT_CONSTEXPR_DECL text_style bg(detail::color_type background) friend FMT_CONSTEXPR_DECL text_style bg(detail::color_type background)
FMT_NOEXCEPT; FMT_NOEXCEPT;
@@ -338,19 +354,22 @@ class text_style {
emphasis ems; emphasis ems;
}; };
FMT_CONSTEXPR text_style fg(detail::color_type foreground) FMT_NOEXCEPT { /** Creates a text style from the foreground (text) color. */
return text_style(/*is_foreground=*/true, foreground); FMT_CONSTEXPR inline text_style fg(detail::color_type foreground) FMT_NOEXCEPT {
return text_style(true, foreground);
} }
FMT_CONSTEXPR text_style bg(detail::color_type background) FMT_NOEXCEPT { /** Creates a text style from the background color. */
return text_style(/*is_foreground=*/false, background); FMT_CONSTEXPR inline text_style bg(detail::color_type background) FMT_NOEXCEPT {
return text_style(false, background);
} }
FMT_CONSTEXPR text_style operator|(emphasis lhs, emphasis rhs) FMT_NOEXCEPT { FMT_CONSTEXPR inline text_style operator|(emphasis lhs,
emphasis rhs) FMT_NOEXCEPT {
return text_style(lhs) | rhs; return text_style(lhs) | rhs;
} }
namespace detail { FMT_BEGIN_DETAIL_NAMESPACE
template <typename Char> struct ansi_color_escape { template <typename Char> struct ansi_color_escape {
FMT_CONSTEXPR ansi_color_escape(detail::color_type text_color, FMT_CONSTEXPR ansi_color_escape(detail::color_type text_color,
@@ -358,7 +377,7 @@ template <typename Char> struct ansi_color_escape {
// If we have a terminal color, we need to output another escape code // If we have a terminal color, we need to output another escape code
// sequence. // sequence.
if (!text_color.is_rgb) { if (!text_color.is_rgb) {
bool is_background = esc == detail::data::background_color; bool is_background = esc == string_view("\x1b[48;2;");
uint32_t value = text_color.value.term_color; uint32_t value = text_color.value.term_color;
// Background ASCII codes are the same as the foreground ones but with // Background ASCII codes are the same as the foreground ones but with
// 10 more. // 10 more.
@@ -411,7 +430,7 @@ template <typename Char> struct ansi_color_escape {
FMT_CONSTEXPR operator const Char*() const FMT_NOEXCEPT { return buffer; } FMT_CONSTEXPR operator const Char*() const FMT_NOEXCEPT { return buffer; }
FMT_CONSTEXPR const Char* begin() const FMT_NOEXCEPT { return buffer; } FMT_CONSTEXPR const Char* begin() const FMT_NOEXCEPT { return buffer; }
FMT_CONSTEXPR const Char* end() const FMT_NOEXCEPT { FMT_CONSTEXPR_CHAR_TRAITS const Char* end() const FMT_NOEXCEPT {
return buffer + std::char_traits<Char>::length(buffer); return buffer + std::char_traits<Char>::length(buffer);
} }
@@ -430,13 +449,13 @@ template <typename Char> struct ansi_color_escape {
template <typename Char> template <typename Char>
FMT_CONSTEXPR ansi_color_escape<Char> make_foreground_color( FMT_CONSTEXPR ansi_color_escape<Char> make_foreground_color(
detail::color_type foreground) FMT_NOEXCEPT { detail::color_type foreground) FMT_NOEXCEPT {
return ansi_color_escape<Char>(foreground, detail::data::foreground_color); return ansi_color_escape<Char>(foreground, "\x1b[38;2;");
} }
template <typename Char> template <typename Char>
FMT_CONSTEXPR ansi_color_escape<Char> make_background_color( FMT_CONSTEXPR ansi_color_escape<Char> make_background_color(
detail::color_type background) FMT_NOEXCEPT { detail::color_type background) FMT_NOEXCEPT {
return ansi_color_escape<Char>(background, detail::data::background_color); return ansi_color_escape<Char>(background, "\x1b[48;2;");
} }
template <typename Char> template <typename Char>
@@ -455,18 +474,17 @@ inline void fputs<wchar_t>(const wchar_t* chars, FILE* stream) FMT_NOEXCEPT {
} }
template <typename Char> inline void reset_color(FILE* stream) FMT_NOEXCEPT { template <typename Char> inline void reset_color(FILE* stream) FMT_NOEXCEPT {
fputs(detail::data::reset_color, stream); fputs("\x1b[0m", stream);
} }
template <> inline void reset_color<wchar_t>(FILE* stream) FMT_NOEXCEPT { template <> inline void reset_color<wchar_t>(FILE* stream) FMT_NOEXCEPT {
fputs(detail::data::wreset_color, stream); fputs(L"\x1b[0m", stream);
} }
template <typename Char> template <typename Char>
inline void reset_color(buffer<Char>& buffer) FMT_NOEXCEPT { inline void reset_color(buffer<Char>& buffer) FMT_NOEXCEPT {
const char* begin = data::reset_color; auto reset_color = string_view("\x1b[0m");
const char* end = begin + sizeof(data::reset_color) - 1; buffer.append(reset_color.begin(), reset_color.end());
buffer.append(begin, end);
} }
template <typename Char> template <typename Char>
@@ -489,10 +507,11 @@ void vformat_to(buffer<Char>& buf, const text_style& ts,
auto background = detail::make_background_color<Char>(ts.get_background()); auto background = detail::make_background_color<Char>(ts.get_background());
buf.append(background.begin(), background.end()); buf.append(background.begin(), background.end());
} }
detail::vformat_to(buf, format_str, args); detail::vformat_to(buf, format_str, args, {});
if (has_style) detail::reset_color<Char>(buf); if (has_style) detail::reset_color<Char>(buf);
} }
} // namespace detail
FMT_END_DETAIL_NAMESPACE
template <typename S, typename Char = char_t<S>> template <typename S, typename Char = char_t<S>>
void vprint(std::FILE* f, const text_style& ts, const S& format, void vprint(std::FILE* f, const text_style& ts, const S& format,
@@ -523,11 +542,15 @@ void print(std::FILE* f, const text_style& ts, const S& format_str,
} }
/** /**
\rst
Formats a string and prints it to stdout using ANSI escape sequences to Formats a string and prints it to stdout using ANSI escape sequences to
specify text formatting. specify text formatting.
Example:
**Example**::
fmt::print(fmt::emphasis::bold | fg(fmt::color::red), fmt::print(fmt::emphasis::bold | fg(fmt::color::red),
"Elapsed time: {0:.2f} seconds", 1.23); "Elapsed time: {0:.2f} seconds", 1.23);
\endrst
*/ */
template <typename S, typename... Args, template <typename S, typename... Args,
FMT_ENABLE_IF(detail::is_string<S>::value)> FMT_ENABLE_IF(detail::is_string<S>::value)>
@@ -559,8 +582,8 @@ inline std::basic_string<Char> vformat(
template <typename S, typename... Args, typename Char = char_t<S>> template <typename S, typename... Args, typename Char = char_t<S>>
inline std::basic_string<Char> format(const text_style& ts, const S& format_str, inline std::basic_string<Char> format(const text_style& ts, const S& format_str,
const Args&... args) { const Args&... args) {
return vformat(ts, to_string_view(format_str), return fmt::vformat(ts, to_string_view(format_str),
fmt::make_args_checked<Args...>(format_str, args...)); fmt::make_args_checked<Args...>(format_str, args...));
} }
/** /**
@@ -571,7 +594,7 @@ template <typename OutputIt, typename Char,
OutputIt vformat_to( OutputIt vformat_to(
OutputIt out, const text_style& ts, basic_string_view<Char> format_str, OutputIt out, const text_style& ts, basic_string_view<Char> format_str,
basic_format_args<buffer_context<type_identity_t<Char>>> args) { basic_format_args<buffer_context<type_identity_t<Char>>> args) {
decltype(detail::get_buffer<Char>(out)) buf(detail::get_buffer_init(out)); auto&& buf = detail::get_buffer<Char>(out);
detail::vformat_to(buf, ts, format_str, args); detail::vformat_to(buf, ts, format_str, args);
return detail::get_iterator(buf); return detail::get_iterator(buf);
} }
@@ -598,6 +621,7 @@ inline auto format_to(OutputIt out, const text_style& ts, const S& format_str,
fmt::make_args_checked<Args...>(format_str, args...)); fmt::make_args_checked<Args...>(format_str, args...));
} }
FMT_MODULE_EXPORT_END
FMT_END_NAMESPACE FMT_END_NAMESPACE
#endif // FMT_COLOR_H_ #endif // FMT_COLOR_H_

View File

@@ -8,13 +8,135 @@
#ifndef FMT_COMPILE_H_ #ifndef FMT_COMPILE_H_
#define FMT_COMPILE_H_ #define FMT_COMPILE_H_
#include <vector>
#include "format.h" #include "format.h"
FMT_BEGIN_NAMESPACE FMT_BEGIN_NAMESPACE
namespace detail { namespace detail {
// An output iterator that counts the number of objects written to it and
// discards them.
class counting_iterator {
private:
size_t count_;
public:
using iterator_category = std::output_iterator_tag;
using difference_type = std::ptrdiff_t;
using pointer = void;
using reference = void;
using _Unchecked_type = counting_iterator; // Mark iterator as checked.
struct value_type {
template <typename T> void operator=(const T&) {}
};
counting_iterator() : count_(0) {}
size_t count() const { return count_; }
counting_iterator& operator++() {
++count_;
return *this;
}
counting_iterator operator++(int) {
auto it = *this;
++*this;
return it;
}
friend counting_iterator operator+(counting_iterator it, difference_type n) {
it.count_ += static_cast<size_t>(n);
return it;
}
value_type operator*() const { return {}; }
};
template <typename Char, typename InputIt>
inline counting_iterator copy_str(InputIt begin, InputIt end,
counting_iterator it) {
return it + (end - begin);
}
template <typename OutputIt> class truncating_iterator_base {
protected:
OutputIt out_;
size_t limit_;
size_t count_ = 0;
truncating_iterator_base() : out_(), limit_(0) {}
truncating_iterator_base(OutputIt out, size_t limit)
: out_(out), limit_(limit) {}
public:
using iterator_category = std::output_iterator_tag;
using value_type = typename std::iterator_traits<OutputIt>::value_type;
using difference_type = std::ptrdiff_t;
using pointer = void;
using reference = void;
using _Unchecked_type =
truncating_iterator_base; // Mark iterator as checked.
OutputIt base() const { return out_; }
size_t count() const { return count_; }
};
// An output iterator that truncates the output and counts the number of objects
// written to it.
template <typename OutputIt,
typename Enable = typename std::is_void<
typename std::iterator_traits<OutputIt>::value_type>::type>
class truncating_iterator;
template <typename OutputIt>
class truncating_iterator<OutputIt, std::false_type>
: public truncating_iterator_base<OutputIt> {
mutable typename truncating_iterator_base<OutputIt>::value_type blackhole_;
public:
using value_type = typename truncating_iterator_base<OutputIt>::value_type;
truncating_iterator() = default;
truncating_iterator(OutputIt out, size_t limit)
: truncating_iterator_base<OutputIt>(out, limit) {}
truncating_iterator& operator++() {
if (this->count_++ < this->limit_) ++this->out_;
return *this;
}
truncating_iterator operator++(int) {
auto it = *this;
++*this;
return it;
}
value_type& operator*() const {
return this->count_ < this->limit_ ? *this->out_ : blackhole_;
}
};
template <typename OutputIt>
class truncating_iterator<OutputIt, std::true_type>
: public truncating_iterator_base<OutputIt> {
public:
truncating_iterator() = default;
truncating_iterator(OutputIt out, size_t limit)
: truncating_iterator_base<OutputIt>(out, limit) {}
template <typename T> truncating_iterator& operator=(T val) {
if (this->count_++ < this->limit_) *this->out_++ = val;
return *this;
}
truncating_iterator& operator++() { return *this; }
truncating_iterator& operator++(int) { return *this; }
truncating_iterator& operator*() { return *this; }
};
// A compile-time string which is compiled into fast formatting code. // A compile-time string which is compiled into fast formatting code.
class compiled_string {}; class compiled_string {};
@@ -34,335 +156,29 @@ struct is_compiled_string : std::is_base_of<compiled_string, S> {};
std::string s = fmt::format(FMT_COMPILE("{}"), 42); std::string s = fmt::format(FMT_COMPILE("{}"), 42);
\endrst \endrst
*/ */
#define FMT_COMPILE(s) FMT_STRING_IMPL(s, fmt::detail::compiled_string) #ifdef __cpp_if_constexpr
# define FMT_COMPILE(s) \
FMT_STRING_IMPL(s, fmt::detail::compiled_string, explicit)
#else
# define FMT_COMPILE(s) FMT_STRING(s)
#endif
#if FMT_USE_NONTYPE_TEMPLATE_PARAMETERS
template <typename Char, size_t N,
fmt::detail_exported::fixed_string<Char, N> Str>
struct udl_compiled_string : compiled_string {
using char_type = Char;
constexpr operator basic_string_view<char_type>() const {
return {Str.data, N - 1};
}
};
#endif
template <typename T, typename... Tail> template <typename T, typename... Tail>
const T& first(const T& value, const Tail&...) { const T& first(const T& value, const Tail&...) {
return value; return value;
} }
// Part of a compiled format string. It can be either literal text or a
// replacement field.
template <typename Char> struct format_part {
enum class kind { arg_index, arg_name, text, replacement };
struct replacement {
arg_ref<Char> arg_id;
dynamic_format_specs<Char> specs;
};
kind part_kind;
union value {
int arg_index;
basic_string_view<Char> str;
replacement repl;
FMT_CONSTEXPR value(int index = 0) : arg_index(index) {}
FMT_CONSTEXPR value(basic_string_view<Char> s) : str(s) {}
FMT_CONSTEXPR value(replacement r) : repl(r) {}
} val;
// Position past the end of the argument id.
const Char* arg_id_end = nullptr;
FMT_CONSTEXPR format_part(kind k = kind::arg_index, value v = {})
: part_kind(k), val(v) {}
static FMT_CONSTEXPR format_part make_arg_index(int index) {
return format_part(kind::arg_index, index);
}
static FMT_CONSTEXPR format_part make_arg_name(basic_string_view<Char> name) {
return format_part(kind::arg_name, name);
}
static FMT_CONSTEXPR format_part make_text(basic_string_view<Char> text) {
return format_part(kind::text, text);
}
static FMT_CONSTEXPR format_part make_replacement(replacement repl) {
return format_part(kind::replacement, repl);
}
};
template <typename Char> struct part_counter {
unsigned num_parts = 0;
FMT_CONSTEXPR void on_text(const Char* begin, const Char* end) {
if (begin != end) ++num_parts;
}
FMT_CONSTEXPR int on_arg_id() { return ++num_parts, 0; }
FMT_CONSTEXPR int on_arg_id(int) { return ++num_parts, 0; }
FMT_CONSTEXPR int on_arg_id(basic_string_view<Char>) {
return ++num_parts, 0;
}
FMT_CONSTEXPR void on_replacement_field(int, const Char*) {}
FMT_CONSTEXPR const Char* on_format_specs(int, const Char* begin,
const Char* end) {
// Find the matching brace.
unsigned brace_counter = 0;
for (; begin != end; ++begin) {
if (*begin == '{') {
++brace_counter;
} else if (*begin == '}') {
if (brace_counter == 0u) break;
--brace_counter;
}
}
return begin;
}
FMT_CONSTEXPR void on_error(const char*) {}
};
// Counts the number of parts in a format string.
template <typename Char>
FMT_CONSTEXPR unsigned count_parts(basic_string_view<Char> format_str) {
part_counter<Char> counter;
parse_format_string<true>(format_str, counter);
return counter.num_parts;
}
template <typename Char, typename PartHandler>
class format_string_compiler : public error_handler {
private:
using part = format_part<Char>;
PartHandler handler_;
part part_;
basic_string_view<Char> format_str_;
basic_format_parse_context<Char> parse_context_;
public:
FMT_CONSTEXPR format_string_compiler(basic_string_view<Char> format_str,
PartHandler handler)
: handler_(handler),
format_str_(format_str),
parse_context_(format_str) {}
FMT_CONSTEXPR void on_text(const Char* begin, const Char* end) {
if (begin != end)
handler_(part::make_text({begin, to_unsigned(end - begin)}));
}
FMT_CONSTEXPR int on_arg_id() {
part_ = part::make_arg_index(parse_context_.next_arg_id());
return 0;
}
FMT_CONSTEXPR int on_arg_id(int id) {
parse_context_.check_arg_id(id);
part_ = part::make_arg_index(id);
return 0;
}
FMT_CONSTEXPR int on_arg_id(basic_string_view<Char> id) {
part_ = part::make_arg_name(id);
return 0;
}
FMT_CONSTEXPR void on_replacement_field(int, const Char* ptr) {
part_.arg_id_end = ptr;
handler_(part_);
}
FMT_CONSTEXPR const Char* on_format_specs(int, const Char* begin,
const Char* end) {
auto repl = typename part::replacement();
dynamic_specs_handler<basic_format_parse_context<Char>> handler(
repl.specs, parse_context_);
auto it = parse_format_specs(begin, end, handler);
if (*it != '}') on_error("missing '}' in format string");
repl.arg_id = part_.part_kind == part::kind::arg_index
? arg_ref<Char>(part_.val.arg_index)
: arg_ref<Char>(part_.val.str);
auto part = part::make_replacement(repl);
part.arg_id_end = begin;
handler_(part);
return it;
}
};
// Compiles a format string and invokes handler(part) for each parsed part.
template <bool IS_CONSTEXPR, typename Char, typename PartHandler>
FMT_CONSTEXPR void compile_format_string(basic_string_view<Char> format_str,
PartHandler handler) {
parse_format_string<IS_CONSTEXPR>(
format_str,
format_string_compiler<Char, PartHandler>(format_str, handler));
}
template <typename OutputIt, typename Context, typename Id>
void format_arg(
basic_format_parse_context<typename Context::char_type>& parse_ctx,
Context& ctx, Id arg_id) {
ctx.advance_to(visit_format_arg(
arg_formatter<OutputIt, typename Context::char_type>(ctx, &parse_ctx),
ctx.arg(arg_id)));
}
// vformat_to is defined in a subnamespace to prevent ADL.
namespace cf {
template <typename Context, typename OutputIt, typename CompiledFormat>
auto vformat_to(OutputIt out, CompiledFormat& cf,
basic_format_args<Context> args) -> typename Context::iterator {
using char_type = typename Context::char_type;
basic_format_parse_context<char_type> parse_ctx(
to_string_view(cf.format_str_));
Context ctx(out, args);
const auto& parts = cf.parts();
for (auto part_it = std::begin(parts); part_it != std::end(parts);
++part_it) {
const auto& part = *part_it;
const auto& value = part.val;
using format_part_t = format_part<char_type>;
switch (part.part_kind) {
case format_part_t::kind::text: {
const auto text = value.str;
auto output = ctx.out();
auto&& it = reserve(output, text.size());
it = std::copy_n(text.begin(), text.size(), it);
ctx.advance_to(output);
break;
}
case format_part_t::kind::arg_index:
advance_to(parse_ctx, part.arg_id_end);
detail::format_arg<OutputIt>(parse_ctx, ctx, value.arg_index);
break;
case format_part_t::kind::arg_name:
advance_to(parse_ctx, part.arg_id_end);
detail::format_arg<OutputIt>(parse_ctx, ctx, value.str);
break;
case format_part_t::kind::replacement: {
const auto& arg_id_value = value.repl.arg_id.val;
const auto arg = value.repl.arg_id.kind == arg_id_kind::index
? ctx.arg(arg_id_value.index)
: ctx.arg(arg_id_value.name);
auto specs = value.repl.specs;
handle_dynamic_spec<width_checker>(specs.width, specs.width_ref, ctx);
handle_dynamic_spec<precision_checker>(specs.precision,
specs.precision_ref, ctx);
error_handler h;
numeric_specs_checker<error_handler> checker(h, arg.type());
if (specs.align == align::numeric) checker.require_numeric_argument();
if (specs.sign != sign::none) checker.check_sign();
if (specs.alt) checker.require_numeric_argument();
if (specs.precision >= 0) checker.check_precision();
advance_to(parse_ctx, part.arg_id_end);
ctx.advance_to(
visit_format_arg(arg_formatter<OutputIt, typename Context::char_type>(
ctx, nullptr, &specs),
arg));
break;
}
}
}
return ctx.out();
}
} // namespace cf
struct basic_compiled_format {};
template <typename S, typename = void>
struct compiled_format_base : basic_compiled_format {
using char_type = char_t<S>;
using parts_container = std::vector<detail::format_part<char_type>>;
parts_container compiled_parts;
explicit compiled_format_base(basic_string_view<char_type> format_str) {
compile_format_string<false>(format_str,
[this](const format_part<char_type>& part) {
compiled_parts.push_back(part);
});
}
const parts_container& parts() const { return compiled_parts; }
};
template <typename Char, unsigned N> struct format_part_array {
format_part<Char> data[N] = {};
FMT_CONSTEXPR format_part_array() = default;
};
template <typename Char, unsigned N>
FMT_CONSTEXPR format_part_array<Char, N> compile_to_parts(
basic_string_view<Char> format_str) {
format_part_array<Char, N> parts;
unsigned counter = 0;
// This is not a lambda for compatibility with older compilers.
struct {
format_part<Char>* parts;
unsigned* counter;
FMT_CONSTEXPR void operator()(const format_part<Char>& part) {
parts[(*counter)++] = part;
}
} collector{parts.data, &counter};
compile_format_string<true>(format_str, collector);
if (counter < N) {
parts.data[counter] =
format_part<Char>::make_text(basic_string_view<Char>());
}
return parts;
}
template <typename T> constexpr const T& constexpr_max(const T& a, const T& b) {
return (a < b) ? b : a;
}
template <typename S>
struct compiled_format_base<S, enable_if_t<is_compile_string<S>::value>>
: basic_compiled_format {
using char_type = char_t<S>;
FMT_CONSTEXPR explicit compiled_format_base(basic_string_view<char_type>) {}
// Workaround for old compilers. Format string compilation will not be
// performed there anyway.
#if FMT_USE_CONSTEXPR
static FMT_CONSTEXPR_DECL const unsigned num_format_parts =
constexpr_max(count_parts(to_string_view(S())), 1u);
#else
static const unsigned num_format_parts = 1;
#endif
using parts_container = format_part<char_type>[num_format_parts];
const parts_container& parts() const {
static FMT_CONSTEXPR_DECL const auto compiled_parts =
compile_to_parts<char_type, num_format_parts>(
detail::to_string_view(S()));
return compiled_parts.data;
}
};
template <typename S, typename... Args>
class compiled_format : private compiled_format_base<S> {
public:
using typename compiled_format_base<S>::char_type;
private:
basic_string_view<char_type> format_str_;
template <typename Context, typename OutputIt, typename CompiledFormat>
friend auto cf::vformat_to(OutputIt out, CompiledFormat& cf,
basic_format_args<Context> args) ->
typename Context::iterator;
public:
compiled_format() = delete;
explicit constexpr compiled_format(basic_string_view<char_type> format_str)
: compiled_format_base<S>(format_str), format_str_(format_str) {}
};
#ifdef __cpp_if_constexpr #ifdef __cpp_if_constexpr
template <typename... Args> struct type_list {}; template <typename... Args> struct type_list {};
@@ -377,6 +193,12 @@ constexpr const auto& get([[maybe_unused]] const T& first,
return get<N - 1>(rest...); return get<N - 1>(rest...);
} }
template <typename Char, typename... Args>
constexpr int get_arg_index_by_name(basic_string_view<Char> name,
type_list<Args...>) {
return get_arg_index_by_name<Args...>(name);
}
template <int N, typename> struct get_type_impl; template <int N, typename> struct get_type_impl;
template <int N, typename... Args> struct get_type_impl<N, type_list<Args...>> { template <int N, typename... Args> struct get_type_impl<N, type_list<Args...>> {
@@ -393,7 +215,7 @@ template <typename Char> struct text {
using char_type = Char; using char_type = Char;
template <typename OutputIt, typename... Args> template <typename OutputIt, typename... Args>
OutputIt format(OutputIt out, const Args&...) const { constexpr OutputIt format(OutputIt out, const Args&...) const {
return write<Char>(out, data); return write<Char>(out, data);
} }
}; };
@@ -412,11 +234,22 @@ template <typename Char> struct code_unit {
using char_type = Char; using char_type = Char;
template <typename OutputIt, typename... Args> template <typename OutputIt, typename... Args>
OutputIt format(OutputIt out, const Args&...) const { constexpr OutputIt format(OutputIt out, const Args&...) const {
return write<Char>(out, value); return write<Char>(out, value);
} }
}; };
// This ensures that the argument type is convertible to `const T&`.
template <typename T, int N, typename... Args>
constexpr const T& get_arg_checked(const Args&... args) {
const auto& arg = get<N>(args...);
if constexpr (detail::is_named_arg<remove_cvref_t<decltype(arg)>>()) {
return arg.value;
} else {
return arg;
}
}
template <typename Char> template <typename Char>
struct is_compiled_format<code_unit<Char>> : std::true_type {}; struct is_compiled_format<code_unit<Char>> : std::true_type {};
@@ -425,29 +258,58 @@ template <typename Char, typename T, int N> struct field {
using char_type = Char; using char_type = Char;
template <typename OutputIt, typename... Args> template <typename OutputIt, typename... Args>
OutputIt format(OutputIt out, const Args&... args) const { constexpr OutputIt format(OutputIt out, const Args&... args) const {
// This ensures that the argument type is convertile to `const T&`. return write<Char>(out, get_arg_checked<T, N>(args...));
const T& arg = get<N>(args...);
return write<Char>(out, arg);
} }
}; };
template <typename Char, typename T, int N> template <typename Char, typename T, int N>
struct is_compiled_format<field<Char, T, N>> : std::true_type {}; struct is_compiled_format<field<Char, T, N>> : std::true_type {};
// A replacement field that refers to argument with name.
template <typename Char> struct runtime_named_field {
using char_type = Char;
basic_string_view<Char> name;
template <typename OutputIt, typename T>
constexpr static bool try_format_argument(
OutputIt& out,
// [[maybe_unused]] due to unused-but-set-parameter warning in GCC 7,8,9
[[maybe_unused]] basic_string_view<Char> arg_name, const T& arg) {
if constexpr (is_named_arg<typename std::remove_cv<T>::type>::value) {
if (arg_name == arg.name) {
out = write<Char>(out, arg.value);
return true;
}
}
return false;
}
template <typename OutputIt, typename... Args>
constexpr OutputIt format(OutputIt out, const Args&... args) const {
bool found = (try_format_argument(out, name, args) || ...);
if (!found) {
throw format_error("argument with specified name is not found");
}
return out;
}
};
template <typename Char>
struct is_compiled_format<runtime_named_field<Char>> : std::true_type {};
// A replacement field that refers to argument N and has format specifiers. // A replacement field that refers to argument N and has format specifiers.
template <typename Char, typename T, int N> struct spec_field { template <typename Char, typename T, int N> struct spec_field {
using char_type = Char; using char_type = Char;
mutable formatter<T, Char> fmt; formatter<T, Char> fmt;
template <typename OutputIt, typename... Args> template <typename OutputIt, typename... Args>
OutputIt format(OutputIt out, const Args&... args) const { constexpr FMT_INLINE OutputIt format(OutputIt out,
// This ensures that the argument type is convertile to `const T&`. const Args&... args) const {
const T& arg = get<N>(args...);
const auto& vargs = const auto& vargs =
make_format_args<basic_format_context<OutputIt, Char>>(args...); fmt::make_format_args<basic_format_context<OutputIt, Char>>(args...);
basic_format_context<OutputIt, Char> ctx(out, vargs); basic_format_context<OutputIt, Char> ctx(out, vargs);
return fmt.format(arg, ctx); return fmt.format(get_arg_checked<T, N>(args...), ctx);
} }
}; };
@@ -460,7 +322,7 @@ template <typename L, typename R> struct concat {
using char_type = typename L::char_type; using char_type = typename L::char_type;
template <typename OutputIt, typename... Args> template <typename OutputIt, typename... Args>
OutputIt format(OutputIt out, const Args&... args) const { constexpr OutputIt format(OutputIt out, const Args&... args) const {
out = lhs.format(out, args...); out = lhs.format(out, args...);
return rhs.format(out, args...); return rhs.format(out, args...);
} }
@@ -508,14 +370,77 @@ template <typename T, typename Char> struct parse_specs_result {
int next_arg_id; int next_arg_id;
}; };
constexpr int manual_indexing_id = -1;
template <typename T, typename Char> template <typename T, typename Char>
constexpr parse_specs_result<T, Char> parse_specs(basic_string_view<Char> str, constexpr parse_specs_result<T, Char> parse_specs(basic_string_view<Char> str,
size_t pos, int arg_id) { size_t pos, int next_arg_id) {
str.remove_prefix(pos); str.remove_prefix(pos);
auto ctx = basic_format_parse_context<Char>(str, {}, arg_id + 1); auto ctx = basic_format_parse_context<Char>(str, {}, next_arg_id);
auto f = formatter<T, Char>(); auto f = formatter<T, Char>();
auto end = f.parse(ctx); auto end = f.parse(ctx);
return {f, pos + (end - str.data()) + 1, ctx.next_arg_id()}; return {f, pos + fmt::detail::to_unsigned(end - str.data()) + 1,
next_arg_id == 0 ? manual_indexing_id : ctx.next_arg_id()};
}
template <typename Char> struct arg_id_handler {
arg_ref<Char> arg_id;
constexpr int operator()() {
FMT_ASSERT(false, "handler cannot be used with automatic indexing");
return 0;
}
constexpr int operator()(int id) {
arg_id = arg_ref<Char>(id);
return 0;
}
constexpr int operator()(basic_string_view<Char> id) {
arg_id = arg_ref<Char>(id);
return 0;
}
constexpr void on_error(const char* message) { throw format_error(message); }
};
template <typename Char> struct parse_arg_id_result {
arg_ref<Char> arg_id;
const Char* arg_id_end;
};
template <int ID, typename Char>
constexpr auto parse_arg_id(const Char* begin, const Char* end) {
auto handler = arg_id_handler<Char>{arg_ref<Char>{}};
auto arg_id_end = parse_arg_id(begin, end, handler);
return parse_arg_id_result<Char>{handler.arg_id, arg_id_end};
}
template <typename T, typename Enable = void> struct field_type {
using type = remove_cvref_t<T>;
};
template <typename T>
struct field_type<T, enable_if_t<detail::is_named_arg<T>::value>> {
using type = remove_cvref_t<decltype(T::value)>;
};
template <typename T, typename Args, size_t END_POS, int ARG_INDEX, int NEXT_ID,
typename S>
constexpr auto parse_replacement_field_then_tail(S format_str) {
using char_type = typename S::char_type;
constexpr auto str = basic_string_view<char_type>(format_str);
constexpr char_type c = END_POS != str.size() ? str[END_POS] : char_type();
if constexpr (c == '}') {
return parse_tail<Args, END_POS + 1, NEXT_ID>(
field<char_type, typename field_type<T>::type, ARG_INDEX>(),
format_str);
} else if constexpr (c == ':') {
constexpr auto result = parse_specs<typename field_type<T>::type>(
str, END_POS + 1, NEXT_ID == manual_indexing_id ? 0 : NEXT_ID);
return parse_tail<Args, result.end, result.next_arg_id>(
spec_field<char_type, typename field_type<T>::type, ARG_INDEX>{
result.fmt},
format_str);
}
} }
// Compiles a non-empty format string and returns the compiled representation // Compiles a non-empty format string and returns the compiled representation
@@ -523,26 +448,58 @@ constexpr parse_specs_result<T, Char> parse_specs(basic_string_view<Char> str,
template <typename Args, size_t POS, int ID, typename S> template <typename Args, size_t POS, int ID, typename S>
constexpr auto compile_format_string(S format_str) { constexpr auto compile_format_string(S format_str) {
using char_type = typename S::char_type; using char_type = typename S::char_type;
constexpr basic_string_view<char_type> str = format_str; constexpr auto str = basic_string_view<char_type>(format_str);
if constexpr (str[POS] == '{') { if constexpr (str[POS] == '{') {
if (POS + 1 == str.size()) if constexpr (POS + 1 == str.size())
throw format_error("unmatched '{' in format string"); throw format_error("unmatched '{' in format string");
if constexpr (str[POS + 1] == '{') { if constexpr (str[POS + 1] == '{') {
return parse_tail<Args, POS + 2, ID>(make_text(str, POS, 1), format_str); return parse_tail<Args, POS + 2, ID>(make_text(str, POS, 1), format_str);
} else if constexpr (str[POS + 1] == '}') { } else if constexpr (str[POS + 1] == '}' || str[POS + 1] == ':') {
using type = get_type<ID, Args>; static_assert(ID != manual_indexing_id,
return parse_tail<Args, POS + 2, ID + 1>(field<char_type, type, ID>(), "cannot switch from manual to automatic argument indexing");
format_str); constexpr auto next_id =
} else if constexpr (str[POS + 1] == ':') { ID != manual_indexing_id ? ID + 1 : manual_indexing_id;
using type = get_type<ID, Args>; return parse_replacement_field_then_tail<get_type<ID, Args>, Args,
constexpr auto result = parse_specs<type>(str, POS + 2, ID); POS + 1, ID, next_id>(
return parse_tail<Args, result.end, result.next_arg_id>( format_str);
spec_field<char_type, type, ID>{result.fmt}, format_str);
} else { } else {
return unknown_format(); constexpr auto arg_id_result =
parse_arg_id<ID>(str.data() + POS + 1, str.data() + str.size());
constexpr auto arg_id_end_pos = arg_id_result.arg_id_end - str.data();
constexpr char_type c =
arg_id_end_pos != str.size() ? str[arg_id_end_pos] : char_type();
static_assert(c == '}' || c == ':', "missing '}' in format string");
if constexpr (arg_id_result.arg_id.kind == arg_id_kind::index) {
static_assert(
ID == manual_indexing_id || ID == 0,
"cannot switch from automatic to manual argument indexing");
constexpr auto arg_index = arg_id_result.arg_id.val.index;
return parse_replacement_field_then_tail<get_type<arg_index, Args>,
Args, arg_id_end_pos,
arg_index, manual_indexing_id>(
format_str);
} else if constexpr (arg_id_result.arg_id.kind == arg_id_kind::name) {
constexpr auto arg_index =
get_arg_index_by_name(arg_id_result.arg_id.val.name, Args{});
if constexpr (arg_index != invalid_arg_index) {
constexpr auto next_id =
ID != manual_indexing_id ? ID + 1 : manual_indexing_id;
return parse_replacement_field_then_tail<
decltype(get_type<arg_index, Args>::value), Args, arg_id_end_pos,
arg_index, next_id>(format_str);
} else {
if constexpr (c == '}') {
return parse_tail<Args, arg_id_end_pos + 1, ID>(
runtime_named_field<char_type>{arg_id_result.arg_id.val.name},
format_str);
} else if constexpr (c == ':') {
return unknown_format(); // no type info for specs parsing
}
}
}
} }
} else if constexpr (str[POS] == '}') { } else if constexpr (str[POS] == '}') {
if (POS + 1 == str.size()) if constexpr (POS + 1 == str.size())
throw format_error("unmatched '}' in format string"); throw format_error("unmatched '}' in format string");
return parse_tail<Args, POS + 2, ID>(make_text(str, POS, 1), format_str); return parse_tail<Args, POS + 2, ID>(make_text(str, POS, 1), format_str);
} else { } else {
@@ -558,144 +515,125 @@ constexpr auto compile_format_string(S format_str) {
} }
template <typename... Args, typename S, template <typename... Args, typename S,
FMT_ENABLE_IF(is_compile_string<S>::value || FMT_ENABLE_IF(detail::is_compiled_string<S>::value)>
detail::is_compiled_string<S>::value)>
constexpr auto compile(S format_str) { constexpr auto compile(S format_str) {
constexpr basic_string_view<typename S::char_type> str = format_str; constexpr auto str = basic_string_view<typename S::char_type>(format_str);
if constexpr (str.size() == 0) { if constexpr (str.size() == 0) {
return detail::make_text(str, 0, 0); return detail::make_text(str, 0, 0);
} else { } else {
constexpr auto result = constexpr auto result =
detail::compile_format_string<detail::type_list<Args...>, 0, 0>( detail::compile_format_string<detail::type_list<Args...>, 0, 0>(
format_str); format_str);
if constexpr (std::is_same<remove_cvref_t<decltype(result)>, return result;
detail::unknown_format>()) {
return detail::compiled_format<S, Args...>(to_string_view(format_str));
} else {
return result;
}
} }
} }
#else
template <typename... Args, typename S,
FMT_ENABLE_IF(is_compile_string<S>::value)>
constexpr auto compile(S format_str) -> detail::compiled_format<S, Args...> {
return detail::compiled_format<S, Args...>(to_string_view(format_str));
}
#endif // __cpp_if_constexpr #endif // __cpp_if_constexpr
// Compiles the format string which must be a string literal.
template <typename... Args, typename Char, size_t N>
auto compile(const Char (&format_str)[N])
-> detail::compiled_format<const Char*, Args...> {
return detail::compiled_format<const Char*, Args...>(
basic_string_view<Char>(format_str, N - 1));
}
} // namespace detail } // namespace detail
// DEPRECATED! use FMT_COMPILE instead. FMT_MODULE_EXPORT_BEGIN
template <typename... Args>
FMT_DEPRECATED auto compile(const Args&... args)
-> decltype(detail::compile(args...)) {
return detail::compile(args...);
}
#if FMT_USE_CONSTEXPR #ifdef __cpp_if_constexpr
# ifdef __cpp_if_constexpr
template <typename CompiledFormat, typename... Args, template <typename CompiledFormat, typename... Args,
typename Char = typename CompiledFormat::char_type, typename Char = typename CompiledFormat::char_type,
FMT_ENABLE_IF(detail::is_compiled_format<CompiledFormat>::value)> FMT_ENABLE_IF(detail::is_compiled_format<CompiledFormat>::value)>
FMT_INLINE std::basic_string<Char> format(const CompiledFormat& cf, FMT_INLINE std::basic_string<Char> format(const CompiledFormat& cf,
const Args&... args) { const Args&... args) {
basic_memory_buffer<Char> buffer; auto s = std::basic_string<Char>();
cf.format(detail::buffer_appender<Char>(buffer), args...); cf.format(std::back_inserter(s), args...);
return to_string(buffer); return s;
} }
template <typename OutputIt, typename CompiledFormat, typename... Args, template <typename OutputIt, typename CompiledFormat, typename... Args,
FMT_ENABLE_IF(detail::is_compiled_format<CompiledFormat>::value)> FMT_ENABLE_IF(detail::is_compiled_format<CompiledFormat>::value)>
OutputIt format_to(OutputIt out, const CompiledFormat& cf, constexpr FMT_INLINE OutputIt format_to(OutputIt out, const CompiledFormat& cf,
const Args&... args) { const Args&... args) {
return cf.format(out, args...); return cf.format(out, args...);
} }
# endif // __cpp_if_constexpr
#endif // FMT_USE_CONSTEXPR
template <typename CompiledFormat, typename... Args,
typename Char = typename CompiledFormat::char_type,
FMT_ENABLE_IF(std::is_base_of<detail::basic_compiled_format,
CompiledFormat>::value)>
std::basic_string<Char> format(const CompiledFormat& cf, const Args&... args) {
basic_memory_buffer<Char> buffer;
using context = buffer_context<Char>;
detail::cf::vformat_to<context>(detail::buffer_appender<Char>(buffer), cf,
make_format_args<context>(args...));
return to_string(buffer);
}
template <typename S, typename... Args, template <typename S, typename... Args,
FMT_ENABLE_IF(detail::is_compiled_string<S>::value)> FMT_ENABLE_IF(detail::is_compiled_string<S>::value)>
FMT_INLINE std::basic_string<typename S::char_type> format(const S&, FMT_INLINE std::basic_string<typename S::char_type> format(const S&,
Args&&... args) { Args&&... args) {
#ifdef __cpp_if_constexpr
if constexpr (std::is_same<typename S::char_type, char>::value) { if constexpr (std::is_same<typename S::char_type, char>::value) {
constexpr basic_string_view<typename S::char_type> str = S(); constexpr auto str = basic_string_view<typename S::char_type>(S());
if (str.size() == 2 && str[0] == '{' && str[1] == '}') if constexpr (str.size() == 2 && str[0] == '{' && str[1] == '}') {
return fmt::to_string(detail::first(args...)); const auto& first = detail::first(args...);
if constexpr (detail::is_named_arg<
remove_cvref_t<decltype(first)>>::value) {
return fmt::to_string(first.value);
} else {
return fmt::to_string(first);
}
}
} }
constexpr auto compiled = detail::compile<Args...>(S());
if constexpr (std::is_same<remove_cvref_t<decltype(compiled)>,
detail::unknown_format>()) {
return format(static_cast<basic_string_view<typename S::char_type>>(S()),
std::forward<Args>(args)...);
} else {
return format(compiled, std::forward<Args>(args)...);
}
}
template <typename OutputIt, typename S, typename... Args,
FMT_ENABLE_IF(detail::is_compiled_string<S>::value)>
FMT_CONSTEXPR OutputIt format_to(OutputIt out, const S&, Args&&... args) {
constexpr auto compiled = detail::compile<Args...>(S());
if constexpr (std::is_same<remove_cvref_t<decltype(compiled)>,
detail::unknown_format>()) {
return format_to(out,
static_cast<basic_string_view<typename S::char_type>>(S()),
std::forward<Args>(args)...);
} else {
return format_to(out, compiled, std::forward<Args>(args)...);
}
}
#endif #endif
constexpr auto compiled = detail::compile<Args...>(S());
return format(compiled, std::forward<Args>(args)...);
}
template <typename OutputIt, typename CompiledFormat, typename... Args,
FMT_ENABLE_IF(std::is_base_of<detail::basic_compiled_format,
CompiledFormat>::value)>
OutputIt format_to(OutputIt out, const CompiledFormat& cf,
const Args&... args) {
using char_type = typename CompiledFormat::char_type;
using context = format_context_t<OutputIt, char_type>;
return detail::cf::vformat_to<context>(out, cf,
make_format_args<context>(args...));
}
template <typename OutputIt, typename S, typename... Args, template <typename OutputIt, typename S, typename... Args,
FMT_ENABLE_IF(detail::is_compiled_string<S>::value)> FMT_ENABLE_IF(detail::is_compiled_string<S>::value)>
OutputIt format_to(OutputIt out, const S&, const Args&... args) { format_to_n_result<OutputIt> format_to_n(OutputIt out, size_t n,
constexpr auto compiled = detail::compile<Args...>(S()); const S& format_str, Args&&... args) {
return format_to(out, compiled, args...); auto it = format_to(detail::truncating_iterator<OutputIt>(out, n), format_str,
} std::forward<Args>(args)...);
template <typename OutputIt, typename CompiledFormat, typename... Args>
auto format_to_n(OutputIt out, size_t n, const CompiledFormat& cf,
const Args&... args) ->
typename std::enable_if<
detail::is_output_iterator<OutputIt,
typename CompiledFormat::char_type>::value &&
std::is_base_of<detail::basic_compiled_format,
CompiledFormat>::value,
format_to_n_result<OutputIt>>::type {
auto it =
format_to(detail::truncating_iterator<OutputIt>(out, n), cf, args...);
return {it.base(), it.count()}; return {it.base(), it.count()};
} }
template <typename OutputIt, typename S, typename... Args, template <typename S, typename... Args,
FMT_ENABLE_IF(detail::is_compiled_string<S>::value)> FMT_ENABLE_IF(detail::is_compiled_string<S>::value)>
format_to_n_result<OutputIt> format_to_n(OutputIt out, size_t n, const S&, size_t formatted_size(const S& format_str, const Args&... args) {
const Args&... args) { return format_to(detail::counting_iterator(), format_str, args...).count();
constexpr auto compiled = detail::compile<Args...>(S());
auto it = format_to(detail::truncating_iterator<OutputIt>(out, n), compiled,
args...);
return {it.base(), it.count()};
} }
template <typename CompiledFormat, typename... Args> template <typename S, typename... Args,
size_t formatted_size(const CompiledFormat& cf, const Args&... args) { FMT_ENABLE_IF(detail::is_compiled_string<S>::value)>
return format_to(detail::counting_iterator(), cf, args...).count(); void print(std::FILE* f, const S& format_str, const Args&... args) {
memory_buffer buffer;
format_to(std::back_inserter(buffer), format_str, args...);
detail::print(f, {buffer.data(), buffer.size()});
} }
template <typename S, typename... Args,
FMT_ENABLE_IF(detail::is_compiled_string<S>::value)>
void print(const S& format_str, const Args&... args) {
print(stdout, format_str, args...);
}
#if FMT_USE_NONTYPE_TEMPLATE_PARAMETERS
inline namespace literals {
template <detail_exported::fixed_string Str>
constexpr detail::udl_compiled_string<
remove_cvref_t<decltype(Str.data[0])>,
sizeof(Str.data) / sizeof(decltype(Str.data[0])), Str>
operator""_cf() {
return {};
}
} // namespace literals
#endif
FMT_MODULE_EXPORT_END
FMT_END_NAMESPACE FMT_END_NAMESPACE
#endif // FMT_COMPILE_H_ #endif // FMT_COMPILE_H_

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -1,64 +1,2 @@
// Formatting library for C++ - std::locale support #include "xchar.h"
// #warning fmt/locale.h is deprecated, include fmt/format.h or fmt/xchar.h instead
// 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 <locale>
#include "format.h"
FMT_BEGIN_NAMESPACE
namespace detail {
template <typename Char>
std::basic_string<Char> vformat(
const std::locale& loc, basic_string_view<Char> format_str,
basic_format_args<buffer_context<type_identity_t<Char>>> args) {
basic_memory_buffer<Char> buffer;
detail::vformat_to(buffer, format_str, args, detail::locale_ref(loc));
return fmt::to_string(buffer);
}
} // namespace detail
template <typename S, typename Char = char_t<S>>
inline std::basic_string<Char> vformat(
const std::locale& loc, const S& format_str,
basic_format_args<buffer_context<type_identity_t<Char>>> args) {
return detail::vformat(loc, to_string_view(format_str), args);
}
template <typename S, typename... Args, typename Char = char_t<S>>
inline std::basic_string<Char> format(const std::locale& loc,
const S& format_str, Args&&... args) {
return detail::vformat(loc, to_string_view(format_str),
fmt::make_args_checked<Args...>(format_str, args...));
}
template <typename S, typename OutputIt, typename... Args,
typename Char = char_t<S>,
FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, Char>::value)>
inline OutputIt vformat_to(
OutputIt out, const std::locale& loc, const S& format_str,
basic_format_args<buffer_context<type_identity_t<Char>>> args) {
decltype(detail::get_buffer<Char>(out)) buf(detail::get_buffer_init(out));
vformat_to(buf, to_string_view(format_str), args, detail::locale_ref(loc));
return detail::get_iterator(buf);
}
template <typename OutputIt, typename S, typename... Args,
bool enable = detail::is_output_iterator<OutputIt, char_t<S>>::value>
inline auto format_to(OutputIt out, const std::locale& loc,
const S& format_str, Args&&... args) ->
typename std::enable_if<enable, OutputIt>::type {
const auto& vargs = fmt::make_args_checked<Args...>(format_str, args...);
return vformat_to(out, loc, to_string_view(format_str), vargs);
}
FMT_END_NAMESPACE
#endif // FMT_LOCALE_H_

View File

@@ -8,16 +8,12 @@
#ifndef FMT_OS_H_ #ifndef FMT_OS_H_
#define FMT_OS_H_ #define FMT_OS_H_
#if defined(__MINGW32__) || defined(__CYGWIN__)
// Workaround MinGW bug https://sourceforge.net/p/mingw/bugs/2024/.
# undef __STRICT_ANSI__
#endif
#include <cerrno> #include <cerrno>
#include <clocale> // for locale_t #include <clocale> // locale_t
#include <cstddef> #include <cstddef>
#include <cstdio> #include <cstdio>
#include <cstdlib> // for strtod_l #include <cstdlib> // strtod_l
#include <system_error> // std::system_error
#if defined __APPLE__ || defined(__FreeBSD__) #if defined __APPLE__ || defined(__FreeBSD__)
# include <xlocale.h> // for LC_NUMERIC_MASK on OS X # include <xlocale.h> // for LC_NUMERIC_MASK on OS X
@@ -74,6 +70,7 @@
#define FMT_RETRY(result, expression) FMT_RETRY_VAL(result, expression, -1) #define FMT_RETRY(result, expression) FMT_RETRY_VAL(result, expression, -1)
FMT_BEGIN_NAMESPACE FMT_BEGIN_NAMESPACE
FMT_MODULE_EXPORT_BEGIN
/** /**
\rst \rst
@@ -122,19 +119,28 @@ template <typename Char> class basic_cstring_view {
using cstring_view = basic_cstring_view<char>; using cstring_view = basic_cstring_view<char>;
using wcstring_view = basic_cstring_view<wchar_t>; using wcstring_view = basic_cstring_view<wchar_t>;
// An error code. template <typename Char> struct formatter<std::error_code, Char> {
class error_code { template <typename ParseContext>
private: FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
int value_; return ctx.begin();
}
public: template <typename FormatContext>
explicit error_code(int value = 0) FMT_NOEXCEPT : value_(value) {} FMT_CONSTEXPR auto format(const std::error_code& ec, FormatContext& ctx) const
-> decltype(ctx.out()) {
int get() const FMT_NOEXCEPT { return value_; } auto out = ctx.out();
out = detail::write_bytes(out, ec.category().name(),
basic_format_specs<Char>());
out = detail::write<Char>(out, Char(':'));
out = detail::write<Char>(out, ec.value());
return out;
}
}; };
#ifdef _WIN32 #ifdef _WIN32
namespace detail { FMT_API const std::error_category& system_category() FMT_NOEXCEPT;
FMT_BEGIN_DETAIL_NAMESPACE
// A converter from UTF-16 to UTF-8. // A converter from UTF-16 to UTF-8.
// It is only provided for Windows since other systems support UTF-8 natively. // It is only provided for Windows since other systems support UTF-8 natively.
class utf16_to_utf8 { class utf16_to_utf8 {
@@ -143,7 +149,7 @@ class utf16_to_utf8 {
public: public:
utf16_to_utf8() {} utf16_to_utf8() {}
FMT_API explicit utf16_to_utf8(wstring_view s); FMT_API explicit utf16_to_utf8(basic_string_view<wchar_t> s);
operator string_view() const { return string_view(&buffer_[0], size()); } operator string_view() const { return string_view(&buffer_[0], size()); }
size_t size() const { return buffer_.size() - 1; } size_t size() const { return buffer_.size() - 1; }
const char* c_str() const { return &buffer_[0]; } const char* c_str() const { return &buffer_[0]; }
@@ -152,59 +158,68 @@ class utf16_to_utf8 {
// Performs conversion returning a system error code instead of // Performs conversion returning a system error code instead of
// throwing exception on conversion error. This method may still throw // throwing exception on conversion error. This method may still throw
// in case of memory allocation error. // in case of memory allocation error.
FMT_API int convert(wstring_view s); FMT_API int convert(basic_string_view<wchar_t> s);
}; };
FMT_API void format_windows_error(buffer<char>& out, int error_code, FMT_API void format_windows_error(buffer<char>& out, int error_code,
string_view message) FMT_NOEXCEPT; const char* message) FMT_NOEXCEPT;
} // namespace detail FMT_END_DETAIL_NAMESPACE
/** A Windows error. */ FMT_API std::system_error vwindows_error(int error_code, string_view format_str,
class windows_error : public system_error { format_args args);
private:
FMT_API void init(int error_code, string_view format_str, format_args args);
public: /**
/** \rst
\rst Constructs a :class:`std::system_error` object with the description
Constructs a :class:`fmt::windows_error` object with the description of the form
of the form
.. parsed-literal:: .. parsed-literal::
*<message>*: *<system-message>* *<message>*: *<system-message>*
where *<message>* is the formatted message and *<system-message>* is the where *<message>* is the formatted message and *<system-message>* is the
system message corresponding to the error code. system message corresponding to the error code.
*error_code* is a Windows error code as given by ``GetLastError``. *error_code* is a Windows error code as given by ``GetLastError``.
If *error_code* is not a valid error code such as -1, the system message If *error_code* is not a valid error code such as -1, the system message
will look like "error -1". will look like "error -1".
**Example**:: **Example**::
// This throws a windows_error with the description // This throws a system_error with the description
// cannot open file 'madeup': The system cannot find the file specified. // cannot open file 'madeup': The system cannot find the file specified.
// or similar (system message may vary). // or similar (system message may vary).
const char *filename = "madeup"; const char *filename = "madeup";
LPOFSTRUCT of = LPOFSTRUCT(); LPOFSTRUCT of = LPOFSTRUCT();
HFILE file = OpenFile(filename, &of, OF_READ); HFILE file = OpenFile(filename, &of, OF_READ);
if (file == HFILE_ERROR) { if (file == HFILE_ERROR) {
throw fmt::windows_error(GetLastError(), throw fmt::windows_error(GetLastError(),
"cannot open file '{}'", filename); "cannot open file '{}'", filename);
} }
\endrst \endrst
*/ */
template <typename... Args> template <typename... Args>
windows_error(int error_code, string_view message, const Args&... args) { std::system_error windows_error(int error_code, string_view message,
init(error_code, message, make_format_args(args...)); const Args&... args) {
} return vwindows_error(error_code, message, fmt::make_format_args(args...));
}; }
// Reports a Windows error without throwing an exception. // Reports a Windows error without throwing an exception.
// Can be used to report errors from destructors. // Can be used to report errors from destructors.
FMT_API void report_windows_error(int error_code, FMT_API void report_windows_error(int error_code,
string_view message) FMT_NOEXCEPT; const char* message) FMT_NOEXCEPT;
#else
inline const std::error_category& system_category() FMT_NOEXCEPT {
return std::system_category();
}
#endif // _WIN32 #endif // _WIN32
// std::system is not available on some platforms such as iOS (#2248).
#ifdef __OSX__
template <typename S, typename... Args, typename Char = char_t<S>>
void say(const S& format_str, Args&&... args) {
std::system(format("say \"{}\"", format(format_str, args...)).c_str());
}
#endif
// A buffered file. // A buffered file.
class buffered_file { class buffered_file {
private: private:
@@ -255,7 +270,7 @@ class buffered_file {
template <typename... Args> template <typename... Args>
inline void print(string_view format_str, const Args&... args) { inline void print(string_view format_str, const Args&... args) {
vprint(format_str, make_format_args(args...)); vprint(format_str, fmt::make_format_args(args...));
} }
}; };
@@ -280,7 +295,8 @@ class file {
WRONLY = FMT_POSIX(O_WRONLY), // Open for writing only. WRONLY = FMT_POSIX(O_WRONLY), // Open for writing only.
RDWR = FMT_POSIX(O_RDWR), // Open for reading and writing. RDWR = FMT_POSIX(O_RDWR), // Open for reading and writing.
CREATE = FMT_POSIX(O_CREAT), // Create if the file doesn't exist. CREATE = FMT_POSIX(O_CREAT), // Create if the file doesn't exist.
APPEND = FMT_POSIX(O_APPEND) // Open in append mode. APPEND = FMT_POSIX(O_APPEND), // Open in append mode.
TRUNC = FMT_POSIX(O_TRUNC) // Truncate the content of the file.
}; };
// Constructs a file object which doesn't represent any file. // Constructs a file object which doesn't represent any file.
@@ -295,7 +311,8 @@ class file {
file(file&& other) FMT_NOEXCEPT : fd_(other.fd_) { other.fd_ = -1; } file(file&& other) FMT_NOEXCEPT : fd_(other.fd_) { other.fd_ = -1; }
file& operator=(file&& other) FMT_NOEXCEPT { // Move assignment is not noexcept because close may throw.
file& operator=(file&& other) {
close(); close();
fd_ = other.fd_; fd_ = other.fd_;
other.fd_ = -1; other.fd_ = -1;
@@ -331,7 +348,7 @@ class file {
// Makes fd be the copy of this file descriptor, closing fd first if // Makes fd be the copy of this file descriptor, closing fd first if
// necessary. // necessary.
FMT_API void dup2(int fd, error_code& ec) FMT_NOEXCEPT; FMT_API void dup2(int fd, std::error_code& ec) FMT_NOEXCEPT;
// Creates a pipe setting up read_end and write_end file objects for reading // Creates a pipe setting up read_end and write_end file objects for reading
// and writing respectively. // and writing respectively.
@@ -345,9 +362,10 @@ class file {
// Returns the memory page size. // Returns the memory page size.
long getpagesize(); long getpagesize();
namespace detail { FMT_BEGIN_DETAIL_NAMESPACE
struct buffer_size { struct buffer_size {
buffer_size() = default;
size_t value = 0; size_t value = 0;
buffer_size operator=(size_t val) const { buffer_size operator=(size_t val) const {
auto bs = buffer_size(); auto bs = buffer_size();
@@ -357,14 +375,14 @@ struct buffer_size {
}; };
struct ostream_params { struct ostream_params {
int oflag = file::WRONLY | file::CREATE; int oflag = file::WRONLY | file::CREATE | file::TRUNC;
size_t buffer_size = BUFSIZ > 32768 ? BUFSIZ : 32768; size_t buffer_size = BUFSIZ > 32768 ? BUFSIZ : 32768;
ostream_params() {} ostream_params() {}
template <typename... T> template <typename... T>
ostream_params(T... params, int oflag) : ostream_params(params...) { ostream_params(T... params, int new_oflag) : ostream_params(params...) {
this->oflag = oflag; oflag = new_oflag;
} }
template <typename... T> template <typename... T>
@@ -373,12 +391,13 @@ struct ostream_params {
this->buffer_size = bs.value; this->buffer_size = bs.value;
} }
}; };
} // namespace detail
static constexpr detail::buffer_size buffer_size; FMT_END_DETAIL_NAMESPACE
// A fast output stream which is not thread-safe. constexpr detail::buffer_size buffer_size;
class ostream final : private detail::buffer<char> {
/** A fast output stream which is not thread-safe. */
class FMT_API ostream final : private detail::buffer<char> {
private: private:
file file_; file file_;
@@ -388,7 +407,7 @@ class ostream final : private detail::buffer<char> {
clear(); clear();
} }
FMT_API void grow(size_t) override final; void grow(size_t) override;
ostream(cstring_view path, const detail::ostream_params& params) ostream(cstring_view path, const detail::ostream_params& params)
: file_(path, params.oflag) { : file_(path, params.oflag) {
@@ -399,6 +418,7 @@ class ostream final : private detail::buffer<char> {
ostream(ostream&& other) ostream(ostream&& other)
: detail::buffer<char>(other.data(), other.size(), other.capacity()), : detail::buffer<char>(other.data(), other.size(), other.capacity()),
file_(std::move(other.file_)) { file_(std::move(other.file_)) {
other.clear();
other.set(nullptr, 0); other.set(nullptr, 0);
} }
~ostream() { ~ostream() {
@@ -414,16 +434,30 @@ class ostream final : private detail::buffer<char> {
file_.close(); file_.close();
} }
template <typename S, typename... Args> /**
void print(const S& format_str, const Args&... args) { Formats ``args`` according to specifications in ``fmt`` and writes the
format_to(detail::buffer_appender<char>(*this), format_str, args...); output to the file.
*/
template <typename... T> void print(format_string<T...> fmt, T&&... args) {
vformat_to(detail::buffer_appender<char>(*this), fmt,
fmt::make_format_args(args...));
} }
}; };
/** /**
Opens a file for writing. Supported parameters passed in `params`: \rst
* ``<integer>``: Output flags (``file::WRONLY | file::CREATE`` by default) Opens a file for writing. Supported parameters passed in *params*:
* ``<integer>``: Flags passed to `open
<https://pubs.opengroup.org/onlinepubs/007904875/functions/open.html>`_
(``file::WRONLY | file::CREATE`` by default)
* ``buffer_size=<integer>``: Output buffer size * ``buffer_size=<integer>``: Output buffer size
**Example**::
auto out = fmt::output_file("guide.txt");
out.print("Don't {}", "Panic");
\endrst
*/ */
template <typename... T> template <typename... T>
inline ostream output_file(cstring_view path, T... params) { inline ostream output_file(cstring_view path, T... params) {
@@ -475,6 +509,7 @@ class locale {
}; };
using Locale FMT_DEPRECATED_ALIAS = locale; using Locale FMT_DEPRECATED_ALIAS = locale;
#endif // FMT_LOCALE #endif // FMT_LOCALE
FMT_MODULE_EXPORT_END
FMT_END_NAMESPACE FMT_END_NAMESPACE
#endif // FMT_OS_H_ #endif // FMT_OS_H_

View File

@@ -85,6 +85,8 @@ template <typename T, typename Char> class is_streamable {
using result = decltype(test<T>(0)); using result = decltype(test<T>(0));
public: public:
is_streamable() = default;
static const bool value = result::value; static const bool value = result::value;
}; };
@@ -149,6 +151,7 @@ struct fallback_formatter<T, Char, enable_if_t<is_streamable<T, Char>::value>>
}; };
} // namespace detail } // namespace detail
FMT_MODULE_EXPORT
template <typename Char> template <typename Char>
void vprint(std::basic_ostream<Char>& os, basic_string_view<Char> format_str, void vprint(std::basic_ostream<Char>& os, basic_string_view<Char> format_str,
basic_format_args<buffer_context<type_identity_t<Char>>> args) { basic_format_args<buffer_context<type_identity_t<Char>>> args) {
@@ -166,6 +169,7 @@ void vprint(std::basic_ostream<Char>& os, basic_string_view<Char> format_str,
fmt::print(cerr, "Don't {}!", "panic"); fmt::print(cerr, "Don't {}!", "panic");
\endrst \endrst
*/ */
FMT_MODULE_EXPORT
template <typename S, typename... Args, template <typename S, typename... Args,
typename Char = enable_if_t<detail::is_string<S>::value, char_t<S>>> typename Char = enable_if_t<detail::is_string<S>::value, char_t<S>>>
void print(std::basic_ostream<Char>& os, const S& format_str, Args&&... args) { void print(std::basic_ostream<Char>& os, const S& format_str, Args&&... args) {

View File

@@ -1,2 +0,0 @@
#include "os.h"
#warning "fmt/posix.h is deprecated; use fmt/os.h instead"

View File

@@ -10,11 +10,54 @@
#include <algorithm> // std::max #include <algorithm> // std::max
#include <limits> // std::numeric_limits #include <limits> // std::numeric_limits
#include <ostream>
#include "ostream.h" #include "format.h"
FMT_BEGIN_NAMESPACE FMT_BEGIN_NAMESPACE
namespace detail { FMT_MODULE_EXPORT_BEGIN
template <typename T> struct printf_formatter { printf_formatter() = delete; };
template <typename Char>
class basic_printf_parse_context : public basic_format_parse_context<Char> {
using basic_format_parse_context<Char>::basic_format_parse_context;
};
template <typename OutputIt, typename Char> class basic_printf_context {
private:
OutputIt out_;
basic_format_args<basic_printf_context> args_;
public:
using char_type = Char;
using format_arg = basic_format_arg<basic_printf_context>;
using parse_context_type = basic_printf_parse_context<Char>;
template <typename T> using formatter_type = printf_formatter<T>;
/**
\rst
Constructs a ``printf_context`` object. References to the arguments are
stored in the context object so make sure they have appropriate lifetimes.
\endrst
*/
basic_printf_context(OutputIt out,
basic_format_args<basic_printf_context> args)
: out_(out), args_(args) {}
OutputIt out() { return out_; }
void advance_to(OutputIt it) { out_ = it; }
detail::locale_ref locale() { return {}; }
format_arg arg(int id) const { return args_.get(id); }
FMT_CONSTEXPR void on_error(const char* message) {
detail::error_handler().on_error(message);
}
};
FMT_BEGIN_DETAIL_NAMESPACE
// Checks if a value fits in int - used to avoid warnings about comparing // Checks if a value fits in int - used to avoid warnings about comparing
// signed and unsigned integers. // signed and unsigned integers.
@@ -178,79 +221,34 @@ template <typename Char> class printf_width_handler {
} }
}; };
template <typename Char, typename Context> // The ``printf`` argument formatter.
void vprintf(buffer<Char>& buf, basic_string_view<Char> format,
basic_format_args<Context> args) {
Context(buffer_appender<Char>(buf), format, args).format();
}
} // namespace detail
// For printing into memory_buffer.
template <typename Char, typename Context>
FMT_DEPRECATED void printf(detail::buffer<Char>& buf,
basic_string_view<Char> format,
basic_format_args<Context> args) {
return detail::vprintf(buf, format, args);
}
using detail::vprintf;
template <typename Char>
class basic_printf_parse_context : public basic_format_parse_context<Char> {
using basic_format_parse_context<Char>::basic_format_parse_context;
};
template <typename OutputIt, typename Char> class basic_printf_context;
/**
\rst
The ``printf`` argument formatter.
\endrst
*/
template <typename OutputIt, typename Char> template <typename OutputIt, typename Char>
class printf_arg_formatter : public detail::arg_formatter_base<OutputIt, Char> { class printf_arg_formatter : public arg_formatter<Char> {
public:
using iterator = OutputIt;
private: private:
using char_type = Char; using base = arg_formatter<Char>;
using base = detail::arg_formatter_base<OutputIt, Char>;
using context_type = basic_printf_context<OutputIt, Char>; using context_type = basic_printf_context<OutputIt, Char>;
using format_specs = basic_format_specs<Char>;
context_type& context_; context_type& context_;
void write_null_pointer(char) { OutputIt write_null_pointer(bool is_string = false) {
this->specs()->type = 0; auto s = this->specs;
this->write("(nil)"); s.type = 0;
} return write_bytes(this->out, is_string ? "(null)" : "(nil)", s);
void write_null_pointer(wchar_t) {
this->specs()->type = 0;
this->write(L"(nil)");
} }
public: public:
using format_specs = typename base::format_specs; printf_arg_formatter(OutputIt iter, format_specs& s, context_type& ctx)
: base{iter, s, locale_ref()}, context_(ctx) {}
/** OutputIt operator()(monostate value) { return base::operator()(value); }
\rst
Constructs an argument formatter object.
*buffer* is a reference to the output buffer and *specs* contains format
specifier information for standard argument types.
\endrst
*/
printf_arg_formatter(iterator iter, format_specs& specs, context_type& ctx)
: base(iter, &specs, detail::locale_ref()), context_(ctx) {}
template <typename T, FMT_ENABLE_IF(fmt::detail::is_integral<T>::value)> template <typename T, FMT_ENABLE_IF(detail::is_integral<T>::value)>
iterator operator()(T value) { OutputIt operator()(T value) {
// MSVC2013 fails to compile separate overloads for bool and char_type so // MSVC2013 fails to compile separate overloads for bool and Char so use
// use std::is_same instead. // std::is_same instead.
if (std::is_same<T, bool>::value) { if (std::is_same<T, Char>::value) {
format_specs& fmt_specs = *this->specs(); format_specs fmt_specs = this->specs;
if (fmt_specs.type != 's') return base::operator()(value ? 1 : 0);
fmt_specs.type = 0;
this->write(value != 0);
} else if (std::is_same<T, char_type>::value) {
format_specs& fmt_specs = *this->specs();
if (fmt_specs.type && fmt_specs.type != 'c') if (fmt_specs.type && fmt_specs.type != 'c')
return (*this)(static_cast<int>(value)); return (*this)(static_cast<int>(value));
fmt_specs.sign = sign::none; fmt_specs.sign = sign::none;
@@ -260,138 +258,49 @@ class printf_arg_formatter : public detail::arg_formatter_base<OutputIt, Char> {
// ignored for non-numeric types // ignored for non-numeric types
if (fmt_specs.align == align::none || fmt_specs.align == align::numeric) if (fmt_specs.align == align::none || fmt_specs.align == align::numeric)
fmt_specs.align = align::right; fmt_specs.align = align::right;
return base::operator()(value); return write<Char>(this->out, static_cast<Char>(value), fmt_specs);
} else {
return base::operator()(value);
} }
return this->out(); return base::operator()(value);
} }
template <typename T, FMT_ENABLE_IF(std::is_floating_point<T>::value)> template <typename T, FMT_ENABLE_IF(std::is_floating_point<T>::value)>
iterator operator()(T value) { OutputIt operator()(T value) {
return base::operator()(value); return base::operator()(value);
} }
/** Formats a null-terminated C string. */ /** Formats a null-terminated C string. */
iterator operator()(const char* value) { OutputIt operator()(const char* value) {
if (value) if (value) return base::operator()(value);
base::operator()(value); return write_null_pointer(this->specs.type != 'p');
else if (this->specs()->type == 'p')
write_null_pointer(char_type());
else
this->write("(null)");
return this->out();
} }
/** Formats a null-terminated wide C string. */ /** Formats a null-terminated wide C string. */
iterator operator()(const wchar_t* value) { OutputIt operator()(const wchar_t* value) {
if (value) if (value) return base::operator()(value);
base::operator()(value); return write_null_pointer(this->specs.type != 'p');
else if (this->specs()->type == 'p')
write_null_pointer(char_type());
else
this->write(L"(null)");
return this->out();
} }
iterator operator()(basic_string_view<char_type> value) { OutputIt operator()(basic_string_view<Char> value) {
return base::operator()(value); return base::operator()(value);
} }
iterator operator()(monostate value) { return base::operator()(value); }
/** Formats a pointer. */ /** Formats a pointer. */
iterator operator()(const void* value) { OutputIt operator()(const void* value) {
if (value) return base::operator()(value); return value ? base::operator()(value) : write_null_pointer();
this->specs()->type = 0;
write_null_pointer(char_type());
return this->out();
} }
/** Formats an argument of a custom (user-defined) type. */ /** Formats an argument of a custom (user-defined) type. */
iterator operator()(typename basic_format_arg<context_type>::handle handle) { OutputIt operator()(typename basic_format_arg<context_type>::handle handle) {
handle.format(context_.parse_context(), context_); auto parse_ctx =
return this->out(); basic_printf_parse_context<Char>(basic_string_view<Char>());
handle.format(parse_ctx, context_);
return this->out;
} }
}; };
template <typename T> struct printf_formatter { template <typename Char>
printf_formatter() = delete; void parse_flags(basic_format_specs<Char>& specs, const Char*& it,
const Char* end) {
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()) {
detail::format_value(detail::get_container(ctx.out()), value);
return ctx.out();
}
};
/**
This template formats data and writes the output through an output iterator.
*/
template <typename OutputIt, typename Char> class basic_printf_context {
public:
/** The character type for the output. */
using char_type = Char;
using iterator = OutputIt;
using format_arg = basic_format_arg<basic_printf_context>;
using parse_context_type = basic_printf_parse_context<Char>;
template <typename T> using formatter_type = printf_formatter<T>;
private:
using format_specs = basic_format_specs<char_type>;
OutputIt out_;
basic_format_args<basic_printf_context> args_;
parse_context_type parse_ctx_;
static void parse_flags(format_specs& specs, const Char*& it,
const Char* end);
// Returns the argument with specified index or, if arg_index is -1, the next
// argument.
format_arg get_arg(int arg_index = -1);
// Parses argument index, flags and width and returns the argument index.
int parse_header(const Char*& it, const Char* end, format_specs& specs);
public:
/**
\rst
Constructs a ``printf_context`` object. References to the arguments 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)
: out_(out), args_(args), parse_ctx_(format_str) {}
OutputIt out() { return out_; }
void advance_to(OutputIt it) { out_ = it; }
detail::locale_ref locale() { return {}; }
format_arg arg(int id) const { return args_.get(id); }
parse_context_type& parse_context() { return parse_ctx_; }
FMT_CONSTEXPR void on_error(const char* message) {
parse_ctx_.on_error(message);
}
/** Formats stored arguments and writes the output to the range. */
template <typename ArgFormatter = printf_arg_formatter<OutputIt, Char>>
OutputIt format();
};
template <typename OutputIt, typename Char>
void basic_printf_context<OutputIt, Char>::parse_flags(format_specs& specs,
const Char*& it,
const Char* end) {
for (; it != end; ++it) { for (; it != end; ++it) {
switch (*it) { switch (*it) {
case '-': case '-':
@@ -417,35 +326,24 @@ void basic_printf_context<OutputIt, Char>::parse_flags(format_specs& specs,
} }
} }
template <typename OutputIt, typename Char> template <typename Char, typename GetArg>
typename basic_printf_context<OutputIt, Char>::format_arg int parse_header(const Char*& it, const Char* end,
basic_printf_context<OutputIt, Char>::get_arg(int arg_index) { basic_format_specs<Char>& specs, GetArg get_arg) {
if (arg_index < 0)
arg_index = parse_ctx_.next_arg_id();
else
parse_ctx_.check_arg_id(--arg_index);
return detail::get_arg(*this, arg_index);
}
template <typename OutputIt, typename Char>
int basic_printf_context<OutputIt, Char>::parse_header(const Char*& it,
const Char* end,
format_specs& specs) {
int arg_index = -1; int arg_index = -1;
char_type c = *it; Char c = *it;
if (c >= '0' && c <= '9') { if (c >= '0' && c <= '9') {
// Parse an argument index (if followed by '$') or a width possibly // Parse an argument index (if followed by '$') or a width possibly
// preceded with '0' flag(s). // preceded with '0' flag(s).
detail::error_handler eh; int value = parse_nonnegative_int(it, end, -1);
int value = parse_nonnegative_int(it, end, eh);
if (it != end && *it == '$') { // value is an argument index if (it != end && *it == '$') { // value is an argument index
++it; ++it;
arg_index = value; arg_index = value != -1 ? value : max_value<int>();
} else { } else {
if (c == '0') specs.fill[0] = '0'; if (c == '0') specs.fill[0] = '0';
if (value != 0) { if (value != 0) {
// Nonzero value means that we parsed width and don't need to // Nonzero value means that we parsed width and don't need to
// parse it or flags again, so return now. // parse it or flags again, so return now.
if (value == -1) FMT_THROW(format_error("number is too big"));
specs.width = value; specs.width = value;
return arg_index; return arg_index;
} }
@@ -455,58 +353,76 @@ int basic_printf_context<OutputIt, Char>::parse_header(const Char*& it,
// Parse width. // Parse width.
if (it != end) { if (it != end) {
if (*it >= '0' && *it <= '9') { if (*it >= '0' && *it <= '9') {
detail::error_handler eh; specs.width = parse_nonnegative_int(it, end, -1);
specs.width = parse_nonnegative_int(it, end, eh); if (specs.width == -1) FMT_THROW(format_error("number is too big"));
} else if (*it == '*') { } else if (*it == '*') {
++it; ++it;
specs.width = static_cast<int>(visit_format_arg( specs.width = static_cast<int>(visit_format_arg(
detail::printf_width_handler<char_type>(specs), get_arg())); detail::printf_width_handler<Char>(specs), get_arg(-1)));
} }
} }
return arg_index; return arg_index;
} }
template <typename OutputIt, typename Char> template <typename Char, typename Context>
template <typename ArgFormatter> void vprintf(buffer<Char>& buf, basic_string_view<Char> format,
OutputIt basic_printf_context<OutputIt, Char>::format() { basic_format_args<Context> args) {
auto out = this->out(); using OutputIt = buffer_appender<Char>;
const Char* start = parse_ctx_.begin(); auto out = OutputIt(buf);
const Char* end = parse_ctx_.end(); auto context = basic_printf_context<OutputIt, Char>(out, args);
auto parse_ctx = basic_printf_parse_context<Char>(format);
// Returns the argument with specified index or, if arg_index is -1, the next
// argument.
auto get_arg = [&](int arg_index) {
if (arg_index < 0)
arg_index = parse_ctx.next_arg_id();
else
parse_ctx.check_arg_id(--arg_index);
return detail::get_arg(context, arg_index);
};
const Char* start = parse_ctx.begin();
const Char* end = parse_ctx.end();
auto it = start; auto it = start;
while (it != end) { while (it != end) {
char_type c = *it++; if (!detail::find<false, Char>(it, end, '%', it)) {
if (c != '%') continue; it = end; // detail::find leaves it == nullptr if it doesn't find '%'
break;
}
Char c = *it++;
if (it != end && *it == c) { if (it != end && *it == c) {
out = std::copy(start, it, out); out = detail::write(
out, basic_string_view<Char>(start, detail::to_unsigned(it - start)));
start = ++it; start = ++it;
continue; continue;
} }
out = std::copy(start, it - 1, out); out = detail::write(out, basic_string_view<Char>(
start, detail::to_unsigned(it - 1 - start)));
format_specs specs; basic_format_specs<Char> specs;
specs.align = align::right; specs.align = align::right;
// Parse argument index, flags and width. // Parse argument index, flags and width.
int arg_index = parse_header(it, end, specs); int arg_index = parse_header(it, end, specs, get_arg);
if (arg_index == 0) on_error("argument not found"); if (arg_index == 0) parse_ctx.on_error("argument not found");
// Parse precision. // Parse precision.
if (it != end && *it == '.') { if (it != end && *it == '.') {
++it; ++it;
c = it != end ? *it : 0; c = it != end ? *it : 0;
if ('0' <= c && c <= '9') { if ('0' <= c && c <= '9') {
detail::error_handler eh; specs.precision = parse_nonnegative_int(it, end, 0);
specs.precision = parse_nonnegative_int(it, end, eh);
} else if (c == '*') { } else if (c == '*') {
++it; ++it;
specs.precision = static_cast<int>( specs.precision = static_cast<int>(
visit_format_arg(detail::printf_precision_handler(), get_arg())); visit_format_arg(detail::printf_precision_handler(), get_arg(-1)));
} else { } else {
specs.precision = 0; specs.precision = 0;
} }
} }
format_arg arg = get_arg(arg_index); auto arg = get_arg(arg_index);
// For d, i, o, u, x, and X conversion specifiers, if a precision is // For d, i, o, u, x, and X conversion specifiers, if a precision is
// specified, the '0' flag is ignored // specified, the '0' flag is ignored
if (specs.precision >= 0 && arg.is_integral()) if (specs.precision >= 0 && arg.is_integral())
@@ -516,9 +432,10 @@ OutputIt basic_printf_context<OutputIt, Char>::format() {
auto str = visit_format_arg(detail::get_cstring<Char>(), arg); auto str = visit_format_arg(detail::get_cstring<Char>(), arg);
auto str_end = str + specs.precision; auto str_end = str + specs.precision;
auto nul = std::find(str, str_end, Char()); auto nul = std::find(str, str_end, Char());
arg = detail::make_arg<basic_printf_context>(basic_string_view<Char>( arg = detail::make_arg<basic_printf_context<OutputIt, Char>>(
str, basic_string_view<Char>(
detail::to_unsigned(nul != str_end ? nul - str : specs.precision))); str, detail::to_unsigned(nul != str_end ? nul - str
: specs.precision)));
} }
if (specs.alt && visit_format_arg(detail::is_zero_int(), arg)) if (specs.alt && visit_format_arg(detail::is_zero_int(), arg))
specs.alt = false; specs.alt = false;
@@ -532,7 +449,7 @@ OutputIt basic_printf_context<OutputIt, Char>::format() {
// Parse length and convert the argument to the required type. // Parse length and convert the argument to the required type.
c = it != end ? *it++ : 0; c = it != end ? *it++ : 0;
char_type t = it != end ? *it : 0; Char t = it != end ? *it : 0;
using detail::convert_arg; using detail::convert_arg;
switch (c) { switch (c) {
case 'h': case 'h':
@@ -582,8 +499,9 @@ OutputIt basic_printf_context<OutputIt, Char>::format() {
specs.type = 'd'; specs.type = 'd';
break; break;
case 'c': case 'c':
visit_format_arg(detail::char_converter<basic_printf_context>(arg), visit_format_arg(
arg); detail::char_converter<basic_printf_context<OutputIt, Char>>(arg),
arg);
break; break;
} }
} }
@@ -591,10 +509,12 @@ OutputIt basic_printf_context<OutputIt, Char>::format() {
start = it; start = it;
// Format argument. // Format argument.
out = visit_format_arg(ArgFormatter(out, specs, *this), arg); out = visit_format_arg(
detail::printf_arg_formatter<OutputIt, Char>(out, specs, context), arg);
} }
return std::copy(start, it, out); detail::write(out, basic_string_view<Char>(start, to_unsigned(it - start)));
} }
FMT_END_DETAIL_NAMESPACE
template <typename Char> template <typename Char>
using basic_printf_context_t = using basic_printf_context_t =
@@ -612,9 +532,9 @@ using wprintf_args = basic_format_args<wprintf_context>;
arguments and can be implicitly converted to `~fmt::printf_args`. arguments and can be implicitly converted to `~fmt::printf_args`.
\endrst \endrst
*/ */
template <typename... Args> template <typename... T>
inline format_arg_store<printf_context, Args...> make_printf_args( inline auto make_printf_args(const T&... args)
const Args&... args) { -> format_arg_store<printf_context, T...> {
return {args...}; return {args...};
} }
@@ -624,18 +544,19 @@ inline format_arg_store<printf_context, Args...> make_printf_args(
arguments and can be implicitly converted to `~fmt::wprintf_args`. arguments and can be implicitly converted to `~fmt::wprintf_args`.
\endrst \endrst
*/ */
template <typename... Args> template <typename... T>
inline format_arg_store<wprintf_context, Args...> make_wprintf_args( inline auto make_wprintf_args(const T&... args)
const Args&... args) { -> format_arg_store<wprintf_context, T...> {
return {args...}; return {args...};
} }
template <typename S, typename Char = char_t<S>> template <typename S, typename Char = char_t<S>>
inline std::basic_string<Char> vsprintf( inline auto vsprintf(
const S& format, const S& fmt,
basic_format_args<basic_printf_context_t<type_identity_t<Char>>> args) { basic_format_args<basic_printf_context_t<type_identity_t<Char>>> args)
-> std::basic_string<Char> {
basic_memory_buffer<Char> buffer; basic_memory_buffer<Char> buffer;
vprintf(buffer, to_string_view(format), args); vprintf(buffer, to_string_view(fmt), args);
return to_string(buffer); return to_string(buffer);
} }
@@ -648,19 +569,20 @@ inline std::basic_string<Char> vsprintf(
std::string message = fmt::sprintf("The answer is %d", 42); std::string message = fmt::sprintf("The answer is %d", 42);
\endrst \endrst
*/ */
template <typename S, typename... Args, template <typename S, typename... T,
typename Char = enable_if_t<detail::is_string<S>::value, char_t<S>>> typename Char = enable_if_t<detail::is_string<S>::value, char_t<S>>>
inline std::basic_string<Char> sprintf(const S& format, const Args&... args) { inline auto sprintf(const S& fmt, const T&... args) -> std::basic_string<Char> {
using context = basic_printf_context_t<Char>; using context = basic_printf_context_t<Char>;
return vsprintf(to_string_view(format), make_format_args<context>(args...)); return vsprintf(to_string_view(fmt), fmt::make_format_args<context>(args...));
} }
template <typename S, typename Char = char_t<S>> template <typename S, typename Char = char_t<S>>
inline int vfprintf( inline auto vfprintf(
std::FILE* f, const S& format, std::FILE* f, const S& fmt,
basic_format_args<basic_printf_context_t<type_identity_t<Char>>> args) { basic_format_args<basic_printf_context_t<type_identity_t<Char>>> args)
-> int {
basic_memory_buffer<Char> buffer; basic_memory_buffer<Char> buffer;
vprintf(buffer, to_string_view(format), args); vprintf(buffer, to_string_view(fmt), args);
size_t size = buffer.size(); size_t size = buffer.size();
return std::fwrite(buffer.data(), sizeof(Char), size, f) < size return std::fwrite(buffer.data(), sizeof(Char), size, f) < size
? -1 ? -1
@@ -676,19 +598,19 @@ inline int vfprintf(
fmt::fprintf(stderr, "Don't %s!", "panic"); fmt::fprintf(stderr, "Don't %s!", "panic");
\endrst \endrst
*/ */
template <typename S, typename... Args, template <typename S, typename... T, typename Char = char_t<S>>
typename Char = enable_if_t<detail::is_string<S>::value, char_t<S>>> inline auto fprintf(std::FILE* f, const S& fmt, const T&... args) -> int {
inline int fprintf(std::FILE* f, const S& format, const Args&... args) {
using context = basic_printf_context_t<Char>; using context = basic_printf_context_t<Char>;
return vfprintf(f, to_string_view(format), return vfprintf(f, to_string_view(fmt),
make_format_args<context>(args...)); fmt::make_format_args<context>(args...));
} }
template <typename S, typename Char = char_t<S>> template <typename S, typename Char = char_t<S>>
inline int vprintf( inline auto vprintf(
const S& format, const S& fmt,
basic_format_args<basic_printf_context_t<type_identity_t<Char>>> args) { basic_format_args<basic_printf_context_t<type_identity_t<Char>>> args)
return vfprintf(stdout, to_string_view(format), args); -> int {
return vfprintf(stdout, to_string_view(fmt), args);
} }
/** /**
@@ -700,52 +622,31 @@ inline int vprintf(
fmt::printf("Elapsed time: %.2f seconds", 1.23); fmt::printf("Elapsed time: %.2f seconds", 1.23);
\endrst \endrst
*/ */
template <typename S, typename... Args, template <typename S, typename... T, FMT_ENABLE_IF(detail::is_string<S>::value)>
FMT_ENABLE_IF(detail::is_string<S>::value)> inline auto printf(const S& fmt, const T&... args) -> int {
inline int printf(const S& format_str, const Args&... args) { return vprintf(
using context = basic_printf_context_t<char_t<S>>; to_string_view(fmt),
return vprintf(to_string_view(format_str), fmt::make_format_args<basic_printf_context_t<char_t<S>>>(args...));
make_format_args<context>(args...));
} }
template <typename S, typename Char = char_t<S>> template <typename S, typename Char = char_t<S>>
inline int vfprintf( FMT_DEPRECATED auto vfprintf(
std::basic_ostream<Char>& os, const S& format, std::basic_ostream<Char>& os, const S& fmt,
basic_format_args<basic_printf_context_t<type_identity_t<Char>>> args) { basic_format_args<basic_printf_context_t<type_identity_t<Char>>> args)
-> int {
basic_memory_buffer<Char> buffer; basic_memory_buffer<Char> buffer;
vprintf(buffer, to_string_view(format), args); vprintf(buffer, to_string_view(fmt), args);
detail::write_buffer(os, buffer); os.write(buffer.data(), static_cast<std::streamsize>(buffer.size()));
return static_cast<int>(buffer.size()); return static_cast<int>(buffer.size());
} }
template <typename S, typename... T, typename Char = char_t<S>>
/** Formats arguments and writes the output to the range. */ FMT_DEPRECATED auto fprintf(std::basic_ostream<Char>& os, const S& fmt,
template <typename ArgFormatter, typename Char, const T&... args) -> int {
typename Context = return vfprintf(os, to_string_view(fmt),
basic_printf_context<typename ArgFormatter::iterator, Char>> fmt::make_format_args<basic_printf_context_t<Char>>(args...));
typename ArgFormatter::iterator vprintf(
detail::buffer<Char>& out, basic_string_view<Char> format_str,
basic_format_args<type_identity_t<Context>> args) {
typename ArgFormatter::iterator iter(out);
Context(iter, format_str, args).template format<ArgFormatter>();
return iter;
} }
/** FMT_MODULE_EXPORT_END
\rst
Prints formatted data to the stream *os*.
**Example**::
fmt::fprintf(cerr, "Don't %s!", "panic");
\endrst
*/
template <typename S, typename... Args, typename Char = char_t<S>>
inline int fprintf(std::basic_ostream<Char>& os, const S& format_str,
const Args&... args) {
using context = basic_printf_context_t<Char>;
return vfprintf(os, to_string_view(format_str),
make_format_args<context>(args...));
}
FMT_END_NAMESPACE FMT_END_NAMESPACE
#endif // FMT_PRINTF_H_ #endif // FMT_PRINTF_H_

View File

@@ -17,41 +17,31 @@
#include "format.h" #include "format.h"
// 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 FMT_BEGIN_NAMESPACE
template <typename Char> struct formatting_base { template <typename Char, typename Enable = void> struct formatting_range {
#ifdef FMT_DEPRECATED_BRACED_RANGES
Char prefix = '{';
Char postfix = '}';
#else
Char prefix = '[';
Char postfix = ']';
#endif
template <typename ParseContext> template <typename ParseContext>
FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) { FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
return ctx.begin(); return ctx.begin();
} }
}; };
template <typename Char, typename Enable = void> template <typename Char, typename Enable = void> struct formatting_tuple {
struct formatting_range : formatting_base<Char> { Char prefix = '(';
static FMT_CONSTEXPR_DECL const size_t range_length_limit = Char postfix = ')';
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> template <typename ParseContext>
struct formatting_tuple : formatting_base<Char> { FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
Char prefix; return ctx.begin();
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 detail { namespace detail {
@@ -75,8 +65,14 @@ OutputIterator copy(char ch, OutputIterator out) {
return out; return out;
} }
template <typename OutputIterator>
OutputIterator copy(wchar_t ch, OutputIterator out) {
*out++ = ch;
return out;
}
/// Return true value if T has std::string interface, like std::string_view. /// Return true value if T has std::string interface, like std::string_view.
template <typename T> class is_like_std_string { template <typename T> class is_std_string_like {
template <typename U> template <typename U>
static auto check(U* p) static auto check(U* p)
-> decltype((void)p->find('a'), p->length(), (void)p->data(), int()); -> decltype((void)p->find('a'), p->length(), (void)p->data(), int());
@@ -88,19 +84,107 @@ template <typename T> class is_like_std_string {
}; };
template <typename Char> template <typename Char>
struct is_like_std_string<fmt::basic_string_view<Char>> : std::true_type {}; struct is_std_string_like<fmt::basic_string_view<Char>> : std::true_type {};
template <typename... Ts> struct conditional_helper {}; template <typename... Ts> struct conditional_helper {};
template <typename T, typename _ = void> struct is_range_ : std::false_type {}; template <typename T, typename _ = void> struct is_range_ : std::false_type {};
#if !FMT_MSC_VER || FMT_MSC_VER > 1800 #if !FMT_MSC_VER || FMT_MSC_VER > 1800
# define FMT_DECLTYPE_RETURN(val) \
->decltype(val) { return val; } \
static_assert( \
true, "") // This makes it so that a semicolon is required after the
// macro, which helps clang-format handle the formatting.
// C array overload
template <typename T, std::size_t N>
auto range_begin(const T (&arr)[N]) -> const T* {
return arr;
}
template <typename T, std::size_t N>
auto range_end(const T (&arr)[N]) -> const T* {
return arr + N;
}
template <typename T, typename Enable = void>
struct has_member_fn_begin_end_t : std::false_type {};
template <typename T> template <typename T>
struct is_range_< struct has_member_fn_begin_end_t<T, void_t<decltype(std::declval<T>().begin()),
T, conditional_t<false, decltype(std::declval<T>().end())>>
conditional_helper<decltype(std::declval<T>().begin()), : std::true_type {};
decltype(std::declval<T>().end())>,
void>> : std::true_type {}; // Member function overload
template <typename T>
auto range_begin(T&& rng) FMT_DECLTYPE_RETURN(static_cast<T&&>(rng).begin());
template <typename T>
auto range_end(T&& rng) FMT_DECLTYPE_RETURN(static_cast<T&&>(rng).end());
// ADL overload. Only participates in overload resolution if member functions
// are not found.
template <typename T>
auto range_begin(T&& rng)
-> enable_if_t<!has_member_fn_begin_end_t<T&&>::value,
decltype(begin(static_cast<T&&>(rng)))> {
return begin(static_cast<T&&>(rng));
}
template <typename T>
auto range_end(T&& rng) -> enable_if_t<!has_member_fn_begin_end_t<T&&>::value,
decltype(end(static_cast<T&&>(rng)))> {
return end(static_cast<T&&>(rng));
}
template <typename T, typename Enable = void>
struct has_const_begin_end : std::false_type {};
template <typename T, typename Enable = void>
struct has_mutable_begin_end : std::false_type {};
template <typename T>
struct has_const_begin_end<
T, void_t<decltype(detail::range_begin(
std::declval<const remove_cvref_t<T>&>())),
decltype(detail::range_begin(
std::declval<const remove_cvref_t<T>&>()))>>
: std::true_type {};
template <typename T>
struct has_mutable_begin_end<
T, void_t<decltype(detail::range_begin(std::declval<T>())),
decltype(detail::range_begin(std::declval<T>())),
enable_if_t<std::is_copy_constructible<T>::value>>>
: std::true_type {};
template <typename T>
struct is_range_<T, void>
: std::integral_constant<bool, (has_const_begin_end<T>::value ||
has_mutable_begin_end<T>::value)> {};
template <typename T, typename Enable = void> struct range_to_view;
template <typename T>
struct range_to_view<T, enable_if_t<has_const_begin_end<T>::value>> {
struct view_t {
const T* m_range_ptr;
auto begin() const FMT_DECLTYPE_RETURN(detail::range_begin(*m_range_ptr));
auto end() const FMT_DECLTYPE_RETURN(detail::range_end(*m_range_ptr));
};
static auto view(const T& range) -> view_t { return {&range}; }
};
template <typename T>
struct range_to_view<T, enable_if_t<!has_const_begin_end<T>::value &&
has_mutable_begin_end<T>::value>> {
struct view_t {
T m_range_copy;
auto begin() FMT_DECLTYPE_RETURN(detail::range_begin(m_range_copy));
auto end() FMT_DECLTYPE_RETURN(detail::range_end(m_range_copy));
};
static auto view(const T& range) -> view_t { return {range}; }
};
# undef FMT_DECLTYPE_RETURN
#endif #endif
/// tuple_size and tuple_element check. /// tuple_size and tuple_element check.
@@ -158,33 +242,42 @@ template <class Tuple, class F> void for_each(Tuple&& tup, F&& f) {
} }
template <typename Range> template <typename Range>
using value_type = remove_cvref_t<decltype(*std::declval<Range>().begin())>; using value_type =
remove_cvref_t<decltype(*detail::range_begin(std::declval<Range>()))>;
template <typename Arg, FMT_ENABLE_IF(!is_like_std_string< template <typename OutputIt> OutputIt write_delimiter(OutputIt out) {
typename std::decay<Arg>::type>::value)> *out++ = ',';
FMT_CONSTEXPR const char* format_str_quoted(bool add_space, const Arg&) { *out++ = ' ';
return add_space ? " {}" : "{}"; return out;
} }
template <typename Arg, FMT_ENABLE_IF(is_like_std_string< template <
typename std::decay<Arg>::type>::value)> typename Char, typename OutputIt, typename Arg,
FMT_CONSTEXPR const char* format_str_quoted(bool add_space, const Arg&) { FMT_ENABLE_IF(is_std_string_like<typename std::decay<Arg>::type>::value)>
return add_space ? " \"{}\"" : "\"{}\""; OutputIt write_range_entry(OutputIt out, const Arg& v) {
*out++ = '"';
out = write<Char>(out, v);
*out++ = '"';
return out;
} }
FMT_CONSTEXPR const char* format_str_quoted(bool add_space, const char*) { template <typename Char, typename OutputIt, typename Arg,
return add_space ? " \"{}\"" : "\"{}\""; FMT_ENABLE_IF(std::is_same<Arg, Char>::value)>
} OutputIt write_range_entry(OutputIt out, const Arg v) {
FMT_CONSTEXPR const wchar_t* format_str_quoted(bool add_space, const wchar_t*) { *out++ = '\'';
return add_space ? L" \"{}\"" : L"\"{}\""; *out++ = v;
*out++ = '\'';
return out;
} }
FMT_CONSTEXPR const char* format_str_quoted(bool add_space, const char) { template <
return add_space ? " '{}'" : "'{}'"; typename Char, typename OutputIt, typename Arg,
} FMT_ENABLE_IF(!is_std_string_like<typename std::decay<Arg>::type>::value &&
FMT_CONSTEXPR const wchar_t* format_str_quoted(bool add_space, const wchar_t) { !std::is_same<Arg, Char>::value)>
return add_space ? L" '{}'" : L"'{}'"; OutputIt write_range_entry(OutputIt out, const Arg& v) {
return write<Char>(out, v);
} }
} // namespace detail } // namespace detail
template <typename T> struct is_tuple_like { template <typename T> struct is_tuple_like {
@@ -198,23 +291,14 @@ struct formatter<TupleT, Char, enable_if_t<fmt::is_tuple_like<TupleT>::value>> {
// C++11 generic lambda for format() // C++11 generic lambda for format()
template <typename FormatContext> struct format_each { template <typename FormatContext> struct format_each {
template <typename T> void operator()(const T& v) { template <typename T> void operator()(const T& v) {
if (i > 0) { if (i > 0) out = detail::write_delimiter(out);
if (formatting.add_prepostfix_space) { out = detail::write_range_entry<Char>(out, v);
*out++ = ' ';
}
out = detail::copy(formatting.delimiter, out);
}
out = format_to(out,
detail::format_str_quoted(
(formatting.add_delimiter_spaces && i > 0), v),
v);
++i; ++i;
} }
formatting_tuple<Char>& formatting; formatting_tuple<Char>& formatting;
size_t& i; size_t& i;
typename std::add_lvalue_reference<decltype( typename std::add_lvalue_reference<
std::declval<FormatContext>().out())>::type out; decltype(std::declval<FormatContext>().out())>::type out;
}; };
public: public:
@@ -229,12 +313,9 @@ struct formatter<TupleT, Char, enable_if_t<fmt::is_tuple_like<TupleT>::value>> {
auto format(const TupleT& values, FormatContext& ctx) -> decltype(ctx.out()) { auto format(const TupleT& values, FormatContext& ctx) -> decltype(ctx.out()) {
auto out = ctx.out(); auto out = ctx.out();
size_t i = 0; size_t i = 0;
detail::copy(formatting.prefix, out);
detail::copy(formatting.prefix, out);
detail::for_each(values, format_each<FormatContext>{formatting, i, out}); detail::for_each(values, format_each<FormatContext>{formatting, i, out});
if (formatting.add_prepostfix_space) {
*out++ = ' ';
}
detail::copy(formatting.postfix, out); detail::copy(formatting.postfix, out);
return ctx.out(); return ctx.out();
@@ -243,7 +324,7 @@ struct formatter<TupleT, Char, enable_if_t<fmt::is_tuple_like<TupleT>::value>> {
template <typename T, typename Char> struct is_range { template <typename T, typename Char> struct is_range {
static FMT_CONSTEXPR_DECL const bool value = static FMT_CONSTEXPR_DECL const bool value =
detail::is_range_<T>::value && !detail::is_like_std_string<T>::value && detail::is_range_<T>::value && !detail::is_std_string_like<T>::value &&
!std::is_convertible<T, std::basic_string<Char>>::value && !std::is_convertible<T, std::basic_string<Char>>::value &&
!std::is_constructible<detail::std_string_view<Char>, T>::value; !std::is_constructible<detail::std_string_view<Char>, T>::value;
}; };
@@ -251,15 +332,14 @@ template <typename T, typename Char> struct is_range {
template <typename T, typename Char> template <typename T, typename Char>
struct formatter< struct formatter<
T, Char, T, Char,
enable_if_t<fmt::is_range<T, Char>::value enable_if_t<
fmt::is_range<T, Char>::value
// Workaround a bug in MSVC 2017 and earlier. // Workaround a bug in MSVC 2017 and earlier.
#if !FMT_MSC_VER || FMT_MSC_VER >= 1927 #if !FMT_MSC_VER || FMT_MSC_VER >= 1927
&& && (has_formatter<detail::value_type<T>, format_context>::value ||
(has_formatter<detail::value_type<T>, format_context>::value || detail::has_fallback_formatter<detail::value_type<T>, Char>::value)
detail::has_fallback_formatter<detail::value_type<T>,
format_context>::value)
#endif #endif
>> { >> {
formatting_range<Char> formatting; formatting_range<Char> formatting;
template <typename ParseContext> template <typename ParseContext>
@@ -271,71 +351,65 @@ struct formatter<
typename FormatContext::iterator format(const T& values, FormatContext& ctx) { typename FormatContext::iterator format(const T& values, FormatContext& ctx) {
auto out = detail::copy(formatting.prefix, ctx.out()); auto out = detail::copy(formatting.prefix, ctx.out());
size_t i = 0; size_t i = 0;
auto it = values.begin(); auto view = detail::range_to_view<T>::view(values);
auto end = values.end(); auto it = view.begin();
auto end = view.end();
for (; it != end; ++it) { for (; it != end; ++it) {
if (i > 0) { if (i > 0) out = detail::write_delimiter(out);
if (formatting.add_prepostfix_space) *out++ = ' '; out = detail::write_range_entry<Char>(out, *it);
out = detail::copy(formatting.delimiter, out); ++i;
}
out = format_to(out,
detail::format_str_quoted(
(formatting.add_delimiter_spaces && i > 0), *it),
*it);
if (++i > formatting.range_length_limit) {
out = format_to(out, " ... <other elements>");
break;
}
} }
if (formatting.add_prepostfix_space) *out++ = ' ';
return detail::copy(formatting.postfix, out); return detail::copy(formatting.postfix, out);
} }
}; };
template <typename Char, typename... T> struct tuple_arg_join : detail::view { template <typename Char, typename... T> struct tuple_join_view : detail::view {
const std::tuple<T...>& tuple; const std::tuple<T...>& tuple;
basic_string_view<Char> sep; basic_string_view<Char> sep;
tuple_arg_join(const std::tuple<T...>& t, basic_string_view<Char> s) tuple_join_view(const std::tuple<T...>& t, basic_string_view<Char> s)
: tuple{t}, sep{s} {} : tuple(t), sep{s} {}
}; };
template <typename Char, typename... T> template <typename Char, typename... T>
struct formatter<tuple_arg_join<Char, T...>, Char> { using tuple_arg_join = tuple_join_view<Char, T...>;
template <typename Char, typename... T>
struct formatter<tuple_join_view<Char, T...>, Char> {
template <typename ParseContext> template <typename ParseContext>
FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) { FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
return ctx.begin(); return ctx.begin();
} }
template <typename FormatContext> template <typename FormatContext>
typename FormatContext::iterator format( auto format(const tuple_join_view<Char, T...>& value, FormatContext& ctx) ->
const tuple_arg_join<Char, T...>& value, FormatContext& ctx) { typename FormatContext::iterator {
return format(value, ctx, detail::make_index_sequence<sizeof...(T)>{}); return format(value, ctx, detail::make_index_sequence<sizeof...(T)>{});
} }
private: private:
template <typename FormatContext, size_t... N> template <typename FormatContext, size_t... N>
typename FormatContext::iterator format( auto format(const tuple_join_view<Char, T...>& value, FormatContext& ctx,
const tuple_arg_join<Char, T...>& value, FormatContext& ctx, detail::index_sequence<N...>) ->
detail::index_sequence<N...>) { typename FormatContext::iterator {
return format_args(value, ctx, std::get<N>(value.tuple)...); using std::get;
return format_args(value, ctx, get<N>(value.tuple)...);
} }
template <typename FormatContext> template <typename FormatContext>
typename FormatContext::iterator format_args( auto format_args(const tuple_join_view<Char, T...>&, FormatContext& ctx) ->
const tuple_arg_join<Char, T...>&, FormatContext& ctx) { typename FormatContext::iterator {
// NOTE: for compilers that support C++17, this empty function instantiation // NOTE: for compilers that support C++17, this empty function instantiation
// can be replaced with a constexpr branch in the variadic overload. // can be replaced with a constexpr branch in the variadic overload.
return ctx.out(); return ctx.out();
} }
template <typename FormatContext, typename Arg, typename... Args> template <typename FormatContext, typename Arg, typename... Args>
typename FormatContext::iterator format_args( auto format_args(const tuple_join_view<Char, T...>& value, FormatContext& ctx,
const tuple_arg_join<Char, T...>& value, FormatContext& ctx, const Arg& arg, const Args&... args) ->
const Arg& arg, const Args&... args) { typename FormatContext::iterator {
using base = formatter<typename std::decay<Arg>::type, Char>; using base = formatter<typename std::decay<Arg>::type, Char>;
auto out = ctx.out(); auto out = base().format(arg, ctx);
out = base{}.format(arg, ctx);
if (sizeof...(Args) > 0) { if (sizeof...(Args) > 0) {
out = std::copy(value.sep.begin(), value.sep.end(), out); out = std::copy(value.sep.begin(), value.sep.end(), out);
ctx.advance_to(out); ctx.advance_to(out);
@@ -345,6 +419,8 @@ struct formatter<tuple_arg_join<Char, T...>, Char> {
} }
}; };
FMT_MODULE_EXPORT_BEGIN
/** /**
\rst \rst
Returns an object that formats `tuple` with elements separated by `sep`. Returns an object that formats `tuple` with elements separated by `sep`.
@@ -357,14 +433,15 @@ struct formatter<tuple_arg_join<Char, T...>, Char> {
\endrst \endrst
*/ */
template <typename... T> template <typename... T>
FMT_CONSTEXPR tuple_arg_join<char, T...> join(const std::tuple<T...>& tuple, FMT_CONSTEXPR auto join(const std::tuple<T...>& tuple, string_view sep)
string_view sep) { -> tuple_join_view<char, T...> {
return {tuple, sep}; return {tuple, sep};
} }
template <typename... T> template <typename... T>
FMT_CONSTEXPR tuple_arg_join<wchar_t, T...> join(const std::tuple<T...>& tuple, FMT_CONSTEXPR auto join(const std::tuple<T...>& tuple,
wstring_view sep) { basic_string_view<wchar_t> sep)
-> tuple_join_view<wchar_t, T...> {
return {tuple, sep}; return {tuple, sep};
} }
@@ -380,17 +457,12 @@ FMT_CONSTEXPR tuple_arg_join<wchar_t, T...> join(const std::tuple<T...>& tuple,
\endrst \endrst
*/ */
template <typename T> template <typename T>
arg_join<const T*, const T*, char> join(std::initializer_list<T> list, auto join(std::initializer_list<T> list, string_view sep)
string_view sep) { -> join_view<const T*, const T*> {
return join(std::begin(list), std::end(list), sep);
}
template <typename T>
arg_join<const T*, const T*, wchar_t> join(std::initializer_list<T> list,
wstring_view sep) {
return join(std::begin(list), std::end(list), sep); return join(std::begin(list), std::end(list), sep);
} }
FMT_MODULE_EXPORT_END
FMT_END_NAMESPACE FMT_END_NAMESPACE
#endif // FMT_RANGES_H_ #endif // FMT_RANGES_H_

View File

@@ -0,0 +1,236 @@
// Formatting library for C++ - optional wchar_t and exotic character support
//
// Copyright (c) 2012 - present, Victor Zverovich
// All rights reserved.
//
// For the license information refer to format.h.
#ifndef FMT_WCHAR_H_
#define FMT_WCHAR_H_
#include <cwchar>
#include <tuple>
#include "format.h"
FMT_BEGIN_NAMESPACE
namespace detail {
template <typename T>
using is_exotic_char = bool_constant<!std::is_same<T, char>::value>;
}
FMT_MODULE_EXPORT_BEGIN
using wstring_view = basic_string_view<wchar_t>;
using wformat_parse_context = basic_format_parse_context<wchar_t>;
using wformat_context = buffer_context<wchar_t>;
using wformat_args = basic_format_args<wformat_context>;
using wmemory_buffer = basic_memory_buffer<wchar_t>;
#if FMT_GCC_VERSION && FMT_GCC_VERSION < 409
// Workaround broken conversion on older gcc.
template <typename... Args> using wformat_string = wstring_view;
#else
template <typename... Args>
using wformat_string = basic_format_string<wchar_t, type_identity_t<Args>...>;
#endif
template <> struct is_char<wchar_t> : std::true_type {};
template <> struct is_char<detail::char8_type> : std::true_type {};
template <> struct is_char<char16_t> : std::true_type {};
template <> struct is_char<char32_t> : std::true_type {};
template <typename... Args>
constexpr format_arg_store<wformat_context, Args...> make_wformat_args(
const Args&... args) {
return {args...};
}
inline namespace literals {
constexpr auto operator"" _format(const wchar_t* s, size_t n)
-> detail::udl_formatter<wchar_t> {
return {{s, n}};
}
#if FMT_USE_USER_DEFINED_LITERALS && !FMT_USE_NONTYPE_TEMPLATE_PARAMETERS
constexpr detail::udl_arg<wchar_t> operator"" _a(const wchar_t* s, size_t) {
return {s};
}
#endif
} // namespace literals
template <typename It, typename Sentinel>
auto join(It begin, Sentinel end, wstring_view sep)
-> join_view<It, Sentinel, wchar_t> {
return {begin, end, sep};
}
template <typename Range>
auto join(Range&& range, wstring_view sep)
-> join_view<detail::iterator_t<Range>, detail::sentinel_t<Range>,
wchar_t> {
return join(std::begin(range), std::end(range), sep);
}
template <typename T>
auto join(std::initializer_list<T> list, wstring_view sep)
-> join_view<const T*, const T*, wchar_t> {
return join(std::begin(list), std::end(list), sep);
}
template <typename Char, FMT_ENABLE_IF(!std::is_same<Char, char>::value)>
auto vformat(basic_string_view<Char> format_str,
basic_format_args<buffer_context<type_identity_t<Char>>> args)
-> std::basic_string<Char> {
basic_memory_buffer<Char> buffer;
detail::vformat_to(buffer, format_str, args);
return to_string(buffer);
}
// Pass char_t as a default template parameter instead of using
// std::basic_string<char_t<S>> to reduce the symbol size.
template <typename S, typename... Args, typename Char = char_t<S>,
FMT_ENABLE_IF(!std::is_same<Char, char>::value)>
auto format(const S& format_str, Args&&... args) -> std::basic_string<Char> {
const auto& vargs = fmt::make_args_checked<Args...>(format_str, args...);
return vformat(to_string_view(format_str), vargs);
}
template <typename Locale, typename S, typename Char = char_t<S>,
FMT_ENABLE_IF(detail::is_locale<Locale>::value&&
detail::is_exotic_char<Char>::value)>
inline auto vformat(
const Locale& loc, const S& format_str,
basic_format_args<buffer_context<type_identity_t<Char>>> args)
-> std::basic_string<Char> {
return detail::vformat(loc, to_string_view(format_str), args);
}
template <typename Locale, typename S, typename... Args,
typename Char = char_t<S>,
FMT_ENABLE_IF(detail::is_locale<Locale>::value&&
detail::is_exotic_char<Char>::value)>
inline auto format(const Locale& loc, const S& format_str, Args&&... args)
-> std::basic_string<Char> {
return detail::vformat(loc, to_string_view(format_str),
fmt::make_args_checked<Args...>(format_str, args...));
}
template <typename OutputIt, typename S, typename Char = char_t<S>,
FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, Char>::value&&
detail::is_exotic_char<Char>::value)>
auto vformat_to(OutputIt out, const S& format_str,
basic_format_args<buffer_context<type_identity_t<Char>>> args)
-> OutputIt {
auto&& buf = detail::get_buffer<Char>(out);
detail::vformat_to(buf, to_string_view(format_str), args);
return detail::get_iterator(buf);
}
template <typename OutputIt, typename S, typename... Args,
typename Char = char_t<S>,
FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, Char>::value&&
detail::is_exotic_char<Char>::value)>
inline auto format_to(OutputIt out, const S& fmt, Args&&... args) -> OutputIt {
const auto& vargs = fmt::make_args_checked<Args...>(fmt, args...);
return vformat_to(out, to_string_view(fmt), vargs);
}
template <typename S, typename... Args, typename Char, size_t SIZE,
typename Allocator, FMT_ENABLE_IF(detail::is_string<S>::value)>
FMT_DEPRECATED auto format_to(basic_memory_buffer<Char, SIZE, Allocator>& buf,
const S& format_str, Args&&... args) ->
typename buffer_context<Char>::iterator {
const auto& vargs = fmt::make_args_checked<Args...>(format_str, args...);
detail::vformat_to(buf, to_string_view(format_str), vargs, {});
return detail::buffer_appender<Char>(buf);
}
template <typename Locale, typename S, typename OutputIt, typename... Args,
typename Char = char_t<S>,
FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, Char>::value&&
detail::is_locale<Locale>::value&&
detail::is_exotic_char<Char>::value)>
inline auto vformat_to(
OutputIt out, const Locale& loc, const S& format_str,
basic_format_args<buffer_context<type_identity_t<Char>>> args) -> OutputIt {
auto&& buf = detail::get_buffer<Char>(out);
vformat_to(buf, to_string_view(format_str), args, detail::locale_ref(loc));
return detail::get_iterator(buf);
}
template <
typename OutputIt, typename Locale, typename S, typename... Args,
typename Char = char_t<S>,
bool enable = detail::is_output_iterator<OutputIt, Char>::value&&
detail::is_locale<Locale>::value&& detail::is_exotic_char<Char>::value>
inline auto format_to(OutputIt out, const Locale& loc, const S& format_str,
Args&&... args) ->
typename std::enable_if<enable, OutputIt>::type {
const auto& vargs = fmt::make_args_checked<Args...>(format_str, args...);
return vformat_to(out, loc, to_string_view(format_str), vargs);
}
template <typename OutputIt, typename Char, typename... Args,
FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, Char>::value&&
detail::is_exotic_char<Char>::value)>
inline auto vformat_to_n(
OutputIt out, size_t n, basic_string_view<Char> format_str,
basic_format_args<buffer_context<type_identity_t<Char>>> args)
-> format_to_n_result<OutputIt> {
detail::iterator_buffer<OutputIt, Char, detail::fixed_buffer_traits> buf(out,
n);
detail::vformat_to(buf, format_str, args);
return {buf.out(), buf.count()};
}
template <typename OutputIt, typename S, typename... Args,
typename Char = char_t<S>,
FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, Char>::value&&
detail::is_exotic_char<Char>::value)>
inline auto format_to_n(OutputIt out, size_t n, const S& fmt,
const Args&... args) -> format_to_n_result<OutputIt> {
const auto& vargs = fmt::make_args_checked<Args...>(fmt, args...);
return vformat_to_n(out, n, to_string_view(fmt), vargs);
}
template <typename S, typename... Args, typename Char = char_t<S>,
FMT_ENABLE_IF(detail::is_exotic_char<Char>::value)>
inline auto formatted_size(const S& fmt, Args&&... args) -> size_t {
detail::counting_buffer<Char> buf;
const auto& vargs = fmt::make_args_checked<Args...>(fmt, args...);
detail::vformat_to(buf, to_string_view(fmt), vargs);
return buf.count();
}
inline void vprint(std::FILE* f, wstring_view fmt, wformat_args args) {
wmemory_buffer buffer;
detail::vformat_to(buffer, fmt, args);
buffer.push_back(L'\0');
if (std::fputws(buffer.data(), f) == -1)
FMT_THROW(system_error(errno, FMT_STRING("cannot write to file")));
}
inline void vprint(wstring_view fmt, wformat_args args) {
vprint(stdout, fmt, args);
}
template <typename... T>
void print(std::FILE* f, wformat_string<T...> fmt, T&&... args) {
return vprint(f, wstring_view(fmt), make_wformat_args(args...));
}
template <typename... T> void print(wformat_string<T...> fmt, T&&... args) {
return vprint(wstring_view(fmt), make_wformat_args(args...));
}
/**
Converts *value* to ``std::wstring`` using the default format for type *T*.
*/
template <typename T> inline auto to_wstring(const T& value) -> std::wstring {
return format(FMT_STRING(L"{}"), value);
}
FMT_MODULE_EXPORT_END
FMT_END_NAMESPACE
#endif // FMT_WCHAR_H_

View File

@@ -9,12 +9,12 @@
// //
#if !defined(SPDLOG_FMT_EXTERNAL) #if !defined(SPDLOG_FMT_EXTERNAL)
#ifdef SPDLOG_HEADER_ONLY # ifdef SPDLOG_HEADER_ONLY
#ifndef FMT_HEADER_ONLY # ifndef FMT_HEADER_ONLY
#define FMT_HEADER_ONLY # define FMT_HEADER_ONLY
#endif # endif
#endif # endif
#include <spdlog/fmt/bundled/chrono.h> # include <spdlog/fmt/bundled/chrono.h>
#else #else
#include <fmt/chrono.h> # include <fmt/chrono.h>
#endif #endif

View File

@@ -0,0 +1,20 @@
//
// Copyright(c) 2016 Gabi Melman.
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
//
#pragma once
//
// include bundled or external copy of fmtlib's ostream support
//
#if !defined(SPDLOG_FMT_EXTERNAL)
# ifdef SPDLOG_HEADER_ONLY
# ifndef FMT_HEADER_ONLY
# define FMT_HEADER_ONLY
# endif
# endif
# include <spdlog/fmt/bundled/compile.h>
#else
# include <fmt/compile.h>
#endif

View File

@@ -11,17 +11,17 @@
// //
#if !defined(SPDLOG_FMT_EXTERNAL) #if !defined(SPDLOG_FMT_EXTERNAL)
#if !defined(SPDLOG_COMPILED_LIB) && !defined(FMT_HEADER_ONLY) # if !defined(SPDLOG_COMPILED_LIB) && !defined(FMT_HEADER_ONLY)
#define FMT_HEADER_ONLY # define FMT_HEADER_ONLY
#endif # endif
#ifndef FMT_USE_WINDOWS_H # ifndef FMT_USE_WINDOWS_H
#define FMT_USE_WINDOWS_H 0 # define FMT_USE_WINDOWS_H 0
#endif # endif
// enable the 'n' flag in for backward compatibility with fmt 6.x // enable the 'n' flag in for backward compatibility with fmt 6.x
#define FMT_DEPRECATED_N_SPECIFIER # define FMT_DEPRECATED_N_SPECIFIER
#include <spdlog/fmt/bundled/core.h> # include <spdlog/fmt/bundled/core.h>
#include <spdlog/fmt/bundled/format.h> # include <spdlog/fmt/bundled/format.h>
#else // SPDLOG_FMT_EXTERNAL is defined - use external fmtlib #else // SPDLOG_FMT_EXTERNAL is defined - use external fmtlib
#include <fmt/core.h> # include <fmt/core.h>
#include <fmt/format.h> # include <fmt/format.h>
#endif #endif

View File

@@ -9,12 +9,12 @@
// //
#if !defined(SPDLOG_FMT_EXTERNAL) #if !defined(SPDLOG_FMT_EXTERNAL)
#ifdef SPDLOG_HEADER_ONLY # ifdef SPDLOG_HEADER_ONLY
#ifndef FMT_HEADER_ONLY # ifndef FMT_HEADER_ONLY
#define FMT_HEADER_ONLY # define FMT_HEADER_ONLY
#endif # endif
#endif # endif
#include <spdlog/fmt/bundled/ostream.h> # include <spdlog/fmt/bundled/ostream.h>
#else #else
#include <fmt/ostream.h> # include <fmt/ostream.h>
#endif #endif

View File

@@ -0,0 +1,20 @@
//
// Copyright(c) 2016 Gabi Melman.
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
//
#pragma once
//
// include bundled or external copy of fmtlib's ostream support
//
#if !defined(SPDLOG_FMT_EXTERNAL)
# ifdef SPDLOG_HEADER_ONLY
# ifndef FMT_HEADER_ONLY
# define FMT_HEADER_ONLY
# endif
# endif
# include <spdlog/fmt/bundled/xchar.h>
#else
# include <fmt/xchar.h>
#endif

View File

@@ -4,7 +4,7 @@
#pragma once #pragma once
#ifndef SPDLOG_HEADER_ONLY #ifndef SPDLOG_HEADER_ONLY
#include <spdlog/logger.h> # include <spdlog/logger.h>
#endif #endif
#include <spdlog/sinks/sink.h> #include <spdlog/sinks/sink.h>

View File

@@ -19,22 +19,27 @@
#include <spdlog/details/backtracer.h> #include <spdlog/details/backtracer.h>
#ifdef SPDLOG_WCHAR_TO_UTF8_SUPPORT #ifdef SPDLOG_WCHAR_TO_UTF8_SUPPORT
#include <spdlog/details/os.h> # ifndef _WIN32
# error SPDLOG_WCHAR_TO_UTF8_SUPPORT only supported on windows
# endif
# include <spdlog/details/os.h>
#endif #endif
#include <vector> #include <vector>
#ifndef SPDLOG_NO_EXCEPTIONS #ifndef SPDLOG_NO_EXCEPTIONS
#define SPDLOG_LOGGER_CATCH() \ # define SPDLOG_LOGGER_CATCH() \
catch (const std::exception &ex) \ catch (const std::exception &ex) \
{ \ { \
err_handler_(ex.what()); \ err_handler_(ex.what()); \
} \ } \
catch (...) \ catch (...) \
{ \ { \
err_handler_("Unknown exception in logger"); \ err_handler_("Rethrowing unknown exception in logger"); \
} throw; \
}
#else #else
#define SPDLOG_LOGGER_CATCH() # define SPDLOG_LOGGER_CATCH()
#endif #endif
namespace spdlog { namespace spdlog {
@@ -70,79 +75,42 @@ public:
logger(const logger &other); logger(const logger &other);
logger(logger &&other) SPDLOG_NOEXCEPT; logger(logger &&other) SPDLOG_NOEXCEPT;
logger &operator=(logger other) SPDLOG_NOEXCEPT; logger &operator=(logger other) SPDLOG_NOEXCEPT;
void swap(spdlog::logger &other) SPDLOG_NOEXCEPT; void swap(spdlog::logger &other) SPDLOG_NOEXCEPT;
// FormatString is a type derived from fmt::compile_string
template<typename FormatString, typename std::enable_if<fmt::is_compile_string<FormatString>::value, int>::type = 0, typename... Args>
void log(source_loc loc, level::level_enum lvl, const FormatString &fmt, Args&&...args)
{
log_(loc, lvl, fmt, std::forward<Args>(args)...);
}
// FormatString is NOT a type derived from fmt::compile_string but is a string_view_t or can be implicitly converted to one
template<typename... Args> template<typename... Args>
void log(source_loc loc, level::level_enum lvl, string_view_t fmt, Args&&...args) void log(source_loc loc, level::level_enum lvl, fmt::format_string<Args...> fmt, Args &&...args)
{ {
log_(loc, lvl, fmt, std::forward<Args>(args)...); log_(loc, lvl, fmt, std::forward<Args>(args)...);
} }
template<typename FormatString, typename... Args> template<typename... Args>
void log(level::level_enum lvl, const FormatString &fmt, Args&&...args) void log(level::level_enum lvl, fmt::format_string<Args...> fmt, Args &&...args)
{ {
log(source_loc{}, lvl, fmt, std::forward<Args>(args)...); log(source_loc{}, lvl, fmt, std::forward<Args>(args)...);
} }
template<typename FormatString, typename... Args>
void trace(const FormatString &fmt, Args&&...args)
{
log(level::trace, fmt, std::forward<Args>(args)...);
}
template<typename FormatString, typename... Args>
void debug(const FormatString &fmt, Args&&...args)
{
log(level::debug, fmt, std::forward<Args>(args)...);
}
template<typename FormatString, typename... Args>
void info(const FormatString &fmt, Args&&...args)
{
log(level::info, fmt, std::forward<Args>(args)...);
}
template<typename FormatString, typename... Args>
void warn(const FormatString &fmt, Args&&...args)
{
log(level::warn, fmt, std::forward<Args>(args)...);
}
template<typename FormatString, typename... Args>
void error(const FormatString &fmt, Args&&...args)
{
log(level::err, fmt, std::forward<Args>(args)...);
}
template<typename FormatString, typename... Args>
void critical(const FormatString &fmt, Args&&...args)
{
log(level::critical, fmt, std::forward<Args>(args)...);
}
template<typename T> template<typename T>
void log(level::level_enum lvl, const T &msg) void log(level::level_enum lvl, const T &msg)
{ {
log(source_loc{}, lvl, msg); log(source_loc{}, lvl, msg);
} }
// T can be statically converted to string_view and isn't a fmt::compile_string // T can be statically converted to string_view
template<class T, typename std::enable_if< template<class T, typename std::enable_if<std::is_convertible<const T &, spdlog::string_view_t>::value, int>::type = 0>
std::is_convertible<const T &, spdlog::string_view_t>::value && !fmt::is_compile_string<T>::value, int>::type = 0>
void log(source_loc loc, level::level_enum lvl, const T &msg) void log(source_loc loc, level::level_enum lvl, const T &msg)
{ {
log(loc, lvl, string_view_t{msg}); log(loc, lvl, string_view_t{msg});
} }
// T cannot be statically converted to neither string_view, nor wstring_view and nor format string
template<class T, typename std::enable_if<!std::is_convertible<const T &, spdlog::string_view_t>::value &&
!is_convertible_to_basic_format_string<const T &>::value,
int>::type = 0>
void log(source_loc loc, level::level_enum lvl, const T &msg)
{
log(loc, lvl, "{}", msg);
}
void log(log_clock::time_point log_time, source_loc loc, level::level_enum lvl, string_view_t msg) void log(log_clock::time_point log_time, source_loc loc, level::level_enum lvl, string_view_t msg)
{ {
bool log_enabled = should_log(lvl); bool log_enabled = should_log(lvl);
@@ -174,15 +142,92 @@ public:
log(source_loc{}, lvl, msg); log(source_loc{}, lvl, msg);
} }
// T cannot be statically converted to string_view or wstring_view template<typename... Args>
template<class T, typename std::enable_if<!std::is_convertible<const T &, spdlog::string_view_t>::value && void trace(fmt::format_string<Args...> fmt, Args &&...args)
!is_convertible_to_wstring_view<const T &>::value,
int>::type = 0>
void log(source_loc loc, level::level_enum lvl, const T &msg)
{ {
log(loc, lvl, "{}", msg); log(level::trace, fmt, std::forward<Args>(args)...);
} }
template<typename... Args>
void debug(fmt::format_string<Args...> fmt, Args &&...args)
{
log(level::debug, fmt, std::forward<Args>(args)...);
}
template<typename... Args>
void info(fmt::format_string<Args...> fmt, Args &&...args)
{
log(level::info, fmt, std::forward<Args>(args)...);
}
template<typename... Args>
void warn(fmt::format_string<Args...> fmt, Args &&...args)
{
log(level::warn, fmt, std::forward<Args>(args)...);
}
template<typename... Args>
void error(fmt::format_string<Args...> fmt, Args &&...args)
{
log(level::err, fmt, std::forward<Args>(args)...);
}
template<typename... Args>
void critical(fmt::format_string<Args...> fmt, Args &&...args)
{
log(level::critical, fmt, std::forward<Args>(args)...);
}
#ifdef SPDLOG_WCHAR_TO_UTF8_SUPPORT
template<typename... Args>
void log(level::level_enum lvl, fmt::wformat_string<Args...> fmt, Args &&...args)
{
log(source_loc{}, lvl, fmt, std::forward<Args>(args)...);
}
template<typename... Args>
void log(source_loc loc, level::level_enum lvl, fmt::wformat_string<Args...> fmt, Args &&...args)
{
log_(loc, lvl, fmt, std::forward<Args>(args)...);
}
template<typename... Args>
void trace(fmt::wformat_string<Args...> fmt, Args &&...args)
{
log(level::trace, fmt, std::forward<Args>(args)...);
}
template<typename... Args>
void debug(fmt::wformat_string<Args...> fmt, Args &&...args)
{
log(level::debug, fmt, std::forward<Args>(args)...);
}
template<typename... Args>
void info(fmt::wformat_string<Args...> fmt, Args &&...args)
{
log(level::info, fmt, std::forward<Args>(args)...);
}
template<typename... Args>
void warn(fmt::wformat_string<Args...> fmt, Args &&...args)
{
log(level::warn, fmt, std::forward<Args>(args)...);
}
template<typename... Args>
void error(fmt::wformat_string<Args...> fmt, Args &&...args)
{
log(level::err, fmt, std::forward<Args>(args)...);
}
template<typename... Args>
void critical(fmt::wformat_string<Args...> fmt, Args &&...args)
{
log(level::critical, fmt, std::forward<Args>(args)...);
}
#endif
template<typename T> template<typename T>
void trace(const T &msg) void trace(const T &msg)
{ {
@@ -219,57 +264,6 @@ public:
log(level::critical, msg); log(level::critical, msg);
} }
#ifdef SPDLOG_WCHAR_TO_UTF8_SUPPORT
#ifndef _WIN32
#error SPDLOG_WCHAR_TO_UTF8_SUPPORT only supported on windows
#else
template<typename... Args>
void log(source_loc loc, level::level_enum lvl, wstring_view_t fmt, Args&&...args)
{
bool log_enabled = should_log(lvl);
bool traceback_enabled = tracer_.enabled();
if (!log_enabled && !traceback_enabled)
{
return;
}
SPDLOG_TRY
{
// format to wmemory_buffer and convert to utf8
fmt::wmemory_buffer wbuf;
fmt::format_to(wbuf, fmt, std::forward<Args>(args)...);
memory_buf_t buf;
details::os::wstr_to_utf8buf(wstring_view_t(wbuf.data(), wbuf.size()), buf);
details::log_msg log_msg(loc, name_, lvl, string_view_t(buf.data(), buf.size()));
log_it_(log_msg, log_enabled, traceback_enabled);
}
SPDLOG_LOGGER_CATCH()
}
// T can be statically converted to wstring_view
template<class T, typename std::enable_if<is_convertible_to_wstring_view<const T &>::value, int>::type = 0>
void log(source_loc loc, level::level_enum lvl, const T &msg)
{
bool log_enabled = should_log(lvl);
bool traceback_enabled = tracer_.enabled();
if (!log_enabled && !traceback_enabled)
{
return;
}
SPDLOG_TRY
{
memory_buf_t buf;
details::os::wstr_to_utf8buf(msg, buf);
details::log_msg log_msg(loc, name_, lvl, string_view_t(buf.data(), buf.size()));
log_it_(log_msg, log_enabled, traceback_enabled);
}
SPDLOG_LOGGER_CATCH()
}
#endif // _WIN32
#endif // SPDLOG_WCHAR_TO_UTF8_SUPPORT
// return true logging is enabled for the given level. // return true logging is enabled for the given level.
bool should_log(level::level_enum msg_level) const bool should_log(level::level_enum msg_level) const
{ {
@@ -325,8 +319,8 @@ protected:
details::backtracer tracer_; details::backtracer tracer_;
// common implementation for after templated public api has been resolved // common implementation for after templated public api has been resolved
template<typename FormatString, typename... Args> template<typename... Args>
void log_(source_loc loc, level::level_enum lvl, const FormatString &fmt, Args&&...args) void log_(source_loc loc, level::level_enum lvl, string_view_t fmt, Args &&...args)
{ {
bool log_enabled = should_log(lvl); bool log_enabled = should_log(lvl);
bool traceback_enabled = tracer_.enabled(); bool traceback_enabled = tracer_.enabled();
@@ -337,13 +331,58 @@ protected:
SPDLOG_TRY SPDLOG_TRY
{ {
memory_buf_t buf; memory_buf_t buf;
fmt::format_to(buf, fmt, std::forward<Args>(args)...); fmt::detail::vformat_to(buf, fmt, fmt::make_format_args(args...));
details::log_msg log_msg(loc, name_, lvl, string_view_t(buf.data(), buf.size())); details::log_msg log_msg(loc, name_, lvl, string_view_t(buf.data(), buf.size()));
log_it_(log_msg, log_enabled, traceback_enabled); log_it_(log_msg, log_enabled, traceback_enabled);
} }
SPDLOG_LOGGER_CATCH() SPDLOG_LOGGER_CATCH()
} }
#ifdef SPDLOG_WCHAR_TO_UTF8_SUPPORT
template<typename... Args>
void log_(source_loc loc, level::level_enum lvl, wstring_view_t fmt, Args &&...args)
{
bool log_enabled = should_log(lvl);
bool traceback_enabled = tracer_.enabled();
if (!log_enabled && !traceback_enabled)
{
return;
}
SPDLOG_TRY
{
// format to wmemory_buffer and convert to utf8
fmt::wmemory_buffer wbuf;
fmt::detail::vformat_to(wbuf, fmt, fmt::make_format_args<fmt::wformat_context>(args...));
memory_buf_t buf;
details::os::wstr_to_utf8buf(wstring_view_t(wbuf.data(), wbuf.size()), buf);
details::log_msg log_msg(loc, name_, lvl, string_view_t(buf.data(), buf.size()));
log_it_(log_msg, log_enabled, traceback_enabled);
}
SPDLOG_LOGGER_CATCH()
}
// T can be statically converted to wstring_view, and no formatting needed.
template<class T, typename std::enable_if<std::is_convertible<const T &, spdlog::wstring_view_t>::value, int>::type = 0>
void log_(source_loc loc, level::level_enum lvl, const T &msg)
{
bool log_enabled = should_log(lvl);
bool traceback_enabled = tracer_.enabled();
if (!log_enabled && !traceback_enabled)
{
return;
}
SPDLOG_TRY
{
memory_buf_t buf;
details::os::wstr_to_utf8buf(msg, buf);
details::log_msg log_msg(loc, name_, lvl, string_view_t(buf.data(), buf.size()));
log_it_(log_msg, log_enabled, traceback_enabled);
}
SPDLOG_LOGGER_CATCH()
}
#endif // SPDLOG_WCHAR_TO_UTF8_SUPPORT
// log the given message (if the given log level is high enough), // log the given message (if the given log level is high enough),
// and save backtrace (if backtrace is enabled). // and save backtrace (if backtrace is enabled).
void log_it_(const details::log_msg &log_msg, bool log_enabled, bool traceback_enabled); void log_it_(const details::log_msg &log_msg, bool log_enabled, bool traceback_enabled);
@@ -362,5 +401,5 @@ void swap(logger &a, logger &b);
} // namespace spdlog } // namespace spdlog
#ifdef SPDLOG_HEADER_ONLY #ifdef SPDLOG_HEADER_ONLY
#include "logger-inl.h" # include "logger-inl.h"
#endif #endif

View File

@@ -4,7 +4,7 @@
#pragma once #pragma once
#ifndef SPDLOG_HEADER_ONLY #ifndef SPDLOG_HEADER_ONLY
#include <spdlog/pattern_formatter.h> # include <spdlog/pattern_formatter.h>
#endif #endif
#include <spdlog/details/fmt_helper.h> #include <spdlog/details/fmt_helper.h>
@@ -817,9 +817,9 @@ public:
{} {}
#ifdef _MSC_VER #ifdef _MSC_VER
#pragma warning(push) # pragma warning(push)
#pragma warning(disable : 4127) // consider using 'if constexpr' instead # pragma warning(disable : 4127) // consider using 'if constexpr' instead
#endif // _MSC_VER #endif // _MSC_VER
static const char *basename(const char *filename) static const char *basename(const char *filename)
{ {
// if the size is 2 (1 character + null terminator) we can use the more efficient strrchr // if the size is 2 (1 character + null terminator) we can use the more efficient strrchr
@@ -831,15 +831,15 @@ public:
} }
else else
{ {
const std::reverse_iterator<const char*> begin(filename + std::strlen(filename)); const std::reverse_iterator<const char *> begin(filename + std::strlen(filename));
const std::reverse_iterator<const char*> end(filename); const std::reverse_iterator<const char *> end(filename);
const auto it = std::find_first_of(begin, end, std::begin(os::folder_seps), std::end(os::folder_seps) - 1); const auto it = std::find_first_of(begin, end, std::begin(os::folder_seps), std::end(os::folder_seps) - 1);
return it != end ? it.base() : filename; return it != end ? it.base() : filename;
} }
} }
#ifdef _MSC_VER #ifdef _MSC_VER
#pragma warning(pop) # pragma warning(pop)
#endif // _MSC_VER #endif // _MSC_VER
void format(const details::log_msg &msg, const std::tm &, memory_buf_t &dest) override void format(const details::log_msg &msg, const std::tm &, memory_buf_t &dest) override
@@ -925,7 +925,7 @@ private:
}; };
// Full info formatter // Full info formatter
// pattern: [%Y-%m-%d %H:%M:%S.%e] [%n] [%l] %v // pattern: [%Y-%m-%d %H:%M:%S.%e] [%n] [%l] [%s:%#] %v
class full_formatter final : public flag_formatter class full_formatter final : public flag_formatter
{ {
public: public:

View File

@@ -92,7 +92,7 @@ public:
void format(const details::log_msg &msg, memory_buf_t &dest) override; void format(const details::log_msg &msg, memory_buf_t &dest) override;
template<typename T, typename... Args> template<typename T, typename... Args>
pattern_formatter &add_flag(char flag, Args&&...args) pattern_formatter &add_flag(char flag, Args &&...args)
{ {
custom_handlers_[flag] = details::make_unique<T>(std::forward<Args>(args)...); custom_handlers_[flag] = details::make_unique<T>(std::forward<Args>(args)...);
return *this; return *this;
@@ -122,5 +122,5 @@ private:
} // namespace spdlog } // namespace spdlog
#ifdef SPDLOG_HEADER_ONLY #ifdef SPDLOG_HEADER_ONLY
#include "pattern_formatter-inl.h" # include "pattern_formatter-inl.h"
#endif #endif

View File

@@ -5,21 +5,21 @@
#ifdef __ANDROID__ #ifdef __ANDROID__
#include <spdlog/details/fmt_helper.h> # include <spdlog/details/fmt_helper.h>
#include <spdlog/details/null_mutex.h> # include <spdlog/details/null_mutex.h>
#include <spdlog/details/os.h> # include <spdlog/details/os.h>
#include <spdlog/sinks/base_sink.h> # include <spdlog/sinks/base_sink.h>
#include <spdlog/details/synchronous_factory.h> # include <spdlog/details/synchronous_factory.h>
#include <android/log.h> # include <android/log.h>
#include <chrono> # include <chrono>
#include <mutex> # include <mutex>
#include <string> # include <string>
#include <thread> # include <thread>
#if !defined(SPDLOG_ANDROID_RETRIES) # if !defined(SPDLOG_ANDROID_RETRIES)
#define SPDLOG_ANDROID_RETRIES 2 # define SPDLOG_ANDROID_RETRIES 2
#endif # endif
namespace spdlog { namespace spdlog {
namespace sinks { namespace sinks {

View File

@@ -4,7 +4,7 @@
#pragma once #pragma once
#ifndef SPDLOG_HEADER_ONLY #ifndef SPDLOG_HEADER_ONLY
#include <spdlog/sinks/ansicolor_sink.h> # include <spdlog/sinks/ansicolor_sink.h>
#endif #endif
#include <spdlog/pattern_formatter.h> #include <spdlog/pattern_formatter.h>

View File

@@ -114,5 +114,5 @@ using ansicolor_stderr_sink_st = ansicolor_stderr_sink<details::console_nullmute
} // namespace spdlog } // namespace spdlog
#ifdef SPDLOG_HEADER_ONLY #ifdef SPDLOG_HEADER_ONLY
#include "ansicolor_sink-inl.h" # include "ansicolor_sink-inl.h"
#endif #endif

View File

@@ -4,7 +4,7 @@
#pragma once #pragma once
#ifndef SPDLOG_HEADER_ONLY #ifndef SPDLOG_HEADER_ONLY
#include <spdlog/sinks/base_sink.h> # include <spdlog/sinks/base_sink.h>
#endif #endif
#include <spdlog/common.h> #include <spdlog/common.h>

View File

@@ -37,7 +37,7 @@ public:
protected: protected:
// sink formatter // sink formatter
std::unique_ptr<spdlog::formatter> formatter_; std::unique_ptr<spdlog::formatter> formatter_;
Mutex mutex_; mutable Mutex mutex_;
virtual void sink_it_(const details::log_msg &msg) = 0; virtual void sink_it_(const details::log_msg &msg) = 0;
virtual void flush_() = 0; virtual void flush_() = 0;
@@ -48,5 +48,5 @@ protected:
} // namespace spdlog } // namespace spdlog
#ifdef SPDLOG_HEADER_ONLY #ifdef SPDLOG_HEADER_ONLY
#include "base_sink-inl.h" # include "base_sink-inl.h"
#endif #endif

View File

@@ -4,7 +4,7 @@
#pragma once #pragma once
#ifndef SPDLOG_HEADER_ONLY #ifndef SPDLOG_HEADER_ONLY
#include <spdlog/sinks/basic_file_sink.h> # include <spdlog/sinks/basic_file_sink.h>
#endif #endif
#include <spdlog/common.h> #include <spdlog/common.h>

View File

@@ -54,5 +54,5 @@ inline std::shared_ptr<logger> basic_logger_st(const std::string &logger_name, c
} // namespace spdlog } // namespace spdlog
#ifdef SPDLOG_HEADER_ONLY #ifdef SPDLOG_HEADER_ONLY
#include "basic_file_sink-inl.h" # include "basic_file_sink-inl.h"
#endif #endif

View File

@@ -39,18 +39,22 @@ struct daily_filename_calculator
/* /*
* Generator of daily log file names with strftime format. * Generator of daily log file names with strftime format.
* Usages: * Usages:
* auto sink = std::make_shared<spdlog::sinks::daily_file_format_sink_mt>("myapp-%Y-%m-%d:%H:%M:%S.log", hour, minute);" * auto sink = std::make_shared<spdlog::sinks::daily_file_format_sink_mt>("myapp-%Y-%m-%d:%H:%M:%S.log", hour, minute);"
* auto logger = spdlog::daily_logger_format_mt("loggername, "myapp-%Y-%m-%d:%X.log", hour, minute)" * auto logger = spdlog::daily_logger_format_mt("loggername, "myapp-%Y-%m-%d:%X.log", hour, minute)"
* *
*/ */
struct daily_filename_format_calculator struct daily_filename_format_calculator
{ {
static filename_t calc_filename (const filename_t &filename, const tm &now_tm) static filename_t calc_filename(const filename_t &filename, const tm &now_tm)
{ {
// generate fmt datetime format string, e.g. {:%Y-%m-%d}. // generate fmt datetime format string, e.g. {:%Y-%m-%d}.
filename_t fmt_filename = fmt::format(SPDLOG_FILENAME_T ("{{:{}}}"), filename); filename_t fmt_filename = fmt::format(SPDLOG_FILENAME_T("{{:{}}}"), filename);
#if defined(_MSC_VER) && defined(SPDLOG_WCHAR_FILENAMES) // for some reason msvc doesnt allow fmt::runtime(..) with wchar here
return fmt::format(fmt_filename, now_tm); return fmt::format(fmt_filename, now_tm);
#else
return fmt::format(SPDLOG_FMT_RUNTIME(fmt_filename), now_tm);
#endif
} }
}; };

View File

@@ -7,6 +7,7 @@
#include <spdlog/details/null_mutex.h> #include <spdlog/details/null_mutex.h>
#include <spdlog/details/log_msg.h> #include <spdlog/details/log_msg.h>
#include <cstdio>
#include <mutex> #include <mutex>
#include <string> #include <string>
#include <chrono> #include <chrono>
@@ -62,10 +63,13 @@ protected:
// log the "skipped.." message // log the "skipped.." message
if (skip_counter_ > 0) if (skip_counter_ > 0)
{ {
memory_buf_t buf; char buf[64];
fmt::format_to(buf, "Skipped {} duplicate messages..", skip_counter_); auto msg_size = ::snprintf(buf, sizeof(buf), "Skipped %u duplicate messages..", static_cast<unsigned>(skip_counter_));
details::log_msg skipped_msg{msg.logger_name, level::info, string_view_t{buf.data(), buf.size()}}; if (msg_size > 0 && static_cast<size_t>(msg_size) < sizeof(buf))
dist_sink<Mutex>::sink_it_(skipped_msg); {
details::log_msg skipped_msg{msg.logger_name, level::info, string_view_t{buf, static_cast<size_t>(msg_size)}};
dist_sink<Mutex>::sink_it_(skipped_msg);
}
} }
// log current message // log current message

View File

@@ -31,8 +31,8 @@ struct hourly_filename_calculator
{ {
filename_t basename, ext; filename_t basename, ext;
std::tie(basename, ext) = details::file_helper::split_by_extension(filename); std::tie(basename, ext) = details::file_helper::split_by_extension(filename);
return fmt::format( return fmt::format(SPDLOG_FILENAME_T("{}_{:04d}{:02d}{:02d}_{:02d}{}"), basename, now_tm.tm_year + 1900, now_tm.tm_mon + 1,
SPDLOG_FILENAME_T("{}_{:04d}{:02d}{:02d}_{:02d}{}"), basename, now_tm.tm_year + 1900, now_tm.tm_mon + 1, now_tm.tm_mday, now_tm.tm_hour, ext); now_tm.tm_mday, now_tm.tm_hour, ext);
} }
}; };

View File

@@ -0,0 +1,98 @@
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
#pragma once
//
// Custom sink for mongodb
// Building and using requires mongocxx library.
// For building mongocxx library check the url below
// http://mongocxx.org/mongocxx-v3/installation/
//
#include "spdlog/common.h"
#include "spdlog/details/log_msg.h"
#include "spdlog/sinks/base_sink.h"
#include <spdlog/details/synchronous_factory.h>
#include <bsoncxx/builder/stream/document.hpp>
#include <bsoncxx/types.hpp>
#include <bsoncxx/view_or_value.hpp>
#include <mongocxx/client.hpp>
#include <mongocxx/instance.hpp>
#include <mongocxx/uri.hpp>
namespace spdlog {
namespace sinks {
template<typename Mutex>
class mongo_sink : public base_sink<Mutex>
{
public:
mongo_sink(const std::string &db_name, const std::string &collection_name, const std::string &uri = "mongodb://localhost:27017")
{
try
{
client_ = std::make_unique<mongocxx::client>(mongocxx::uri{uri});
db_name_ = db_name;
coll_name_ = collection_name;
}
catch (const std::exception)
{
throw spdlog_ex("Error opening database");
}
}
~mongo_sink()
{
flush_();
}
protected:
void sink_it_(const details::log_msg &msg) override
{
using bsoncxx::builder::stream::document;
using bsoncxx::builder::stream::finalize;
if (client_ != nullptr)
{
auto doc = document{} << "timestamp" << bsoncxx::types::b_date(msg.time) << "level" << level::to_string_view(msg.level).data()
<< "message" << std::string(msg.payload.begin(), msg.payload.end()) << "logger_name"
<< std::string(msg.logger_name.begin(), msg.logger_name.end()) << "thread_id"
<< static_cast<int>(msg.thread_id) << finalize;
client_->database(db_name_).collection(coll_name_).insert_one(doc.view());
}
}
void flush_() override {}
private:
static mongocxx::instance instance_;
std::string db_name_;
std::string coll_name_;
std::unique_ptr<mongocxx::client> client_ = nullptr;
};
mongocxx::instance mongo_sink<std::mutex>::instance_{};
#include "spdlog/details/null_mutex.h"
#include <mutex>
using mongo_sink_mt = mongo_sink<std::mutex>;
using mongo_sink_st = mongo_sink<spdlog::details::null_mutex>;
} // namespace sinks
template<typename Factory = spdlog::synchronous_factory>
inline std::shared_ptr<logger> mongo_logger_mt(const std::string &logger_name, const std::string &db_name,
const std::string &collection_name, const std::string &uri = "mongodb://localhost:27017")
{
return Factory::template create<sinks::mongo_sink_mt>(logger_name, db_name, collection_name, uri);
}
template<typename Factory = spdlog::synchronous_factory>
inline std::shared_ptr<logger> mongo_logger_st(const std::string &logger_name, const std::string &db_name,
const std::string &collection_name, const std::string &uri = "mongodb://localhost:27017")
{
return Factory::template create<sinks::mongo_sink_st>(logger_name, db_name, collection_name, uri);
}
} // namespace spdlog

View File

@@ -5,12 +5,11 @@
#if defined(_WIN32) #if defined(_WIN32)
#include <spdlog/details/null_mutex.h> # include <spdlog/details/null_mutex.h>
#include <spdlog/sinks/base_sink.h> # include <spdlog/sinks/base_sink.h>
#include <mutex>
#include <string>
# include <mutex>
# include <string>
// Avoid including windows.h (https://stackoverflow.com/a/30741042) // Avoid including windows.h (https://stackoverflow.com/a/30741042)
extern "C" __declspec(dllimport) void __stdcall OutputDebugStringA(const char *lpOutputString); extern "C" __declspec(dllimport) void __stdcall OutputDebugStringA(const char *lpOutputString);

View File

@@ -0,0 +1,200 @@
// Copyright(c) 2015-present, Gabi Melman, mguludag and spdlog contributors.
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
#pragma once
//
// Custom sink for QPlainTextEdit or QTextEdit and its children(QTextBrowser...
// etc) Building and using requires Qt library.
//
#include "spdlog/common.h"
#include "spdlog/details/log_msg.h"
#include "spdlog/details/synchronous_factory.h"
#include "spdlog/sinks/base_sink.h"
#include <QObject>
#include <QPlainTextEdit>
#include <QTextEdit>
namespace _spdlog_p {
namespace _sinks_p {
//
// Private class for QTextEdit and its derivatives
//
class qtextedit_sink_p : public QObject
{
Q_OBJECT
public:
qtextedit_sink_p(QTextEdit *textedit = nullptr)
{
if (textedit != nullptr)
{
textedit_ = textedit;
connect(this, &qtextedit_sink_p::append_text, textedit_, &QTextEdit::append);
}
}
~qtextedit_sink_p() {}
void append(const spdlog::string_view_t &str)
{
emit append_text(QString::fromUtf8(str.data(), static_cast<int>(str.size() - 2)));
}
signals:
void append_text(const QString &);
private:
QTextEdit *textedit_ = nullptr;
};
//
// Private class for QPlainTextEdit
//
class qplaintextedit_sink_p : public QObject
{
Q_OBJECT
public:
qplaintextedit_sink_p(QPlainTextEdit *textedit = nullptr)
{
if (textedit != nullptr)
{
textedit_ = textedit;
connect(this, &qplaintextedit_sink_p::append_text, textedit_, &QPlainTextEdit::appendPlainText);
}
}
~qplaintextedit_sink_p() {}
void append(const spdlog::string_view_t &str)
{
emit append_text(QString::fromUtf8(str.data(), static_cast<int>(str.size() - 2)));
}
signals:
void append_text(const QString &);
private:
QPlainTextEdit *textedit_ = nullptr;
};
} // namespace _sinks_p
} // namespace _spdlog_p
//
// qtextedit_sink class
//
namespace spdlog {
namespace sinks {
template<typename Mutex>
class qtextedit_sink : public base_sink<Mutex>
{
public:
qtextedit_sink(QTextEdit *textedit = nullptr)
{
if (textedit != nullptr)
{
textedit_p = std::make_shared<_spdlog_p::_sinks_p::qtextedit_sink_p>(textedit);
}
else
{
throw spdlog_ex("Error opening QTextEdit");
}
}
~qtextedit_sink()
{
flush_();
}
protected:
void sink_it_(const details::log_msg &msg) override
{
memory_buf_t formatted;
base_sink<Mutex>::formatter_->format(msg, formatted);
string_view_t str_v = string_view_t(formatted.data(), formatted.size());
textedit_p->append(str_v);
}
void flush_() override {}
private:
std::shared_ptr<_spdlog_p::_sinks_p::qtextedit_sink_p> textedit_p = nullptr;
};
//
// qplaintextedit_sink class
//
template<typename Mutex>
class qplaintextedit_sink : public base_sink<Mutex>
{
public:
qplaintextedit_sink(QPlainTextEdit *textedit = nullptr)
{
if (textedit != nullptr)
{
textedit_p = std::make_shared<_spdlog_p::_sinks_p::qplaintextedit_sink_p>(textedit);
}
else
{
throw spdlog_ex("Error opening QPlainTextEdit");
}
}
~qplaintextedit_sink()
{
flush_();
}
protected:
void sink_it_(const details::log_msg &msg) override
{
memory_buf_t formatted;
base_sink<Mutex>::formatter_->format(msg, formatted);
string_view_t str_v = string_view_t(formatted.data(), formatted.size());
textedit_p->append(str_v);
}
void flush_() override {}
private:
std::shared_ptr<_spdlog_p::_sinks_p::qplaintextedit_sink_p> textedit_p = nullptr;
};
#include "spdlog/details/null_mutex.h"
#include <mutex>
using qtextedit_sink_mt = qtextedit_sink<std::mutex>;
using qtextedit_sink_st = qtextedit_sink<spdlog::details::null_mutex>;
using qplaintextedit_sink_mt = qplaintextedit_sink<std::mutex>;
using qplaintextedit_sink_st = qplaintextedit_sink<spdlog::details::null_mutex>;
} // namespace sinks
//
// Factory functions
//
template<typename Factory = spdlog::synchronous_factory>
inline std::shared_ptr<logger> qtextedit_logger_mt(const std::string &logger_name, QTextEdit *qtextedit = nullptr)
{
return Factory::template create<sinks::qtextedit_sink_mt>(logger_name, qtextedit);
}
template<typename Factory = spdlog::synchronous_factory>
inline std::shared_ptr<logger> qtextedit_logger_st(const std::string &logger_name, QTextEdit *qtextedit = nullptr)
{
return Factory::template create<sinks::qtextedit_sink_st>(logger_name, qtextedit);
}
template<typename Factory = spdlog::synchronous_factory>
inline std::shared_ptr<logger> qplaintextedit_logger_mt(const std::string &logger_name, QPlainTextEdit *qplaintextedit = nullptr)
{
return Factory::template create<sinks::qplaintextedit_sink_mt>(logger_name, qplaintextedit);
}
template<typename Factory = spdlog::synchronous_factory>
inline std::shared_ptr<logger> qplaintextedit_logger_st(const std::string &logger_name, QPlainTextEdit *qplaintextedit = nullptr)
{
return Factory::template create<sinks::qplaintextedit_sink_st>(logger_name, qplaintextedit);
}
} // namespace spdlog

View File

@@ -4,7 +4,7 @@
#pragma once #pragma once
#ifndef SPDLOG_HEADER_ONLY #ifndef SPDLOG_HEADER_ONLY
#include <spdlog/sinks/rotating_file_sink.h> # include <spdlog/sinks/rotating_file_sink.h>
#endif #endif
#include <spdlog/common.h> #include <spdlog/common.h>

View File

@@ -74,5 +74,5 @@ inline std::shared_ptr<logger> rotating_logger_st(
} // namespace spdlog } // namespace spdlog
#ifdef SPDLOG_HEADER_ONLY #ifdef SPDLOG_HEADER_ONLY
#include "rotating_file_sink-inl.h" # include "rotating_file_sink-inl.h"
#endif #endif

View File

@@ -4,7 +4,7 @@
#pragma once #pragma once
#ifndef SPDLOG_HEADER_ONLY #ifndef SPDLOG_HEADER_ONLY
#include <spdlog/sinks/sink.h> # include <spdlog/sinks/sink.h>
#endif #endif
#include <spdlog/common.h> #include <spdlog/common.h>

View File

@@ -31,5 +31,5 @@ protected:
} // namespace spdlog } // namespace spdlog
#ifdef SPDLOG_HEADER_ONLY #ifdef SPDLOG_HEADER_ONLY
#include "sink-inl.h" # include "sink-inl.h"
#endif #endif

View File

@@ -4,7 +4,7 @@
#pragma once #pragma once
#ifndef SPDLOG_HEADER_ONLY #ifndef SPDLOG_HEADER_ONLY
#include <spdlog/sinks/stdout_color_sinks.h> # include <spdlog/sinks/stdout_color_sinks.h>
#endif #endif
#include <spdlog/logger.h> #include <spdlog/logger.h>
@@ -35,4 +35,4 @@ SPDLOG_INLINE std::shared_ptr<logger> stderr_color_st(const std::string &logger_
{ {
return Factory::template create<sinks::stderr_color_sink_st>(logger_name, mode); return Factory::template create<sinks::stderr_color_sink_st>(logger_name, mode);
} }
} // namespace spdlog } // namespace spdlog

View File

@@ -4,9 +4,9 @@
#pragma once #pragma once
#ifdef _WIN32 #ifdef _WIN32
#include <spdlog/sinks/wincolor_sink.h> # include <spdlog/sinks/wincolor_sink.h>
#else #else
#include <spdlog/sinks/ansicolor_sink.h> # include <spdlog/sinks/ansicolor_sink.h>
#endif #endif
#include <spdlog/details/synchronous_factory.h> #include <spdlog/details/synchronous_factory.h>
@@ -41,5 +41,5 @@ std::shared_ptr<logger> stderr_color_st(const std::string &logger_name, color_mo
} // namespace spdlog } // namespace spdlog
#ifdef SPDLOG_HEADER_ONLY #ifdef SPDLOG_HEADER_ONLY
#include "stdout_color_sinks-inl.h" # include "stdout_color_sinks-inl.h"
#endif #endif

View File

@@ -4,7 +4,7 @@
#pragma once #pragma once
#ifndef SPDLOG_HEADER_ONLY #ifndef SPDLOG_HEADER_ONLY
#include <spdlog/sinks/stdout_sinks.h> # include <spdlog/sinks/stdout_sinks.h>
#endif #endif
#include <spdlog/details/console_globals.h> #include <spdlog/details/console_globals.h>
@@ -14,11 +14,15 @@
#ifdef _WIN32 #ifdef _WIN32
// under windows using fwrite to non-binary stream results in \r\r\n (see issue #1675) // under windows using fwrite to non-binary stream results in \r\r\n (see issue #1675)
// so instead we use ::FileWrite // so instead we use ::FileWrite
#include <spdlog/details/windows_include.h> # include <spdlog/details/windows_include.h>
#include <fileapi.h> // WriteFile (..)
#include <io.h> // _get_osfhandle(..) # ifndef _USING_V110_SDK71_ // fileapi.h doesnt exist in winxp
#include <stdio.h> // _fileno(..) # include <fileapi.h> // WriteFile (..)
#endif // WIN32 # endif
# include <io.h> // _get_osfhandle(..)
# include <stdio.h> // _fileno(..)
#endif // WIN32
namespace spdlog { namespace spdlog {
@@ -32,9 +36,9 @@ SPDLOG_INLINE stdout_sink_base<ConsoleMutex>::stdout_sink_base(FILE *file)
{ {
#ifdef _WIN32 #ifdef _WIN32
// get windows handle from the FILE* object // get windows handle from the FILE* object
handle_ = (HANDLE)::_get_osfhandle(::_fileno(file_)); handle_ = (HANDLE)::_get_osfhandle(::_fileno(file_));
// don't throw to support cases where no console is attached, // don't throw to support cases where no console is attached,
// and let the log method to do nothing if (handle_ == INVALID_HANDLE_VALUE). // and let the log method to do nothing if (handle_ == INVALID_HANDLE_VALUE).
// throw only if non stdout/stderr target is requested (probably regular file and not console). // throw only if non stdout/stderr target is requested (probably regular file and not console).
@@ -50,7 +54,7 @@ SPDLOG_INLINE void stdout_sink_base<ConsoleMutex>::log(const details::log_msg &m
{ {
#ifdef _WIN32 #ifdef _WIN32
if (handle_ == INVALID_HANDLE_VALUE) if (handle_ == INVALID_HANDLE_VALUE)
{ {
return; return;
} }
std::lock_guard<mutex_t> lock(mutex_); std::lock_guard<mutex_t> lock(mutex_);
@@ -70,7 +74,7 @@ SPDLOG_INLINE void stdout_sink_base<ConsoleMutex>::log(const details::log_msg &m
formatter_->format(msg, formatted); formatter_->format(msg, formatted);
::fwrite(formatted.data(), sizeof(char), formatted.size(), file_); ::fwrite(formatted.data(), sizeof(char), formatted.size(), file_);
::fflush(file_); // flush every line to terminal ::fflush(file_); // flush every line to terminal
#endif // WIN32 #endif // WIN32
} }
template<typename ConsoleMutex> template<typename ConsoleMutex>

View File

@@ -9,7 +9,7 @@
#include <cstdio> #include <cstdio>
#ifdef _WIN32 #ifdef _WIN32
#include <spdlog/details/windows_include.h> # include <spdlog/details/windows_include.h>
#endif #endif
namespace spdlog { namespace spdlog {
@@ -41,7 +41,7 @@ protected:
FILE *file_; FILE *file_;
std::unique_ptr<spdlog::formatter> formatter_; std::unique_ptr<spdlog::formatter> formatter_;
#ifdef _WIN32 #ifdef _WIN32
HANDLE handle_; HANDLE handle_;
#endif // WIN32 #endif // WIN32
}; };
@@ -83,5 +83,5 @@ std::shared_ptr<logger> stderr_logger_st(const std::string &logger_name);
} // namespace spdlog } // namespace spdlog
#ifdef SPDLOG_HEADER_ONLY #ifdef SPDLOG_HEADER_ONLY
#include "stdout_sinks-inl.h" # include "stdout_sinks-inl.h"
#endif #endif

View File

@@ -9,7 +9,7 @@
#include <array> #include <array>
#ifndef SD_JOURNAL_SUPPRESS_LOCATION #ifndef SD_JOURNAL_SUPPRESS_LOCATION
#define SD_JOURNAL_SUPPRESS_LOCATION # define SD_JOURNAL_SUPPRESS_LOCATION
#endif #endif
#include <systemd/sd-journal.h> #include <systemd/sd-journal.h>

View File

@@ -7,9 +7,9 @@
#include <spdlog/sinks/base_sink.h> #include <spdlog/sinks/base_sink.h>
#include <spdlog/details/null_mutex.h> #include <spdlog/details/null_mutex.h>
#ifdef _WIN32 #ifdef _WIN32
#include <spdlog/details/tcp_client-windows.h> # include <spdlog/details/tcp_client-windows.h>
#else #else
#include <spdlog/details/tcp_client.h> # include <spdlog/details/tcp_client.h>
#endif #endif
#include <mutex> #include <mutex>

View File

@@ -229,11 +229,11 @@ protected:
LPCWSTR lp_wstr = buf.data(); LPCWSTR lp_wstr = buf.data();
succeeded = ::ReportEventW(event_log_handle(), eventlog::get_event_type(msg), eventlog::get_event_category(msg), event_id_, succeeded = ::ReportEventW(event_log_handle(), eventlog::get_event_type(msg), eventlog::get_event_category(msg), event_id_,
current_user_sid_.as_sid(), 1, 0, &lp_wstr, nullptr); current_user_sid_.as_sid(), 1, 0, &lp_wstr, nullptr);
#else #else
LPCSTR lp_str = formatted.data(); LPCSTR lp_str = formatted.data();
succeeded = ::ReportEventA(event_log_handle(), eventlog::get_event_type(msg), eventlog::get_event_category(msg), event_id_, succeeded = ::ReportEventA(event_log_handle(), eventlog::get_event_type(msg), eventlog::get_event_category(msg), event_id_,
current_user_sid_.as_sid(), 1, 0, &lp_str, nullptr); current_user_sid_.as_sid(), 1, 0, &lp_str, nullptr);
#endif #endif
if (!succeeded) if (!succeeded)

View File

@@ -4,7 +4,7 @@
#pragma once #pragma once
#ifndef SPDLOG_HEADER_ONLY #ifndef SPDLOG_HEADER_ONLY
#include <spdlog/sinks/wincolor_sink.h> # include <spdlog/sinks/wincolor_sink.h>
#endif #endif
#include <spdlog/details/windows_include.h> #include <spdlog/details/windows_include.h>
@@ -129,10 +129,10 @@ std::uint16_t SPDLOG_INLINE wincolor_sink<ConsoleMutex>::set_foreground_color_(s
if (!::GetConsoleScreenBufferInfo(static_cast<HANDLE>(out_handle_), &orig_buffer_info)) if (!::GetConsoleScreenBufferInfo(static_cast<HANDLE>(out_handle_), &orig_buffer_info))
{ {
// just return white if failed getting console info // just return white if failed getting console info
return FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE; return FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE;
} }
// change only the foreground bits (lowest 4 bits) // change only the foreground bits (lowest 4 bits)
auto new_attribs = static_cast<WORD>(attribs) | (orig_buffer_info.wAttributes & 0xfff0); auto new_attribs = static_cast<WORD>(attribs) | (orig_buffer_info.wAttributes & 0xfff0);
auto ignored = ::SetConsoleTextAttribute(static_cast<HANDLE>(out_handle_), static_cast<WORD>(new_attribs)); auto ignored = ::SetConsoleTextAttribute(static_cast<HANDLE>(out_handle_), static_cast<WORD>(new_attribs));
(void)(ignored); (void)(ignored);

View File

@@ -23,7 +23,7 @@ namespace sinks {
template<typename ConsoleMutex> template<typename ConsoleMutex>
class wincolor_sink : public sink class wincolor_sink : public sink
{ {
public: public:
wincolor_sink(void *out_handle, color_mode mode); wincolor_sink(void *out_handle, color_mode mode);
~wincolor_sink() override; ~wincolor_sink() override;
@@ -41,7 +41,7 @@ public:
protected: protected:
using mutex_t = typename ConsoleMutex::mutex_t; using mutex_t = typename ConsoleMutex::mutex_t;
void *out_handle_; void *out_handle_;
mutex_t &mutex_; mutex_t &mutex_;
bool should_do_colors_; bool should_do_colors_;
std::unique_ptr<spdlog::formatter> formatter_; std::unique_ptr<spdlog::formatter> formatter_;
std::array<std::uint16_t, level::n_levels> colors_; std::array<std::uint16_t, level::n_levels> colors_;
@@ -81,5 +81,5 @@ using wincolor_stderr_sink_st = wincolor_stderr_sink<details::console_nullmutex>
} // namespace spdlog } // namespace spdlog
#ifdef SPDLOG_HEADER_ONLY #ifdef SPDLOG_HEADER_ONLY
#include "wincolor_sink-inl.h" # include "wincolor_sink-inl.h"
#endif #endif

View File

@@ -4,7 +4,7 @@
#pragma once #pragma once
#ifndef SPDLOG_HEADER_ONLY #ifndef SPDLOG_HEADER_ONLY
#include <spdlog/spdlog.h> # include <spdlog/spdlog.h>
#endif #endif
#include <spdlog/common.h> #include <spdlog/common.h>

View File

@@ -127,50 +127,50 @@ SPDLOG_API spdlog::logger *default_logger_raw();
SPDLOG_API void set_default_logger(std::shared_ptr<spdlog::logger> default_logger); SPDLOG_API void set_default_logger(std::shared_ptr<spdlog::logger> default_logger);
template<typename FormatString, typename... Args> template<typename... Args>
inline void log(source_loc source, level::level_enum lvl, const FormatString &fmt, Args&&...args) inline void log(source_loc source, level::level_enum lvl, fmt::format_string<Args...> fmt, Args &&...args)
{ {
default_logger_raw()->log(source, lvl, fmt, std::forward<Args>(args)...); default_logger_raw()->log(source, lvl, fmt, std::forward<Args>(args)...);
} }
template<typename FormatString, typename... Args> template<typename... Args>
inline void log(level::level_enum lvl, const FormatString &fmt, Args&&...args) inline void log(level::level_enum lvl, fmt::format_string<Args...> fmt, Args &&...args)
{ {
default_logger_raw()->log(source_loc{}, lvl, fmt, std::forward<Args>(args)...); default_logger_raw()->log(source_loc{}, lvl, fmt, std::forward<Args>(args)...);
} }
template<typename FormatString, typename... Args> template<typename... Args>
inline void trace(const FormatString &fmt, Args&&...args) inline void trace(fmt::format_string<Args...> fmt, Args &&...args)
{ {
default_logger_raw()->trace(fmt, std::forward<Args>(args)...); default_logger_raw()->trace(fmt, std::forward<Args>(args)...);
} }
template<typename FormatString, typename... Args> template<typename... Args>
inline void debug(const FormatString &fmt, Args&&...args) inline void debug(fmt::format_string<Args...> fmt, Args &&...args)
{ {
default_logger_raw()->debug(fmt, std::forward<Args>(args)...); default_logger_raw()->debug(fmt, std::forward<Args>(args)...);
} }
template<typename FormatString, typename... Args> template<typename... Args>
inline void info(const FormatString &fmt, Args&&...args) inline void info(fmt::format_string<Args...> fmt, Args &&...args)
{ {
default_logger_raw()->info(fmt, std::forward<Args>(args)...); default_logger_raw()->info(fmt, std::forward<Args>(args)...);
} }
template<typename FormatString, typename... Args> template<typename... Args>
inline void warn(const FormatString &fmt, Args&&...args) inline void warn(fmt::format_string<Args...> fmt, Args &&...args)
{ {
default_logger_raw()->warn(fmt, std::forward<Args>(args)...); default_logger_raw()->warn(fmt, std::forward<Args>(args)...);
} }
template<typename FormatString, typename... Args> template<typename... Args>
inline void error(const FormatString &fmt, Args&&...args) inline void error(fmt::format_string<Args...> fmt, Args &&...args)
{ {
default_logger_raw()->error(fmt, std::forward<Args>(args)...); default_logger_raw()->error(fmt, std::forward<Args>(args)...);
} }
template<typename FormatString, typename... Args> template<typename... Args>
inline void critical(const FormatString &fmt, Args&&...args) inline void critical(fmt::format_string<Args...> fmt, Args &&...args)
{ {
default_logger_raw()->critical(fmt, std::forward<Args>(args)...); default_logger_raw()->critical(fmt, std::forward<Args>(args)...);
} }
@@ -187,6 +187,56 @@ inline void log(level::level_enum lvl, const T &msg)
default_logger_raw()->log(lvl, msg); default_logger_raw()->log(lvl, msg);
} }
#ifdef SPDLOG_WCHAR_TO_UTF8_SUPPORT
template<typename... Args>
inline void log(source_loc source, level::level_enum lvl, fmt::wformat_string<Args...> fmt, Args &&...args)
{
default_logger_raw()->log(source, lvl, fmt, std::forward<Args>(args)...);
}
template<typename... Args>
inline void log(level::level_enum lvl, fmt::wformat_string<Args...> fmt, Args &&...args)
{
default_logger_raw()->log(source_loc{}, lvl, fmt, std::forward<Args>(args)...);
}
template<typename... Args>
inline void trace(fmt::wformat_string<Args...> fmt, Args &&...args)
{
default_logger_raw()->trace(fmt, std::forward<Args>(args)...);
}
template<typename... Args>
inline void debug(fmt::wformat_string<Args...> fmt, Args &&...args)
{
default_logger_raw()->debug(fmt, std::forward<Args>(args)...);
}
template<typename... Args>
inline void info(fmt::wformat_string<Args...> fmt, Args &&...args)
{
default_logger_raw()->info(fmt, std::forward<Args>(args)...);
}
template<typename... Args>
inline void warn(fmt::wformat_string<Args...> fmt, Args &&...args)
{
default_logger_raw()->warn(fmt, std::forward<Args>(args)...);
}
template<typename... Args>
inline void error(fmt::wformat_string<Args...> fmt, Args &&...args)
{
default_logger_raw()->error(fmt, std::forward<Args>(args)...);
}
template<typename... Args>
inline void critical(fmt::wformat_string<Args...> fmt, Args &&...args)
{
default_logger_raw()->critical(fmt, std::forward<Args>(args)...);
}
#endif
template<typename T> template<typename T>
inline void trace(const T &msg) inline void trace(const T &msg)
{ {
@@ -241,55 +291,55 @@ inline void critical(const T &msg)
#define SPDLOG_LOGGER_CALL(logger, level, ...) (logger)->log(spdlog::source_loc{__FILE__, __LINE__, SPDLOG_FUNCTION}, level, __VA_ARGS__) #define SPDLOG_LOGGER_CALL(logger, level, ...) (logger)->log(spdlog::source_loc{__FILE__, __LINE__, SPDLOG_FUNCTION}, level, __VA_ARGS__)
#if SPDLOG_ACTIVE_LEVEL <= SPDLOG_LEVEL_TRACE #if SPDLOG_ACTIVE_LEVEL <= SPDLOG_LEVEL_TRACE
#define SPDLOG_LOGGER_TRACE(logger, ...) SPDLOG_LOGGER_CALL(logger, spdlog::level::trace, __VA_ARGS__) # define SPDLOG_LOGGER_TRACE(logger, ...) SPDLOG_LOGGER_CALL(logger, spdlog::level::trace, __VA_ARGS__)
#define SPDLOG_TRACE(...) SPDLOG_LOGGER_TRACE(spdlog::default_logger_raw(), __VA_ARGS__) # define SPDLOG_TRACE(...) SPDLOG_LOGGER_TRACE(spdlog::default_logger_raw(), __VA_ARGS__)
#else #else
#define SPDLOG_LOGGER_TRACE(logger, ...) (void)0 # define SPDLOG_LOGGER_TRACE(logger, ...) (void)0
#define SPDLOG_TRACE(...) (void)0 # define SPDLOG_TRACE(...) (void)0
#endif #endif
#if SPDLOG_ACTIVE_LEVEL <= SPDLOG_LEVEL_DEBUG #if SPDLOG_ACTIVE_LEVEL <= SPDLOG_LEVEL_DEBUG
#define SPDLOG_LOGGER_DEBUG(logger, ...) SPDLOG_LOGGER_CALL(logger, spdlog::level::debug, __VA_ARGS__) # define SPDLOG_LOGGER_DEBUG(logger, ...) SPDLOG_LOGGER_CALL(logger, spdlog::level::debug, __VA_ARGS__)
#define SPDLOG_DEBUG(...) SPDLOG_LOGGER_DEBUG(spdlog::default_logger_raw(), __VA_ARGS__) # define SPDLOG_DEBUG(...) SPDLOG_LOGGER_DEBUG(spdlog::default_logger_raw(), __VA_ARGS__)
#else #else
#define SPDLOG_LOGGER_DEBUG(logger, ...) (void)0 # define SPDLOG_LOGGER_DEBUG(logger, ...) (void)0
#define SPDLOG_DEBUG(...) (void)0 # define SPDLOG_DEBUG(...) (void)0
#endif #endif
#if SPDLOG_ACTIVE_LEVEL <= SPDLOG_LEVEL_INFO #if SPDLOG_ACTIVE_LEVEL <= SPDLOG_LEVEL_INFO
#define SPDLOG_LOGGER_INFO(logger, ...) SPDLOG_LOGGER_CALL(logger, spdlog::level::info, __VA_ARGS__) # define SPDLOG_LOGGER_INFO(logger, ...) SPDLOG_LOGGER_CALL(logger, spdlog::level::info, __VA_ARGS__)
#define SPDLOG_INFO(...) SPDLOG_LOGGER_INFO(spdlog::default_logger_raw(), __VA_ARGS__) # define SPDLOG_INFO(...) SPDLOG_LOGGER_INFO(spdlog::default_logger_raw(), __VA_ARGS__)
#else #else
#define SPDLOG_LOGGER_INFO(logger, ...) (void)0 # define SPDLOG_LOGGER_INFO(logger, ...) (void)0
#define SPDLOG_INFO(...) (void)0 # define SPDLOG_INFO(...) (void)0
#endif #endif
#if SPDLOG_ACTIVE_LEVEL <= SPDLOG_LEVEL_WARN #if SPDLOG_ACTIVE_LEVEL <= SPDLOG_LEVEL_WARN
#define SPDLOG_LOGGER_WARN(logger, ...) SPDLOG_LOGGER_CALL(logger, spdlog::level::warn, __VA_ARGS__) # define SPDLOG_LOGGER_WARN(logger, ...) SPDLOG_LOGGER_CALL(logger, spdlog::level::warn, __VA_ARGS__)
#define SPDLOG_WARN(...) SPDLOG_LOGGER_WARN(spdlog::default_logger_raw(), __VA_ARGS__) # define SPDLOG_WARN(...) SPDLOG_LOGGER_WARN(spdlog::default_logger_raw(), __VA_ARGS__)
#else #else
#define SPDLOG_LOGGER_WARN(logger, ...) (void)0 # define SPDLOG_LOGGER_WARN(logger, ...) (void)0
#define SPDLOG_WARN(...) (void)0 # define SPDLOG_WARN(...) (void)0
#endif #endif
#if SPDLOG_ACTIVE_LEVEL <= SPDLOG_LEVEL_ERROR #if SPDLOG_ACTIVE_LEVEL <= SPDLOG_LEVEL_ERROR
#define SPDLOG_LOGGER_ERROR(logger, ...) SPDLOG_LOGGER_CALL(logger, spdlog::level::err, __VA_ARGS__) # define SPDLOG_LOGGER_ERROR(logger, ...) SPDLOG_LOGGER_CALL(logger, spdlog::level::err, __VA_ARGS__)
#define SPDLOG_ERROR(...) SPDLOG_LOGGER_ERROR(spdlog::default_logger_raw(), __VA_ARGS__) # define SPDLOG_ERROR(...) SPDLOG_LOGGER_ERROR(spdlog::default_logger_raw(), __VA_ARGS__)
#else #else
#define SPDLOG_LOGGER_ERROR(logger, ...) (void)0 # define SPDLOG_LOGGER_ERROR(logger, ...) (void)0
#define SPDLOG_ERROR(...) (void)0 # define SPDLOG_ERROR(...) (void)0
#endif #endif
#if SPDLOG_ACTIVE_LEVEL <= SPDLOG_LEVEL_CRITICAL #if SPDLOG_ACTIVE_LEVEL <= SPDLOG_LEVEL_CRITICAL
#define SPDLOG_LOGGER_CRITICAL(logger, ...) SPDLOG_LOGGER_CALL(logger, spdlog::level::critical, __VA_ARGS__) # define SPDLOG_LOGGER_CRITICAL(logger, ...) SPDLOG_LOGGER_CALL(logger, spdlog::level::critical, __VA_ARGS__)
#define SPDLOG_CRITICAL(...) SPDLOG_LOGGER_CRITICAL(spdlog::default_logger_raw(), __VA_ARGS__) # define SPDLOG_CRITICAL(...) SPDLOG_LOGGER_CRITICAL(spdlog::default_logger_raw(), __VA_ARGS__)
#else #else
#define SPDLOG_LOGGER_CRITICAL(logger, ...) (void)0 # define SPDLOG_LOGGER_CRITICAL(logger, ...) (void)0
#define SPDLOG_CRITICAL(...) (void)0 # define SPDLOG_CRITICAL(...) (void)0
#endif #endif
#ifdef SPDLOG_HEADER_ONLY #ifdef SPDLOG_HEADER_ONLY
#include "spdlog-inl.h" # include "spdlog-inl.h"
#endif #endif
#endif // SPDLOG_H #endif // SPDLOG_H

View File

@@ -87,7 +87,7 @@
/////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////
// Uncomment to customize level names (e.g. "MT TRACE") // Uncomment to customize level names (e.g. "MY TRACE")
// //
// #define SPDLOG_LEVEL_NAMES { "MY TRACE", "MY DEBUG", "MY INFO", "MY WARNING", // #define SPDLOG_LEVEL_NAMES { "MY TRACE", "MY DEBUG", "MY INFO", "MY WARNING",
// "MY ERROR", "MY CRITICAL", "OFF" } // "MY ERROR", "MY CRITICAL", "OFF" }

View File

@@ -4,7 +4,7 @@
#pragma once #pragma once
#define SPDLOG_VER_MAJOR 1 #define SPDLOG_VER_MAJOR 1
#define SPDLOG_VER_MINOR 8 #define SPDLOG_VER_MINOR 9
#define SPDLOG_VER_PATCH 5 #define SPDLOG_VER_PATCH 0
#define SPDLOG_VERSION (SPDLOG_VER_MAJOR * 10000 + SPDLOG_VER_MINOR * 100 + SPDLOG_VER_PATCH) #define SPDLOG_VERSION (SPDLOG_VER_MAJOR * 10000 + SPDLOG_VER_MINOR * 100 + SPDLOG_VER_PATCH)

View File

@@ -4,14 +4,14 @@ import os
import re import re
base_path = os.path.abspath(os.path.join(os.path.dirname(__file__), '..')) base_path = os.path.abspath(os.path.join(os.path.dirname(__file__), '..'))
config_h = os.path.join(base_path, 'include', 'spdlog', 'version.h') config_h = os.path.join(base_path, 'include', 'spdlog', 'version.h')
data = {'MAJOR': 0, 'MINOR': 0, 'PATCH': 0} data = {'MAJOR': 0, 'MINOR': 0, 'PATCH': 0}
reg = re.compile(r'^\s*#define\s+SPDLOG_VER_([A-Z]+)\s+([0-9]+).*$') reg = re.compile(r'^\s*#define\s+SPDLOG_VER_([A-Z]+)\s+([0-9]+).*$')
with open(config_h, 'r') as fp: with open(config_h, 'r') as fp:
for l in fp: for l in fp:
m = reg.match(l) m = reg.match(l)
if m: if m:
data[m.group(1)] = int(m.group(2)) data[m.group(1)] = int(m.group(2))
print('{}.{}.{}'.format(data['MAJOR'], data['MINOR'], data['PATCH'])) print(f"{data['MAJOR']}.{data['MINOR']}.{data['PATCH']}")

View File

@@ -2,7 +2,7 @@
// Distributed under the MIT License (http://opensource.org/licenses/MIT) // Distributed under the MIT License (http://opensource.org/licenses/MIT)
#ifndef SPDLOG_COMPILED_LIB #ifndef SPDLOG_COMPILED_LIB
#error Please define SPDLOG_COMPILED_LIB to compile this file. # error Please define SPDLOG_COMPILED_LIB to compile this file.
#endif #endif
#include <spdlog/async.h> #include <spdlog/async.h>
@@ -10,4 +10,4 @@
#include <spdlog/details/periodic_worker-inl.h> #include <spdlog/details/periodic_worker-inl.h>
#include <spdlog/details/thread_pool-inl.h> #include <spdlog/details/thread_pool-inl.h>
template class SPDLOG_API spdlog::details::mpmc_blocking_queue<spdlog::details::async_msg>; template class SPDLOG_API spdlog::details::mpmc_blocking_queue<spdlog::details::async_msg>;

View File

@@ -2,7 +2,7 @@
// Distributed under the MIT License (http://opensource.org/licenses/MIT) // Distributed under the MIT License (http://opensource.org/licenses/MIT)
#ifndef SPDLOG_COMPILED_LIB #ifndef SPDLOG_COMPILED_LIB
#error Please define SPDLOG_COMPILED_LIB to compile this file. # error Please define SPDLOG_COMPILED_LIB to compile this file.
#endif #endif
#include <spdlog/cfg/helpers-inl.h> #include <spdlog/cfg/helpers-inl.h>

View File

@@ -2,7 +2,7 @@
// Distributed under the MIT License (http://opensource.org/licenses/MIT) // Distributed under the MIT License (http://opensource.org/licenses/MIT)
#ifndef SPDLOG_COMPILED_LIB #ifndef SPDLOG_COMPILED_LIB
#error Please define SPDLOG_COMPILED_LIB to compile this file. # error Please define SPDLOG_COMPILED_LIB to compile this file.
#endif #endif
#include <mutex> #include <mutex>
@@ -13,7 +13,7 @@
// color sinks // color sinks
// //
#ifdef _WIN32 #ifdef _WIN32
#include <spdlog/sinks/wincolor_sink-inl.h> # include <spdlog/sinks/wincolor_sink-inl.h>
template class SPDLOG_API spdlog::sinks::wincolor_sink<spdlog::details::console_mutex>; template class SPDLOG_API spdlog::sinks::wincolor_sink<spdlog::details::console_mutex>;
template class SPDLOG_API spdlog::sinks::wincolor_sink<spdlog::details::console_nullmutex>; template class SPDLOG_API spdlog::sinks::wincolor_sink<spdlog::details::console_nullmutex>;
template class SPDLOG_API spdlog::sinks::wincolor_stdout_sink<spdlog::details::console_mutex>; template class SPDLOG_API spdlog::sinks::wincolor_stdout_sink<spdlog::details::console_mutex>;
@@ -21,7 +21,7 @@ template class SPDLOG_API spdlog::sinks::wincolor_stdout_sink<spdlog::details::c
template class SPDLOG_API spdlog::sinks::wincolor_stderr_sink<spdlog::details::console_mutex>; template class SPDLOG_API spdlog::sinks::wincolor_stderr_sink<spdlog::details::console_mutex>;
template class SPDLOG_API spdlog::sinks::wincolor_stderr_sink<spdlog::details::console_nullmutex>; template class SPDLOG_API spdlog::sinks::wincolor_stderr_sink<spdlog::details::console_nullmutex>;
#else #else
#include "spdlog/sinks/ansicolor_sink-inl.h" # include "spdlog/sinks/ansicolor_sink-inl.h"
template class SPDLOG_API spdlog::sinks::ansicolor_sink<spdlog::details::console_mutex>; template class SPDLOG_API spdlog::sinks::ansicolor_sink<spdlog::details::console_mutex>;
template class SPDLOG_API spdlog::sinks::ansicolor_sink<spdlog::details::console_nullmutex>; template class SPDLOG_API spdlog::sinks::ansicolor_sink<spdlog::details::console_nullmutex>;
template class SPDLOG_API spdlog::sinks::ansicolor_stdout_sink<spdlog::details::console_mutex>; template class SPDLOG_API spdlog::sinks::ansicolor_stdout_sink<spdlog::details::console_mutex>;

View File

@@ -2,7 +2,7 @@
// Distributed under the MIT License (http://opensource.org/licenses/MIT) // Distributed under the MIT License (http://opensource.org/licenses/MIT)
#ifndef SPDLOG_COMPILED_LIB #ifndef SPDLOG_COMPILED_LIB
#error Please define SPDLOG_COMPILED_LIB to compile this file. # error Please define SPDLOG_COMPILED_LIB to compile this file.
#endif #endif
#include <spdlog/details/null_mutex.h> #include <spdlog/details/null_mutex.h>
@@ -17,4 +17,4 @@ template class SPDLOG_API spdlog::sinks::basic_file_sink<spdlog::details::null_m
#include <spdlog/sinks/rotating_file_sink-inl.h> #include <spdlog/sinks/rotating_file_sink-inl.h>
template class SPDLOG_API spdlog::sinks::rotating_file_sink<std::mutex>; template class SPDLOG_API spdlog::sinks::rotating_file_sink<std::mutex>;
template class SPDLOG_API spdlog::sinks::rotating_file_sink<spdlog::details::null_mutex>; template class SPDLOG_API spdlog::sinks::rotating_file_sink<spdlog::details::null_mutex>;

View File

@@ -3,101 +3,66 @@
// All rights reserved. // All rights reserved.
#ifndef SPDLOG_COMPILED_LIB #ifndef SPDLOG_COMPILED_LIB
#error Please define SPDLOG_COMPILED_LIB to compile this file. # error Please define SPDLOG_COMPILED_LIB to compile this file.
#endif #endif
#if !defined(SPDLOG_FMT_EXTERNAL) #if !defined(SPDLOG_FMT_EXTERNAL)
#include <spdlog/fmt/bundled/format-inl.h> # include <spdlog/fmt/bundled/format-inl.h>
FMT_BEGIN_NAMESPACE FMT_BEGIN_NAMESPACE
namespace detail { namespace detail {
template <typename T> template<typename T>
int format_float(char* buf, std::size_t size, const char* format, int precision, int format_float(char *buf, std::size_t size, const char *format, int precision, T value)
T value) { {
#ifdef FMT_FUZZ # ifdef FMT_FUZZ
if (precision > 100000) if (precision > 100000)
throw std::runtime_error( throw std::runtime_error("fuzz mode - avoid large allocation inside snprintf");
"fuzz mode - avoid large allocation inside snprintf"); # endif
#endif // Suppress the warning about nonliteral format string.
// Suppress the warning about nonliteral format string. int (*snprintf_ptr)(char *, size_t, const char *, ...) = FMT_SNPRINTF;
int (*snprintf_ptr)(char*, size_t, const char*, ...) = FMT_SNPRINTF; return precision < 0 ? snprintf_ptr(buf, size, format, value) : snprintf_ptr(buf, size, format, precision, value);
return precision < 0 ? snprintf_ptr(buf, size, format, value)
: snprintf_ptr(buf, size, format, precision, value);
} }
template FMT_API dragonbox::decimal_fp<float> dragonbox::to_decimal(float x) template FMT_API dragonbox::decimal_fp<float> dragonbox::to_decimal(float x) FMT_NOEXCEPT;
FMT_NOEXCEPT; template FMT_API dragonbox::decimal_fp<double> dragonbox::to_decimal(double x) FMT_NOEXCEPT;
template FMT_API dragonbox::decimal_fp<double> dragonbox::to_decimal(double x) } // namespace detail
FMT_NOEXCEPT;
// DEPRECATED! This function exists for ABI compatibility.
template <typename Char>
typename basic_format_context<std::back_insert_iterator<buffer<Char>>,
Char>::iterator
vformat_to(buffer<Char>& buf, basic_string_view<Char> format_str,
basic_format_args<basic_format_context<
std::back_insert_iterator<buffer<type_identity_t<Char>>>,
type_identity_t<Char>>>
args) {
using iterator = std::back_insert_iterator<buffer<char>>;
using context = basic_format_context<
std::back_insert_iterator<buffer<type_identity_t<Char>>>,
type_identity_t<Char>>;
auto out = iterator(buf);
format_handler<iterator, Char, context> h(out, format_str, args, {});
parse_format_string<false>(format_str, h);
return out;
}
template basic_format_context<std::back_insert_iterator<buffer<char>>,
char>::iterator
vformat_to(buffer<char>&, string_view,
basic_format_args<basic_format_context<
std::back_insert_iterator<buffer<type_identity_t<char>>>,
type_identity_t<char>>>);
} // namespace detail
template struct FMT_INSTANTIATION_DEF_API detail::basic_data<void>;
// Workaround a bug in MSVC2013 that prevents instantiation of format_float. // Workaround a bug in MSVC2013 that prevents instantiation of format_float.
int (*instantiate_format_float)(double, int, detail::float_specs, int (*instantiate_format_float)(double, int, detail::float_specs, detail::buffer<char> &) = detail::format_float;
detail::buffer<char>&) = detail::format_float;
#ifndef FMT_STATIC_THOUSANDS_SEPARATOR # ifndef FMT_STATIC_THOUSANDS_SEPARATOR
template FMT_API detail::locale_ref::locale_ref(const std::locale& loc); template FMT_API detail::locale_ref::locale_ref(const std::locale &loc);
template FMT_API std::locale detail::locale_ref::get<std::locale>() const; template FMT_API std::locale detail::locale_ref::get<std::locale>() const;
#endif # endif
// Explicit instantiations for char. // Explicit instantiations for char.
template FMT_API std::string detail::grouping_impl<char>(locale_ref); template FMT_API auto detail::thousands_sep_impl(locale_ref) -> thousands_sep_result<char>;
template FMT_API char detail::thousands_sep_impl(locale_ref);
template FMT_API char detail::decimal_point_impl(locale_ref); template FMT_API char detail::decimal_point_impl(locale_ref);
template FMT_API void detail::buffer<char>::append(const char*, const char*); template FMT_API void detail::buffer<char>::append(const char *, const char *);
// DEPRECATED!
// There is no correspondent extern template in format.h because of
// incompatibility between clang and gcc (#2377).
template FMT_API void detail::vformat_to( template FMT_API void detail::vformat_to(
detail::buffer<char>&, string_view, detail::buffer<char> &, string_view, basic_format_args<FMT_BUFFER_CONTEXT(char)>, detail::locale_ref);
basic_format_args<FMT_BUFFER_CONTEXT(char)>, detail::locale_ref);
template FMT_API int detail::snprintf_float(double, int, detail::float_specs, template FMT_API int detail::snprintf_float(double, int, detail::float_specs, detail::buffer<char> &);
detail::buffer<char>&); template FMT_API int detail::snprintf_float(long double, int, detail::float_specs, detail::buffer<char> &);
template FMT_API int detail::snprintf_float(long double, int, template FMT_API int detail::format_float(double, int, detail::float_specs, detail::buffer<char> &);
detail::float_specs, template FMT_API int detail::format_float(long double, int, detail::float_specs, detail::buffer<char> &);
detail::buffer<char>&);
template FMT_API int detail::format_float(double, int, detail::float_specs,
detail::buffer<char>&);
template FMT_API int detail::format_float(long double, int, detail::float_specs,
detail::buffer<char>&);
// Explicit instantiations for wchar_t. // Explicit instantiations for wchar_t.
template FMT_API std::string detail::grouping_impl<wchar_t>(locale_ref); template FMT_API auto detail::thousands_sep_impl(locale_ref) -> thousands_sep_result<wchar_t>;
template FMT_API wchar_t detail::thousands_sep_impl(locale_ref);
template FMT_API wchar_t detail::decimal_point_impl(locale_ref); template FMT_API wchar_t detail::decimal_point_impl(locale_ref);
template FMT_API void detail::buffer<wchar_t>::append(const wchar_t*, template FMT_API void detail::buffer<wchar_t>::append(const wchar_t *, const wchar_t *);
const wchar_t*);
template struct detail::basic_data<void>;
FMT_END_NAMESPACE FMT_END_NAMESPACE
#endif // !SPDLOG_FMT_EXTERNAL #endif // !SPDLOG_FMT_EXTERNAL

Some files were not shown because too many files have changed in this diff Show More