mirror of
https://github.com/gabime/spdlog.git
synced 2026-01-02 01:47:54 +08:00
Compare commits
604 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
76fb40d954 | ||
|
|
757e9f8ec6 | ||
|
|
fc51c095ba | ||
|
|
36b4b9dac9 | ||
|
|
083ea59fbd | ||
|
|
c1aeefb0c9 | ||
|
|
3c1ee54112 | ||
|
|
a49456f7f2 | ||
|
|
52dc210423 | ||
|
|
b1478d98f0 | ||
|
|
5ee969e4f6 | ||
|
|
7f8a61e79d | ||
|
|
69cac816aa | ||
|
|
2f2d04b3e8 | ||
|
|
9cd9c98f59 | ||
|
|
f2461f1430 | ||
|
|
a732a0dc85 | ||
|
|
4c2ce2c82c | ||
|
|
4cea9b8729 | ||
|
|
53c9b70ea3 | ||
|
|
71105e0b07 | ||
|
|
c432fdd987 | ||
|
|
d8199b607d | ||
|
|
b7836c33ae | ||
|
|
d497f494f0 | ||
|
|
0b48976be4 | ||
|
|
5b03dc1796 | ||
|
|
ec8b0beddd | ||
|
|
7536192058 | ||
|
|
5afff7821f | ||
|
|
8fb112158a | ||
|
|
792d618c02 | ||
|
|
93f59d04e9 | ||
|
|
666bec5017 | ||
|
|
2382c87aa3 | ||
|
|
caa0e54396 | ||
|
|
28b9adf794 | ||
|
|
584d77237e | ||
|
|
d9ec02d400 | ||
|
|
5568b16ed5 | ||
|
|
eab522e743 | ||
|
|
4cfdc8c5c8 | ||
|
|
2a4c34b878 | ||
|
|
729d7f6d88 | ||
|
|
3540ba32e9 | ||
|
|
32fedcf90c | ||
|
|
626efad307 | ||
|
|
cc30229abb | ||
|
|
a087dee98a | ||
|
|
f096c615c3 | ||
|
|
f81cb9f365 | ||
|
|
7e95963940 | ||
|
|
3f49f0f247 | ||
|
|
4cb1187871 | ||
|
|
fe782edc53 | ||
|
|
702cf4f54a | ||
|
|
0c84e21022 | ||
|
|
ee74321ac3 | ||
|
|
e45c11f98a | ||
|
|
c211288576 | ||
|
|
4fefd51e08 | ||
|
|
ad08f13aac | ||
|
|
6638c23cfc | ||
|
|
378a42c887 | ||
|
|
9abcf38b90 | ||
|
|
8715f51c61 | ||
|
|
37cbab363e | ||
|
|
afdcfc710e | ||
|
|
16bc6d04ad | ||
|
|
ac6908a139 | ||
|
|
28e415fb3e | ||
|
|
ab2e72340a | ||
|
|
da9c16278a | ||
|
|
b5d6c939fd | ||
|
|
fda2b361da | ||
|
|
6636ff05e6 | ||
|
|
9e17fafe1b | ||
|
|
1f58535920 | ||
|
|
8dd012096a | ||
|
|
f81970191a | ||
|
|
b8b16e49a5 | ||
|
|
2c21d9ecf8 | ||
|
|
2a45eff693 | ||
|
|
5bf8728cfa | ||
|
|
e3e4c4bc95 | ||
|
|
0c611af552 | ||
|
|
f304ca3daf | ||
|
|
d93cea97ec | ||
|
|
cabbe65be4 | ||
|
|
8a6b5b9e62 | ||
|
|
c15262c493 | ||
|
|
9a12e4a885 | ||
|
|
f52d526e1e | ||
|
|
e1a4b28039 | ||
|
|
b3560d1567 | ||
|
|
c6d144dab9 | ||
|
|
d5c000394d | ||
|
|
58e2b455fb | ||
|
|
2ab86a46d0 | ||
|
|
569b851b80 | ||
|
|
232df72b82 | ||
|
|
e65efdbbe1 | ||
|
|
29b41741cb | ||
|
|
17f21df441 | ||
|
|
94d2a84995 | ||
|
|
aac187d3a0 | ||
|
|
8d46977060 | ||
|
|
ca1eaedf7b | ||
|
|
8bd5f4f883 | ||
|
|
dc030ec53c | ||
|
|
1756c5d37f | ||
|
|
2b4e07dd91 | ||
|
|
0df2582674 | ||
|
|
24e47efae0 | ||
|
|
10b640d773 | ||
|
|
ff80d10820 | ||
|
|
126a9fb261 | ||
|
|
4001032858 | ||
|
|
ad779e4865 | ||
|
|
701ef17227 | ||
|
|
5d6af189f1 | ||
|
|
518bf36aa9 | ||
|
|
5b7dfefc7e | ||
|
|
484bf07379 | ||
|
|
0ded003703 | ||
|
|
95aa159bdd | ||
|
|
ba120e524b | ||
|
|
a6945d046f | ||
|
|
108c656e66 | ||
|
|
2d77ef92b0 | ||
|
|
f6901606f5 | ||
|
|
849e90bd01 | ||
|
|
e86be93b4a | ||
|
|
698516f3f5 | ||
|
|
da621e4402 | ||
|
|
ea92864a4d | ||
|
|
a5fa6eb356 | ||
|
|
cbaf4880ad | ||
|
|
b813bb863d | ||
|
|
30fb78813b | ||
|
|
a3ad8b5f26 | ||
|
|
24a551c14e | ||
|
|
8e359baaec | ||
|
|
85bdfc8695 | ||
|
|
c466e2d8f8 | ||
|
|
d75de3d3b2 | ||
|
|
c8ba643f53 | ||
|
|
591eedcf36 | ||
|
|
48e35f9c3e | ||
|
|
89c4b1aabe | ||
|
|
6ff1b83038 | ||
|
|
4008f31add | ||
|
|
c475418975 | ||
|
|
a31ae23db1 | ||
|
|
44a4517e2b | ||
|
|
ff9313e6dd | ||
|
|
c47ae3b15d | ||
|
|
6aafa89d20 | ||
|
|
acbf18d0dd | ||
|
|
8826011c81 | ||
|
|
d6a78cb85b | ||
|
|
7812a4c89f | ||
|
|
ef540c1243 | ||
|
|
8ffbc0f114 | ||
|
|
21ba38972b | ||
|
|
d54b8e89c0 | ||
|
|
14eecc6e2a | ||
|
|
99fda0ed22 | ||
|
|
8e055a4086 | ||
|
|
d4967358a5 | ||
|
|
bae78f7b6c | ||
|
|
f97dcc72dc | ||
|
|
dd10e41b27 | ||
|
|
c0d10efabf | ||
|
|
fecb3f4307 | ||
|
|
9bb66c00e9 | ||
|
|
1ec50cdcfc | ||
|
|
5906ce844a | ||
|
|
2e66a27081 | ||
|
|
497fa60f57 | ||
|
|
2d1217006b | ||
|
|
444df2b287 | ||
|
|
8ee1c167b9 | ||
|
|
486dc5102e | ||
|
|
a1d9f501e3 | ||
|
|
4501f21ae7 | ||
|
|
649424b8ea | ||
|
|
a15f5137ef | ||
|
|
410e641dff | ||
|
|
c5fd8a0b97 | ||
|
|
5df9b11141 | ||
|
|
e159052e6d | ||
|
|
23f47ebc47 | ||
|
|
58e7f68004 | ||
|
|
29e5930090 | ||
|
|
deb178a0b1 | ||
|
|
e185926beb | ||
|
|
0d10e21c2f | ||
|
|
ed27592537 | ||
|
|
df45d78d14 | ||
|
|
c98b29aa67 | ||
|
|
388679b00e | ||
|
|
119467c580 | ||
|
|
c2550ac24a | ||
|
|
12ee35a3d1 | ||
|
|
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-*,
|
||||
|
||||
9
.gitignore
vendored
9
.gitignore
vendored
@@ -1,4 +1,6 @@
|
||||
# Auto generated files
|
||||
[Dd]ebug/
|
||||
[Rr]elease/
|
||||
build/*
|
||||
*.slo
|
||||
*.lo
|
||||
@@ -55,6 +57,7 @@ example/*
|
||||
|
||||
# generated files
|
||||
generated
|
||||
version.rc
|
||||
|
||||
# Cmake
|
||||
CMakeCache.txt
|
||||
@@ -67,6 +70,8 @@ install_manifest.txt
|
||||
/tests/tests.VC.db
|
||||
/tests/tests
|
||||
/tests/logs/*
|
||||
spdlogConfig.cmake
|
||||
spdlogConfigVersion.cmake
|
||||
|
||||
# idea
|
||||
.idea/
|
||||
@@ -81,3 +86,7 @@ cmake-build-*/
|
||||
*.tcl
|
||||
*.user
|
||||
*.sln
|
||||
|
||||
# macos
|
||||
*.DS_store
|
||||
*.xcodeproj/
|
||||
|
||||
101
.travis.yml
101
.travis.yml
@@ -5,11 +5,11 @@
|
||||
sudo: required
|
||||
language: cpp
|
||||
|
||||
# gcc 4.8
|
||||
addons: &gcc48
|
||||
# gcc 4.9
|
||||
addons: &gcc49
|
||||
apt:
|
||||
packages:
|
||||
- g++-4.8
|
||||
- g++-4.9
|
||||
sources:
|
||||
- ubuntu-toolchain-r-test
|
||||
|
||||
@@ -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,73 @@ 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"
|
||||
|
||||
|
||||
env:
|
||||
global:
|
||||
- BUILD_EXAMPLE='ON'
|
||||
|
||||
matrix:
|
||||
include:
|
||||
# Test gcc-4.8: C++11, Build=Debug/Release
|
||||
- env: GCC_VERSION=4.8 BUILD_TYPE=Debug CPP=11
|
||||
# Test gcc-4.9: C++11, Build=Release
|
||||
- env: GCC_VERSION=4.9 BUILD_TYPE=Release CPP=11 BUILD_EXAMPLE='OFF'
|
||||
os: linux
|
||||
addons: *gcc48
|
||||
|
||||
- env: GCC_VERSION=4.8 BUILD_TYPE=Release CPP=11
|
||||
os: linux
|
||||
addons: *gcc48
|
||||
|
||||
addons: *gcc49
|
||||
|
||||
# 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
|
||||
@@ -99,12 +142,12 @@ script:
|
||||
--warn-uninitialized \
|
||||
-DCMAKE_BUILD_TYPE=$BUILD_TYPE \
|
||||
-DCMAKE_CXX_STANDARD=$CPP \
|
||||
-DSPDLOG_BUILD_EXAMPLE=ON \
|
||||
-DSPDLOG_BUILD_EXAMPLE_HO=ON \
|
||||
-DSPDLOG_ENABLE_WARNINGS=ON \
|
||||
-DSPDLOG_BUILD_EXAMPLE=$BUILD_EXAMPLE \
|
||||
-DSPDLOG_BUILD_EXAMPLE_HO=$BUILD_EXAMPLE \
|
||||
-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
|
||||
|
||||
131
CMakeLists.txt
131
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...3.21)
|
||||
|
||||
# ---------------------------------------------------------------------------------------
|
||||
# Start spdlog project
|
||||
@@ -38,9 +30,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()
|
||||
|
||||
@@ -83,6 +80,7 @@ option(SPDLOG_BUILD_WARNINGS "Enable compiler warnings" OFF)
|
||||
|
||||
# install options
|
||||
option(SPDLOG_INSTALL "Generate the install target" ${SPDLOG_MASTER_PROJECT})
|
||||
option(SPDLOG_USE_STD_FORMAT "Use std::format instead of fmt library. No compile-time format string checking." OFF)
|
||||
option(SPDLOG_FMT_EXTERNAL "Use external fmt library instead of bundled" OFF)
|
||||
option(SPDLOG_FMT_EXTERNAL_HO "Use external fmt header-only library instead of bundled" OFF)
|
||||
option(SPDLOG_NO_EXCEPTIONS "Compile with -fno-exceptions. Call abort() on any spdlog exceptions" OFF)
|
||||
@@ -91,14 +89,27 @@ if(SPDLOG_FMT_EXTERNAL AND SPDLOG_FMT_EXTERNAL_HO)
|
||||
message(FATAL_ERROR "SPDLOG_FMT_EXTERNAL and SPDLOG_FMT_EXTERNAL_HO are mutually exclusive")
|
||||
endif()
|
||||
|
||||
if(SPDLOG_USE_STD_FORMAT AND SPDLOG_FMT_EXTERNAL_HO)
|
||||
message(FATAL_ERROR "SPDLOG_USE_STD_FORMAT and SPDLOG_FMT_EXTERNAL_HO are mutually exclusive")
|
||||
endif()
|
||||
|
||||
if(SPDLOG_USE_STD_FORMAT AND SPDLOG_FMT_EXTERNAL)
|
||||
message(FATAL_ERROR "SPDLOG_USE_STD_FORMAT and SPDLOG_FMT_EXTERNAL are mutually exclusive")
|
||||
endif()
|
||||
|
||||
# misc tweakme options
|
||||
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 +119,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")
|
||||
@@ -127,22 +139,22 @@ message(STATUS "Build type: " ${CMAKE_BUILD_TYPE})
|
||||
# ---------------------------------------------------------------------------------------
|
||||
set(SPDLOG_SRCS src/spdlog.cpp src/stdout_sinks.cpp src/color_sinks.cpp src/file_sinks.cpp src/async.cpp src/cfg.cpp)
|
||||
|
||||
if(NOT SPDLOG_FMT_EXTERNAL AND NOT SPDLOG_FMT_EXTERNAL_HO)
|
||||
if(NOT SPDLOG_USE_STD_FORMAT AND 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)
|
||||
if(NOT SPDLOG_USE_STD_FORMAT AND NOT SPDLOG_FMT_EXTERNAL AND NOT SPDLOG_FMT_EXTERNAL_HO)
|
||||
target_compile_definitions(spdlog PRIVATE FMT_EXPORT PUBLIC FMT_SHARED)
|
||||
endif()
|
||||
else()
|
||||
@@ -180,7 +192,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 +209,38 @@ 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
|
||||
SPDLOG_USE_STD_FORMAT)
|
||||
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()
|
||||
|
||||
# ---------------------------------------------------------------------------------------
|
||||
@@ -292,7 +290,7 @@ if(SPDLOG_INSTALL)
|
||||
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
|
||||
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR})
|
||||
|
||||
if(NOT SPDLOG_FMT_EXTERNAL AND NOT SPDLOG_FMT_EXTERNAL_HO)
|
||||
if(NOT SPDLOG_USE_STD_FORMAT AND NOT SPDLOG_FMT_EXTERNAL AND NOT SPDLOG_FMT_EXTERNAL_HO)
|
||||
install(DIRECTORY include/${PROJECT_NAME}/fmt/bundled/
|
||||
DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}/${PROJECT_NAME}/fmt/bundled/")
|
||||
endif()
|
||||
@@ -312,7 +310,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}")
|
||||
|
||||
71
README.md
71
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://app.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
|
||||
@@ -22,15 +22,17 @@ $ cmake .. && make -j
|
||||
* Android
|
||||
|
||||
## Package managers:
|
||||
* Debian: `sudo apt install libspdlog-dev`
|
||||
* Homebrew: `brew install spdlog`
|
||||
* MacPorts: `sudo port install spdlog`
|
||||
* FreeBSD: `cd /usr/ports/devel/spdlog/ && make install clean`
|
||||
* Fedora: `yum install spdlog`
|
||||
* FreeBSD: `pkg 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"
|
||||
|
||||
@@ -359,6 +373,35 @@ $ export SPDLOG_LEVEL=info,mylogger=trace
|
||||
$ ./example
|
||||
```
|
||||
|
||||
|
||||
---
|
||||
#### Log file open/close event handlers
|
||||
```c++
|
||||
// You can get callbacks from spdlog before/after log file has been opened or closed.
|
||||
// This is useful for cleanup procedures or for adding someting the start/end of the log files.
|
||||
void file_events_example()
|
||||
{
|
||||
// pass the spdlog::file_event_handlers to file sinks for open/close log file notifications
|
||||
spdlog::file_event_handlers handlers;
|
||||
handlers.before_open = [](spdlog::filename_t filename) { spdlog::info("Before opening {}", filename); };
|
||||
handlers.after_open = [](spdlog::filename_t filename, std::FILE *fstream) { fputs("After opening\n", fstream); };
|
||||
handlers.before_close = [](spdlog::filename_t filename, std::FILE *fstream) { fputs("Before closing\n", fstream); };
|
||||
handlers.after_close = [](spdlog::filename_t filename) { spdlog::info("After closing {}", filename); };
|
||||
auto my_logger = spdlog::basic_logger_st("some_logger", "logs/events-sample.txt", true, handlers);
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
#### Replace the Default Logger
|
||||
```c++
|
||||
void replace_default_logger_example()
|
||||
{
|
||||
auto new_logger = spdlog::basic_logger_mt("new_default_logger", "logs/new-default-log.txt", true);
|
||||
spdlog::set_default_logger(new_logger);
|
||||
spdlog::info("new logger log message");
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
## Benchmarks
|
||||
|
||||
@@ -418,6 +461,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>
|
||||
|
||||
|
||||
|
||||
79
appveyor.yml
79
appveyor.yml
@@ -4,32 +4,95 @@ environment:
|
||||
matrix:
|
||||
- GENERATOR: '"Visual Studio 14 2015"'
|
||||
BUILD_TYPE: Debug
|
||||
WCHAR: 'OFF'
|
||||
BUILD_SHARED: 'OFF'
|
||||
WCHAR: 'OFF'
|
||||
WCHAR_FILES: 'OFF'
|
||||
BUILD_EXAMPLE: 'ON'
|
||||
USE_STD_FORMAT: 'OFF'
|
||||
CXX_STANDARD: 11
|
||||
- GENERATOR: '"Visual Studio 14 2015"'
|
||||
BUILD_TYPE: Release
|
||||
WCHAR: 'ON'
|
||||
BUILD_SHARED: 'OFF'
|
||||
WCHAR: 'ON'
|
||||
WCHAR_FILES: 'OFF'
|
||||
BUILD_EXAMPLE: 'ON'
|
||||
USE_STD_FORMAT: 'OFF'
|
||||
CXX_STANDARD: 11
|
||||
- GENERATOR: '"Visual Studio 14 2015 Win64"'
|
||||
BUILD_TYPE: Debug
|
||||
WCHAR: 'ON'
|
||||
BUILD_SHARED: 'OFF'
|
||||
WCHAR: 'ON'
|
||||
WCHAR_FILES: 'OFF'
|
||||
BUILD_EXAMPLE: 'ON'
|
||||
USE_STD_FORMAT: 'OFF'
|
||||
CXX_STANDARD: 11
|
||||
- GENERATOR: '"Visual Studio 14 2015 Win64"'
|
||||
BUILD_TYPE: Release
|
||||
WCHAR: 'ON'
|
||||
BUILD_SHARED: 'OFF'
|
||||
WCHAR: 'ON'
|
||||
WCHAR_FILES: 'OFF'
|
||||
BUILD_EXAMPLE: 'ON'
|
||||
USE_STD_FORMAT: 'OFF'
|
||||
CXX_STANDARD: 11
|
||||
- GENERATOR: '"Visual Studio 15 2017 Win64"'
|
||||
BUILD_TYPE: Debug
|
||||
BUILD_SHARED: 'OFF'
|
||||
WCHAR: 'ON'
|
||||
BUILD_SHARED: 'OFF'
|
||||
WCHAR_FILES: 'OFF'
|
||||
BUILD_EXAMPLE: 'ON'
|
||||
USE_STD_FORMAT: 'OFF'
|
||||
CXX_STANDARD: 11
|
||||
- GENERATOR: '"Visual Studio 15 2017 Win64"'
|
||||
BUILD_TYPE: Release
|
||||
WCHAR: 'OFF'
|
||||
BUILD_SHARED: 'OFF'
|
||||
WCHAR: 'OFF'
|
||||
WCHAR_FILES: 'OFF'
|
||||
BUILD_EXAMPLE: 'ON'
|
||||
USE_STD_FORMAT: 'OFF'
|
||||
CXX_STANDARD: 11
|
||||
- GENERATOR: '"Visual Studio 15 2017 Win64"'
|
||||
BUILD_TYPE: Release
|
||||
WCHAR: 'OFF'
|
||||
BUILD_SHARED: 'ON'
|
||||
WCHAR: 'OFF'
|
||||
WCHAR_FILES: 'OFF'
|
||||
BUILD_EXAMPLE: 'ON'
|
||||
USE_STD_FORMAT: 'OFF'
|
||||
CXX_STANDARD: 11
|
||||
- GENERATOR: '"Visual Studio 15 2017 Win64"'
|
||||
BUILD_TYPE: Release
|
||||
BUILD_SHARED: 'ON'
|
||||
WCHAR: 'ON'
|
||||
WCHAR_FILES: 'ON'
|
||||
BUILD_EXAMPLE: 'OFF'
|
||||
USE_STD_FORMAT: 'OFF'
|
||||
CXX_STANDARD: 11
|
||||
- GENERATOR: '"Visual Studio 16 2019" -A x64'
|
||||
BUILD_TYPE: Release
|
||||
BUILD_SHARED: 'ON'
|
||||
WCHAR: 'OFF'
|
||||
WCHAR_FILES: 'OFF'
|
||||
BUILD_EXAMPLE: 'OFF'
|
||||
USE_STD_FORMAT: 'OFF'
|
||||
CXX_STANDARD: 17
|
||||
APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2019
|
||||
- GENERATOR: '"Visual Studio 17 2022" -A x64'
|
||||
BUILD_TYPE: Release
|
||||
BUILD_SHARED: 'ON'
|
||||
WCHAR: 'OFF'
|
||||
WCHAR_FILES: 'OFF'
|
||||
BUILD_EXAMPLE: 'OFF'
|
||||
USE_STD_FORMAT: 'ON'
|
||||
CXX_STANDARD: 23 # std::format is only available with /std:c++latest at the moment.
|
||||
APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2022
|
||||
- GENERATOR: '"Visual Studio 17 2022" -A x64'
|
||||
BUILD_TYPE: Release
|
||||
BUILD_SHARED: 'ON'
|
||||
WCHAR: 'ON'
|
||||
WCHAR_FILES: 'ON'
|
||||
BUILD_EXAMPLE: 'OFF'
|
||||
USE_STD_FORMAT: 'ON'
|
||||
CXX_STANDARD: 23 # std::format is only available with /std:c++latest at the moment.
|
||||
APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2022
|
||||
build_script:
|
||||
- cmd: >-
|
||||
set
|
||||
@@ -40,7 +103,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 -D SPDLOG_USE_STD_FORMAT=%USE_STD_FORMAT% -D CMAKE_CXX_STANDARD=%CXX_STANDARD% ..
|
||||
|
||||
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,23 @@ 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)
|
||||
|
||||
# disable tests
|
||||
set(BENCHMARK_ENABLE_TESTING OFF CACHE INTERNAL "")
|
||||
# Do not build and run googlebenchmark tests
|
||||
FetchContent_Declare(googlebenchmark GIT_REPOSITORY https://github.com/google/benchmark.git GIT_TAG v1.6.0)
|
||||
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,14 @@
|
||||
#include "spdlog/spdlog.h"
|
||||
#include "spdlog/async.h"
|
||||
#include "spdlog/sinks/basic_file_sink.h"
|
||||
#include "spdlog/sinks/stdout_color_sinks.h"
|
||||
|
||||
#if defined(SPDLOG_USE_STD_FORMAT)
|
||||
# include <format>
|
||||
#elif defined(SPDLOG_FMT_EXTERNAL)
|
||||
# include <fmt/format.h>
|
||||
#else
|
||||
# include "spdlog/fmt/bundled/format.h"
|
||||
#endif
|
||||
|
||||
#include "utils.h"
|
||||
#include <atomic>
|
||||
@@ -27,9 +34,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 +55,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 +105,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";
|
||||
@@ -155,7 +162,7 @@ void thread_fun(std::shared_ptr<spdlog::logger> logger, int howmany)
|
||||
void bench_mt(int howmany, std::shared_ptr<spdlog::logger> logger, int thread_count)
|
||||
{
|
||||
using std::chrono::high_resolution_clock;
|
||||
vector<thread> threads;
|
||||
vector<std::thread> threads;
|
||||
auto start = high_resolution_clock::now();
|
||||
|
||||
int msgs_per_thread = howmany / thread_count;
|
||||
@@ -175,5 +182,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,14 @@
|
||||
#include "spdlog/sinks/null_sink.h"
|
||||
#include "spdlog/sinks/rotating_file_sink.h"
|
||||
|
||||
#if defined(SPDLOG_USE_STD_FORMAT)
|
||||
# include <format>
|
||||
#elif defined(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 +40,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(spdlog::fmt_lib::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 +76,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(spdlog::fmt_lib::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 +130,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(spdlog::fmt_lib::format("Number of threads exceeds maximum({})", max_threads));
|
||||
}
|
||||
|
||||
bench_single_threaded(iters);
|
||||
@@ -152,7 +160,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(spdlog::fmt_lib::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 +191,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(spdlog::fmt_lib::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 +215,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 +242,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,15 +1,20 @@
|
||||
# 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@)
|
||||
set(SPDLOG_FMT_EXTERNAL_HO @SPDLOG_FMT_EXTERNAL_HO@)
|
||||
set(config_targets_file @config_targets_file@)
|
||||
|
||||
if(SPDLOG_FMT_EXTERNAL)
|
||||
if(SPDLOG_FMT_EXTERNAL OR SPDLOG_FMT_EXTERNAL_HO)
|
||||
include(CMakeFindDependencyMacro)
|
||||
find_dependency(fmt CONFIG)
|
||||
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)
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
// spdlog usage example
|
||||
|
||||
#include <cstdio>
|
||||
#include <chrono>
|
||||
|
||||
void load_levels_example();
|
||||
void stdout_logger_example();
|
||||
@@ -13,15 +14,21 @@ void rotating_example();
|
||||
void daily_example();
|
||||
void async_example();
|
||||
void binary_example();
|
||||
void vector_example();
|
||||
void stopwatch_example();
|
||||
void trace_example();
|
||||
void multi_sink_example();
|
||||
void user_defined_example();
|
||||
void err_handler_example();
|
||||
void syslog_example();
|
||||
void udp_example();
|
||||
void custom_flags_example();
|
||||
void file_events_example();
|
||||
void replace_default_logger_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 *[])
|
||||
{
|
||||
@@ -67,11 +74,16 @@ int main(int, char *[])
|
||||
daily_example();
|
||||
async_example();
|
||||
binary_example();
|
||||
vector_example();
|
||||
multi_sink_example();
|
||||
user_defined_example();
|
||||
err_handler_example();
|
||||
trace_example();
|
||||
stopwatch_example();
|
||||
udp_example();
|
||||
custom_flags_example();
|
||||
file_events_example();
|
||||
replace_default_logger_example();
|
||||
|
||||
// Flush all *registered* loggers using a worker thread every 3 seconds.
|
||||
// note: registered loggers *must* be thread safe for this to work correctly!
|
||||
@@ -107,7 +119,7 @@ void stdout_logger_example()
|
||||
void basic_example()
|
||||
{
|
||||
// Create basic file logger (not rotated).
|
||||
auto my_logger = spdlog::basic_logger_mt("file_logger", "logs/basic-log.txt");
|
||||
auto my_logger = spdlog::basic_logger_mt("file_logger", "logs/basic-log.txt", true);
|
||||
}
|
||||
|
||||
#include "spdlog/sinks/rotating_file_sink.h"
|
||||
@@ -127,7 +139,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:
|
||||
@@ -178,6 +190,21 @@ void binary_example()
|
||||
// logger->info("hexdump style, 20 chars per line {:a}", spdlog::to_hex(buf, 20));
|
||||
}
|
||||
|
||||
// Log a vector of numbers
|
||||
#ifndef SPDLOG_USE_STD_FORMAT
|
||||
# include "spdlog/fmt/ranges.h"
|
||||
void vector_example()
|
||||
{
|
||||
std::vector<int> vec = {1, 2, 3};
|
||||
spdlog::info("Vector example: {}", vec);
|
||||
}
|
||||
|
||||
#else
|
||||
void vector_example() {}
|
||||
#endif
|
||||
|
||||
// ! DSPDLOG_USE_STD_FORMAT
|
||||
|
||||
// Compile time log levels.
|
||||
// define SPDLOG_ACTIVE_LEVEL to required level (e.g. SPDLOG_LEVEL_TRACE)
|
||||
void trace_example()
|
||||
@@ -192,6 +219,25 @@ 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);
|
||||
}
|
||||
|
||||
#include "spdlog/sinks/udp_sink.h"
|
||||
void udp_example()
|
||||
{
|
||||
spdlog::sinks::udp_sink_config cfg("127.0.0.1", 11091);
|
||||
auto my_logger = spdlog::udp_logger_mt("udplog", cfg);
|
||||
my_logger->set_level(spdlog::level::debug);
|
||||
my_logger->info("hello world");
|
||||
}
|
||||
|
||||
// A logger with multiple sinks (stdout and file) - each with a different format and log level.
|
||||
void multi_sink_example()
|
||||
{
|
||||
@@ -208,21 +254,27 @@ void multi_sink_example()
|
||||
logger.info("this message should not appear in the console, only in the file");
|
||||
}
|
||||
|
||||
// User defined types logging by implementing operator<<
|
||||
#include "spdlog/fmt/ostr.h" // must be included
|
||||
// User defined types logging
|
||||
struct my_type
|
||||
{
|
||||
int i;
|
||||
template<typename OStream>
|
||||
friend OStream &operator<<(OStream &os, const my_type &c)
|
||||
int i = 0;
|
||||
explicit my_type(int i)
|
||||
: i(i){};
|
||||
};
|
||||
|
||||
namespace fmt_lib = spdlog::fmt_lib;
|
||||
template<>
|
||||
struct fmt_lib::formatter<my_type> : fmt_lib::formatter<std::string>
|
||||
{
|
||||
auto format(my_type my, format_context &ctx) -> decltype(ctx.out())
|
||||
{
|
||||
return os << "[my_type i=" << c.i << "]";
|
||||
return fmt_lib::format_to(ctx.out(), "[my_type i={}]", my.i);
|
||||
}
|
||||
};
|
||||
|
||||
void user_defined_example()
|
||||
{
|
||||
spdlog::info("user defined type: {}", my_type{14});
|
||||
spdlog::info("user defined type: {}", my_type(14));
|
||||
}
|
||||
|
||||
// Custom error handler. Will be triggered on log failure.
|
||||
@@ -234,7 +286,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 +297,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";
|
||||
@@ -278,5 +330,40 @@ void custom_flags_example()
|
||||
using spdlog::details::make_unique; // for pre c++14
|
||||
auto formatter = make_unique<spdlog::pattern_formatter>();
|
||||
formatter->add_flag<my_formatter_flag>('*').set_pattern("[%n] [%*] [%^%l%$] %v");
|
||||
spdlog::set_formatter(std::move(formatter));
|
||||
// set the new formatter using spdlog::set_formatter(formatter) or logger->set_formatter(formatter)
|
||||
// spdlog::set_formatter(std::move(formatter));
|
||||
}
|
||||
|
||||
void file_events_example()
|
||||
{
|
||||
// pass the spdlog::file_event_handlers to file sinks for open/close log file notifications
|
||||
spdlog::file_event_handlers handlers;
|
||||
handlers.before_open = [](spdlog::filename_t filename) { spdlog::info("Before opening {}", filename); };
|
||||
handlers.after_open = [](spdlog::filename_t filename, std::FILE *fstream) {
|
||||
spdlog::info("After opening {}", filename);
|
||||
fputs("After opening\n", fstream);
|
||||
};
|
||||
handlers.before_close = [](spdlog::filename_t filename, std::FILE *fstream) {
|
||||
spdlog::info("Before closing {}", filename);
|
||||
fputs("Before closing\n", fstream);
|
||||
};
|
||||
handlers.after_close = [](spdlog::filename_t filename) { spdlog::info("After closing {}", filename); };
|
||||
auto file_sink = std::make_shared<spdlog::sinks::basic_file_sink_mt>("logs/events-sample.txt", true, handlers);
|
||||
spdlog::logger my_logger("some_logger", file_sink);
|
||||
my_logger.info("Some log line");
|
||||
}
|
||||
|
||||
void replace_default_logger_example()
|
||||
{
|
||||
// store the old logger so we don't break other examples.
|
||||
auto old_logger = spdlog::default_logger();
|
||||
|
||||
auto new_logger = spdlog::basic_logger_mt("new_default_logger", "logs/new-default-log.txt", true);
|
||||
spdlog::set_default_logger(new_logger);
|
||||
spdlog::set_level(spdlog::level::info);
|
||||
spdlog::debug("This message should not be displayed!");
|
||||
spdlog::set_level(spdlog::level::trace);
|
||||
spdlog::debug("This message should be displayed..");
|
||||
|
||||
spdlog::set_default_logger(old_logger);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -73,16 +73,22 @@ inline std::shared_ptr<spdlog::logger> create_async_nb(std::string logger_name,
|
||||
}
|
||||
|
||||
// set global thread pool.
|
||||
inline void init_thread_pool(size_t q_size, size_t thread_count, std::function<void()> on_thread_start)
|
||||
inline void init_thread_pool(
|
||||
size_t q_size, size_t thread_count, std::function<void()> on_thread_start, std::function<void()> on_thread_stop)
|
||||
{
|
||||
auto tp = std::make_shared<details::thread_pool>(q_size, thread_count, on_thread_start);
|
||||
auto tp = std::make_shared<details::thread_pool>(q_size, thread_count, on_thread_start, on_thread_stop);
|
||||
details::registry::instance().set_tp(std::move(tp));
|
||||
}
|
||||
|
||||
// set global thread pool.
|
||||
inline void init_thread_pool(size_t q_size, size_t thread_count, std::function<void()> on_thread_start)
|
||||
{
|
||||
init_thread_pool(q_size, thread_count, on_thread_start, [] {});
|
||||
}
|
||||
|
||||
inline void init_thread_pool(size_t q_size, size_t thread_count)
|
||||
{
|
||||
init_thread_pool(q_size, thread_count, [] {});
|
||||
init_thread_pool(
|
||||
q_size, thread_count, [] {}, [] {});
|
||||
}
|
||||
|
||||
// get the global thread pool.
|
||||
|
||||
@@ -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>
|
||||
@@ -62,7 +62,7 @@ SPDLOG_INLINE void spdlog::async_logger::backend_sink_it_(const details::log_msg
|
||||
{
|
||||
sink->log(msg);
|
||||
}
|
||||
SPDLOG_LOGGER_CATCH()
|
||||
SPDLOG_LOGGER_CATCH(msg.source)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -80,7 +80,7 @@ SPDLOG_INLINE void spdlog::async_logger::backend_flush_()
|
||||
{
|
||||
sink->flush();
|
||||
}
|
||||
SPDLOG_LOGGER_CATCH()
|
||||
SPDLOG_LOGGER_CATCH(source_loc())
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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")
|
||||
{
|
||||
@@ -53,9 +55,13 @@ SPDLOG_INLINE spdlog_ex::spdlog_ex(std::string msg)
|
||||
|
||||
SPDLOG_INLINE spdlog_ex::spdlog_ex(const std::string &msg, int last_errno)
|
||||
{
|
||||
#ifdef SPDLOG_USE_STD_FORMAT
|
||||
msg_ = std::system_error(std::error_code(last_errno, std::generic_category()), msg).what();
|
||||
#else
|
||||
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);
|
||||
#endif
|
||||
}
|
||||
|
||||
SPDLOG_INLINE const char *spdlog_ex::what() const SPDLOG_NOEXCEPT
|
||||
|
||||
@@ -14,68 +14,95 @@
|
||||
#include <string>
|
||||
#include <type_traits>
|
||||
#include <functional>
|
||||
#include <cstdio>
|
||||
|
||||
#ifdef SPDLOG_USE_STD_FORMAT
|
||||
# include <string_view>
|
||||
#endif
|
||||
|
||||
#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(SPDLOG_SHARED_LIB)
|
||||
# if defined(_WIN32)
|
||||
# ifdef spdlog_EXPORTS
|
||||
# define SPDLOG_API __declspec(dllexport)
|
||||
# else // !spdlog_EXPORTS
|
||||
# define SPDLOG_API __declspec(dllimport)
|
||||
# endif
|
||||
# else // !defined(_WIN32)
|
||||
# define SPDLOG_API __attribute__((visibility("default")))
|
||||
# endif
|
||||
# else // !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>
|
||||
|
||||
// visual studio upto 2013 does not support noexcept nor constexpr
|
||||
#ifndef SPDLOG_USE_STD_FORMAT
|
||||
# if FMT_VERSION >= 80000 // backward compatibility with fmt versions older than 8
|
||||
# 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
|
||||
#endif
|
||||
|
||||
// visual studio up to 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
|
||||
# define SPDLOG_CONSTEXPR_FUNC
|
||||
#else
|
||||
#define SPDLOG_NOEXCEPT noexcept
|
||||
#define SPDLOG_CONSTEXPR constexpr
|
||||
# define SPDLOG_NOEXCEPT noexcept
|
||||
# define SPDLOG_CONSTEXPR constexpr
|
||||
# if __cplusplus >= 201402L
|
||||
# define SPDLOG_CONSTEXPR_FUNC constexpr
|
||||
# else
|
||||
# define SPDLOG_CONSTEXPR_FUNC
|
||||
# endif
|
||||
#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,33 +115,78 @@ 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;
|
||||
using sink_ptr = std::shared_ptr<sinks::sink>;
|
||||
using sinks_init_list = std::initializer_list<sink_ptr>;
|
||||
using err_handler = std::function<void(const std::string &err_msg)>;
|
||||
#ifdef SPDLOG_USE_STD_FORMAT
|
||||
namespace fmt_lib = std;
|
||||
|
||||
using string_view_t = std::string_view;
|
||||
using memory_buf_t = std::string;
|
||||
|
||||
template<typename... Args>
|
||||
using format_string_t = std::string_view;
|
||||
|
||||
template<class T, class Char = char>
|
||||
struct is_convertible_to_basic_format_string : std::integral_constant<bool, std::is_convertible<T, std::basic_string_view<Char>>::value>
|
||||
{};
|
||||
|
||||
# if defined(SPDLOG_WCHAR_FILENAMES) || defined(SPDLOG_WCHAR_TO_UTF8_SUPPORT)
|
||||
using wstring_view_t = std::wstring_view;
|
||||
using wmemory_buf_t = std::wstring;
|
||||
|
||||
template<typename... Args>
|
||||
using wformat_string_t = std::wstring_view;
|
||||
# endif
|
||||
|
||||
#else // use fmt lib instead of std::format
|
||||
namespace fmt_lib = fmt;
|
||||
|
||||
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>;
|
||||
|
||||
template<typename... Args>
|
||||
using format_string_t = fmt::format_string<Args...>;
|
||||
|
||||
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>
|
||||
{};
|
||||
|
||||
# if defined(SPDLOG_WCHAR_FILENAMES) || defined(SPDLOG_WCHAR_TO_UTF8_SUPPORT)
|
||||
using wstring_view_t = fmt::basic_string_view<wchar_t>;
|
||||
using wmemory_buf_t = fmt::basic_memory_buffer<wchar_t, 250>;
|
||||
|
||||
template<typename... Args>
|
||||
using wformat_string_t = fmt::wformat_string<Args...>;
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#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,12 +203,12 @@ 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
|
||||
namespace level {
|
||||
enum level_enum
|
||||
enum level_enum : int
|
||||
{
|
||||
trace = SPDLOG_LEVEL_TRACE,
|
||||
debug = SPDLOG_LEVEL_DEBUG,
|
||||
@@ -148,26 +220,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 +284,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
|
||||
{
|
||||
@@ -225,12 +305,31 @@ struct source_loc
|
||||
const char *funcname{nullptr};
|
||||
};
|
||||
|
||||
struct file_event_handlers
|
||||
{
|
||||
std::function<void(const filename_t &filename)> before_open;
|
||||
std::function<void(const filename_t &filename, std::FILE *file_stream)> after_open;
|
||||
std::function<void(const filename_t &filename, std::FILE *file_stream)> before_close;
|
||||
std::function<void(const filename_t &filename)> after_close;
|
||||
file_event_handlers()
|
||||
: before_open{nullptr}
|
||||
, after_open{nullptr}
|
||||
, before_close{nullptr}
|
||||
, after_close{nullptr}
|
||||
{}
|
||||
};
|
||||
|
||||
namespace details {
|
||||
|
||||
// make_unique support for pre c++14
|
||||
|
||||
#if __cplusplus >= 201402L // C++14 and beyond
|
||||
using std::enable_if_t;
|
||||
using std::make_unique;
|
||||
#else
|
||||
template<bool B, class T = void>
|
||||
using enable_if_t = typename std::enable_if<B, T>::type;
|
||||
|
||||
template<typename T, typename... Args>
|
||||
std::unique_ptr<T> make_unique(Args &&... args)
|
||||
{
|
||||
@@ -238,9 +337,23 @@ std::unique_ptr<T> make_unique(Args &&... args)
|
||||
return std::unique_ptr<T>(new T(std::forward<Args>(args)...));
|
||||
}
|
||||
#endif
|
||||
|
||||
// to avoid useless casts (see https://github.com/nlohmann/json/issues/2893#issuecomment-889152324)
|
||||
template<typename T, typename U, enable_if_t<!std::is_same<T, U>::value, int> = 0>
|
||||
constexpr T conditional_static_cast(U value)
|
||||
{
|
||||
return static_cast<T>(value);
|
||||
}
|
||||
|
||||
template<typename T, typename U, enable_if_t<std::is_same<T, U>::value, int> = 0>
|
||||
constexpr T conditional_static_cast(U value)
|
||||
{
|
||||
return value;
|
||||
}
|
||||
|
||||
} // namespace details
|
||||
} // 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>
|
||||
@@ -20,6 +20,10 @@
|
||||
namespace spdlog {
|
||||
namespace details {
|
||||
|
||||
SPDLOG_INLINE file_helper::file_helper(const file_event_handlers &event_handlers)
|
||||
: event_handlers_(event_handlers)
|
||||
{}
|
||||
|
||||
SPDLOG_INLINE file_helper::~file_helper()
|
||||
{
|
||||
close();
|
||||
@@ -29,14 +33,37 @@ 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");
|
||||
|
||||
if (event_handlers_.before_open)
|
||||
{
|
||||
event_handlers_.before_open(filename_);
|
||||
}
|
||||
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))
|
||||
{
|
||||
if (event_handlers_.after_open)
|
||||
{
|
||||
event_handlers_.after_open(filename_, fd_);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -57,15 +84,28 @@ SPDLOG_INLINE void file_helper::reopen(bool truncate)
|
||||
|
||||
SPDLOG_INLINE void file_helper::flush()
|
||||
{
|
||||
std::fflush(fd_);
|
||||
if (std::fflush(fd_) != 0)
|
||||
{
|
||||
throw_spdlog_ex("Failed flush to file " + os::filename_to_str(filename_), errno);
|
||||
}
|
||||
}
|
||||
|
||||
SPDLOG_INLINE void file_helper::close()
|
||||
{
|
||||
if (fd_ != nullptr)
|
||||
{
|
||||
if (event_handlers_.before_close)
|
||||
{
|
||||
event_handlers_.before_close(filename_, fd_);
|
||||
}
|
||||
|
||||
std::fclose(fd_);
|
||||
fd_ = nullptr;
|
||||
|
||||
if (event_handlers_.after_close)
|
||||
{
|
||||
event_handlers_.after_close(filename_);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -118,7 +158,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());
|
||||
|
||||
@@ -16,7 +16,8 @@ namespace details {
|
||||
class SPDLOG_API file_helper
|
||||
{
|
||||
public:
|
||||
explicit file_helper() = default;
|
||||
file_helper() = default;
|
||||
explicit file_helper(const file_event_handlers &event_handlers);
|
||||
|
||||
file_helper(const file_helper &) = delete;
|
||||
file_helper &operator=(const file_helper &) = delete;
|
||||
@@ -47,13 +48,14 @@ 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_;
|
||||
file_event_handlers event_handlers_;
|
||||
};
|
||||
} // namespace details
|
||||
} // namespace spdlog
|
||||
|
||||
#ifdef SPDLOG_HEADER_ONLY
|
||||
#include "file_helper-inl.h"
|
||||
# include "file_helper-inl.h"
|
||||
#endif
|
||||
|
||||
@@ -4,9 +4,15 @@
|
||||
|
||||
#include <chrono>
|
||||
#include <type_traits>
|
||||
#include <iterator>
|
||||
#include <spdlog/fmt/fmt.h>
|
||||
#include <spdlog/common.h>
|
||||
|
||||
#ifdef SPDLOG_USE_STD_FORMAT
|
||||
# include <charconv>
|
||||
# include <limits>
|
||||
#endif
|
||||
|
||||
// Some fmt helpers to efficiently format and pad ints and strings
|
||||
namespace spdlog {
|
||||
namespace details {
|
||||
@@ -23,18 +29,73 @@ inline void append_string_view(spdlog::string_view_t view, memory_buf_t &dest)
|
||||
dest.append(buf_ptr, buf_ptr + view.size());
|
||||
}
|
||||
|
||||
#ifdef SPDLOG_USE_STD_FORMAT
|
||||
template<typename T>
|
||||
inline void append_int(T n, memory_buf_t &dest)
|
||||
{
|
||||
// Buffer should be large enough to hold all digits (digits10 + 1) and a sign
|
||||
SPDLOG_CONSTEXPR const auto BUF_SIZE = std::numeric_limits<T>::digits10 + 2;
|
||||
char buf[BUF_SIZE];
|
||||
|
||||
auto [ptr, ec] = std::to_chars(buf, buf + BUF_SIZE, n, 10);
|
||||
if (ec == std::errc())
|
||||
{
|
||||
dest.append(buf, ptr);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw_spdlog_ex("Failed to format int", static_cast<int>(ec));
|
||||
}
|
||||
}
|
||||
#else
|
||||
template<typename T>
|
||||
inline void append_int(T n, memory_buf_t &dest)
|
||||
{
|
||||
fmt::format_int i(n);
|
||||
dest.append(i.data(), i.data() + i.size());
|
||||
}
|
||||
#endif
|
||||
|
||||
template<typename T>
|
||||
SPDLOG_CONSTEXPR_FUNC unsigned int count_digits_fallback(T n)
|
||||
{
|
||||
// taken from fmt: https://github.com/fmtlib/fmt/blob/8.0.1/include/fmt/format.h#L899-L912
|
||||
unsigned int count = 1;
|
||||
for (;;)
|
||||
{
|
||||
// Integer division is slow so do it for a group of four digits instead
|
||||
// of for every digit. The idea comes from the talk by Alexandrescu
|
||||
// "Three Optimization Tips for C++". See speed-test for a comparison.
|
||||
if (n < 10)
|
||||
return count;
|
||||
if (n < 100)
|
||||
return count + 1;
|
||||
if (n < 1000)
|
||||
return count + 2;
|
||||
if (n < 10000)
|
||||
return count + 3;
|
||||
n /= 10000u;
|
||||
count += 4;
|
||||
}
|
||||
}
|
||||
|
||||
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)));
|
||||
#ifdef SPDLOG_USE_STD_FORMAT
|
||||
return count_digits_fallback(static_cast<count_type>(n));
|
||||
#else
|
||||
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)));
|
||||
#endif
|
||||
}
|
||||
|
||||
inline void pad2(int n, memory_buf_t &dest)
|
||||
@@ -46,7 +107,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_lib::format_to(std::back_inserter(dest), "{: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
|
||||
|
||||
@@ -49,7 +49,7 @@ public:
|
||||
push_cv_.notify_one();
|
||||
}
|
||||
|
||||
// try to dequeue item. if no item found. wait upto timeout and try again
|
||||
// try to dequeue item. if no item found. wait up to timeout and try again
|
||||
// Return true, if succeeded dequeue item, false otherwise
|
||||
bool dequeue_for(T &popped_item, std::chrono::milliseconds wait_duration)
|
||||
{
|
||||
@@ -87,7 +87,7 @@ public:
|
||||
push_cv_.notify_one();
|
||||
}
|
||||
|
||||
// try to dequeue item. if no item found. wait upto timeout and try again
|
||||
// try to dequeue item. if no item found. wait up to timeout and try again
|
||||
// Return true, if succeeded dequeue item, false otherwise
|
||||
bool dequeue_for(T &popped_item, std::chrono::milliseconds wait_duration)
|
||||
{
|
||||
@@ -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_getthrds_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,23 +138,23 @@ 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)
|
||||
{
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
*fp = ::fdopen(fd, mode.c_str());
|
||||
if (*fp == nullptr)
|
||||
{
|
||||
::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__)
|
||||
// OpenBSD and AIX doesn't compile with :: before the fileno(..)
|
||||
# if defined(__OpenBSD__) || defined(_AIX)
|
||||
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
|
||||
{
|
||||
@@ -295,7 +305,7 @@ SPDLOG_INLINE int utc_minutes_offset(const std::tm &tm)
|
||||
((local_year / 100 >> 2) - (gmt_year / 100 >> 2))
|
||||
|
||||
// + difference in years * 365 */
|
||||
+ (long int)(local_year - gmt_year) * 365);
|
||||
+ static_cast<long int>(local_year - gmt_year) * 365);
|
||||
|
||||
long int hours = (24 * days) + (localtm.tm_hour - gmtm.tm_hour);
|
||||
long int mins = (60 * hours) + (localtm.tm_min - gmtm.tm_min);
|
||||
@@ -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,11 +332,18 @@ 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__)
|
||||
#elif defined(_AIX)
|
||||
struct __pthrdsinfo buf;
|
||||
int reg_size = 0;
|
||||
pthread_t pt = pthread_self();
|
||||
int retval = pthread_getthrds_np(&pt, PTHRDSINFO_QUERY_TID, &buf, sizeof(buf), NULL, ®_size);
|
||||
int tid = (!retval) ? buf.__pi_tid : 0;
|
||||
return static_cast<size_t>(tid);
|
||||
#elif defined(__DragonFly__) || defined(__FreeBSD__)
|
||||
return static_cast<size_t>(::pthread_getthreadid_np());
|
||||
#elif defined(__NetBSD__)
|
||||
return static_cast<size_t>(::_lwp_self());
|
||||
@@ -356,7 +373,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);
|
||||
@@ -371,7 +388,11 @@ SPDLOG_INLINE std::string filename_to_str(const filename_t &filename)
|
||||
{
|
||||
memory_buf_t buf;
|
||||
wstr_to_utf8buf(filename, buf);
|
||||
# ifdef SPDLOG_USE_STD_FORMAT
|
||||
return buf;
|
||||
# else
|
||||
return fmt::to_string(buf);
|
||||
# endif
|
||||
}
|
||||
#else
|
||||
SPDLOG_INLINE std::string filename_to_str(const filename_t &filename)
|
||||
@@ -384,9 +405,9 @@ SPDLOG_INLINE int pid() SPDLOG_NOEXCEPT
|
||||
{
|
||||
|
||||
#ifdef _WIN32
|
||||
return static_cast<int>(::GetCurrentProcessId());
|
||||
return conditional_static_cast<int>(::GetCurrentProcessId());
|
||||
#else
|
||||
return static_cast<int>(::getpid());
|
||||
return conditional_static_cast<int>(::getpid());
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -397,17 +418,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 +457,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");
|
||||
}
|
||||
@@ -457,7 +487,42 @@ SPDLOG_INLINE void wstr_to_utf8buf(wstring_view_t wstr, memory_buf_t &target)
|
||||
}
|
||||
}
|
||||
|
||||
throw_spdlog_ex(fmt::format("WideCharToMultiByte failed. Last error: {}", ::GetLastError()));
|
||||
throw_spdlog_ex(fmt_lib::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_lib::format("MultiByteToWideChar failed. Last error: {}", ::GetLastError()));
|
||||
}
|
||||
#endif // (defined(SPDLOG_WCHAR_TO_UTF8_SUPPORT) || defined(SPDLOG_WCHAR_FILENAMES)) && defined(_WIN32)
|
||||
|
||||
@@ -465,11 +530,11 @@ SPDLOG_INLINE void wstr_to_utf8buf(wstring_view_t wstr, memory_buf_t &target)
|
||||
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
|
||||
@@ -477,7 +542,7 @@ static SPDLOG_INLINE bool mkdir_(const filename_t &path)
|
||||
|
||||
// create the given directory - and all directories leading to it
|
||||
// return true on success or if the directory already exists
|
||||
SPDLOG_INLINE bool create_dir(filename_t path)
|
||||
SPDLOG_INLINE bool create_dir(const filename_t &path)
|
||||
{
|
||||
if (path_exists(path))
|
||||
{
|
||||
@@ -489,15 +554,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)
|
||||
{
|
||||
@@ -521,13 +581,9 @@ SPDLOG_INLINE bool create_dir(filename_t path)
|
||||
// "abc/" => "abc"
|
||||
// "abc" => ""
|
||||
// "abc///" => "abc//"
|
||||
SPDLOG_INLINE filename_t dir_name(filename_t path)
|
||||
SPDLOG_INLINE filename_t dir_name(const 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 +591,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
|
||||
@@ -92,11 +99,11 @@ SPDLOG_API void wstr_to_utf8buf(wstring_view_t wstr, memory_buf_t &target);
|
||||
// "abc/" => "abc"
|
||||
// "abc" => ""
|
||||
// "abc///" => "abc//"
|
||||
SPDLOG_API filename_t dir_name(filename_t path);
|
||||
SPDLOG_API filename_t dir_name(const filename_t &path);
|
||||
|
||||
// Create a dir from the given path.
|
||||
// Return true if succeeded or if this dir already exists.
|
||||
SPDLOG_API bool create_dir(filename_t path);
|
||||
SPDLOG_API bool create_dir(const filename_t &path);
|
||||
|
||||
// non thread safe, cross platform getenv/getenv_s
|
||||
// return empty string if field not found
|
||||
@@ -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
|
||||
|
||||
@@ -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>
|
||||
@@ -25,20 +25,6 @@ class tcp_client
|
||||
{
|
||||
SOCKET socket_ = INVALID_SOCKET;
|
||||
|
||||
static bool winsock_initialized_()
|
||||
{
|
||||
SOCKET s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
|
||||
if (s == INVALID_SOCKET)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
closesocket(s);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
static void init_winsock_()
|
||||
{
|
||||
WSADATA wsaData;
|
||||
@@ -52,13 +38,24 @@ class tcp_client
|
||||
static void throw_winsock_error_(const std::string &msg, int last_error)
|
||||
{
|
||||
char buf[512];
|
||||
::FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, last_error,
|
||||
::FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, last_error,
|
||||
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), buf, (sizeof(buf) / sizeof(char)), NULL);
|
||||
|
||||
throw_spdlog_ex(fmt::format("tcp_sink - {}: {}", msg, buf));
|
||||
throw_spdlog_ex(fmt_lib::format("tcp_sink - {}: {}", msg, buf));
|
||||
}
|
||||
|
||||
public:
|
||||
tcp_client()
|
||||
{
|
||||
init_winsock_();
|
||||
}
|
||||
|
||||
~tcp_client()
|
||||
{
|
||||
close();
|
||||
::WSACleanup();
|
||||
}
|
||||
|
||||
bool is_connected() const
|
||||
{
|
||||
return socket_ != INVALID_SOCKET;
|
||||
@@ -68,7 +65,6 @@ public:
|
||||
{
|
||||
::closesocket(socket_);
|
||||
socket_ = INVALID_SOCKET;
|
||||
WSACleanup();
|
||||
}
|
||||
|
||||
SOCKET fd() const
|
||||
@@ -76,20 +72,9 @@ public:
|
||||
return socket_;
|
||||
}
|
||||
|
||||
~tcp_client()
|
||||
{
|
||||
close();
|
||||
}
|
||||
|
||||
// try to connect or throw on failure
|
||||
void connect(const std::string &host, int port)
|
||||
{
|
||||
// initialize winsock if needed
|
||||
if (!winsock_initialized_())
|
||||
{
|
||||
init_winsock_();
|
||||
}
|
||||
|
||||
if (is_connected())
|
||||
{
|
||||
close();
|
||||
@@ -144,7 +129,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
|
||||
@@ -67,15 +67,18 @@ public:
|
||||
auto rv = ::getaddrinfo(host.c_str(), port_str.c_str(), &hints, &addrinfo_result);
|
||||
if (rv != 0)
|
||||
{
|
||||
auto msg = fmt::format("::getaddrinfo failed: {}", gai_strerror(rv));
|
||||
throw_spdlog_ex(msg);
|
||||
throw_spdlog_ex(fmt_lib::format("::getaddrinfo failed: {}", gai_strerror(rv)));
|
||||
}
|
||||
|
||||
// Try each address until we successfully connect(2).
|
||||
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 +90,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 +102,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>
|
||||
@@ -13,7 +13,8 @@
|
||||
namespace spdlog {
|
||||
namespace details {
|
||||
|
||||
SPDLOG_INLINE thread_pool::thread_pool(size_t q_max_items, size_t threads_n, std::function<void()> on_thread_start)
|
||||
SPDLOG_INLINE thread_pool::thread_pool(
|
||||
size_t q_max_items, size_t threads_n, std::function<void()> on_thread_start, std::function<void()> on_thread_stop)
|
||||
: q_(q_max_items)
|
||||
{
|
||||
if (threads_n == 0 || threads_n > 1000)
|
||||
@@ -23,15 +24,21 @@ SPDLOG_INLINE thread_pool::thread_pool(size_t q_max_items, size_t threads_n, std
|
||||
}
|
||||
for (size_t i = 0; i < threads_n; i++)
|
||||
{
|
||||
threads_.emplace_back([this, on_thread_start] {
|
||||
threads_.emplace_back([this, on_thread_start, on_thread_stop] {
|
||||
on_thread_start();
|
||||
this->thread_pool::worker_loop_();
|
||||
on_thread_stop();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
SPDLOG_INLINE thread_pool::thread_pool(size_t q_max_items, size_t threads_n, std::function<void()> on_thread_start)
|
||||
: thread_pool(q_max_items, threads_n, on_thread_start, [] {})
|
||||
{}
|
||||
|
||||
SPDLOG_INLINE thread_pool::thread_pool(size_t q_max_items, size_t threads_n)
|
||||
: thread_pool(q_max_items, threads_n, [] {})
|
||||
: thread_pool(
|
||||
q_max_items, threads_n, [] {}, [] {})
|
||||
{}
|
||||
|
||||
// message all threads to terminate gracefully join them
|
||||
@@ -49,7 +56,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 +75,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
|
||||
@@ -85,10 +84,11 @@ public:
|
||||
using item_type = async_msg;
|
||||
using q_type = details::mpmc_blocking_queue<item_type>;
|
||||
|
||||
thread_pool(size_t q_max_items, size_t threads_n, std::function<void()> on_thread_start, std::function<void()> on_thread_stop);
|
||||
thread_pool(size_t q_max_items, size_t threads_n, std::function<void()> on_thread_start);
|
||||
thread_pool(size_t q_max_items, size_t threads_n);
|
||||
|
||||
// message all threads to terminate gracefully join them
|
||||
// message all threads to terminate gracefully and join them
|
||||
~thread_pool();
|
||||
|
||||
thread_pool(const thread_pool &) = delete;
|
||||
@@ -97,6 +97,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 +117,5 @@ private:
|
||||
} // namespace spdlog
|
||||
|
||||
#ifdef SPDLOG_HEADER_ONLY
|
||||
#include "thread_pool-inl.h"
|
||||
# include "thread_pool-inl.h"
|
||||
#endif
|
||||
|
||||
111
include/spdlog/details/udp_client-windows.h
Normal file
111
include/spdlog/details/udp_client-windows.h
Normal file
@@ -0,0 +1,111 @@
|
||||
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
|
||||
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||
|
||||
#pragma once
|
||||
|
||||
// Helper RAII over winsock udp client socket.
|
||||
// Will throw on construction if socket creation failed.
|
||||
|
||||
#include <spdlog/common.h>
|
||||
#include <spdlog/details/os.h>
|
||||
#include <spdlog/details/windows_include.h>
|
||||
#include <winsock2.h>
|
||||
#include <ws2tcpip.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string>
|
||||
|
||||
#pragma comment(lib, "Ws2_32.lib")
|
||||
#pragma comment(lib, "Mswsock.lib")
|
||||
#pragma comment(lib, "AdvApi32.lib")
|
||||
|
||||
namespace spdlog {
|
||||
namespace details {
|
||||
class udp_client
|
||||
{
|
||||
static constexpr int TX_BUFFER_SIZE = 1024 * 10;
|
||||
SOCKET socket_ = INVALID_SOCKET;
|
||||
sockaddr_in addr_ = {0};
|
||||
|
||||
static void init_winsock_()
|
||||
{
|
||||
WSADATA wsaData;
|
||||
auto rv = ::WSAStartup(MAKEWORD(2, 2), &wsaData);
|
||||
if (rv != 0)
|
||||
{
|
||||
throw_winsock_error_("WSAStartup failed", ::WSAGetLastError());
|
||||
}
|
||||
}
|
||||
|
||||
static void throw_winsock_error_(const std::string &msg, int last_error)
|
||||
{
|
||||
char buf[512];
|
||||
::FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, last_error,
|
||||
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), buf, (sizeof(buf) / sizeof(char)), NULL);
|
||||
|
||||
throw_spdlog_ex(fmt_lib::format("udp_sink - {}: {}", msg, buf));
|
||||
}
|
||||
|
||||
void cleanup_()
|
||||
{
|
||||
if (socket_ != INVALID_SOCKET)
|
||||
{
|
||||
::closesocket(socket_);
|
||||
}
|
||||
socket_ = INVALID_SOCKET;
|
||||
::WSACleanup();
|
||||
}
|
||||
|
||||
public:
|
||||
udp_client(const std::string &host, uint16_t port)
|
||||
{
|
||||
init_winsock_();
|
||||
|
||||
addr_.sin_family = PF_INET;
|
||||
addr_.sin_port = htons(port);
|
||||
addr_.sin_addr.s_addr = INADDR_ANY;
|
||||
if (InetPtonA(PF_INET, host.c_str(), &addr_.sin_addr.s_addr) != 1)
|
||||
{
|
||||
int last_error = ::WSAGetLastError();
|
||||
::WSACleanup();
|
||||
throw_winsock_error_("error: Invalid address!", last_error);
|
||||
}
|
||||
|
||||
socket_ = ::socket(PF_INET, SOCK_DGRAM, 0);
|
||||
if (socket_ == INVALID_SOCKET)
|
||||
{
|
||||
int last_error = ::WSAGetLastError();
|
||||
::WSACleanup();
|
||||
throw_winsock_error_("error: Create Socket failed", last_error);
|
||||
}
|
||||
|
||||
int option_value = TX_BUFFER_SIZE;
|
||||
if (::setsockopt(socket_, SOL_SOCKET, SO_SNDBUF, reinterpret_cast<const char *>(&option_value), sizeof(option_value)) < 0)
|
||||
{
|
||||
int last_error = ::WSAGetLastError();
|
||||
cleanup_();
|
||||
throw_winsock_error_("error: setsockopt(SO_SNDBUF) Failed!", last_error);
|
||||
}
|
||||
}
|
||||
|
||||
~udp_client()
|
||||
{
|
||||
cleanup_();
|
||||
}
|
||||
|
||||
SOCKET fd() const
|
||||
{
|
||||
return socket_;
|
||||
}
|
||||
|
||||
void send(const char *data, size_t n_bytes)
|
||||
{
|
||||
socklen_t tolen = sizeof(struct sockaddr);
|
||||
if (::sendto(socket_, data, static_cast<int>(n_bytes), 0, (struct sockaddr *)&addr_, tolen) == -1)
|
||||
{
|
||||
throw_spdlog_ex("sendto(2) failed", errno);
|
||||
}
|
||||
}
|
||||
};
|
||||
} // namespace details
|
||||
} // namespace spdlog
|
||||
94
include/spdlog/details/udp_client.h
Normal file
94
include/spdlog/details/udp_client.h
Normal file
@@ -0,0 +1,94 @@
|
||||
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
|
||||
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||
|
||||
#pragma once
|
||||
|
||||
// Helper RAII over unix udp client socket.
|
||||
// Will throw on construction if the socket creation failed.
|
||||
|
||||
#ifdef _WIN32
|
||||
# error "include udp_client-windows.h instead"
|
||||
#endif
|
||||
|
||||
#include <spdlog/common.h>
|
||||
#include <spdlog/details/os.h>
|
||||
|
||||
#include <sys/socket.h>
|
||||
#include <netinet/in.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <unistd.h>
|
||||
#include <netdb.h>
|
||||
#include <netinet/udp.h>
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace spdlog {
|
||||
namespace details {
|
||||
|
||||
class udp_client
|
||||
{
|
||||
static constexpr int TX_BUFFER_SIZE = 1024 * 10;
|
||||
int socket_ = -1;
|
||||
struct sockaddr_in sockAddr_;
|
||||
|
||||
void cleanup_()
|
||||
{
|
||||
if (socket_ != -1)
|
||||
{
|
||||
::close(socket_);
|
||||
socket_ = -1;
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
udp_client(const std::string &host, uint16_t port)
|
||||
{
|
||||
socket_ = ::socket(PF_INET, SOCK_DGRAM, 0);
|
||||
if (socket_ < 0)
|
||||
{
|
||||
throw_spdlog_ex("error: Create Socket Failed!");
|
||||
}
|
||||
|
||||
int option_value = TX_BUFFER_SIZE;
|
||||
if (::setsockopt(socket_, SOL_SOCKET, SO_SNDBUF, reinterpret_cast<const char *>(&option_value), sizeof(option_value)) < 0)
|
||||
{
|
||||
cleanup_();
|
||||
throw_spdlog_ex("error: setsockopt(SO_SNDBUF) Failed!");
|
||||
}
|
||||
|
||||
sockAddr_.sin_family = AF_INET;
|
||||
sockAddr_.sin_port = htons(port);
|
||||
|
||||
if (::inet_aton(host.c_str(), &sockAddr_.sin_addr) == 0)
|
||||
{
|
||||
cleanup_();
|
||||
throw_spdlog_ex("error: Invalid address!");
|
||||
}
|
||||
|
||||
::memset(sockAddr_.sin_zero, 0x00, sizeof(sockAddr_.sin_zero));
|
||||
}
|
||||
|
||||
~udp_client()
|
||||
{
|
||||
cleanup_();
|
||||
}
|
||||
|
||||
int fd() const
|
||||
{
|
||||
return socket_;
|
||||
}
|
||||
|
||||
// Send exactly n_bytes of the given data.
|
||||
// On error close the connection and throw.
|
||||
void send(const char *data, size_t n_bytes)
|
||||
{
|
||||
ssize_t toslen = 0;
|
||||
socklen_t tolen = sizeof(struct sockaddr);
|
||||
if ((toslen = ::sendto(socket_, data, n_bytes, 0, (struct sockaddr *)&sockAddr_, tolen)) == -1)
|
||||
{
|
||||
throw_spdlog_ex("sendto(2) failed", errno);
|
||||
}
|
||||
}
|
||||
};
|
||||
} // namespace details
|
||||
} // namespace spdlog
|
||||
@@ -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,19 @@
|
||||
#pragma once
|
||||
|
||||
#include <cctype>
|
||||
#include <spdlog/common.h>
|
||||
|
||||
#if defined(__has_include) && __has_include(<version>)
|
||||
# include <version>
|
||||
#endif
|
||||
|
||||
#if __cpp_lib_span >= 202002L
|
||||
# include <span>
|
||||
#endif
|
||||
|
||||
//
|
||||
// Support for logging binary data as hex
|
||||
// format flags:
|
||||
// format flags, any combination of the following:
|
||||
// {:X} - print in uppercase.
|
||||
// {:s} - don't separate each byte with space.
|
||||
// {:p} - don't print the position on each line start.
|
||||
@@ -38,11 +47,12 @@ public:
|
||||
, size_per_line_(size_per_line)
|
||||
{}
|
||||
|
||||
It begin() const
|
||||
// do not use begin() and end() to avoid collision with fmt/ranges
|
||||
It get_begin() const
|
||||
{
|
||||
return begin_;
|
||||
}
|
||||
It end() const
|
||||
It get_end() const
|
||||
{
|
||||
return end_;
|
||||
}
|
||||
@@ -66,6 +76,20 @@ inline details::dump_info<typename Container::const_iterator> to_hex(const Conta
|
||||
return details::dump_info<Iter>(std::begin(container), std::end(container), size_per_line);
|
||||
}
|
||||
|
||||
#if __cpp_lib_span >= 202002L
|
||||
|
||||
template<typename Value, size_t Extent>
|
||||
inline details::dump_info<typename std::span<Value, Extent>::iterator> to_hex(
|
||||
const std::span<Value, Extent> &container, size_t size_per_line = 32)
|
||||
{
|
||||
using Container = std::span<Value, Extent>;
|
||||
static_assert(sizeof(typename Container::value_type) == 1, "sizeof(Container::value_type) != 1");
|
||||
using Iter = typename Container::iterator;
|
||||
return details::dump_info<Iter>(std::begin(container), std::end(container), size_per_line);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
// create dump_info from ranges
|
||||
template<typename It>
|
||||
inline details::dump_info<It> to_hex(const It range_begin, const It range_end, size_t size_per_line = 32)
|
||||
@@ -75,10 +99,16 @@ inline details::dump_info<It> to_hex(const It range_begin, const It range_end, s
|
||||
|
||||
} // namespace spdlog
|
||||
|
||||
namespace fmt {
|
||||
namespace
|
||||
#ifdef SPDLOG_USE_STD_FORMAT
|
||||
std
|
||||
#else
|
||||
fmt
|
||||
#endif
|
||||
{
|
||||
|
||||
template<typename T>
|
||||
struct formatter<spdlog::details::dump_info<T>>
|
||||
struct formatter<spdlog::details::dump_info<T>, char>
|
||||
{
|
||||
const char delimiter = ' ';
|
||||
bool put_newlines = true;
|
||||
@@ -89,10 +119,10 @@ struct formatter<spdlog::details::dump_info<T>>
|
||||
|
||||
// parse the format string flags
|
||||
template<typename ParseContext>
|
||||
auto parse(ParseContext &ctx) -> decltype(ctx.begin())
|
||||
SPDLOG_CONSTEXPR_FUNC auto parse(ParseContext &ctx) -> decltype(ctx.begin())
|
||||
{
|
||||
auto it = ctx.begin();
|
||||
while (*it && *it != '}')
|
||||
while (it != ctx.end() && *it != '}')
|
||||
{
|
||||
switch (*it)
|
||||
{
|
||||
@@ -130,21 +160,21 @@ struct formatter<spdlog::details::dump_info<T>>
|
||||
SPDLOG_CONSTEXPR const char *hex_lower = "0123456789abcdef";
|
||||
const char *hex_chars = use_uppercase ? hex_upper : hex_lower;
|
||||
|
||||
#if FMT_VERSION < 60000
|
||||
#if !defined(SPDLOG_USE_STD_FORMAT) && FMT_VERSION < 60000
|
||||
auto inserter = ctx.begin();
|
||||
#else
|
||||
auto inserter = ctx.out();
|
||||
#endif
|
||||
|
||||
int size_per_line = static_cast<int>(the_range.size_per_line());
|
||||
auto start_of_line = the_range.begin();
|
||||
for (auto i = the_range.begin(); i != the_range.end(); i++)
|
||||
auto start_of_line = the_range.get_begin();
|
||||
for (auto i = the_range.get_begin(); i != the_range.get_end(); i++)
|
||||
{
|
||||
auto ch = static_cast<unsigned char>(*i);
|
||||
|
||||
if (put_newlines && (i == the_range.begin() || i - start_of_line >= size_per_line))
|
||||
if (put_newlines && (i == the_range.get_begin() || i - start_of_line >= size_per_line))
|
||||
{
|
||||
if (show_ascii && i != the_range.begin())
|
||||
if (show_ascii && i != the_range.get_begin())
|
||||
{
|
||||
*inserter++ = delimiter;
|
||||
*inserter++ = delimiter;
|
||||
@@ -155,7 +185,7 @@ struct formatter<spdlog::details::dump_info<T>>
|
||||
}
|
||||
}
|
||||
|
||||
put_newline(inserter, static_cast<size_t>(i - the_range.begin()));
|
||||
put_newline(inserter, static_cast<size_t>(i - the_range.get_begin()));
|
||||
|
||||
// put first byte without delimiter in front of it
|
||||
*inserter++ = hex_chars[(ch >> 4) & 0x0f];
|
||||
@@ -174,9 +204,9 @@ struct formatter<spdlog::details::dump_info<T>>
|
||||
}
|
||||
if (show_ascii) // add ascii to last line
|
||||
{
|
||||
if (the_range.end() - the_range.begin() > size_per_line)
|
||||
if (the_range.get_end() - the_range.get_begin() > size_per_line)
|
||||
{
|
||||
auto blank_num = size_per_line - (the_range.end() - start_of_line);
|
||||
auto blank_num = size_per_line - (the_range.get_end() - start_of_line);
|
||||
while (blank_num-- > 0)
|
||||
{
|
||||
*inserter++ = delimiter;
|
||||
@@ -189,7 +219,7 @@ struct formatter<spdlog::details::dump_info<T>>
|
||||
}
|
||||
*inserter++ = delimiter;
|
||||
*inserter++ = delimiter;
|
||||
for (auto j = start_of_line; j != the_range.end(); j++)
|
||||
for (auto j = start_of_line; j != the_range.get_end(); j++)
|
||||
{
|
||||
auto pc = static_cast<unsigned char>(*j);
|
||||
*inserter++ = std::isprint(pc) ? static_cast<char>(*j) : '.';
|
||||
@@ -209,8 +239,8 @@ struct formatter<spdlog::details::dump_info<T>>
|
||||
|
||||
if (put_positions)
|
||||
{
|
||||
fmt::format_to(inserter, "{:<04X}: ", pos);
|
||||
spdlog::fmt_lib::format_to(inserter, "{:04X}: ", pos);
|
||||
}
|
||||
}
|
||||
};
|
||||
} // namespace fmt
|
||||
} // namespace std
|
||||
|
||||
234
include/spdlog/fmt/bundled/args.h
Normal file
234
include/spdlog/fmt/bundled/args.h
Normal file
@@ -0,0 +1,234 @@
|
||||
// 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:
|
||||
constexpr dynamic_format_arg_store() = default;
|
||||
|
||||
/**
|
||||
\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_
|
||||
File diff suppressed because it is too large
Load Diff
@@ -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)
|
||||
@@ -177,9 +185,13 @@ enum class terminal_color : uint8_t {
|
||||
|
||||
enum class emphasis : uint8_t {
|
||||
bold = 1,
|
||||
italic = 1 << 1,
|
||||
underline = 1 << 2,
|
||||
strikethrough = 1 << 3
|
||||
faint = 1 << 1,
|
||||
italic = 1 << 2,
|
||||
underline = 1 << 3,
|
||||
blink = 1 << 4,
|
||||
reverse = 1 << 5,
|
||||
conceal = 1 << 6,
|
||||
strikethrough = 1 << 7,
|
||||
};
|
||||
|
||||
// rgb is a struct for red, green and blue colors.
|
||||
@@ -198,7 +210,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 +233,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 +273,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 +345,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>('[');
|
||||
|
||||
@@ -390,16 +413,18 @@ template <typename Char> struct ansi_color_escape {
|
||||
buffer[19] = static_cast<Char>(0);
|
||||
}
|
||||
FMT_CONSTEXPR ansi_color_escape(emphasis em) FMT_NOEXCEPT {
|
||||
uint8_t em_codes[4] = {};
|
||||
uint8_t em_bits = static_cast<uint8_t>(em);
|
||||
if (em_bits & static_cast<uint8_t>(emphasis::bold)) em_codes[0] = 1;
|
||||
if (em_bits & static_cast<uint8_t>(emphasis::italic)) em_codes[1] = 3;
|
||||
if (em_bits & static_cast<uint8_t>(emphasis::underline)) em_codes[2] = 4;
|
||||
if (em_bits & static_cast<uint8_t>(emphasis::strikethrough))
|
||||
em_codes[3] = 9;
|
||||
uint8_t em_codes[num_emphases] = {};
|
||||
if (has_emphasis(em, emphasis::bold)) em_codes[0] = 1;
|
||||
if (has_emphasis(em, emphasis::faint)) em_codes[1] = 2;
|
||||
if (has_emphasis(em, emphasis::italic)) em_codes[2] = 3;
|
||||
if (has_emphasis(em, emphasis::underline)) em_codes[3] = 4;
|
||||
if (has_emphasis(em, emphasis::blink)) em_codes[4] = 5;
|
||||
if (has_emphasis(em, emphasis::reverse)) em_codes[5] = 7;
|
||||
if (has_emphasis(em, emphasis::conceal)) em_codes[6] = 8;
|
||||
if (has_emphasis(em, emphasis::strikethrough)) em_codes[7] = 9;
|
||||
|
||||
std::size_t index = 0;
|
||||
for (int i = 0; i < 4; ++i) {
|
||||
size_t index = 0;
|
||||
for (size_t i = 0; i < num_emphases; ++i) {
|
||||
if (!em_codes[i]) continue;
|
||||
buffer[index++] = static_cast<Char>('\x1b');
|
||||
buffer[index++] = static_cast<Char>('[');
|
||||
@@ -411,12 +436,13 @@ 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);
|
||||
}
|
||||
|
||||
private:
|
||||
Char buffer[7u + 3u * 4u + 1u];
|
||||
static constexpr size_t num_emphases = 8;
|
||||
Char buffer[7u + 3u * num_emphases + 1u];
|
||||
|
||||
static FMT_CONSTEXPR void to_esc(uint8_t c, Char* out,
|
||||
char delimiter) FMT_NOEXCEPT {
|
||||
@@ -425,18 +451,22 @@ template <typename Char> struct ansi_color_escape {
|
||||
out[2] = static_cast<Char>('0' + c % 10);
|
||||
out[3] = static_cast<Char>(delimiter);
|
||||
}
|
||||
static FMT_CONSTEXPR bool has_emphasis(emphasis em,
|
||||
emphasis mask) FMT_NOEXCEPT {
|
||||
return static_cast<uint8_t>(em) & static_cast<uint8_t>(mask);
|
||||
}
|
||||
};
|
||||
|
||||
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 +485,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 +574,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 +593,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
|
||||
|
||||
527
include/spdlog/fmt/bundled/os.h
Normal file
527
include/spdlog/fmt/bundled/os.h
Normal file
@@ -0,0 +1,527 @@
|
||||
// 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"
|
||||
|
||||
#ifndef FMT_USE_FCNTL
|
||||
// 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
|
||||
#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;
|
||||
}
|
||||
|
||||
// Intel has a bug that results in failure to deduce a constructor
|
||||
// for empty parameter packs.
|
||||
# if defined(__INTEL_COMPILER) && __INTEL_COMPILER < 2000
|
||||
ostream_params(int new_oflag) : oflag(new_oflag) {}
|
||||
ostream_params(detail::buffer_size bs) : buffer_size(bs.value) {}
|
||||
# endif
|
||||
};
|
||||
|
||||
FMT_END_DETAIL_NAMESPACE
|
||||
|
||||
// Added {} below to work around default constructor error known to
|
||||
// occur in Xcode versions 7.2.1 and 8.2.1.
|
||||
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 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();
|
||||
}
|
||||
|
||||
void flush() {
|
||||
if (size() == 0) return;
|
||||
file_.write(data(), size());
|
||||
clear();
|
||||
}
|
||||
|
||||
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.
|
||||
FMT_DEPRECATED 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,73 +14,46 @@
|
||||
|
||||
FMT_BEGIN_NAMESPACE
|
||||
|
||||
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:
|
||||
using int_type = typename std::basic_streambuf<Char>::int_type;
|
||||
using traits_type = typename std::basic_streambuf<Char>::traits_type;
|
||||
|
||||
buffer<Char>& buffer_;
|
||||
|
||||
public:
|
||||
formatbuf(buffer<Char>& buf) : buffer_(buf) {}
|
||||
|
||||
protected:
|
||||
// The put-area is actually always empty. This makes the implementation
|
||||
// simpler and has the advantage that the streambuf and the buffer are always
|
||||
// in sync and sputc never writes into uninitialized memory. The obvious
|
||||
// disadvantage is that each call to sputc always results in a (virtual) call
|
||||
// to overflow. There is no disadvantage here for sputn since this always
|
||||
// results in a call to xsputn.
|
||||
|
||||
int_type overflow(int_type ch = traits_type::eof()) FMT_OVERRIDE {
|
||||
if (!traits_type::eq_int_type(ch, traits_type::eof()))
|
||||
buffer_.push_back(static_cast<Char>(ch));
|
||||
return ch;
|
||||
}
|
||||
|
||||
std::streamsize xsputn(const Char* s, std::streamsize count) FMT_OVERRIDE {
|
||||
buffer_.append(s, s + count);
|
||||
return count;
|
||||
}
|
||||
};
|
||||
|
||||
template <typename Char> struct test_stream : std::basic_ostream<Char> {
|
||||
private:
|
||||
// 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);
|
||||
};
|
||||
|
||||
// Checks if T has a user-defined operator<< (e.g. not a member of
|
||||
// std::ostream).
|
||||
template <typename T, typename Char> class is_streamable {
|
||||
// Checks if T has a user-defined operator<<.
|
||||
template <typename T, typename Char, typename Enable = void>
|
||||
class is_streamable {
|
||||
private:
|
||||
template <typename U>
|
||||
static bool_constant<!std::is_same<decltype(std::declval<test_stream<Char>&>()
|
||||
<< std::declval<U>()),
|
||||
void_t<>>::value>
|
||||
test(int);
|
||||
static auto test(int)
|
||||
-> bool_constant<sizeof(std::declval<std::basic_ostream<Char>&>()
|
||||
<< std::declval<U>()) != 0>;
|
||||
|
||||
template <typename> static std::false_type test(...);
|
||||
template <typename> static auto test(...) -> std::false_type;
|
||||
|
||||
using result = decltype(test<T>(0));
|
||||
|
||||
public:
|
||||
is_streamable() = default;
|
||||
|
||||
static const bool value = result::value;
|
||||
};
|
||||
|
||||
// Formatting of built-in types and arrays is intentionally disabled because
|
||||
// it's handled by standard (non-ostream) formatters.
|
||||
template <typename T, typename Char>
|
||||
struct is_streamable<
|
||||
T, Char,
|
||||
enable_if_t<
|
||||
std::is_arithmetic<T>::value || std::is_array<T>::value ||
|
||||
std::is_pointer<T>::value || std::is_same<T, char8_type>::value ||
|
||||
std::is_same<T, std::basic_string<Char>>::value ||
|
||||
std::is_same<T, std_string_view<Char>>::value ||
|
||||
(std::is_convertible<T, int>::value && !std::is_enum<T>::value)>>
|
||||
: std::false_type {};
|
||||
|
||||
// Write the content of buf to os.
|
||||
// It is a separate function rather than a part of vprint to simplify testing.
|
||||
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();
|
||||
@@ -96,54 +69,49 @@ void write(std::basic_ostream<Char>& os, buffer<Char>& buf) {
|
||||
template <typename Char, typename T>
|
||||
void format_value(buffer<Char>& buf, const T& value,
|
||||
locale_ref loc = locale_ref()) {
|
||||
formatbuf<Char> format_buf(buf);
|
||||
std::basic_ostream<Char> output(&format_buf);
|
||||
auto&& format_buf = formatbuf<std::basic_streambuf<Char>>(buf);
|
||||
auto&& output = std::basic_ostream<Char>(&format_buf);
|
||||
#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()) {
|
||||
return formatter<basic_string_view<Char>, Char>::parse(ctx);
|
||||
}
|
||||
template <typename ParseCtx,
|
||||
FMT_ENABLE_IF(std::is_same<
|
||||
ParseCtx, basic_printf_parse_context<Char>>::value)>
|
||||
auto parse(ParseCtx& ctx) -> decltype(ctx.begin()) {
|
||||
return ctx.begin();
|
||||
}
|
||||
using formatter<basic_string_view<Char>, Char>::parse;
|
||||
|
||||
template <typename OutputIt>
|
||||
auto format(const T& value, basic_format_context<OutputIt, Char>& ctx)
|
||||
-> OutputIt {
|
||||
basic_memory_buffer<Char> buffer;
|
||||
auto buffer = basic_memory_buffer<Char>();
|
||||
format_value(buffer, value, ctx.locale());
|
||||
basic_string_view<Char> str(buffer.data(), buffer.size());
|
||||
return formatter<basic_string_view<Char>, Char>::format(str, ctx);
|
||||
return formatter<basic_string_view<Char>, Char>::format(
|
||||
{buffer.data(), buffer.size()}, ctx);
|
||||
}
|
||||
|
||||
// DEPRECATED!
|
||||
template <typename OutputIt>
|
||||
auto format(const T& value, basic_printf_context<OutputIt, Char>& ctx)
|
||||
-> OutputIt {
|
||||
basic_memory_buffer<Char> buffer;
|
||||
auto buffer = basic_memory_buffer<Char>();
|
||||
format_value(buffer, value, ctx.locale());
|
||||
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);
|
||||
auto buffer = basic_memory_buffer<Char>();
|
||||
detail::vformat_to(buffer, format_str, args);
|
||||
detail::write_buffer(os, buffer);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -155,11 +123,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,88 @@ 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 = presentation_type::none;
|
||||
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();
|
||||
if (fmt_specs.type && fmt_specs.type != 'c')
|
||||
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 != presentation_type::none &&
|
||||
fmt_specs.type != presentation_type::chr) {
|
||||
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 != presentation_type::pointer);
|
||||
}
|
||||
|
||||
/** 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 != presentation_type::pointer);
|
||||
}
|
||||
|
||||
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 +315,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 +328,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 +355,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 +476,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);
|
||||
@@ -547,33 +492,38 @@ OutputIt basic_printf_context<OutputIt, Char>::format() {
|
||||
|
||||
// Parse type.
|
||||
if (it == end) FMT_THROW(format_error("invalid format string"));
|
||||
specs.type = static_cast<char>(*it++);
|
||||
char type = static_cast<char>(*it++);
|
||||
if (arg.is_integral()) {
|
||||
// Normalize type.
|
||||
switch (specs.type) {
|
||||
switch (type) {
|
||||
case 'i':
|
||||
case 'u':
|
||||
specs.type = 'd';
|
||||
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;
|
||||
}
|
||||
}
|
||||
specs.type = parse_presentation_type(type);
|
||||
if (specs.type == presentation_type::none)
|
||||
parse_ctx.on_error("invalid type specifier");
|
||||
|
||||
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 +537,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 +549,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 +574,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 +603,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 +627,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_
|
||||
|
||||
@@ -13,48 +13,14 @@
|
||||
#define FMT_RANGES_H_
|
||||
|
||||
#include <initializer_list>
|
||||
#include <tuple>
|
||||
#include <type_traits>
|
||||
|
||||
#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 ParseContext>
|
||||
FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
|
||||
return ctx.begin();
|
||||
}
|
||||
};
|
||||
|
||||
template <typename Char, typename Enable = void>
|
||||
struct formatting_range : formatting_base<Char> {
|
||||
static FMT_CONSTEXPR_DECL const std::size_t range_length_limit =
|
||||
FMT_RANGE_OUTPUT_LENGTH_LIMIT; // output only up to N items from the
|
||||
// range.
|
||||
Char prefix;
|
||||
Char delimiter;
|
||||
Char postfix;
|
||||
formatting_range() : prefix('{'), delimiter(','), postfix('}') {}
|
||||
static FMT_CONSTEXPR_DECL const bool add_delimiter_spaces = true;
|
||||
static FMT_CONSTEXPR_DECL const bool add_prepostfix_space = false;
|
||||
};
|
||||
|
||||
template <typename Char, typename Enable = void>
|
||||
struct formatting_tuple : formatting_base<Char> {
|
||||
Char prefix;
|
||||
Char delimiter;
|
||||
Char postfix;
|
||||
formatting_tuple() : prefix('('), delimiter(','), postfix(')') {}
|
||||
static FMT_CONSTEXPR_DECL const bool add_delimiter_spaces = true;
|
||||
static FMT_CONSTEXPR_DECL const bool add_prepostfix_space = false;
|
||||
};
|
||||
|
||||
namespace internal {
|
||||
namespace detail {
|
||||
|
||||
template <typename RangeT, typename OutputIterator>
|
||||
OutputIterator copy(const RangeT& range, OutputIterator out) {
|
||||
@@ -75,8 +41,14 @@ OutputIterator copy(char ch, OutputIterator out) {
|
||||
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 OutputIterator>
|
||||
OutputIterator copy(wchar_t ch, OutputIterator out) {
|
||||
*out++ = ch;
|
||||
return out;
|
||||
}
|
||||
|
||||
// Returns true if T has a std::string-like interface, like std::string_view.
|
||||
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());
|
||||
@@ -84,26 +56,118 @@ template <typename T> class is_like_std_string {
|
||||
|
||||
public:
|
||||
static FMT_CONSTEXPR_DECL const bool value =
|
||||
is_string<T>::value || !std::is_void<decltype(check<T>(nullptr))>::value;
|
||||
is_string<T>::value ||
|
||||
std::is_convertible<T, std_string_view<char>>::value ||
|
||||
!std::is_void<decltype(check<T>(nullptr))>::value;
|
||||
};
|
||||
|
||||
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 T> class is_map {
|
||||
template <typename U> static auto check(U*) -> typename U::mapped_type;
|
||||
template <typename> static void check(...);
|
||||
|
||||
public:
|
||||
#ifdef FMT_FORMAT_MAP_AS_LIST
|
||||
static FMT_CONSTEXPR_DECL const bool value = false;
|
||||
#else
|
||||
static FMT_CONSTEXPR_DECL const bool value =
|
||||
!std::is_void<decltype(check<T>(nullptr))>::value;
|
||||
#endif
|
||||
};
|
||||
|
||||
template <typename T> class is_set {
|
||||
template <typename U> static auto check(U*) -> typename U::key_type;
|
||||
template <typename> static void check(...);
|
||||
|
||||
public:
|
||||
#ifdef FMT_FORMAT_SET_AS_LIST
|
||||
static FMT_CONSTEXPR_DECL const bool value = false;
|
||||
#else
|
||||
static FMT_CONSTEXPR_DECL const bool value =
|
||||
!std::is_void<decltype(check<T>(nullptr))>::value && !is_map<T>::value;
|
||||
#endif
|
||||
};
|
||||
|
||||
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_end(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_end(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)> {};
|
||||
# undef FMT_DECLTYPE_RETURN
|
||||
#endif
|
||||
|
||||
/// tuple_size and tuple_element check.
|
||||
// tuple_size and tuple_element check.
|
||||
template <typename T> class is_tuple_like_ {
|
||||
template <typename U>
|
||||
static auto check(U* p) -> decltype(std::tuple_size<U>::value, int());
|
||||
@@ -118,26 +182,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,183 +221,531 @@ 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 ? " \"{}\"" : "\"{}\"";
|
||||
struct singleton {
|
||||
unsigned char upper;
|
||||
unsigned char lower_count;
|
||||
};
|
||||
|
||||
inline auto is_printable(uint16_t x, const singleton* singletons,
|
||||
size_t singletons_size,
|
||||
const unsigned char* singleton_lowers,
|
||||
const unsigned char* normal, size_t normal_size)
|
||||
-> bool {
|
||||
auto upper = x >> 8;
|
||||
auto lower_start = 0;
|
||||
for (size_t i = 0; i < singletons_size; ++i) {
|
||||
auto s = singletons[i];
|
||||
auto lower_end = lower_start + s.lower_count;
|
||||
if (upper < s.upper) break;
|
||||
if (upper == s.upper) {
|
||||
for (auto j = lower_start; j < lower_end; ++j) {
|
||||
if (singleton_lowers[j] == (x & 0xff)) return false;
|
||||
}
|
||||
}
|
||||
lower_start = lower_end;
|
||||
}
|
||||
|
||||
auto xsigned = static_cast<int>(x);
|
||||
auto current = true;
|
||||
for (size_t i = 0; i < normal_size; ++i) {
|
||||
auto v = static_cast<int>(normal[i]);
|
||||
auto len = (v & 0x80) != 0 ? (v & 0x7f) << 8 | normal[++i] : v;
|
||||
xsigned -= len;
|
||||
if (xsigned < 0) break;
|
||||
current = !current;
|
||||
}
|
||||
return current;
|
||||
}
|
||||
|
||||
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"\"{}\"";
|
||||
// Returns true iff the code point cp is printable.
|
||||
// This code is generated by support/printable.py.
|
||||
inline auto is_printable(uint32_t cp) -> bool {
|
||||
static constexpr singleton singletons0[] = {
|
||||
{0x00, 1}, {0x03, 5}, {0x05, 6}, {0x06, 3}, {0x07, 6}, {0x08, 8},
|
||||
{0x09, 17}, {0x0a, 28}, {0x0b, 25}, {0x0c, 20}, {0x0d, 16}, {0x0e, 13},
|
||||
{0x0f, 4}, {0x10, 3}, {0x12, 18}, {0x13, 9}, {0x16, 1}, {0x17, 5},
|
||||
{0x18, 2}, {0x19, 3}, {0x1a, 7}, {0x1c, 2}, {0x1d, 1}, {0x1f, 22},
|
||||
{0x20, 3}, {0x2b, 3}, {0x2c, 2}, {0x2d, 11}, {0x2e, 1}, {0x30, 3},
|
||||
{0x31, 2}, {0x32, 1}, {0xa7, 2}, {0xa9, 2}, {0xaa, 4}, {0xab, 8},
|
||||
{0xfa, 2}, {0xfb, 5}, {0xfd, 4}, {0xfe, 3}, {0xff, 9},
|
||||
};
|
||||
static constexpr unsigned char singletons0_lower[] = {
|
||||
0xad, 0x78, 0x79, 0x8b, 0x8d, 0xa2, 0x30, 0x57, 0x58, 0x8b, 0x8c, 0x90,
|
||||
0x1c, 0x1d, 0xdd, 0x0e, 0x0f, 0x4b, 0x4c, 0xfb, 0xfc, 0x2e, 0x2f, 0x3f,
|
||||
0x5c, 0x5d, 0x5f, 0xb5, 0xe2, 0x84, 0x8d, 0x8e, 0x91, 0x92, 0xa9, 0xb1,
|
||||
0xba, 0xbb, 0xc5, 0xc6, 0xc9, 0xca, 0xde, 0xe4, 0xe5, 0xff, 0x00, 0x04,
|
||||
0x11, 0x12, 0x29, 0x31, 0x34, 0x37, 0x3a, 0x3b, 0x3d, 0x49, 0x4a, 0x5d,
|
||||
0x84, 0x8e, 0x92, 0xa9, 0xb1, 0xb4, 0xba, 0xbb, 0xc6, 0xca, 0xce, 0xcf,
|
||||
0xe4, 0xe5, 0x00, 0x04, 0x0d, 0x0e, 0x11, 0x12, 0x29, 0x31, 0x34, 0x3a,
|
||||
0x3b, 0x45, 0x46, 0x49, 0x4a, 0x5e, 0x64, 0x65, 0x84, 0x91, 0x9b, 0x9d,
|
||||
0xc9, 0xce, 0xcf, 0x0d, 0x11, 0x29, 0x45, 0x49, 0x57, 0x64, 0x65, 0x8d,
|
||||
0x91, 0xa9, 0xb4, 0xba, 0xbb, 0xc5, 0xc9, 0xdf, 0xe4, 0xe5, 0xf0, 0x0d,
|
||||
0x11, 0x45, 0x49, 0x64, 0x65, 0x80, 0x84, 0xb2, 0xbc, 0xbe, 0xbf, 0xd5,
|
||||
0xd7, 0xf0, 0xf1, 0x83, 0x85, 0x8b, 0xa4, 0xa6, 0xbe, 0xbf, 0xc5, 0xc7,
|
||||
0xce, 0xcf, 0xda, 0xdb, 0x48, 0x98, 0xbd, 0xcd, 0xc6, 0xce, 0xcf, 0x49,
|
||||
0x4e, 0x4f, 0x57, 0x59, 0x5e, 0x5f, 0x89, 0x8e, 0x8f, 0xb1, 0xb6, 0xb7,
|
||||
0xbf, 0xc1, 0xc6, 0xc7, 0xd7, 0x11, 0x16, 0x17, 0x5b, 0x5c, 0xf6, 0xf7,
|
||||
0xfe, 0xff, 0x80, 0x0d, 0x6d, 0x71, 0xde, 0xdf, 0x0e, 0x0f, 0x1f, 0x6e,
|
||||
0x6f, 0x1c, 0x1d, 0x5f, 0x7d, 0x7e, 0xae, 0xaf, 0xbb, 0xbc, 0xfa, 0x16,
|
||||
0x17, 0x1e, 0x1f, 0x46, 0x47, 0x4e, 0x4f, 0x58, 0x5a, 0x5c, 0x5e, 0x7e,
|
||||
0x7f, 0xb5, 0xc5, 0xd4, 0xd5, 0xdc, 0xf0, 0xf1, 0xf5, 0x72, 0x73, 0x8f,
|
||||
0x74, 0x75, 0x96, 0x2f, 0x5f, 0x26, 0x2e, 0x2f, 0xa7, 0xaf, 0xb7, 0xbf,
|
||||
0xc7, 0xcf, 0xd7, 0xdf, 0x9a, 0x40, 0x97, 0x98, 0x30, 0x8f, 0x1f, 0xc0,
|
||||
0xc1, 0xce, 0xff, 0x4e, 0x4f, 0x5a, 0x5b, 0x07, 0x08, 0x0f, 0x10, 0x27,
|
||||
0x2f, 0xee, 0xef, 0x6e, 0x6f, 0x37, 0x3d, 0x3f, 0x42, 0x45, 0x90, 0x91,
|
||||
0xfe, 0xff, 0x53, 0x67, 0x75, 0xc8, 0xc9, 0xd0, 0xd1, 0xd8, 0xd9, 0xe7,
|
||||
0xfe, 0xff,
|
||||
};
|
||||
static constexpr singleton singletons1[] = {
|
||||
{0x00, 6}, {0x01, 1}, {0x03, 1}, {0x04, 2}, {0x08, 8}, {0x09, 2},
|
||||
{0x0a, 5}, {0x0b, 2}, {0x0e, 4}, {0x10, 1}, {0x11, 2}, {0x12, 5},
|
||||
{0x13, 17}, {0x14, 1}, {0x15, 2}, {0x17, 2}, {0x19, 13}, {0x1c, 5},
|
||||
{0x1d, 8}, {0x24, 1}, {0x6a, 3}, {0x6b, 2}, {0xbc, 2}, {0xd1, 2},
|
||||
{0xd4, 12}, {0xd5, 9}, {0xd6, 2}, {0xd7, 2}, {0xda, 1}, {0xe0, 5},
|
||||
{0xe1, 2}, {0xe8, 2}, {0xee, 32}, {0xf0, 4}, {0xf8, 2}, {0xf9, 2},
|
||||
{0xfa, 2}, {0xfb, 1},
|
||||
};
|
||||
static constexpr unsigned char singletons1_lower[] = {
|
||||
0x0c, 0x27, 0x3b, 0x3e, 0x4e, 0x4f, 0x8f, 0x9e, 0x9e, 0x9f, 0x06, 0x07,
|
||||
0x09, 0x36, 0x3d, 0x3e, 0x56, 0xf3, 0xd0, 0xd1, 0x04, 0x14, 0x18, 0x36,
|
||||
0x37, 0x56, 0x57, 0x7f, 0xaa, 0xae, 0xaf, 0xbd, 0x35, 0xe0, 0x12, 0x87,
|
||||
0x89, 0x8e, 0x9e, 0x04, 0x0d, 0x0e, 0x11, 0x12, 0x29, 0x31, 0x34, 0x3a,
|
||||
0x45, 0x46, 0x49, 0x4a, 0x4e, 0x4f, 0x64, 0x65, 0x5c, 0xb6, 0xb7, 0x1b,
|
||||
0x1c, 0x07, 0x08, 0x0a, 0x0b, 0x14, 0x17, 0x36, 0x39, 0x3a, 0xa8, 0xa9,
|
||||
0xd8, 0xd9, 0x09, 0x37, 0x90, 0x91, 0xa8, 0x07, 0x0a, 0x3b, 0x3e, 0x66,
|
||||
0x69, 0x8f, 0x92, 0x6f, 0x5f, 0xee, 0xef, 0x5a, 0x62, 0x9a, 0x9b, 0x27,
|
||||
0x28, 0x55, 0x9d, 0xa0, 0xa1, 0xa3, 0xa4, 0xa7, 0xa8, 0xad, 0xba, 0xbc,
|
||||
0xc4, 0x06, 0x0b, 0x0c, 0x15, 0x1d, 0x3a, 0x3f, 0x45, 0x51, 0xa6, 0xa7,
|
||||
0xcc, 0xcd, 0xa0, 0x07, 0x19, 0x1a, 0x22, 0x25, 0x3e, 0x3f, 0xc5, 0xc6,
|
||||
0x04, 0x20, 0x23, 0x25, 0x26, 0x28, 0x33, 0x38, 0x3a, 0x48, 0x4a, 0x4c,
|
||||
0x50, 0x53, 0x55, 0x56, 0x58, 0x5a, 0x5c, 0x5e, 0x60, 0x63, 0x65, 0x66,
|
||||
0x6b, 0x73, 0x78, 0x7d, 0x7f, 0x8a, 0xa4, 0xaa, 0xaf, 0xb0, 0xc0, 0xd0,
|
||||
0xae, 0xaf, 0x79, 0xcc, 0x6e, 0x6f, 0x93,
|
||||
};
|
||||
static constexpr unsigned char normal0[] = {
|
||||
0x00, 0x20, 0x5f, 0x22, 0x82, 0xdf, 0x04, 0x82, 0x44, 0x08, 0x1b, 0x04,
|
||||
0x06, 0x11, 0x81, 0xac, 0x0e, 0x80, 0xab, 0x35, 0x28, 0x0b, 0x80, 0xe0,
|
||||
0x03, 0x19, 0x08, 0x01, 0x04, 0x2f, 0x04, 0x34, 0x04, 0x07, 0x03, 0x01,
|
||||
0x07, 0x06, 0x07, 0x11, 0x0a, 0x50, 0x0f, 0x12, 0x07, 0x55, 0x07, 0x03,
|
||||
0x04, 0x1c, 0x0a, 0x09, 0x03, 0x08, 0x03, 0x07, 0x03, 0x02, 0x03, 0x03,
|
||||
0x03, 0x0c, 0x04, 0x05, 0x03, 0x0b, 0x06, 0x01, 0x0e, 0x15, 0x05, 0x3a,
|
||||
0x03, 0x11, 0x07, 0x06, 0x05, 0x10, 0x07, 0x57, 0x07, 0x02, 0x07, 0x15,
|
||||
0x0d, 0x50, 0x04, 0x43, 0x03, 0x2d, 0x03, 0x01, 0x04, 0x11, 0x06, 0x0f,
|
||||
0x0c, 0x3a, 0x04, 0x1d, 0x25, 0x5f, 0x20, 0x6d, 0x04, 0x6a, 0x25, 0x80,
|
||||
0xc8, 0x05, 0x82, 0xb0, 0x03, 0x1a, 0x06, 0x82, 0xfd, 0x03, 0x59, 0x07,
|
||||
0x15, 0x0b, 0x17, 0x09, 0x14, 0x0c, 0x14, 0x0c, 0x6a, 0x06, 0x0a, 0x06,
|
||||
0x1a, 0x06, 0x59, 0x07, 0x2b, 0x05, 0x46, 0x0a, 0x2c, 0x04, 0x0c, 0x04,
|
||||
0x01, 0x03, 0x31, 0x0b, 0x2c, 0x04, 0x1a, 0x06, 0x0b, 0x03, 0x80, 0xac,
|
||||
0x06, 0x0a, 0x06, 0x21, 0x3f, 0x4c, 0x04, 0x2d, 0x03, 0x74, 0x08, 0x3c,
|
||||
0x03, 0x0f, 0x03, 0x3c, 0x07, 0x38, 0x08, 0x2b, 0x05, 0x82, 0xff, 0x11,
|
||||
0x18, 0x08, 0x2f, 0x11, 0x2d, 0x03, 0x20, 0x10, 0x21, 0x0f, 0x80, 0x8c,
|
||||
0x04, 0x82, 0x97, 0x19, 0x0b, 0x15, 0x88, 0x94, 0x05, 0x2f, 0x05, 0x3b,
|
||||
0x07, 0x02, 0x0e, 0x18, 0x09, 0x80, 0xb3, 0x2d, 0x74, 0x0c, 0x80, 0xd6,
|
||||
0x1a, 0x0c, 0x05, 0x80, 0xff, 0x05, 0x80, 0xdf, 0x0c, 0xee, 0x0d, 0x03,
|
||||
0x84, 0x8d, 0x03, 0x37, 0x09, 0x81, 0x5c, 0x14, 0x80, 0xb8, 0x08, 0x80,
|
||||
0xcb, 0x2a, 0x38, 0x03, 0x0a, 0x06, 0x38, 0x08, 0x46, 0x08, 0x0c, 0x06,
|
||||
0x74, 0x0b, 0x1e, 0x03, 0x5a, 0x04, 0x59, 0x09, 0x80, 0x83, 0x18, 0x1c,
|
||||
0x0a, 0x16, 0x09, 0x4c, 0x04, 0x80, 0x8a, 0x06, 0xab, 0xa4, 0x0c, 0x17,
|
||||
0x04, 0x31, 0xa1, 0x04, 0x81, 0xda, 0x26, 0x07, 0x0c, 0x05, 0x05, 0x80,
|
||||
0xa5, 0x11, 0x81, 0x6d, 0x10, 0x78, 0x28, 0x2a, 0x06, 0x4c, 0x04, 0x80,
|
||||
0x8d, 0x04, 0x80, 0xbe, 0x03, 0x1b, 0x03, 0x0f, 0x0d,
|
||||
};
|
||||
static constexpr unsigned char normal1[] = {
|
||||
0x5e, 0x22, 0x7b, 0x05, 0x03, 0x04, 0x2d, 0x03, 0x66, 0x03, 0x01, 0x2f,
|
||||
0x2e, 0x80, 0x82, 0x1d, 0x03, 0x31, 0x0f, 0x1c, 0x04, 0x24, 0x09, 0x1e,
|
||||
0x05, 0x2b, 0x05, 0x44, 0x04, 0x0e, 0x2a, 0x80, 0xaa, 0x06, 0x24, 0x04,
|
||||
0x24, 0x04, 0x28, 0x08, 0x34, 0x0b, 0x01, 0x80, 0x90, 0x81, 0x37, 0x09,
|
||||
0x16, 0x0a, 0x08, 0x80, 0x98, 0x39, 0x03, 0x63, 0x08, 0x09, 0x30, 0x16,
|
||||
0x05, 0x21, 0x03, 0x1b, 0x05, 0x01, 0x40, 0x38, 0x04, 0x4b, 0x05, 0x2f,
|
||||
0x04, 0x0a, 0x07, 0x09, 0x07, 0x40, 0x20, 0x27, 0x04, 0x0c, 0x09, 0x36,
|
||||
0x03, 0x3a, 0x05, 0x1a, 0x07, 0x04, 0x0c, 0x07, 0x50, 0x49, 0x37, 0x33,
|
||||
0x0d, 0x33, 0x07, 0x2e, 0x08, 0x0a, 0x81, 0x26, 0x52, 0x4e, 0x28, 0x08,
|
||||
0x2a, 0x56, 0x1c, 0x14, 0x17, 0x09, 0x4e, 0x04, 0x1e, 0x0f, 0x43, 0x0e,
|
||||
0x19, 0x07, 0x0a, 0x06, 0x48, 0x08, 0x27, 0x09, 0x75, 0x0b, 0x3f, 0x41,
|
||||
0x2a, 0x06, 0x3b, 0x05, 0x0a, 0x06, 0x51, 0x06, 0x01, 0x05, 0x10, 0x03,
|
||||
0x05, 0x80, 0x8b, 0x62, 0x1e, 0x48, 0x08, 0x0a, 0x80, 0xa6, 0x5e, 0x22,
|
||||
0x45, 0x0b, 0x0a, 0x06, 0x0d, 0x13, 0x39, 0x07, 0x0a, 0x36, 0x2c, 0x04,
|
||||
0x10, 0x80, 0xc0, 0x3c, 0x64, 0x53, 0x0c, 0x48, 0x09, 0x0a, 0x46, 0x45,
|
||||
0x1b, 0x48, 0x08, 0x53, 0x1d, 0x39, 0x81, 0x07, 0x46, 0x0a, 0x1d, 0x03,
|
||||
0x47, 0x49, 0x37, 0x03, 0x0e, 0x08, 0x0a, 0x06, 0x39, 0x07, 0x0a, 0x81,
|
||||
0x36, 0x19, 0x80, 0xb7, 0x01, 0x0f, 0x32, 0x0d, 0x83, 0x9b, 0x66, 0x75,
|
||||
0x0b, 0x80, 0xc4, 0x8a, 0xbc, 0x84, 0x2f, 0x8f, 0xd1, 0x82, 0x47, 0xa1,
|
||||
0xb9, 0x82, 0x39, 0x07, 0x2a, 0x04, 0x02, 0x60, 0x26, 0x0a, 0x46, 0x0a,
|
||||
0x28, 0x05, 0x13, 0x82, 0xb0, 0x5b, 0x65, 0x4b, 0x04, 0x39, 0x07, 0x11,
|
||||
0x40, 0x05, 0x0b, 0x02, 0x0e, 0x97, 0xf8, 0x08, 0x84, 0xd6, 0x2a, 0x09,
|
||||
0xa2, 0xf7, 0x81, 0x1f, 0x31, 0x03, 0x11, 0x04, 0x08, 0x81, 0x8c, 0x89,
|
||||
0x04, 0x6b, 0x05, 0x0d, 0x03, 0x09, 0x07, 0x10, 0x93, 0x60, 0x80, 0xf6,
|
||||
0x0a, 0x73, 0x08, 0x6e, 0x17, 0x46, 0x80, 0x9a, 0x14, 0x0c, 0x57, 0x09,
|
||||
0x19, 0x80, 0x87, 0x81, 0x47, 0x03, 0x85, 0x42, 0x0f, 0x15, 0x85, 0x50,
|
||||
0x2b, 0x80, 0xd5, 0x2d, 0x03, 0x1a, 0x04, 0x02, 0x81, 0x70, 0x3a, 0x05,
|
||||
0x01, 0x85, 0x00, 0x80, 0xd7, 0x29, 0x4c, 0x04, 0x0a, 0x04, 0x02, 0x83,
|
||||
0x11, 0x44, 0x4c, 0x3d, 0x80, 0xc2, 0x3c, 0x06, 0x01, 0x04, 0x55, 0x05,
|
||||
0x1b, 0x34, 0x02, 0x81, 0x0e, 0x2c, 0x04, 0x64, 0x0c, 0x56, 0x0a, 0x80,
|
||||
0xae, 0x38, 0x1d, 0x0d, 0x2c, 0x04, 0x09, 0x07, 0x02, 0x0e, 0x06, 0x80,
|
||||
0x9a, 0x83, 0xd8, 0x08, 0x0d, 0x03, 0x0d, 0x03, 0x74, 0x0c, 0x59, 0x07,
|
||||
0x0c, 0x14, 0x0c, 0x04, 0x38, 0x08, 0x0a, 0x06, 0x28, 0x08, 0x22, 0x4e,
|
||||
0x81, 0x54, 0x0c, 0x15, 0x03, 0x03, 0x05, 0x07, 0x09, 0x19, 0x07, 0x07,
|
||||
0x09, 0x03, 0x0d, 0x07, 0x29, 0x80, 0xcb, 0x25, 0x0a, 0x84, 0x06,
|
||||
};
|
||||
auto lower = static_cast<uint16_t>(cp);
|
||||
if (cp < 0x10000) {
|
||||
return is_printable(lower, singletons0,
|
||||
sizeof(singletons0) / sizeof(*singletons0),
|
||||
singletons0_lower, normal0, sizeof(normal0));
|
||||
}
|
||||
if (cp < 0x20000) {
|
||||
return is_printable(lower, singletons1,
|
||||
sizeof(singletons1) / sizeof(*singletons1),
|
||||
singletons1_lower, normal1, sizeof(normal1));
|
||||
}
|
||||
if (0x2a6de <= cp && cp < 0x2a700) return false;
|
||||
if (0x2b735 <= cp && cp < 0x2b740) return false;
|
||||
if (0x2b81e <= cp && cp < 0x2b820) return false;
|
||||
if (0x2cea2 <= cp && cp < 0x2ceb0) return false;
|
||||
if (0x2ebe1 <= cp && cp < 0x2f800) return false;
|
||||
if (0x2fa1e <= cp && cp < 0x30000) return false;
|
||||
if (0x3134b <= cp && cp < 0xe0100) return false;
|
||||
if (0xe01f0 <= cp && cp < 0x110000) return false;
|
||||
return cp < 0x110000;
|
||||
}
|
||||
|
||||
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"'{}'";
|
||||
inline auto needs_escape(uint32_t cp) -> bool {
|
||||
return cp < 0x20 || cp == 0x7f || cp == '"' || cp == '\\' ||
|
||||
!is_printable(cp);
|
||||
}
|
||||
|
||||
} // namespace internal
|
||||
template <typename Char> struct find_escape_result {
|
||||
const Char* begin;
|
||||
const Char* end;
|
||||
uint32_t cp;
|
||||
};
|
||||
|
||||
template <typename Char>
|
||||
auto find_escape(const Char* begin, const Char* end)
|
||||
-> find_escape_result<Char> {
|
||||
for (; begin != end; ++begin) {
|
||||
auto cp = static_cast<typename std::make_unsigned<Char>::type>(*begin);
|
||||
if (sizeof(Char) == 1 && cp >= 0x80) continue;
|
||||
if (needs_escape(cp)) return {begin, begin + 1, cp};
|
||||
}
|
||||
return {begin, nullptr, 0};
|
||||
}
|
||||
|
||||
inline auto find_escape(const char* begin, const char* end)
|
||||
-> find_escape_result<char> {
|
||||
if (!is_utf8()) return find_escape<char>(begin, end);
|
||||
auto result = find_escape_result<char>{end, nullptr, 0};
|
||||
for_each_codepoint(string_view(begin, to_unsigned(end - begin)),
|
||||
[&](uint32_t cp, string_view sv) {
|
||||
if (needs_escape(cp)) {
|
||||
result = {sv.begin(), sv.end(), cp};
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
template <typename Char, typename OutputIt>
|
||||
auto write_range_entry(OutputIt out, basic_string_view<Char> str) -> OutputIt {
|
||||
*out++ = '"';
|
||||
auto begin = str.begin(), end = str.end();
|
||||
do {
|
||||
auto escape = find_escape(begin, end);
|
||||
out = copy_str<Char>(begin, escape.begin, out);
|
||||
begin = escape.end;
|
||||
if (!begin) break;
|
||||
auto c = static_cast<Char>(escape.cp);
|
||||
switch (escape.cp) {
|
||||
case '\n':
|
||||
*out++ = '\\';
|
||||
c = 'n';
|
||||
break;
|
||||
case '\r':
|
||||
*out++ = '\\';
|
||||
c = 'r';
|
||||
break;
|
||||
case '\t':
|
||||
*out++ = '\\';
|
||||
c = 't';
|
||||
break;
|
||||
case '"':
|
||||
FMT_FALLTHROUGH;
|
||||
case '\\':
|
||||
*out++ = '\\';
|
||||
break;
|
||||
default:
|
||||
if (is_utf8()) {
|
||||
if (escape.cp < 0x100) {
|
||||
out = format_to(out, "\\x{:02x}", escape.cp);
|
||||
continue;
|
||||
}
|
||||
if (escape.cp < 0x10000) {
|
||||
out = format_to(out, "\\u{:04x}", escape.cp);
|
||||
continue;
|
||||
}
|
||||
if (escape.cp < 0x110000) {
|
||||
out = format_to(out, "\\U{:08x}", escape.cp);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
for (Char escape_char : basic_string_view<Char>(
|
||||
escape.begin, to_unsigned(escape.end - escape.begin))) {
|
||||
out = format_to(
|
||||
out, "\\x{:02x}",
|
||||
static_cast<typename std::make_unsigned<Char>::type>(escape_char));
|
||||
}
|
||||
continue;
|
||||
}
|
||||
*out++ = c;
|
||||
} while (begin != end);
|
||||
*out++ = '"';
|
||||
return out;
|
||||
}
|
||||
|
||||
template <typename Char, typename OutputIt, typename T,
|
||||
FMT_ENABLE_IF(std::is_convertible<T, std_string_view<char>>::value)>
|
||||
inline auto write_range_entry(OutputIt out, const T& str) -> OutputIt {
|
||||
auto sv = std_string_view<Char>(str);
|
||||
return write_range_entry<Char>(out, basic_string_view<Char>(sv));
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
template <
|
||||
typename Char, typename OutputIt, typename Arg,
|
||||
FMT_ENABLE_IF(!is_std_string_like<typename std::decay<Arg>::type>::value &&
|
||||
!std::is_same<Arg, Char>::value)>
|
||||
OutputIt write_range_entry(OutputIt out, const Arg& v) {
|
||||
return write<Char>(out, v);
|
||||
}
|
||||
|
||||
} // namespace detail
|
||||
|
||||
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>
|
||||
struct formatter<TupleT, Char, enable_if_t<fmt::is_tuple_like<TupleT>::value>> {
|
||||
private:
|
||||
// C++11 generic lambda for format()
|
||||
// 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;
|
||||
int i;
|
||||
typename FormatContext::iterator& out;
|
||||
};
|
||||
|
||||
public:
|
||||
formatting_tuple<Char> formatting;
|
||||
|
||||
template <typename ParseContext>
|
||||
FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
|
||||
return formatting.parse(ctx);
|
||||
}
|
||||
|
||||
template <typename FormatContext = format_context>
|
||||
auto format(const TupleT& values, FormatContext& ctx) -> decltype(ctx.out()) {
|
||||
auto out = ctx.out();
|
||||
std::size_t i = 0;
|
||||
internal::copy(formatting.prefix, out);
|
||||
|
||||
internal::for_each(values, format_each<FormatContext>{formatting, i, out});
|
||||
if (formatting.add_prepostfix_space) {
|
||||
*out++ = ' ';
|
||||
}
|
||||
internal::copy(formatting.postfix, out);
|
||||
|
||||
return ctx.out();
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T, typename Char> struct is_range {
|
||||
static FMT_CONSTEXPR_DECL const bool value =
|
||||
internal::is_range_<T>::value &&
|
||||
!internal::is_like_std_string<T>::value &&
|
||||
!std::is_convertible<T, std::basic_string<Char>>::value &&
|
||||
!std::is_constructible<internal::std_string_view<Char>, T>::value;
|
||||
};
|
||||
|
||||
template <typename RangeT, typename Char>
|
||||
struct formatter<RangeT, Char,
|
||||
enable_if_t<fmt::is_range<RangeT, Char>::value>> {
|
||||
formatting_range<Char> formatting;
|
||||
|
||||
template <typename ParseContext>
|
||||
FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
|
||||
return formatting.parse(ctx);
|
||||
}
|
||||
|
||||
template <typename FormatContext>
|
||||
typename FormatContext::iterator format(const RangeT& values,
|
||||
FormatContext& ctx) {
|
||||
auto out = 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;
|
||||
}
|
||||
}
|
||||
if (formatting.add_prepostfix_space) *out++ = ' ';
|
||||
return internal::copy(formatting.postfix, out);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename Char, typename... T> struct tuple_arg_join : internal::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} {}
|
||||
};
|
||||
|
||||
template <typename Char, typename... T>
|
||||
struct formatter<tuple_arg_join<Char, T...>, Char> {
|
||||
template <typename ParseContext>
|
||||
FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
|
||||
return ctx.begin();
|
||||
}
|
||||
|
||||
template <typename FormatContext = format_context>
|
||||
auto format(const TupleT& values, FormatContext& ctx) -> decltype(ctx.out()) {
|
||||
auto out = ctx.out();
|
||||
*out++ = '(';
|
||||
detail::for_each(values, format_each<FormatContext>{0, out});
|
||||
*out++ = ')';
|
||||
return out;
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T, typename Char> struct is_range {
|
||||
static FMT_CONSTEXPR_DECL const bool value =
|
||||
detail::is_range_<T>::value && !detail::is_std_string_like<T>::value &&
|
||||
!detail::is_map<T>::value &&
|
||||
!std::is_convertible<T, std::basic_string<Char>>::value &&
|
||||
!std::is_constructible<detail::std_string_view<Char>, T>::value;
|
||||
};
|
||||
|
||||
template <typename T, typename Char>
|
||||
struct formatter<
|
||||
T, Char,
|
||||
enable_if_t<
|
||||
fmt::is_range<T, Char>::value
|
||||
// Workaround a bug in MSVC 2019 and earlier.
|
||||
#if !FMT_MSC_VER
|
||||
&& (is_formattable<detail::value_type<T>, Char>::value ||
|
||||
detail::has_fallback_formatter<detail::value_type<T>, Char>::value)
|
||||
#endif
|
||||
>> {
|
||||
template <typename ParseContext>
|
||||
FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
|
||||
return ctx.begin();
|
||||
}
|
||||
|
||||
template <
|
||||
typename FormatContext, typename U,
|
||||
FMT_ENABLE_IF(
|
||||
std::is_same<U, conditional_t<detail::has_const_begin_end<T>::value,
|
||||
const T, T>>::value)>
|
||||
auto format(U& range, FormatContext& ctx) -> decltype(ctx.out()) {
|
||||
#ifdef FMT_DEPRECATED_BRACED_RANGES
|
||||
Char prefix = '{';
|
||||
Char postfix = '}';
|
||||
#else
|
||||
Char prefix = detail::is_set<T>::value ? '{' : '[';
|
||||
Char postfix = detail::is_set<T>::value ? '}' : ']';
|
||||
#endif
|
||||
auto out = ctx.out();
|
||||
*out++ = prefix;
|
||||
int i = 0;
|
||||
auto it = std::begin(range);
|
||||
auto end = std::end(range);
|
||||
for (; it != end; ++it) {
|
||||
if (i > 0) out = detail::write_delimiter(out);
|
||||
out = detail::write_range_entry<Char>(out, *it);
|
||||
++i;
|
||||
}
|
||||
*out++ = postfix;
|
||||
return out;
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T, typename Char>
|
||||
struct formatter<
|
||||
T, Char,
|
||||
enable_if_t<
|
||||
detail::is_map<T>::value
|
||||
// Workaround a bug in MSVC 2019 and earlier.
|
||||
#if !FMT_MSC_VER
|
||||
&& (is_formattable<detail::value_type<T>, Char>::value ||
|
||||
detail::has_fallback_formatter<detail::value_type<T>, Char>::value)
|
||||
#endif
|
||||
>> {
|
||||
template <typename ParseContext>
|
||||
FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
|
||||
return ctx.begin();
|
||||
}
|
||||
|
||||
template <
|
||||
typename FormatContext, typename U,
|
||||
FMT_ENABLE_IF(
|
||||
std::is_same<U, conditional_t<detail::has_const_begin_end<T>::value,
|
||||
const T, T>>::value)>
|
||||
auto format(U& map, FormatContext& ctx) -> decltype(ctx.out()) {
|
||||
auto out = ctx.out();
|
||||
*out++ = '{';
|
||||
int i = 0;
|
||||
for (const auto& item : map) {
|
||||
if (i > 0) out = detail::write_delimiter(out);
|
||||
out = detail::write_range_entry<Char>(out, item.first);
|
||||
*out++ = ':';
|
||||
*out++ = ' ';
|
||||
out = detail::write_range_entry<Char>(out, item.second);
|
||||
++i;
|
||||
}
|
||||
*out++ = '}';
|
||||
return out;
|
||||
}
|
||||
};
|
||||
|
||||
template <typename Char, typename... T> struct tuple_join_view : detail::view {
|
||||
const std::tuple<T...>& tuple;
|
||||
basic_string_view<Char> sep;
|
||||
|
||||
tuple_join_view(const std::tuple<T...>& t, basic_string_view<Char> s)
|
||||
: tuple(t), sep{s} {}
|
||||
};
|
||||
|
||||
template <typename Char, typename... T>
|
||||
using tuple_arg_join = tuple_join_view<Char, T...>;
|
||||
|
||||
// Define FMT_TUPLE_JOIN_SPECIFIERS to enable experimental format specifiers
|
||||
// support in tuple_join. It is disabled by default because of issues with
|
||||
// the dynamic width and precision.
|
||||
#ifndef FMT_TUPLE_JOIN_SPECIFIERS
|
||||
# define FMT_TUPLE_JOIN_SPECIFIERS 0
|
||||
#endif
|
||||
|
||||
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 do_parse(ctx, std::integral_constant<size_t, sizeof...(T)>());
|
||||
}
|
||||
|
||||
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) const -> typename FormatContext::iterator {
|
||||
return do_format(value, ctx,
|
||||
std::integral_constant<size_t, 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)...);
|
||||
std::tuple<formatter<typename std::decay<T>::type, Char>...> formatters_;
|
||||
|
||||
template <typename ParseContext>
|
||||
FMT_CONSTEXPR auto do_parse(ParseContext& ctx,
|
||||
std::integral_constant<size_t, 0>)
|
||||
-> decltype(ctx.begin()) {
|
||||
return ctx.begin();
|
||||
}
|
||||
|
||||
template <typename ParseContext, size_t N>
|
||||
FMT_CONSTEXPR auto do_parse(ParseContext& ctx,
|
||||
std::integral_constant<size_t, N>)
|
||||
-> decltype(ctx.begin()) {
|
||||
auto end = ctx.begin();
|
||||
#if FMT_TUPLE_JOIN_SPECIFIERS
|
||||
end = std::get<sizeof...(T) - N>(formatters_).parse(ctx);
|
||||
if (N > 1) {
|
||||
auto end1 = do_parse(ctx, std::integral_constant<size_t, N - 1>());
|
||||
if (end != end1)
|
||||
FMT_THROW(format_error("incompatible format specs for tuple elements"));
|
||||
}
|
||||
#endif
|
||||
return end;
|
||||
}
|
||||
|
||||
template <typename FormatContext>
|
||||
typename FormatContext::iterator format_args(
|
||||
const tuple_arg_join<Char, T...>&, FormatContext& ctx) {
|
||||
// NOTE: for compilers that support C++17, this empty function instantiation
|
||||
// can be replaced with a constexpr branch in the variadic overload.
|
||||
auto do_format(const tuple_join_view<Char, T...>&, FormatContext& ctx,
|
||||
std::integral_constant<size_t, 0>) const ->
|
||||
typename FormatContext::iterator {
|
||||
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) {
|
||||
using base = formatter<typename std::decay<Arg>::type, Char>;
|
||||
auto out = ctx.out();
|
||||
out = base{}.format(arg, ctx);
|
||||
if (sizeof...(Args) > 0) {
|
||||
template <typename FormatContext, size_t N>
|
||||
auto do_format(const tuple_join_view<Char, T...>& value, FormatContext& ctx,
|
||||
std::integral_constant<size_t, N>) const ->
|
||||
typename FormatContext::iterator {
|
||||
auto out = std::get<sizeof...(T) - N>(formatters_)
|
||||
.format(std::get<sizeof...(T) - N>(value.tuple), ctx);
|
||||
if (N > 1) {
|
||||
out = std::copy(value.sep.begin(), value.sep.end(), out);
|
||||
ctx.advance_to(out);
|
||||
return format_args(value, ctx, args...);
|
||||
return do_format(value, ctx, std::integral_constant<size_t, N - 1>());
|
||||
}
|
||||
return out;
|
||||
}
|
||||
};
|
||||
|
||||
FMT_MODULE_EXPORT_BEGIN
|
||||
|
||||
/**
|
||||
\rst
|
||||
Returns an object that formats `tuple` with elements separated by `sep`.
|
||||
@@ -348,14 +758,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 +782,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_XCHAR_H_
|
||||
#define FMT_XCHAR_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), fmt::make_wformat_args(args...));
|
||||
}
|
||||
|
||||
template <typename... T> void print(wformat_string<T...> fmt, T&&... args) {
|
||||
return vprint(wstring_view(fmt), 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_XCHAR_H_
|
||||
22
include/spdlog/fmt/chrono.h
Normal file
22
include/spdlog/fmt/chrono.h
Normal file
@@ -0,0 +1,22 @@
|
||||
//
|
||||
// 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_USE_STD_FORMAT)
|
||||
# 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
|
||||
#endif
|
||||
22
include/spdlog/fmt/compile.h
Normal file
22
include/spdlog/fmt/compile.h
Normal file
@@ -0,0 +1,22 @@
|
||||
//
|
||||
// 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 compile-time support
|
||||
//
|
||||
|
||||
#if !defined(SPDLOG_USE_STD_FORMAT)
|
||||
# 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
|
||||
#endif
|
||||
@@ -10,16 +10,20 @@
|
||||
// By default spdlog include its own copy.
|
||||
//
|
||||
|
||||
#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_USE_STD_FORMAT) // SPDLOG_USE_STD_FORMAT is defined - use std::format
|
||||
# include <format>
|
||||
#elif !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
|
||||
// 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
|
||||
|
||||
@@ -8,13 +8,15 @@
|
||||
// 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/ostream.h>
|
||||
#else
|
||||
#include <fmt/ostream.h>
|
||||
#if !defined(SPDLOG_USE_STD_FORMAT)
|
||||
# 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>
|
||||
# else
|
||||
# include <fmt/ostream.h>
|
||||
# endif
|
||||
#endif
|
||||
|
||||
22
include/spdlog/fmt/ranges.h
Normal file
22
include/spdlog/fmt/ranges.h
Normal file
@@ -0,0 +1,22 @@
|
||||
//
|
||||
// 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 ranges support
|
||||
//
|
||||
|
||||
#if !defined(SPDLOG_USE_STD_FORMAT)
|
||||
# if !defined(SPDLOG_FMT_EXTERNAL)
|
||||
# ifdef SPDLOG_HEADER_ONLY
|
||||
# ifndef FMT_HEADER_ONLY
|
||||
# define FMT_HEADER_ONLY
|
||||
# endif
|
||||
# endif
|
||||
# include <spdlog/fmt/bundled/ranges.h>
|
||||
# else
|
||||
# include <fmt/ranges.h>
|
||||
# endif
|
||||
#endif
|
||||
22
include/spdlog/fmt/xchar.h
Normal file
22
include/spdlog/fmt/xchar.h
Normal file
@@ -0,0 +1,22 @@
|
||||
//
|
||||
// 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 xchar support
|
||||
//
|
||||
|
||||
#if !defined(SPDLOG_USE_STD_FORMAT)
|
||||
# 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
|
||||
#endif
|
||||
@@ -11,4 +11,8 @@ namespace sinks {
|
||||
class sink;
|
||||
}
|
||||
|
||||
namespace level {
|
||||
enum level_enum : int;
|
||||
}
|
||||
|
||||
} // namespace spdlog
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
#pragma once
|
||||
|
||||
#ifndef SPDLOG_HEADER_ONLY
|
||||
#include <spdlog/logger.h>
|
||||
# include <spdlog/logger.h>
|
||||
#endif
|
||||
|
||||
#include <spdlog/sinks/sink.h>
|
||||
@@ -185,7 +185,7 @@ SPDLOG_INLINE void logger::sink_it_(const details::log_msg &msg)
|
||||
{
|
||||
sink->log(msg);
|
||||
}
|
||||
SPDLOG_LOGGER_CATCH()
|
||||
SPDLOG_LOGGER_CATCH(msg.source)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -203,7 +203,7 @@ SPDLOG_INLINE void logger::flush_()
|
||||
{
|
||||
sink->flush();
|
||||
}
|
||||
SPDLOG_LOGGER_CATCH()
|
||||
SPDLOG_LOGGER_CATCH(source_loc())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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,34 @@
|
||||
#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(location) \
|
||||
catch (const std::exception &ex) \
|
||||
{ \
|
||||
if (location.filename) \
|
||||
{ \
|
||||
err_handler_(fmt_lib::format("{} [{}({})]", ex.what(), location.filename, location.line)); \
|
||||
} \
|
||||
else \
|
||||
{ \
|
||||
err_handler_(ex.what()); \
|
||||
} \
|
||||
} \
|
||||
catch (...) \
|
||||
{ \
|
||||
err_handler_("Rethrowing unknown exception in logger"); \
|
||||
throw; \
|
||||
}
|
||||
#else
|
||||
#define SPDLOG_LOGGER_CATCH()
|
||||
# define SPDLOG_LOGGER_CATCH(location)
|
||||
#endif
|
||||
|
||||
namespace spdlog {
|
||||
@@ -70,68 +82,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, format_string_t<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, format_string_t<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>
|
||||
@@ -140,11 +102,11 @@ public:
|
||||
log(source_loc{}, lvl, msg);
|
||||
}
|
||||
|
||||
// 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>
|
||||
// T cannot be statically converted to format string (including string_view/wstring_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, string_view_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)
|
||||
@@ -178,15 +140,127 @@ 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(format_string_t<Args...> fmt, Args &&... args)
|
||||
{
|
||||
log(loc, lvl, "{}", msg);
|
||||
log(level::trace, fmt, std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
template<typename... Args>
|
||||
void debug(format_string_t<Args...> fmt, Args &&... args)
|
||||
{
|
||||
log(level::debug, fmt, std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
template<typename... Args>
|
||||
void info(format_string_t<Args...> fmt, Args &&... args)
|
||||
{
|
||||
log(level::info, fmt, std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
template<typename... Args>
|
||||
void warn(format_string_t<Args...> fmt, Args &&... args)
|
||||
{
|
||||
log(level::warn, fmt, std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
template<typename... Args>
|
||||
void error(format_string_t<Args...> fmt, Args &&... args)
|
||||
{
|
||||
log(level::err, fmt, std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
template<typename... Args>
|
||||
void critical(format_string_t<Args...> fmt, Args &&... args)
|
||||
{
|
||||
log(level::critical, fmt, std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
#ifdef SPDLOG_WCHAR_TO_UTF8_SUPPORT
|
||||
template<typename... Args>
|
||||
void log(source_loc loc, level::level_enum lvl, wformat_string_t<Args...> fmt, Args &&... args)
|
||||
{
|
||||
log_(loc, lvl, fmt, std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
template<typename... Args>
|
||||
void log(level::level_enum lvl, wformat_string_t<Args...> fmt, Args &&... args)
|
||||
{
|
||||
log(source_loc{}, lvl, fmt, std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
void log(log_clock::time_point log_time, source_loc loc, level::level_enum lvl, wstring_view_t msg)
|
||||
{
|
||||
bool log_enabled = should_log(lvl);
|
||||
bool traceback_enabled = tracer_.enabled();
|
||||
if (!log_enabled && !traceback_enabled)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
memory_buf_t buf;
|
||||
details::os::wstr_to_utf8buf(wstring_view_t(msg.data(), msg.size()), buf);
|
||||
details::log_msg log_msg(log_time, loc, name_, lvl, string_view_t(buf.data(), buf.size()));
|
||||
log_it_(log_msg, log_enabled, traceback_enabled);
|
||||
}
|
||||
|
||||
void log(source_loc loc, level::level_enum lvl, wstring_view_t msg)
|
||||
{
|
||||
bool log_enabled = should_log(lvl);
|
||||
bool traceback_enabled = tracer_.enabled();
|
||||
if (!log_enabled && !traceback_enabled)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
memory_buf_t buf;
|
||||
details::os::wstr_to_utf8buf(wstring_view_t(msg.data(), msg.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);
|
||||
}
|
||||
|
||||
void log(level::level_enum lvl, wstring_view_t msg)
|
||||
{
|
||||
log(source_loc{}, lvl, msg);
|
||||
}
|
||||
|
||||
template<typename... Args>
|
||||
void trace(wformat_string_t<Args...> fmt, Args &&... args)
|
||||
{
|
||||
log(level::trace, fmt, std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
template<typename... Args>
|
||||
void debug(wformat_string_t<Args...> fmt, Args &&... args)
|
||||
{
|
||||
log(level::debug, fmt, std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
template<typename... Args>
|
||||
void info(wformat_string_t<Args...> fmt, Args &&... args)
|
||||
{
|
||||
log(level::info, fmt, std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
template<typename... Args>
|
||||
void warn(wformat_string_t<Args...> fmt, Args &&... args)
|
||||
{
|
||||
log(level::warn, fmt, std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
template<typename... Args>
|
||||
void error(wformat_string_t<Args...> fmt, Args &&... args)
|
||||
{
|
||||
log(level::err, fmt, std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
template<typename... Args>
|
||||
void critical(wformat_string_t<Args...> fmt, Args &&... args)
|
||||
{
|
||||
log(level::critical, fmt, std::forward<Args>(args)...);
|
||||
}
|
||||
#endif
|
||||
|
||||
template<typename T>
|
||||
void trace(const T &msg)
|
||||
{
|
||||
@@ -223,99 +297,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 +351,80 @@ 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
|
||||
{
|
||||
#ifdef SPDLOG_USE_STD_FORMAT
|
||||
memory_buf_t buf = std::vformat(fmt, std::make_format_args(std::forward<Args>(args)...));
|
||||
#else
|
||||
memory_buf_t buf;
|
||||
fmt::detail::vformat_to(buf, fmt, fmt::make_format_args(std::forward<Args>(args)...));
|
||||
#endif
|
||||
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(loc)
|
||||
}
|
||||
|
||||
#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
|
||||
;
|
||||
# ifdef SPDLOG_USE_STD_FORMAT
|
||||
wmemory_buf_t wbuf = std::vformat(fmt, std::make_wformat_args(std::forward<Args>(args)...));
|
||||
# else
|
||||
wmemory_buf_t wbuf;
|
||||
fmt::detail::vformat_to(wbuf, fmt, fmt::make_format_args<fmt::wformat_context>(std::forward<Args>(args)...));
|
||||
# endif
|
||||
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(loc)
|
||||
}
|
||||
|
||||
// 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(loc)
|
||||
}
|
||||
|
||||
#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 +443,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);
|
||||
}
|
||||
@@ -764,6 +766,7 @@ public:
|
||||
{
|
||||
if (msg.source.empty())
|
||||
{
|
||||
ScopedPadder p(0, padinfo_, dest);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -798,6 +801,7 @@ public:
|
||||
{
|
||||
if (msg.source.empty())
|
||||
{
|
||||
ScopedPadder p(0, padinfo_, dest);
|
||||
return;
|
||||
}
|
||||
size_t text_size = padinfo_.enabled() ? std::char_traits<char>::length(msg.source.filename) : 0;
|
||||
@@ -814,16 +818,37 @@ 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
|
||||
{
|
||||
if (msg.source.empty())
|
||||
{
|
||||
ScopedPadder p(0, padinfo_, dest);
|
||||
return;
|
||||
}
|
||||
auto filename = basename(msg.source.filename);
|
||||
@@ -845,6 +870,7 @@ public:
|
||||
{
|
||||
if (msg.source.empty())
|
||||
{
|
||||
ScopedPadder p(0, padinfo_, dest);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -867,6 +893,7 @@ public:
|
||||
{
|
||||
if (msg.source.empty())
|
||||
{
|
||||
ScopedPadder p(0, padinfo_, dest);
|
||||
return;
|
||||
}
|
||||
size_t text_size = padinfo_.enabled() ? std::char_traits<char>::length(msg.source.funcname) : 0;
|
||||
@@ -903,7 +930,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:
|
||||
@@ -997,6 +1024,7 @@ SPDLOG_INLINE pattern_formatter::pattern_formatter(
|
||||
: pattern_(std::move(pattern))
|
||||
, eol_(std::move(eol))
|
||||
, pattern_time_type_(time_type)
|
||||
, need_localtime_(false)
|
||||
, last_log_secs_(0)
|
||||
, custom_handlers_(std::move(custom_user_flags))
|
||||
{
|
||||
@@ -1009,6 +1037,7 @@ SPDLOG_INLINE pattern_formatter::pattern_formatter(pattern_time_type time_type,
|
||||
: pattern_("%+")
|
||||
, eol_(std::move(eol))
|
||||
, pattern_time_type_(time_type)
|
||||
, need_localtime_(true)
|
||||
, last_log_secs_(0)
|
||||
{
|
||||
std::memset(&cached_tm_, 0, sizeof(cached_tm_));
|
||||
@@ -1027,11 +1056,14 @@ SPDLOG_INLINE std::unique_ptr<formatter> pattern_formatter::clone() const
|
||||
|
||||
SPDLOG_INLINE void pattern_formatter::format(const details::log_msg &msg, memory_buf_t &dest)
|
||||
{
|
||||
auto secs = std::chrono::duration_cast<std::chrono::seconds>(msg.time.time_since_epoch());
|
||||
if (secs != last_log_secs_)
|
||||
if (need_localtime_)
|
||||
{
|
||||
cached_tm_ = get_time_(msg);
|
||||
last_log_secs_ = secs;
|
||||
const auto secs = std::chrono::duration_cast<std::chrono::seconds>(msg.time.time_since_epoch());
|
||||
if (secs != last_log_secs_)
|
||||
{
|
||||
cached_tm_ = get_time_(msg);
|
||||
last_log_secs_ = secs;
|
||||
}
|
||||
}
|
||||
|
||||
for (auto &f : formatters_)
|
||||
@@ -1045,6 +1077,7 @@ SPDLOG_INLINE void pattern_formatter::format(const details::log_msg &msg, memory
|
||||
SPDLOG_INLINE void pattern_formatter::set_pattern(std::string pattern)
|
||||
{
|
||||
pattern_ = std::move(pattern);
|
||||
need_localtime_ = false;
|
||||
compile_pattern_(pattern_);
|
||||
}
|
||||
|
||||
@@ -1075,6 +1108,7 @@ SPDLOG_INLINE void pattern_formatter::handle_flag_(char flag, details::padding_i
|
||||
{
|
||||
case ('+'): // default formatter
|
||||
formatters_.push_back(details::make_unique<details::full_formatter>(padding));
|
||||
need_localtime_ = true;
|
||||
break;
|
||||
|
||||
case 'n': // logger name
|
||||
@@ -1099,60 +1133,74 @@ SPDLOG_INLINE void pattern_formatter::handle_flag_(char flag, details::padding_i
|
||||
|
||||
case ('a'): // weekday
|
||||
formatters_.push_back(details::make_unique<details::a_formatter<Padder>>(padding));
|
||||
need_localtime_ = true;
|
||||
break;
|
||||
|
||||
case ('A'): // short weekday
|
||||
formatters_.push_back(details::make_unique<details::A_formatter<Padder>>(padding));
|
||||
need_localtime_ = true;
|
||||
break;
|
||||
|
||||
case ('b'):
|
||||
case ('h'): // month
|
||||
formatters_.push_back(details::make_unique<details::b_formatter<Padder>>(padding));
|
||||
need_localtime_ = true;
|
||||
break;
|
||||
|
||||
case ('B'): // short month
|
||||
formatters_.push_back(details::make_unique<details::B_formatter<Padder>>(padding));
|
||||
need_localtime_ = true;
|
||||
break;
|
||||
|
||||
case ('c'): // datetime
|
||||
formatters_.push_back(details::make_unique<details::c_formatter<Padder>>(padding));
|
||||
need_localtime_ = true;
|
||||
break;
|
||||
|
||||
case ('C'): // year 2 digits
|
||||
formatters_.push_back(details::make_unique<details::C_formatter<Padder>>(padding));
|
||||
need_localtime_ = true;
|
||||
break;
|
||||
|
||||
case ('Y'): // year 4 digits
|
||||
formatters_.push_back(details::make_unique<details::Y_formatter<Padder>>(padding));
|
||||
need_localtime_ = true;
|
||||
break;
|
||||
|
||||
case ('D'):
|
||||
case ('x'): // datetime MM/DD/YY
|
||||
formatters_.push_back(details::make_unique<details::D_formatter<Padder>>(padding));
|
||||
need_localtime_ = true;
|
||||
break;
|
||||
|
||||
case ('m'): // month 1-12
|
||||
formatters_.push_back(details::make_unique<details::m_formatter<Padder>>(padding));
|
||||
need_localtime_ = true;
|
||||
break;
|
||||
|
||||
case ('d'): // day of month 1-31
|
||||
formatters_.push_back(details::make_unique<details::d_formatter<Padder>>(padding));
|
||||
need_localtime_ = true;
|
||||
break;
|
||||
|
||||
case ('H'): // hours 24
|
||||
formatters_.push_back(details::make_unique<details::H_formatter<Padder>>(padding));
|
||||
need_localtime_ = true;
|
||||
break;
|
||||
|
||||
case ('I'): // hours 12
|
||||
formatters_.push_back(details::make_unique<details::I_formatter<Padder>>(padding));
|
||||
need_localtime_ = true;
|
||||
break;
|
||||
|
||||
case ('M'): // minutes
|
||||
formatters_.push_back(details::make_unique<details::M_formatter<Padder>>(padding));
|
||||
need_localtime_ = true;
|
||||
break;
|
||||
|
||||
case ('S'): // seconds
|
||||
formatters_.push_back(details::make_unique<details::S_formatter<Padder>>(padding));
|
||||
need_localtime_ = true;
|
||||
break;
|
||||
|
||||
case ('e'): // milliseconds
|
||||
@@ -1173,23 +1221,28 @@ SPDLOG_INLINE void pattern_formatter::handle_flag_(char flag, details::padding_i
|
||||
|
||||
case ('p'): // am/pm
|
||||
formatters_.push_back(details::make_unique<details::p_formatter<Padder>>(padding));
|
||||
need_localtime_ = true;
|
||||
break;
|
||||
|
||||
case ('r'): // 12 hour clock 02:55:02 pm
|
||||
formatters_.push_back(details::make_unique<details::r_formatter<Padder>>(padding));
|
||||
need_localtime_ = true;
|
||||
break;
|
||||
|
||||
case ('R'): // 24-hour HH:MM time
|
||||
formatters_.push_back(details::make_unique<details::R_formatter<Padder>>(padding));
|
||||
need_localtime_ = true;
|
||||
break;
|
||||
|
||||
case ('T'):
|
||||
case ('X'): // ISO 8601 time format (HH:MM:SS)
|
||||
formatters_.push_back(details::make_unique<details::T_formatter<Padder>>(padding));
|
||||
need_localtime_ = true;
|
||||
break;
|
||||
|
||||
case ('z'): // timezone
|
||||
formatters_.push_back(details::make_unique<details::z_formatter<Padder>>(padding));
|
||||
need_localtime_ = true;
|
||||
break;
|
||||
|
||||
case ('P'): // pid
|
||||
@@ -1246,9 +1299,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 +1338,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;
|
||||
}
|
||||
|
||||
@@ -1305,7 +1373,6 @@ SPDLOG_INLINE details::padding_info pattern_formatter::handle_padspec_(std::stri
|
||||
{
|
||||
truncate = false;
|
||||
}
|
||||
|
||||
return details::padding_info{std::min<size_t>(width, max_width), side, truncate};
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
@@ -68,7 +68,7 @@ class SPDLOG_API custom_flag_formatter : public details::flag_formatter
|
||||
public:
|
||||
virtual std::unique_ptr<custom_flag_formatter> clone() const = 0;
|
||||
|
||||
void set_padding_info(details::padding_info padding)
|
||||
void set_padding_info(const details::padding_info &padding)
|
||||
{
|
||||
flag_formatter::padinfo_ = padding;
|
||||
}
|
||||
@@ -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);
|
||||
@@ -103,6 +103,7 @@ private:
|
||||
std::string pattern_;
|
||||
std::string eol_;
|
||||
pattern_time_type pattern_time_type_;
|
||||
bool need_localtime_;
|
||||
std::tm cached_tm_;
|
||||
std::chrono::seconds last_log_secs_;
|
||||
std::vector<std::unique_ptr<details::flag_formatter>> formatters_;
|
||||
@@ -122,5 +123,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>
|
||||
@@ -34,7 +34,7 @@ template<typename ConsoleMutex>
|
||||
SPDLOG_INLINE void ansicolor_sink<ConsoleMutex>::set_color(level::level_enum color_level, string_view_t color)
|
||||
{
|
||||
std::lock_guard<mutex_t> lock(mutex_);
|
||||
colors_[color_level] = to_string_(color);
|
||||
colors_[static_cast<size_t>(color_level)] = to_string_(color);
|
||||
}
|
||||
|
||||
template<typename ConsoleMutex>
|
||||
@@ -52,7 +52,7 @@ SPDLOG_INLINE void ansicolor_sink<ConsoleMutex>::log(const details::log_msg &msg
|
||||
// before color range
|
||||
print_range_(formatted, 0, msg.color_range_start);
|
||||
// in color range
|
||||
print_ccode_(colors_[msg.level]);
|
||||
print_ccode_(colors_[static_cast<size_t>(msg.level)]);
|
||||
print_range_(formatted, msg.color_range_start, msg.color_range_end);
|
||||
print_ccode_(reset);
|
||||
// after color range
|
||||
@@ -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>
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
namespace spdlog {
|
||||
namespace sinks {
|
||||
template<typename Mutex>
|
||||
class base_sink : public sink
|
||||
class SPDLOG_API base_sink : public sink
|
||||
{
|
||||
public:
|
||||
base_sink();
|
||||
@@ -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>
|
||||
@@ -14,7 +14,8 @@ namespace spdlog {
|
||||
namespace sinks {
|
||||
|
||||
template<typename Mutex>
|
||||
SPDLOG_INLINE basic_file_sink<Mutex>::basic_file_sink(const filename_t &filename, bool truncate)
|
||||
SPDLOG_INLINE basic_file_sink<Mutex>::basic_file_sink(const filename_t &filename, bool truncate, const file_event_handlers &event_handlers)
|
||||
: file_helper_{event_handlers}
|
||||
{
|
||||
file_helper_.open(filename, truncate);
|
||||
}
|
||||
|
||||
@@ -20,7 +20,7 @@ template<typename Mutex>
|
||||
class basic_file_sink final : public base_sink<Mutex>
|
||||
{
|
||||
public:
|
||||
explicit basic_file_sink(const filename_t &filename, bool truncate = false);
|
||||
explicit basic_file_sink(const filename_t &filename, bool truncate = false, const file_event_handlers &event_handlers = {});
|
||||
const filename_t &filename() const;
|
||||
|
||||
protected:
|
||||
@@ -40,19 +40,21 @@ using basic_file_sink_st = basic_file_sink<details::null_mutex>;
|
||||
// factory functions
|
||||
//
|
||||
template<typename Factory = spdlog::synchronous_factory>
|
||||
inline std::shared_ptr<logger> basic_logger_mt(const std::string &logger_name, const filename_t &filename, bool truncate = false)
|
||||
inline std::shared_ptr<logger> basic_logger_mt(
|
||||
const std::string &logger_name, const filename_t &filename, bool truncate = false, const file_event_handlers &event_handlers = {})
|
||||
{
|
||||
return Factory::template create<sinks::basic_file_sink_mt>(logger_name, filename, truncate);
|
||||
return Factory::template create<sinks::basic_file_sink_mt>(logger_name, filename, truncate, event_handlers);
|
||||
}
|
||||
|
||||
template<typename Factory = spdlog::synchronous_factory>
|
||||
inline std::shared_ptr<logger> basic_logger_st(const std::string &logger_name, const filename_t &filename, bool truncate = false)
|
||||
inline std::shared_ptr<logger> basic_logger_st(
|
||||
const std::string &logger_name, const filename_t &filename, bool truncate = false, const file_event_handlers &event_handlers = {})
|
||||
{
|
||||
return Factory::template create<sinks::basic_file_sink_st>(logger_name, filename, truncate);
|
||||
return Factory::template create<sinks::basic_file_sink_st>(logger_name, filename, truncate, event_handlers);
|
||||
}
|
||||
|
||||
} // 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>
|
||||
@@ -31,11 +32,80 @@ struct daily_filename_calculator
|
||||
{
|
||||
filename_t basename, ext;
|
||||
std::tie(basename, ext) = details::file_helper::split_by_extension(filename);
|
||||
return fmt::format(
|
||||
return fmt_lib::format(
|
||||
SPDLOG_FILENAME_T("{}_{:04d}-{:02d}-{:02d}{}"), basename, now_tm.tm_year + 1900, now_tm.tm_mon + 1, now_tm.tm_mday, ext);
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
* 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)
|
||||
{
|
||||
#ifdef SPDLOG_USE_STD_FORMAT
|
||||
// adapted from fmtlib: https://github.com/fmtlib/fmt/blob/8.0.1/include/fmt/chrono.h#L522-L546
|
||||
|
||||
filename_t tm_format;
|
||||
tm_format.append(filename);
|
||||
// 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(' ');
|
||||
|
||||
const size_t MIN_SIZE = 10;
|
||||
filename_t buf;
|
||||
buf.resize(MIN_SIZE);
|
||||
for (;;)
|
||||
{
|
||||
size_t count = strftime(buf.data(), buf.size(), tm_format.c_str(), &now_tm);
|
||||
if (count != 0)
|
||||
{
|
||||
// Remove the extra space.
|
||||
buf.resize(count - 1);
|
||||
break;
|
||||
}
|
||||
buf.resize(buf.size() * 2);
|
||||
}
|
||||
|
||||
return buf;
|
||||
#else
|
||||
// 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 doesn't 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
|
||||
#endif
|
||||
}
|
||||
|
||||
private:
|
||||
#if defined __GNUC__
|
||||
# pragma GCC diagnostic push
|
||||
# pragma GCC diagnostic ignored "-Wformat-nonliteral"
|
||||
#endif
|
||||
|
||||
static size_t strftime(char *str, size_t count, const char *format, const std::tm *time)
|
||||
{
|
||||
return std::strftime(str, count, format, time);
|
||||
}
|
||||
|
||||
static size_t strftime(wchar_t *str, size_t count, const wchar_t *format, const std::tm *time)
|
||||
{
|
||||
return std::wcsftime(str, count, format, time);
|
||||
}
|
||||
|
||||
#if defined(__GNUC__)
|
||||
# pragma GCC diagnostic pop
|
||||
#endif
|
||||
};
|
||||
|
||||
/*
|
||||
* Rotating file sink based on date.
|
||||
* If truncate != false , the created file will be truncated.
|
||||
@@ -46,10 +116,12 @@ class daily_file_sink final : public base_sink<Mutex>
|
||||
{
|
||||
public:
|
||||
// create daily file sink which rotates on given time
|
||||
daily_file_sink(filename_t base_filename, int rotation_hour, int rotation_minute, bool truncate = false, uint16_t max_files = 0)
|
||||
daily_file_sink(filename_t base_filename, int rotation_hour, int rotation_minute, bool truncate = false, uint16_t max_files = 0,
|
||||
const file_event_handlers &event_handlers = {})
|
||||
: base_filename_(std::move(base_filename))
|
||||
, rotation_h_(rotation_hour)
|
||||
, rotation_m_(rotation_minute)
|
||||
, file_helper_{event_handlers}
|
||||
, truncate_(truncate)
|
||||
, max_files_(max_files)
|
||||
, filenames_q_()
|
||||
@@ -182,6 +254,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
|
||||
|
||||
@@ -189,16 +263,32 @@ using daily_file_sink_st = daily_file_sink<details::null_mutex>;
|
||||
// factory functions
|
||||
//
|
||||
template<typename Factory = spdlog::synchronous_factory>
|
||||
inline std::shared_ptr<logger> daily_logger_mt(
|
||||
const std::string &logger_name, const filename_t &filename, int hour = 0, int minute = 0, bool truncate = false, uint16_t max_files = 0)
|
||||
inline std::shared_ptr<logger> daily_logger_mt(const std::string &logger_name, const filename_t &filename, int hour = 0, int minute = 0,
|
||||
bool truncate = false, uint16_t max_files = 0, const file_event_handlers &event_handlers = {})
|
||||
{
|
||||
return Factory::template create<sinks::daily_file_sink_mt>(logger_name, filename, hour, minute, truncate, max_files);
|
||||
return Factory::template create<sinks::daily_file_sink_mt>(logger_name, filename, hour, minute, truncate, max_files, event_handlers);
|
||||
}
|
||||
|
||||
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)
|
||||
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, const file_event_handlers &event_handlers = {})
|
||||
{
|
||||
return Factory::template create<sinks::daily_file_sink_st>(logger_name, filename, hour, minute, truncate, max_files);
|
||||
return Factory::template create<sinks::daily_file_format_sink_mt>(
|
||||
logger_name, filename, hour, minute, truncate, max_files, event_handlers);
|
||||
}
|
||||
|
||||
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, const file_event_handlers &event_handlers = {})
|
||||
{
|
||||
return Factory::template create<sinks::daily_file_sink_st>(logger_name, filename, hour, minute, truncate, max_files, event_handlers);
|
||||
}
|
||||
|
||||
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, const file_event_handlers &event_handlers = {})
|
||||
{
|
||||
return Factory::template create<sinks::daily_file_format_sink_st>(
|
||||
logger_name, filename, hour, minute, truncate, max_files, event_handlers);
|
||||
}
|
||||
} // 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
|
||||
|
||||
196
include/spdlog/sinks/hourly_file_sink.h
Normal file
196
include/spdlog/sinks/hourly_file_sink.h
Normal file
@@ -0,0 +1,196 @@
|
||||
// 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_lib::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, const file_event_handlers &event_handlers = {})
|
||||
: base_filename_(std::move(base_filename))
|
||||
, file_helper_{event_handlers}
|
||||
, 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, const file_event_handlers &event_handlers = {})
|
||||
{
|
||||
return Factory::template create<sinks::hourly_file_sink_mt>(logger_name, filename, truncate, max_files, event_handlers);
|
||||
}
|
||||
|
||||
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, const file_event_handlers &event_handlers = {})
|
||||
{
|
||||
return Factory::template create<sinks::hourly_file_sink_st>(logger_name, filename, truncate, max_files, event_handlers);
|
||||
}
|
||||
} // namespace spdlog
|
||||
99
include/spdlog/sinks/mongo_sink.h
Normal file
99
include/spdlog/sinks/mongo_sink.h
Normal file
@@ -0,0 +1,99 @@
|
||||
// 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;
|
||||
};
|
||||
template<>
|
||||
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,15 +23,18 @@ 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);
|
||||
# ifdef SPDLOG_USE_STD_FORMAT
|
||||
OutputDebugStringA(formatted.c_str());
|
||||
# else
|
||||
OutputDebugStringA(fmt::to_string(formatted).c_str());
|
||||
# endif
|
||||
}
|
||||
|
||||
void flush_() override {}
|
||||
|
||||
102
include/spdlog/sinks/qt_sinks.h
Normal file
102
include/spdlog/sinks/qt_sinks.h
Normal file
@@ -0,0 +1,102 @@
|
||||
// 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, 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
|
||||
@@ -50,7 +50,11 @@ public:
|
||||
{
|
||||
memory_buf_t formatted;
|
||||
base_sink<Mutex>::formatter_->format(q_.at(i), formatted);
|
||||
#ifdef SPDLOG_USE_STD_FORMAT
|
||||
ret.push_back(std::move(formatted));
|
||||
#else
|
||||
ret.push_back(fmt::to_string(formatted));
|
||||
#endif
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
@@ -25,16 +25,27 @@ namespace sinks {
|
||||
|
||||
template<typename Mutex>
|
||||
SPDLOG_INLINE rotating_file_sink<Mutex>::rotating_file_sink(
|
||||
filename_t base_filename, std::size_t max_size, std::size_t max_files, bool rotate_on_open)
|
||||
filename_t base_filename, std::size_t max_size, std::size_t max_files, bool rotate_on_open, const file_event_handlers &event_handlers)
|
||||
: base_filename_(std::move(base_filename))
|
||||
, max_size_(max_size)
|
||||
, max_files_(max_files)
|
||||
, file_helper_{event_handlers}
|
||||
{
|
||||
if (max_size == 0)
|
||||
{
|
||||
throw_spdlog_ex("rotating sink constructor: max_size arg cannot be zero");
|
||||
}
|
||||
|
||||
if (max_files > 200000)
|
||||
{
|
||||
throw_spdlog_ex("rotating sink constructor: max_files arg cannot exceed 200000");
|
||||
}
|
||||
file_helper_.open(calc_filename(base_filename_, 0));
|
||||
current_size_ = file_helper_.size(); // expensive. called only once
|
||||
if (rotate_on_open && current_size_ > 0)
|
||||
{
|
||||
rotate_();
|
||||
current_size_ = 0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -50,7 +61,7 @@ SPDLOG_INLINE filename_t rotating_file_sink<Mutex>::calc_filename(const filename
|
||||
|
||||
filename_t basename, ext;
|
||||
std::tie(basename, ext) = details::file_helper::split_by_extension(filename);
|
||||
return fmt::format(SPDLOG_FILENAME_T("{}.{}{}"), basename, index, ext);
|
||||
return fmt_lib::format(SPDLOG_FILENAME_T("{}.{}{}"), basename, index, ext);
|
||||
}
|
||||
|
||||
template<typename Mutex>
|
||||
@@ -65,13 +76,22 @@ SPDLOG_INLINE void rotating_file_sink<Mutex>::sink_it_(const details::log_msg &m
|
||||
{
|
||||
memory_buf_t formatted;
|
||||
base_sink<Mutex>::formatter_->format(msg, formatted);
|
||||
current_size_ += formatted.size();
|
||||
if (current_size_ > max_size_)
|
||||
auto new_size = current_size_ + formatted.size();
|
||||
|
||||
// rotate if the new estimated file size exceeds max size.
|
||||
// rotate only if the real size > 0 to better deal with full disk (see issue #2261).
|
||||
// we only check the real size when new_size > max_size_ because it is relatively expensive.
|
||||
if (new_size > max_size_)
|
||||
{
|
||||
rotate_();
|
||||
current_size_ = formatted.size();
|
||||
file_helper_.flush();
|
||||
if (file_helper_.size() > 0)
|
||||
{
|
||||
rotate_();
|
||||
new_size = formatted.size();
|
||||
}
|
||||
}
|
||||
file_helper_.write(formatted);
|
||||
current_size_ = new_size;
|
||||
}
|
||||
|
||||
template<typename Mutex>
|
||||
@@ -90,6 +110,7 @@ SPDLOG_INLINE void rotating_file_sink<Mutex>::rotate_()
|
||||
{
|
||||
using details::os::filename_to_str;
|
||||
using details::os::path_exists;
|
||||
|
||||
file_helper_.close();
|
||||
for (auto i = max_files_; i > 0; --i)
|
||||
{
|
||||
|
||||
@@ -22,7 +22,8 @@ template<typename Mutex>
|
||||
class rotating_file_sink final : public base_sink<Mutex>
|
||||
{
|
||||
public:
|
||||
rotating_file_sink(filename_t base_filename, std::size_t max_size, std::size_t max_files, bool rotate_on_open = false);
|
||||
rotating_file_sink(filename_t base_filename, std::size_t max_size, std::size_t max_files, bool rotate_on_open = false,
|
||||
const file_event_handlers &event_handlers = {});
|
||||
static filename_t calc_filename(const filename_t &filename, std::size_t index);
|
||||
filename_t filename();
|
||||
|
||||
@@ -59,20 +60,22 @@ using rotating_file_sink_st = rotating_file_sink<details::null_mutex>;
|
||||
//
|
||||
|
||||
template<typename Factory = spdlog::synchronous_factory>
|
||||
inline std::shared_ptr<logger> rotating_logger_mt(
|
||||
const std::string &logger_name, const filename_t &filename, size_t max_file_size, size_t max_files, bool rotate_on_open = false)
|
||||
inline std::shared_ptr<logger> rotating_logger_mt(const std::string &logger_name, const filename_t &filename, size_t max_file_size,
|
||||
size_t max_files, bool rotate_on_open = false, const file_event_handlers &event_handlers = {})
|
||||
{
|
||||
return Factory::template create<sinks::rotating_file_sink_mt>(logger_name, filename, max_file_size, max_files, rotate_on_open);
|
||||
return Factory::template create<sinks::rotating_file_sink_mt>(
|
||||
logger_name, filename, max_file_size, max_files, rotate_on_open, event_handlers);
|
||||
}
|
||||
|
||||
template<typename Factory = spdlog::synchronous_factory>
|
||||
inline std::shared_ptr<logger> rotating_logger_st(
|
||||
const std::string &logger_name, const filename_t &filename, size_t max_file_size, size_t max_files, bool rotate_on_open = false)
|
||||
inline std::shared_ptr<logger> rotating_logger_st(const std::string &logger_name, const filename_t &filename, size_t max_file_size,
|
||||
size_t max_files, bool rotate_on_open = false, const file_event_handlers &event_handlers = {})
|
||||
{
|
||||
return Factory::template create<sinks::rotating_file_sink_st>(logger_name, filename, max_file_size, max_files, rotate_on_open);
|
||||
return Factory::template create<sinks::rotating_file_sink_st>(
|
||||
logger_name, filename, max_file_size, max_files, rotate_on_open, event_handlers);
|
||||
}
|
||||
} // 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 doesn't 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_ = reinterpret_cast<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 something 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>
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user