mirror of
https://github.com/gabime/spdlog.git
synced 2025-09-29 01:29:35 +08:00
Compare commits
399 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
eb3220622e | ||
![]() |
8f26e819ad | ||
![]() |
b6b1c2f95d | ||
![]() |
9ce9804a88 | ||
![]() |
ddaa61ca9a | ||
![]() |
4646bd082a | ||
![]() |
53aca9c3d0 | ||
![]() |
aa1e794213 | ||
![]() |
45e3b678b0 | ||
![]() |
bd99496423 | ||
![]() |
e471ec884e | ||
![]() |
b400705a1c | ||
![]() |
cb35191fc1 | ||
![]() |
1945a93b33 | ||
![]() |
dfd12e6dac | ||
![]() |
ba29e1d75d | ||
![]() |
8f6d123586 | ||
![]() |
d368ed586c | ||
![]() |
87095a9f1f | ||
![]() |
dd6d203488 | ||
![]() |
f463ebf54a | ||
![]() |
3547d7e24f | ||
![]() |
a9c01aba78 | ||
![]() |
f237947bdc | ||
![]() |
890df3d90b | ||
![]() |
14783585b6 | ||
![]() |
243c4beac7 | ||
![]() |
fe9cb54e0d | ||
![]() |
dabec32748 | ||
![]() |
6faa5fc95b | ||
![]() |
dbbec6cdb4 | ||
![]() |
43923cf038 | ||
![]() |
2ccba49b01 | ||
![]() |
362fdc6ceb | ||
![]() |
7bb53541e4 | ||
![]() |
c07b3aeef9 | ||
![]() |
fb47935a7b | ||
![]() |
ec3538c2ee | ||
![]() |
84e15d1ee2 | ||
![]() |
5b4c4f3f77 | ||
![]() |
aecdfc60a0 | ||
![]() |
816ede3a17 | ||
![]() |
353c79ca71 | ||
![]() |
e93115f436 | ||
![]() |
197c9639bb | ||
![]() |
a358a38b84 | ||
![]() |
16d76e2293 | ||
![]() |
536e583cbe | ||
![]() |
9049f9aeb9 | ||
![]() |
bee3e63e1b | ||
![]() |
d8f13cbd5b | ||
![]() |
0f39da5490 | ||
![]() |
28fef35a12 | ||
![]() |
1344d44a5a | ||
![]() |
61ed2a670e | ||
![]() |
db1bc035f7 | ||
![]() |
8de6cdaa82 | ||
![]() |
fe1a4f5fb6 | ||
![]() |
d38f89cae8 | ||
![]() |
9c90fe8264 | ||
![]() |
b85a666f72 | ||
![]() |
5ba95f6816 | ||
![]() |
dc38b7c3c4 | ||
![]() |
6484b03dd9 | ||
![]() |
29235d9b4b | ||
![]() |
4b3687f1a6 | ||
![]() |
e7e8b75a4c | ||
![]() |
e98265a49b | ||
![]() |
e87f69bdb6 | ||
![]() |
70d2832c0d | ||
![]() |
7636f1f659 | ||
![]() |
1523c83650 | ||
![]() |
a6987efaec | ||
![]() |
6491abb519 | ||
![]() |
8faabb4e3a | ||
![]() |
2838c2c8a5 | ||
![]() |
3315bad009 | ||
![]() |
3eeced78b5 | ||
![]() |
c23430b438 | ||
![]() |
0e49bfff51 | ||
![]() |
3ed40d04a9 | ||
![]() |
70b36aa55d | ||
![]() |
0f83b33d4f | ||
![]() |
b83106bed4 | ||
![]() |
21413e599a | ||
![]() |
5f4cc7b036 | ||
![]() |
9aa26fb969 | ||
![]() |
7f74012a0d | ||
![]() |
96ebef093f | ||
![]() |
a19f4bba0c | ||
![]() |
c24b957e17 | ||
![]() |
5ba2f77230 | ||
![]() |
a09f490804 | ||
![]() |
082d6fbea9 | ||
![]() |
37372960a8 | ||
![]() |
0035a0c98d | ||
![]() |
036cc5d575 | ||
![]() |
14950926ed | ||
![]() |
baa3b1a07e | ||
![]() |
2a09f66a44 | ||
![]() |
e50b62c770 | ||
![]() |
13d8b0f17f | ||
![]() |
9e0c658b29 | ||
![]() |
74fec56927 | ||
![]() |
514f304a47 | ||
![]() |
7f85a5c988 | ||
![]() |
14d626d961 | ||
![]() |
7560cacb3f | ||
![]() |
3cd9bcdab9 | ||
![]() |
32f1efdc99 | ||
![]() |
bcc9f03457 | ||
![]() |
4c845bf02b | ||
![]() |
c727864393 | ||
![]() |
4548573a75 | ||
![]() |
385246730d | ||
![]() |
e06d21a4c0 | ||
![]() |
c132d2ae8c | ||
![]() |
ece31100e0 | ||
![]() |
ce4e1ac54b | ||
![]() |
ef61fb11f0 | ||
![]() |
42f2b11ec8 | ||
![]() |
ac87cbb0d1 | ||
![]() |
c65de3d689 | ||
![]() |
1433fa4209 | ||
![]() |
d51149e5ac | ||
![]() |
ffd813435a | ||
![]() |
d75fd2c7f9 | ||
![]() |
cdad84aa46 | ||
![]() |
0fdb545d8c | ||
![]() |
a5f5ff70e0 | ||
![]() |
4f0e320236 | ||
![]() |
68aed6a5eb | ||
![]() |
6811112208 | ||
![]() |
9ebc4b24d9 | ||
![]() |
b990080a52 | ||
![]() |
efbe3e4d57 | ||
![]() |
7b14a65b2b | ||
![]() |
5887744d8b | ||
![]() |
8bf718671a | ||
![]() |
c858b14c03 | ||
![]() |
12df172575 | ||
![]() |
7fa751d36e | ||
![]() |
7a7611e977 | ||
![]() |
ec8763adf2 | ||
![]() |
f2d1d573f5 | ||
![]() |
a530b87fd0 | ||
![]() |
6c21789aed | ||
![]() |
616866fcf4 | ||
![]() |
faf06bcfe5 | ||
![]() |
cd376a5c43 | ||
![]() |
6ba5ab6d67 | ||
![]() |
1bee3218b4 | ||
![]() |
802eaadd2d | ||
![]() |
ee22eed23d | ||
![]() |
ab72de5f7a | ||
![]() |
a32cea24fd | ||
![]() |
af0d805be4 | ||
![]() |
181c22f798 | ||
![]() |
87133ef6b7 | ||
![]() |
1ef2f014ee | ||
![]() |
0a92d1d684 | ||
![]() |
ff5221b693 | ||
![]() |
db484cc4b8 | ||
![]() |
6442963f49 | ||
![]() |
0f7b95ce47 | ||
![]() |
632a2e0894 | ||
![]() |
e9635c7b2d | ||
![]() |
8e3b1338a5 | ||
![]() |
9d3dde0900 | ||
![]() |
c5abaeddca | ||
![]() |
ca2cd6f3e7 | ||
![]() |
7d07e0312a | ||
![]() |
e1c73fd8f4 | ||
![]() |
b83ab21283 | ||
![]() |
8001156ca8 | ||
![]() |
57e31f0a58 | ||
![]() |
51fadf6b7e | ||
![]() |
2a6a8aa0a0 | ||
![]() |
aa264a7fb2 | ||
![]() |
5e35c2b6ab | ||
![]() |
0385372314 | ||
![]() |
efbff95ec7 | ||
![]() |
2a9edb2153 | ||
![]() |
be14e60d9e | ||
![]() |
ef4641cad7 | ||
![]() |
100f30043f | ||
![]() |
1574b5b0a2 | ||
![]() |
012fe99ab1 | ||
![]() |
8ff5a3e096 | ||
![]() |
65317eb019 | ||
![]() |
ac3e26b0ff | ||
![]() |
e86f450428 | ||
![]() |
7b2776fdc7 | ||
![]() |
2a16d1d230 | ||
![]() |
53e1c9ab11 | ||
![]() |
410abc4626 | ||
![]() |
a2e28443f0 | ||
![]() |
c1af0a3f21 | ||
![]() |
bb5e1ee2f9 | ||
![]() |
3aee89c8fd | ||
![]() |
44e1f9f682 | ||
![]() |
37d76b961c | ||
![]() |
1305663d99 | ||
![]() |
8f4efe57a2 | ||
![]() |
0613dbc4a2 | ||
![]() |
0ed0d69368 | ||
![]() |
2ffbbee1f6 | ||
![]() |
b9d2f2537b | ||
![]() |
69dc173979 | ||
![]() |
ded8b5ebd4 | ||
![]() |
ed58ae9f98 | ||
![]() |
f7f790b4b3 | ||
![]() |
fe74c80992 | ||
![]() |
fa659bf7ad | ||
![]() |
9b41649601 | ||
![]() |
1b3438f5a5 | ||
![]() |
3eed64e5c4 | ||
![]() |
0fac33781d | ||
![]() |
3135b6a33d | ||
![]() |
2686ae2322 | ||
![]() |
a709e29586 | ||
![]() |
dd46579cb4 | ||
![]() |
f4b7210e7b | ||
![]() |
05a0b0d7b0 | ||
![]() |
c1f4d7506a | ||
![]() |
b6ba0be550 | ||
![]() |
23dfb4e2f9 | ||
![]() |
7a10e31982 | ||
![]() |
de89c4fd01 | ||
![]() |
5d4956d34b | ||
![]() |
42c5eb59c9 | ||
![]() |
09cc6e7754 | ||
![]() |
4a5bc41e89 | ||
![]() |
0ade18828d | ||
![]() |
91046e6ca4 | ||
![]() |
17e1ba8ae2 | ||
![]() |
c47c854f15 | ||
![]() |
e931866b35 | ||
![]() |
7828a065bf | ||
![]() |
3e689e700e | ||
![]() |
a9964afcf7 | ||
![]() |
95c19876c6 | ||
![]() |
5efccfa5e2 | ||
![]() |
89e737a258 | ||
![]() |
8fbc853b0d | ||
![]() |
2e008b319c | ||
![]() |
ff6e3c95f2 | ||
![]() |
7e9385405f | ||
![]() |
592ea36a86 | ||
![]() |
e059ebf99d | ||
![]() |
609480ed78 | ||
![]() |
4271185936 | ||
![]() |
aacae62591 | ||
![]() |
47cbf3828d | ||
![]() |
46d418164d | ||
![]() |
ede8d84884 | ||
![]() |
53d223b45f | ||
![]() |
ac35dd5a6f | ||
![]() |
9e19012cb0 | ||
![]() |
1234cda3b3 | ||
![]() |
710a0e3a45 | ||
![]() |
b7f24b2456 | ||
![]() |
fc594b551a | ||
![]() |
f39ccccc0c | ||
![]() |
f0a4ddd78b | ||
![]() |
c691769e46 | ||
![]() |
19dc30567e | ||
![]() |
a453bccff0 | ||
![]() |
aa2053a575 | ||
![]() |
3d8f71c4d2 | ||
![]() |
6aaaabbc4d | ||
![]() |
42c36f48ed | ||
![]() |
a5f4139102 | ||
![]() |
030d85a9b3 | ||
![]() |
adcfb7fb55 | ||
![]() |
cec365888a | ||
![]() |
55bfa8dd11 | ||
![]() |
e99759fe45 | ||
![]() |
17c6e6ee3f | ||
![]() |
7fff900a1a | ||
![]() |
c67974e4c8 | ||
![]() |
a36696e02e | ||
![]() |
9b80ca6c41 | ||
![]() |
22f514aabf | ||
![]() |
211478e13e | ||
![]() |
5e33a7e58b | ||
![]() |
b2e31721e8 | ||
![]() |
de0dbfa359 | ||
![]() |
f93459579f | ||
![]() |
2b81c40b90 | ||
![]() |
233e97c5e4 | ||
![]() |
fc1ce48dc7 | ||
![]() |
fd5562eebe | ||
![]() |
0695d9cb5f | ||
![]() |
456b24134d | ||
![]() |
f8ba24afee | ||
![]() |
eebb921c9f | ||
![]() |
e17ee87f38 | ||
![]() |
18e3f07f7d | ||
![]() |
9ce39a470f | ||
![]() |
23572369fc | ||
![]() |
01b350de96 | ||
![]() |
365e470a32 | ||
![]() |
a42b40656c | ||
![]() |
40160f2a57 | ||
![]() |
90b33b1552 | ||
![]() |
5567ed01e5 | ||
![]() |
cbe9448650 | ||
![]() |
5b345534dc | ||
![]() |
c8dc318fb3 | ||
![]() |
23cb1a1080 | ||
![]() |
21cf8d7d3c | ||
![]() |
3cf4d34094 | ||
![]() |
16d78ae5db | ||
![]() |
62b4b7af83 | ||
![]() |
9799ecac6a | ||
![]() |
dccb766095 | ||
![]() |
c97983a91c | ||
![]() |
680fb07fd5 | ||
![]() |
cfd0ea197c | ||
![]() |
48d4ed9bc0 | ||
![]() |
3bed78356e | ||
![]() |
8923922f30 | ||
![]() |
7542e42e4f | ||
![]() |
7a9b23e4f4 | ||
![]() |
47253ba2a1 | ||
![]() |
69b54dd9e4 | ||
![]() |
36138617fc | ||
![]() |
231ca50700 | ||
![]() |
c7613f3e91 | ||
![]() |
05d5546eb1 | ||
![]() |
cefe67726e | ||
![]() |
1ac2dcc537 | ||
![]() |
3a68eecb28 | ||
![]() |
54a8259b42 | ||
![]() |
32b6f1619f | ||
![]() |
99b8c5d379 | ||
![]() |
5deb7c55e1 | ||
![]() |
9cd25dd216 | ||
![]() |
4a9ccf7e38 | ||
![]() |
2963b9f07f | ||
![]() |
a4a9bc4d8e | ||
![]() |
a16a029790 | ||
![]() |
97fea81599 | ||
![]() |
ccffb6ecd6 | ||
![]() |
63b5a1a4d8 | ||
![]() |
dfc777803a | ||
![]() |
934a9bb23e | ||
![]() |
7097f7a894 | ||
![]() |
537fd7c4ba | ||
![]() |
d5048b8b0c | ||
![]() |
0348556aac | ||
![]() |
d6329b9dce | ||
![]() |
34f3d29d93 | ||
![]() |
cd701761f9 | ||
![]() |
23c2c00d69 | ||
![]() |
fa501b46cf | ||
![]() |
38dc0a5c5d | ||
![]() |
685cc4edbc | ||
![]() |
78369375e3 | ||
![]() |
6587058f74 | ||
![]() |
30f738e49a | ||
![]() |
726ca01e5c | ||
![]() |
83b40b8cda | ||
![]() |
db0d0438ff | ||
![]() |
3c527488e7 | ||
![]() |
99abcf6ded | ||
![]() |
7009727559 | ||
![]() |
ae02fba141 | ||
![]() |
76cdeb62e3 | ||
![]() |
ae9627c64c | ||
![]() |
58875bdcd7 | ||
![]() |
616caa5d30 | ||
![]() |
8236ee3ff6 | ||
![]() |
19f2804661 | ||
![]() |
c62ba5f48d | ||
![]() |
22bee8128a | ||
![]() |
39150eb8c7 | ||
![]() |
1b14fa53ef | ||
![]() |
cf55e5d4f8 | ||
![]() |
1a1ea028f6 | ||
![]() |
814c3445a3 | ||
![]() |
075dcee042 | ||
![]() |
fe97a03033 | ||
![]() |
f593aad786 | ||
![]() |
4a8c602a59 | ||
![]() |
7143dbc46a | ||
![]() |
e69699e12c | ||
![]() |
d6dbdbf27a | ||
![]() |
a0dae55a69 | ||
![]() |
7f15fb2a21 | ||
![]() |
d5aa8db36f | ||
![]() |
357b6c9d8c | ||
![]() |
b0c4794305 | ||
![]() |
071206ef59 | ||
![]() |
63ab8e6341 | ||
![]() |
741b0d6e82 | ||
![]() |
3041faffab | ||
![]() |
30ee690401 |
@@ -104,5 +104,6 @@ SpacesInSquareBrackets: false
|
||||
Standard: Cpp11
|
||||
TabWidth: 8
|
||||
UseTab: Never
|
||||
IndentPPDirectives: AfterHash
|
||||
...
|
||||
|
||||
|
@@ -2,7 +2,7 @@ Checks: 'cppcoreguidelines-*,
|
||||
performance-*,
|
||||
modernize-*,
|
||||
google-*,
|
||||
misc-*,
|
||||
misc-*
|
||||
cert-*,
|
||||
readability-*,
|
||||
clang-analyzer-*,
|
||||
|
84
.travis.yml
84
.travis.yml
@@ -5,7 +5,7 @@
|
||||
sudo: required
|
||||
language: cpp
|
||||
|
||||
# gcc 4.8
|
||||
# gcc t
|
||||
addons: &gcc48
|
||||
apt:
|
||||
packages:
|
||||
@@ -21,6 +21,24 @@ addons: &gcc7
|
||||
sources:
|
||||
- ubuntu-toolchain-r-test
|
||||
|
||||
|
||||
# gcc 9.0
|
||||
addons: &gcc9
|
||||
apt:
|
||||
packages:
|
||||
- g++-9
|
||||
sources:
|
||||
- ubuntu-toolchain-r-test
|
||||
|
||||
# gcc 11.0
|
||||
addons: &gcc11
|
||||
apt:
|
||||
packages:
|
||||
- g++-11
|
||||
sources:
|
||||
- ubuntu-toolchain-r-test
|
||||
|
||||
|
||||
# Clang 3.5
|
||||
addons: &clang35
|
||||
apt:
|
||||
@@ -40,48 +58,70 @@ addons: &clang10
|
||||
sources:
|
||||
- sourceline: "deb http://apt.llvm.org/bionic/ llvm-toolchain-bionic-10 main"
|
||||
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:
|
||||
include:
|
||||
# Test gcc-4.8: C++11, Build=Debug/Release
|
||||
- env: GCC_VERSION=4.8 BUILD_TYPE=Debug CPP=11
|
||||
os: linux
|
||||
addons: *gcc48
|
||||
|
||||
# Test gcc-4.8: C++11, Build=Release
|
||||
- env: GCC_VERSION=4.8 BUILD_TYPE=Release CPP=11
|
||||
os: linux
|
||||
addons: *gcc48
|
||||
|
||||
|
||||
# Test gcc-7: C++11, Build=Release
|
||||
- env: GCC_VERSION=7 BUILD_TYPE=Release CPP=11
|
||||
os: linux
|
||||
addons: *gcc7
|
||||
|
||||
# Test clang-3.5: C++11, Build=Debug/Release
|
||||
- env: CLANG_VERSION=3.5 BUILD_TYPE=Debug CPP=11
|
||||
|
||||
# Test gcc-9: C++17, Build=Release
|
||||
- env: GCC_VERSION=9 BUILD_TYPE=Release CPP=17
|
||||
os: linux
|
||||
addons: *clang35
|
||||
addons: *gcc9
|
||||
|
||||
# Test gcc-11.0: C++20, Build=Debug
|
||||
- env: GCC_VERSION=11 BUILD_TYPE=Debug CPP=20 ASAN=Off
|
||||
os: linux
|
||||
dist: bionic
|
||||
addons: *gcc11
|
||||
|
||||
# Test clang-3.5: C++11, Build=Release
|
||||
- env: CLANG_VERSION=3.5 BUILD_TYPE=Release CPP=11
|
||||
os: linux
|
||||
addons: *clang35
|
||||
|
||||
# osx
|
||||
# Text osx
|
||||
- env: BUILD_TYPE=Release CPP=11 ASAN=Off TSAN=Off
|
||||
os: osx
|
||||
|
||||
# Test clang-10.0: C++11, Build=Debug/Release
|
||||
- env: CLANG_VERSION=10 BUILD_TYPE=Debug CPP=11
|
||||
os: linux
|
||||
dist: bionic
|
||||
addons: *clang10
|
||||
|
||||
# Test clang-10.0: C++11, Build=Release
|
||||
- env: CLANG_VERSION=10 BUILD_TYPE=Release CPP=11 ASAN=On
|
||||
os: linux
|
||||
dist: bionic
|
||||
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:
|
||||
- if [ -n "$GCC_VERSION" ]; then export CXX="g++-${GCC_VERSION}" CC="gcc-${GCC_VERSION}"; fi
|
||||
- if [ -n "$CLANG_VERSION" ]; then export CXX="clang++-${CLANG_VERSION}" CC="clang-${CLANG_VERSION}"; fi
|
||||
@@ -101,10 +141,10 @@ script:
|
||||
-DCMAKE_CXX_STANDARD=$CPP \
|
||||
-DSPDLOG_BUILD_EXAMPLE=ON \
|
||||
-DSPDLOG_BUILD_EXAMPLE_HO=ON \
|
||||
-DSPDLOG_ENABLE_WARNINGS=ON \
|
||||
-DSPDLOG_BUILD_WARNINGS=ON \
|
||||
-DSPDLOG_BUILD_BENCH=OFF \
|
||||
-DSPDLOG_BUILD_TESTS=ON \
|
||||
-DSPDLOG_BUILD_TESTS_HO=OFf \
|
||||
-DSPDLOG_BUILD_TESTS_HO=OFF \
|
||||
-DSPDLOG_SANITIZE_ADDRESS=$ASAN
|
||||
|
||||
- make VERBOSE=1 -j2
|
||||
|
122
CMakeLists.txt
122
CMakeLists.txt
@@ -1,14 +1,6 @@
|
||||
# Copyright(c) 2019 spdlog authors Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||
|
||||
cmake_minimum_required(VERSION 3.2)
|
||||
|
||||
if(${CMAKE_VERSION} VERSION_LESS 3.11)
|
||||
cmake_policy(VERSION ${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION})
|
||||
else()
|
||||
cmake_policy(VERSION 3.11)
|
||||
endif()
|
||||
|
||||
enable_language(C)
|
||||
cmake_minimum_required(VERSION 3.10)
|
||||
|
||||
# ---------------------------------------------------------------------------------------
|
||||
# Start spdlog project
|
||||
@@ -23,6 +15,13 @@ message(STATUS "Build spdlog: ${SPDLOG_VERSION}")
|
||||
|
||||
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
|
||||
# ---------------------------------------------------------------------------------------
|
||||
@@ -38,9 +37,14 @@ if(NOT CMAKE_CXX_STANDARD)
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||
endif()
|
||||
|
||||
# make sure __cplusplus is defined when using msvc and enable parallel build
|
||||
if(MSVC)
|
||||
string(APPEND CMAKE_CXX_FLAGS " /Zc:__cplusplus /MP")
|
||||
endif()
|
||||
|
||||
set(CMAKE_CXX_EXTENSIONS OFF)
|
||||
|
||||
if(CMAKE_SYSTEM_NAME MATCHES "CYGWIN")
|
||||
if(CMAKE_SYSTEM_NAME MATCHES "CYGWIN" OR CMAKE_SYSTEM_NAME MATCHES "MSYS")
|
||||
set(CMAKE_CXX_EXTENSIONS ON)
|
||||
endif()
|
||||
|
||||
@@ -95,10 +99,15 @@ endif()
|
||||
if(WIN32)
|
||||
option(SPDLOG_WCHAR_SUPPORT "Support wchar api" 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()
|
||||
|
||||
if(${CMAKE_SYSTEM_NAME} STREQUAL "Linux")
|
||||
option(SPDLOG_CLOCK_COARSE
|
||||
"Use the much faster (but much less accurate) CLOCK_REALTIME_COARSE instead of the regular clock," OFF)
|
||||
option(SPDLOG_CLOCK_COARSE "Use CLOCK_REALTIME_COARSE instead of the regular clock," OFF)
|
||||
else()
|
||||
set(SPDLOG_CLOCK_COARSE OFF CACHE BOOL "non supported option" FORCE)
|
||||
endif()
|
||||
|
||||
option(SPDLOG_PREVENT_CHILD_FD "Prevent from child processes to inherit log file descriptors" OFF)
|
||||
@@ -108,6 +117,7 @@ option(
|
||||
SPDLOG_NO_ATOMIC_LEVELS
|
||||
"prevent spdlog from using of std::atomic log levels (use only if your code never modifies log levels concurrently"
|
||||
OFF)
|
||||
option(SPDLOG_DISABLE_DEFAULT_LOGGER "Disable default logger creation" OFF)
|
||||
|
||||
# clang-tidy
|
||||
if(${CMAKE_VERSION} VERSION_GREATER "3.5")
|
||||
@@ -131,16 +141,16 @@ if(NOT SPDLOG_FMT_EXTERNAL AND NOT SPDLOG_FMT_EXTERNAL_HO)
|
||||
list(APPEND SPDLOG_SRCS src/fmt.cpp)
|
||||
endif()
|
||||
|
||||
if(WIN32 AND SPDLOG_BUILD_SHARED)
|
||||
list(APPEND SPDLOG_SRCS ${CMAKE_CURRENT_BINARY_DIR}/version.rc)
|
||||
endif()
|
||||
|
||||
if(SPDLOG_BUILD_SHARED)
|
||||
if(SPDLOG_BUILD_SHARED OR BUILD_SHARED_LIBS)
|
||||
if(WIN32)
|
||||
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/cmake/version.rc.in ${CMAKE_CURRENT_BINARY_DIR}/version.rc @ONLY)
|
||||
list(APPEND SPDLOG_SRCS ${CMAKE_CURRENT_BINARY_DIR}/version.rc)
|
||||
endif()
|
||||
add_library(spdlog SHARED ${SPDLOG_SRCS} ${SPDLOG_ALL_HEADERS})
|
||||
target_compile_definitions(spdlog PUBLIC SPDLOG_SHARED_LIB)
|
||||
if(WIN32)
|
||||
target_compile_options(spdlog PUBLIC /wd4251 /wd4275)
|
||||
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/cmake/version.rc.in ${CMAKE_CURRENT_BINARY_DIR}/version.rc @ONLY)
|
||||
if(MSVC)
|
||||
target_compile_options(spdlog PUBLIC $<$<AND:$<CXX_COMPILER_ID:MSVC>,$<NOT:$<COMPILE_LANGUAGE:CUDA>>>:/wd4251
|
||||
/wd4275>)
|
||||
endif()
|
||||
if(NOT SPDLOG_FMT_EXTERNAL AND NOT SPDLOG_FMT_EXTERNAL_HO)
|
||||
target_compile_definitions(spdlog PRIVATE FMT_EXPORT PUBLIC FMT_SHARED)
|
||||
@@ -180,7 +190,7 @@ target_link_libraries(spdlog_header_only INTERFACE Threads::Threads)
|
||||
# ---------------------------------------------------------------------------------------
|
||||
if(SPDLOG_FMT_EXTERNAL OR SPDLOG_FMT_EXTERNAL_HO)
|
||||
if(NOT TARGET fmt::fmt)
|
||||
find_package(fmt 5.3.0 REQUIRED)
|
||||
find_package(fmt CONFIG REQUIRED)
|
||||
endif()
|
||||
target_compile_definitions(spdlog PUBLIC SPDLOG_FMT_EXTERNAL)
|
||||
target_compile_definitions(spdlog_header_only INTERFACE SPDLOG_FMT_EXTERNAL)
|
||||
@@ -197,52 +207,37 @@ if(SPDLOG_FMT_EXTERNAL OR SPDLOG_FMT_EXTERNAL_HO)
|
||||
set(PKG_CONFIG_REQUIRES fmt) # add dependency to pkg-config
|
||||
endif()
|
||||
|
||||
# ---------------------------------------------------------------------------------------
|
||||
# Add required libraries for Android CMake build
|
||||
# ---------------------------------------------------------------------------------------
|
||||
if(ANDROID)
|
||||
target_link_libraries(spdlog PUBLIC log)
|
||||
target_link_libraries(spdlog_header_only INTERFACE log)
|
||||
endif()
|
||||
|
||||
# ---------------------------------------------------------------------------------------
|
||||
# Misc definitions according to tweak options
|
||||
# ---------------------------------------------------------------------------------------
|
||||
if(SPDLOG_WCHAR_SUPPORT)
|
||||
target_compile_definitions(spdlog PUBLIC SPDLOG_WCHAR_TO_UTF8_SUPPORT)
|
||||
target_compile_definitions(spdlog_header_only INTERFACE SPDLOG_WCHAR_TO_UTF8_SUPPORT)
|
||||
endif()
|
||||
|
||||
if(SPDLOG_WCHAR_FILENAMES)
|
||||
target_compile_definitions(spdlog PUBLIC SPDLOG_WCHAR_FILENAMES)
|
||||
target_compile_definitions(spdlog_header_only INTERFACE SPDLOG_WCHAR_FILENAMES)
|
||||
endif()
|
||||
|
||||
if(SPDLOG_NO_EXCEPTIONS)
|
||||
target_compile_definitions(spdlog PUBLIC SPDLOG_NO_EXCEPTIONS)
|
||||
|
||||
target_compile_definitions(spdlog_header_only INTERFACE SPDLOG_NO_EXCEPTIONS)
|
||||
|
||||
if(NOT MSVC)
|
||||
target_compile_options(spdlog PRIVATE -fno-exceptions)
|
||||
set(SPDLOG_WCHAR_TO_UTF8_SUPPORT ${SPDLOG_WCHAR_SUPPORT})
|
||||
foreach(
|
||||
SPDLOG_OPTION
|
||||
SPDLOG_WCHAR_TO_UTF8_SUPPORT
|
||||
SPDLOG_WCHAR_FILENAMES
|
||||
SPDLOG_NO_EXCEPTIONS
|
||||
SPDLOG_CLOCK_COARSE
|
||||
SPDLOG_PREVENT_CHILD_FD
|
||||
SPDLOG_NO_THREAD_ID
|
||||
SPDLOG_NO_TLS
|
||||
SPDLOG_NO_ATOMIC_LEVELS
|
||||
SPDLOG_DISABLE_DEFAULT_LOGGER)
|
||||
if(${SPDLOG_OPTION})
|
||||
target_compile_definitions(spdlog PUBLIC ${SPDLOG_OPTION})
|
||||
target_compile_definitions(spdlog_header_only INTERFACE ${SPDLOG_OPTION})
|
||||
endif()
|
||||
endif()
|
||||
endforeach()
|
||||
|
||||
if(SPDLOG_CLOCK_COARSE)
|
||||
target_compile_definitions(spdlog PRIVATE SPDLOG_CLOCK_COARSE)
|
||||
target_compile_definitions(spdlog_header_only INTERFACE SPDLOG_CLOCK_COARSE)
|
||||
endif()
|
||||
|
||||
if(SPDLOG_PREVENT_CHILD_FD)
|
||||
target_compile_definitions(spdlog PRIVATE SPDLOG_PREVENT_CHILD_FD)
|
||||
target_compile_definitions(spdlog_header_only INTERFACE SPDLOG_PREVENT_CHILD_FD)
|
||||
endif()
|
||||
|
||||
if(SPDLOG_NO_THREAD_ID)
|
||||
target_compile_definitions(spdlog PRIVATE SPDLOG_NO_THREAD_ID)
|
||||
target_compile_definitions(spdlog_header_only INTERFACE SPDLOG_NO_THREAD_ID)
|
||||
endif()
|
||||
|
||||
if(SPDLOG_NO_TLS)
|
||||
target_compile_definitions(spdlog PRIVATE SPDLOG_NO_TLS)
|
||||
target_compile_definitions(spdlog_header_only INTERFACE SPDLOG_NO_TLS)
|
||||
endif()
|
||||
|
||||
if(SPDLOG_NO_ATOMIC_LEVELS)
|
||||
target_compile_definitions(spdlog PUBLIC SPDLOG_NO_ATOMIC_LEVELS)
|
||||
target_compile_definitions(spdlog_header_only INTERFACE SPDLOG_NO_ATOMIC_LEVELS)
|
||||
if(SPDLOG_NO_EXCEPTIONS AND NOT MSVC)
|
||||
target_compile_options(spdlog PRIVATE -fno-exceptions)
|
||||
endif()
|
||||
|
||||
# ---------------------------------------------------------------------------------------
|
||||
@@ -312,7 +307,8 @@ if(SPDLOG_INSTALL)
|
||||
install(EXPORT spdlog DESTINATION ${export_dest_dir} NAMESPACE spdlog:: FILE ${config_targets_file})
|
||||
|
||||
include(CMakePackageConfigHelpers)
|
||||
configure_file("${project_config_in}" "${project_config_out}" @ONLY)
|
||||
configure_package_config_file("${project_config_in}" "${project_config_out}"
|
||||
INSTALL_DESTINATION ${export_dest_dir})
|
||||
|
||||
write_basic_package_version_file("${version_config_file}" COMPATIBILITY SameMajorVersion)
|
||||
install(FILES "${project_config_out}" "${version_config_file}" DESTINATION "${export_dest_dir}")
|
||||
|
40
README.md
40
README.md
@@ -1,10 +1,10 @@
|
||||
# spdlog
|
||||
|
||||
Very fast, header-only/compiled, C++ logging library. [](https://travis-ci.org/gabime/spdlog) [](https://ci.appveyor.com/project/gabime/spdlog) [](https://github.com/gabime/spdlog/releases/latest)
|
||||
Very fast, header-only/compiled, C++ logging library. [](https://travis-ci.com/gabime/spdlog) [](https://ci.appveyor.com/project/gabime/spdlog) [](https://github.com/gabime/spdlog/releases/latest)
|
||||
|
||||
## Install
|
||||
#### Header only version
|
||||
Copy the source [folder](https://github.com/gabime/spdlog/tree/v1.x/include/spdlog) to your build tree and use a C++11 compiler.
|
||||
Copy the include [folder](https://github.com/gabime/spdlog/tree/v1.x/include/spdlog) to your build tree and use a C++11 compiler.
|
||||
|
||||
#### Static lib version (recommended - much faster compile times)
|
||||
```console
|
||||
@@ -25,12 +25,14 @@ $ cmake .. && make -j
|
||||
* Homebrew: `brew install spdlog`
|
||||
* MacPorts: `sudo port install spdlog`
|
||||
* FreeBSD: `cd /usr/ports/devel/spdlog/ && make install clean`
|
||||
* Fedora: `yum install spdlog`
|
||||
* Fedora: `dnf install spdlog`
|
||||
* Gentoo: `emerge dev-libs/spdlog`
|
||||
* Arch Linux: `pacman -S spdlog`
|
||||
* vcpkg: `vcpkg install spdlog`
|
||||
* conan: `spdlog/[>=1.4.1]`
|
||||
* conda: `conda install -c conda-forge spdlog`
|
||||
* build2: ```depends: spdlog ^1.8.2```
|
||||
|
||||
|
||||
|
||||
## Features
|
||||
@@ -45,8 +47,9 @@ $ cmake .. && make -j
|
||||
* Daily log files.
|
||||
* Console logging (colors supported).
|
||||
* syslog.
|
||||
* Windows debugger (```OutputDebugString(..)```)
|
||||
* Easily extendable with custom log targets (just implement a single function in the [sink](include/spdlog/sinks/sink.h) interface).
|
||||
* Windows event log.
|
||||
* Windows debugger (```OutputDebugString(..)```).
|
||||
* 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.
|
||||
* 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.
|
||||
@@ -56,7 +59,6 @@ $ cmake .. && make -j
|
||||
#### Basic usage
|
||||
```c++
|
||||
#include "spdlog/spdlog.h"
|
||||
#include "spdlog/sinks/basic_file_sink.h"
|
||||
|
||||
int main()
|
||||
{
|
||||
@@ -79,10 +81,6 @@ int main()
|
||||
// define SPDLOG_ACTIVE_LEVEL to desired level
|
||||
SPDLOG_TRACE("Some trace message with param {}", 42);
|
||||
SPDLOG_DEBUG("Some debug message");
|
||||
|
||||
// Set the default logger to file logger
|
||||
auto file_logger = spdlog::basic_logger_mt("basic_logger", "logs/basic.txt");
|
||||
spdlog::set_default_logger(file_logger);
|
||||
}
|
||||
|
||||
```
|
||||
@@ -145,8 +143,9 @@ void daily_example()
|
||||
---
|
||||
#### Backtrace support
|
||||
```c++
|
||||
// Loggers can store in a ring buffer all messages (including debug/trace) and display later on demand.
|
||||
// When needed, call dump_backtrace() to see them
|
||||
// Debug messages can be stored in a ring buffer instead of being logged immediately.
|
||||
// This is useful in order to display debug logs only when really needed (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.
|
||||
// or my_logger->enable_backtrace(32)..
|
||||
@@ -169,6 +168,20 @@ spdlog::flush_every(std::chrono::seconds(3));
|
||||
|
||||
```
|
||||
|
||||
---
|
||||
#### Stopwatch
|
||||
```c++
|
||||
// Stopwatch support for spdlog
|
||||
#include "spdlog/stopwatch.h"
|
||||
void stopwatch_example()
|
||||
{
|
||||
spdlog::stopwatch sw;
|
||||
spdlog::debug("Elapsed {}", sw);
|
||||
spdlog::debug("Elapsed {:.3}", sw);
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
---
|
||||
#### Log binary data in hex
|
||||
```c++
|
||||
@@ -179,6 +192,7 @@ spdlog::flush_every(std::chrono::seconds(3));
|
||||
// {:s} - don't separate each byte with space.
|
||||
// {:p} - don't print the position on each line start.
|
||||
// {:n} - don't split the output to lines.
|
||||
// {:a} - show ASCII if :n is not set.
|
||||
|
||||
#include "spdlog/fmt/bin_to_hex.h"
|
||||
|
||||
@@ -418,6 +432,6 @@ Documentation can be found in the [wiki](https://github.com/gabime/spdlog/wiki/1
|
||||
|
||||
---
|
||||
|
||||
Thanks to [JetBrains](https://www.jetbrains.com/?from=spdlog) for donating licenses to their products to help developing **spdlog** <a href="https://www.jetbrains.com/?from=spdlog"><img src="logos/jetbrains-variant-4.svg" width="94" align="center" /></a>
|
||||
Thanks to [JetBrains](https://www.jetbrains.com/?from=spdlog) for donating product licenses to help develop **spdlog** <a href="https://www.jetbrains.com/?from=spdlog"><img src="logos/jetbrains-variant-4.svg" width="94" align="center" /></a>
|
||||
|
||||
|
||||
|
43
appveyor.yml
43
appveyor.yml
@@ -4,32 +4,59 @@ environment:
|
||||
matrix:
|
||||
- GENERATOR: '"Visual Studio 14 2015"'
|
||||
BUILD_TYPE: Debug
|
||||
WCHAR: 'OFF'
|
||||
BUILD_SHARED: 'OFF'
|
||||
WCHAR: 'OFF'
|
||||
WCHAR_FILES: 'OFF'
|
||||
BUILD_EXAMPLE: 'ON'
|
||||
- GENERATOR: '"Visual Studio 14 2015"'
|
||||
BUILD_TYPE: Release
|
||||
WCHAR: 'ON'
|
||||
BUILD_SHARED: 'OFF'
|
||||
WCHAR: 'ON'
|
||||
WCHAR_FILES: 'OFF'
|
||||
BUILD_EXAMPLE: 'ON'
|
||||
- GENERATOR: '"Visual Studio 14 2015 Win64"'
|
||||
BUILD_TYPE: Debug
|
||||
WCHAR: 'ON'
|
||||
BUILD_SHARED: 'OFF'
|
||||
WCHAR: 'ON'
|
||||
WCHAR_FILES: 'OFF'
|
||||
BUILD_EXAMPLE: 'ON'
|
||||
- GENERATOR: '"Visual Studio 14 2015 Win64"'
|
||||
BUILD_TYPE: Release
|
||||
WCHAR: 'ON'
|
||||
BUILD_SHARED: 'OFF'
|
||||
WCHAR: 'ON'
|
||||
WCHAR_FILES: 'OFF'
|
||||
BUILD_EXAMPLE: 'ON'
|
||||
- GENERATOR: '"Visual Studio 15 2017 Win64"'
|
||||
BUILD_TYPE: Debug
|
||||
BUILD_SHARED: 'OFF'
|
||||
WCHAR: 'ON'
|
||||
BUILD_SHARED: 'OFF'
|
||||
WCHAR_FILES: 'OFF'
|
||||
BUILD_EXAMPLE: 'ON'
|
||||
- GENERATOR: '"Visual Studio 15 2017 Win64"'
|
||||
BUILD_TYPE: Release
|
||||
WCHAR: 'OFF'
|
||||
BUILD_SHARED: 'OFF'
|
||||
WCHAR: 'OFF'
|
||||
WCHAR_FILES: 'OFF'
|
||||
BUILD_EXAMPLE: 'ON'
|
||||
- GENERATOR: '"Visual Studio 15 2017 Win64"'
|
||||
BUILD_TYPE: Release
|
||||
WCHAR: 'OFF'
|
||||
BUILD_SHARED: 'ON'
|
||||
WCHAR: 'OFF'
|
||||
WCHAR_FILES: 'OFF'
|
||||
BUILD_EXAMPLE: 'ON'
|
||||
- GENERATOR: '"Visual Studio 15 2017 Win64"'
|
||||
BUILD_TYPE: Release
|
||||
BUILD_SHARED: 'ON'
|
||||
WCHAR: 'ON'
|
||||
WCHAR_FILES: 'ON'
|
||||
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:
|
||||
- cmd: >-
|
||||
set
|
||||
@@ -40,7 +67,7 @@ build_script:
|
||||
|
||||
set PATH=%PATH%;C:\Program Files\Git\usr\bin
|
||||
|
||||
cmake .. -G %GENERATOR% -DCMAKE_BUILD_TYPE=%BUILD_TYPE% -DSPDLOG_WCHAR_SUPPORT=%WCHAR% -DSPDLOG_BUILD_SHARED=%BUILD_SHARED% -DSPDLOG_BUILD_EXAMPLE=ON -DSPDLOG_BUILD_EXAMPLE_HO=ON -DSPDLOG_BUILD_TESTS=ON -DSPDLOG_BUILD_TESTS_HO=OFF -DSPDLOG_ENABLE_WARNINGS=ON
|
||||
cmake -G %GENERATOR% -D CMAKE_BUILD_TYPE=%BUILD_TYPE% -D BUILD_SHARED_LIBS=%BUILD_SHARED% -D SPDLOG_WCHAR_SUPPORT=%WCHAR% -D SPDLOG_WCHAR_FILENAMES=%WCHAR_FILES% -D SPDLOG_BUILD_EXAMPLE=%BUILD_EXAMPLE% -D SPDLOG_BUILD_EXAMPLE_HO=%BUILD_EXAMPLE% -D SPDLOG_BUILD_TESTS=ON -D SPDLOG_BUILD_TESTS_HO=OFF -D SPDLOG_BUILD_WARNINGS=ON ..
|
||||
|
||||
cmake --build . --config %BUILD_TYPE%
|
||||
|
||||
|
@@ -1,6 +1,6 @@
|
||||
# Copyright(c) 2019 spdlog authors Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||
|
||||
cmake_minimum_required(VERSION 3.1)
|
||||
cmake_minimum_required(VERSION 3.10)
|
||||
project(spdlog_bench CXX)
|
||||
|
||||
if(NOT TARGET spdlog)
|
||||
@@ -9,7 +9,22 @@ if(NOT TARGET spdlog)
|
||||
endif()
|
||||
|
||||
find_package(Threads REQUIRED)
|
||||
find_package(benchmark CONFIG REQUIRED)
|
||||
find_package(benchmark CONFIG)
|
||||
if(NOT benchmark_FOUND)
|
||||
message(STATUS "Using CMake Version ${CMAKE_VERSION}")
|
||||
if(${CMAKE_VERSION} VERSION_GREATER_EQUAL "3.11.0")
|
||||
# User can fetch googlebenchmark
|
||||
message(STATUS "Downloading GoogleBenchmark")
|
||||
include(FetchContent)
|
||||
set(BENCHMARK_ENABLE_GTEST_TESTS OFF CACHE INTERNAL "")
|
||||
# Do not build and run googlebenchmark tests
|
||||
FetchContent_Declare(googlebenchmark GIT_REPOSITORY https://github.com/google/benchmark.git GIT_TAG v1.5.2)
|
||||
|
||||
FetchContent_MakeAvailable(googlebenchmark)
|
||||
else()
|
||||
message(FATAL_ERROR "GoogleBenchmark is missing. Use CMake >= 3.11 or download it")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
add_executable(bench bench.cpp)
|
||||
spdlog_enable_warnings(bench)
|
||||
|
@@ -9,7 +9,12 @@
|
||||
#include "spdlog/spdlog.h"
|
||||
#include "spdlog/async.h"
|
||||
#include "spdlog/sinks/basic_file_sink.h"
|
||||
#include "spdlog/sinks/stdout_color_sinks.h"
|
||||
|
||||
#ifdef SPDLOG_FMT_EXTERNAL
|
||||
# include <fmt/format.h>
|
||||
#else
|
||||
# include "spdlog/fmt/bundled/format.h"
|
||||
#endif
|
||||
|
||||
#include "utils.h"
|
||||
#include <atomic>
|
||||
@@ -27,9 +32,9 @@ using namespace utils;
|
||||
void bench_mt(int howmany, std::shared_ptr<spdlog::logger> log, int thread_count);
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(push)
|
||||
#pragma warning(disable : 4996) // disable fopen warning under msvc
|
||||
#endif // _MSC_VER
|
||||
# pragma warning(push)
|
||||
# pragma warning(disable : 4996) // disable fopen warning under msvc
|
||||
#endif // _MSC_VER
|
||||
|
||||
int count_lines(const char *filename)
|
||||
{
|
||||
@@ -48,18 +53,18 @@ int count_lines(const char *filename)
|
||||
|
||||
void verify_file(const char *filename, int expected_count)
|
||||
{
|
||||
spdlog::info("Verifying {} to contain {:n} line..", filename, expected_count);
|
||||
spdlog::info("Verifying {} to contain {} line..", filename, expected_count);
|
||||
auto count = count_lines(filename);
|
||||
if (count != expected_count)
|
||||
{
|
||||
spdlog::error("Test failed. {} has {:n} lines instead of {:n}", filename, count, expected_count);
|
||||
spdlog::error("Test failed. {} has {} lines instead of {}", filename, count, expected_count);
|
||||
exit(1);
|
||||
}
|
||||
spdlog::info("Line count OK ({:n})\n", count);
|
||||
spdlog::info("Line count OK ({})\n", count);
|
||||
}
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(pop)
|
||||
# pragma warning(pop)
|
||||
#endif
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
@@ -98,11 +103,11 @@ int main(int argc, char *argv[])
|
||||
|
||||
auto slot_size = sizeof(spdlog::details::async_msg);
|
||||
spdlog::info("-------------------------------------------------");
|
||||
spdlog::info("Messages : {:n}", howmany);
|
||||
spdlog::info("Threads : {:n}", threads);
|
||||
spdlog::info("Queue : {:n} slots", queue_size);
|
||||
spdlog::info("Queue memory : {:n} x {} = {:n} KB ", queue_size, slot_size, (queue_size * slot_size) / 1024);
|
||||
spdlog::info("Total iters : {:n}", iters);
|
||||
spdlog::info("Messages : {:L}", howmany);
|
||||
spdlog::info("Threads : {:L}", threads);
|
||||
spdlog::info("Queue : {:L} slots", queue_size);
|
||||
spdlog::info("Queue memory : {:L} x {:L} = {:L} KB ", queue_size, slot_size, (queue_size * slot_size) / 1024);
|
||||
spdlog::info("Total iters : {:L}", iters);
|
||||
spdlog::info("-------------------------------------------------");
|
||||
|
||||
const char *filename = "logs/basic_async.log";
|
||||
@@ -175,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_d = duration_cast<duration<double>>(delta).count();
|
||||
spdlog::info("Elapsed: {} secs\t {:n}/sec", delta_d, int(howmany / delta_d));
|
||||
spdlog::info("Elapsed: {} secs\t {:L}/sec", delta_d, int(howmany / delta_d));
|
||||
}
|
||||
|
@@ -12,6 +12,12 @@
|
||||
#include "spdlog/sinks/null_sink.h"
|
||||
#include "spdlog/sinks/rotating_file_sink.h"
|
||||
|
||||
#ifdef SPDLOG_FMT_EXTERNAL
|
||||
# include <fmt/locale.h>
|
||||
#else
|
||||
# include "spdlog/fmt/bundled/format.h"
|
||||
#endif
|
||||
|
||||
#include "utils.h"
|
||||
#include <atomic>
|
||||
#include <cstdlib> // EXIT_FAILURE
|
||||
@@ -32,7 +38,7 @@ static const int max_threads = 1000;
|
||||
void bench_threaded_logging(size_t threads, int iters)
|
||||
{
|
||||
spdlog::info("**************************************************************");
|
||||
spdlog::info("Multi threaded: {:n} threads, {:n} messages", threads, iters);
|
||||
spdlog::info(fmt::format(std::locale("en_US.UTF-8"), "Multi threaded: {:L} threads, {:L} messages", threads, iters));
|
||||
spdlog::info("**************************************************************");
|
||||
|
||||
auto basic_mt = spdlog::basic_logger_mt("basic_mt", "logs/basic_mt.log", true);
|
||||
@@ -68,7 +74,7 @@ void bench_threaded_logging(size_t threads, int iters)
|
||||
void bench_single_threaded(int iters)
|
||||
{
|
||||
spdlog::info("**************************************************************");
|
||||
spdlog::info("Single threaded: {:n} messages", iters);
|
||||
spdlog::info(fmt::format(std::locale("en_US.UTF-8"), "Single threaded: {} messages", iters));
|
||||
spdlog::info("**************************************************************");
|
||||
|
||||
auto basic_st = spdlog::basic_logger_st("basic_st", "logs/basic_st.log", true);
|
||||
@@ -122,7 +128,7 @@ int main(int argc, char *argv[])
|
||||
|
||||
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);
|
||||
@@ -152,7 +158,8 @@ void bench(int howmany, std::shared_ptr<spdlog::logger> log)
|
||||
auto delta = high_resolution_clock::now() - start;
|
||||
auto delta_d = duration_cast<duration<double>>(delta).count();
|
||||
|
||||
spdlog::info("{:<30} Elapsed: {:0.2f} secs {:>16n}/sec", log->name(), delta_d, int(howmany / delta_d));
|
||||
spdlog::info(
|
||||
fmt::format(std::locale("en_US.UTF-8"), "{:<30} Elapsed: {:0.2f} secs {:>16L}/sec", log->name(), delta_d, int(howmany / delta_d)));
|
||||
spdlog::drop(log->name());
|
||||
}
|
||||
|
||||
@@ -182,7 +189,8 @@ void bench_mt(int howmany, std::shared_ptr<spdlog::logger> log, size_t thread_co
|
||||
|
||||
auto delta = high_resolution_clock::now() - start;
|
||||
auto delta_d = duration_cast<duration<double>>(delta).count();
|
||||
spdlog::info("{:<30} Elapsed: {:0.2f} secs {:>16n}/sec", log->name(), delta_d, int(howmany / delta_d));
|
||||
spdlog::info(
|
||||
fmt::format(std::locale("en_US.UTF-8"), "{:<30} Elapsed: {:0.2f} secs {:>16L}/sec", log->name(), delta_d, int(howmany / delta_d)));
|
||||
spdlog::drop(log->name());
|
||||
}
|
||||
|
||||
@@ -205,7 +213,7 @@ void bench_default_api(int howmany, std::shared_ptr<spdlog::logger> log)
|
||||
auto delta_d = duration_cast<duration<double>>(delta).count();
|
||||
spdlog::drop(log->name());
|
||||
spdlog::set_default_logger(std::move(orig_default));
|
||||
spdlog::info("{:<30} Elapsed: {:0.2f} secs {:>16n}/sec", log->name(), delta_d, int(howmany / delta_d));
|
||||
spdlog::info("{:<30} Elapsed: {:0.2f} secs {:>16}/sec", log->name(), delta_d, int(howmany / delta_d));
|
||||
}
|
||||
|
||||
void bench_c_string(int howmany, std::shared_ptr<spdlog::logger> log)
|
||||
@@ -232,7 +240,7 @@ void bench_c_string(int howmany, std::shared_ptr<spdlog::logger> log)
|
||||
auto delta_d = duration_cast<duration<double>>(delta).count();
|
||||
spdlog::drop(log->name());
|
||||
spdlog::set_default_logger(std::move(orig_default));
|
||||
spdlog::info("{:<30} Elapsed: {:0.2f} secs {:>16n}/sec", log->name(), delta_d, int(howmany / delta_d));
|
||||
spdlog::info("{:<30} Elapsed: {:0.2f} secs {:>16}/sec", log->name(), delta_d, int(howmany / delta_d));
|
||||
}
|
||||
|
||||
*/
|
@@ -34,14 +34,14 @@ void bench_formatters()
|
||||
for (auto &flag : all_flags)
|
||||
{
|
||||
auto pattern = std::string("%") + flag;
|
||||
benchmark::RegisterBenchmark(pattern.c_str(), bench_formatter, pattern);
|
||||
benchmark::RegisterBenchmark(pattern.c_str(), &bench_formatter, pattern);
|
||||
|
||||
// pattern = std::string("%16") + flag;
|
||||
// benchmark::RegisterBenchmark(pattern.c_str(), bench_formatter, pattern);
|
||||
// benchmark::RegisterBenchmark(pattern.c_str(), &bench_formatter, pattern);
|
||||
//
|
||||
// // bench center padding
|
||||
// pattern = std::string("%=16") + flag;
|
||||
// benchmark::RegisterBenchmark(pattern.c_str(), bench_formatter, pattern);
|
||||
// benchmark::RegisterBenchmark(pattern.c_str(), &bench_formatter, pattern);
|
||||
}
|
||||
|
||||
// complex patterns
|
||||
@@ -52,7 +52,7 @@ void bench_formatters()
|
||||
};
|
||||
for (auto &pattern : patterns)
|
||||
{
|
||||
benchmark::RegisterBenchmark(pattern.c_str(), bench_formatter, pattern)->Iterations(2500000);
|
||||
benchmark::RegisterBenchmark(pattern.c_str(), &bench_formatter, pattern)->Iterations(2500000);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -73,7 +73,7 @@ int main(int argc, char *argv[])
|
||||
}
|
||||
else
|
||||
{
|
||||
benchmark::RegisterBenchmark(pattern.c_str(), bench_formatter, pattern);
|
||||
benchmark::RegisterBenchmark(pattern.c_str(), &bench_formatter, pattern);
|
||||
}
|
||||
benchmark::Initialize(&argc, argv);
|
||||
benchmark::RunSpecifiedBenchmarks();
|
||||
|
@@ -39,6 +39,16 @@ void bench_logger(benchmark::State &state, std::shared_ptr<spdlog::logger> logge
|
||||
}
|
||||
}
|
||||
|
||||
void bench_logger_fmt_string(benchmark::State &state, std::shared_ptr<spdlog::logger> logger)
|
||||
{
|
||||
int i = 0;
|
||||
for (auto _ : state)
|
||||
{
|
||||
logger->info(FMT_STRING("Hello logger: msg number {}..............."), ++i);
|
||||
;
|
||||
}
|
||||
}
|
||||
|
||||
void bench_disabled_macro(benchmark::State &state, std::shared_ptr<spdlog::logger> logger)
|
||||
{
|
||||
int i = 0;
|
||||
@@ -88,6 +98,7 @@ int main(int argc, char *argv[])
|
||||
auto null_logger_st = std::make_shared<spdlog::logger>("bench", std::make_shared<null_sink_st>());
|
||||
benchmark::RegisterBenchmark("null_sink_st (500_bytes c_str)", bench_c_string, std::move(null_logger_st));
|
||||
benchmark::RegisterBenchmark("null_sink_st", bench_logger, null_logger_st);
|
||||
benchmark::RegisterBenchmark("null_sink_fmt_string", bench_logger_fmt_string, null_logger_st);
|
||||
// with backtrace of 64
|
||||
auto tracing_null_logger_st = std::make_shared<spdlog::logger>("bench", std::make_shared<null_sink_st>());
|
||||
tracing_null_logger_st->enable_backtrace(64);
|
||||
|
@@ -18,23 +18,37 @@ set(CPACK_PACKAGE_RELOCATABLE ON CACHE BOOL "Build relocatable package")
|
||||
|
||||
set(CPACK_RPM_PACKAGE_LICENSE "MIT")
|
||||
set(CPACK_RPM_PACKAGE_GROUP "Development/Libraries")
|
||||
set(CPACK_DEBIAN_PACKAGE_SECTION "libs")
|
||||
set(CPACK_RPM_PACKAGE_URL ${CPACK_PROJECT_URL})
|
||||
set(CPACK_DEBIAN_PACKAGE_HOMEPAGE ${CPACK_PROJECT_URL})
|
||||
set(CPACK_RPM_PACKAGE_DESCRIPTION "Very fast, header-only/compiled, C++ logging library.")
|
||||
set(CPACK_DEBIAN_PACKAGE_DESCRIPTION "Very fast, header-only/compiled, C++ logging library.")
|
||||
|
||||
if(CPACK_PACKAGE_NAME)
|
||||
set(CPACK_RPM_FILE_NAME "${CPACK_PACKAGE_NAME}-${CPACK_PACKAGE_VERSION}")
|
||||
set(CPACK_DEBIAN_FILE_NAME "${CPACK_PACKAGE_NAME}-${CPACK_PACKAGE_VERSION}")
|
||||
else()
|
||||
set(CPACK_RPM_FILE_NAME "${PROJECT_NAME}-${CPACK_PACKAGE_VERSION}")
|
||||
set(CPACK_DEBIAN_FILE_NAME "${PROJECT_NAME}-${CPACK_PACKAGE_VERSION}")
|
||||
set(CPACK_RPM_PACKAGE_NAME "${PROJECT_NAME}")
|
||||
set(CPACK_DEBIAN_PACKAGE_NAME "${PROJECT_NAME}")
|
||||
endif()
|
||||
|
||||
if(CPACK_RPM_PACKAGE_RELEASE)
|
||||
set(CPACK_RPM_FILE_NAME "${CPACK_RPM_FILE_NAME}-${CPACK_RPM_PACKAGE_RELEASE}")
|
||||
endif()
|
||||
if(CPACK_DEBIAN_PACKAGE_RELEASE)
|
||||
set(CPACK_DEBIAN_FILE_NAME "${CPACK_DEBIAN_FILE_NAME}-${CPACK_DEBIAN_PACKAGE_RELEASE}")
|
||||
endif()
|
||||
|
||||
if(CPACK_RPM_PACKAGE_ARCHITECTURE)
|
||||
set(CPACK_RPM_FILE_NAME "${CPACK_RPM_FILE_NAME}.${CPACK_RPM_PACKAGE_ARCHITECTURE}")
|
||||
endif()
|
||||
if(CPACK_DEBIAN_PACKAGE_ARCHITECTURE)
|
||||
set(CPACK_DEBIAN_FILE_NAME "${CPACK_DEBIAN_FILE_NAME}.${CPACK_DEBIAN_PACKAGE_ARCHITECTURE}")
|
||||
endif()
|
||||
set(CPACK_RPM_FILE_NAME "${CPACK_RPM_FILE_NAME}.rpm")
|
||||
set(CPACK_DEBIAN_FILE_NAME "${CPACK_DEBIAN_FILE_NAME}.deb")
|
||||
|
||||
if(NOT CPACK_PACKAGE_RELOCATABLE)
|
||||
# Depend on pkgconfig rpm to create the system pkgconfig folder
|
||||
|
@@ -1,6 +1,8 @@
|
||||
# Copyright(c) 2019 spdlog authors
|
||||
# Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||
|
||||
@PACKAGE_INIT@
|
||||
|
||||
find_package(Threads REQUIRED)
|
||||
|
||||
set(SPDLOG_FMT_EXTERNAL @SPDLOG_FMT_EXTERNAL@)
|
||||
@@ -13,3 +15,5 @@ endif()
|
||||
|
||||
|
||||
include("${CMAKE_CURRENT_LIST_DIR}/${config_targets_file}")
|
||||
|
||||
check_required_components(spdlog)
|
@@ -42,6 +42,7 @@ function(spdlog_enable_warnings target_name)
|
||||
-Wextra
|
||||
-Wconversion
|
||||
-pedantic
|
||||
-Werror
|
||||
-Wfatal-errors>
|
||||
$<$<CXX_COMPILER_ID:MSVC>:${MSVC_OPTIONS}>)
|
||||
endif()
|
||||
|
@@ -1,5 +1,5 @@
|
||||
#define APSTUDIO_READONLY_SYMBOLS
|
||||
#include "winres.h"
|
||||
#include <windows.h>
|
||||
#undef APSTUDIO_READONLY_SYMBOLS
|
||||
|
||||
LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
|
||||
|
@@ -1,6 +1,6 @@
|
||||
# Copyright(c) 2019 spdlog authors Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||
|
||||
cmake_minimum_required(VERSION 3.2)
|
||||
cmake_minimum_required(VERSION 3.10)
|
||||
project(spdlog_examples CXX)
|
||||
|
||||
if(NOT TARGET spdlog)
|
||||
|
@@ -13,6 +13,7 @@ void rotating_example();
|
||||
void daily_example();
|
||||
void async_example();
|
||||
void binary_example();
|
||||
void stopwatch_example();
|
||||
void trace_example();
|
||||
void multi_sink_example();
|
||||
void user_defined_example();
|
||||
@@ -21,7 +22,8 @@ void syslog_example();
|
||||
void custom_flags_example();
|
||||
|
||||
#include "spdlog/spdlog.h"
|
||||
#include "spdlog/cfg/env.h" // 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 *[])
|
||||
{
|
||||
@@ -71,6 +73,7 @@ int main(int, char *[])
|
||||
user_defined_example();
|
||||
err_handler_example();
|
||||
trace_example();
|
||||
stopwatch_example();
|
||||
custom_flags_example();
|
||||
|
||||
// Flush all *registered* loggers using a worker thread every 3 seconds.
|
||||
@@ -127,7 +130,7 @@ void daily_example()
|
||||
#include "spdlog/cfg/env.h"
|
||||
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::cfg::load_env_levels();
|
||||
// or from command line:
|
||||
@@ -192,6 +195,16 @@ void trace_example()
|
||||
SPDLOG_LOGGER_TRACE(logger, "another trace message");
|
||||
}
|
||||
|
||||
// stopwatch example
|
||||
#include "spdlog/stopwatch.h"
|
||||
#include <thread>
|
||||
void stopwatch_example()
|
||||
{
|
||||
spdlog::stopwatch sw;
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(123));
|
||||
spdlog::info("Stopwatch: {} seconds", sw);
|
||||
}
|
||||
|
||||
// A logger with multiple sinks (stdout and file) - each with a different format and log level.
|
||||
void multi_sink_example()
|
||||
{
|
||||
@@ -209,7 +222,6 @@ void multi_sink_example()
|
||||
}
|
||||
|
||||
// User defined types logging by implementing operator<<
|
||||
#include "spdlog/fmt/ostr.h" // must be included
|
||||
struct my_type
|
||||
{
|
||||
int i;
|
||||
@@ -234,7 +246,7 @@ void err_handler_example()
|
||||
|
||||
// syslog example (linux/osx/freebsd)
|
||||
#ifndef _WIN32
|
||||
#include "spdlog/sinks/syslog_sink.h"
|
||||
# include "spdlog/sinks/syslog_sink.h"
|
||||
void syslog_example()
|
||||
{
|
||||
std::string ident = "spdlog-example";
|
||||
@@ -245,7 +257,7 @@ void syslog_example()
|
||||
|
||||
// Android example.
|
||||
#if defined(__ANDROID__)
|
||||
#include "spdlog/sinks/android_sink.h"
|
||||
# include "spdlog/sinks/android_sink.h"
|
||||
void android_example()
|
||||
{
|
||||
std::string tag = "spdlog-android";
|
||||
|
@@ -35,7 +35,7 @@ template<async_overflow_policy OverflowPolicy = async_overflow_policy::block>
|
||||
struct async_factory_impl
|
||||
{
|
||||
template<typename Sink, typename... SinkArgs>
|
||||
static std::shared_ptr<async_logger> create(std::string logger_name, SinkArgs &&... args)
|
||||
static std::shared_ptr<async_logger> create(std::string logger_name, SinkArgs &&...args)
|
||||
{
|
||||
auto ®istry_inst = details::registry::instance();
|
||||
|
||||
@@ -46,7 +46,7 @@ struct async_factory_impl
|
||||
auto tp = registry_inst.get_tp();
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -61,13 +61,13 @@ using async_factory = async_factory_impl<async_overflow_policy::block>;
|
||||
using async_factory_nonblock = async_factory_impl<async_overflow_policy::overrun_oldest>;
|
||||
|
||||
template<typename Sink, typename... SinkArgs>
|
||||
inline std::shared_ptr<spdlog::logger> create_async(std::string logger_name, SinkArgs &&... sink_args)
|
||||
inline std::shared_ptr<spdlog::logger> create_async(std::string logger_name, SinkArgs &&...sink_args)
|
||||
{
|
||||
return async_factory::create<Sink>(std::move(logger_name), std::forward<SinkArgs>(sink_args)...);
|
||||
}
|
||||
|
||||
template<typename Sink, typename... SinkArgs>
|
||||
inline std::shared_ptr<spdlog::logger> create_async_nb(std::string logger_name, SinkArgs &&... sink_args)
|
||||
inline std::shared_ptr<spdlog::logger> create_async_nb(std::string logger_name, SinkArgs &&...sink_args)
|
||||
{
|
||||
return async_factory_nonblock::create<Sink>(std::move(logger_name), std::forward<SinkArgs>(sink_args)...);
|
||||
}
|
||||
|
@@ -4,7 +4,7 @@
|
||||
#pragma once
|
||||
|
||||
#ifndef SPDLOG_HEADER_ONLY
|
||||
#include <spdlog/async_logger.h>
|
||||
# include <spdlog/async_logger.h>
|
||||
#endif
|
||||
|
||||
#include <spdlog/sinks/sink.h>
|
||||
|
@@ -64,5 +64,5 @@ private:
|
||||
} // namespace spdlog
|
||||
|
||||
#ifdef SPDLOG_HEADER_ONLY
|
||||
#include "async_logger-inl.h"
|
||||
# include "async_logger-inl.h"
|
||||
#endif
|
||||
|
@@ -21,7 +21,7 @@ namespace spdlog {
|
||||
namespace cfg {
|
||||
|
||||
// search for SPDLOG_LEVEL= in the args and use it to init the levels
|
||||
void load_argv_levels(int argc, const char **argv)
|
||||
inline void load_argv_levels(int argc, const char **argv)
|
||||
{
|
||||
const std::string spdlog_level_prefix = "SPDLOG_LEVEL=";
|
||||
for (int i = 1; i < argc; i++)
|
||||
@@ -30,13 +30,12 @@ void load_argv_levels(int argc, const char **argv)
|
||||
if (arg.find(spdlog_level_prefix) == 0)
|
||||
{
|
||||
auto levels_string = arg.substr(spdlog_level_prefix.size());
|
||||
auto levels = helpers::extract_levels(levels_string);
|
||||
details::registry::instance().update_levels(std::move(levels));
|
||||
helpers::load_levels(levels_string);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void load_argv_levels(int argc, char **argv)
|
||||
inline void load_argv_levels(int argc, char **argv)
|
||||
{
|
||||
load_argv_levels(argc, const_cast<const char **>(argv));
|
||||
}
|
||||
|
@@ -17,7 +17,7 @@
|
||||
// export SPDLOG_LEVEL=debug
|
||||
//
|
||||
// turn off all logging except for logger1:
|
||||
// export SPDLOG_LEVEL="off,logger1=debug"
|
||||
// export SPDLOG_LEVEL="*=off,logger1=debug"
|
||||
//
|
||||
|
||||
// turn off all logging except for logger1 and logger2:
|
||||
@@ -25,11 +25,13 @@
|
||||
|
||||
namespace spdlog {
|
||||
namespace cfg {
|
||||
void load_env_levels()
|
||||
inline void load_env_levels()
|
||||
{
|
||||
auto env_val = details::os::getenv("SPDLOG_LEVEL");
|
||||
auto levels = helpers::extract_levels(env_val);
|
||||
details::registry::instance().update_levels(std::move(levels));
|
||||
if (!env_val.empty())
|
||||
{
|
||||
helpers::load_levels(env_val);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace cfg
|
||||
|
@@ -4,13 +4,14 @@
|
||||
#pragma once
|
||||
|
||||
#ifndef SPDLOG_HEADER_ONLY
|
||||
#include <spdlog/cfg/helpers.h>
|
||||
# include <spdlog/cfg/helpers.h>
|
||||
#endif
|
||||
|
||||
#include <spdlog/spdlog.h>
|
||||
#include <spdlog/details/os.h>
|
||||
#include <spdlog/details/registry.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <sstream>
|
||||
@@ -78,24 +79,40 @@ inline std::unordered_map<std::string, std::string> extract_key_vals_(const std:
|
||||
return rv;
|
||||
}
|
||||
|
||||
SPDLOG_INLINE log_levels extract_levels(const std::string &input)
|
||||
SPDLOG_INLINE void load_levels(const std::string &input)
|
||||
{
|
||||
if (input.empty() || input.size() > 512)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
auto key_vals = extract_key_vals_(input);
|
||||
log_levels rv;
|
||||
std::unordered_map<std::string, level::level_enum> levels;
|
||||
level::level_enum global_level = level::info;
|
||||
bool global_level_found = false;
|
||||
|
||||
for (auto &name_level : key_vals)
|
||||
{
|
||||
auto &logger_name = name_level.first;
|
||||
auto level_name = to_lower_(name_level.second);
|
||||
auto level = level::from_str(level_name);
|
||||
// fallback to "info" if unrecognized level name
|
||||
// ignore unrecognized level names
|
||||
if (level == level::off && level_name != "off")
|
||||
{
|
||||
level = level::info;
|
||||
continue;
|
||||
}
|
||||
if (logger_name.empty()) // no logger name indicate global level
|
||||
{
|
||||
global_level_found = true;
|
||||
global_level = level;
|
||||
}
|
||||
else
|
||||
{
|
||||
levels[logger_name] = level;
|
||||
}
|
||||
rv.set(logger_name, level);
|
||||
}
|
||||
return rv;
|
||||
|
||||
details::registry::instance().set_levels(std::move(levels), global_level_found ? &global_level : nullptr);
|
||||
}
|
||||
|
||||
} // namespace helpers
|
||||
|
@@ -3,7 +3,8 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <spdlog/cfg/log_levels.h>
|
||||
#include <spdlog/common.h>
|
||||
#include <unordered_map>
|
||||
|
||||
namespace spdlog {
|
||||
namespace cfg {
|
||||
@@ -17,12 +18,12 @@ namespace helpers {
|
||||
// turn off all logging except for logger1: "off,logger1=debug"
|
||||
// turn off all logging except for logger1 and logger2: "off,logger1=debug,logger2=info"
|
||||
//
|
||||
SPDLOG_API log_levels extract_levels(const std::string &txt);
|
||||
SPDLOG_API void load_levels(const std::string &txt);
|
||||
} // namespace helpers
|
||||
|
||||
} // namespace cfg
|
||||
} // namespace spdlog
|
||||
|
||||
#ifdef SPDLOG_HEADER_ONLY
|
||||
#include "helpers-inl.h"
|
||||
# include "helpers-inl.h"
|
||||
#endif // SPDLOG_HEADER_ONLY
|
||||
|
@@ -1,47 +0,0 @@
|
||||
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
|
||||
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <spdlog/common.h>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
|
||||
namespace spdlog {
|
||||
namespace cfg {
|
||||
class log_levels
|
||||
{
|
||||
std::unordered_map<std::string, spdlog::level::level_enum> levels_;
|
||||
spdlog::level::level_enum default_level_ = level::info;
|
||||
|
||||
public:
|
||||
void set(const std::string &logger_name, level::level_enum lvl)
|
||||
{
|
||||
if (logger_name.empty())
|
||||
{
|
||||
default_level_ = lvl;
|
||||
}
|
||||
else
|
||||
{
|
||||
levels_[logger_name] = lvl;
|
||||
}
|
||||
}
|
||||
|
||||
void set_default(level::level_enum lvl)
|
||||
{
|
||||
default_level_ = lvl;
|
||||
}
|
||||
|
||||
level::level_enum get(const std::string &logger_name)
|
||||
{
|
||||
auto it = levels_.find(logger_name);
|
||||
return it != levels_.end() ? it->second : default_level_;
|
||||
}
|
||||
|
||||
level::level_enum default_level()
|
||||
{
|
||||
return default_level_;
|
||||
}
|
||||
};
|
||||
} // namespace cfg
|
||||
} // namespace spdlog
|
@@ -4,16 +4,23 @@
|
||||
#pragma once
|
||||
|
||||
#ifndef SPDLOG_HEADER_ONLY
|
||||
#include <spdlog/common.h>
|
||||
# include <spdlog/common.h>
|
||||
#endif
|
||||
|
||||
#include <algorithm>
|
||||
#include <iterator>
|
||||
|
||||
namespace spdlog {
|
||||
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;
|
||||
|
||||
SPDLOG_INLINE string_view_t &to_string_view(spdlog::level::level_enum l) SPDLOG_NOEXCEPT
|
||||
SPDLOG_INLINE const string_view_t &to_string_view(spdlog::level::level_enum l) SPDLOG_NOEXCEPT
|
||||
{
|
||||
return level_string_views[l];
|
||||
}
|
||||
@@ -25,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
|
||||
{
|
||||
int level = 0;
|
||||
for (const auto &level_str : level_string_views)
|
||||
{
|
||||
if (level_str == name)
|
||||
{
|
||||
return static_cast<level::level_enum>(level);
|
||||
}
|
||||
level++;
|
||||
}
|
||||
auto it = std::find(std::begin(level_string_views), std::end(level_string_views), name);
|
||||
if (it != std::end(level_string_views))
|
||||
return static_cast<level::level_enum>(it - std::begin(level_string_views));
|
||||
|
||||
// check also for "warn" and "err" before giving up..
|
||||
if (name == "warn")
|
||||
{
|
||||
@@ -54,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)
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
||||
|
@@ -16,66 +16,77 @@
|
||||
#include <functional>
|
||||
|
||||
#ifdef SPDLOG_COMPILED_LIB
|
||||
#undef SPDLOG_HEADER_ONLY
|
||||
#if defined(_WIN32) && defined(SPDLOG_SHARED_LIB)
|
||||
#ifdef spdlog_EXPORTS
|
||||
#define SPDLOG_API __declspec(dllexport)
|
||||
#else
|
||||
#define SPDLOG_API __declspec(dllimport)
|
||||
#endif
|
||||
#else // !defined(_WIN32) || !defined(SPDLOG_SHARED_LIB)
|
||||
#define SPDLOG_API
|
||||
#endif
|
||||
#define SPDLOG_INLINE
|
||||
# undef SPDLOG_HEADER_ONLY
|
||||
# if defined(_WIN32) && defined(SPDLOG_SHARED_LIB)
|
||||
# ifdef spdlog_EXPORTS
|
||||
# define SPDLOG_API __declspec(dllexport)
|
||||
# else
|
||||
# define SPDLOG_API __declspec(dllimport)
|
||||
# endif
|
||||
# else // !defined(_WIN32) || !defined(SPDLOG_SHARED_LIB)
|
||||
# define SPDLOG_API
|
||||
# endif
|
||||
# define SPDLOG_INLINE
|
||||
#else // !defined(SPDLOG_COMPILED_LIB)
|
||||
#define SPDLOG_API
|
||||
#define SPDLOG_HEADER_ONLY
|
||||
#define SPDLOG_INLINE inline
|
||||
# define SPDLOG_API
|
||||
# define SPDLOG_HEADER_ONLY
|
||||
# define SPDLOG_INLINE inline
|
||||
#endif // #ifdef SPDLOG_COMPILED_LIB
|
||||
|
||||
#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
|
||||
#if defined(_MSC_VER) && (_MSC_VER < 1900)
|
||||
#define SPDLOG_NOEXCEPT _NOEXCEPT
|
||||
#define SPDLOG_CONSTEXPR
|
||||
# define SPDLOG_NOEXCEPT _NOEXCEPT
|
||||
# define SPDLOG_CONSTEXPR
|
||||
#else
|
||||
#define SPDLOG_NOEXCEPT noexcept
|
||||
#define SPDLOG_CONSTEXPR constexpr
|
||||
# define SPDLOG_NOEXCEPT noexcept
|
||||
# define SPDLOG_CONSTEXPR constexpr
|
||||
#endif
|
||||
|
||||
#if defined(__GNUC__) || defined(__clang__)
|
||||
#define SPDLOG_DEPRECATED __attribute__((deprecated))
|
||||
# define SPDLOG_DEPRECATED __attribute__((deprecated))
|
||||
#elif defined(_MSC_VER)
|
||||
#define SPDLOG_DEPRECATED __declspec(deprecated)
|
||||
# define SPDLOG_DEPRECATED __declspec(deprecated)
|
||||
#else
|
||||
#define SPDLOG_DEPRECATED
|
||||
# define SPDLOG_DEPRECATED
|
||||
#endif
|
||||
|
||||
// disable thread local on msvc 2013
|
||||
#ifndef SPDLOG_NO_TLS
|
||||
#if (defined(_MSC_VER) && (_MSC_VER < 1900)) || defined(__cplusplus_winrt)
|
||||
#define SPDLOG_NO_TLS 1
|
||||
#endif
|
||||
# if (defined(_MSC_VER) && (_MSC_VER < 1900)) || defined(__cplusplus_winrt)
|
||||
# define SPDLOG_NO_TLS 1
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#ifndef SPDLOG_FUNCTION
|
||||
#define SPDLOG_FUNCTION static_cast<const char *>(__FUNCTION__)
|
||||
# define SPDLOG_FUNCTION static_cast<const char *>(__FUNCTION__)
|
||||
#endif
|
||||
|
||||
#ifdef SPDLOG_NO_EXCEPTIONS
|
||||
#define SPDLOG_TRY
|
||||
#define SPDLOG_THROW(ex) \
|
||||
do \
|
||||
{ \
|
||||
printf("spdlog fatal error: %s\n", ex.what()); \
|
||||
std::abort(); \
|
||||
} while (0)
|
||||
#define SPDLOG_CATCH_ALL()
|
||||
# define SPDLOG_TRY
|
||||
# define SPDLOG_THROW(ex) \
|
||||
do \
|
||||
{ \
|
||||
printf("spdlog fatal error: %s\n", ex.what()); \
|
||||
std::abort(); \
|
||||
} while (0)
|
||||
# define SPDLOG_CATCH_STD
|
||||
#else
|
||||
#define SPDLOG_TRY try
|
||||
#define SPDLOG_THROW(ex) throw(ex)
|
||||
#define SPDLOG_CATCH_ALL() catch (...)
|
||||
# define SPDLOG_TRY try
|
||||
# define SPDLOG_THROW(ex) throw(ex)
|
||||
# define SPDLOG_CATCH_STD \
|
||||
catch (const std::exception &) {}
|
||||
#endif
|
||||
|
||||
namespace spdlog {
|
||||
@@ -88,10 +99,12 @@ class sink;
|
||||
|
||||
#if defined(_WIN32) && defined(SPDLOG_WCHAR_FILENAMES)
|
||||
using filename_t = std::wstring;
|
||||
#define SPDLOG_FILENAME_T(s) L##s
|
||||
// allow macro expansion to occur in SPDLOG_FILENAME_T
|
||||
# define SPDLOG_FILENAME_T_INNER(s) L##s
|
||||
# define SPDLOG_FILENAME_T(s) SPDLOG_FILENAME_T_INNER(s)
|
||||
#else
|
||||
using filename_t = std::string;
|
||||
#define SPDLOG_FILENAME_T(s) s
|
||||
# define SPDLOG_FILENAME_T(s) s
|
||||
#endif
|
||||
|
||||
using log_clock = std::chrono::system_clock;
|
||||
@@ -101,20 +114,29 @@ using err_handler = std::function<void(const std::string &err_msg)>;
|
||||
using string_view_t = fmt::basic_string_view<char>;
|
||||
using wstring_view_t = fmt::basic_string_view<wchar_t>;
|
||||
using memory_buf_t = fmt::basic_memory_buffer<char, 250>;
|
||||
using wmemory_buf_t = fmt::basic_memory_buffer<wchar_t, 250>;
|
||||
|
||||
template<class T>
|
||||
using remove_cvref_t = typename std::remove_cv<typename std::remove_reference<T>::type>::type;
|
||||
|
||||
// clang doesn't like SFINAE disabled constructor in std::is_convertible<> so have to repeat the condition from basic_format_string here,
|
||||
// in addition, fmt::basic_runtime<Char> is only convertible to basic_format_string<Char> but not basic_string_view<Char>
|
||||
template<class T, class Char = char>
|
||||
struct is_convertible_to_basic_format_string
|
||||
: std::integral_constant<bool,
|
||||
std::is_convertible<T, fmt::basic_string_view<Char>>::value || std::is_same<remove_cvref_t<T>, fmt::basic_runtime<Char>>::value>
|
||||
{};
|
||||
|
||||
#ifdef SPDLOG_WCHAR_TO_UTF8_SUPPORT
|
||||
#ifndef _WIN32
|
||||
#error SPDLOG_WCHAR_TO_UTF8_SUPPORT only supported on windows
|
||||
#else
|
||||
template<typename T>
|
||||
struct is_convertible_to_wstring_view : std::is_convertible<T, wstring_view_t>
|
||||
# ifndef _WIN32
|
||||
# error SPDLOG_WCHAR_TO_UTF8_SUPPORT only supported on windows
|
||||
# endif // _WIN32
|
||||
#endif // SPDLOG_WCHAR_TO_UTF8_SUPPORT
|
||||
|
||||
template<class T>
|
||||
struct is_convertible_to_any_format_string : std::integral_constant<bool, is_convertible_to_basic_format_string<T, char>::value ||
|
||||
is_convertible_to_basic_format_string<T, wchar_t>::value>
|
||||
{};
|
||||
#endif // _WIN32
|
||||
#else
|
||||
template<typename>
|
||||
struct is_convertible_to_wstring_view : std::false_type
|
||||
{};
|
||||
#endif // SPDLOG_WCHAR_TO_UTF8_SUPPORT
|
||||
|
||||
#if defined(SPDLOG_NO_ATOMIC_LEVELS)
|
||||
using level_t = details::null_atomic_int;
|
||||
@@ -131,7 +153,7 @@ using level_t = std::atomic<int>;
|
||||
#define SPDLOG_LEVEL_OFF 6
|
||||
|
||||
#if !defined(SPDLOG_ACTIVE_LEVEL)
|
||||
#define SPDLOG_ACTIVE_LEVEL SPDLOG_LEVEL_INFO
|
||||
# define SPDLOG_ACTIVE_LEVEL SPDLOG_LEVEL_INFO
|
||||
#endif
|
||||
|
||||
// Log level enum
|
||||
@@ -148,26 +170,34 @@ enum level_enum
|
||||
n_levels
|
||||
};
|
||||
|
||||
#define SPDLOG_LEVEL_NAME_TRACE spdlog::string_view_t("trace", 5)
|
||||
#define SPDLOG_LEVEL_NAME_DEBUG spdlog::string_view_t("debug", 5)
|
||||
#define SPDLOG_LEVEL_NAME_INFO spdlog::string_view_t("info", 4)
|
||||
#define SPDLOG_LEVEL_NAME_WARNING spdlog::string_view_t("warning", 7)
|
||||
#define SPDLOG_LEVEL_NAME_ERROR spdlog::string_view_t("error", 5)
|
||||
#define SPDLOG_LEVEL_NAME_CRITICAL spdlog::string_view_t("critical", 8)
|
||||
#define SPDLOG_LEVEL_NAME_OFF spdlog::string_view_t("off", 3)
|
||||
|
||||
#if !defined(SPDLOG_LEVEL_NAMES)
|
||||
#define SPDLOG_LEVEL_NAMES \
|
||||
{ \
|
||||
"trace", "debug", "info", "warning", "error", "critical", "off" \
|
||||
}
|
||||
# define SPDLOG_LEVEL_NAMES \
|
||||
{ \
|
||||
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
|
||||
|
||||
#if !defined(SPDLOG_SHORT_LEVEL_NAMES)
|
||||
|
||||
#define SPDLOG_SHORT_LEVEL_NAMES \
|
||||
{ \
|
||||
"T", "D", "I", "W", "E", "C", "O" \
|
||||
}
|
||||
# define SPDLOG_SHORT_LEVEL_NAMES \
|
||||
{ \
|
||||
"T", "D", "I", "W", "E", "C", "O" \
|
||||
}
|
||||
#endif
|
||||
|
||||
SPDLOG_API 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 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;
|
||||
|
||||
using level_hasher = std::hash<int>;
|
||||
} // namespace level
|
||||
|
||||
//
|
||||
@@ -204,8 +234,8 @@ private:
|
||||
std::string msg_;
|
||||
};
|
||||
|
||||
SPDLOG_API void throw_spdlog_ex(const std::string &msg, int last_errno);
|
||||
SPDLOG_API void throw_spdlog_ex(std::string msg);
|
||||
[[noreturn]] SPDLOG_API void throw_spdlog_ex(const std::string &msg, int last_errno);
|
||||
[[noreturn]] SPDLOG_API void throw_spdlog_ex(std::string msg);
|
||||
|
||||
struct source_loc
|
||||
{
|
||||
@@ -232,7 +262,7 @@ namespace details {
|
||||
using std::make_unique;
|
||||
#else
|
||||
template<typename T, typename... Args>
|
||||
std::unique_ptr<T> make_unique(Args &&... args)
|
||||
std::unique_ptr<T> make_unique(Args &&...args)
|
||||
{
|
||||
static_assert(!std::is_array<T>::value, "arrays not supported");
|
||||
return std::unique_ptr<T>(new T(std::forward<Args>(args)...));
|
||||
@@ -242,5 +272,5 @@ std::unique_ptr<T> make_unique(Args &&... args)
|
||||
} // namespace spdlog
|
||||
|
||||
#ifdef SPDLOG_HEADER_ONLY
|
||||
#include "common-inl.h"
|
||||
# include "common-inl.h"
|
||||
#endif
|
||||
|
@@ -4,7 +4,7 @@
|
||||
#pragma once
|
||||
|
||||
#ifndef SPDLOG_HEADER_ONLY
|
||||
#include <spdlog/details/backtracer.h>
|
||||
# include <spdlog/details/backtracer.h>
|
||||
#endif
|
||||
namespace spdlog {
|
||||
namespace details {
|
||||
|
@@ -41,5 +41,5 @@ public:
|
||||
} // namespace spdlog
|
||||
|
||||
#ifdef SPDLOG_HEADER_ONLY
|
||||
#include "backtracer-inl.h"
|
||||
#endif
|
||||
# include "backtracer-inl.h"
|
||||
#endif
|
||||
|
@@ -4,7 +4,7 @@
|
||||
#pragma once
|
||||
|
||||
#ifndef SPDLOG_HEADER_ONLY
|
||||
#include <spdlog/details/file_helper.h>
|
||||
# include <spdlog/details/file_helper.h>
|
||||
#endif
|
||||
|
||||
#include <spdlog/details/os.h>
|
||||
@@ -29,12 +29,27 @@ SPDLOG_INLINE void file_helper::open(const filename_t &fname, bool truncate)
|
||||
{
|
||||
close();
|
||||
filename_ = fname;
|
||||
auto *mode = truncate ? SPDLOG_FILENAME_T("wb") : SPDLOG_FILENAME_T("ab");
|
||||
|
||||
auto *mode = SPDLOG_FILENAME_T("ab");
|
||||
auto *trunc_mode = SPDLOG_FILENAME_T("wb");
|
||||
|
||||
for (int tries = 0; tries < open_tries_; ++tries)
|
||||
{
|
||||
// create containing folder if not exists already.
|
||||
os::create_dir(os::dir_name(fname));
|
||||
if (truncate)
|
||||
{
|
||||
// Truncate by opening-and-closing a tmp file in "wb" mode, always
|
||||
// opening the actual log-we-write-to in "ab" mode, since that
|
||||
// interacts more politely with eternal processes that might
|
||||
// rotate/truncate the file underneath us.
|
||||
std::FILE *tmp;
|
||||
if (os::fopen_s(&tmp, fname, trunc_mode))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
std::fclose(tmp);
|
||||
}
|
||||
if (!os::fopen_s(&fd_, fname, mode))
|
||||
{
|
||||
return;
|
||||
@@ -118,7 +133,7 @@ SPDLOG_INLINE std::tuple<filename_t, filename_t> file_helper::split_by_extension
|
||||
}
|
||||
|
||||
// treat cases like "/etc/rc.d/somelogfile or "/abc/.hiddenfile"
|
||||
auto folder_index = fname.rfind(details::os::folder_sep);
|
||||
auto folder_index = fname.find_last_of(details::os::folder_seps_filename);
|
||||
if (folder_index != filename_t::npos && folder_index >= ext_index - 1)
|
||||
{
|
||||
return std::make_tuple(fname, filename_t());
|
||||
|
@@ -47,7 +47,7 @@ public:
|
||||
|
||||
private:
|
||||
const int open_tries_ = 5;
|
||||
const int open_interval_ = 10;
|
||||
const unsigned int open_interval_ = 10;
|
||||
std::FILE *fd_{nullptr};
|
||||
filename_t filename_;
|
||||
};
|
||||
@@ -55,5 +55,5 @@ private:
|
||||
} // namespace spdlog
|
||||
|
||||
#ifdef SPDLOG_HEADER_ONLY
|
||||
#include "file_helper-inl.h"
|
||||
# include "file_helper-inl.h"
|
||||
#endif
|
||||
|
@@ -4,6 +4,7 @@
|
||||
|
||||
#include <chrono>
|
||||
#include <type_traits>
|
||||
#include <iterator>
|
||||
#include <spdlog/fmt/fmt.h>
|
||||
#include <spdlog/common.h>
|
||||
|
||||
@@ -34,7 +35,15 @@ template<typename T>
|
||||
inline unsigned int count_digits(T n)
|
||||
{
|
||||
using count_type = typename std::conditional<(sizeof(T) > sizeof(uint32_t)), uint64_t, uint32_t>::type;
|
||||
return static_cast<unsigned int>(fmt::internal::count_digits(static_cast<count_type>(n)));
|
||||
return static_cast<unsigned int>(fmt::
|
||||
// fmt 7.0.0 renamed the internal namespace to detail.
|
||||
// See: https://github.com/fmtlib/fmt/issues/1538
|
||||
#if FMT_VERSION < 70000
|
||||
internal
|
||||
#else
|
||||
detail
|
||||
#endif
|
||||
::count_digits(static_cast<count_type>(n)));
|
||||
}
|
||||
|
||||
inline void pad2(int n, memory_buf_t &dest)
|
||||
@@ -46,7 +55,7 @@ inline void pad2(int n, memory_buf_t &dest)
|
||||
}
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -4,7 +4,7 @@
|
||||
#pragma once
|
||||
|
||||
#ifndef SPDLOG_HEADER_ONLY
|
||||
#include <spdlog/details/log_msg.h>
|
||||
# include <spdlog/details/log_msg.h>
|
||||
#endif
|
||||
|
||||
#include <spdlog/details/os.h>
|
||||
|
@@ -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(string_view_t logger_name, level::level_enum lvl, string_view_t msg);
|
||||
log_msg(const log_msg &other) = default;
|
||||
log_msg &operator=(const log_msg &other) = default;
|
||||
|
||||
string_view_t logger_name;
|
||||
level::level_enum level{level::off};
|
||||
@@ -32,5 +33,5 @@ struct SPDLOG_API log_msg
|
||||
} // namespace spdlog
|
||||
|
||||
#ifdef SPDLOG_HEADER_ONLY
|
||||
#include "log_msg-inl.h"
|
||||
# include "log_msg-inl.h"
|
||||
#endif
|
||||
|
@@ -4,7 +4,7 @@
|
||||
#pragma once
|
||||
|
||||
#ifndef SPDLOG_HEADER_ONLY
|
||||
#include <spdlog/details/log_msg_buffer.h>
|
||||
# include <spdlog/details/log_msg_buffer.h>
|
||||
#endif
|
||||
|
||||
namespace spdlog {
|
||||
|
@@ -29,5 +29,5 @@ public:
|
||||
} // namespace spdlog
|
||||
|
||||
#ifdef SPDLOG_HEADER_ONLY
|
||||
#include "log_msg_buffer-inl.h"
|
||||
# include "log_msg_buffer-inl.h"
|
||||
#endif
|
||||
|
@@ -110,6 +110,12 @@ public:
|
||||
return q_.overrun_counter();
|
||||
}
|
||||
|
||||
size_t size()
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(queue_mutex_);
|
||||
return q_.size();
|
||||
}
|
||||
|
||||
private:
|
||||
std::mutex queue_mutex_;
|
||||
std::condition_variable push_cv_;
|
||||
|
@@ -4,7 +4,7 @@
|
||||
#pragma once
|
||||
|
||||
#ifndef SPDLOG_HEADER_ONLY
|
||||
#include <spdlog/details/os.h>
|
||||
# include <spdlog/details/os.h>
|
||||
#endif
|
||||
|
||||
#include <spdlog/common.h>
|
||||
@@ -23,45 +23,45 @@
|
||||
|
||||
#ifdef _WIN32
|
||||
|
||||
#include <io.h> // _get_osfhandle and _isatty support
|
||||
#include <process.h> // _get_pid support
|
||||
#include <spdlog/details/windows_include.h>
|
||||
# include <io.h> // _get_osfhandle and _isatty support
|
||||
# include <process.h> // _get_pid support
|
||||
# include <spdlog/details/windows_include.h>
|
||||
|
||||
#ifdef __MINGW32__
|
||||
#include <share.h>
|
||||
#endif
|
||||
# ifdef __MINGW32__
|
||||
# include <share.h>
|
||||
# endif
|
||||
|
||||
#if defined(SPDLOG_WCHAR_TO_UTF8_SUPPORT) || defined(SPDLOG_WCHAR_FILENAMES)
|
||||
#include <limits>
|
||||
#endif
|
||||
# if defined(SPDLOG_WCHAR_TO_UTF8_SUPPORT) || defined(SPDLOG_WCHAR_FILENAMES)
|
||||
# include <limits>
|
||||
# endif
|
||||
|
||||
#include <direct.h> // for _mkdir/_wmkdir
|
||||
# include <direct.h> // for _mkdir/_wmkdir
|
||||
|
||||
#else // unix
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
# include <fcntl.h>
|
||||
# include <unistd.h>
|
||||
|
||||
#ifdef __linux__
|
||||
#include <sys/syscall.h> //Use gettid() syscall under linux to get thread id
|
||||
# ifdef __linux__
|
||||
# include <sys/syscall.h> //Use gettid() syscall under linux to get thread id
|
||||
|
||||
#elif defined(_AIX)
|
||||
#include <pthread.h> // for pthread_getthreadid_np
|
||||
# elif defined(_AIX)
|
||||
# include <pthread.h> // for pthread_getthreadid_np
|
||||
|
||||
#elif defined(__DragonFly__) || defined(__FreeBSD__)
|
||||
#include <pthread_np.h> // for pthread_getthreadid_np
|
||||
# elif defined(__DragonFly__) || defined(__FreeBSD__)
|
||||
# include <pthread_np.h> // for pthread_getthreadid_np
|
||||
|
||||
#elif defined(__NetBSD__)
|
||||
#include <lwp.h> // for _lwp_self
|
||||
# elif defined(__NetBSD__)
|
||||
# include <lwp.h> // for _lwp_self
|
||||
|
||||
#elif defined(__sun)
|
||||
#include <thread.h> // for thr_self
|
||||
#endif
|
||||
# elif defined(__sun)
|
||||
# include <thread.h> // for thr_self
|
||||
# endif
|
||||
|
||||
#endif // unix
|
||||
|
||||
#ifndef __has_feature // Clang - feature checking macros.
|
||||
#define __has_feature(x) 0 // Compatibility with non-clang compilers.
|
||||
#ifndef __has_feature // Clang - feature checking macros.
|
||||
# define __has_feature(x) 0 // Compatibility with non-clang compilers.
|
||||
#endif
|
||||
|
||||
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)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
#ifdef SPDLOG_WCHAR_FILENAMES
|
||||
# ifdef SPDLOG_WCHAR_FILENAMES
|
||||
*fp = ::_wfsopen((filename.c_str()), mode.c_str(), _SH_DENYNO);
|
||||
#else
|
||||
# else
|
||||
*fp = ::_fsopen((filename.c_str()), mode.c_str(), _SH_DENYNO);
|
||||
#endif
|
||||
#if defined(SPDLOG_PREVENT_CHILD_FD)
|
||||
# endif
|
||||
# if defined(SPDLOG_PREVENT_CHILD_FD)
|
||||
if (*fp != nullptr)
|
||||
{
|
||||
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;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
# endif
|
||||
#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 fd = ::open((filename.c_str()), O_CREAT | O_WRONLY | O_CLOEXEC | mode_flag, mode_t(0644));
|
||||
if (fd == -1)
|
||||
@@ -152,9 +152,9 @@ SPDLOG_INLINE bool fopen_s(FILE **fp, const filename_t &filename, const filename
|
||||
{
|
||||
::close(fd);
|
||||
}
|
||||
#else
|
||||
# else
|
||||
*fp = ::fopen((filename.c_str()), mode.c_str());
|
||||
#endif
|
||||
# endif
|
||||
#endif
|
||||
|
||||
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
|
||||
{
|
||||
#ifdef _WIN32
|
||||
#ifdef SPDLOG_WCHAR_FILENAMES
|
||||
# ifdef SPDLOG_WCHAR_FILENAMES
|
||||
auto attribs = ::GetFileAttributesW(filename.c_str());
|
||||
#else
|
||||
# else
|
||||
auto attribs = ::GetFileAttributesA(filename.c_str());
|
||||
#endif
|
||||
# endif
|
||||
return attribs != INVALID_FILE_ATTRIBUTES;
|
||||
#else // common linux/unix all have the stat system call
|
||||
struct stat buffer;
|
||||
@@ -199,6 +199,12 @@ SPDLOG_INLINE bool path_exists(const filename_t &filename) SPDLOG_NOEXCEPT
|
||||
#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
|
||||
SPDLOG_INLINE size_t filesize(FILE *f)
|
||||
{
|
||||
@@ -208,59 +214,63 @@ SPDLOG_INLINE size_t filesize(FILE *f)
|
||||
}
|
||||
#if defined(_WIN32) && !defined(__CYGWIN__)
|
||||
int fd = ::_fileno(f);
|
||||
#if _WIN64 // 64 bits
|
||||
# if defined(_WIN64) // 64 bits
|
||||
__int64 ret = ::_filelengthi64(fd);
|
||||
if (ret >= 0)
|
||||
{
|
||||
return static_cast<size_t>(ret);
|
||||
}
|
||||
|
||||
#else // windows 32 bits
|
||||
# else // windows 32 bits
|
||||
long ret = ::_filelength(fd);
|
||||
if (ret >= 0)
|
||||
{
|
||||
return static_cast<size_t>(ret);
|
||||
}
|
||||
#endif
|
||||
# endif
|
||||
|
||||
#else // unix
|
||||
// OpenBSD doesn't compile with :: before the fileno(..)
|
||||
#if defined(__OpenBSD__)
|
||||
# if defined(__OpenBSD__)
|
||||
int fd = fileno(f);
|
||||
#else
|
||||
# else
|
||||
int fd = ::fileno(f);
|
||||
#endif
|
||||
# endif
|
||||
// 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;
|
||||
if (::fstat64(fd, &st) == 0)
|
||||
{
|
||||
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;
|
||||
if (::fstat(fd, &st) == 0)
|
||||
{
|
||||
return static_cast<size_t>(st.st_size);
|
||||
}
|
||||
#endif
|
||||
# endif
|
||||
#endif
|
||||
throw_spdlog_ex("Failed getting file size from fd", errno);
|
||||
return 0; // will not be reached.
|
||||
}
|
||||
|
||||
#ifdef _MSC_VER
|
||||
# pragma warning(pop)
|
||||
#endif
|
||||
|
||||
// Return utc offset in minutes or throw spdlog_ex on failure
|
||||
SPDLOG_INLINE int utc_minutes_offset(const std::tm &tm)
|
||||
{
|
||||
|
||||
#ifdef _WIN32
|
||||
#if _WIN32_WINNT < _WIN32_WINNT_WS08
|
||||
# if _WIN32_WINNT < _WIN32_WINNT_WS08
|
||||
TIME_ZONE_INFORMATION tzinfo;
|
||||
auto rv = ::GetTimeZoneInformation(&tzinfo);
|
||||
#else
|
||||
# else
|
||||
DYNAMIC_TIME_ZONE_INFORMATION tzinfo;
|
||||
auto rv = ::GetDynamicTimeZoneInformation(&tzinfo);
|
||||
#endif
|
||||
# endif
|
||||
if (rv == TIME_ZONE_ID_INVALID)
|
||||
throw_spdlog_ex("Failed getting timezone info. ", errno);
|
||||
|
||||
@@ -276,7 +286,7 @@ SPDLOG_INLINE int utc_minutes_offset(const std::tm &tm)
|
||||
return offset;
|
||||
#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
|
||||
struct helper
|
||||
{
|
||||
@@ -306,9 +316,9 @@ SPDLOG_INLINE int utc_minutes_offset(const std::tm &tm)
|
||||
};
|
||||
|
||||
auto offset_seconds = helper::calculate_gmt_offset(tm);
|
||||
#else
|
||||
# else
|
||||
auto offset_seconds = tm.tm_gmtoff;
|
||||
#endif
|
||||
# endif
|
||||
|
||||
return static_cast<int>(offset_seconds / 60);
|
||||
#endif
|
||||
@@ -322,9 +332,9 @@ SPDLOG_INLINE size_t _thread_id() SPDLOG_NOEXCEPT
|
||||
#ifdef _WIN32
|
||||
return static_cast<size_t>(::GetCurrentThreadId());
|
||||
#elif defined(__linux__)
|
||||
#if defined(__ANDROID__) && defined(__ANDROID_API__) && (__ANDROID_API__ < 21)
|
||||
#define SYS_gettid __NR_gettid
|
||||
#endif
|
||||
# if defined(__ANDROID__) && defined(__ANDROID_API__) && (__ANDROID_API__ < 21)
|
||||
# define SYS_gettid __NR_gettid
|
||||
# endif
|
||||
return static_cast<size_t>(::syscall(SYS_gettid));
|
||||
#elif defined(_AIX) || defined(__DragonFly__) || defined(__FreeBSD__)
|
||||
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.
|
||||
// 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)
|
||||
::Sleep(milliseconds);
|
||||
@@ -397,17 +407,26 @@ SPDLOG_INLINE bool is_color_terminal() SPDLOG_NOEXCEPT
|
||||
#ifdef _WIN32
|
||||
return true;
|
||||
#else
|
||||
static constexpr std::array<const char *, 14> terms = {
|
||||
{"ansi", "color", "console", "cygwin", "gnome", "konsole", "kterm", "linux", "msys", "putty", "rxvt", "screen", "vt100", "xterm"}};
|
||||
|
||||
const char *env_p = std::getenv("TERM");
|
||||
if (env_p == nullptr)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
static const bool result = []() {
|
||||
const char *env_colorterm_p = std::getenv("COLORTERM");
|
||||
if (env_colorterm_p != nullptr)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
static constexpr std::array<const char *, 16> terms = {{"ansi", "color", "console", "cygwin", "gnome", "konsole", "kterm", "linux",
|
||||
"msys", "putty", "rxvt", "screen", "vt100", "xterm", "alacritty", "vt102"}};
|
||||
|
||||
const char *env_term_p = std::getenv("TERM");
|
||||
if (env_term_p == nullptr)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return std::any_of(terms.begin(), terms.end(), [&](const char *term) { return std::strstr(env_term_p, term) != nullptr; });
|
||||
}();
|
||||
|
||||
static const bool result =
|
||||
std::any_of(terms.begin(), terms.end(), [&](const char *term) { return std::strstr(env_p, term) != nullptr; });
|
||||
return result;
|
||||
#endif
|
||||
}
|
||||
@@ -427,7 +446,7 @@ SPDLOG_INLINE bool in_terminal(FILE *file) SPDLOG_NOEXCEPT
|
||||
#if (defined(SPDLOG_WCHAR_TO_UTF8_SUPPORT) || defined(SPDLOG_WCHAR_FILENAMES)) && defined(_WIN32)
|
||||
SPDLOG_INLINE void wstr_to_utf8buf(wstring_view_t wstr, memory_buf_t &target)
|
||||
{
|
||||
if (wstr.size() > static_cast<size_t>((std::numeric_limits<int>::max)()))
|
||||
if (wstr.size() > static_cast<size_t>((std::numeric_limits<int>::max)()) / 2 - 1)
|
||||
{
|
||||
throw_spdlog_ex("UTF-16 string is too big to be converted to UTF-8");
|
||||
}
|
||||
@@ -459,17 +478,52 @@ SPDLOG_INLINE void wstr_to_utf8buf(wstring_view_t wstr, memory_buf_t &target)
|
||||
|
||||
throw_spdlog_ex(fmt::format("WideCharToMultiByte failed. Last error: {}", ::GetLastError()));
|
||||
}
|
||||
|
||||
SPDLOG_INLINE void utf8_to_wstrbuf(string_view_t str, wmemory_buf_t &target)
|
||||
{
|
||||
if (str.size() > static_cast<size_t>((std::numeric_limits<int>::max)()) - 1)
|
||||
{
|
||||
throw_spdlog_ex("UTF-8 string is too big to be converted to UTF-16");
|
||||
}
|
||||
|
||||
int str_size = static_cast<int>(str.size());
|
||||
if (str_size == 0)
|
||||
{
|
||||
target.resize(0);
|
||||
return;
|
||||
}
|
||||
|
||||
int result_size = static_cast<int>(target.capacity());
|
||||
if (str_size + 1 > result_size)
|
||||
{
|
||||
result_size = ::MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, str.data(), str_size, NULL, 0);
|
||||
}
|
||||
|
||||
if (result_size > 0)
|
||||
{
|
||||
target.resize(result_size);
|
||||
result_size = ::MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, str.data(), str_size, target.data(), result_size);
|
||||
|
||||
if (result_size > 0)
|
||||
{
|
||||
target.resize(result_size);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
throw_spdlog_ex(fmt::format("MultiByteToWideChar failed. Last error: {}", ::GetLastError()));
|
||||
}
|
||||
#endif // (defined(SPDLOG_WCHAR_TO_UTF8_SUPPORT) || defined(SPDLOG_WCHAR_FILENAMES)) && defined(_WIN32)
|
||||
|
||||
// return true on success
|
||||
static SPDLOG_INLINE bool mkdir_(const filename_t &path)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
#ifdef SPDLOG_WCHAR_FILENAMES
|
||||
# ifdef SPDLOG_WCHAR_FILENAMES
|
||||
return ::_wmkdir(path.c_str()) == 0;
|
||||
#else
|
||||
# else
|
||||
return ::_mkdir(path.c_str()) == 0;
|
||||
#endif
|
||||
# endif
|
||||
#else
|
||||
return ::mkdir(path.c_str(), mode_t(0755)) == 0;
|
||||
#endif
|
||||
@@ -489,15 +543,10 @@ SPDLOG_INLINE bool create_dir(filename_t path)
|
||||
return false;
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
// support forward slash in windows
|
||||
std::replace(path.begin(), path.end(), '/', folder_sep);
|
||||
#endif
|
||||
|
||||
size_t search_offset = 0;
|
||||
do
|
||||
{
|
||||
auto token_pos = path.find(folder_sep, search_offset);
|
||||
auto token_pos = path.find_first_of(folder_seps_filename, search_offset);
|
||||
// treat the entire path as a folder if no folder separator not found
|
||||
if (token_pos == filename_t::npos)
|
||||
{
|
||||
@@ -523,11 +572,7 @@ SPDLOG_INLINE bool create_dir(filename_t path)
|
||||
// "abc///" => "abc//"
|
||||
SPDLOG_INLINE filename_t dir_name(filename_t path)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
// support forward slash in windows
|
||||
std::replace(path.begin(), path.end(), '/', folder_sep);
|
||||
#endif
|
||||
auto pos = path.find_last_of(folder_sep);
|
||||
auto pos = path.find_last_of(folder_seps_filename);
|
||||
return pos != filename_t::npos ? path.substr(0, pos) : filename_t{};
|
||||
}
|
||||
|
||||
@@ -535,14 +580,14 @@ std::string SPDLOG_INLINE getenv(const char *field)
|
||||
{
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
#if defined(__cplusplus_winrt)
|
||||
# if defined(__cplusplus_winrt)
|
||||
return std::string{}; // not supported under uwp
|
||||
#else
|
||||
# else
|
||||
size_t len = 0;
|
||||
char buf[128];
|
||||
bool ok = ::getenv_s(&len, buf, sizeof(buf), field) == 0;
|
||||
return ok ? buf : std::string{};
|
||||
#endif
|
||||
# endif
|
||||
#else // revert to getenv
|
||||
char *buf = ::getenv(field);
|
||||
return buf ? buf : std::string{};
|
||||
|
@@ -22,22 +22,27 @@ SPDLOG_API std::tm gmtime() SPDLOG_NOEXCEPT;
|
||||
|
||||
// eol definition
|
||||
#if !defined(SPDLOG_EOL)
|
||||
#ifdef _WIN32
|
||||
#define SPDLOG_EOL "\r\n"
|
||||
#else
|
||||
#define SPDLOG_EOL "\n"
|
||||
#endif
|
||||
# ifdef _WIN32
|
||||
# define SPDLOG_EOL "\r\n"
|
||||
# else
|
||||
# define SPDLOG_EOL "\n"
|
||||
# endif
|
||||
#endif
|
||||
|
||||
SPDLOG_CONSTEXPR static const char *default_eol = SPDLOG_EOL;
|
||||
|
||||
// folder separator
|
||||
#ifdef _WIN32
|
||||
static const char folder_sep = '\\';
|
||||
#else
|
||||
SPDLOG_CONSTEXPR static const char folder_sep = '/';
|
||||
#if !defined(SPDLOG_FOLDER_SEPS)
|
||||
# ifdef _WIN32
|
||||
# define SPDLOG_FOLDER_SEPS "\\/"
|
||||
# else
|
||||
# define SPDLOG_FOLDER_SEPS "/"
|
||||
# endif
|
||||
#endif
|
||||
|
||||
SPDLOG_CONSTEXPR static const char folder_seps[] = SPDLOG_FOLDER_SEPS;
|
||||
SPDLOG_CONSTEXPR static const filename_t::value_type folder_seps_filename[] = SPDLOG_FILENAME_T(SPDLOG_FOLDER_SEPS);
|
||||
|
||||
// fopen_s on non windows for writing
|
||||
SPDLOG_API bool fopen_s(FILE **fp, const filename_t &filename, const filename_t &mode);
|
||||
|
||||
@@ -69,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.
|
||||
// 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);
|
||||
|
||||
@@ -85,6 +90,8 @@ SPDLOG_API bool in_terminal(FILE *file) SPDLOG_NOEXCEPT;
|
||||
|
||||
#if (defined(SPDLOG_WCHAR_TO_UTF8_SUPPORT) || defined(SPDLOG_WCHAR_FILENAMES)) && defined(_WIN32)
|
||||
SPDLOG_API void wstr_to_utf8buf(wstring_view_t wstr, memory_buf_t &target);
|
||||
|
||||
SPDLOG_API void utf8_to_wstrbuf(string_view_t str, wmemory_buf_t &target);
|
||||
#endif
|
||||
|
||||
// Return directory name from given path or empty string
|
||||
@@ -107,5 +114,5 @@ SPDLOG_API std::string getenv(const char *field);
|
||||
} // namespace spdlog
|
||||
|
||||
#ifdef SPDLOG_HEADER_ONLY
|
||||
#include "os-inl.h"
|
||||
# include "os-inl.h"
|
||||
#endif
|
||||
|
@@ -4,7 +4,7 @@
|
||||
#pragma once
|
||||
|
||||
#ifndef SPDLOG_HEADER_ONLY
|
||||
#include <spdlog/details/periodic_worker.h>
|
||||
# include <spdlog/details/periodic_worker.h>
|
||||
#endif
|
||||
|
||||
namespace spdlog {
|
||||
|
@@ -36,5 +36,5 @@ private:
|
||||
} // namespace spdlog
|
||||
|
||||
#ifdef SPDLOG_HEADER_ONLY
|
||||
#include "periodic_worker-inl.h"
|
||||
# include "periodic_worker-inl.h"
|
||||
#endif
|
||||
|
@@ -4,7 +4,7 @@
|
||||
#pragma once
|
||||
|
||||
#ifndef SPDLOG_HEADER_ONLY
|
||||
#include <spdlog/details/registry.h>
|
||||
# include <spdlog/details/registry.h>
|
||||
#endif
|
||||
|
||||
#include <spdlog/common.h>
|
||||
@@ -14,11 +14,11 @@
|
||||
|
||||
#ifndef SPDLOG_DISABLE_DEFAULT_LOGGER
|
||||
// support for the default stdout color logger
|
||||
#ifdef _WIN32
|
||||
#include <spdlog/sinks/wincolor_sink.h>
|
||||
#else
|
||||
#include <spdlog/sinks/ansicolor_sink.h>
|
||||
#endif
|
||||
# ifdef _WIN32
|
||||
# include <spdlog/sinks/wincolor_sink.h>
|
||||
# else
|
||||
# include <spdlog/sinks/ansicolor_sink.h>
|
||||
# endif
|
||||
#endif // SPDLOG_DISABLE_DEFAULT_LOGGER
|
||||
|
||||
#include <chrono>
|
||||
@@ -36,11 +36,11 @@ SPDLOG_INLINE registry::registry()
|
||||
|
||||
#ifndef SPDLOG_DISABLE_DEFAULT_LOGGER
|
||||
// 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>();
|
||||
#else
|
||||
# else
|
||||
auto color_sink = std::make_shared<sinks::ansicolor_stdout_sink_mt>();
|
||||
#endif
|
||||
# endif
|
||||
|
||||
const char *default_logger_name = "";
|
||||
default_logger_ = std::make_shared<spdlog::logger>(default_logger_name, std::move(color_sink));
|
||||
@@ -67,7 +67,11 @@ SPDLOG_INLINE void registry::initialize_logger(std::shared_ptr<logger> new_logge
|
||||
new_logger->set_error_handler(err_handler_);
|
||||
}
|
||||
|
||||
new_logger->set_level(levels_.get(new_logger->name()));
|
||||
// set new level according to previously configured level or default level
|
||||
auto it = log_levels_.find(new_logger->name());
|
||||
auto new_level = it != log_levels_.end() ? it->second : global_log_level_;
|
||||
new_logger->set_level(new_level);
|
||||
|
||||
new_logger->flush_on(flush_level_);
|
||||
|
||||
if (backtrace_n_messages_ > 0)
|
||||
@@ -171,7 +175,7 @@ SPDLOG_INLINE void registry::set_level(level::level_enum log_level)
|
||||
{
|
||||
l.second->set_level(log_level);
|
||||
}
|
||||
levels_.set_default(log_level);
|
||||
global_log_level_ = log_level;
|
||||
}
|
||||
|
||||
SPDLOG_INLINE void registry::flush_on(level::level_enum log_level)
|
||||
@@ -191,14 +195,14 @@ SPDLOG_INLINE void registry::flush_every(std::chrono::seconds 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_);
|
||||
for (auto &l : loggers_)
|
||||
{
|
||||
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)
|
||||
@@ -263,14 +267,24 @@ SPDLOG_INLINE void registry::set_automatic_registration(bool automatic_registrat
|
||||
automatic_registration_ = automatic_registration;
|
||||
}
|
||||
|
||||
SPDLOG_INLINE void registry::update_levels(cfg::log_levels levels)
|
||||
SPDLOG_INLINE void registry::set_levels(log_levels levels, level::level_enum *global_level)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(logger_map_mutex_);
|
||||
levels_ = std::move(levels);
|
||||
for (auto &l : loggers_)
|
||||
log_levels_ = std::move(levels);
|
||||
auto global_level_requested = global_level != nullptr;
|
||||
global_log_level_ = global_level_requested ? *global_level : global_log_level_;
|
||||
|
||||
for (auto &logger : loggers_)
|
||||
{
|
||||
auto &logger = l.second;
|
||||
logger->set_level(levels_.get(logger->name()));
|
||||
auto logger_entry = log_levels_.find(logger.first);
|
||||
if (logger_entry != log_levels_.end())
|
||||
{
|
||||
logger.second->set_level(logger_entry->second);
|
||||
}
|
||||
else if (global_level_requested)
|
||||
{
|
||||
logger.second->set_level(*global_level);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -9,7 +9,6 @@
|
||||
// This class is thread safe
|
||||
|
||||
#include <spdlog/common.h>
|
||||
#include <spdlog/cfg/log_levels.h>
|
||||
|
||||
#include <chrono>
|
||||
#include <functional>
|
||||
@@ -28,6 +27,7 @@ class periodic_worker;
|
||||
class SPDLOG_API registry
|
||||
{
|
||||
public:
|
||||
using log_levels = std::unordered_map<std::string, level::level_enum>;
|
||||
registry(const registry &) = delete;
|
||||
registry &operator=(const registry &) = delete;
|
||||
|
||||
@@ -63,7 +63,7 @@ public:
|
||||
|
||||
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);
|
||||
|
||||
@@ -80,7 +80,8 @@ public:
|
||||
|
||||
void set_automatic_registration(bool automatic_registration);
|
||||
|
||||
void update_levels(cfg::log_levels levels);
|
||||
// set levels for all existing/future loggers. global_level can be null if should not set.
|
||||
void set_levels(log_levels levels, level::level_enum *global_level);
|
||||
|
||||
static registry &instance();
|
||||
|
||||
@@ -90,13 +91,15 @@ private:
|
||||
|
||||
void throw_if_exists_(const std::string &logger_name);
|
||||
void register_logger_(std::shared_ptr<logger> new_logger);
|
||||
bool set_level_from_cfg_(logger *logger);
|
||||
std::mutex logger_map_mutex_, flusher_mutex_;
|
||||
std::recursive_mutex tp_mutex_;
|
||||
std::unordered_map<std::string, std::shared_ptr<logger>> loggers_;
|
||||
cfg::log_levels levels_;
|
||||
log_levels log_levels_;
|
||||
std::unique_ptr<formatter> formatter_;
|
||||
spdlog::level::level_enum global_log_level_ = level::info;
|
||||
level::level_enum flush_level_ = level::off;
|
||||
void (*err_handler_)(const std::string &msg);
|
||||
err_handler err_handler_;
|
||||
std::shared_ptr<thread_pool> tp_;
|
||||
std::unique_ptr<periodic_worker> periodic_flusher_;
|
||||
std::shared_ptr<logger> default_logger_;
|
||||
@@ -108,5 +111,5 @@ private:
|
||||
} // namespace spdlog
|
||||
|
||||
#ifdef SPDLOG_HEADER_ONLY
|
||||
#include "registry-inl.h"
|
||||
# include "registry-inl.h"
|
||||
#endif
|
||||
|
@@ -13,7 +13,7 @@ class logger;
|
||||
struct synchronous_factory
|
||||
{
|
||||
template<typename Sink, typename... SinkArgs>
|
||||
static std::shared_ptr<spdlog::logger> create(std::string logger_name, SinkArgs &&... args)
|
||||
static std::shared_ptr<spdlog::logger> create(std::string logger_name, SinkArgs &&...args)
|
||||
{
|
||||
auto sink = std::make_shared<Sink>(std::forward<SinkArgs>(args)...);
|
||||
auto new_logger = std::make_shared<spdlog::logger>(std::move(logger_name), std::move(sink));
|
||||
@@ -21,4 +21,4 @@ struct synchronous_factory
|
||||
return new_logger;
|
||||
}
|
||||
};
|
||||
} // namespace spdlog
|
||||
} // namespace spdlog
|
||||
|
@@ -8,8 +8,8 @@
|
||||
#include <spdlog/common.h>
|
||||
#include <spdlog/details/os.h>
|
||||
|
||||
#include <windows.h>
|
||||
#include <winsock2.h>
|
||||
#include <windows.h>
|
||||
#include <ws2tcpip.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
@@ -144,7 +144,7 @@ public:
|
||||
|
||||
// set TCP_NODELAY
|
||||
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.
|
||||
|
@@ -4,7 +4,7 @@
|
||||
#pragma once
|
||||
|
||||
#ifdef _WIN32
|
||||
#error include tcp_client-windows.h instead
|
||||
# error include tcp_client-windows.h instead
|
||||
#endif
|
||||
|
||||
// tcp client helper
|
||||
@@ -75,7 +75,11 @@ public:
|
||||
int last_errno = 0;
|
||||
for (auto *rp = addrinfo_result; rp != nullptr; rp = rp->ai_next)
|
||||
{
|
||||
int const flags = SOCK_CLOEXEC;
|
||||
#if defined(SOCK_CLOEXEC)
|
||||
const int flags = SOCK_CLOEXEC;
|
||||
#else
|
||||
const int flags = 0;
|
||||
#endif
|
||||
socket_ = ::socket(rp->ai_family, rp->ai_socktype | flags, rp->ai_protocol);
|
||||
if (socket_ == -1)
|
||||
{
|
||||
@@ -87,12 +91,9 @@ public:
|
||||
{
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
last_errno = errno;
|
||||
::close(socket_);
|
||||
socket_ = -1;
|
||||
}
|
||||
last_errno = errno;
|
||||
::close(socket_);
|
||||
socket_ = -1;
|
||||
}
|
||||
::freeaddrinfo(addrinfo_result);
|
||||
if (socket_ == -1)
|
||||
@@ -102,15 +103,15 @@ public:
|
||||
|
||||
// set TCP_NODELAY
|
||||
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
|
||||
#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
|
||||
|
||||
#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
|
||||
}
|
||||
|
||||
|
@@ -4,7 +4,7 @@
|
||||
#pragma once
|
||||
|
||||
#ifndef SPDLOG_HEADER_ONLY
|
||||
#include <spdlog/details/thread_pool.h>
|
||||
# include <spdlog/details/thread_pool.h>
|
||||
#endif
|
||||
|
||||
#include <spdlog/common.h>
|
||||
@@ -49,7 +49,7 @@ SPDLOG_INLINE thread_pool::~thread_pool()
|
||||
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)
|
||||
@@ -68,6 +68,11 @@ size_t SPDLOG_INLINE thread_pool::overrun_counter()
|
||||
return q_.overrun_counter();
|
||||
}
|
||||
|
||||
size_t SPDLOG_INLINE thread_pool::queue_size()
|
||||
{
|
||||
return q_.size();
|
||||
}
|
||||
|
||||
void SPDLOG_INLINE thread_pool::post_async_msg_(async_msg &&new_msg, async_overflow_policy overflow_policy)
|
||||
{
|
||||
if (overflow_policy == async_overflow_policy::block)
|
||||
|
@@ -27,7 +27,6 @@ enum class async_msg_type
|
||||
terminate
|
||||
};
|
||||
|
||||
#include <spdlog/details/log_msg_buffer.h>
|
||||
// Async msg to move to/from the queue
|
||||
// Movable only. should never be copied
|
||||
struct async_msg : log_msg_buffer
|
||||
@@ -97,6 +96,7 @@ public:
|
||||
void post_log(async_logger_ptr &&worker_ptr, const details::log_msg &msg, async_overflow_policy overflow_policy);
|
||||
void post_flush(async_logger_ptr &&worker_ptr, async_overflow_policy overflow_policy);
|
||||
size_t overrun_counter();
|
||||
size_t queue_size();
|
||||
|
||||
private:
|
||||
q_type q_;
|
||||
@@ -116,5 +116,5 @@ private:
|
||||
} // namespace spdlog
|
||||
|
||||
#ifdef SPDLOG_HEADER_ONLY
|
||||
#include "thread_pool-inl.h"
|
||||
# include "thread_pool-inl.h"
|
||||
#endif
|
||||
|
@@ -1,11 +1,11 @@
|
||||
#pragma once
|
||||
|
||||
#ifndef NOMINMAX
|
||||
#define NOMINMAX // prevent windows redefining min/max
|
||||
# define NOMINMAX // prevent windows redefining min/max
|
||||
#endif
|
||||
|
||||
#ifndef WIN32_LEAN_AND_MEAN
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
# define WIN32_LEAN_AND_MEAN
|
||||
#endif
|
||||
|
||||
#include <windows.h>
|
||||
|
@@ -6,10 +6,11 @@
|
||||
#pragma once
|
||||
|
||||
#include <cctype>
|
||||
#include <spdlog/common.h>
|
||||
|
||||
//
|
||||
// Support for logging binary data as hex
|
||||
// format flags:
|
||||
// format flags, any combination of the followng:
|
||||
// {:X} - print in uppercase.
|
||||
// {:s} - don't separate each byte with space.
|
||||
// {:p} - don't print the position on each line start.
|
||||
@@ -89,10 +90,10 @@ struct formatter<spdlog::details::dump_info<T>>
|
||||
|
||||
// parse the format string flags
|
||||
template<typename ParseContext>
|
||||
auto parse(ParseContext &ctx) -> decltype(ctx.begin())
|
||||
FMT_CONSTEXPR auto parse(ParseContext &ctx) -> decltype(ctx.begin())
|
||||
{
|
||||
auto it = ctx.begin();
|
||||
while (*it && *it != '}')
|
||||
while (it != ctx.end() && *it != '}')
|
||||
{
|
||||
switch (*it)
|
||||
{
|
||||
@@ -209,7 +210,7 @@ struct formatter<spdlog::details::dump_info<T>>
|
||||
|
||||
if (put_positions)
|
||||
{
|
||||
fmt::format_to(inserter, "{:<04X}: ", pos);
|
||||
fmt::format_to(inserter, "{:04X}: ", pos);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@@ -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.
|
232
include/spdlog/fmt/bundled/args.h
Normal file
232
include/spdlog/fmt/bundled/args.h
Normal 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_
|
@@ -8,13 +8,13 @@
|
||||
#ifndef FMT_CHRONO_H_
|
||||
#define FMT_CHRONO_H_
|
||||
|
||||
#include <algorithm>
|
||||
#include <chrono>
|
||||
#include <ctime>
|
||||
#include <locale>
|
||||
#include <sstream>
|
||||
|
||||
#include "format.h"
|
||||
#include "locale.h"
|
||||
|
||||
FMT_BEGIN_NAMESPACE
|
||||
|
||||
@@ -48,7 +48,7 @@ FMT_CONSTEXPR To lossless_integral_conversion(const From from, int& ec) {
|
||||
// From fits in To without any problem.
|
||||
} else {
|
||||
// From does not always fit in To, resort to a dynamic check.
|
||||
if (from < T::min() || from > T::max()) {
|
||||
if (from < (T::min)() || from > (T::max)()) {
|
||||
// outside range.
|
||||
ec = 1;
|
||||
return {};
|
||||
@@ -72,43 +72,27 @@ FMT_CONSTEXPR To lossless_integral_conversion(const From from, int& ec) {
|
||||
static_assert(F::is_integer, "From must be integral");
|
||||
static_assert(T::is_integer, "To must be integral");
|
||||
|
||||
if (F::is_signed && !T::is_signed) {
|
||||
if (detail::const_check(F::is_signed && !T::is_signed)) {
|
||||
// From may be negative, not allowed!
|
||||
if (fmt::internal::is_negative(from)) {
|
||||
if (fmt::detail::is_negative(from)) {
|
||||
ec = 1;
|
||||
return {};
|
||||
}
|
||||
|
||||
// From is positive. Can it always fit in To?
|
||||
if (F::digits <= T::digits) {
|
||||
// yes, From always fits in To.
|
||||
} else {
|
||||
// from may not fit in To, we have to do a dynamic check
|
||||
if (from > static_cast<From>(T::max())) {
|
||||
ec = 1;
|
||||
return {};
|
||||
}
|
||||
if (F::digits > T::digits &&
|
||||
from > static_cast<From>(detail::max_value<To>())) {
|
||||
ec = 1;
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
if (!F::is_signed && T::is_signed) {
|
||||
// can from be held in To?
|
||||
if (F::digits < T::digits) {
|
||||
// yes, From always fits in To.
|
||||
} else {
|
||||
// from may not fit in To, we have to do a dynamic check
|
||||
if (from > static_cast<From>(T::max())) {
|
||||
// outside range.
|
||||
ec = 1;
|
||||
return {};
|
||||
}
|
||||
}
|
||||
if (!F::is_signed && T::is_signed && F::digits >= T::digits &&
|
||||
from > static_cast<From>(detail::max_value<To>())) {
|
||||
ec = 1;
|
||||
return {};
|
||||
}
|
||||
|
||||
// reaching here means all is ok for lossless conversion.
|
||||
return static_cast<To>(from);
|
||||
|
||||
} // function
|
||||
return static_cast<To>(from); // Lossless conversion.
|
||||
}
|
||||
|
||||
template <typename To, typename From,
|
||||
FMT_ENABLE_IF(std::is_same<From, To>::value)>
|
||||
@@ -141,7 +125,7 @@ FMT_CONSTEXPR To safe_float_conversion(const From from, int& ec) {
|
||||
|
||||
// catch the only happy case
|
||||
if (std::isfinite(from)) {
|
||||
if (from >= T::lowest() && from <= T::max()) {
|
||||
if (from >= T::lowest() && from <= (T::max)()) {
|
||||
return static_cast<To>(from);
|
||||
}
|
||||
// not within range.
|
||||
@@ -190,17 +174,16 @@ To safe_duration_cast(std::chrono::duration<FromRep, FromPeriod> from,
|
||||
// safe conversion to IntermediateRep
|
||||
IntermediateRep count =
|
||||
lossless_integral_conversion<IntermediateRep>(from.count(), ec);
|
||||
if (ec) {
|
||||
return {};
|
||||
}
|
||||
if (ec) return {};
|
||||
// multiply with Factor::num without overflow or underflow
|
||||
if (Factor::num != 1) {
|
||||
const auto max1 = internal::max_value<IntermediateRep>() / Factor::num;
|
||||
if (detail::const_check(Factor::num != 1)) {
|
||||
const auto max1 = detail::max_value<IntermediateRep>() / Factor::num;
|
||||
if (count > max1) {
|
||||
ec = 1;
|
||||
return {};
|
||||
}
|
||||
const auto min1 = std::numeric_limits<IntermediateRep>::min() / Factor::num;
|
||||
const auto min1 =
|
||||
(std::numeric_limits<IntermediateRep>::min)() / Factor::num;
|
||||
if (count < min1) {
|
||||
ec = 1;
|
||||
return {};
|
||||
@@ -208,17 +191,9 @@ To safe_duration_cast(std::chrono::duration<FromRep, FromPeriod> from,
|
||||
count *= Factor::num;
|
||||
}
|
||||
|
||||
// this can't go wrong, right? den>0 is checked earlier.
|
||||
if (Factor::den != 1) {
|
||||
count /= Factor::den;
|
||||
}
|
||||
// convert to the to type, safely
|
||||
using ToRep = typename To::rep;
|
||||
const ToRep tocount = lossless_integral_conversion<ToRep>(count, ec);
|
||||
if (ec) {
|
||||
return {};
|
||||
}
|
||||
return To{tocount};
|
||||
if (detail::const_check(Factor::den != 1)) count /= Factor::den;
|
||||
auto tocount = lossless_integral_conversion<typename To::rep>(count, ec);
|
||||
return ec ? To() : To(tocount);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -269,7 +244,7 @@ To safe_duration_cast(std::chrono::duration<FromRep, FromPeriod> from,
|
||||
|
||||
// multiply with Factor::num without overflow or underflow
|
||||
if (Factor::num != 1) {
|
||||
constexpr auto max1 = internal::max_value<IntermediateRep>() /
|
||||
constexpr auto max1 = detail::max_value<IntermediateRep>() /
|
||||
static_cast<IntermediateRep>(Factor::num);
|
||||
if (count > max1) {
|
||||
ec = 1;
|
||||
@@ -306,14 +281,90 @@ To safe_duration_cast(std::chrono::duration<FromRep, FromPeriod> from,
|
||||
// Usage: f FMT_NOMACRO()
|
||||
#define FMT_NOMACRO
|
||||
|
||||
namespace internal {
|
||||
namespace detail {
|
||||
template <typename T = void> struct null {};
|
||||
inline null<> localtime_r FMT_NOMACRO(...) { return null<>(); }
|
||||
inline null<> localtime_s(...) { return null<>(); }
|
||||
inline null<> gmtime_r(...) { return null<>(); }
|
||||
inline null<> gmtime_s(...) { return null<>(); }
|
||||
} // namespace internal
|
||||
|
||||
// Thread-safe replacement for std::localtime
|
||||
inline 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
|
||||
|
||||
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) {
|
||||
struct dispatcher {
|
||||
std::time_t time_;
|
||||
@@ -322,22 +373,22 @@ inline std::tm localtime(std::time_t time) {
|
||||
dispatcher(std::time_t t) : time_(t) {}
|
||||
|
||||
bool run() {
|
||||
using namespace fmt::internal;
|
||||
using namespace fmt::detail;
|
||||
return handle(localtime_r(&time_, &tm_));
|
||||
}
|
||||
|
||||
bool handle(std::tm* tm) { return tm != nullptr; }
|
||||
|
||||
bool handle(internal::null<>) {
|
||||
using namespace fmt::internal;
|
||||
bool handle(detail::null<>) {
|
||||
using namespace fmt::detail;
|
||||
return fallback(localtime_s(&tm_, &time_));
|
||||
}
|
||||
|
||||
bool fallback(int res) { return res == 0; }
|
||||
|
||||
#if !FMT_MSC_VER
|
||||
bool fallback(internal::null<>) {
|
||||
using namespace fmt::internal;
|
||||
bool fallback(detail::null<>) {
|
||||
using namespace fmt::detail;
|
||||
std::tm* tm = std::localtime(&time_);
|
||||
if (tm) tm_ = *tm;
|
||||
return tm != nullptr;
|
||||
@@ -350,7 +401,16 @@ inline std::tm localtime(std::time_t time) {
|
||||
return lt.tm_;
|
||||
}
|
||||
|
||||
// Thread-safe replacement for std::gmtime
|
||||
inline std::tm localtime(
|
||||
std::chrono::time_point<std::chrono::system_clock> time_point) {
|
||||
return localtime(std::chrono::system_clock::to_time_t(time_point));
|
||||
}
|
||||
|
||||
/**
|
||||
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) {
|
||||
struct dispatcher {
|
||||
std::time_t time_;
|
||||
@@ -359,21 +419,21 @@ inline std::tm gmtime(std::time_t time) {
|
||||
dispatcher(std::time_t t) : time_(t) {}
|
||||
|
||||
bool run() {
|
||||
using namespace fmt::internal;
|
||||
using namespace fmt::detail;
|
||||
return handle(gmtime_r(&time_, &tm_));
|
||||
}
|
||||
|
||||
bool handle(std::tm* tm) { return tm != nullptr; }
|
||||
|
||||
bool handle(internal::null<>) {
|
||||
using namespace fmt::internal;
|
||||
bool handle(detail::null<>) {
|
||||
using namespace fmt::detail;
|
||||
return fallback(gmtime_s(&tm_, &time_));
|
||||
}
|
||||
|
||||
bool fallback(int res) { return res == 0; }
|
||||
|
||||
#if !FMT_MSC_VER
|
||||
bool fallback(internal::null<>) {
|
||||
bool fallback(detail::null<>) {
|
||||
std::tm* tm = std::gmtime(&time_);
|
||||
if (tm) tm_ = *tm;
|
||||
return tm != nullptr;
|
||||
@@ -386,86 +446,132 @@ inline std::tm gmtime(std::time_t time) {
|
||||
return gt.tm_;
|
||||
}
|
||||
|
||||
namespace internal {
|
||||
inline std::size_t strftime(char* str, std::size_t count, const char* format,
|
||||
const std::tm* time) {
|
||||
return std::strftime(str, count, format, time);
|
||||
inline std::tm gmtime(
|
||||
std::chrono::time_point<std::chrono::system_clock> time_point) {
|
||||
return gmtime(std::chrono::system_clock::to_time_t(time_point));
|
||||
}
|
||||
|
||||
inline std::size_t strftime(wchar_t* str, std::size_t count,
|
||||
const wchar_t* format, const std::tm* time) {
|
||||
return std::wcsftime(str, count, format, time);
|
||||
}
|
||||
} // namespace internal
|
||||
FMT_BEGIN_DETAIL_NAMESPACE
|
||||
|
||||
inline size_t strftime(char* str, size_t count, const char* format,
|
||||
const std::tm* 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,
|
||||
const std::tm* 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);
|
||||
}
|
||||
|
||||
FMT_END_DETAIL_NAMESPACE
|
||||
|
||||
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 Char> struct formatter<std::tm, Char> {
|
||||
template <typename ParseContext>
|
||||
auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
|
||||
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;
|
||||
tm_format.reserve(internal::to_unsigned(end - it + 1));
|
||||
tm_format.append(it, end);
|
||||
tm_format.push_back('\0');
|
||||
if (end != it) this->specs = {it, detail::to_unsigned(end - it)};
|
||||
return end;
|
||||
}
|
||||
|
||||
template <typename FormatContext>
|
||||
auto format(const std::tm& tm, FormatContext& ctx) -> decltype(ctx.out()) {
|
||||
auto format(std::chrono::time_point<std::chrono::system_clock> val,
|
||||
FormatContext& ctx) -> decltype(ctx.out()) {
|
||||
std::tm time = localtime(val);
|
||||
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 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;
|
||||
specs = {it, detail::to_unsigned(end - it)};
|
||||
return end;
|
||||
}
|
||||
|
||||
template <typename FormatContext>
|
||||
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;
|
||||
std::size_t start = buf.size();
|
||||
size_t start = buf.size();
|
||||
for (;;) {
|
||||
std::size_t size = buf.capacity() - start;
|
||||
std::size_t count =
|
||||
internal::strftime(&buf[start], size, &tm_format[0], &tm);
|
||||
size_t size = buf.capacity() - start;
|
||||
size_t count = detail::strftime(&buf[start], size, &tm_format[0], &tm);
|
||||
if (count != 0) {
|
||||
buf.resize(start + count);
|
||||
break;
|
||||
}
|
||||
if (size >= tm_format.size() * 256) {
|
||||
// If the buffer is 256 times larger than the format string, assume
|
||||
// that `strftime` gives an empty result. There doesn't seem to be a
|
||||
// better way to distinguish the two cases:
|
||||
// https://github.com/fmtlib/fmt/issues/367
|
||||
break;
|
||||
}
|
||||
const std::size_t MIN_GROWTH = 10;
|
||||
const size_t MIN_GROWTH = 10;
|
||||
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 internal {
|
||||
template <typename Period> FMT_CONSTEXPR const char* get_units() {
|
||||
FMT_BEGIN_DETAIL_NAMESPACE
|
||||
|
||||
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;
|
||||
}
|
||||
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 {
|
||||
standard,
|
||||
@@ -631,33 +737,50 @@ FMT_CONSTEXPR const Char* parse_chrono_format(const Char* begin,
|
||||
return ptr;
|
||||
}
|
||||
|
||||
struct chrono_format_checker {
|
||||
FMT_NORETURN void report_no_date() { FMT_THROW(format_error("no date")); }
|
||||
template <typename Derived> struct null_chrono_spec_handler {
|
||||
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*) {}
|
||||
FMT_NORETURN void on_abbr_weekday() { report_no_date(); }
|
||||
FMT_NORETURN void on_full_weekday() { report_no_date(); }
|
||||
FMT_NORETURN void on_dec0_weekday(numeric_system) { report_no_date(); }
|
||||
FMT_NORETURN void on_dec1_weekday(numeric_system) { report_no_date(); }
|
||||
FMT_NORETURN void on_abbr_month() { report_no_date(); }
|
||||
FMT_NORETURN void on_full_month() { report_no_date(); }
|
||||
void on_24_hour(numeric_system) {}
|
||||
void on_12_hour(numeric_system) {}
|
||||
void on_minute(numeric_system) {}
|
||||
void on_second(numeric_system) {}
|
||||
FMT_NORETURN void on_datetime(numeric_system) { report_no_date(); }
|
||||
FMT_NORETURN void on_loc_date(numeric_system) { report_no_date(); }
|
||||
FMT_NORETURN void on_loc_time(numeric_system) { report_no_date(); }
|
||||
FMT_NORETURN void on_us_date() { report_no_date(); }
|
||||
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(); }
|
||||
struct chrono_format_checker : null_chrono_spec_handler<chrono_format_checker> {
|
||||
FMT_NORETURN void unsupported() { FMT_THROW(format_error("no date")); }
|
||||
|
||||
template <typename Char>
|
||||
FMT_CONSTEXPR void on_text(const Char*, const Char*) {}
|
||||
FMT_CONSTEXPR void on_24_hour(numeric_system) {}
|
||||
FMT_CONSTEXPR void on_12_hour(numeric_system) {}
|
||||
FMT_CONSTEXPR void on_minute(numeric_system) {}
|
||||
FMT_CONSTEXPR void on_second(numeric_system) {}
|
||||
FMT_CONSTEXPR void on_12_hour_time() {}
|
||||
FMT_CONSTEXPR void on_24_hour_time() {}
|
||||
FMT_CONSTEXPR void on_iso_time() {}
|
||||
FMT_CONSTEXPR void on_am_pm() {}
|
||||
FMT_CONSTEXPR void on_duration_value() {}
|
||||
FMT_CONSTEXPR void on_duration_unit() {}
|
||||
};
|
||||
|
||||
template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)>
|
||||
@@ -681,7 +804,8 @@ inline bool isfinite(T value) {
|
||||
// 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)>
|
||||
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;
|
||||
return static_cast<int>(value);
|
||||
}
|
||||
@@ -759,30 +883,47 @@ inline std::chrono::duration<Rep, std::milli> get_milliseconds(
|
||||
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,
|
||||
FMT_ENABLE_IF(std::is_integral<Rep>::value)>
|
||||
OutputIt format_duration_value(OutputIt out, Rep val, int) {
|
||||
return write<Char>(out, 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) {
|
||||
const Char pr_f[] = {'{', ':', '.', '{', '}', 'f', '}', 0};
|
||||
if (precision >= 0) return format_to(out, pr_f, val, precision);
|
||||
const Char fp_f[] = {'{', ':', 'g', '}', 0};
|
||||
const Char format[] = {'{', '}', 0};
|
||||
return format_to(out, std::is_floating_point<Rep>::value ? fp_f : format,
|
||||
val);
|
||||
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>
|
||||
OutputIt copy_unit(string_view unit, OutputIt out, Char) {
|
||||
return std::copy(unit.begin(), unit.end(), out);
|
||||
}
|
||||
|
||||
template <typename OutputIt>
|
||||
OutputIt copy_unit(string_view unit, OutputIt out, wchar_t) {
|
||||
// This works when wchar_t is UTF-32 because units only contain characters
|
||||
// that have the same representation in UTF-16 and UTF-32.
|
||||
utf8_to_utf16 u(unit);
|
||||
return std::copy(u.c_str(), u.c_str() + u.size(), out);
|
||||
}
|
||||
|
||||
template <typename Char, typename Period, typename OutputIt>
|
||||
OutputIt format_duration_unit(OutputIt out) {
|
||||
if (const char* unit = get_units<Period>()) {
|
||||
string_view s(unit);
|
||||
if (const_check(std::is_same<Char, wchar_t>())) {
|
||||
utf8_to_utf16 u(s);
|
||||
return std::copy(u.c_str(), u.c_str() + u.size(), out);
|
||||
}
|
||||
return std::copy(s.begin(), s.end(), out);
|
||||
if (const char* unit = get_units<Period>())
|
||||
return copy_unit(string_view(unit), out, Char());
|
||||
*out++ = '[';
|
||||
out = write<Char>(out, Period::num);
|
||||
if (const_check(Period::den != 1)) {
|
||||
*out++ = '/';
|
||||
out = write<Char>(out, Period::den);
|
||||
}
|
||||
const Char num_f[] = {'[', '{', '}', ']', 's', 0};
|
||||
if (Period::den == 1) return format_to(out, num_f, Period::num);
|
||||
const Char num_def_f[] = {'[', '{', '}', '/', '{', '}', ']', 's', 0};
|
||||
return format_to(out, num_def_f, Period::num, Period::den);
|
||||
*out++ = ']';
|
||||
*out++ = 's';
|
||||
return out;
|
||||
}
|
||||
|
||||
template <typename FormatContext, typename OutputIt, typename Rep,
|
||||
@@ -791,6 +932,7 @@ struct chrono_formatter {
|
||||
FormatContext& context;
|
||||
OutputIt out;
|
||||
int precision;
|
||||
bool localized = false;
|
||||
// rep is unsigned to avoid overflow.
|
||||
using rep =
|
||||
conditional_t<std::is_integral<Rep>::value && sizeof(Rep) < sizeof(int),
|
||||
@@ -874,9 +1016,9 @@ struct chrono_formatter {
|
||||
if (isnan(value)) return write_nan();
|
||||
uint32_or_64_or_128_t<int> n =
|
||||
to_unsigned(to_nonnegative_int(value, max_value<int>()));
|
||||
int num_digits = internal::count_digits(n);
|
||||
int num_digits = detail::count_digits(n);
|
||||
if (width > num_digits) out = std::fill_n(out, width - num_digits, '0');
|
||||
out = format_decimal<char_type>(out, n, num_digits);
|
||||
out = format_decimal<char_type>(out, n, num_digits).end;
|
||||
}
|
||||
|
||||
void write_nan() { std::copy_n("nan", 3, out); }
|
||||
@@ -885,13 +1027,9 @@ struct chrono_formatter {
|
||||
|
||||
void format_localized(const tm& time, char format, char modifier = 0) {
|
||||
if (isnan(val)) return write_nan();
|
||||
auto locale = context.locale().template get<std::locale>();
|
||||
auto& facet = std::use_facet<std::time_put<char_type>>(locale);
|
||||
std::basic_ostringstream<char_type> os;
|
||||
os.imbue(locale);
|
||||
facet.put(os, os, ' ', &time, format, modifier);
|
||||
auto str = os.str();
|
||||
std::copy(str.begin(), str.end(), out);
|
||||
const auto& loc = localized ? context.locale().template get<std::locale>()
|
||||
: std::locale::classic();
|
||||
out = detail::write(out, time, loc, format, modifier);
|
||||
}
|
||||
|
||||
void on_text(const char_type* begin, const char_type* end) {
|
||||
@@ -1004,17 +1142,59 @@ struct chrono_formatter {
|
||||
out = format_duration_unit<char_type, Period>(out);
|
||||
}
|
||||
};
|
||||
} // namespace internal
|
||||
|
||||
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>
|
||||
struct formatter<std::chrono::duration<Rep, Period>, Char> {
|
||||
private:
|
||||
basic_format_specs<Char> specs;
|
||||
int precision;
|
||||
using arg_ref_type = internal::arg_ref<Char>;
|
||||
int precision = -1;
|
||||
using arg_ref_type = detail::arg_ref<Char>;
|
||||
arg_ref_type width_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>;
|
||||
|
||||
struct spec_handler {
|
||||
@@ -1032,22 +1212,26 @@ struct formatter<std::chrono::duration<Rep, Period>, Char> {
|
||||
return arg_ref_type(arg_id);
|
||||
}
|
||||
|
||||
FMT_CONSTEXPR arg_ref_type make_arg_ref(internal::auto_id) {
|
||||
FMT_CONSTEXPR arg_ref_type make_arg_ref(detail::auto_id) {
|
||||
return arg_ref_type(context.next_arg_id());
|
||||
}
|
||||
|
||||
void on_error(const char* msg) { FMT_THROW(format_error(msg)); }
|
||||
void on_fill(basic_string_view<Char> fill) { f.specs.fill = fill; }
|
||||
void on_align(align_t align) { f.specs.align = align; }
|
||||
void on_width(int width) { f.specs.width = width; }
|
||||
void on_precision(int _precision) { f.precision = _precision; }
|
||||
void end_precision() {}
|
||||
FMT_CONSTEXPR void on_fill(basic_string_view<Char> fill) {
|
||||
f.specs.fill = fill;
|
||||
}
|
||||
FMT_CONSTEXPR void on_align(align_t align) { f.specs.align = align; }
|
||||
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);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
};
|
||||
@@ -1062,58 +1246,63 @@ struct formatter<std::chrono::duration<Rep, Period>, Char> {
|
||||
auto begin = ctx.begin(), end = ctx.end();
|
||||
if (begin == end || *begin == '}') return {begin, begin};
|
||||
spec_handler handler{*this, ctx, format_str};
|
||||
begin = internal::parse_align(begin, end, handler);
|
||||
begin = detail::parse_align(begin, end, handler);
|
||||
if (begin == end) return {begin, begin};
|
||||
begin = internal::parse_width(begin, end, handler);
|
||||
begin = detail::parse_width(begin, end, handler);
|
||||
if (begin == end) return {begin, begin};
|
||||
if (*begin == '.') {
|
||||
if (std::is_floating_point<Rep>::value)
|
||||
begin = internal::parse_precision(begin, end, handler);
|
||||
begin = detail::parse_precision(begin, end, handler);
|
||||
else
|
||||
handler.on_error("precision not allowed for this argument type");
|
||||
}
|
||||
end = parse_chrono_format(begin, end, internal::chrono_format_checker());
|
||||
if (begin != end && *begin == 'L') {
|
||||
++begin;
|
||||
localized = true;
|
||||
}
|
||||
end = parse_chrono_format(begin, end, detail::chrono_format_checker());
|
||||
return {begin, end};
|
||||
}
|
||||
|
||||
public:
|
||||
formatter() : precision(-1) {}
|
||||
|
||||
FMT_CONSTEXPR auto parse(basic_format_parse_context<Char>& ctx)
|
||||
-> decltype(ctx.begin()) {
|
||||
auto range = do_parse(ctx);
|
||||
format_str = basic_string_view<Char>(
|
||||
&*range.begin, internal::to_unsigned(range.end - range.begin));
|
||||
&*range.begin, detail::to_unsigned(range.end - range.begin));
|
||||
return range.end;
|
||||
}
|
||||
|
||||
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();
|
||||
// As a possible future optimization, we could avoid extra copying if width
|
||||
// is not specified.
|
||||
basic_memory_buffer<Char> buf;
|
||||
auto out = std::back_inserter(buf);
|
||||
using range = internal::output_range<decltype(ctx.out()), Char>;
|
||||
internal::basic_writer<range> w(range(ctx.out()));
|
||||
internal::handle_dynamic_spec<internal::width_checker>(specs.width,
|
||||
width_ref, ctx);
|
||||
internal::handle_dynamic_spec<internal::precision_checker>(
|
||||
precision, precision_ref, ctx);
|
||||
detail::handle_dynamic_spec<detail::width_checker>(specs_copy.width,
|
||||
width_ref, ctx);
|
||||
detail::handle_dynamic_spec<detail::precision_checker>(precision_copy,
|
||||
precision_ref, ctx);
|
||||
if (begin == end || *begin == '}') {
|
||||
out = internal::format_duration_value<Char>(out, d.count(), precision);
|
||||
internal::format_duration_unit<Char, Period>(out);
|
||||
out = detail::format_duration_value<Char>(out, d.count(), precision_copy);
|
||||
detail::format_duration_unit<Char, Period>(out);
|
||||
} else {
|
||||
internal::chrono_formatter<FormatContext, decltype(out), Rep, Period> f(
|
||||
detail::chrono_formatter<FormatContext, decltype(out), Rep, Period> f(
|
||||
ctx, out, d);
|
||||
f.precision = precision;
|
||||
parse_chrono_format(begin, end, f);
|
||||
f.precision = precision_copy;
|
||||
f.localized = localized;
|
||||
detail::parse_chrono_format(begin, end, f);
|
||||
}
|
||||
w.write(buf.data(), buf.size(), specs);
|
||||
return w.out();
|
||||
return detail::write(
|
||||
ctx.out(), basic_string_view<Char>(buf.data(), buf.size()), specs_copy);
|
||||
}
|
||||
};
|
||||
|
||||
FMT_MODULE_EXPORT_END
|
||||
FMT_END_NAMESPACE
|
||||
|
||||
#endif // FMT_CHRONO_H_
|
||||
|
@@ -10,7 +10,15 @@
|
||||
|
||||
#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_MODULE_EXPORT_BEGIN
|
||||
|
||||
enum class color : uint32_t {
|
||||
alice_blue = 0xF0F8FF, // rgb(240,248,255)
|
||||
@@ -198,7 +206,7 @@ struct rgb {
|
||||
uint8_t b;
|
||||
};
|
||||
|
||||
namespace internal {
|
||||
FMT_BEGIN_DETAIL_NAMESPACE
|
||||
|
||||
// color is a struct of either a rgb color or a terminal color.
|
||||
struct color_type {
|
||||
@@ -221,9 +229,10 @@ struct color_type {
|
||||
uint32_t rgb_color;
|
||||
} value;
|
||||
};
|
||||
} // namespace internal
|
||||
|
||||
// Experimental text formatting support.
|
||||
FMT_END_DETAIL_NAMESPACE
|
||||
|
||||
/** A text style consisting of foreground and background colors and emphasis. */
|
||||
class text_style {
|
||||
public:
|
||||
FMT_CONSTEXPR text_style(emphasis em = emphasis()) FMT_NOEXCEPT
|
||||
@@ -260,7 +269,55 @@ class text_style {
|
||||
return lhs |= rhs;
|
||||
}
|
||||
|
||||
FMT_CONSTEXPR text_style& operator&=(const text_style& rhs) {
|
||||
FMT_DEPRECATED_NONMSVC FMT_CONSTEXPR text_style& operator&=(
|
||||
const text_style& rhs) {
|
||||
return and_assign(rhs);
|
||||
}
|
||||
|
||||
FMT_DEPRECATED_NONMSVC friend FMT_CONSTEXPR text_style
|
||||
operator&(text_style lhs, const text_style& rhs) {
|
||||
return lhs.and_assign(rhs);
|
||||
}
|
||||
|
||||
FMT_CONSTEXPR bool has_foreground() const FMT_NOEXCEPT {
|
||||
return set_foreground_color;
|
||||
}
|
||||
FMT_CONSTEXPR bool has_background() const FMT_NOEXCEPT {
|
||||
return set_background_color;
|
||||
}
|
||||
FMT_CONSTEXPR bool has_emphasis() const FMT_NOEXCEPT {
|
||||
return static_cast<uint8_t>(ems) != 0;
|
||||
}
|
||||
FMT_CONSTEXPR detail::color_type get_foreground() const FMT_NOEXCEPT {
|
||||
FMT_ASSERT(has_foreground(), "no foreground specified for this style");
|
||||
return foreground_color;
|
||||
}
|
||||
FMT_CONSTEXPR detail::color_type get_background() const FMT_NOEXCEPT {
|
||||
FMT_ASSERT(has_background(), "no background specified for this style");
|
||||
return background_color;
|
||||
}
|
||||
FMT_CONSTEXPR emphasis get_emphasis() const FMT_NOEXCEPT {
|
||||
FMT_ASSERT(has_emphasis(), "no emphasis specified for this style");
|
||||
return ems;
|
||||
}
|
||||
|
||||
private:
|
||||
FMT_CONSTEXPR text_style(bool is_foreground,
|
||||
detail::color_type text_color) FMT_NOEXCEPT
|
||||
: set_foreground_color(),
|
||||
set_background_color(),
|
||||
ems() {
|
||||
if (is_foreground) {
|
||||
foreground_color = text_color;
|
||||
set_foreground_color = true;
|
||||
} else {
|
||||
background_color = text_color;
|
||||
set_background_color = true;
|
||||
}
|
||||
}
|
||||
|
||||
// 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;
|
||||
@@ -284,87 +341,49 @@ class text_style {
|
||||
return *this;
|
||||
}
|
||||
|
||||
friend FMT_CONSTEXPR text_style operator&(text_style lhs,
|
||||
const text_style& rhs) {
|
||||
return lhs &= rhs;
|
||||
}
|
||||
|
||||
FMT_CONSTEXPR bool has_foreground() const FMT_NOEXCEPT {
|
||||
return set_foreground_color;
|
||||
}
|
||||
FMT_CONSTEXPR bool has_background() const FMT_NOEXCEPT {
|
||||
return set_background_color;
|
||||
}
|
||||
FMT_CONSTEXPR bool has_emphasis() const FMT_NOEXCEPT {
|
||||
return static_cast<uint8_t>(ems) != 0;
|
||||
}
|
||||
FMT_CONSTEXPR internal::color_type get_foreground() const FMT_NOEXCEPT {
|
||||
FMT_ASSERT(has_foreground(), "no foreground specified for this style");
|
||||
return foreground_color;
|
||||
}
|
||||
FMT_CONSTEXPR internal::color_type get_background() const FMT_NOEXCEPT {
|
||||
FMT_ASSERT(has_background(), "no background specified for this style");
|
||||
return background_color;
|
||||
}
|
||||
FMT_CONSTEXPR emphasis get_emphasis() const FMT_NOEXCEPT {
|
||||
FMT_ASSERT(has_emphasis(), "no emphasis specified for this style");
|
||||
return ems;
|
||||
}
|
||||
|
||||
private:
|
||||
FMT_CONSTEXPR text_style(bool is_foreground,
|
||||
internal::color_type text_color) FMT_NOEXCEPT
|
||||
: set_foreground_color(),
|
||||
set_background_color(),
|
||||
ems() {
|
||||
if (is_foreground) {
|
||||
foreground_color = text_color;
|
||||
set_foreground_color = true;
|
||||
} else {
|
||||
background_color = text_color;
|
||||
set_background_color = true;
|
||||
}
|
||||
}
|
||||
|
||||
friend FMT_CONSTEXPR_DECL text_style fg(internal::color_type foreground)
|
||||
FMT_NOEXCEPT;
|
||||
friend FMT_CONSTEXPR_DECL text_style bg(internal::color_type background)
|
||||
friend FMT_CONSTEXPR_DECL text_style fg(detail::color_type foreground)
|
||||
FMT_NOEXCEPT;
|
||||
|
||||
internal::color_type foreground_color;
|
||||
internal::color_type background_color;
|
||||
friend FMT_CONSTEXPR_DECL text_style bg(detail::color_type background)
|
||||
FMT_NOEXCEPT;
|
||||
|
||||
detail::color_type foreground_color;
|
||||
detail::color_type background_color;
|
||||
bool set_foreground_color;
|
||||
bool set_background_color;
|
||||
emphasis ems;
|
||||
};
|
||||
|
||||
FMT_CONSTEXPR text_style fg(internal::color_type foreground) FMT_NOEXCEPT {
|
||||
return text_style(/*is_foreground=*/true, foreground);
|
||||
/** Creates a text style from the foreground (text) color. */
|
||||
FMT_CONSTEXPR inline text_style fg(detail::color_type foreground) FMT_NOEXCEPT {
|
||||
return text_style(true, foreground);
|
||||
}
|
||||
|
||||
FMT_CONSTEXPR text_style bg(internal::color_type background) FMT_NOEXCEPT {
|
||||
return text_style(/*is_foreground=*/false, background);
|
||||
/** Creates a text style from the background color. */
|
||||
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;
|
||||
}
|
||||
|
||||
namespace internal {
|
||||
FMT_BEGIN_DETAIL_NAMESPACE
|
||||
|
||||
template <typename Char> struct ansi_color_escape {
|
||||
FMT_CONSTEXPR ansi_color_escape(internal::color_type text_color,
|
||||
FMT_CONSTEXPR ansi_color_escape(detail::color_type text_color,
|
||||
const char* esc) FMT_NOEXCEPT {
|
||||
// If we have a terminal color, we need to output another escape code
|
||||
// sequence.
|
||||
if (!text_color.is_rgb) {
|
||||
bool is_background = esc == internal::data::background_color;
|
||||
bool is_background = esc == string_view("\x1b[48;2;");
|
||||
uint32_t value = text_color.value.term_color;
|
||||
// Background ASCII codes are the same as the foreground ones but with
|
||||
// 10 more.
|
||||
if (is_background) value += 10u;
|
||||
|
||||
std::size_t index = 0;
|
||||
size_t index = 0;
|
||||
buffer[index++] = static_cast<Char>('\x1b');
|
||||
buffer[index++] = static_cast<Char>('[');
|
||||
|
||||
@@ -398,7 +417,7 @@ template <typename Char> struct ansi_color_escape {
|
||||
if (em_bits & static_cast<uint8_t>(emphasis::strikethrough))
|
||||
em_codes[3] = 9;
|
||||
|
||||
std::size_t index = 0;
|
||||
size_t index = 0;
|
||||
for (int i = 0; i < 4; ++i) {
|
||||
if (!em_codes[i]) continue;
|
||||
buffer[index++] = static_cast<Char>('\x1b');
|
||||
@@ -411,7 +430,7 @@ template <typename Char> struct ansi_color_escape {
|
||||
FMT_CONSTEXPR operator const Char*() 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);
|
||||
}
|
||||
|
||||
@@ -429,14 +448,14 @@ template <typename Char> struct ansi_color_escape {
|
||||
|
||||
template <typename Char>
|
||||
FMT_CONSTEXPR ansi_color_escape<Char> make_foreground_color(
|
||||
internal::color_type foreground) FMT_NOEXCEPT {
|
||||
return ansi_color_escape<Char>(foreground, internal::data::foreground_color);
|
||||
detail::color_type foreground) FMT_NOEXCEPT {
|
||||
return ansi_color_escape<Char>(foreground, "\x1b[38;2;");
|
||||
}
|
||||
|
||||
template <typename Char>
|
||||
FMT_CONSTEXPR ansi_color_escape<Char> make_background_color(
|
||||
internal::color_type background) FMT_NOEXCEPT {
|
||||
return ansi_color_escape<Char>(background, internal::data::background_color);
|
||||
detail::color_type background) FMT_NOEXCEPT {
|
||||
return ansi_color_escape<Char>(background, "\x1b[48;2;");
|
||||
}
|
||||
|
||||
template <typename Char>
|
||||
@@ -455,82 +474,86 @@ inline void fputs<wchar_t>(const wchar_t* chars, FILE* stream) FMT_NOEXCEPT {
|
||||
}
|
||||
|
||||
template <typename Char> inline void reset_color(FILE* stream) FMT_NOEXCEPT {
|
||||
fputs(internal::data::reset_color, stream);
|
||||
fputs("\x1b[0m", stream);
|
||||
}
|
||||
|
||||
template <> inline void reset_color<wchar_t>(FILE* stream) FMT_NOEXCEPT {
|
||||
fputs(internal::data::wreset_color, stream);
|
||||
fputs(L"\x1b[0m", stream);
|
||||
}
|
||||
|
||||
template <typename Char>
|
||||
inline void reset_color(basic_memory_buffer<Char>& buffer) FMT_NOEXCEPT {
|
||||
const char* begin = data::reset_color;
|
||||
const char* end = begin + sizeof(data::reset_color) - 1;
|
||||
buffer.append(begin, end);
|
||||
inline void reset_color(buffer<Char>& buffer) FMT_NOEXCEPT {
|
||||
auto reset_color = string_view("\x1b[0m");
|
||||
buffer.append(reset_color.begin(), reset_color.end());
|
||||
}
|
||||
|
||||
template <typename Char>
|
||||
void vformat_to(basic_memory_buffer<Char>& buf, const text_style& ts,
|
||||
void vformat_to(buffer<Char>& buf, const text_style& ts,
|
||||
basic_string_view<Char> format_str,
|
||||
basic_format_args<buffer_context<Char>> args) {
|
||||
basic_format_args<buffer_context<type_identity_t<Char>>> args) {
|
||||
bool has_style = false;
|
||||
if (ts.has_emphasis()) {
|
||||
has_style = true;
|
||||
auto emphasis = internal::make_emphasis<Char>(ts.get_emphasis());
|
||||
auto emphasis = detail::make_emphasis<Char>(ts.get_emphasis());
|
||||
buf.append(emphasis.begin(), emphasis.end());
|
||||
}
|
||||
if (ts.has_foreground()) {
|
||||
has_style = true;
|
||||
auto foreground =
|
||||
internal::make_foreground_color<Char>(ts.get_foreground());
|
||||
auto foreground = detail::make_foreground_color<Char>(ts.get_foreground());
|
||||
buf.append(foreground.begin(), foreground.end());
|
||||
}
|
||||
if (ts.has_background()) {
|
||||
has_style = true;
|
||||
auto background =
|
||||
internal::make_background_color<Char>(ts.get_background());
|
||||
auto background = detail::make_background_color<Char>(ts.get_background());
|
||||
buf.append(background.begin(), background.end());
|
||||
}
|
||||
internal::vformat_to(buf, format_str, args);
|
||||
if (has_style) internal::reset_color<Char>(buf);
|
||||
detail::vformat_to(buf, format_str, args, {});
|
||||
if (has_style) detail::reset_color<Char>(buf);
|
||||
}
|
||||
} // namespace internal
|
||||
|
||||
FMT_END_DETAIL_NAMESPACE
|
||||
|
||||
template <typename S, typename Char = char_t<S>>
|
||||
void vprint(std::FILE* f, const text_style& ts, const S& format,
|
||||
basic_format_args<buffer_context<Char>> args) {
|
||||
basic_format_args<buffer_context<type_identity_t<Char>>> args) {
|
||||
basic_memory_buffer<Char> buf;
|
||||
internal::vformat_to(buf, ts, to_string_view(format), args);
|
||||
detail::vformat_to(buf, ts, to_string_view(format), args);
|
||||
buf.push_back(Char(0));
|
||||
internal::fputs(buf.data(), f);
|
||||
detail::fputs(buf.data(), f);
|
||||
}
|
||||
|
||||
/**
|
||||
\rst
|
||||
Formats a string and prints it to the specified file stream using ANSI
|
||||
escape sequences to specify text formatting.
|
||||
Example:
|
||||
|
||||
**Example**::
|
||||
|
||||
fmt::print(fmt::emphasis::bold | fg(fmt::color::red),
|
||||
"Elapsed time: {0:.2f} seconds", 1.23);
|
||||
\endrst
|
||||
*/
|
||||
template <typename S, typename... Args,
|
||||
FMT_ENABLE_IF(internal::is_string<S>::value)>
|
||||
FMT_ENABLE_IF(detail::is_string<S>::value)>
|
||||
void print(std::FILE* f, const text_style& ts, const S& format_str,
|
||||
const Args&... args) {
|
||||
internal::check_format_string<Args...>(format_str);
|
||||
using context = buffer_context<char_t<S>>;
|
||||
format_arg_store<context, Args...> as{args...};
|
||||
vprint(f, ts, format_str, basic_format_args<context>(as));
|
||||
vprint(f, ts, format_str,
|
||||
fmt::make_args_checked<Args...>(format_str, args...));
|
||||
}
|
||||
|
||||
/**
|
||||
\rst
|
||||
Formats a string and prints it to stdout using ANSI escape sequences to
|
||||
specify text formatting.
|
||||
Example:
|
||||
|
||||
**Example**::
|
||||
|
||||
fmt::print(fmt::emphasis::bold | fg(fmt::color::red),
|
||||
"Elapsed time: {0:.2f} seconds", 1.23);
|
||||
\endrst
|
||||
*/
|
||||
template <typename S, typename... Args,
|
||||
FMT_ENABLE_IF(internal::is_string<S>::value)>
|
||||
FMT_ENABLE_IF(detail::is_string<S>::value)>
|
||||
void print(const text_style& ts, const S& format_str, const Args&... args) {
|
||||
return print(stdout, ts, format_str, args...);
|
||||
}
|
||||
@@ -540,7 +563,7 @@ inline std::basic_string<Char> vformat(
|
||||
const text_style& ts, const S& format_str,
|
||||
basic_format_args<buffer_context<type_identity_t<Char>>> args) {
|
||||
basic_memory_buffer<Char> buf;
|
||||
internal::vformat_to(buf, ts, to_string_view(format_str), args);
|
||||
detail::vformat_to(buf, ts, to_string_view(format_str), args);
|
||||
return fmt::to_string(buf);
|
||||
}
|
||||
|
||||
@@ -559,10 +582,46 @@ inline std::basic_string<Char> vformat(
|
||||
template <typename S, typename... Args, typename Char = char_t<S>>
|
||||
inline std::basic_string<Char> format(const text_style& ts, const S& format_str,
|
||||
const Args&... args) {
|
||||
return vformat(ts, to_string_view(format_str),
|
||||
internal::make_args_checked<Args...>(format_str, args...));
|
||||
return fmt::vformat(ts, to_string_view(format_str),
|
||||
fmt::make_args_checked<Args...>(format_str, args...));
|
||||
}
|
||||
|
||||
/**
|
||||
Formats a string with the given text_style and writes the output to ``out``.
|
||||
*/
|
||||
template <typename OutputIt, typename Char,
|
||||
FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, Char>::value)>
|
||||
OutputIt vformat_to(
|
||||
OutputIt out, const text_style& ts, basic_string_view<Char> format_str,
|
||||
basic_format_args<buffer_context<type_identity_t<Char>>> args) {
|
||||
auto&& buf = detail::get_buffer<Char>(out);
|
||||
detail::vformat_to(buf, ts, format_str, args);
|
||||
return detail::get_iterator(buf);
|
||||
}
|
||||
|
||||
/**
|
||||
\rst
|
||||
Formats arguments with the given text_style, writes the result to the output
|
||||
iterator ``out`` and returns the iterator past the end of the output range.
|
||||
|
||||
**Example**::
|
||||
|
||||
std::vector<char> out;
|
||||
fmt::format_to(std::back_inserter(out),
|
||||
fmt::emphasis::bold | fg(fmt::color::red), "{}", 42);
|
||||
\endrst
|
||||
*/
|
||||
template <typename OutputIt, typename S, typename... Args,
|
||||
bool enable = detail::is_output_iterator<OutputIt, char_t<S>>::value&&
|
||||
detail::is_string<S>::value>
|
||||
inline auto format_to(OutputIt out, const text_style& ts, const S& format_str,
|
||||
Args&&... args) ->
|
||||
typename std::enable_if<enable, OutputIt>::type {
|
||||
return vformat_to(out, ts, to_string_view(format_str),
|
||||
fmt::make_args_checked<Args...>(format_str, args...));
|
||||
}
|
||||
|
||||
FMT_MODULE_EXPORT_END
|
||||
FMT_END_NAMESPACE
|
||||
|
||||
#endif // FMT_COLOR_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
File diff suppressed because it is too large
Load Diff
@@ -1,78 +1,2 @@
|
||||
// Formatting library for C++ - std::locale support
|
||||
//
|
||||
// Copyright (c) 2012 - present, Victor Zverovich
|
||||
// All rights reserved.
|
||||
//
|
||||
// For the license information refer to format.h.
|
||||
|
||||
#ifndef FMT_LOCALE_H_
|
||||
#define FMT_LOCALE_H_
|
||||
|
||||
#include <locale>
|
||||
|
||||
#include "format.h"
|
||||
|
||||
FMT_BEGIN_NAMESPACE
|
||||
|
||||
namespace internal {
|
||||
template <typename Char>
|
||||
typename buffer_context<Char>::iterator vformat_to(
|
||||
const std::locale& loc, buffer<Char>& buf,
|
||||
basic_string_view<Char> format_str,
|
||||
basic_format_args<buffer_context<type_identity_t<Char>>> args) {
|
||||
using range = buffer_range<Char>;
|
||||
return vformat_to<arg_formatter<range>>(buf, to_string_view(format_str), args,
|
||||
internal::locale_ref(loc));
|
||||
}
|
||||
|
||||
template <typename Char>
|
||||
std::basic_string<Char> vformat(
|
||||
const std::locale& loc, basic_string_view<Char> format_str,
|
||||
basic_format_args<buffer_context<type_identity_t<Char>>> args) {
|
||||
basic_memory_buffer<Char> buffer;
|
||||
internal::vformat_to(loc, buffer, format_str, args);
|
||||
return fmt::to_string(buffer);
|
||||
}
|
||||
} // namespace internal
|
||||
|
||||
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 internal::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 internal::vformat(
|
||||
loc, to_string_view(format_str),
|
||||
internal::make_args_checked<Args...>(format_str, args...));
|
||||
}
|
||||
|
||||
template <typename S, typename OutputIt, typename... Args,
|
||||
typename Char = enable_if_t<
|
||||
internal::is_output_iterator<OutputIt>::value, char_t<S>>>
|
||||
inline OutputIt vformat_to(
|
||||
OutputIt out, const std::locale& loc, const S& format_str,
|
||||
format_args_t<type_identity_t<OutputIt>, Char> args) {
|
||||
using range = internal::output_range<OutputIt, Char>;
|
||||
return vformat_to<arg_formatter<range>>(
|
||||
range(out), to_string_view(format_str), args, internal::locale_ref(loc));
|
||||
}
|
||||
|
||||
template <typename OutputIt, typename S, typename... Args,
|
||||
FMT_ENABLE_IF(internal::is_output_iterator<OutputIt>::value&&
|
||||
internal::is_string<S>::value)>
|
||||
inline OutputIt format_to(OutputIt out, const std::locale& loc,
|
||||
const S& format_str, Args&&... args) {
|
||||
internal::check_format_string<Args...>(format_str);
|
||||
using context = format_context_t<OutputIt, char_t<S>>;
|
||||
format_arg_store<context, Args...> as{args...};
|
||||
return vformat_to(out, loc, to_string_view(format_str),
|
||||
basic_format_args<context>(as));
|
||||
}
|
||||
|
||||
FMT_END_NAMESPACE
|
||||
|
||||
#endif // FMT_LOCALE_H_
|
||||
#include "xchar.h"
|
||||
#warning fmt/locale.h is deprecated, include fmt/format.h or fmt/xchar.h instead
|
||||
|
515
include/spdlog/fmt/bundled/os.h
Normal file
515
include/spdlog/fmt/bundled/os.h
Normal file
@@ -0,0 +1,515 @@
|
||||
// Formatting library for C++ - optional OS-specific functionality
|
||||
//
|
||||
// Copyright (c) 2012 - present, Victor Zverovich
|
||||
// All rights reserved.
|
||||
//
|
||||
// For the license information refer to format.h.
|
||||
|
||||
#ifndef FMT_OS_H_
|
||||
#define FMT_OS_H_
|
||||
|
||||
#include <cerrno>
|
||||
#include <clocale> // locale_t
|
||||
#include <cstddef>
|
||||
#include <cstdio>
|
||||
#include <cstdlib> // strtod_l
|
||||
#include <system_error> // std::system_error
|
||||
|
||||
#if defined __APPLE__ || defined(__FreeBSD__)
|
||||
# include <xlocale.h> // for LC_NUMERIC_MASK on OS X
|
||||
#endif
|
||||
|
||||
#include "format.h"
|
||||
|
||||
// UWP doesn't provide _pipe.
|
||||
#if FMT_HAS_INCLUDE("winapifamily.h")
|
||||
# include <winapifamily.h>
|
||||
#endif
|
||||
#if (FMT_HAS_INCLUDE(<fcntl.h>) || defined(__APPLE__) || \
|
||||
defined(__linux__)) && \
|
||||
(!defined(WINAPI_FAMILY) || (WINAPI_FAMILY == WINAPI_FAMILY_DESKTOP_APP))
|
||||
# include <fcntl.h> // for O_RDONLY
|
||||
# define FMT_USE_FCNTL 1
|
||||
#else
|
||||
# define FMT_USE_FCNTL 0
|
||||
#endif
|
||||
|
||||
#ifndef FMT_POSIX
|
||||
# if defined(_WIN32) && !defined(__MINGW32__)
|
||||
// Fix warnings about deprecated symbols.
|
||||
# define FMT_POSIX(call) _##call
|
||||
# else
|
||||
# define FMT_POSIX(call) call
|
||||
# endif
|
||||
#endif
|
||||
|
||||
// Calls to system functions are wrapped in FMT_SYSTEM for testability.
|
||||
#ifdef FMT_SYSTEM
|
||||
# define FMT_POSIX_CALL(call) FMT_SYSTEM(call)
|
||||
#else
|
||||
# define FMT_SYSTEM(call) ::call
|
||||
# ifdef _WIN32
|
||||
// Fix warnings about deprecated symbols.
|
||||
# define FMT_POSIX_CALL(call) ::_##call
|
||||
# else
|
||||
# define FMT_POSIX_CALL(call) ::call
|
||||
# endif
|
||||
#endif
|
||||
|
||||
// Retries the expression while it evaluates to error_result and errno
|
||||
// equals to EINTR.
|
||||
#ifndef _WIN32
|
||||
# define FMT_RETRY_VAL(result, expression, error_result) \
|
||||
do { \
|
||||
(result) = (expression); \
|
||||
} while ((result) == (error_result) && errno == EINTR)
|
||||
#else
|
||||
# define FMT_RETRY_VAL(result, expression, error_result) result = (expression)
|
||||
#endif
|
||||
|
||||
#define FMT_RETRY(result, expression) FMT_RETRY_VAL(result, expression, -1)
|
||||
|
||||
FMT_BEGIN_NAMESPACE
|
||||
FMT_MODULE_EXPORT_BEGIN
|
||||
|
||||
/**
|
||||
\rst
|
||||
A reference to a null-terminated string. It can be constructed from a C
|
||||
string or ``std::string``.
|
||||
|
||||
You can use one of the following type aliases for common character types:
|
||||
|
||||
+---------------+-----------------------------+
|
||||
| Type | Definition |
|
||||
+===============+=============================+
|
||||
| cstring_view | basic_cstring_view<char> |
|
||||
+---------------+-----------------------------+
|
||||
| wcstring_view | basic_cstring_view<wchar_t> |
|
||||
+---------------+-----------------------------+
|
||||
|
||||
This class is most useful as a parameter type to allow passing
|
||||
different types of strings to a function, for example::
|
||||
|
||||
template <typename... Args>
|
||||
std::string format(cstring_view format_str, const Args & ... args);
|
||||
|
||||
format("{}", 42);
|
||||
format(std::string("{}"), 42);
|
||||
\endrst
|
||||
*/
|
||||
template <typename Char> class basic_cstring_view {
|
||||
private:
|
||||
const Char* data_;
|
||||
|
||||
public:
|
||||
/** Constructs a string reference object from a C string. */
|
||||
basic_cstring_view(const Char* s) : data_(s) {}
|
||||
|
||||
/**
|
||||
\rst
|
||||
Constructs a string reference from an ``std::string`` object.
|
||||
\endrst
|
||||
*/
|
||||
basic_cstring_view(const std::basic_string<Char>& s) : data_(s.c_str()) {}
|
||||
|
||||
/** Returns the pointer to a C string. */
|
||||
const Char* c_str() const { return data_; }
|
||||
};
|
||||
|
||||
using cstring_view = basic_cstring_view<char>;
|
||||
using wcstring_view = basic_cstring_view<wchar_t>;
|
||||
|
||||
template <typename Char> struct formatter<std::error_code, Char> {
|
||||
template <typename ParseContext>
|
||||
FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
|
||||
return ctx.begin();
|
||||
}
|
||||
|
||||
template <typename FormatContext>
|
||||
FMT_CONSTEXPR auto format(const std::error_code& ec, FormatContext& ctx) const
|
||||
-> decltype(ctx.out()) {
|
||||
auto out = ctx.out();
|
||||
out = detail::write_bytes(out, ec.category().name(),
|
||||
basic_format_specs<Char>());
|
||||
out = detail::write<Char>(out, Char(':'));
|
||||
out = detail::write<Char>(out, ec.value());
|
||||
return out;
|
||||
}
|
||||
};
|
||||
|
||||
#ifdef _WIN32
|
||||
FMT_API const std::error_category& system_category() FMT_NOEXCEPT;
|
||||
|
||||
FMT_BEGIN_DETAIL_NAMESPACE
|
||||
// A converter from UTF-16 to UTF-8.
|
||||
// It is only provided for Windows since other systems support UTF-8 natively.
|
||||
class utf16_to_utf8 {
|
||||
private:
|
||||
memory_buffer buffer_;
|
||||
|
||||
public:
|
||||
utf16_to_utf8() {}
|
||||
FMT_API explicit utf16_to_utf8(basic_string_view<wchar_t> s);
|
||||
operator string_view() const { return string_view(&buffer_[0], size()); }
|
||||
size_t size() const { return buffer_.size() - 1; }
|
||||
const char* c_str() const { return &buffer_[0]; }
|
||||
std::string str() const { return std::string(&buffer_[0], size()); }
|
||||
|
||||
// Performs conversion returning a system error code instead of
|
||||
// throwing exception on conversion error. This method may still throw
|
||||
// in case of memory allocation error.
|
||||
FMT_API int convert(basic_string_view<wchar_t> s);
|
||||
};
|
||||
|
||||
FMT_API void format_windows_error(buffer<char>& out, int error_code,
|
||||
const char* message) FMT_NOEXCEPT;
|
||||
FMT_END_DETAIL_NAMESPACE
|
||||
|
||||
FMT_API std::system_error vwindows_error(int error_code, string_view format_str,
|
||||
format_args args);
|
||||
|
||||
/**
|
||||
\rst
|
||||
Constructs a :class:`std::system_error` object with the description
|
||||
of the form
|
||||
|
||||
.. parsed-literal::
|
||||
*<message>*: *<system-message>*
|
||||
|
||||
where *<message>* is the formatted message and *<system-message>* is the
|
||||
system message corresponding to the error code.
|
||||
*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
|
||||
will look like "error -1".
|
||||
|
||||
**Example**::
|
||||
|
||||
// This throws a system_error with the description
|
||||
// cannot open file 'madeup': The system cannot find the file specified.
|
||||
// or similar (system message may vary).
|
||||
const char *filename = "madeup";
|
||||
LPOFSTRUCT of = LPOFSTRUCT();
|
||||
HFILE file = OpenFile(filename, &of, OF_READ);
|
||||
if (file == HFILE_ERROR) {
|
||||
throw fmt::windows_error(GetLastError(),
|
||||
"cannot open file '{}'", filename);
|
||||
}
|
||||
\endrst
|
||||
*/
|
||||
template <typename... Args>
|
||||
std::system_error windows_error(int error_code, string_view message,
|
||||
const Args&... args) {
|
||||
return vwindows_error(error_code, message, fmt::make_format_args(args...));
|
||||
}
|
||||
|
||||
// Reports a Windows error without throwing an exception.
|
||||
// Can be used to report errors from destructors.
|
||||
FMT_API void report_windows_error(int error_code,
|
||||
const char* message) FMT_NOEXCEPT;
|
||||
#else
|
||||
inline const std::error_category& system_category() FMT_NOEXCEPT {
|
||||
return std::system_category();
|
||||
}
|
||||
#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.
|
||||
class buffered_file {
|
||||
private:
|
||||
FILE* file_;
|
||||
|
||||
friend class file;
|
||||
|
||||
explicit buffered_file(FILE* f) : file_(f) {}
|
||||
|
||||
public:
|
||||
buffered_file(const buffered_file&) = delete;
|
||||
void operator=(const buffered_file&) = delete;
|
||||
|
||||
// Constructs a buffered_file object which doesn't represent any file.
|
||||
buffered_file() FMT_NOEXCEPT : file_(nullptr) {}
|
||||
|
||||
// Destroys the object closing the file it represents if any.
|
||||
FMT_API ~buffered_file() FMT_NOEXCEPT;
|
||||
|
||||
public:
|
||||
buffered_file(buffered_file&& other) FMT_NOEXCEPT : file_(other.file_) {
|
||||
other.file_ = nullptr;
|
||||
}
|
||||
|
||||
buffered_file& operator=(buffered_file&& other) {
|
||||
close();
|
||||
file_ = other.file_;
|
||||
other.file_ = nullptr;
|
||||
return *this;
|
||||
}
|
||||
|
||||
// Opens a file.
|
||||
FMT_API buffered_file(cstring_view filename, cstring_view mode);
|
||||
|
||||
// Closes the file.
|
||||
FMT_API void close();
|
||||
|
||||
// Returns the pointer to a FILE object representing this file.
|
||||
FILE* get() const FMT_NOEXCEPT { return file_; }
|
||||
|
||||
// We place parentheses around fileno to workaround a bug in some versions
|
||||
// of MinGW that define fileno as a macro.
|
||||
FMT_API int(fileno)() const;
|
||||
|
||||
void vprint(string_view format_str, format_args args) {
|
||||
fmt::vprint(file_, format_str, args);
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
inline void print(string_view format_str, const Args&... args) {
|
||||
vprint(format_str, fmt::make_format_args(args...));
|
||||
}
|
||||
};
|
||||
|
||||
#if FMT_USE_FCNTL
|
||||
// A file. Closed file is represented by a file object with descriptor -1.
|
||||
// Methods that are not declared with FMT_NOEXCEPT may throw
|
||||
// fmt::system_error in case of failure. Note that some errors such as
|
||||
// closing the file multiple times will cause a crash on Windows rather
|
||||
// than an exception. You can get standard behavior by overriding the
|
||||
// invalid parameter handler with _set_invalid_parameter_handler.
|
||||
class file {
|
||||
private:
|
||||
int fd_; // File descriptor.
|
||||
|
||||
// Constructs a file object with a given descriptor.
|
||||
explicit file(int fd) : fd_(fd) {}
|
||||
|
||||
public:
|
||||
// Possible values for the oflag argument to the constructor.
|
||||
enum {
|
||||
RDONLY = FMT_POSIX(O_RDONLY), // Open for reading only.
|
||||
WRONLY = FMT_POSIX(O_WRONLY), // Open for writing only.
|
||||
RDWR = FMT_POSIX(O_RDWR), // Open for reading and writing.
|
||||
CREATE = FMT_POSIX(O_CREAT), // Create if the file doesn't exist.
|
||||
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.
|
||||
file() FMT_NOEXCEPT : fd_(-1) {}
|
||||
|
||||
// Opens a file and constructs a file object representing this file.
|
||||
FMT_API file(cstring_view path, int oflag);
|
||||
|
||||
public:
|
||||
file(const file&) = delete;
|
||||
void operator=(const file&) = delete;
|
||||
|
||||
file(file&& other) FMT_NOEXCEPT : fd_(other.fd_) { other.fd_ = -1; }
|
||||
|
||||
// Move assignment is not noexcept because close may throw.
|
||||
file& operator=(file&& other) {
|
||||
close();
|
||||
fd_ = other.fd_;
|
||||
other.fd_ = -1;
|
||||
return *this;
|
||||
}
|
||||
|
||||
// Destroys the object closing the file it represents if any.
|
||||
FMT_API ~file() FMT_NOEXCEPT;
|
||||
|
||||
// Returns the file descriptor.
|
||||
int descriptor() const FMT_NOEXCEPT { return fd_; }
|
||||
|
||||
// Closes the file.
|
||||
FMT_API void close();
|
||||
|
||||
// Returns the file size. The size has signed type for consistency with
|
||||
// stat::st_size.
|
||||
FMT_API long long size() const;
|
||||
|
||||
// Attempts to read count bytes from the file into the specified buffer.
|
||||
FMT_API size_t read(void* buffer, size_t count);
|
||||
|
||||
// Attempts to write count bytes from the specified buffer to the file.
|
||||
FMT_API size_t write(const void* buffer, size_t count);
|
||||
|
||||
// Duplicates a file descriptor with the dup function and returns
|
||||
// the duplicate as a file object.
|
||||
FMT_API static file dup(int fd);
|
||||
|
||||
// Makes fd be the copy of this file descriptor, closing fd first if
|
||||
// necessary.
|
||||
FMT_API void dup2(int fd);
|
||||
|
||||
// Makes fd be the copy of this file descriptor, closing fd first if
|
||||
// necessary.
|
||||
FMT_API void dup2(int fd, std::error_code& ec) FMT_NOEXCEPT;
|
||||
|
||||
// Creates a pipe setting up read_end and write_end file objects for reading
|
||||
// and writing respectively.
|
||||
FMT_API static void pipe(file& read_end, file& write_end);
|
||||
|
||||
// Creates a buffered_file object associated with this file and detaches
|
||||
// this file object from the file.
|
||||
FMT_API buffered_file fdopen(const char* mode);
|
||||
};
|
||||
|
||||
// Returns the memory page size.
|
||||
long getpagesize();
|
||||
|
||||
FMT_BEGIN_DETAIL_NAMESPACE
|
||||
|
||||
struct buffer_size {
|
||||
buffer_size() = default;
|
||||
size_t value = 0;
|
||||
buffer_size operator=(size_t val) const {
|
||||
auto bs = buffer_size();
|
||||
bs.value = val;
|
||||
return bs;
|
||||
}
|
||||
};
|
||||
|
||||
struct ostream_params {
|
||||
int oflag = file::WRONLY | file::CREATE | file::TRUNC;
|
||||
size_t buffer_size = BUFSIZ > 32768 ? BUFSIZ : 32768;
|
||||
|
||||
ostream_params() {}
|
||||
|
||||
template <typename... T>
|
||||
ostream_params(T... params, int new_oflag) : ostream_params(params...) {
|
||||
oflag = new_oflag;
|
||||
}
|
||||
|
||||
template <typename... T>
|
||||
ostream_params(T... params, detail::buffer_size bs)
|
||||
: ostream_params(params...) {
|
||||
this->buffer_size = bs.value;
|
||||
}
|
||||
};
|
||||
|
||||
FMT_END_DETAIL_NAMESPACE
|
||||
|
||||
constexpr detail::buffer_size buffer_size;
|
||||
|
||||
/** A fast output stream which is not thread-safe. */
|
||||
class FMT_API ostream final : private detail::buffer<char> {
|
||||
private:
|
||||
file file_;
|
||||
|
||||
void flush() {
|
||||
if (size() == 0) return;
|
||||
file_.write(data(), size());
|
||||
clear();
|
||||
}
|
||||
|
||||
void grow(size_t) override;
|
||||
|
||||
ostream(cstring_view path, const detail::ostream_params& params)
|
||||
: file_(path, params.oflag) {
|
||||
set(new char[params.buffer_size], params.buffer_size);
|
||||
}
|
||||
|
||||
public:
|
||||
ostream(ostream&& other)
|
||||
: detail::buffer<char>(other.data(), other.size(), other.capacity()),
|
||||
file_(std::move(other.file_)) {
|
||||
other.clear();
|
||||
other.set(nullptr, 0);
|
||||
}
|
||||
~ostream() {
|
||||
flush();
|
||||
delete[] data();
|
||||
}
|
||||
|
||||
template <typename... T>
|
||||
friend ostream output_file(cstring_view path, T... params);
|
||||
|
||||
void close() {
|
||||
flush();
|
||||
file_.close();
|
||||
}
|
||||
|
||||
/**
|
||||
Formats ``args`` according to specifications in ``fmt`` and writes the
|
||||
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...));
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
\rst
|
||||
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
|
||||
|
||||
**Example**::
|
||||
|
||||
auto out = fmt::output_file("guide.txt");
|
||||
out.print("Don't {}", "Panic");
|
||||
\endrst
|
||||
*/
|
||||
template <typename... T>
|
||||
inline ostream output_file(cstring_view path, T... params) {
|
||||
return {path, detail::ostream_params(params...)};
|
||||
}
|
||||
#endif // FMT_USE_FCNTL
|
||||
|
||||
#ifdef FMT_LOCALE
|
||||
// A "C" numeric locale.
|
||||
class locale {
|
||||
private:
|
||||
# ifdef _WIN32
|
||||
using locale_t = _locale_t;
|
||||
|
||||
static void freelocale(locale_t loc) { _free_locale(loc); }
|
||||
|
||||
static double strtod_l(const char* nptr, char** endptr, _locale_t loc) {
|
||||
return _strtod_l(nptr, endptr, loc);
|
||||
}
|
||||
# endif
|
||||
|
||||
locale_t locale_;
|
||||
|
||||
public:
|
||||
using type = locale_t;
|
||||
locale(const locale&) = delete;
|
||||
void operator=(const locale&) = delete;
|
||||
|
||||
locale() {
|
||||
# ifndef _WIN32
|
||||
locale_ = FMT_SYSTEM(newlocale(LC_NUMERIC_MASK, "C", nullptr));
|
||||
# else
|
||||
locale_ = _create_locale(LC_NUMERIC, "C");
|
||||
# endif
|
||||
if (!locale_) FMT_THROW(system_error(errno, "cannot create locale"));
|
||||
}
|
||||
~locale() { freelocale(locale_); }
|
||||
|
||||
type get() const { return locale_; }
|
||||
|
||||
// Converts string to floating-point number and advances str past the end
|
||||
// of the parsed input.
|
||||
double strtod(const char*& str) const {
|
||||
char* end = nullptr;
|
||||
double result = strtod_l(str, &end, locale_);
|
||||
str = end;
|
||||
return result;
|
||||
}
|
||||
};
|
||||
using Locale FMT_DEPRECATED_ALIAS = locale;
|
||||
#endif // FMT_LOCALE
|
||||
FMT_MODULE_EXPORT_END
|
||||
FMT_END_NAMESPACE
|
||||
|
||||
#endif // FMT_OS_H_
|
@@ -14,10 +14,10 @@
|
||||
|
||||
FMT_BEGIN_NAMESPACE
|
||||
|
||||
template <typename CHar> class basic_printf_parse_context;
|
||||
template <typename Char> class basic_printf_parse_context;
|
||||
template <typename OutputIt, typename Char> class basic_printf_context;
|
||||
|
||||
namespace internal {
|
||||
namespace detail {
|
||||
|
||||
template <class Char> class formatbuf : public std::basic_streambuf<Char> {
|
||||
private:
|
||||
@@ -49,17 +49,27 @@ template <class Char> class formatbuf : public std::basic_streambuf<Char> {
|
||||
}
|
||||
};
|
||||
|
||||
struct converter {
|
||||
template <typename T, FMT_ENABLE_IF(is_integral<T>::value)> converter(T);
|
||||
};
|
||||
|
||||
template <typename Char> struct test_stream : std::basic_ostream<Char> {
|
||||
private:
|
||||
// Hide all operator<< from std::basic_ostream<Char>.
|
||||
void_t<> operator<<(null<>);
|
||||
void_t<> operator<<(const Char*);
|
||||
|
||||
template <typename T, FMT_ENABLE_IF(std::is_convertible<T, int>::value &&
|
||||
!std::is_enum<T>::value)>
|
||||
void_t<> operator<<(T);
|
||||
void_t<> operator<<(converter);
|
||||
};
|
||||
|
||||
// Hide insertion operators for built-in types.
|
||||
template <typename Char, typename Traits>
|
||||
void_t<> operator<<(std::basic_ostream<Char, Traits>&, Char);
|
||||
template <typename Char, typename Traits>
|
||||
void_t<> operator<<(std::basic_ostream<Char, Traits>&, char);
|
||||
template <typename Traits>
|
||||
void_t<> operator<<(std::basic_ostream<char, Traits>&, char);
|
||||
template <typename Traits>
|
||||
void_t<> operator<<(std::basic_ostream<char, Traits>&, signed char);
|
||||
template <typename Traits>
|
||||
void_t<> operator<<(std::basic_ostream<char, Traits>&, unsigned char);
|
||||
|
||||
// Checks if T has a user-defined operator<< (e.g. not a member of
|
||||
// std::ostream).
|
||||
template <typename T, typename Char> class is_streamable {
|
||||
@@ -75,12 +85,14 @@ template <typename T, typename Char> class is_streamable {
|
||||
using result = decltype(test<T>(0));
|
||||
|
||||
public:
|
||||
is_streamable() = default;
|
||||
|
||||
static const bool value = result::value;
|
||||
};
|
||||
|
||||
// Write the content of buf to os.
|
||||
template <typename Char>
|
||||
void write(std::basic_ostream<Char>& os, buffer<Char>& buf) {
|
||||
void write_buffer(std::basic_ostream<Char>& os, buffer<Char>& buf) {
|
||||
const Char* buf_data = buf.data();
|
||||
using unsigned_streamsize = std::make_unsigned<std::streamsize>::type;
|
||||
unsigned_streamsize size = buf.size();
|
||||
@@ -101,16 +113,17 @@ void format_value(buffer<Char>& buf, const T& value,
|
||||
#if !defined(FMT_STATIC_THOUSANDS_SEPARATOR)
|
||||
if (loc) output.imbue(loc.get<std::locale>());
|
||||
#endif
|
||||
output.exceptions(std::ios_base::failbit | std::ios_base::badbit);
|
||||
output << value;
|
||||
buf.resize(buf.size());
|
||||
output.exceptions(std::ios_base::failbit | std::ios_base::badbit);
|
||||
buf.try_resize(buf.size());
|
||||
}
|
||||
|
||||
// Formats an object of type T that has an overloaded ostream operator<<.
|
||||
template <typename T, typename Char>
|
||||
struct fallback_formatter<T, Char, enable_if_t<is_streamable<T, Char>::value>>
|
||||
: private formatter<basic_string_view<Char>, Char> {
|
||||
auto parse(basic_format_parse_context<Char>& ctx) -> decltype(ctx.begin()) {
|
||||
FMT_CONSTEXPR auto parse(basic_format_parse_context<Char>& ctx)
|
||||
-> decltype(ctx.begin()) {
|
||||
return formatter<basic_string_view<Char>, Char>::parse(ctx);
|
||||
}
|
||||
template <typename ParseCtx,
|
||||
@@ -136,14 +149,15 @@ struct fallback_formatter<T, Char, enable_if_t<is_streamable<T, Char>::value>>
|
||||
return std::copy(buffer.begin(), buffer.end(), ctx.out());
|
||||
}
|
||||
};
|
||||
} // namespace internal
|
||||
} // namespace detail
|
||||
|
||||
FMT_MODULE_EXPORT
|
||||
template <typename Char>
|
||||
void vprint(std::basic_ostream<Char>& os, basic_string_view<Char> format_str,
|
||||
basic_format_args<buffer_context<type_identity_t<Char>>> args) {
|
||||
basic_memory_buffer<Char> buffer;
|
||||
internal::vformat_to(buffer, format_str, args);
|
||||
internal::write(os, buffer);
|
||||
detail::vformat_to(buffer, format_str, args);
|
||||
detail::write_buffer(os, buffer);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -155,11 +169,12 @@ void vprint(std::basic_ostream<Char>& os, basic_string_view<Char> format_str,
|
||||
fmt::print(cerr, "Don't {}!", "panic");
|
||||
\endrst
|
||||
*/
|
||||
FMT_MODULE_EXPORT
|
||||
template <typename S, typename... Args,
|
||||
typename Char = enable_if_t<internal::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) {
|
||||
vprint(os, to_string_view(format_str),
|
||||
internal::make_args_checked<Args...>(format_str, args...));
|
||||
fmt::make_args_checked<Args...>(format_str, args...));
|
||||
}
|
||||
FMT_END_NAMESPACE
|
||||
|
||||
|
@@ -1,2 +0,0 @@
|
||||
#include "os.h"
|
||||
#warning "fmt/posix.h is deprecated; use fmt/os.h instead"
|
@@ -10,11 +10,54 @@
|
||||
|
||||
#include <algorithm> // std::max
|
||||
#include <limits> // std::numeric_limits
|
||||
#include <ostream>
|
||||
|
||||
#include "ostream.h"
|
||||
#include "format.h"
|
||||
|
||||
FMT_BEGIN_NAMESPACE
|
||||
namespace internal {
|
||||
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
|
||||
// signed and unsigned integers.
|
||||
@@ -90,11 +133,11 @@ template <typename T, typename Context> class arg_converter {
|
||||
if (const_check(sizeof(target_type) <= sizeof(int))) {
|
||||
// Extra casts are used to silence warnings.
|
||||
if (is_signed) {
|
||||
arg_ = internal::make_arg<Context>(
|
||||
arg_ = detail::make_arg<Context>(
|
||||
static_cast<int>(static_cast<target_type>(value)));
|
||||
} else {
|
||||
using unsigned_type = typename make_unsigned_or_bool<target_type>::type;
|
||||
arg_ = internal::make_arg<Context>(
|
||||
arg_ = detail::make_arg<Context>(
|
||||
static_cast<unsigned>(static_cast<unsigned_type>(value)));
|
||||
}
|
||||
} else {
|
||||
@@ -102,9 +145,9 @@ template <typename T, typename Context> class arg_converter {
|
||||
// glibc's printf doesn't sign extend arguments of smaller types:
|
||||
// std::printf("%lld", -42); // prints "4294967254"
|
||||
// but we don't have to do the same because it's a UB.
|
||||
arg_ = internal::make_arg<Context>(static_cast<long long>(value));
|
||||
arg_ = detail::make_arg<Context>(static_cast<long long>(value));
|
||||
} else {
|
||||
arg_ = internal::make_arg<Context>(
|
||||
arg_ = detail::make_arg<Context>(
|
||||
static_cast<typename make_unsigned_or_bool<U>::type>(value));
|
||||
}
|
||||
}
|
||||
@@ -133,7 +176,7 @@ template <typename Context> class char_converter {
|
||||
|
||||
template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)>
|
||||
void operator()(T value) {
|
||||
arg_ = internal::make_arg<Context>(
|
||||
arg_ = detail::make_arg<Context>(
|
||||
static_cast<typename Context::char_type>(value));
|
||||
}
|
||||
|
||||
@@ -141,6 +184,13 @@ template <typename Context> class char_converter {
|
||||
void operator()(T) {} // No conversion needed for non-integral types.
|
||||
};
|
||||
|
||||
// An argument visitor that return a pointer to a C string if argument is a
|
||||
// string or null otherwise.
|
||||
template <typename Char> struct get_cstring {
|
||||
template <typename T> const Char* operator()(T) { return nullptr; }
|
||||
const Char* operator()(const Char* s) { return s; }
|
||||
};
|
||||
|
||||
// Checks if an argument is a valid printf width specifier and sets
|
||||
// left alignment if it is negative.
|
||||
template <typename Char> class printf_width_handler {
|
||||
@@ -155,7 +205,7 @@ template <typename Char> class printf_width_handler {
|
||||
template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)>
|
||||
unsigned operator()(T value) {
|
||||
auto width = static_cast<uint32_or_64_or_128_t<T>>(value);
|
||||
if (internal::is_negative(value)) {
|
||||
if (detail::is_negative(value)) {
|
||||
specs_.align = align::left;
|
||||
width = 0 - width;
|
||||
}
|
||||
@@ -171,217 +221,86 @@ template <typename Char> class printf_width_handler {
|
||||
}
|
||||
};
|
||||
|
||||
template <typename Char, typename Context>
|
||||
void printf(buffer<Char>& buf, basic_string_view<Char> format,
|
||||
basic_format_args<Context> args) {
|
||||
Context(std::back_inserter(buf), format, args).format();
|
||||
}
|
||||
|
||||
template <typename OutputIt, typename Char, typename Context>
|
||||
internal::truncating_iterator<OutputIt> printf(
|
||||
internal::truncating_iterator<OutputIt> it, basic_string_view<Char> format,
|
||||
basic_format_args<Context> args) {
|
||||
return Context(it, format, args).format();
|
||||
}
|
||||
} // namespace internal
|
||||
|
||||
using internal::printf; // For printing into memory_buffer.
|
||||
|
||||
template <typename Range> class printf_arg_formatter;
|
||||
|
||||
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 Range>
|
||||
class printf_arg_formatter : public internal::arg_formatter_base<Range> {
|
||||
public:
|
||||
using iterator = typename Range::iterator;
|
||||
|
||||
// The ``printf`` argument formatter.
|
||||
template <typename OutputIt, typename Char>
|
||||
class printf_arg_formatter : public arg_formatter<Char> {
|
||||
private:
|
||||
using char_type = typename Range::value_type;
|
||||
using base = internal::arg_formatter_base<Range>;
|
||||
using context_type = basic_printf_context<iterator, char_type>;
|
||||
using base = arg_formatter<Char>;
|
||||
using context_type = basic_printf_context<OutputIt, Char>;
|
||||
using format_specs = basic_format_specs<Char>;
|
||||
|
||||
context_type& context_;
|
||||
|
||||
void write_null_pointer(char) {
|
||||
this->specs()->type = 0;
|
||||
this->write("(nil)");
|
||||
}
|
||||
|
||||
void write_null_pointer(wchar_t) {
|
||||
this->specs()->type = 0;
|
||||
this->write(L"(nil)");
|
||||
OutputIt write_null_pointer(bool is_string = false) {
|
||||
auto s = this->specs;
|
||||
s.type = 0;
|
||||
return write_bytes(this->out, is_string ? "(null)" : "(nil)", s);
|
||||
}
|
||||
|
||||
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) {}
|
||||
|
||||
/**
|
||||
\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(Range(iter), &specs, internal::locale_ref()), context_(ctx) {}
|
||||
OutputIt operator()(monostate value) { return base::operator()(value); }
|
||||
|
||||
template <typename T, FMT_ENABLE_IF(fmt::internal::is_integral<T>::value)>
|
||||
iterator operator()(T value) {
|
||||
// MSVC2013 fails to compile separate overloads for bool and char_type so
|
||||
// use std::is_same instead.
|
||||
if (std::is_same<T, bool>::value) {
|
||||
format_specs& fmt_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();
|
||||
template <typename T, FMT_ENABLE_IF(detail::is_integral<T>::value)>
|
||||
OutputIt operator()(T value) {
|
||||
// MSVC2013 fails to compile separate overloads for bool and Char so use
|
||||
// std::is_same instead.
|
||||
if (std::is_same<T, Char>::value) {
|
||||
format_specs fmt_specs = this->specs;
|
||||
if (fmt_specs.type && fmt_specs.type != 'c')
|
||||
return (*this)(static_cast<int>(value));
|
||||
fmt_specs.sign = sign::none;
|
||||
fmt_specs.alt = false;
|
||||
fmt_specs.align = align::right;
|
||||
return base::operator()(value);
|
||||
} else {
|
||||
return base::operator()(value);
|
||||
fmt_specs.fill[0] = ' '; // Ignore '0' flag for char types.
|
||||
// align::numeric needs to be overwritten here since the '0' flag is
|
||||
// ignored for non-numeric types
|
||||
if (fmt_specs.align == align::none || fmt_specs.align == align::numeric)
|
||||
fmt_specs.align = align::right;
|
||||
return write<Char>(this->out, static_cast<Char>(value), fmt_specs);
|
||||
}
|
||||
return this->out();
|
||||
return base::operator()(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);
|
||||
}
|
||||
|
||||
/** Formats a null-terminated C string. */
|
||||
iterator operator()(const char* value) {
|
||||
if (value)
|
||||
base::operator()(value);
|
||||
else if (this->specs()->type == 'p')
|
||||
write_null_pointer(char_type());
|
||||
else
|
||||
this->write("(null)");
|
||||
return this->out();
|
||||
OutputIt operator()(const char* value) {
|
||||
if (value) return base::operator()(value);
|
||||
return write_null_pointer(this->specs.type != 'p');
|
||||
}
|
||||
|
||||
/** Formats a null-terminated wide C string. */
|
||||
iterator operator()(const wchar_t* value) {
|
||||
if (value)
|
||||
base::operator()(value);
|
||||
else if (this->specs()->type == 'p')
|
||||
write_null_pointer(char_type());
|
||||
else
|
||||
this->write(L"(null)");
|
||||
return this->out();
|
||||
OutputIt operator()(const wchar_t* value) {
|
||||
if (value) return base::operator()(value);
|
||||
return write_null_pointer(this->specs.type != 'p');
|
||||
}
|
||||
|
||||
iterator operator()(basic_string_view<char_type> value) {
|
||||
OutputIt operator()(basic_string_view<Char> value) {
|
||||
return base::operator()(value);
|
||||
}
|
||||
|
||||
iterator operator()(monostate value) { return base::operator()(value); }
|
||||
|
||||
/** Formats a pointer. */
|
||||
iterator operator()(const void* value) {
|
||||
if (value) return base::operator()(value);
|
||||
this->specs()->type = 0;
|
||||
write_null_pointer(char_type());
|
||||
return this->out();
|
||||
OutputIt operator()(const void* value) {
|
||||
return value ? base::operator()(value) : write_null_pointer();
|
||||
}
|
||||
|
||||
/** Formats an argument of a custom (user-defined) type. */
|
||||
iterator operator()(typename basic_format_arg<context_type>::handle handle) {
|
||||
handle.format(context_.parse_context(), context_);
|
||||
return this->out();
|
||||
OutputIt operator()(typename basic_format_arg<context_type>::handle handle) {
|
||||
auto parse_ctx =
|
||||
basic_printf_parse_context<Char>(basic_string_view<Char>());
|
||||
handle.format(parse_ctx, context_);
|
||||
return this->out;
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T> struct printf_formatter {
|
||||
printf_formatter() = delete;
|
||||
|
||||
template <typename ParseContext>
|
||||
auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
|
||||
return ctx.begin();
|
||||
}
|
||||
|
||||
template <typename FormatContext>
|
||||
auto format(const T& value, FormatContext& ctx) -> decltype(ctx.out()) {
|
||||
internal::format_value(internal::get_container(ctx.out()), value);
|
||||
return ctx.out();
|
||||
}
|
||||
};
|
||||
|
||||
/** This template formats data and writes the output to a writer. */
|
||||
template <typename OutputIt, typename Char> 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 and
|
||||
the writer are stored in the context object so make sure they have
|
||||
appropriate lifetimes.
|
||||
\endrst
|
||||
*/
|
||||
basic_printf_context(OutputIt out, basic_string_view<char_type> format_str,
|
||||
basic_format_args<basic_printf_context> args)
|
||||
: out_(out), args_(args), parse_ctx_(format_str) {}
|
||||
|
||||
OutputIt out() { return out_; }
|
||||
void advance_to(OutputIt it) { out_ = it; }
|
||||
|
||||
internal::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<buffer_range<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) {
|
||||
template <typename Char>
|
||||
void parse_flags(basic_format_specs<Char>& specs, const Char*& it,
|
||||
const Char* end) {
|
||||
for (; it != end; ++it) {
|
||||
switch (*it) {
|
||||
case '-':
|
||||
@@ -394,7 +313,9 @@ void basic_printf_context<OutputIt, Char>::parse_flags(format_specs& specs,
|
||||
specs.fill[0] = '0';
|
||||
break;
|
||||
case ' ':
|
||||
specs.sign = sign::space;
|
||||
if (specs.sign != sign::plus) {
|
||||
specs.sign = sign::space;
|
||||
}
|
||||
break;
|
||||
case '#':
|
||||
specs.alt = true;
|
||||
@@ -405,35 +326,24 @@ void basic_printf_context<OutputIt, Char>::parse_flags(format_specs& specs,
|
||||
}
|
||||
}
|
||||
|
||||
template <typename OutputIt, typename Char>
|
||||
typename basic_printf_context<OutputIt, Char>::format_arg
|
||||
basic_printf_context<OutputIt, Char>::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 internal::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) {
|
||||
template <typename Char, typename GetArg>
|
||||
int parse_header(const Char*& it, const Char* end,
|
||||
basic_format_specs<Char>& specs, GetArg get_arg) {
|
||||
int arg_index = -1;
|
||||
char_type c = *it;
|
||||
Char c = *it;
|
||||
if (c >= '0' && c <= '9') {
|
||||
// Parse an argument index (if followed by '$') or a width possibly
|
||||
// preceded with '0' flag(s).
|
||||
internal::error_handler eh;
|
||||
int value = parse_nonnegative_int(it, end, eh);
|
||||
int value = parse_nonnegative_int(it, end, -1);
|
||||
if (it != end && *it == '$') { // value is an argument index
|
||||
++it;
|
||||
arg_index = value;
|
||||
arg_index = value != -1 ? value : max_value<int>();
|
||||
} else {
|
||||
if (c == '0') specs.fill[0] = '0';
|
||||
if (value != 0) {
|
||||
// Nonzero value means that we parsed width and don't need to
|
||||
// parse it or flags again, so return now.
|
||||
if (value == -1) FMT_THROW(format_error("number is too big"));
|
||||
specs.width = value;
|
||||
return arg_index;
|
||||
}
|
||||
@@ -443,71 +353,104 @@ int basic_printf_context<OutputIt, Char>::parse_header(const Char*& it,
|
||||
// Parse width.
|
||||
if (it != end) {
|
||||
if (*it >= '0' && *it <= '9') {
|
||||
internal::error_handler eh;
|
||||
specs.width = parse_nonnegative_int(it, end, eh);
|
||||
specs.width = parse_nonnegative_int(it, end, -1);
|
||||
if (specs.width == -1) FMT_THROW(format_error("number is too big"));
|
||||
} else if (*it == '*') {
|
||||
++it;
|
||||
specs.width = static_cast<int>(visit_format_arg(
|
||||
internal::printf_width_handler<char_type>(specs), get_arg()));
|
||||
detail::printf_width_handler<Char>(specs), get_arg(-1)));
|
||||
}
|
||||
}
|
||||
return arg_index;
|
||||
}
|
||||
|
||||
template <typename OutputIt, typename Char>
|
||||
template <typename ArgFormatter>
|
||||
OutputIt basic_printf_context<OutputIt, Char>::format() {
|
||||
auto out = this->out();
|
||||
const Char* start = parse_ctx_.begin();
|
||||
const Char* end = parse_ctx_.end();
|
||||
template <typename Char, typename Context>
|
||||
void vprintf(buffer<Char>& buf, basic_string_view<Char> format,
|
||||
basic_format_args<Context> args) {
|
||||
using OutputIt = buffer_appender<Char>;
|
||||
auto out = OutputIt(buf);
|
||||
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;
|
||||
while (it != end) {
|
||||
char_type c = *it++;
|
||||
if (c != '%') continue;
|
||||
if (!detail::find<false, Char>(it, end, '%', it)) {
|
||||
it = end; // detail::find leaves it == nullptr if it doesn't find '%'
|
||||
break;
|
||||
}
|
||||
Char c = *it++;
|
||||
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;
|
||||
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;
|
||||
|
||||
// Parse argument index, flags and width.
|
||||
int arg_index = parse_header(it, end, specs);
|
||||
if (arg_index == 0) on_error("argument index out of range");
|
||||
int arg_index = parse_header(it, end, specs, get_arg);
|
||||
if (arg_index == 0) parse_ctx.on_error("argument not found");
|
||||
|
||||
// Parse precision.
|
||||
if (it != end && *it == '.') {
|
||||
++it;
|
||||
c = it != end ? *it : 0;
|
||||
if ('0' <= c && c <= '9') {
|
||||
internal::error_handler eh;
|
||||
specs.precision = parse_nonnegative_int(it, end, eh);
|
||||
specs.precision = parse_nonnegative_int(it, end, 0);
|
||||
} else if (c == '*') {
|
||||
++it;
|
||||
specs.precision = static_cast<int>(
|
||||
visit_format_arg(internal::printf_precision_handler(), get_arg()));
|
||||
visit_format_arg(detail::printf_precision_handler(), get_arg(-1)));
|
||||
} else {
|
||||
specs.precision = 0;
|
||||
}
|
||||
}
|
||||
|
||||
format_arg arg = get_arg(arg_index);
|
||||
if (specs.alt && visit_format_arg(internal::is_zero_int(), arg))
|
||||
auto arg = get_arg(arg_index);
|
||||
// For d, i, o, u, x, and X conversion specifiers, if a precision is
|
||||
// specified, the '0' flag is ignored
|
||||
if (specs.precision >= 0 && arg.is_integral())
|
||||
specs.fill[0] =
|
||||
' '; // Ignore '0' flag for non-numeric types or if '-' present.
|
||||
if (specs.precision >= 0 && arg.type() == detail::type::cstring_type) {
|
||||
auto str = visit_format_arg(detail::get_cstring<Char>(), arg);
|
||||
auto str_end = str + specs.precision;
|
||||
auto nul = std::find(str, str_end, Char());
|
||||
arg = detail::make_arg<basic_printf_context<OutputIt, Char>>(
|
||||
basic_string_view<Char>(
|
||||
str, detail::to_unsigned(nul != str_end ? nul - str
|
||||
: specs.precision)));
|
||||
}
|
||||
if (specs.alt && visit_format_arg(detail::is_zero_int(), arg))
|
||||
specs.alt = false;
|
||||
if (specs.fill[0] == '0') {
|
||||
if (arg.is_arithmetic())
|
||||
if (arg.is_arithmetic() && specs.align != align::left)
|
||||
specs.align = align::numeric;
|
||||
else
|
||||
specs.fill[0] = ' '; // Ignore '0' flag for non-numeric types.
|
||||
specs.fill[0] = ' '; // Ignore '0' flag for non-numeric types or if '-'
|
||||
// flag is also present.
|
||||
}
|
||||
|
||||
// Parse length and convert the argument to the required type.
|
||||
c = it != end ? *it++ : 0;
|
||||
char_type t = it != end ? *it : 0;
|
||||
using internal::convert_arg;
|
||||
Char t = it != end ? *it : 0;
|
||||
using detail::convert_arg;
|
||||
switch (c) {
|
||||
case 'h':
|
||||
if (t == 'h') {
|
||||
@@ -531,7 +474,7 @@ OutputIt basic_printf_context<OutputIt, Char>::format() {
|
||||
convert_arg<intmax_t>(arg, t);
|
||||
break;
|
||||
case 'z':
|
||||
convert_arg<std::size_t>(arg, t);
|
||||
convert_arg<size_t>(arg, t);
|
||||
break;
|
||||
case 't':
|
||||
convert_arg<std::ptrdiff_t>(arg, t);
|
||||
@@ -556,8 +499,9 @@ OutputIt basic_printf_context<OutputIt, Char>::format() {
|
||||
specs.type = 'd';
|
||||
break;
|
||||
case 'c':
|
||||
visit_format_arg(internal::char_converter<basic_printf_context>(arg),
|
||||
arg);
|
||||
visit_format_arg(
|
||||
detail::char_converter<basic_printf_context<OutputIt, Char>>(arg),
|
||||
arg);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -565,15 +509,16 @@ OutputIt basic_printf_context<OutputIt, Char>::format() {
|
||||
start = it;
|
||||
|
||||
// Format argument.
|
||||
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>
|
||||
using basic_printf_context_t =
|
||||
basic_printf_context<std::back_insert_iterator<internal::buffer<Char>>,
|
||||
Char>;
|
||||
basic_printf_context<detail::buffer_appender<Char>, Char>;
|
||||
|
||||
using printf_context = basic_printf_context_t<char>;
|
||||
using wprintf_context = basic_printf_context_t<wchar_t>;
|
||||
@@ -587,9 +532,9 @@ using wprintf_args = basic_format_args<wprintf_context>;
|
||||
arguments and can be implicitly converted to `~fmt::printf_args`.
|
||||
\endrst
|
||||
*/
|
||||
template <typename... Args>
|
||||
inline format_arg_store<printf_context, Args...> make_printf_args(
|
||||
const Args&... args) {
|
||||
template <typename... T>
|
||||
inline auto make_printf_args(const T&... args)
|
||||
-> format_arg_store<printf_context, T...> {
|
||||
return {args...};
|
||||
}
|
||||
|
||||
@@ -599,18 +544,19 @@ inline format_arg_store<printf_context, Args...> make_printf_args(
|
||||
arguments and can be implicitly converted to `~fmt::wprintf_args`.
|
||||
\endrst
|
||||
*/
|
||||
template <typename... Args>
|
||||
inline format_arg_store<wprintf_context, Args...> make_wprintf_args(
|
||||
const Args&... args) {
|
||||
template <typename... T>
|
||||
inline auto make_wprintf_args(const T&... args)
|
||||
-> format_arg_store<wprintf_context, T...> {
|
||||
return {args...};
|
||||
}
|
||||
|
||||
template <typename S, typename Char = char_t<S>>
|
||||
inline std::basic_string<Char> vsprintf(
|
||||
const S& format,
|
||||
basic_format_args<basic_printf_context_t<type_identity_t<Char>>> args) {
|
||||
inline auto vsprintf(
|
||||
const S& fmt,
|
||||
basic_format_args<basic_printf_context_t<type_identity_t<Char>>> args)
|
||||
-> std::basic_string<Char> {
|
||||
basic_memory_buffer<Char> buffer;
|
||||
printf(buffer, to_string_view(format), args);
|
||||
vprintf(buffer, to_string_view(fmt), args);
|
||||
return to_string(buffer);
|
||||
}
|
||||
|
||||
@@ -623,20 +569,21 @@ inline std::basic_string<Char> vsprintf(
|
||||
std::string message = fmt::sprintf("The answer is %d", 42);
|
||||
\endrst
|
||||
*/
|
||||
template <typename S, typename... Args,
|
||||
typename Char = enable_if_t<internal::is_string<S>::value, char_t<S>>>
|
||||
inline std::basic_string<Char> sprintf(const S& format, const Args&... args) {
|
||||
template <typename S, typename... T,
|
||||
typename Char = enable_if_t<detail::is_string<S>::value, char_t<S>>>
|
||||
inline auto sprintf(const S& fmt, const T&... args) -> std::basic_string<Char> {
|
||||
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>>
|
||||
inline int vfprintf(
|
||||
std::FILE* f, const S& format,
|
||||
basic_format_args<basic_printf_context_t<type_identity_t<Char>>> args) {
|
||||
inline auto vfprintf(
|
||||
std::FILE* f, const S& fmt,
|
||||
basic_format_args<basic_printf_context_t<type_identity_t<Char>>> args)
|
||||
-> int {
|
||||
basic_memory_buffer<Char> buffer;
|
||||
printf(buffer, to_string_view(format), args);
|
||||
std::size_t size = buffer.size();
|
||||
vprintf(buffer, to_string_view(fmt), args);
|
||||
size_t size = buffer.size();
|
||||
return std::fwrite(buffer.data(), sizeof(Char), size, f) < size
|
||||
? -1
|
||||
: static_cast<int>(size);
|
||||
@@ -651,19 +598,19 @@ inline int vfprintf(
|
||||
fmt::fprintf(stderr, "Don't %s!", "panic");
|
||||
\endrst
|
||||
*/
|
||||
template <typename S, typename... Args,
|
||||
typename Char = enable_if_t<internal::is_string<S>::value, char_t<S>>>
|
||||
inline int fprintf(std::FILE* f, const S& format, const Args&... args) {
|
||||
template <typename S, typename... T, typename Char = char_t<S>>
|
||||
inline auto fprintf(std::FILE* f, const S& fmt, const T&... args) -> int {
|
||||
using context = basic_printf_context_t<Char>;
|
||||
return vfprintf(f, to_string_view(format),
|
||||
make_format_args<context>(args...));
|
||||
return vfprintf(f, to_string_view(fmt),
|
||||
fmt::make_format_args<context>(args...));
|
||||
}
|
||||
|
||||
template <typename S, typename Char = char_t<S>>
|
||||
inline int vprintf(
|
||||
const S& format,
|
||||
basic_format_args<basic_printf_context_t<type_identity_t<Char>>> args) {
|
||||
return vfprintf(stdout, to_string_view(format), args);
|
||||
inline auto vprintf(
|
||||
const S& fmt,
|
||||
basic_format_args<basic_printf_context_t<type_identity_t<Char>>> args)
|
||||
-> int {
|
||||
return vfprintf(stdout, to_string_view(fmt), args);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -675,52 +622,31 @@ inline int vprintf(
|
||||
fmt::printf("Elapsed time: %.2f seconds", 1.23);
|
||||
\endrst
|
||||
*/
|
||||
template <typename S, typename... Args,
|
||||
FMT_ENABLE_IF(internal::is_string<S>::value)>
|
||||
inline int printf(const S& format_str, const Args&... args) {
|
||||
using context = basic_printf_context_t<char_t<S>>;
|
||||
return vprintf(to_string_view(format_str),
|
||||
make_format_args<context>(args...));
|
||||
template <typename S, typename... T, FMT_ENABLE_IF(detail::is_string<S>::value)>
|
||||
inline auto printf(const S& fmt, const T&... args) -> int {
|
||||
return vprintf(
|
||||
to_string_view(fmt),
|
||||
fmt::make_format_args<basic_printf_context_t<char_t<S>>>(args...));
|
||||
}
|
||||
|
||||
template <typename S, typename Char = char_t<S>>
|
||||
inline int vfprintf(
|
||||
std::basic_ostream<Char>& os, const S& format,
|
||||
basic_format_args<basic_printf_context_t<type_identity_t<Char>>> args) {
|
||||
FMT_DEPRECATED auto vfprintf(
|
||||
std::basic_ostream<Char>& os, const S& fmt,
|
||||
basic_format_args<basic_printf_context_t<type_identity_t<Char>>> args)
|
||||
-> int {
|
||||
basic_memory_buffer<Char> buffer;
|
||||
printf(buffer, to_string_view(format), args);
|
||||
internal::write(os, buffer);
|
||||
vprintf(buffer, to_string_view(fmt), args);
|
||||
os.write(buffer.data(), static_cast<std::streamsize>(buffer.size()));
|
||||
return static_cast<int>(buffer.size());
|
||||
}
|
||||
|
||||
/** Formats arguments and writes the output to the range. */
|
||||
template <typename ArgFormatter, typename Char,
|
||||
typename Context =
|
||||
basic_printf_context<typename ArgFormatter::iterator, Char>>
|
||||
typename ArgFormatter::iterator vprintf(
|
||||
internal::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;
|
||||
template <typename S, typename... T, typename Char = char_t<S>>
|
||||
FMT_DEPRECATED auto fprintf(std::basic_ostream<Char>& os, const S& fmt,
|
||||
const T&... args) -> int {
|
||||
return vfprintf(os, to_string_view(fmt),
|
||||
fmt::make_format_args<basic_printf_context_t<Char>>(args...));
|
||||
}
|
||||
|
||||
/**
|
||||
\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_MODULE_EXPORT_END
|
||||
FMT_END_NAMESPACE
|
||||
|
||||
#endif // FMT_PRINTF_H_
|
||||
|
@@ -17,44 +17,34 @@
|
||||
|
||||
#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
|
||||
|
||||
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>
|
||||
FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
|
||||
return ctx.begin();
|
||||
}
|
||||
};
|
||||
|
||||
template <typename Char, typename Enable = void>
|
||||
struct formatting_range : formatting_base<Char> {
|
||||
static FMT_CONSTEXPR_DECL const std::size_t range_length_limit =
|
||||
FMT_RANGE_OUTPUT_LENGTH_LIMIT; // output only up to N items from the
|
||||
// range.
|
||||
Char prefix;
|
||||
Char delimiter;
|
||||
Char postfix;
|
||||
formatting_range() : prefix('{'), delimiter(','), postfix('}') {}
|
||||
static FMT_CONSTEXPR_DECL const bool add_delimiter_spaces = true;
|
||||
static FMT_CONSTEXPR_DECL const bool add_prepostfix_space = false;
|
||||
template <typename Char, typename Enable = void> struct formatting_tuple {
|
||||
Char prefix = '(';
|
||||
Char postfix = ')';
|
||||
|
||||
template <typename ParseContext>
|
||||
FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
|
||||
return ctx.begin();
|
||||
}
|
||||
};
|
||||
|
||||
template <typename Char, typename Enable = void>
|
||||
struct formatting_tuple : formatting_base<Char> {
|
||||
Char prefix;
|
||||
Char delimiter;
|
||||
Char postfix;
|
||||
formatting_tuple() : prefix('('), delimiter(','), postfix(')') {}
|
||||
static FMT_CONSTEXPR_DECL const bool add_delimiter_spaces = true;
|
||||
static FMT_CONSTEXPR_DECL const bool add_prepostfix_space = false;
|
||||
};
|
||||
|
||||
namespace internal {
|
||||
namespace detail {
|
||||
|
||||
template <typename RangeT, typename OutputIterator>
|
||||
OutputIterator copy(const RangeT& range, OutputIterator out) {
|
||||
@@ -75,8 +65,14 @@ OutputIterator copy(char ch, OutputIterator 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.
|
||||
template <typename T> class is_like_std_string {
|
||||
template <typename T> class is_std_string_like {
|
||||
template <typename U>
|
||||
static auto check(U* p)
|
||||
-> decltype((void)p->find('a'), p->length(), (void)p->data(), int());
|
||||
@@ -88,19 +84,107 @@ template <typename T> class is_like_std_string {
|
||||
};
|
||||
|
||||
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 T, typename _ = void> struct is_range_ : std::false_type {};
|
||||
|
||||
#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>
|
||||
struct is_range_<
|
||||
T, conditional_t<false,
|
||||
conditional_helper<decltype(std::declval<T>().begin()),
|
||||
decltype(std::declval<T>().end())>,
|
||||
void>> : std::true_type {};
|
||||
struct has_member_fn_begin_end_t<T, void_t<decltype(std::declval<T>().begin()),
|
||||
decltype(std::declval<T>().end())>>
|
||||
: 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
|
||||
|
||||
/// tuple_size and tuple_element check.
|
||||
@@ -118,26 +202,24 @@ template <typename T> class is_tuple_like_ {
|
||||
#if defined(__cpp_lib_integer_sequence) || FMT_MSC_VER >= 1900
|
||||
template <typename T, T... N>
|
||||
using integer_sequence = std::integer_sequence<T, N...>;
|
||||
template <std::size_t... N> using index_sequence = std::index_sequence<N...>;
|
||||
template <std::size_t N>
|
||||
using make_index_sequence = std::make_index_sequence<N>;
|
||||
template <size_t... N> using index_sequence = std::index_sequence<N...>;
|
||||
template <size_t N> using make_index_sequence = std::make_index_sequence<N>;
|
||||
#else
|
||||
template <typename T, T... N> struct integer_sequence {
|
||||
using value_type = T;
|
||||
|
||||
static FMT_CONSTEXPR std::size_t size() { return sizeof...(N); }
|
||||
static FMT_CONSTEXPR size_t size() { return sizeof...(N); }
|
||||
};
|
||||
|
||||
template <std::size_t... N>
|
||||
using index_sequence = integer_sequence<std::size_t, N...>;
|
||||
template <size_t... N> using index_sequence = integer_sequence<size_t, N...>;
|
||||
|
||||
template <typename T, std::size_t N, T... Ns>
|
||||
template <typename T, size_t N, T... Ns>
|
||||
struct make_integer_sequence : make_integer_sequence<T, N - 1, N - 1, Ns...> {};
|
||||
template <typename T, T... Ns>
|
||||
struct make_integer_sequence<T, 0, Ns...> : integer_sequence<T, Ns...> {};
|
||||
|
||||
template <std::size_t N>
|
||||
using make_index_sequence = make_integer_sequence<std::size_t, N>;
|
||||
template <size_t N>
|
||||
using make_index_sequence = make_integer_sequence<size_t, N>;
|
||||
#endif
|
||||
|
||||
template <class Tuple, class F, size_t... Is>
|
||||
@@ -159,37 +241,48 @@ template <class Tuple, class F> void for_each(Tuple&& tup, F&& f) {
|
||||
for_each(indexes, std::forward<Tuple>(tup), std::forward<F>(f));
|
||||
}
|
||||
|
||||
template <typename Arg, FMT_ENABLE_IF(!is_like_std_string<
|
||||
typename std::decay<Arg>::type>::value)>
|
||||
FMT_CONSTEXPR const char* format_str_quoted(bool add_space, const Arg&) {
|
||||
return add_space ? " {}" : "{}";
|
||||
template <typename Range>
|
||||
using value_type =
|
||||
remove_cvref_t<decltype(*detail::range_begin(std::declval<Range>()))>;
|
||||
|
||||
template <typename OutputIt> OutputIt write_delimiter(OutputIt out) {
|
||||
*out++ = ',';
|
||||
*out++ = ' ';
|
||||
return out;
|
||||
}
|
||||
|
||||
template <typename Arg, FMT_ENABLE_IF(is_like_std_string<
|
||||
typename std::decay<Arg>::type>::value)>
|
||||
FMT_CONSTEXPR const char* format_str_quoted(bool add_space, const Arg&) {
|
||||
return add_space ? " \"{}\"" : "\"{}\"";
|
||||
template <
|
||||
typename Char, typename OutputIt, typename Arg,
|
||||
FMT_ENABLE_IF(is_std_string_like<typename std::decay<Arg>::type>::value)>
|
||||
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*) {
|
||||
return add_space ? " \"{}\"" : "\"{}\"";
|
||||
}
|
||||
FMT_CONSTEXPR const wchar_t* format_str_quoted(bool add_space, const wchar_t*) {
|
||||
return add_space ? L" \"{}\"" : L"\"{}\"";
|
||||
template <typename Char, typename OutputIt, typename Arg,
|
||||
FMT_ENABLE_IF(std::is_same<Arg, Char>::value)>
|
||||
OutputIt write_range_entry(OutputIt out, const Arg v) {
|
||||
*out++ = '\'';
|
||||
*out++ = v;
|
||||
*out++ = '\'';
|
||||
return out;
|
||||
}
|
||||
|
||||
FMT_CONSTEXPR const char* format_str_quoted(bool add_space, const char) {
|
||||
return add_space ? " '{}'" : "'{}'";
|
||||
}
|
||||
FMT_CONSTEXPR const wchar_t* format_str_quoted(bool add_space, const wchar_t) {
|
||||
return add_space ? L" '{}'" : L"'{}'";
|
||||
template <
|
||||
typename Char, typename OutputIt, typename Arg,
|
||||
FMT_ENABLE_IF(!is_std_string_like<typename std::decay<Arg>::type>::value &&
|
||||
!std::is_same<Arg, Char>::value)>
|
||||
OutputIt write_range_entry(OutputIt out, const Arg& v) {
|
||||
return write<Char>(out, v);
|
||||
}
|
||||
|
||||
} // namespace internal
|
||||
} // namespace detail
|
||||
|
||||
template <typename T> struct is_tuple_like {
|
||||
static FMT_CONSTEXPR_DECL const bool value =
|
||||
internal::is_tuple_like_<T>::value && !internal::is_range_<T>::value;
|
||||
detail::is_tuple_like_<T>::value && !detail::is_range_<T>::value;
|
||||
};
|
||||
|
||||
template <typename TupleT, typename Char>
|
||||
@@ -198,23 +291,14 @@ struct formatter<TupleT, Char, enable_if_t<fmt::is_tuple_like<TupleT>::value>> {
|
||||
// C++11 generic lambda for format()
|
||||
template <typename FormatContext> struct format_each {
|
||||
template <typename T> void operator()(const T& v) {
|
||||
if (i > 0) {
|
||||
if (formatting.add_prepostfix_space) {
|
||||
*out++ = ' ';
|
||||
}
|
||||
out = internal::copy(formatting.delimiter, out);
|
||||
}
|
||||
out = format_to(out,
|
||||
internal::format_str_quoted(
|
||||
(formatting.add_delimiter_spaces && i > 0), v),
|
||||
v);
|
||||
if (i > 0) out = detail::write_delimiter(out);
|
||||
out = detail::write_range_entry<Char>(out, v);
|
||||
++i;
|
||||
}
|
||||
|
||||
formatting_tuple<Char>& formatting;
|
||||
std::size_t& i;
|
||||
typename std::add_lvalue_reference<decltype(
|
||||
std::declval<FormatContext>().out())>::type out;
|
||||
size_t& i;
|
||||
typename std::add_lvalue_reference<
|
||||
decltype(std::declval<FormatContext>().out())>::type out;
|
||||
};
|
||||
|
||||
public:
|
||||
@@ -228,14 +312,11 @@ struct formatter<TupleT, Char, enable_if_t<fmt::is_tuple_like<TupleT>::value>> {
|
||||
template <typename FormatContext = format_context>
|
||||
auto format(const TupleT& values, FormatContext& ctx) -> decltype(ctx.out()) {
|
||||
auto out = ctx.out();
|
||||
std::size_t i = 0;
|
||||
internal::copy(formatting.prefix, out);
|
||||
size_t i = 0;
|
||||
|
||||
internal::for_each(values, format_each<FormatContext>{formatting, i, out});
|
||||
if (formatting.add_prepostfix_space) {
|
||||
*out++ = ' ';
|
||||
}
|
||||
internal::copy(formatting.postfix, out);
|
||||
detail::copy(formatting.prefix, out);
|
||||
detail::for_each(values, format_each<FormatContext>{formatting, i, out});
|
||||
detail::copy(formatting.postfix, out);
|
||||
|
||||
return ctx.out();
|
||||
}
|
||||
@@ -243,15 +324,22 @@ struct formatter<TupleT, Char, enable_if_t<fmt::is_tuple_like<TupleT>::value>> {
|
||||
|
||||
template <typename T, typename Char> struct is_range {
|
||||
static FMT_CONSTEXPR_DECL const bool value =
|
||||
internal::is_range_<T>::value &&
|
||||
!internal::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_constructible<internal::std_string_view<Char>, T>::value;
|
||||
!std::is_constructible<detail::std_string_view<Char>, T>::value;
|
||||
};
|
||||
|
||||
template <typename RangeT, typename Char>
|
||||
struct formatter<RangeT, Char,
|
||||
enable_if_t<fmt::is_range<RangeT, Char>::value>> {
|
||||
template <typename T, typename Char>
|
||||
struct formatter<
|
||||
T, Char,
|
||||
enable_if_t<
|
||||
fmt::is_range<T, Char>::value
|
||||
// Workaround a bug in MSVC 2017 and earlier.
|
||||
#if !FMT_MSC_VER || FMT_MSC_VER >= 1927
|
||||
&& (has_formatter<detail::value_type<T>, format_context>::value ||
|
||||
detail::has_fallback_formatter<detail::value_type<T>, Char>::value)
|
||||
#endif
|
||||
>> {
|
||||
formatting_range<Char> formatting;
|
||||
|
||||
template <typename ParseContext>
|
||||
@@ -260,73 +348,68 @@ struct formatter<RangeT, Char,
|
||||
}
|
||||
|
||||
template <typename FormatContext>
|
||||
typename FormatContext::iterator format(const RangeT& values,
|
||||
FormatContext& ctx) {
|
||||
auto out = internal::copy(formatting.prefix, ctx.out());
|
||||
std::size_t i = 0;
|
||||
for (auto it = values.begin(), end = values.end(); it != end; ++it) {
|
||||
if (i > 0) {
|
||||
if (formatting.add_prepostfix_space) *out++ = ' ';
|
||||
out = internal::copy(formatting.delimiter, out);
|
||||
}
|
||||
out = format_to(out,
|
||||
internal::format_str_quoted(
|
||||
(formatting.add_delimiter_spaces && i > 0), *it),
|
||||
*it);
|
||||
if (++i > formatting.range_length_limit) {
|
||||
out = format_to(out, " ... <other elements>");
|
||||
break;
|
||||
}
|
||||
typename FormatContext::iterator format(const T& values, FormatContext& ctx) {
|
||||
auto out = detail::copy(formatting.prefix, ctx.out());
|
||||
size_t i = 0;
|
||||
auto view = detail::range_to_view<T>::view(values);
|
||||
auto it = view.begin();
|
||||
auto end = view.end();
|
||||
for (; it != end; ++it) {
|
||||
if (i > 0) out = detail::write_delimiter(out);
|
||||
out = detail::write_range_entry<Char>(out, *it);
|
||||
++i;
|
||||
}
|
||||
if (formatting.add_prepostfix_space) *out++ = ' ';
|
||||
return internal::copy(formatting.postfix, out);
|
||||
return detail::copy(formatting.postfix, out);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename Char, typename... T> struct tuple_arg_join : internal::view {
|
||||
template <typename Char, typename... T> struct tuple_join_view : detail::view {
|
||||
const std::tuple<T...>& tuple;
|
||||
basic_string_view<Char> sep;
|
||||
|
||||
tuple_arg_join(const std::tuple<T...>& t, basic_string_view<Char> s)
|
||||
: tuple{t}, sep{s} {}
|
||||
tuple_join_view(const std::tuple<T...>& t, basic_string_view<Char> s)
|
||||
: tuple(t), sep{s} {}
|
||||
};
|
||||
|
||||
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>
|
||||
FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
|
||||
return ctx.begin();
|
||||
}
|
||||
|
||||
template <typename FormatContext>
|
||||
typename FormatContext::iterator format(
|
||||
const tuple_arg_join<Char, T...>& value, FormatContext& ctx) {
|
||||
return format(value, ctx, internal::make_index_sequence<sizeof...(T)>{});
|
||||
auto format(const tuple_join_view<Char, T...>& value, FormatContext& ctx) ->
|
||||
typename FormatContext::iterator {
|
||||
return format(value, ctx, detail::make_index_sequence<sizeof...(T)>{});
|
||||
}
|
||||
|
||||
private:
|
||||
template <typename FormatContext, size_t... N>
|
||||
typename FormatContext::iterator format(
|
||||
const tuple_arg_join<Char, T...>& value, FormatContext& ctx,
|
||||
internal::index_sequence<N...>) {
|
||||
return format_args(value, ctx, std::get<N>(value.tuple)...);
|
||||
auto format(const tuple_join_view<Char, T...>& value, FormatContext& ctx,
|
||||
detail::index_sequence<N...>) ->
|
||||
typename FormatContext::iterator {
|
||||
using std::get;
|
||||
return format_args(value, ctx, get<N>(value.tuple)...);
|
||||
}
|
||||
|
||||
template <typename FormatContext>
|
||||
typename FormatContext::iterator format_args(
|
||||
const tuple_arg_join<Char, T...>&, FormatContext& ctx) {
|
||||
auto format_args(const tuple_join_view<Char, T...>&, FormatContext& ctx) ->
|
||||
typename FormatContext::iterator {
|
||||
// NOTE: for compilers that support C++17, this empty function instantiation
|
||||
// can be replaced with a constexpr branch in the variadic overload.
|
||||
return ctx.out();
|
||||
}
|
||||
|
||||
template <typename FormatContext, typename Arg, typename... Args>
|
||||
typename FormatContext::iterator format_args(
|
||||
const tuple_arg_join<Char, T...>& value, FormatContext& ctx,
|
||||
const Arg& arg, const Args&... args) {
|
||||
auto format_args(const tuple_join_view<Char, T...>& value, FormatContext& ctx,
|
||||
const Arg& arg, const Args&... args) ->
|
||||
typename FormatContext::iterator {
|
||||
using base = formatter<typename std::decay<Arg>::type, Char>;
|
||||
auto out = ctx.out();
|
||||
out = base{}.format(arg, ctx);
|
||||
auto out = base().format(arg, ctx);
|
||||
if (sizeof...(Args) > 0) {
|
||||
out = std::copy(value.sep.begin(), value.sep.end(), out);
|
||||
ctx.advance_to(out);
|
||||
@@ -336,6 +419,8 @@ struct formatter<tuple_arg_join<Char, T...>, Char> {
|
||||
}
|
||||
};
|
||||
|
||||
FMT_MODULE_EXPORT_BEGIN
|
||||
|
||||
/**
|
||||
\rst
|
||||
Returns an object that formats `tuple` with elements separated by `sep`.
|
||||
@@ -348,14 +433,15 @@ struct formatter<tuple_arg_join<Char, T...>, Char> {
|
||||
\endrst
|
||||
*/
|
||||
template <typename... T>
|
||||
FMT_CONSTEXPR tuple_arg_join<char, T...> join(const std::tuple<T...>& tuple,
|
||||
string_view sep) {
|
||||
FMT_CONSTEXPR auto join(const std::tuple<T...>& tuple, string_view sep)
|
||||
-> tuple_join_view<char, T...> {
|
||||
return {tuple, sep};
|
||||
}
|
||||
|
||||
template <typename... T>
|
||||
FMT_CONSTEXPR tuple_arg_join<wchar_t, T...> join(const std::tuple<T...>& tuple,
|
||||
wstring_view sep) {
|
||||
FMT_CONSTEXPR auto join(const std::tuple<T...>& tuple,
|
||||
basic_string_view<wchar_t> sep)
|
||||
-> tuple_join_view<wchar_t, T...> {
|
||||
return {tuple, sep};
|
||||
}
|
||||
|
||||
@@ -371,17 +457,12 @@ FMT_CONSTEXPR tuple_arg_join<wchar_t, T...> join(const std::tuple<T...>& tuple,
|
||||
\endrst
|
||||
*/
|
||||
template <typename T>
|
||||
arg_join<internal::iterator_t<const std::initializer_list<T>>, char> join(
|
||||
std::initializer_list<T> list, string_view sep) {
|
||||
return join(std::begin(list), std::end(list), sep);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
arg_join<internal::iterator_t<const std::initializer_list<T>>, wchar_t> join(
|
||||
std::initializer_list<T> list, wstring_view sep) {
|
||||
auto join(std::initializer_list<T> list, string_view sep)
|
||||
-> join_view<const T*, const T*> {
|
||||
return join(std::begin(list), std::end(list), sep);
|
||||
}
|
||||
|
||||
FMT_MODULE_EXPORT_END
|
||||
FMT_END_NAMESPACE
|
||||
|
||||
#endif // FMT_RANGES_H_
|
||||
|
236
include/spdlog/fmt/bundled/xchar.h
Normal file
236
include/spdlog/fmt/bundled/xchar.h
Normal 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_
|
20
include/spdlog/fmt/chrono.h
Normal file
20
include/spdlog/fmt/chrono.h
Normal 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 chrono support
|
||||
//
|
||||
|
||||
#if !defined(SPDLOG_FMT_EXTERNAL)
|
||||
# ifdef SPDLOG_HEADER_ONLY
|
||||
# ifndef FMT_HEADER_ONLY
|
||||
# define FMT_HEADER_ONLY
|
||||
# endif
|
||||
# endif
|
||||
# include <spdlog/fmt/bundled/chrono.h>
|
||||
#else
|
||||
# include <fmt/chrono.h>
|
||||
#endif
|
20
include/spdlog/fmt/compile.h
Normal file
20
include/spdlog/fmt/compile.h
Normal 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
|
@@ -11,15 +11,17 @@
|
||||
//
|
||||
|
||||
#if !defined(SPDLOG_FMT_EXTERNAL)
|
||||
#if !defined(SPDLOG_COMPILED_LIB) && !defined(FMT_HEADER_ONLY)
|
||||
#define FMT_HEADER_ONLY
|
||||
#endif
|
||||
#ifndef FMT_USE_WINDOWS_H
|
||||
#define FMT_USE_WINDOWS_H 0
|
||||
#endif
|
||||
#include <spdlog/fmt/bundled/core.h>
|
||||
#include <spdlog/fmt/bundled/format.h>
|
||||
# if !defined(SPDLOG_COMPILED_LIB) && !defined(FMT_HEADER_ONLY)
|
||||
# define FMT_HEADER_ONLY
|
||||
# endif
|
||||
# ifndef FMT_USE_WINDOWS_H
|
||||
# define FMT_USE_WINDOWS_H 0
|
||||
# endif
|
||||
// enable the 'n' flag in for backward compatibility with fmt 6.x
|
||||
# define FMT_DEPRECATED_N_SPECIFIER
|
||||
# include <spdlog/fmt/bundled/core.h>
|
||||
# include <spdlog/fmt/bundled/format.h>
|
||||
#else // SPDLOG_FMT_EXTERNAL is defined - use external fmtlib
|
||||
#include <fmt/core.h>
|
||||
#include <fmt/format.h>
|
||||
#endif
|
||||
# include <fmt/core.h>
|
||||
# include <fmt/format.h>
|
||||
#endif
|
||||
|
@@ -9,12 +9,12 @@
|
||||
//
|
||||
|
||||
#if !defined(SPDLOG_FMT_EXTERNAL)
|
||||
#ifdef SPDLOG_HEADER_ONLY
|
||||
#ifndef FMT_HEADER_ONLY
|
||||
#define FMT_HEADER_ONLY
|
||||
#endif
|
||||
#endif
|
||||
#include <spdlog/fmt/bundled/ostream.h>
|
||||
# ifdef SPDLOG_HEADER_ONLY
|
||||
# ifndef FMT_HEADER_ONLY
|
||||
# define FMT_HEADER_ONLY
|
||||
# endif
|
||||
# endif
|
||||
# include <spdlog/fmt/bundled/ostream.h>
|
||||
#else
|
||||
#include <fmt/ostream.h>
|
||||
# include <fmt/ostream.h>
|
||||
#endif
|
||||
|
20
include/spdlog/fmt/xchar.h
Normal file
20
include/spdlog/fmt/xchar.h
Normal 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
|
@@ -4,7 +4,7 @@
|
||||
#pragma once
|
||||
|
||||
#ifndef SPDLOG_HEADER_ONLY
|
||||
#include <spdlog/logger.h>
|
||||
# include <spdlog/logger.h>
|
||||
#endif
|
||||
|
||||
#include <spdlog/sinks/sink.h>
|
||||
@@ -247,7 +247,11 @@ SPDLOG_INLINE void logger::err_handler_(const std::string &msg)
|
||||
auto tm_time = details::os::localtime(system_clock::to_time_t(now));
|
||||
char date_buf[64];
|
||||
std::strftime(date_buf, sizeof(date_buf), "%Y-%m-%d %H:%M:%S", &tm_time);
|
||||
fprintf(stderr, "[*** LOG ERROR #%04zu ***] [%s] [%s] {%s}\n", err_counter, date_buf, name().c_str(), msg.c_str());
|
||||
#if defined(USING_R) && defined(R_R_H) // if in R environment
|
||||
REprintf("[*** LOG ERROR #%04zu ***] [%s] [%s] {%s}\n", err_counter, date_buf, name().c_str(), msg.c_str());
|
||||
#else
|
||||
std::fprintf(stderr, "[*** LOG ERROR #%04zu ***] [%s] [%s] {%s}\n", err_counter, date_buf, name().c_str(), msg.c_str());
|
||||
#endif
|
||||
}
|
||||
}
|
||||
} // namespace spdlog
|
||||
|
@@ -19,22 +19,27 @@
|
||||
#include <spdlog/details/backtracer.h>
|
||||
|
||||
#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
|
||||
|
||||
#include <vector>
|
||||
|
||||
#ifndef SPDLOG_NO_EXCEPTIONS
|
||||
#define SPDLOG_LOGGER_CATCH() \
|
||||
catch (const std::exception &ex) \
|
||||
{ \
|
||||
err_handler_(ex.what()); \
|
||||
} \
|
||||
catch (...) \
|
||||
{ \
|
||||
err_handler_("Unknown exception in logger"); \
|
||||
}
|
||||
# define SPDLOG_LOGGER_CATCH() \
|
||||
catch (const std::exception &ex) \
|
||||
{ \
|
||||
err_handler_(ex.what()); \
|
||||
} \
|
||||
catch (...) \
|
||||
{ \
|
||||
err_handler_("Rethrowing unknown exception in logger"); \
|
||||
throw; \
|
||||
}
|
||||
#else
|
||||
#define SPDLOG_LOGGER_CATCH()
|
||||
# define SPDLOG_LOGGER_CATCH()
|
||||
#endif
|
||||
|
||||
namespace spdlog {
|
||||
@@ -70,68 +75,18 @@ public:
|
||||
logger(const logger &other);
|
||||
logger(logger &&other) SPDLOG_NOEXCEPT;
|
||||
logger &operator=(logger other) SPDLOG_NOEXCEPT;
|
||||
|
||||
void swap(spdlog::logger &other) SPDLOG_NOEXCEPT;
|
||||
|
||||
template<typename... Args>
|
||||
void log(source_loc loc, level::level_enum lvl, string_view_t fmt, const Args &... args)
|
||||
void log(source_loc loc, level::level_enum lvl, fmt::format_string<Args...> fmt, Args &&...args)
|
||||
{
|
||||
bool log_enabled = should_log(lvl);
|
||||
bool traceback_enabled = tracer_.enabled();
|
||||
if (!log_enabled && !traceback_enabled)
|
||||
{
|
||||
return;
|
||||
}
|
||||
SPDLOG_TRY
|
||||
{
|
||||
memory_buf_t buf;
|
||||
fmt::format_to(buf, fmt, args...);
|
||||
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()
|
||||
log_(loc, lvl, fmt, std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
template<typename... Args>
|
||||
void log(level::level_enum lvl, string_view_t fmt, const Args &... args)
|
||||
void log(level::level_enum lvl, fmt::format_string<Args...> fmt, Args &&...args)
|
||||
{
|
||||
log(source_loc{}, lvl, fmt, args...);
|
||||
}
|
||||
|
||||
template<typename... Args>
|
||||
void trace(string_view_t fmt, const Args &... args)
|
||||
{
|
||||
log(level::trace, fmt, args...);
|
||||
}
|
||||
|
||||
template<typename... Args>
|
||||
void debug(string_view_t fmt, const Args &... args)
|
||||
{
|
||||
log(level::debug, fmt, args...);
|
||||
}
|
||||
|
||||
template<typename... Args>
|
||||
void info(string_view_t fmt, const Args &... args)
|
||||
{
|
||||
log(level::info, fmt, args...);
|
||||
}
|
||||
|
||||
template<typename... Args>
|
||||
void warn(string_view_t fmt, const Args &... args)
|
||||
{
|
||||
log(level::warn, fmt, args...);
|
||||
}
|
||||
|
||||
template<typename... Args>
|
||||
void error(string_view_t fmt, const Args &... args)
|
||||
{
|
||||
log(level::err, fmt, args...);
|
||||
}
|
||||
|
||||
template<typename... Args>
|
||||
void critical(string_view_t fmt, const Args &... args)
|
||||
{
|
||||
log(level::critical, fmt, args...);
|
||||
log(source_loc{}, lvl, fmt, std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
@@ -141,12 +96,19 @@ public:
|
||||
}
|
||||
|
||||
// T can be statically converted to string_view
|
||||
template<class T, typename std::enable_if<std::is_convertible<const T &, spdlog::string_view_t>::value, T>::type * = nullptr>
|
||||
template<class T, typename std::enable_if<std::is_convertible<const T &, spdlog::string_view_t>::value, int>::type = 0>
|
||||
void log(source_loc loc, level::level_enum lvl, const T &msg)
|
||||
{
|
||||
log(loc, lvl, string_view_t{msg});
|
||||
}
|
||||
|
||||
// T cannot be statically converted to format string (including string_view)
|
||||
template<class T, typename std::enable_if<!is_convertible_to_any_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)
|
||||
{
|
||||
bool log_enabled = should_log(lvl);
|
||||
@@ -178,15 +140,92 @@ public:
|
||||
log(source_loc{}, lvl, msg);
|
||||
}
|
||||
|
||||
// T cannot be statically converted to string_view or wstring_view
|
||||
template<class T, typename std::enable_if<!std::is_convertible<const T &, spdlog::string_view_t>::value &&
|
||||
!is_convertible_to_wstring_view<const T &>::value,
|
||||
T>::type * = nullptr>
|
||||
void log(source_loc loc, level::level_enum lvl, const T &msg)
|
||||
template<typename... Args>
|
||||
void trace(fmt::format_string<Args...> fmt, Args &&...args)
|
||||
{
|
||||
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>
|
||||
void trace(const T &msg)
|
||||
{
|
||||
@@ -223,99 +262,6 @@ public:
|
||||
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, const 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, 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()
|
||||
}
|
||||
|
||||
template<typename... Args>
|
||||
void log(level::level_enum lvl, wstring_view_t fmt, const Args &... args)
|
||||
{
|
||||
log(source_loc{}, lvl, fmt, args...);
|
||||
}
|
||||
|
||||
template<typename... Args>
|
||||
void trace(wstring_view_t fmt, const Args &... args)
|
||||
{
|
||||
log(level::trace, fmt, args...);
|
||||
}
|
||||
|
||||
template<typename... Args>
|
||||
void debug(wstring_view_t fmt, const Args &... args)
|
||||
{
|
||||
log(level::debug, fmt, args...);
|
||||
}
|
||||
|
||||
template<typename... Args>
|
||||
void info(wstring_view_t fmt, const Args &... args)
|
||||
{
|
||||
log(level::info, fmt, args...);
|
||||
}
|
||||
|
||||
template<typename... Args>
|
||||
void warn(wstring_view_t fmt, const Args &... args)
|
||||
{
|
||||
log(level::warn, fmt, args...);
|
||||
}
|
||||
|
||||
template<typename... Args>
|
||||
void error(wstring_view_t fmt, const Args &... args)
|
||||
{
|
||||
log(level::err, fmt, args...);
|
||||
}
|
||||
|
||||
template<typename... Args>
|
||||
void critical(wstring_view_t fmt, const Args &... args)
|
||||
{
|
||||
log(level::critical, fmt, args...);
|
||||
}
|
||||
|
||||
// T can be statically converted to wstring_view
|
||||
template<class T, typename std::enable_if<is_convertible_to_wstring_view<const T &>::value, T>::type * = nullptr>
|
||||
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.
|
||||
bool should_log(level::level_enum msg_level) const
|
||||
{
|
||||
@@ -370,6 +316,71 @@ protected:
|
||||
err_handler custom_err_handler_{nullptr};
|
||||
details::backtracer tracer_;
|
||||
|
||||
// common implementation for after templated public api has been resolved
|
||||
template<typename... Args>
|
||||
void log_(source_loc loc, level::level_enum lvl, string_view_t fmt, Args &&...args)
|
||||
{
|
||||
bool log_enabled = should_log(lvl);
|
||||
bool traceback_enabled = tracer_.enabled();
|
||||
if (!log_enabled && !traceback_enabled)
|
||||
{
|
||||
return;
|
||||
}
|
||||
SPDLOG_TRY
|
||||
{
|
||||
memory_buf_t buf;
|
||||
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()));
|
||||
log_it_(log_msg, log_enabled, traceback_enabled);
|
||||
}
|
||||
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),
|
||||
// and save backtrace (if backtrace is enabled).
|
||||
void log_it_(const details::log_msg &log_msg, bool log_enabled, bool traceback_enabled);
|
||||
@@ -388,5 +399,5 @@ void swap(logger &a, logger &b);
|
||||
} // namespace spdlog
|
||||
|
||||
#ifdef SPDLOG_HEADER_ONLY
|
||||
#include "logger-inl.h"
|
||||
# include "logger-inl.h"
|
||||
#endif
|
||||
|
@@ -4,7 +4,7 @@
|
||||
#pragma once
|
||||
|
||||
#ifndef SPDLOG_HEADER_ONLY
|
||||
#include <spdlog/pattern_formatter.h>
|
||||
# include <spdlog/pattern_formatter.h>
|
||||
#endif
|
||||
|
||||
#include <spdlog/details/fmt_helper.h>
|
||||
@@ -13,11 +13,13 @@
|
||||
#include <spdlog/fmt/fmt.h>
|
||||
#include <spdlog/formatter.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <chrono>
|
||||
#include <ctime>
|
||||
#include <cctype>
|
||||
#include <cstring>
|
||||
#include <iterator>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
@@ -45,12 +47,12 @@ public:
|
||||
return;
|
||||
}
|
||||
|
||||
if (padinfo_.side_ == padding_info::left)
|
||||
if (padinfo_.side_ == padding_info::pad_side::left)
|
||||
{
|
||||
pad_it(remaining_pad_);
|
||||
remaining_pad_ = 0;
|
||||
}
|
||||
else if (padinfo_.side_ == padding_info::center)
|
||||
else if (padinfo_.side_ == padding_info::pad_side::center)
|
||||
{
|
||||
auto half_pad = remaining_pad_ / 2;
|
||||
auto reminder = remaining_pad_ & 1;
|
||||
@@ -127,7 +129,7 @@ public:
|
||||
|
||||
void format(const details::log_msg &msg, const std::tm &, memory_buf_t &dest) override
|
||||
{
|
||||
string_view_t &level_name = level::to_string_view(msg.level);
|
||||
const string_view_t &level_name = level::to_string_view(msg.level);
|
||||
ScopedPadder p(level_name.size(), padinfo_, dest);
|
||||
fmt_helper::append_string_view(level_name, dest);
|
||||
}
|
||||
@@ -814,11 +816,31 @@ public:
|
||||
: flag_formatter(padinfo)
|
||||
{}
|
||||
|
||||
#ifdef _MSC_VER
|
||||
# pragma warning(push)
|
||||
# pragma warning(disable : 4127) // consider using 'if constexpr' instead
|
||||
#endif // _MSC_VER
|
||||
static const char *basename(const char *filename)
|
||||
{
|
||||
const char *rv = std::strrchr(filename, os::folder_sep);
|
||||
return rv != nullptr ? rv + 1 : filename;
|
||||
// if the size is 2 (1 character + null terminator) we can use the more efficient strrchr
|
||||
// the branch will be elided by optimizations
|
||||
if (sizeof(os::folder_seps) == 2)
|
||||
{
|
||||
const char *rv = std::strrchr(filename, os::folder_seps[0]);
|
||||
return rv != nullptr ? rv + 1 : filename;
|
||||
}
|
||||
else
|
||||
{
|
||||
const std::reverse_iterator<const char *> begin(filename + std::strlen(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);
|
||||
return it != end ? it.base() : filename;
|
||||
}
|
||||
}
|
||||
#ifdef _MSC_VER
|
||||
# pragma warning(pop)
|
||||
#endif // _MSC_VER
|
||||
|
||||
void format(const details::log_msg &msg, const std::tm &, memory_buf_t &dest) override
|
||||
{
|
||||
@@ -903,7 +925,7 @@ private:
|
||||
};
|
||||
|
||||
// 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
|
||||
{
|
||||
public:
|
||||
@@ -1246,9 +1268,24 @@ SPDLOG_INLINE void pattern_formatter::handle_flag_(char flag, details::padding_i
|
||||
|
||||
default: // Unknown flag appears as is
|
||||
auto unknown_flag = details::make_unique<details::aggregate_formatter>();
|
||||
unknown_flag->add_ch('%');
|
||||
unknown_flag->add_ch(flag);
|
||||
formatters_.push_back((std::move(unknown_flag)));
|
||||
|
||||
if (!padding.truncate_)
|
||||
{
|
||||
unknown_flag->add_ch('%');
|
||||
unknown_flag->add_ch(flag);
|
||||
formatters_.push_back((std::move(unknown_flag)));
|
||||
}
|
||||
// fix issue #1617 (prev char was '!' and should have been treated as funcname flag instead of truncating flag)
|
||||
// spdlog::set_pattern("[%10!] %v") => "[ main] some message"
|
||||
// spdlog::set_pattern("[%3!!] %v") => "[mai] some message"
|
||||
else
|
||||
{
|
||||
padding.truncate_ = false;
|
||||
formatters_.push_back(details::make_unique<details::source_funcname_formatter<Padder>>(padding));
|
||||
unknown_flag->add_ch(flag);
|
||||
formatters_.push_back((std::move(unknown_flag)));
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -1270,15 +1307,15 @@ SPDLOG_INLINE details::padding_info pattern_formatter::handle_padspec_(std::stri
|
||||
switch (*it)
|
||||
{
|
||||
case '-':
|
||||
side = padding_info::right;
|
||||
side = padding_info::pad_side::right;
|
||||
++it;
|
||||
break;
|
||||
case '=':
|
||||
side = padding_info::center;
|
||||
side = padding_info::pad_side::center;
|
||||
++it;
|
||||
break;
|
||||
default:
|
||||
side = details::padding_info::left;
|
||||
side = details::padding_info::pad_side::left;
|
||||
break;
|
||||
}
|
||||
|
||||
|
@@ -22,7 +22,7 @@ namespace details {
|
||||
// padding information.
|
||||
struct padding_info
|
||||
{
|
||||
enum pad_side
|
||||
enum class pad_side
|
||||
{
|
||||
left,
|
||||
right,
|
||||
@@ -42,7 +42,7 @@ struct padding_info
|
||||
return enabled_;
|
||||
}
|
||||
size_t width_ = 0;
|
||||
pad_side side_ = left;
|
||||
pad_side side_ = pad_side::left;
|
||||
bool truncate_ = false;
|
||||
bool enabled_ = false;
|
||||
};
|
||||
@@ -92,9 +92,9 @@ public:
|
||||
void format(const details::log_msg &msg, memory_buf_t &dest) override;
|
||||
|
||||
template<typename T, typename... Args>
|
||||
pattern_formatter &add_flag(char flag, const Args &... args)
|
||||
pattern_formatter &add_flag(char flag, Args &&...args)
|
||||
{
|
||||
custom_handlers_[flag] = details::make_unique<T>(args...);
|
||||
custom_handlers_[flag] = details::make_unique<T>(std::forward<Args>(args)...);
|
||||
return *this;
|
||||
}
|
||||
void set_pattern(std::string pattern);
|
||||
@@ -122,5 +122,5 @@ private:
|
||||
} // namespace spdlog
|
||||
|
||||
#ifdef SPDLOG_HEADER_ONLY
|
||||
#include "pattern_formatter-inl.h"
|
||||
# include "pattern_formatter-inl.h"
|
||||
#endif
|
||||
|
@@ -5,21 +5,21 @@
|
||||
|
||||
#ifdef __ANDROID__
|
||||
|
||||
#include <spdlog/details/fmt_helper.h>
|
||||
#include <spdlog/details/null_mutex.h>
|
||||
#include <spdlog/details/os.h>
|
||||
#include <spdlog/sinks/base_sink.h>
|
||||
#include <spdlog/details/synchronous_factory.h>
|
||||
# include <spdlog/details/fmt_helper.h>
|
||||
# include <spdlog/details/null_mutex.h>
|
||||
# include <spdlog/details/os.h>
|
||||
# include <spdlog/sinks/base_sink.h>
|
||||
# include <spdlog/details/synchronous_factory.h>
|
||||
|
||||
#include <android/log.h>
|
||||
#include <chrono>
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
#include <thread>
|
||||
# include <android/log.h>
|
||||
# include <chrono>
|
||||
# include <mutex>
|
||||
# include <string>
|
||||
# include <thread>
|
||||
|
||||
#if !defined(SPDLOG_ANDROID_RETRIES)
|
||||
#define SPDLOG_ANDROID_RETRIES 2
|
||||
#endif
|
||||
# if !defined(SPDLOG_ANDROID_RETRIES)
|
||||
# define SPDLOG_ANDROID_RETRIES 2
|
||||
# endif
|
||||
|
||||
namespace spdlog {
|
||||
namespace sinks {
|
||||
|
@@ -4,7 +4,7 @@
|
||||
#pragma once
|
||||
|
||||
#ifndef SPDLOG_HEADER_ONLY
|
||||
#include <spdlog/sinks/ansicolor_sink.h>
|
||||
# include <spdlog/sinks/ansicolor_sink.h>
|
||||
#endif
|
||||
|
||||
#include <spdlog/pattern_formatter.h>
|
||||
@@ -106,6 +106,8 @@ SPDLOG_INLINE void ansicolor_sink<ConsoleMutex>::set_color_mode(color_mode mode)
|
||||
case color_mode::never:
|
||||
should_do_colors_ = false;
|
||||
return;
|
||||
default:
|
||||
should_do_colors_ = false;
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -114,5 +114,5 @@ using ansicolor_stderr_sink_st = ansicolor_stderr_sink<details::console_nullmute
|
||||
} // namespace spdlog
|
||||
|
||||
#ifdef SPDLOG_HEADER_ONLY
|
||||
#include "ansicolor_sink-inl.h"
|
||||
# include "ansicolor_sink-inl.h"
|
||||
#endif
|
||||
|
@@ -4,7 +4,7 @@
|
||||
#pragma once
|
||||
|
||||
#ifndef SPDLOG_HEADER_ONLY
|
||||
#include <spdlog/sinks/base_sink.h>
|
||||
# include <spdlog/sinks/base_sink.h>
|
||||
#endif
|
||||
|
||||
#include <spdlog/common.h>
|
||||
|
@@ -37,7 +37,7 @@ public:
|
||||
protected:
|
||||
// sink formatter
|
||||
std::unique_ptr<spdlog::formatter> formatter_;
|
||||
Mutex mutex_;
|
||||
mutable Mutex mutex_;
|
||||
|
||||
virtual void sink_it_(const details::log_msg &msg) = 0;
|
||||
virtual void flush_() = 0;
|
||||
@@ -48,5 +48,5 @@ protected:
|
||||
} // namespace spdlog
|
||||
|
||||
#ifdef SPDLOG_HEADER_ONLY
|
||||
#include "base_sink-inl.h"
|
||||
# include "base_sink-inl.h"
|
||||
#endif
|
||||
|
@@ -4,7 +4,7 @@
|
||||
#pragma once
|
||||
|
||||
#ifndef SPDLOG_HEADER_ONLY
|
||||
#include <spdlog/sinks/basic_file_sink.h>
|
||||
# include <spdlog/sinks/basic_file_sink.h>
|
||||
#endif
|
||||
|
||||
#include <spdlog/common.h>
|
||||
|
@@ -54,5 +54,5 @@ inline std::shared_ptr<logger> basic_logger_st(const std::string &logger_name, c
|
||||
} // namespace spdlog
|
||||
|
||||
#ifdef SPDLOG_HEADER_ONLY
|
||||
#include "basic_file_sink-inl.h"
|
||||
#endif
|
||||
# include "basic_file_sink-inl.h"
|
||||
#endif
|
||||
|
@@ -7,6 +7,7 @@
|
||||
#include <spdlog/details/file_helper.h>
|
||||
#include <spdlog/details/null_mutex.h>
|
||||
#include <spdlog/fmt/fmt.h>
|
||||
#include <spdlog/fmt/chrono.h>
|
||||
#include <spdlog/sinks/base_sink.h>
|
||||
#include <spdlog/details/os.h>
|
||||
#include <spdlog/details/circular_q.h>
|
||||
@@ -36,6 +37,27 @@ struct daily_filename_calculator
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
* Generator of daily log file names with strftime format.
|
||||
* Usages:
|
||||
* 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)"
|
||||
*
|
||||
*/
|
||||
struct daily_filename_format_calculator
|
||||
{
|
||||
static filename_t calc_filename(const filename_t &filename, const tm &now_tm)
|
||||
{
|
||||
// generate fmt datetime format string, e.g. {:%Y-%m-%d}.
|
||||
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);
|
||||
#else
|
||||
return fmt::format(SPDLOG_FMT_RUNTIME(fmt_filename), now_tm);
|
||||
#endif
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
* Rotating file sink based on date.
|
||||
* If truncate != false , the created file will be truncated.
|
||||
@@ -182,6 +204,8 @@ private:
|
||||
|
||||
using daily_file_sink_mt = daily_file_sink<std::mutex>;
|
||||
using daily_file_sink_st = daily_file_sink<details::null_mutex>;
|
||||
using daily_file_format_sink_mt = daily_file_sink<std::mutex, daily_filename_format_calculator>;
|
||||
using daily_file_format_sink_st = daily_file_sink<details::null_mutex, daily_filename_format_calculator>;
|
||||
|
||||
} // namespace sinks
|
||||
|
||||
@@ -195,10 +219,24 @@ inline std::shared_ptr<logger> daily_logger_mt(
|
||||
return Factory::template create<sinks::daily_file_sink_mt>(logger_name, filename, hour, minute, truncate, max_files);
|
||||
}
|
||||
|
||||
template<typename Factory = spdlog::synchronous_factory>
|
||||
inline std::shared_ptr<logger> daily_logger_format_mt(
|
||||
const std::string &logger_name, const filename_t &filename, int hour = 0, int minute = 0, bool truncate = false, uint16_t max_files = 0)
|
||||
{
|
||||
return Factory::template create<sinks::daily_file_format_sink_mt>(logger_name, filename, hour, minute, truncate, max_files);
|
||||
}
|
||||
|
||||
template<typename Factory = spdlog::synchronous_factory>
|
||||
inline std::shared_ptr<logger> daily_logger_st(
|
||||
const std::string &logger_name, const filename_t &filename, int hour = 0, int minute = 0, bool truncate = false, uint16_t max_files = 0)
|
||||
{
|
||||
return Factory::template create<sinks::daily_file_sink_st>(logger_name, filename, hour, minute, truncate, max_files);
|
||||
}
|
||||
|
||||
template<typename Factory = spdlog::synchronous_factory>
|
||||
inline std::shared_ptr<logger> daily_logger_format_st(
|
||||
const std::string &logger_name, const filename_t &filename, int hour = 0, int minute = 0, bool truncate = false, uint16_t max_files = 0)
|
||||
{
|
||||
return Factory::template create<sinks::daily_file_format_sink_st>(logger_name, filename, hour, minute, truncate, max_files);
|
||||
}
|
||||
} // namespace spdlog
|
||||
|
@@ -7,6 +7,7 @@
|
||||
#include <spdlog/details/null_mutex.h>
|
||||
#include <spdlog/details/log_msg.h>
|
||||
|
||||
#include <cstdio>
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
#include <chrono>
|
||||
@@ -62,10 +63,13 @@ protected:
|
||||
// log the "skipped.." message
|
||||
if (skip_counter_ > 0)
|
||||
{
|
||||
memory_buf_t buf;
|
||||
fmt::format_to(buf, "Skipped {} duplicate messages..", skip_counter_);
|
||||
details::log_msg skipped_msg{msg.logger_name, msg.level, string_view_t{buf.data(), buf.size()}};
|
||||
dist_sink<Mutex>::sink_it_(skipped_msg);
|
||||
char buf[64];
|
||||
auto msg_size = ::snprintf(buf, sizeof(buf), "Skipped %u duplicate messages..", static_cast<unsigned>(skip_counter_));
|
||||
if (msg_size > 0 && static_cast<size_t>(msg_size) < sizeof(buf))
|
||||
{
|
||||
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
|
||||
|
194
include/spdlog/sinks/hourly_file_sink.h
Normal file
194
include/spdlog/sinks/hourly_file_sink.h
Normal file
@@ -0,0 +1,194 @@
|
||||
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
|
||||
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <spdlog/common.h>
|
||||
#include <spdlog/details/file_helper.h>
|
||||
#include <spdlog/details/null_mutex.h>
|
||||
#include <spdlog/fmt/fmt.h>
|
||||
#include <spdlog/sinks/base_sink.h>
|
||||
#include <spdlog/details/os.h>
|
||||
#include <spdlog/details/circular_q.h>
|
||||
#include <spdlog/details/synchronous_factory.h>
|
||||
|
||||
#include <chrono>
|
||||
#include <cstdio>
|
||||
#include <ctime>
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
|
||||
namespace spdlog {
|
||||
namespace sinks {
|
||||
|
||||
/*
|
||||
* Generator of Hourly log file names in format basename.YYYY-MM-DD-HH.ext
|
||||
*/
|
||||
struct hourly_filename_calculator
|
||||
{
|
||||
// Create filename for the form basename.YYYY-MM-DD-H
|
||||
static filename_t calc_filename(const filename_t &filename, const tm &now_tm)
|
||||
{
|
||||
filename_t basename, ext;
|
||||
std::tie(basename, ext) = details::file_helper::split_by_extension(filename);
|
||||
return fmt::format(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);
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
* Rotating file sink based on time.
|
||||
* If truncate != false , the created file will be truncated.
|
||||
* If max_files > 0, retain only the last max_files and delete previous.
|
||||
*/
|
||||
template<typename Mutex, typename FileNameCalc = hourly_filename_calculator>
|
||||
class hourly_file_sink final : public base_sink<Mutex>
|
||||
{
|
||||
public:
|
||||
// create hourly file sink which rotates on given time
|
||||
hourly_file_sink(filename_t base_filename, bool truncate = false, uint16_t max_files = 0)
|
||||
: base_filename_(std::move(base_filename))
|
||||
, truncate_(truncate)
|
||||
, max_files_(max_files)
|
||||
, filenames_q_()
|
||||
{
|
||||
auto now = log_clock::now();
|
||||
auto filename = FileNameCalc::calc_filename(base_filename_, now_tm(now));
|
||||
file_helper_.open(filename, truncate_);
|
||||
rotation_tp_ = next_rotation_tp_();
|
||||
|
||||
if (max_files_ > 0)
|
||||
{
|
||||
init_filenames_q_();
|
||||
}
|
||||
}
|
||||
|
||||
filename_t filename()
|
||||
{
|
||||
std::lock_guard<Mutex> lock(base_sink<Mutex>::mutex_);
|
||||
return file_helper_.filename();
|
||||
}
|
||||
|
||||
protected:
|
||||
void sink_it_(const details::log_msg &msg) override
|
||||
{
|
||||
auto time = msg.time;
|
||||
bool should_rotate = time >= rotation_tp_;
|
||||
if (should_rotate)
|
||||
{
|
||||
auto filename = FileNameCalc::calc_filename(base_filename_, now_tm(time));
|
||||
file_helper_.open(filename, truncate_);
|
||||
rotation_tp_ = next_rotation_tp_();
|
||||
}
|
||||
memory_buf_t formatted;
|
||||
base_sink<Mutex>::formatter_->format(msg, formatted);
|
||||
file_helper_.write(formatted);
|
||||
|
||||
// Do the cleaning only at the end because it might throw on failure.
|
||||
if (should_rotate && max_files_ > 0)
|
||||
{
|
||||
delete_old_();
|
||||
}
|
||||
}
|
||||
|
||||
void flush_() override
|
||||
{
|
||||
file_helper_.flush();
|
||||
}
|
||||
|
||||
private:
|
||||
void init_filenames_q_()
|
||||
{
|
||||
using details::os::path_exists;
|
||||
|
||||
filenames_q_ = details::circular_q<filename_t>(static_cast<size_t>(max_files_));
|
||||
std::vector<filename_t> filenames;
|
||||
auto now = log_clock::now();
|
||||
while (filenames.size() < max_files_)
|
||||
{
|
||||
auto filename = FileNameCalc::calc_filename(base_filename_, now_tm(now));
|
||||
if (!path_exists(filename))
|
||||
{
|
||||
break;
|
||||
}
|
||||
filenames.emplace_back(filename);
|
||||
now -= std::chrono::hours(1);
|
||||
}
|
||||
for (auto iter = filenames.rbegin(); iter != filenames.rend(); ++iter)
|
||||
{
|
||||
filenames_q_.push_back(std::move(*iter));
|
||||
}
|
||||
}
|
||||
|
||||
tm now_tm(log_clock::time_point tp)
|
||||
{
|
||||
time_t tnow = log_clock::to_time_t(tp);
|
||||
return spdlog::details::os::localtime(tnow);
|
||||
}
|
||||
|
||||
log_clock::time_point next_rotation_tp_()
|
||||
{
|
||||
auto now = log_clock::now();
|
||||
tm date = now_tm(now);
|
||||
date.tm_min = 0;
|
||||
date.tm_sec = 0;
|
||||
auto rotation_time = log_clock::from_time_t(std::mktime(&date));
|
||||
if (rotation_time > now)
|
||||
{
|
||||
return rotation_time;
|
||||
}
|
||||
return {rotation_time + std::chrono::hours(1)};
|
||||
}
|
||||
|
||||
// Delete the file N rotations ago.
|
||||
// Throw spdlog_ex on failure to delete the old file.
|
||||
void delete_old_()
|
||||
{
|
||||
using details::os::filename_to_str;
|
||||
using details::os::remove_if_exists;
|
||||
|
||||
filename_t current_file = file_helper_.filename();
|
||||
if (filenames_q_.full())
|
||||
{
|
||||
auto old_filename = std::move(filenames_q_.front());
|
||||
filenames_q_.pop_front();
|
||||
bool ok = remove_if_exists(old_filename) == 0;
|
||||
if (!ok)
|
||||
{
|
||||
filenames_q_.push_back(std::move(current_file));
|
||||
SPDLOG_THROW(spdlog_ex("Failed removing hourly file " + filename_to_str(old_filename), errno));
|
||||
}
|
||||
}
|
||||
filenames_q_.push_back(std::move(current_file));
|
||||
}
|
||||
|
||||
filename_t base_filename_;
|
||||
log_clock::time_point rotation_tp_;
|
||||
details::file_helper file_helper_;
|
||||
bool truncate_;
|
||||
uint16_t max_files_;
|
||||
details::circular_q<filename_t> filenames_q_;
|
||||
};
|
||||
|
||||
using hourly_file_sink_mt = hourly_file_sink<std::mutex>;
|
||||
using hourly_file_sink_st = hourly_file_sink<details::null_mutex>;
|
||||
|
||||
} // namespace sinks
|
||||
|
||||
//
|
||||
// factory functions
|
||||
//
|
||||
template<typename Factory = spdlog::synchronous_factory>
|
||||
inline std::shared_ptr<logger> hourly_logger_mt(
|
||||
const std::string &logger_name, const filename_t &filename, bool truncate = false, uint16_t max_files = 0)
|
||||
{
|
||||
return Factory::template create<sinks::hourly_file_sink_mt>(logger_name, filename, truncate, max_files);
|
||||
}
|
||||
|
||||
template<typename Factory = spdlog::synchronous_factory>
|
||||
inline std::shared_ptr<logger> hourly_logger_st(
|
||||
const std::string &logger_name, const filename_t &filename, bool truncate = false, uint16_t max_files = 0)
|
||||
{
|
||||
return Factory::template create<sinks::hourly_file_sink_st>(logger_name, filename, truncate, max_files);
|
||||
}
|
||||
} // namespace spdlog
|
98
include/spdlog/sinks/mongo_sink.h
Normal file
98
include/spdlog/sinks/mongo_sink.h
Normal 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_ = spdlog::details::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
|
@@ -5,14 +5,14 @@
|
||||
|
||||
#if defined(_WIN32)
|
||||
|
||||
#include <spdlog/details/null_mutex.h>
|
||||
#include <spdlog/sinks/base_sink.h>
|
||||
# include <spdlog/details/null_mutex.h>
|
||||
# include <spdlog/sinks/base_sink.h>
|
||||
|
||||
#include <spdlog/details/windows_include.h>
|
||||
#include <winbase.h>
|
||||
# include <mutex>
|
||||
# include <string>
|
||||
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
// Avoid including windows.h (https://stackoverflow.com/a/30741042)
|
||||
extern "C" __declspec(dllimport) void __stdcall OutputDebugStringA(const char *lpOutputString);
|
||||
|
||||
namespace spdlog {
|
||||
namespace sinks {
|
||||
@@ -23,12 +23,11 @@ template<typename Mutex>
|
||||
class msvc_sink : public base_sink<Mutex>
|
||||
{
|
||||
public:
|
||||
explicit msvc_sink() {}
|
||||
msvc_sink() = default;
|
||||
|
||||
protected:
|
||||
void sink_it_(const details::log_msg &msg) override
|
||||
{
|
||||
|
||||
memory_buf_t formatted;
|
||||
base_sink<Mutex>::formatter_->format(msg, formatted);
|
||||
OutputDebugStringA(fmt::to_string(formatted).c_str());
|
||||
|
93
include/spdlog/sinks/qt_sinks.h
Normal file
93
include/spdlog/sinks/qt_sinks.h
Normal file
@@ -0,0 +1,93 @@
|
||||
// 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 childs(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 <QTextEdit>
|
||||
#include <QPlainTextEdit>
|
||||
|
||||
//
|
||||
// qt_sink class
|
||||
//
|
||||
namespace spdlog {
|
||||
namespace sinks {
|
||||
template <typename Mutex> class qt_sink : public base_sink<Mutex> {
|
||||
public:
|
||||
qt_sink(QObject *qt_object = nullptr, const std::string &meta_method = "") {
|
||||
qt_object_ = qt_object;
|
||||
meta_method_ = meta_method;
|
||||
}
|
||||
|
||||
~qt_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 = string_view_t(formatted.data(), formatted.size());
|
||||
QMetaObject::invokeMethod(qt_object_, meta_method_.c_str(), Qt::AutoConnection,
|
||||
Q_ARG(QString, QString::fromUtf8(str.data(), static_cast<int>(str.size())).trimmed()));
|
||||
}
|
||||
|
||||
void flush_() override {}
|
||||
|
||||
private:
|
||||
QObject *qt_object_ = nullptr;
|
||||
std::string meta_method_;
|
||||
};
|
||||
|
||||
#include "spdlog/details/null_mutex.h"
|
||||
#include <mutex>
|
||||
using qt_sink_mt = qt_sink<std::mutex>;
|
||||
using qt_sink_st = qt_sink<spdlog::details::null_mutex>;
|
||||
} // namespace sinks
|
||||
|
||||
//
|
||||
// Factory functions
|
||||
//
|
||||
template <typename Factory = spdlog::synchronous_factory>
|
||||
inline std::shared_ptr<logger>
|
||||
qt_logger_mt(const std::string &logger_name, QTextEdit* qt_object, const std::string &meta_method = "append") {
|
||||
return Factory::template create<sinks::qt_sink_mt>(logger_name, qt_object, meta_method);
|
||||
}
|
||||
|
||||
template <typename Factory = spdlog::synchronous_factory>
|
||||
inline std::shared_ptr<logger>
|
||||
qt_logger_st(const std::string &logger_name, QTextEdit* qt_object, const std::string &meta_method = "append") {
|
||||
return Factory::template create<sinks::qt_sink_st>(logger_name, qt_object, meta_method);
|
||||
}
|
||||
|
||||
template <typename Factory = spdlog::synchronous_factory>
|
||||
inline std::shared_ptr<logger>
|
||||
qt_logger_mt(const std::string &logger_name, QPlainTextEdit* qt_object , const std::string &meta_method = "appendPlainText") {
|
||||
return Factory::template create<sinks::qt_sink_mt>(logger_name, qt_object, meta_method);
|
||||
}
|
||||
|
||||
template <typename Factory = spdlog::synchronous_factory>
|
||||
inline std::shared_ptr<logger>
|
||||
qt_logger_st(const std::string &logger_name, QPlainTextEdit* qt_object, const std::string &meta_method = "appendPlainText") {
|
||||
return Factory::template create<sinks::qt_sink_st>(logger_name, qt_object, meta_method);
|
||||
}
|
||||
|
||||
template <typename Factory = spdlog::synchronous_factory>
|
||||
inline std::shared_ptr<logger>
|
||||
qt_logger_mt(const std::string &logger_name, QObject* qt_object, const std::string &meta_method) {
|
||||
return Factory::template create<sinks::qt_sink_mt>(logger_name, qt_object, meta_method);
|
||||
}
|
||||
|
||||
template <typename Factory = spdlog::synchronous_factory>
|
||||
inline std::shared_ptr<logger>
|
||||
qt_logger_st(const std::string &logger_name, QObject* qt_object, const std::string &meta_method) {
|
||||
return Factory::template create<sinks::qt_sink_st>(logger_name, qt_object, meta_method);
|
||||
}
|
||||
} // namespace spdlog
|
@@ -4,7 +4,7 @@
|
||||
#pragma once
|
||||
|
||||
#ifndef SPDLOG_HEADER_ONLY
|
||||
#include <spdlog/sinks/rotating_file_sink.h>
|
||||
# include <spdlog/sinks/rotating_file_sink.h>
|
||||
#endif
|
||||
|
||||
#include <spdlog/common.h>
|
||||
|
@@ -74,5 +74,5 @@ inline std::shared_ptr<logger> rotating_logger_st(
|
||||
} // namespace spdlog
|
||||
|
||||
#ifdef SPDLOG_HEADER_ONLY
|
||||
#include "rotating_file_sink-inl.h"
|
||||
# include "rotating_file_sink-inl.h"
|
||||
#endif
|
||||
|
@@ -4,7 +4,7 @@
|
||||
#pragma once
|
||||
|
||||
#ifndef SPDLOG_HEADER_ONLY
|
||||
#include <spdlog/sinks/sink.h>
|
||||
# include <spdlog/sinks/sink.h>
|
||||
#endif
|
||||
|
||||
#include <spdlog/common.h>
|
||||
|
@@ -31,5 +31,5 @@ protected:
|
||||
} // namespace spdlog
|
||||
|
||||
#ifdef SPDLOG_HEADER_ONLY
|
||||
#include "sink-inl.h"
|
||||
# include "sink-inl.h"
|
||||
#endif
|
||||
|
@@ -4,7 +4,7 @@
|
||||
#pragma once
|
||||
|
||||
#ifndef SPDLOG_HEADER_ONLY
|
||||
#include <spdlog/sinks/stdout_color_sinks.h>
|
||||
# include <spdlog/sinks/stdout_color_sinks.h>
|
||||
#endif
|
||||
|
||||
#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);
|
||||
}
|
||||
} // namespace spdlog
|
||||
} // namespace spdlog
|
||||
|
@@ -4,9 +4,9 @@
|
||||
#pragma once
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <spdlog/sinks/wincolor_sink.h>
|
||||
# include <spdlog/sinks/wincolor_sink.h>
|
||||
#else
|
||||
#include <spdlog/sinks/ansicolor_sink.h>
|
||||
# include <spdlog/sinks/ansicolor_sink.h>
|
||||
#endif
|
||||
|
||||
#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
|
||||
|
||||
#ifdef SPDLOG_HEADER_ONLY
|
||||
#include "stdout_color_sinks-inl.h"
|
||||
# include "stdout_color_sinks-inl.h"
|
||||
#endif
|
||||
|
@@ -4,13 +4,26 @@
|
||||
#pragma once
|
||||
|
||||
#ifndef SPDLOG_HEADER_ONLY
|
||||
#include <spdlog/sinks/stdout_sinks.h>
|
||||
# include <spdlog/sinks/stdout_sinks.h>
|
||||
#endif
|
||||
|
||||
#include <spdlog/details/console_globals.h>
|
||||
#include <spdlog/pattern_formatter.h>
|
||||
#include <memory>
|
||||
|
||||
#ifdef _WIN32
|
||||
// under windows using fwrite to non-binary stream results in \r\r\n (see issue #1675)
|
||||
// so instead we use ::FileWrite
|
||||
# include <spdlog/details/windows_include.h>
|
||||
|
||||
# ifndef _USING_V110_SDK71_ // fileapi.h doesnt exist in winxp
|
||||
# include <fileapi.h> // WriteFile (..)
|
||||
# endif
|
||||
|
||||
# include <io.h> // _get_osfhandle(..)
|
||||
# include <stdio.h> // _fileno(..)
|
||||
#endif // WIN32
|
||||
|
||||
namespace spdlog {
|
||||
|
||||
namespace sinks {
|
||||
@@ -20,16 +33,48 @@ SPDLOG_INLINE stdout_sink_base<ConsoleMutex>::stdout_sink_base(FILE *file)
|
||||
: mutex_(ConsoleMutex::mutex())
|
||||
, file_(file)
|
||||
, formatter_(details::make_unique<spdlog::pattern_formatter>())
|
||||
{}
|
||||
{
|
||||
#ifdef _WIN32
|
||||
// get windows handle from the FILE* object
|
||||
|
||||
handle_ = (HANDLE)::_get_osfhandle(::_fileno(file_));
|
||||
|
||||
// don't throw to support cases where no console is attached,
|
||||
// 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).
|
||||
if (handle_ == INVALID_HANDLE_VALUE && file != stdout && file != stderr)
|
||||
{
|
||||
throw_spdlog_ex("spdlog::stdout_sink_base: _get_osfhandle() failed", errno);
|
||||
}
|
||||
#endif // WIN32
|
||||
}
|
||||
|
||||
template<typename ConsoleMutex>
|
||||
SPDLOG_INLINE void stdout_sink_base<ConsoleMutex>::log(const details::log_msg &msg)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
if (handle_ == INVALID_HANDLE_VALUE)
|
||||
{
|
||||
return;
|
||||
}
|
||||
std::lock_guard<mutex_t> lock(mutex_);
|
||||
memory_buf_t formatted;
|
||||
formatter_->format(msg, formatted);
|
||||
fwrite(formatted.data(), sizeof(char), formatted.size(), file_);
|
||||
fflush(file_); // flush every line to terminal
|
||||
::fflush(file_); // flush in case there is somthing in this file_ already
|
||||
auto size = static_cast<DWORD>(formatted.size());
|
||||
DWORD bytes_written = 0;
|
||||
bool ok = ::WriteFile(handle_, formatted.data(), size, &bytes_written, nullptr) != 0;
|
||||
if (!ok)
|
||||
{
|
||||
throw_spdlog_ex("stdout_sink_base: WriteFile() failed. GetLastError(): " + std::to_string(::GetLastError()));
|
||||
}
|
||||
#else
|
||||
std::lock_guard<mutex_t> lock(mutex_);
|
||||
memory_buf_t formatted;
|
||||
formatter_->format(msg, formatted);
|
||||
::fwrite(formatted.data(), sizeof(char), formatted.size(), file_);
|
||||
::fflush(file_); // flush every line to terminal
|
||||
#endif // WIN32
|
||||
}
|
||||
|
||||
template<typename ConsoleMutex>
|
||||
|
@@ -8,6 +8,10 @@
|
||||
#include <spdlog/sinks/sink.h>
|
||||
#include <cstdio>
|
||||
|
||||
#ifdef _WIN32
|
||||
# include <spdlog/details/windows_include.h>
|
||||
#endif
|
||||
|
||||
namespace spdlog {
|
||||
|
||||
namespace sinks {
|
||||
@@ -36,6 +40,9 @@ protected:
|
||||
mutex_t &mutex_;
|
||||
FILE *file_;
|
||||
std::unique_ptr<spdlog::formatter> formatter_;
|
||||
#ifdef _WIN32
|
||||
HANDLE handle_;
|
||||
#endif // WIN32
|
||||
};
|
||||
|
||||
template<typename ConsoleMutex>
|
||||
@@ -76,5 +83,5 @@ std::shared_ptr<logger> stderr_logger_st(const std::string &logger_name);
|
||||
} // namespace spdlog
|
||||
|
||||
#ifdef SPDLOG_HEADER_ONLY
|
||||
#include "stdout_sinks-inl.h"
|
||||
# include "stdout_sinks-inl.h"
|
||||
#endif
|
||||
|
@@ -9,7 +9,7 @@
|
||||
|
||||
#include <array>
|
||||
#ifndef SD_JOURNAL_SUPPRESS_LOCATION
|
||||
#define SD_JOURNAL_SUPPRESS_LOCATION
|
||||
# define SD_JOURNAL_SUPPRESS_LOCATION
|
||||
#endif
|
||||
#include <systemd/sd-journal.h>
|
||||
|
||||
|
@@ -7,9 +7,9 @@
|
||||
#include <spdlog/sinks/base_sink.h>
|
||||
#include <spdlog/details/null_mutex.h>
|
||||
#ifdef _WIN32
|
||||
#include <spdlog/details/tcp_client-windows.h>
|
||||
# include <spdlog/details/tcp_client-windows.h>
|
||||
#else
|
||||
#include <spdlog/details/tcp_client.h>
|
||||
# include <spdlog/details/tcp_client.h>
|
||||
#endif
|
||||
|
||||
#include <mutex>
|
||||
|
@@ -57,7 +57,7 @@ struct win32_error : public spdlog_ex
|
||||
|
||||
LPSTR format_message_result{};
|
||||
auto format_message_succeeded =
|
||||
::FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, nullptr,
|
||||
::FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, nullptr,
|
||||
error_code, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPSTR)&format_message_result, 0, nullptr);
|
||||
|
||||
if (format_message_succeeded && format_message_result)
|
||||
@@ -203,7 +203,7 @@ private:
|
||||
{
|
||||
if (!hEventLog_)
|
||||
{
|
||||
hEventLog_ = ::RegisterEventSource(nullptr, source_.c_str());
|
||||
hEventLog_ = ::RegisterEventSourceA(nullptr, source_.c_str());
|
||||
if (!hEventLog_ || hEventLog_ == (HANDLE)ERROR_ACCESS_DENIED)
|
||||
{
|
||||
SPDLOG_THROW(internal::win32_error("RegisterEventSource"));
|
||||
@@ -218,13 +218,23 @@ protected:
|
||||
{
|
||||
using namespace internal;
|
||||
|
||||
bool succeeded;
|
||||
memory_buf_t formatted;
|
||||
base_sink<Mutex>::formatter_->format(msg, formatted);
|
||||
formatted.push_back('\0');
|
||||
LPCSTR lp_str = static_cast<LPCSTR>(formatted.data());
|
||||
|
||||
auto succeeded = ::ReportEvent(event_log_handle(), eventlog::get_event_type(msg), eventlog::get_event_category(msg), event_id_,
|
||||
#ifdef SPDLOG_WCHAR_TO_UTF8_SUPPORT
|
||||
wmemory_buf_t buf;
|
||||
details::os::utf8_to_wstrbuf(string_view_t(formatted.data(), formatted.size()), buf);
|
||||
|
||||
LPCWSTR lp_wstr = buf.data();
|
||||
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);
|
||||
#else
|
||||
LPCSTR lp_str = formatted.data();
|
||||
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);
|
||||
#endif
|
||||
|
||||
if (!succeeded)
|
||||
{
|
||||
|
@@ -4,33 +4,33 @@
|
||||
#pragma once
|
||||
|
||||
#ifndef SPDLOG_HEADER_ONLY
|
||||
#include <spdlog/sinks/wincolor_sink.h>
|
||||
# include <spdlog/sinks/wincolor_sink.h>
|
||||
#endif
|
||||
|
||||
#include <spdlog/details/windows_include.h>
|
||||
#include <wincon.h>
|
||||
|
||||
#include <spdlog/common.h>
|
||||
#include <spdlog/pattern_formatter.h>
|
||||
|
||||
namespace spdlog {
|
||||
namespace sinks {
|
||||
|
||||
template<typename ConsoleMutex>
|
||||
SPDLOG_INLINE wincolor_sink<ConsoleMutex>::wincolor_sink(HANDLE out_handle, color_mode mode)
|
||||
SPDLOG_INLINE wincolor_sink<ConsoleMutex>::wincolor_sink(void *out_handle, color_mode mode)
|
||||
: out_handle_(out_handle)
|
||||
, mutex_(ConsoleMutex::mutex())
|
||||
, formatter_(details::make_unique<spdlog::pattern_formatter>())
|
||||
{
|
||||
// check if out_handle is points to the actual console.
|
||||
// ::GetConsoleMode() should return 0 if it is redirected or not valid console handle.
|
||||
DWORD console_mode;
|
||||
in_console_ = ::GetConsoleMode(out_handle, &console_mode) != 0;
|
||||
|
||||
set_color_mode(mode);
|
||||
colors_[level::trace] = WHITE;
|
||||
colors_[level::debug] = CYAN;
|
||||
colors_[level::info] = GREEN;
|
||||
colors_[level::warn] = YELLOW | BOLD;
|
||||
colors_[level::err] = RED | BOLD; // red bold
|
||||
colors_[level::critical] = BACKGROUND_RED | WHITE | BOLD; // white bold on red background
|
||||
set_color_mode_impl(mode);
|
||||
// set level colors
|
||||
colors_[level::trace] = FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE; // white
|
||||
colors_[level::debug] = FOREGROUND_GREEN | FOREGROUND_BLUE; // cyan
|
||||
colors_[level::info] = FOREGROUND_GREEN; // green
|
||||
colors_[level::warn] = FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_INTENSITY; // intense yellow
|
||||
colors_[level::err] = FOREGROUND_RED | FOREGROUND_INTENSITY; // intense red
|
||||
colors_[level::critical] =
|
||||
BACKGROUND_RED | FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE | FOREGROUND_INTENSITY; // intense white on red background
|
||||
colors_[level::off] = 0;
|
||||
}
|
||||
|
||||
@@ -42,7 +42,7 @@ SPDLOG_INLINE wincolor_sink<ConsoleMutex>::~wincolor_sink()
|
||||
|
||||
// change the color for the given level
|
||||
template<typename ConsoleMutex>
|
||||
void SPDLOG_INLINE wincolor_sink<ConsoleMutex>::set_color(level::level_enum level, WORD color)
|
||||
void SPDLOG_INLINE wincolor_sink<ConsoleMutex>::set_color(level::level_enum level, std::uint16_t color)
|
||||
{
|
||||
std::lock_guard<mutex_t> lock(mutex_);
|
||||
colors_[level] = color;
|
||||
@@ -51,31 +51,30 @@ void SPDLOG_INLINE wincolor_sink<ConsoleMutex>::set_color(level::level_enum leve
|
||||
template<typename ConsoleMutex>
|
||||
void SPDLOG_INLINE wincolor_sink<ConsoleMutex>::log(const details::log_msg &msg)
|
||||
{
|
||||
if (out_handle_ == nullptr || out_handle_ == INVALID_HANDLE_VALUE)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<mutex_t> lock(mutex_);
|
||||
msg.color_range_start = 0;
|
||||
msg.color_range_end = 0;
|
||||
memory_buf_t formatted;
|
||||
formatter_->format(msg, formatted);
|
||||
if (!in_console_)
|
||||
{
|
||||
write_to_file_(formatted);
|
||||
return;
|
||||
}
|
||||
if (should_do_colors_ && msg.color_range_end > msg.color_range_start)
|
||||
{
|
||||
// before color range
|
||||
print_range_(formatted, 0, msg.color_range_start);
|
||||
|
||||
// in color range
|
||||
auto orig_attribs = set_foreground_color_(colors_[msg.level]);
|
||||
auto orig_attribs = static_cast<WORD>(set_foreground_color_(colors_[msg.level]));
|
||||
print_range_(formatted, msg.color_range_start, msg.color_range_end);
|
||||
// reset to orig colors
|
||||
::SetConsoleTextAttribute(out_handle_, orig_attribs);
|
||||
::SetConsoleTextAttribute(static_cast<HANDLE>(out_handle_), orig_attribs);
|
||||
print_range_(formatted, msg.color_range_end, formatted.size());
|
||||
}
|
||||
else // print without colors if color range is invalid (or color is disabled)
|
||||
{
|
||||
print_range_(formatted, 0, formatted.size());
|
||||
write_to_file_(formatted);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -102,66 +101,63 @@ void SPDLOG_INLINE wincolor_sink<ConsoleMutex>::set_formatter(std::unique_ptr<sp
|
||||
template<typename ConsoleMutex>
|
||||
void SPDLOG_INLINE wincolor_sink<ConsoleMutex>::set_color_mode(color_mode mode)
|
||||
{
|
||||
switch (mode)
|
||||
std::lock_guard<mutex_t> lock(mutex_);
|
||||
set_color_mode_impl(mode);
|
||||
}
|
||||
|
||||
template<typename ConsoleMutex>
|
||||
void SPDLOG_INLINE wincolor_sink<ConsoleMutex>::set_color_mode_impl(color_mode mode)
|
||||
{
|
||||
if (mode == color_mode::automatic)
|
||||
{
|
||||
case color_mode::always:
|
||||
case color_mode::automatic:
|
||||
should_do_colors_ = true;
|
||||
break;
|
||||
case color_mode::never:
|
||||
should_do_colors_ = false;
|
||||
break;
|
||||
default:
|
||||
should_do_colors_ = true;
|
||||
// should do colors only if out_handle_ points to actual console.
|
||||
DWORD console_mode;
|
||||
bool in_console = ::GetConsoleMode(static_cast<HANDLE>(out_handle_), &console_mode) != 0;
|
||||
should_do_colors_ = in_console;
|
||||
}
|
||||
else
|
||||
{
|
||||
should_do_colors_ = mode == color_mode::always ? true : false;
|
||||
}
|
||||
}
|
||||
|
||||
// set foreground color and return the orig console attributes (for resetting later)
|
||||
template<typename ConsoleMutex>
|
||||
WORD SPDLOG_INLINE wincolor_sink<ConsoleMutex>::set_foreground_color_(WORD attribs)
|
||||
std::uint16_t SPDLOG_INLINE wincolor_sink<ConsoleMutex>::set_foreground_color_(std::uint16_t attribs)
|
||||
{
|
||||
CONSOLE_SCREEN_BUFFER_INFO orig_buffer_info;
|
||||
::GetConsoleScreenBufferInfo(out_handle_, &orig_buffer_info);
|
||||
WORD back_color = orig_buffer_info.wAttributes;
|
||||
// retrieve the current background color
|
||||
back_color &= static_cast<WORD>(~(FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE | FOREGROUND_INTENSITY));
|
||||
// keep the background color unchanged
|
||||
::SetConsoleTextAttribute(out_handle_, attribs | back_color);
|
||||
return orig_buffer_info.wAttributes; // return orig attribs
|
||||
if (!::GetConsoleScreenBufferInfo(static_cast<HANDLE>(out_handle_), &orig_buffer_info))
|
||||
{
|
||||
// just return white if failed getting console info
|
||||
return FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE;
|
||||
}
|
||||
|
||||
// change only the foreground bits (lowest 4 bits)
|
||||
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));
|
||||
(void)(ignored);
|
||||
return static_cast<std::uint16_t>(orig_buffer_info.wAttributes); // return orig attribs
|
||||
}
|
||||
|
||||
// print a range of formatted message to console
|
||||
template<typename ConsoleMutex>
|
||||
void SPDLOG_INLINE wincolor_sink<ConsoleMutex>::print_range_(const memory_buf_t &formatted, size_t start, size_t end)
|
||||
{
|
||||
auto size = static_cast<DWORD>(end - start);
|
||||
::WriteConsoleA(out_handle_, formatted.data() + start, size, nullptr, nullptr);
|
||||
if (end > start)
|
||||
{
|
||||
auto size = static_cast<DWORD>(end - start);
|
||||
auto ignored = ::WriteConsoleA(static_cast<HANDLE>(out_handle_), formatted.data() + start, size, nullptr, nullptr);
|
||||
(void)(ignored);
|
||||
}
|
||||
}
|
||||
|
||||
template<typename ConsoleMutex>
|
||||
void SPDLOG_INLINE wincolor_sink<ConsoleMutex>::write_to_file_(const memory_buf_t &formatted)
|
||||
{
|
||||
if (out_handle_ == nullptr) // no console and no file redirect
|
||||
{
|
||||
return;
|
||||
}
|
||||
auto size = static_cast<DWORD>(formatted.size());
|
||||
if (size == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
DWORD total_written = 0;
|
||||
do
|
||||
{
|
||||
DWORD bytes_written = 0;
|
||||
bool ok = ::WriteFile(out_handle_, formatted.data() + total_written, size - total_written, &bytes_written, nullptr) != 0;
|
||||
if (!ok || bytes_written == 0)
|
||||
{
|
||||
throw_spdlog_ex("wincolor_sink: write_to_file_ failed. GetLastError(): " + std::to_string(::GetLastError()));
|
||||
}
|
||||
total_written += bytes_written;
|
||||
} while (total_written < size);
|
||||
DWORD bytes_written = 0;
|
||||
auto ignored = ::WriteFile(static_cast<HANDLE>(out_handle_), formatted.data(), size, &bytes_written, nullptr);
|
||||
(void)(ignored);
|
||||
}
|
||||
|
||||
// wincolor_stdout_sink
|
||||
@@ -175,6 +171,5 @@ template<typename ConsoleMutex>
|
||||
SPDLOG_INLINE wincolor_stderr_sink<ConsoleMutex>::wincolor_stderr_sink(color_mode mode)
|
||||
: wincolor_sink<ConsoleMutex>(::GetStdHandle(STD_ERROR_HANDLE), mode)
|
||||
{}
|
||||
|
||||
} // namespace sinks
|
||||
} // namespace spdlog
|
||||
|
@@ -12,9 +12,7 @@
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
#include <array>
|
||||
|
||||
#include <spdlog/details/windows_include.h>
|
||||
#include <wincon.h>
|
||||
#include <cstdint>
|
||||
|
||||
namespace spdlog {
|
||||
namespace sinks {
|
||||
@@ -26,21 +24,14 @@ template<typename ConsoleMutex>
|
||||
class wincolor_sink : public sink
|
||||
{
|
||||
public:
|
||||
const WORD BOLD = FOREGROUND_INTENSITY;
|
||||
const WORD RED = FOREGROUND_RED;
|
||||
const WORD GREEN = FOREGROUND_GREEN;
|
||||
const WORD CYAN = FOREGROUND_GREEN | FOREGROUND_BLUE;
|
||||
const WORD WHITE = FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE;
|
||||
const WORD YELLOW = FOREGROUND_RED | FOREGROUND_GREEN;
|
||||
|
||||
wincolor_sink(HANDLE out_handle, color_mode mode);
|
||||
wincolor_sink(void *out_handle, color_mode mode);
|
||||
~wincolor_sink() override;
|
||||
|
||||
wincolor_sink(const wincolor_sink &other) = delete;
|
||||
wincolor_sink &operator=(const wincolor_sink &other) = delete;
|
||||
|
||||
// change the color for the given level
|
||||
void set_color(level::level_enum level, WORD color);
|
||||
void set_color(level::level_enum level, std::uint16_t color);
|
||||
void log(const details::log_msg &msg) final override;
|
||||
void flush() final override;
|
||||
void set_pattern(const std::string &pattern) override final;
|
||||
@@ -49,21 +40,22 @@ public:
|
||||
|
||||
protected:
|
||||
using mutex_t = typename ConsoleMutex::mutex_t;
|
||||
HANDLE out_handle_;
|
||||
void *out_handle_;
|
||||
mutex_t &mutex_;
|
||||
bool in_console_;
|
||||
bool should_do_colors_;
|
||||
std::unique_ptr<spdlog::formatter> formatter_;
|
||||
std::array<WORD, level::n_levels> colors_;
|
||||
std::array<std::uint16_t, level::n_levels> colors_;
|
||||
|
||||
// set foreground color and return the orig console attributes (for resetting later)
|
||||
WORD set_foreground_color_(WORD attribs);
|
||||
std::uint16_t set_foreground_color_(std::uint16_t attribs);
|
||||
|
||||
// print a range of formatted message to console
|
||||
void print_range_(const memory_buf_t &formatted, size_t start, size_t end);
|
||||
|
||||
// in case we are redirected to file (not in console mode)
|
||||
void write_to_file_(const memory_buf_t &formatted);
|
||||
|
||||
void set_color_mode_impl(color_mode mode);
|
||||
};
|
||||
|
||||
template<typename ConsoleMutex>
|
||||
@@ -85,10 +77,9 @@ using wincolor_stdout_sink_st = wincolor_stdout_sink<details::console_nullmutex>
|
||||
|
||||
using wincolor_stderr_sink_mt = wincolor_stderr_sink<details::console_mutex>;
|
||||
using wincolor_stderr_sink_st = wincolor_stderr_sink<details::console_nullmutex>;
|
||||
|
||||
} // namespace sinks
|
||||
} // namespace spdlog
|
||||
|
||||
#ifdef SPDLOG_HEADER_ONLY
|
||||
#include "wincolor_sink-inl.h"
|
||||
# include "wincolor_sink-inl.h"
|
||||
#endif
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user