Compare commits

..

65 Commits

Author SHA1 Message Date
gabime
cbe9448650 version 1.8.1 2020-09-30 17:30:06 +03:00
gabime
5b345534dc Minor change in ifdef for clarity 2020-09-30 17:29:44 +03:00
Gabi Melman
c8dc318fb3 Update logger-inl.h 2020-09-29 02:23:03 +03:00
Gabi Melman
23cb1a1080 Update logger-inl.h 2020-09-29 02:20:43 +03:00
Gabi Melman
21cf8d7d3c Merge pull request #1685 from eddelbuettel/feature/r_use_of_reprintf
let R header defines switch to REprintf over fprintf(stdderr)
2020-09-29 02:18:35 +03:00
Dirk Eddelbuettel
3cf4d34094 let R header defines switch to REprintf over fprintf(stdderr) 2020-09-28 17:13:09 -05:00
Gabi Melman
16d78ae5db Update stdout_sinks-inl.h 2020-09-28 17:08:13 +03:00
gabime
62b4b7af83 Fix #1667 2020-09-28 13:39:31 +03:00
Gabi Melman
9799ecac6a Remove redundant size check before calling WriteFile 2020-09-27 21:27:58 +03:00
Gabi Melman
dccb766095 Fix warning about enum usage 2020-09-27 19:08:24 +03:00
gabime
c97983a91c Fix linux build 2020-09-27 18:42:27 +03:00
gabime
680fb07fd5 Updatd WriteFile usage 2020-09-27 18:34:01 +03:00
gabime
cfd0ea197c Simplify WriteFile() usage under windows 2020-09-27 18:32:08 +03:00
gabime
48d4ed9bc0 Fix #1675 2020-09-27 18:27:41 +03:00
gabime
3bed78356e Added cfg tests 2020-09-27 02:10:52 +03:00
gabime
8923922f30 Cleaned level loading from env var 2020-09-27 02:08:24 +03:00
gabime
7542e42e4f removed extra parentheses 2020-09-27 01:05:55 +03:00
Gabi Melman
7a9b23e4f4 Update registry-inl.h 2020-09-26 16:09:43 +03:00
gabime
47253ba2a1 Updated comment 2020-09-26 15:56:57 +03:00
gabime
69b54dd9e4 Merge branch 'v1.x' of https://github.com/gabime/spdlog into v1.x 2020-09-26 15:49:21 +03:00
gabime
36138617fc small readablilty update in registry 2020-09-26 15:36:57 +03:00
gabime
231ca50700 clang-format 2020-09-26 15:34:05 +03:00
gabime
c7613f3e91 Fixed #1680 2020-09-26 15:32:44 +03:00
gabime
05d5546eb1 Restore example 2020-09-26 15:30:45 +03:00
gabime
cefe67726e wip fix #1680 again 2020-09-26 15:06:53 +03:00
gabime
1ac2dcc537 wip fix #1680 again 2020-09-26 14:41:33 +03:00
gabime
3a68eecb28 Fix issue #1680 2020-09-25 18:19:50 +03:00
gabime
54a8259b42 Fix #1680 2020-09-25 15:03:13 +03:00
gabime
32b6f1619f Added tests for issue #1680 2020-09-25 14:44:01 +03:00
Gabi Melman
99b8c5d379 Update bin_to_hex.h 2020-09-24 10:04:07 +03:00
Gabi Melman
5deb7c55e1 Fix #1676 2020-09-21 03:31:19 +03:00
gabime
9cd25dd216 Remove un needed hasher declaration 2020-09-01 23:49:54 +03:00
gabime
4a9ccf7e38 Fixed chrono wrapper 2020-09-01 12:34:59 +03:00
gabime
2963b9f07f Updated comment 2020-09-01 12:24:11 +03:00
gabime
a4a9bc4d8e version 1.8.0 2020-09-01 12:05:23 +03:00
gabime
a16a029790 Added bundled fmt os.h file 2020-09-01 12:04:36 +03:00
Gabi Melman
97fea81599 Update README.md 2020-08-31 03:51:17 +03:00
gabime
ccffb6ecd6 Fix #1581 2020-08-30 20:44:42 +03:00
gabime
63b5a1a4d8 Fix #1581 2020-08-30 20:44:04 +03:00
gabime
dfc777803a enable the 'n' flag in for backward compatibility with fmt 6.x 2020-08-30 15:58:22 +03:00
gabime
934a9bb23e Fix issue #1617 2020-08-30 15:18:33 +03:00
gabime
7097f7a894 Updated stopwatch example to README.md 2020-08-29 03:50:48 +03:00
gabime
537fd7c4ba Added stopwatch example to README.md 2020-08-29 03:49:18 +03:00
gabime
d5048b8b0c Added stopwatch example to README.md 2020-08-29 03:48:24 +03:00
gabime
0348556aac Added stopwatch example to README.md 2020-08-29 03:46:29 +03:00
gabime
d6329b9dce Added some stopwatch tests 2020-08-29 03:25:16 +03:00
gabime
34f3d29d93 Added stopwatch support 2020-08-29 02:48:43 +03:00
gabime
cd701761f9 clang-format 2020-08-26 00:10:05 +03:00
gabime
23c2c00d69 Bumb fmt version to 7.0.3 2020-08-26 00:02:09 +03:00
Gabi Melman
fa501b46cf Merge pull request #1653 from gk6k6k/patch-1
Update README.md
2020-08-20 12:26:03 +03:00
gk6k6k
38dc0a5c5d Update README.md
Hi,
fedora yum is obsolite log time ago...
https://www.2daygeek.com/comparison-difference-between-dnf-vs-yum/

Works for fedora 31:

[root@Galleon ~]# uname -a
Linux Galleon 5.6.13-200.fc31.x86_64 #1 SMP Thu May 14 23:26:14 UTC 2020 x86_64 x86_64 x86_64 GNU/Linux
[root@Galleon ~]#
2020-08-20 11:01:49 +02:00
Gabi Melman
685cc4edbc Revert last commit 2020-08-20 00:00:54 +03:00
Gabi Melman
78369375e3 Update helpers-inl.h 2020-08-19 23:57:47 +03:00
Gabi Melman
6587058f74 Update argv.h 2020-08-19 23:56:56 +03:00
Gabi Melman
30f738e49a Merge pull request #1651 from bareya/make_load_env_levels_inline
Make load_env_levels inline function
2020-08-19 23:55:40 +03:00
Piotr Barejko
726ca01e5c Make load_env_levels inline function 2020-08-19 12:43:41 -07:00
Gabi Melman
83b40b8cda Merge pull request #1640 from dkruempe/v1.x
tcp_client.h bugfix for macOS and add tcp example
2020-08-06 13:06:08 +03:00
Dominik Krümpelmann
db0d0438ff tcp_client.h bugfix for macOS and add tcp example
-> fix the issue that the head is not compilable bc. of missing declaration
-> optimize if/else block in connection function. Else not needed bc. of break in if
2020-08-06 11:40:17 +02:00
Gabi Melman
3c527488e7 Update spdlog-inl.h 2020-07-24 13:06:29 +03:00
Gabi Melman
99abcf6ded Merge pull request #1628 from eyalroz/issue-1627
Fix for issue #1627: Global get_level() and should_log()
2020-07-24 00:31:51 +03:00
Eyal Rozenberg
7009727559 Fix for issue #1627:
* Added: `spdlog::get_level()` API function - like `logger::level()`, except for the name change
* Added: `spdlog::should_log()` API function - like `logger: should_log()`
2020-07-23 23:05:12 +03:00
Gabi Melman
ae02fba141 Merge pull request #1624 from tambry/cmake_range
Bump CMake requirement to 3.10, don't enable C language
2020-07-21 14:28:42 +03:00
Raul Tambre
76cdeb62e3 Don't enable C language in CMake
spdlog doesn't seem to actually require the C language.
Not enabling it results in a significant initial configure time speedup.
2020-07-21 11:39:40 +03:00
Raul Tambre
ae9627c64c Raise CMake requirement to 3.13
* Reduces the range of possible version we'd need to test with.
* Enables newer policies reducing possible deprecation warnings from new policies.
* Allows removing some code for compatibility with older versions.
* Coincides with LLVM's bump to requiring CMake 3.13.
2020-07-20 22:03:50 +03:00
Gabi Melman
58875bdcd7 Update tcp_client-windows.h 2020-07-14 23:15:49 +03:00
52 changed files with 3256 additions and 2329 deletions

View File

@@ -1,14 +1,6 @@
# Copyright(c) 2019 spdlog authors Distributed under the MIT License (http://opensource.org/licenses/MIT)
cmake_minimum_required(VERSION 3.2)
if(${CMAKE_VERSION} VERSION_LESS 3.11)
cmake_policy(VERSION ${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION})
else()
cmake_policy(VERSION 3.11)
endif()
enable_language(C)
cmake_minimum_required(VERSION 3.10)
# ---------------------------------------------------------------------------------------
# Start spdlog project
@@ -40,7 +32,7 @@ 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()
@@ -200,7 +192,8 @@ endif()
# Misc definitions according to tweak options
# ---------------------------------------------------------------------------------------
set(SPDLOG_WCHAR_TO_UTF8_SUPPORT ${SPDLOG_WCHAR_SUPPORT})
foreach(SPDLOG_OPTION
foreach(
SPDLOG_OPTION
SPDLOG_WCHAR_TO_UTF8_SUPPORT
SPDLOG_WCHAR_FILENAMES
SPDLOG_NO_EXCEPTIONS
@@ -208,8 +201,7 @@ foreach(SPDLOG_OPTION
SPDLOG_PREVENT_CHILD_FD
SPDLOG_NO_THREAD_ID
SPDLOG_NO_TLS
SPDLOG_NO_ATOMIC_LEVELS
)
SPDLOG_NO_ATOMIC_LEVELS)
if(${SPDLOG_OPTION})
target_compile_definitions(spdlog PUBLIC ${SPDLOG_OPTION})
target_compile_definitions(spdlog_header_only INTERFACE ${SPDLOG_OPTION})

View File

@@ -25,7 +25,7 @@ $ cmake .. && make -j
* Homebrew: `brew install spdlog`
* MacPorts: `sudo port install spdlog`
* FreeBSD: `cd /usr/ports/devel/spdlog/ && make install clean`
* Fedora: `yum install spdlog`
* Fedora: `dnf install spdlog`
* Gentoo: `emerge dev-libs/spdlog`
* Arch Linux: `pacman -S spdlog`
* vcpkg: `vcpkg install spdlog`
@@ -56,7 +56,6 @@ $ cmake .. && make -j
#### Basic usage
```c++
#include "spdlog/spdlog.h"
#include "spdlog/sinks/basic_file_sink.h"
int main()
{
@@ -79,10 +78,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);
}
```
@@ -169,6 +164,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++

View File

@@ -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)

View File

@@ -9,7 +9,7 @@
#include "spdlog/spdlog.h"
#include "spdlog/async.h"
#include "spdlog/sinks/basic_file_sink.h"
#include "spdlog/sinks/stdout_color_sinks.h"
#include "spdlog/fmt/bundled/locale.h"
#include "utils.h"
#include <atomic>
@@ -48,14 +48,14 @@ 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
@@ -98,11 +98,12 @@ 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(fmt::format(std::locale("en_US.UTF-8"), "Messages : {:L}", howmany));
spdlog::info(fmt::format(std::locale("en_US.UTF-8"), "Threads : {:L}", threads));
spdlog::info(fmt::format(std::locale("en_US.UTF-8"), "Queue : {:L} slots", queue_size));
spdlog::info(fmt::format(
std::locale("en_US.UTF-8"), "Queue memory : {:L} x {:L} = {:L} KB ", queue_size, slot_size, (queue_size * slot_size) / 1024));
spdlog::info(fmt::format(std::locale("en_US.UTF-8"), "Total iters : {:L}", iters));
spdlog::info("-------------------------------------------------");
const char *filename = "logs/basic_async.log";
@@ -175,5 +176,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(fmt::format(std::locale("en_US.UTF-8"), "Elapsed: {} secs\t {:L}/sec", delta_d, int(howmany / delta_d)));
}

View File

@@ -11,6 +11,7 @@
#include "spdlog/sinks/daily_file_sink.h"
#include "spdlog/sinks/null_sink.h"
#include "spdlog/sinks/rotating_file_sink.h"
#include "spdlog/fmt/bundled/locale.h"
#include "utils.h"
#include <atomic>
@@ -32,7 +33,7 @@ static const int max_threads = 1000;
void bench_threaded_logging(size_t threads, int iters)
{
spdlog::info("**************************************************************");
spdlog::info("Multi threaded: {:n} threads, {:n} messages", threads, iters);
spdlog::info(fmt::format(std::locale("en_US.UTF-8"), "Multi threaded: {:L} threads, {:L} messages", threads, iters));
spdlog::info("**************************************************************");
auto basic_mt = spdlog::basic_logger_mt("basic_mt", "logs/basic_mt.log", true);
@@ -68,7 +69,7 @@ void bench_threaded_logging(size_t threads, int iters)
void bench_single_threaded(int iters)
{
spdlog::info("**************************************************************");
spdlog::info("Single threaded: {:n} messages", iters);
spdlog::info(fmt::format(std::locale("en_US.UTF-8"), "Single threaded: {} messages", iters));
spdlog::info("**************************************************************");
auto basic_st = spdlog::basic_logger_st("basic_st", "logs/basic_st.log", true);
@@ -152,7 +153,8 @@ void bench(int howmany, std::shared_ptr<spdlog::logger> log)
auto delta = high_resolution_clock::now() - start;
auto delta_d = duration_cast<duration<double>>(delta).count();
spdlog::info("{:<30} Elapsed: {:0.2f} secs {:>16n}/sec", log->name(), delta_d, int(howmany / delta_d));
spdlog::info(
fmt::format(std::locale("en_US.UTF-8"), "{:<30} Elapsed: {:0.2f} secs {:>16L}/sec", log->name(), delta_d, int(howmany / delta_d)));
spdlog::drop(log->name());
}
@@ -182,7 +184,8 @@ void bench_mt(int howmany, std::shared_ptr<spdlog::logger> log, size_t thread_co
auto delta = high_resolution_clock::now() - start;
auto delta_d = duration_cast<duration<double>>(delta).count();
spdlog::info("{:<30} Elapsed: {:0.2f} secs {:>16n}/sec", log->name(), delta_d, int(howmany / delta_d));
spdlog::info(
fmt::format(std::locale("en_US.UTF-8"), "{:<30} Elapsed: {:0.2f} secs {:>16L}/sec", log->name(), delta_d, int(howmany / delta_d)));
spdlog::drop(log->name());
}
@@ -205,7 +208,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 +235,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));
}
*/

View File

@@ -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)

View File

@@ -13,6 +13,7 @@ void rotating_example();
void daily_example();
void async_example();
void binary_example();
void stopwatch_example();
void trace_example();
void multi_sink_example();
void user_defined_example();
@@ -71,6 +72,7 @@ int main(int, char *[])
user_defined_example();
err_handler_example();
trace_example();
stopwatch_example();
custom_flags_example();
// Flush all *registered* loggers using a worker thread every 3 seconds.
@@ -192,6 +194,16 @@ void trace_example()
SPDLOG_LOGGER_TRACE(logger, "another trace message");
}
// stopwatch example
#include "spdlog/stopwatch.h"
#include <thread>
void stopwatch_example()
{
spdlog::stopwatch sw;
std::this_thread::sleep_for(std::chrono::milliseconds(123));
spdlog::info("Stopwatch: {} seconds", sw);
}
// A logger with multiple sinks (stdout and file) - each with a different format and log level.
void multi_sink_example()
{

View File

@@ -35,7 +35,7 @@ template<async_overflow_policy OverflowPolicy = async_overflow_policy::block>
struct async_factory_impl
{
template<typename Sink, typename... SinkArgs>
static std::shared_ptr<async_logger> create(std::string logger_name, SinkArgs &&... args)
static std::shared_ptr<async_logger> create(std::string logger_name, SinkArgs &&...args)
{
auto &registry_inst = details::registry::instance();
@@ -61,13 +61,13 @@ using async_factory = async_factory_impl<async_overflow_policy::block>;
using async_factory_nonblock = async_factory_impl<async_overflow_policy::overrun_oldest>;
template<typename Sink, typename... SinkArgs>
inline std::shared_ptr<spdlog::logger> create_async(std::string logger_name, SinkArgs &&... sink_args)
inline std::shared_ptr<spdlog::logger> create_async(std::string logger_name, SinkArgs &&...sink_args)
{
return async_factory::create<Sink>(std::move(logger_name), std::forward<SinkArgs>(sink_args)...);
}
template<typename Sink, typename... SinkArgs>
inline std::shared_ptr<spdlog::logger> create_async_nb(std::string logger_name, SinkArgs &&... sink_args)
inline std::shared_ptr<spdlog::logger> create_async_nb(std::string logger_name, SinkArgs &&...sink_args)
{
return async_factory_nonblock::create<Sink>(std::move(logger_name), std::forward<SinkArgs>(sink_args)...);
}

View File

@@ -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));
}

View File

@@ -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

View File

@@ -78,24 +78,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;
}
rv.set(logger_name, level);
if (logger_name.empty()) // no logger name indicate global level
{
global_level_found = true;
global_level = level;
}
return rv;
else
{
levels[logger_name] = level;
}
}
details::registry::instance().set_levels(std::move(levels), global_level_found ? &global_level : nullptr);
}
} // namespace helpers

View File

@@ -3,7 +3,8 @@
#pragma once
#include <spdlog/cfg/log_levels.h>
#include <spdlog/common.h>
#include <unordered_map>
namespace spdlog {
namespace cfg {
@@ -17,7 +18,7 @@ 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

View File

@@ -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

View File

@@ -167,7 +167,6 @@ SPDLOG_API string_view_t &to_string_view(spdlog::level::level_enum l) SPDLOG_NOE
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
//
@@ -232,7 +231,7 @@ namespace details {
using std::make_unique;
#else
template<typename T, typename... Args>
std::unique_ptr<T> make_unique(Args &&... args)
std::unique_ptr<T> make_unique(Args &&...args)
{
static_assert(!std::is_array<T>::value, "arrays not supported");
return std::unique_ptr<T>(new T(std::forward<Args>(args)...));

View File

@@ -38,11 +38,11 @@ inline unsigned int count_digits(T n)
// fmt 7.0.0 renamed the internal namespace to detail.
// See: https://github.com/fmtlib/fmt/issues/1538
#if FMT_VERSION < 70000
internal
internal
#else
detail
detail
#endif
::count_digits(static_cast<count_type>(n)));
::count_digits(static_cast<count_type>(n)));
}
inline void pad2(int n, memory_buf_t &dest)

View File

@@ -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)
@@ -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);
}
}
}

View File

@@ -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;
@@ -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,11 +91,13 @@ 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);
std::shared_ptr<thread_pool> tp_;

View File

@@ -13,7 +13,7 @@ class logger;
struct synchronous_factory
{
template<typename Sink, typename... SinkArgs>
static std::shared_ptr<spdlog::logger> create(std::string logger_name, SinkArgs &&... args)
static std::shared_ptr<spdlog::logger> create(std::string logger_name, SinkArgs &&...args)
{
auto sink = std::make_shared<Sink>(std::forward<SinkArgs>(args)...);
auto new_logger = std::make_shared<spdlog::logger>(std::move(logger_name), std::move(sink));

View File

@@ -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>

View File

@@ -75,7 +75,11 @@ public:
int last_errno = 0;
for (auto *rp = addrinfo_result; rp != nullptr; rp = rp->ai_next)
{
int const flags = SOCK_CLOEXEC;
#if defined(SOCK_CLOEXEC)
const int flags = SOCK_CLOEXEC;
#else
const int flags = 0;
#endif
socket_ = ::socket(rp->ai_family, rp->ai_socktype | flags, rp->ai_protocol);
if (socket_ == -1)
{
@@ -87,13 +91,10 @@ public:
{
break;
}
else
{
last_errno = errno;
::close(socket_);
socket_ = -1;
}
}
::freeaddrinfo(addrinfo_result);
if (socket_ == -1)
{

View File

@@ -9,7 +9,7 @@
//
// Support for logging binary data as hex
// format flags:
// format flags, any combination of the followng:
// {:X} - print in uppercase.
// {:s} - don't separate each byte with space.
// {:p} - don't print the position on each line start.

View File

@@ -48,7 +48,7 @@ FMT_CONSTEXPR To lossless_integral_conversion(const From from, int& ec) {
// From fits in To without any problem.
} else {
// From does not always fit in To, resort to a dynamic check.
if (from < T::min() || from > T::max()) {
if (from < (T::min)() || from > (T::max)()) {
// outside range.
ec = 1;
return {};
@@ -74,7 +74,7 @@ FMT_CONSTEXPR To lossless_integral_conversion(const From from, int& ec) {
if (F::is_signed && !T::is_signed) {
// From may be negative, not allowed!
if (fmt::internal::is_negative(from)) {
if (fmt::detail::is_negative(from)) {
ec = 1;
return {};
}
@@ -84,7 +84,7 @@ FMT_CONSTEXPR To lossless_integral_conversion(const From from, int& ec) {
// yes, From always fits in To.
} else {
// from may not fit in To, we have to do a dynamic check
if (from > static_cast<From>(T::max())) {
if (from > static_cast<From>((T::max)())) {
ec = 1;
return {};
}
@@ -97,7 +97,7 @@ FMT_CONSTEXPR To lossless_integral_conversion(const From from, int& ec) {
// yes, From always fits in To.
} else {
// from may not fit in To, we have to do a dynamic check
if (from > static_cast<From>(T::max())) {
if (from > static_cast<From>((T::max)())) {
// outside range.
ec = 1;
return {};
@@ -141,7 +141,7 @@ FMT_CONSTEXPR To safe_float_conversion(const From from, int& ec) {
// catch the only happy case
if (std::isfinite(from)) {
if (from >= T::lowest() && from <= T::max()) {
if (from >= T::lowest() && from <= (T::max)()) {
return static_cast<To>(from);
}
// not within range.
@@ -195,12 +195,13 @@ To safe_duration_cast(std::chrono::duration<FromRep, FromPeriod> from,
}
// multiply with Factor::num without overflow or underflow
if (Factor::num != 1) {
const auto max1 = internal::max_value<IntermediateRep>() / Factor::num;
const auto max1 = detail::max_value<IntermediateRep>() / Factor::num;
if (count > max1) {
ec = 1;
return {};
}
const auto min1 = std::numeric_limits<IntermediateRep>::min() / Factor::num;
const auto min1 =
(std::numeric_limits<IntermediateRep>::min)() / Factor::num;
if (count < min1) {
ec = 1;
return {};
@@ -269,7 +270,7 @@ To safe_duration_cast(std::chrono::duration<FromRep, FromPeriod> from,
// multiply with Factor::num without overflow or underflow
if (Factor::num != 1) {
constexpr auto max1 = internal::max_value<IntermediateRep>() /
constexpr auto max1 = detail::max_value<IntermediateRep>() /
static_cast<IntermediateRep>(Factor::num);
if (count > max1) {
ec = 1;
@@ -306,12 +307,12 @@ To safe_duration_cast(std::chrono::duration<FromRep, FromPeriod> from,
// Usage: f FMT_NOMACRO()
#define FMT_NOMACRO
namespace internal {
namespace detail {
inline null<> localtime_r FMT_NOMACRO(...) { return null<>(); }
inline null<> localtime_s(...) { return null<>(); }
inline null<> gmtime_r(...) { return null<>(); }
inline null<> gmtime_s(...) { return null<>(); }
} // namespace internal
} // namespace detail
// Thread-safe replacement for std::localtime
inline std::tm localtime(std::time_t time) {
@@ -322,22 +323,22 @@ inline std::tm localtime(std::time_t time) {
dispatcher(std::time_t t) : time_(t) {}
bool run() {
using namespace fmt::internal;
using namespace fmt::detail;
return handle(localtime_r(&time_, &tm_));
}
bool handle(std::tm* tm) { return tm != nullptr; }
bool handle(internal::null<>) {
using namespace fmt::internal;
bool handle(detail::null<>) {
using namespace fmt::detail;
return fallback(localtime_s(&tm_, &time_));
}
bool fallback(int res) { return res == 0; }
#if !FMT_MSC_VER
bool fallback(internal::null<>) {
using namespace fmt::internal;
bool fallback(detail::null<>) {
using namespace fmt::detail;
std::tm* tm = std::localtime(&time_);
if (tm) tm_ = *tm;
return tm != nullptr;
@@ -359,21 +360,21 @@ inline std::tm gmtime(std::time_t time) {
dispatcher(std::time_t t) : time_(t) {}
bool run() {
using namespace fmt::internal;
using namespace fmt::detail;
return handle(gmtime_r(&time_, &tm_));
}
bool handle(std::tm* tm) { return tm != nullptr; }
bool handle(internal::null<>) {
using namespace fmt::internal;
bool handle(detail::null<>) {
using namespace fmt::detail;
return fallback(gmtime_s(&tm_, &time_));
}
bool fallback(int res) { return res == 0; }
#if !FMT_MSC_VER
bool fallback(internal::null<>) {
bool fallback(detail::null<>) {
std::tm* tm = std::gmtime(&time_);
if (tm) tm_ = *tm;
return tm != nullptr;
@@ -386,17 +387,17 @@ inline std::tm gmtime(std::time_t time) {
return gt.tm_;
}
namespace internal {
inline std::size_t strftime(char* str, std::size_t count, const char* format,
namespace detail {
inline size_t strftime(char* str, size_t count, const char* format,
const std::tm* time) {
return std::strftime(str, count, format, time);
}
inline std::size_t strftime(wchar_t* str, std::size_t count,
const wchar_t* format, const std::tm* time) {
inline size_t strftime(wchar_t* str, size_t count, const wchar_t* format,
const std::tm* time) {
return std::wcsftime(str, count, format, time);
}
} // namespace internal
} // namespace detail
template <typename Char> struct formatter<std::tm, Char> {
template <typename ParseContext>
@@ -405,7 +406,7 @@ template <typename Char> struct formatter<std::tm, Char> {
if (it != ctx.end() && *it == ':') ++it;
auto end = it;
while (end != ctx.end() && *end != '}') ++end;
tm_format.reserve(internal::to_unsigned(end - it + 1));
tm_format.reserve(detail::to_unsigned(end - it + 1));
tm_format.append(it, end);
tm_format.push_back('\0');
return end;
@@ -414,11 +415,10 @@ template <typename Char> struct formatter<std::tm, Char> {
template <typename FormatContext>
auto format(const std::tm& tm, FormatContext& ctx) -> decltype(ctx.out()) {
basic_memory_buffer<Char> buf;
std::size_t start = buf.size();
size_t start = buf.size();
for (;;) {
std::size_t size = buf.capacity() - start;
std::size_t count =
internal::strftime(&buf[start], size, &tm_format[0], &tm);
size_t size = buf.capacity() - start;
size_t count = detail::strftime(&buf[start], size, &tm_format[0], &tm);
if (count != 0) {
buf.resize(start + count);
break;
@@ -430,7 +430,7 @@ template <typename Char> struct formatter<std::tm, Char> {
// https://github.com/fmtlib/fmt/issues/367
break;
}
const std::size_t MIN_GROWTH = 10;
const size_t MIN_GROWTH = 10;
buf.reserve(buf.capacity() + (size > MIN_GROWTH ? size : MIN_GROWTH));
}
return std::copy(buf.begin(), buf.end(), ctx.out());
@@ -439,7 +439,7 @@ template <typename Char> struct formatter<std::tm, Char> {
basic_memory_buffer<Char> tm_format;
};
namespace internal {
namespace detail {
template <typename Period> FMT_CONSTEXPR const char* get_units() {
return nullptr;
}
@@ -768,19 +768,25 @@ OutputIt format_duration_value(OutputIt out, Rep val, int precision) {
return format_to(out, std::is_floating_point<Rep>::value ? fp_f : format,
val);
}
template <typename Char, typename OutputIt>
OutputIt copy_unit(string_view unit, OutputIt out, Char) {
return std::copy(unit.begin(), unit.end(), out);
}
template <typename OutputIt>
OutputIt copy_unit(string_view unit, OutputIt out, wchar_t) {
// This works when wchar_t is UTF-32 because units only contain characters
// that have the same representation in UTF-16 and UTF-32.
utf8_to_utf16 u(unit);
return std::copy(u.c_str(), u.c_str() + u.size(), out);
}
template <typename Char, typename Period, typename OutputIt>
OutputIt format_duration_unit(OutputIt out) {
if (const char* unit = get_units<Period>()) {
string_view s(unit);
if (const_check(std::is_same<Char, wchar_t>())) {
utf8_to_utf16 u(s);
return std::copy(u.c_str(), u.c_str() + u.size(), out);
}
return std::copy(s.begin(), s.end(), out);
}
if (const char* unit = get_units<Period>())
return copy_unit(string_view(unit), out, Char());
const Char num_f[] = {'[', '{', '}', ']', 's', 0};
if (Period::den == 1) return format_to(out, num_f, Period::num);
if (const_check(Period::den == 1)) return format_to(out, num_f, Period::num);
const Char num_def_f[] = {'[', '{', '}', '/', '{', '}', ']', 's', 0};
return format_to(out, num_def_f, Period::num, Period::den);
}
@@ -874,9 +880,9 @@ struct chrono_formatter {
if (isnan(value)) return write_nan();
uint32_or_64_or_128_t<int> n =
to_unsigned(to_nonnegative_int(value, max_value<int>()));
int num_digits = internal::count_digits(n);
int num_digits = detail::count_digits(n);
if (width > num_digits) out = std::fill_n(out, width - num_digits, '0');
out = format_decimal<char_type>(out, n, num_digits);
out = format_decimal<char_type>(out, n, num_digits).end;
}
void write_nan() { std::copy_n("nan", 3, out); }
@@ -1004,14 +1010,14 @@ struct chrono_formatter {
out = format_duration_unit<char_type, Period>(out);
}
};
} // namespace internal
} // namespace detail
template <typename Rep, typename Period, typename Char>
struct formatter<std::chrono::duration<Rep, Period>, Char> {
private:
basic_format_specs<Char> specs;
int precision;
using arg_ref_type = internal::arg_ref<Char>;
using arg_ref_type = detail::arg_ref<Char>;
arg_ref_type width_ref;
arg_ref_type precision_ref;
mutable basic_string_view<Char> format_str;
@@ -1032,7 +1038,7 @@ struct formatter<std::chrono::duration<Rep, Period>, Char> {
return arg_ref_type(arg_id);
}
FMT_CONSTEXPR arg_ref_type make_arg_ref(internal::auto_id) {
FMT_CONSTEXPR arg_ref_type make_arg_ref(detail::auto_id) {
return arg_ref_type(context.next_arg_id());
}
@@ -1062,17 +1068,17 @@ struct formatter<std::chrono::duration<Rep, Period>, Char> {
auto begin = ctx.begin(), end = ctx.end();
if (begin == end || *begin == '}') return {begin, begin};
spec_handler handler{*this, ctx, format_str};
begin = internal::parse_align(begin, end, handler);
begin = detail::parse_align(begin, end, handler);
if (begin == end) return {begin, begin};
begin = internal::parse_width(begin, end, handler);
begin = detail::parse_width(begin, end, handler);
if (begin == end) return {begin, begin};
if (*begin == '.') {
if (std::is_floating_point<Rep>::value)
begin = internal::parse_precision(begin, end, handler);
begin = detail::parse_precision(begin, end, handler);
else
handler.on_error("precision not allowed for this argument type");
}
end = parse_chrono_format(begin, end, internal::chrono_format_checker());
end = parse_chrono_format(begin, end, detail::chrono_format_checker());
return {begin, end};
}
@@ -1083,7 +1089,7 @@ struct formatter<std::chrono::duration<Rep, Period>, Char> {
-> decltype(ctx.begin()) {
auto range = do_parse(ctx);
format_str = basic_string_view<Char>(
&*range.begin, internal::to_unsigned(range.end - range.begin));
&*range.begin, detail::to_unsigned(range.end - range.begin));
return range.end;
}
@@ -1094,23 +1100,21 @@ struct formatter<std::chrono::duration<Rep, Period>, Char> {
// is not specified.
basic_memory_buffer<Char> buf;
auto out = std::back_inserter(buf);
using range = internal::output_range<decltype(ctx.out()), Char>;
internal::basic_writer<range> w(range(ctx.out()));
internal::handle_dynamic_spec<internal::width_checker>(specs.width,
width_ref, ctx);
internal::handle_dynamic_spec<internal::precision_checker>(
precision, precision_ref, ctx);
detail::handle_dynamic_spec<detail::width_checker>(specs.width, width_ref,
ctx);
detail::handle_dynamic_spec<detail::precision_checker>(precision,
precision_ref, ctx);
if (begin == end || *begin == '}') {
out = internal::format_duration_value<Char>(out, d.count(), precision);
internal::format_duration_unit<Char, Period>(out);
out = detail::format_duration_value<Char>(out, d.count(), precision);
detail::format_duration_unit<Char, Period>(out);
} else {
internal::chrono_formatter<FormatContext, decltype(out), Rep, Period> f(
detail::chrono_formatter<FormatContext, decltype(out), Rep, Period> f(
ctx, out, d);
f.precision = precision;
parse_chrono_format(begin, end, f);
}
w.write(buf.data(), buf.size(), specs);
return w.out();
return detail::write(
ctx.out(), basic_string_view<Char>(buf.data(), buf.size()), specs);
}
};

View File

@@ -198,7 +198,7 @@ struct rgb {
uint8_t b;
};
namespace internal {
namespace detail {
// color is a struct of either a rgb color or a terminal color.
struct color_type {
@@ -221,7 +221,7 @@ struct color_type {
uint32_t rgb_color;
} value;
};
} // namespace internal
} // namespace detail
// Experimental text formatting support.
class text_style {
@@ -298,11 +298,11 @@ class text_style {
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_CONSTEXPR detail::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_CONSTEXPR detail::color_type get_background() const FMT_NOEXCEPT {
FMT_ASSERT(has_background(), "no background specified for this style");
return background_color;
}
@@ -313,7 +313,7 @@ class text_style {
private:
FMT_CONSTEXPR text_style(bool is_foreground,
internal::color_type text_color) FMT_NOEXCEPT
detail::color_type text_color) FMT_NOEXCEPT
: set_foreground_color(),
set_background_color(),
ems() {
@@ -326,23 +326,23 @@ class text_style {
}
}
friend FMT_CONSTEXPR_DECL text_style fg(internal::color_type foreground)
friend FMT_CONSTEXPR_DECL text_style fg(detail::color_type foreground)
FMT_NOEXCEPT;
friend FMT_CONSTEXPR_DECL text_style bg(internal::color_type background)
friend FMT_CONSTEXPR_DECL text_style bg(detail::color_type background)
FMT_NOEXCEPT;
internal::color_type foreground_color;
internal::color_type background_color;
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 {
FMT_CONSTEXPR text_style fg(detail::color_type foreground) FMT_NOEXCEPT {
return text_style(/*is_foreground=*/true, foreground);
}
FMT_CONSTEXPR text_style bg(internal::color_type background) FMT_NOEXCEPT {
FMT_CONSTEXPR text_style bg(detail::color_type background) FMT_NOEXCEPT {
return text_style(/*is_foreground=*/false, background);
}
@@ -350,21 +350,21 @@ FMT_CONSTEXPR text_style operator|(emphasis lhs, emphasis rhs) FMT_NOEXCEPT {
return text_style(lhs) | rhs;
}
namespace internal {
namespace detail {
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 == detail::data::background_color;
uint32_t value = text_color.value.term_color;
// Background ASCII codes are the same as the foreground ones but with
// 10 more.
if (is_background) value += 10u;
std::size_t index = 0;
size_t index = 0;
buffer[index++] = static_cast<Char>('\x1b');
buffer[index++] = static_cast<Char>('[');
@@ -398,7 +398,7 @@ template <typename Char> struct ansi_color_escape {
if (em_bits & static_cast<uint8_t>(emphasis::strikethrough))
em_codes[3] = 9;
std::size_t index = 0;
size_t index = 0;
for (int i = 0; i < 4; ++i) {
if (!em_codes[i]) continue;
buffer[index++] = static_cast<Char>('\x1b');
@@ -429,14 +429,14 @@ template <typename Char> struct ansi_color_escape {
template <typename Char>
FMT_CONSTEXPR ansi_color_escape<Char> make_foreground_color(
internal::color_type foreground) FMT_NOEXCEPT {
return ansi_color_escape<Char>(foreground, internal::data::foreground_color);
detail::color_type foreground) FMT_NOEXCEPT {
return ansi_color_escape<Char>(foreground, detail::data::foreground_color);
}
template <typename Char>
FMT_CONSTEXPR ansi_color_escape<Char> make_background_color(
internal::color_type background) FMT_NOEXCEPT {
return ansi_color_escape<Char>(background, internal::data::background_color);
detail::color_type background) FMT_NOEXCEPT {
return ansi_color_escape<Char>(background, detail::data::background_color);
}
template <typename Char>
@@ -455,11 +455,11 @@ 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(detail::data::reset_color, stream);
}
template <> inline void reset_color<wchar_t>(FILE* stream) FMT_NOEXCEPT {
fputs(internal::data::wreset_color, stream);
fputs(detail::data::wreset_color, stream);
}
template <typename Char>
@@ -476,33 +476,31 @@ void vformat_to(basic_memory_buffer<Char>& buf, const text_style& ts,
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
} // namespace detail
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_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);
}
/**
@@ -513,10 +511,10 @@ void vprint(std::FILE* f, const text_style& ts, const S& format,
"Elapsed time: {0:.2f} seconds", 1.23);
*/
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);
detail::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));
@@ -530,7 +528,7 @@ void print(std::FILE* f, const text_style& ts, const S& format_str,
"Elapsed time: {0:.2f} seconds", 1.23);
*/
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 +538,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);
}
@@ -560,7 +558,7 @@ 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...));
detail::make_args_checked<Args...>(format_str, args...));
}
FMT_END_NAMESPACE

View File

@@ -13,7 +13,33 @@
#include "format.h"
FMT_BEGIN_NAMESPACE
namespace internal {
namespace detail {
// A compile-time string which is compiled into fast formatting code.
class compiled_string {};
template <typename S>
struct is_compiled_string : std::is_base_of<compiled_string, S> {};
/**
\rst
Converts a string literal *s* into a format string that will be parsed at
compile time and converted into efficient formatting code. Requires C++17
``constexpr if`` compiler support.
**Example**::
// Converts 42 into std::string using the most efficient method and no
// runtime format string processing.
std::string s = fmt::format(FMT_COMPILE("{}"), 42);
\endrst
*/
#define FMT_COMPILE(s) FMT_STRING_IMPL(s, fmt::detail::compiled_string)
template <typename T, typename... Tail>
const T& first(const T& value, const Tail&...) {
return value;
}
// Part of a compiled format string. It can be either literal text or a
// replacement field.
@@ -62,13 +88,15 @@ template <typename Char> struct part_counter {
if (begin != end) ++num_parts;
}
FMT_CONSTEXPR void on_arg_id() { ++num_parts; }
FMT_CONSTEXPR void on_arg_id(int) { ++num_parts; }
FMT_CONSTEXPR void on_arg_id(basic_string_view<Char>) { ++num_parts; }
FMT_CONSTEXPR int on_arg_id() { return ++num_parts, 0; }
FMT_CONSTEXPR int on_arg_id(int) { return ++num_parts, 0; }
FMT_CONSTEXPR int on_arg_id(basic_string_view<Char>) {
return ++num_parts, 0;
}
FMT_CONSTEXPR void on_replacement_field(const Char*) {}
FMT_CONSTEXPR void on_replacement_field(int, const Char*) {}
FMT_CONSTEXPR const Char* on_format_specs(const Char* begin,
FMT_CONSTEXPR const Char* on_format_specs(int, const Char* begin,
const Char* end) {
// Find the matching brace.
unsigned brace_counter = 0;
@@ -116,25 +144,28 @@ class format_string_compiler : public error_handler {
handler_(part::make_text({begin, to_unsigned(end - begin)}));
}
FMT_CONSTEXPR void on_arg_id() {
FMT_CONSTEXPR int on_arg_id() {
part_ = part::make_arg_index(parse_context_.next_arg_id());
return 0;
}
FMT_CONSTEXPR void on_arg_id(int id) {
FMT_CONSTEXPR int on_arg_id(int id) {
parse_context_.check_arg_id(id);
part_ = part::make_arg_index(id);
return 0;
}
FMT_CONSTEXPR void on_arg_id(basic_string_view<Char> id) {
FMT_CONSTEXPR int on_arg_id(basic_string_view<Char> id) {
part_ = part::make_arg_name(id);
return 0;
}
FMT_CONSTEXPR void on_replacement_field(const Char* ptr) {
FMT_CONSTEXPR void on_replacement_field(int, const Char* ptr) {
part_.arg_id_end = ptr;
handler_(part_);
}
FMT_CONSTEXPR const Char* on_format_specs(const Char* begin,
FMT_CONSTEXPR const Char* on_format_specs(int, const Char* begin,
const Char* end) {
auto repl = typename part::replacement();
dynamic_specs_handler<basic_format_parse_context<Char>> handler(
@@ -160,23 +191,24 @@ FMT_CONSTEXPR void compile_format_string(basic_string_view<Char> format_str,
format_string_compiler<Char, PartHandler>(format_str, handler));
}
template <typename Range, typename Context, typename Id>
template <typename OutputIt, typename Context, typename Id>
void format_arg(
basic_format_parse_context<typename Range::value_type>& parse_ctx,
basic_format_parse_context<typename Context::char_type>& parse_ctx,
Context& ctx, Id arg_id) {
ctx.advance_to(
visit_format_arg(arg_formatter<Range>(ctx, &parse_ctx), ctx.arg(arg_id)));
ctx.advance_to(visit_format_arg(
arg_formatter<OutputIt, typename Context::char_type>(ctx, &parse_ctx),
ctx.arg(arg_id)));
}
// vformat_to is defined in a subnamespace to prevent ADL.
namespace cf {
template <typename Context, typename Range, typename CompiledFormat>
auto vformat_to(Range out, CompiledFormat& cf, basic_format_args<Context> args)
-> typename Context::iterator {
template <typename Context, typename OutputIt, typename CompiledFormat>
auto vformat_to(OutputIt out, CompiledFormat& cf,
basic_format_args<Context> args) -> typename Context::iterator {
using char_type = typename Context::char_type;
basic_format_parse_context<char_type> parse_ctx(
to_string_view(cf.format_str_));
Context ctx(out.begin(), args);
Context ctx(out, args);
const auto& parts = cf.parts();
for (auto part_it = std::begin(parts); part_it != std::end(parts);
@@ -197,12 +229,12 @@ auto vformat_to(Range out, CompiledFormat& cf, basic_format_args<Context> args)
case format_part_t::kind::arg_index:
advance_to(parse_ctx, part.arg_id_end);
internal::format_arg<Range>(parse_ctx, ctx, value.arg_index);
detail::format_arg<OutputIt>(parse_ctx, ctx, value.arg_index);
break;
case format_part_t::kind::arg_name:
advance_to(parse_ctx, part.arg_id_end);
internal::format_arg<Range>(parse_ctx, ctx, value.str);
detail::format_arg<OutputIt>(parse_ctx, ctx, value.str);
break;
case format_part_t::kind::replacement: {
@@ -226,7 +258,9 @@ auto vformat_to(Range out, CompiledFormat& cf, basic_format_args<Context> args)
advance_to(parse_ctx, part.arg_id_end);
ctx.advance_to(
visit_format_arg(arg_formatter<Range>(ctx, nullptr, &specs), arg));
visit_format_arg(arg_formatter<OutputIt, typename Context::char_type>(
ctx, nullptr, &specs),
arg));
break;
}
}
@@ -240,7 +274,7 @@ struct basic_compiled_format {};
template <typename S, typename = void>
struct compiled_format_base : basic_compiled_format {
using char_type = char_t<S>;
using parts_container = std::vector<internal::format_part<char_type>>;
using parts_container = std::vector<detail::format_part<char_type>>;
parts_container compiled_parts;
@@ -305,7 +339,7 @@ struct compiled_format_base<S, enable_if_t<is_compile_string<S>::value>>
const parts_container& parts() const {
static FMT_CONSTEXPR_DECL const auto compiled_parts =
compile_to_parts<char_type, num_format_parts>(
internal::to_string_view(S()));
detail::to_string_view(S()));
return compiled_parts.data;
}
};
@@ -318,8 +352,8 @@ class compiled_format : private compiled_format_base<S> {
private:
basic_string_view<char_type> format_str_;
template <typename Context, typename Range, typename CompiledFormat>
friend auto cf::vformat_to(Range out, CompiledFormat& cf,
template <typename Context, typename OutputIt, typename CompiledFormat>
friend auto cf::vformat_to(OutputIt out, CompiledFormat& cf,
basic_format_args<Context> args) ->
typename Context::iterator;
@@ -359,8 +393,7 @@ template <typename Char> struct text {
template <typename OutputIt, typename... Args>
OutputIt format(OutputIt out, const Args&...) const {
// TODO: reserve
return copy_str<Char>(data.begin(), data.end(), out);
return write<Char>(out, data);
}
};
@@ -373,33 +406,6 @@ constexpr text<Char> make_text(basic_string_view<Char> s, size_t pos,
return {{&s[pos], size}};
}
template <typename Char, typename OutputIt, typename T,
std::enable_if_t<std::is_integral_v<T>, int> = 0>
OutputIt format_default(OutputIt out, T value) {
// TODO: reserve
format_int fi(value);
return std::copy(fi.data(), fi.data() + fi.size(), out);
}
template <typename Char, typename OutputIt>
OutputIt format_default(OutputIt out, double value) {
writer w(out);
w.write(value);
return w.out();
}
template <typename Char, typename OutputIt>
OutputIt format_default(OutputIt out, Char value) {
*out++ = value;
return out;
}
template <typename Char, typename OutputIt>
OutputIt format_default(OutputIt out, const Char* value) {
auto length = std::char_traits<Char>::length(value);
return copy_str<Char>(value, value + length, out);
}
// A replacement field that refers to argument N.
template <typename Char, typename T, int N> struct field {
using char_type = Char;
@@ -408,13 +414,30 @@ template <typename Char, typename T, int N> struct field {
OutputIt format(OutputIt out, const Args&... args) const {
// This ensures that the argument type is convertile to `const T&`.
const T& arg = get<N>(args...);
return format_default<Char>(out, arg);
return write<Char>(out, arg);
}
};
template <typename Char, typename T, int N>
struct is_compiled_format<field<Char, T, N>> : std::true_type {};
// A replacement field that refers to argument N and has format specifiers.
template <typename Char, typename T, int N> struct spec_field {
using char_type = Char;
mutable formatter<T, Char> fmt;
template <typename OutputIt, typename... Args>
OutputIt format(OutputIt out, const Args&... args) const {
// This ensures that the argument type is convertile to `const T&`.
const T& arg = get<N>(args...);
basic_format_context<OutputIt, Char> ctx(out, {});
return fmt.format(arg, ctx);
}
};
template <typename Char, typename T, int N>
struct is_compiled_format<spec_field<Char, T, N>> : std::true_type {};
template <typename L, typename R> struct concat {
L lhs;
R rhs;
@@ -450,7 +473,8 @@ constexpr auto compile_format_string(S format_str);
template <typename Args, size_t POS, int ID, typename T, typename S>
constexpr auto parse_tail(T head, S format_str) {
if constexpr (POS != to_string_view(format_str).size()) {
if constexpr (POS !=
basic_string_view<typename S::char_type>(format_str).size()) {
constexpr auto tail = compile_format_string<Args, POS, ID>(format_str);
if constexpr (std::is_same<remove_cvref_t<decltype(tail)>,
unknown_format>())
@@ -462,6 +486,21 @@ constexpr auto parse_tail(T head, S format_str) {
}
}
template <typename T, typename Char> struct parse_specs_result {
formatter<T, Char> fmt;
size_t end;
};
template <typename T, typename Char>
constexpr parse_specs_result<T, Char> parse_specs(basic_string_view<Char> str,
size_t pos) {
str.remove_prefix(pos);
auto ctx = basic_format_parse_context<Char>(str);
auto f = formatter<T, Char>();
auto end = f.parse(ctx);
return {f, pos + (end - str.data()) + 1};
}
// Compiles a non-empty format string and returns the compiled representation
// or unknown_format() on unrecognized input.
template <typename Args, size_t POS, int ID, typename S>
@@ -475,12 +514,13 @@ constexpr auto compile_format_string(S format_str) {
return parse_tail<Args, POS + 2, ID>(make_text(str, POS, 1), format_str);
} else if constexpr (str[POS + 1] == '}') {
using type = get_type<ID, Args>;
if constexpr (std::is_same<type, int>::value) {
return parse_tail<Args, POS + 2, ID + 1>(field<char_type, type, ID>(),
format_str);
} else {
return unknown_format();
}
} else if constexpr (str[POS + 1] == ':') {
using type = get_type<ID, Args>;
constexpr auto result = parse_specs<type>(str, POS + 2);
return parse_tail<Args, result.end, ID + 1>(
spec_field<char_type, type, ID>{result.fmt}, format_str);
} else {
return unknown_format();
}
@@ -494,100 +534,130 @@ constexpr auto compile_format_string(S format_str) {
format_str);
}
}
#endif // __cpp_if_constexpr
} // namespace internal
#if FMT_USE_CONSTEXPR
# ifdef __cpp_if_constexpr
template <typename... Args, typename S,
FMT_ENABLE_IF(is_compile_string<S>::value)>
FMT_ENABLE_IF(is_compile_string<S>::value ||
detail::is_compiled_string<S>::value)>
constexpr auto compile(S format_str) {
constexpr basic_string_view<typename S::char_type> str = format_str;
if constexpr (str.size() == 0) {
return internal::make_text(str, 0, 0);
return detail::make_text(str, 0, 0);
} else {
constexpr auto result =
internal::compile_format_string<internal::type_list<Args...>, 0, 0>(
detail::compile_format_string<detail::type_list<Args...>, 0, 0>(
format_str);
if constexpr (std::is_same<remove_cvref_t<decltype(result)>,
internal::unknown_format>()) {
return internal::compiled_format<S, Args...>(to_string_view(format_str));
detail::unknown_format>()) {
return detail::compiled_format<S, Args...>(to_string_view(format_str));
} else {
return result;
}
}
}
template <typename CompiledFormat, typename... Args,
typename Char = typename CompiledFormat::char_type,
FMT_ENABLE_IF(internal::is_compiled_format<CompiledFormat>::value)>
std::basic_string<Char> format(const CompiledFormat& cf, const Args&... args) {
basic_memory_buffer<Char> buffer;
cf.format(std::back_inserter(buffer), args...);
return to_string(buffer);
}
template <typename OutputIt, typename CompiledFormat, typename... Args,
FMT_ENABLE_IF(internal::is_compiled_format<CompiledFormat>::value)>
OutputIt format_to(OutputIt out, const CompiledFormat& cf,
const Args&... args) {
return cf.format(out, args...);
}
# else
#else
template <typename... Args, typename S,
FMT_ENABLE_IF(is_compile_string<S>::value)>
constexpr auto compile(S format_str) -> internal::compiled_format<S, Args...> {
return internal::compiled_format<S, Args...>(to_string_view(format_str));
constexpr auto compile(S format_str) -> detail::compiled_format<S, Args...> {
return detail::compiled_format<S, Args...>(to_string_view(format_str));
}
# endif // __cpp_if_constexpr
#endif // FMT_USE_CONSTEXPR
#endif // __cpp_if_constexpr
// Compiles the format string which must be a string literal.
template <typename... Args, typename Char, size_t N>
auto compile(const Char (&format_str)[N])
-> internal::compiled_format<const Char*, Args...> {
return internal::compiled_format<const Char*, Args...>(
-> detail::compiled_format<const Char*, Args...> {
return detail::compiled_format<const Char*, Args...>(
basic_string_view<Char>(format_str, N - 1));
}
} // namespace detail
// DEPRECATED! use FMT_COMPILE instead.
template <typename... Args>
FMT_DEPRECATED auto compile(const Args&... args)
-> decltype(detail::compile(args...)) {
return detail::compile(args...);
}
#if FMT_USE_CONSTEXPR
# ifdef __cpp_if_constexpr
template <typename CompiledFormat, typename... Args,
typename Char = typename CompiledFormat::char_type,
FMT_ENABLE_IF(std::is_base_of<internal::basic_compiled_format,
CompiledFormat>::value)>
std::basic_string<Char> format(const CompiledFormat& cf, const Args&... args) {
FMT_ENABLE_IF(detail::is_compiled_format<CompiledFormat>::value)>
FMT_INLINE std::basic_string<Char> format(const CompiledFormat& cf,
const Args&... args) {
basic_memory_buffer<Char> buffer;
using range = buffer_range<Char>;
using context = buffer_context<Char>;
internal::cf::vformat_to<context>(range(buffer), cf,
make_format_args<context>(args...));
detail::buffer<Char>& base = buffer;
cf.format(std::back_inserter(base), args...);
return to_string(buffer);
}
template <typename OutputIt, typename CompiledFormat, typename... Args,
FMT_ENABLE_IF(std::is_base_of<internal::basic_compiled_format,
FMT_ENABLE_IF(detail::is_compiled_format<CompiledFormat>::value)>
OutputIt format_to(OutputIt out, const CompiledFormat& cf,
const Args&... args) {
return cf.format(out, args...);
}
# endif // __cpp_if_constexpr
#endif // FMT_USE_CONSTEXPR
template <typename CompiledFormat, typename... Args,
typename Char = typename CompiledFormat::char_type,
FMT_ENABLE_IF(std::is_base_of<detail::basic_compiled_format,
CompiledFormat>::value)>
std::basic_string<Char> format(const CompiledFormat& cf, const Args&... args) {
basic_memory_buffer<Char> buffer;
using context = buffer_context<Char>;
detail::buffer<Char>& base = buffer;
detail::cf::vformat_to<context>(std::back_inserter(base), cf,
make_format_args<context>(args...));
return to_string(buffer);
}
template <typename S, typename... Args,
FMT_ENABLE_IF(detail::is_compiled_string<S>::value)>
FMT_INLINE std::basic_string<typename S::char_type> format(const S&,
Args&&... args) {
constexpr basic_string_view<typename S::char_type> str = S();
if (str.size() == 2 && str[0] == '{' && str[1] == '}')
return fmt::to_string(detail::first(args...));
constexpr auto compiled = detail::compile<Args...>(S());
return format(compiled, std::forward<Args>(args)...);
}
template <typename OutputIt, typename CompiledFormat, typename... Args,
FMT_ENABLE_IF(std::is_base_of<detail::basic_compiled_format,
CompiledFormat>::value)>
OutputIt format_to(OutputIt out, const CompiledFormat& cf,
const Args&... args) {
using char_type = typename CompiledFormat::char_type;
using range = internal::output_range<OutputIt, char_type>;
using context = format_context_t<OutputIt, char_type>;
return internal::cf::vformat_to<context>(range(out), cf,
return detail::cf::vformat_to<context>(out, cf,
make_format_args<context>(args...));
}
template <typename OutputIt, typename CompiledFormat, typename... Args,
FMT_ENABLE_IF(internal::is_output_iterator<OutputIt>::value)>
template <typename OutputIt, typename S, typename... Args,
FMT_ENABLE_IF(detail::is_compiled_string<S>::value)>
OutputIt format_to(OutputIt out, const S&, const Args&... args) {
constexpr auto compiled = detail::compile<Args...>(S());
return format_to(out, compiled, args...);
}
template <
typename OutputIt, typename CompiledFormat, typename... Args,
FMT_ENABLE_IF(detail::is_output_iterator<OutputIt>::value&& std::is_base_of<
detail::basic_compiled_format, CompiledFormat>::value)>
format_to_n_result<OutputIt> format_to_n(OutputIt out, size_t n,
const CompiledFormat& cf,
const Args&... args) {
auto it =
format_to(internal::truncating_iterator<OutputIt>(out, n), cf, args...);
format_to(detail::truncating_iterator<OutputIt>(out, n), cf, args...);
return {it.base(), it.count()};
}
template <typename CompiledFormat, typename... Args>
std::size_t formatted_size(const CompiledFormat& cf, const Args&... args) {
return format_to(internal::counting_iterator(), cf, args...).count();
size_t formatted_size(const CompiledFormat& cf, const Args&... args) {
return format_to(detail::counting_iterator(), cf, args...).count();
}
FMT_END_NAMESPACE

File diff suppressed because it is too large Load Diff

View File

@@ -15,6 +15,7 @@
#include <cstdarg>
#include <cstring> // for std::memmove
#include <cwchar>
#include <exception>
#include "format.h"
#if !defined(FMT_STATIC_THOUSANDS_SEPARATOR)
@@ -22,8 +23,16 @@
#endif
#ifdef _WIN32
# include <io.h>
# if !defined(NOMINMAX) && !defined(WIN32_LEAN_AND_MEAN)
# define NOMINMAX
# define WIN32_LEAN_AND_MEAN
# include <windows.h>
# undef WIN32_LEAN_AND_MEAN
# undef NOMINMAX
# else
# include <windows.h>
# endif
# include <io.h>
#endif
#ifdef _MSC_VER
@@ -33,15 +42,19 @@
// Dummy implementations of strerror_r and strerror_s called if corresponding
// system functions are not available.
inline fmt::internal::null<> strerror_r(int, char*, ...) { return {}; }
inline fmt::internal::null<> strerror_s(char*, std::size_t, ...) { return {}; }
inline fmt::detail::null<> strerror_r(int, char*, ...) { return {}; }
inline fmt::detail::null<> strerror_s(char*, size_t, ...) { return {}; }
FMT_BEGIN_NAMESPACE
namespace internal {
namespace detail {
FMT_FUNC void assert_fail(const char* file, int line, const char* message) {
print(stderr, "{}:{}: assertion failed: {}", file, line, message);
std::abort();
// Use unchecked std::fprintf to avoid triggering another assertion when
// writing to stderr fails
std::fprintf(stderr, "%s:%d: assertion failed: %s", file, line, message);
// Chosen instead of std::abort to satisfy Clang in CUDA mode during device
// code pass.
std::terminate();
}
#ifndef _MSC_VER
@@ -67,14 +80,14 @@ inline int fmt_snprintf(char* buffer, size_t size, const char* format, ...) {
// other - failure
// Buffer should be at least of size 1.
FMT_FUNC int safe_strerror(int error_code, char*& buffer,
std::size_t buffer_size) FMT_NOEXCEPT {
size_t buffer_size) FMT_NOEXCEPT {
FMT_ASSERT(buffer != nullptr && buffer_size != 0, "invalid buffer");
class dispatcher {
private:
int error_code_;
char*& buffer_;
std::size_t buffer_size_;
size_t buffer_size_;
// A noop assignment operator to avoid bogus warnings.
void operator=(const dispatcher&) {}
@@ -97,7 +110,7 @@ FMT_FUNC int safe_strerror(int error_code, char*& buffer,
// Handle the case when strerror_r is not available.
FMT_MAYBE_UNUSED
int handle(internal::null<>) {
int handle(detail::null<>) {
return fallback(strerror_s(buffer_, buffer_size_, error_code_));
}
@@ -111,7 +124,7 @@ FMT_FUNC int safe_strerror(int error_code, char*& buffer,
#if !FMT_MSC_VER
// Fallback to strerror if strerror_r and strerror_s are not available.
int fallback(internal::null<>) {
int fallback(detail::null<>) {
errno = 0;
buffer_ = strerror(error_code_);
return errno;
@@ -119,7 +132,7 @@ FMT_FUNC int safe_strerror(int error_code, char*& buffer,
#endif
public:
dispatcher(int err_code, char*& buf, std::size_t buf_size)
dispatcher(int err_code, char*& buf, size_t buf_size)
: error_code_(err_code), buffer_(buf), buffer_size_(buf_size) {}
int run() { return handle(strerror_r(error_code_, buffer_, buffer_size_)); }
@@ -127,7 +140,7 @@ FMT_FUNC int safe_strerror(int error_code, char*& buffer,
return dispatcher(error_code, buffer, buffer_size).run();
}
FMT_FUNC void format_error_code(internal::buffer<char>& out, int error_code,
FMT_FUNC void format_error_code(detail::buffer<char>& out, int error_code,
string_view message) FMT_NOEXCEPT {
// Report error code making sure that the output fits into
// inline_buffer_size to avoid dynamic memory allocation and potential
@@ -136,20 +149,17 @@ FMT_FUNC void format_error_code(internal::buffer<char>& out, int error_code,
static const char SEP[] = ": ";
static const char ERROR_STR[] = "error ";
// Subtract 2 to account for terminating null characters in SEP and ERROR_STR.
std::size_t error_code_size = sizeof(SEP) + sizeof(ERROR_STR) - 2;
size_t error_code_size = sizeof(SEP) + sizeof(ERROR_STR) - 2;
auto abs_value = static_cast<uint32_or_64_or_128_t<int>>(error_code);
if (internal::is_negative(error_code)) {
if (detail::is_negative(error_code)) {
abs_value = 0 - abs_value;
++error_code_size;
}
error_code_size += internal::to_unsigned(internal::count_digits(abs_value));
internal::writer w(out);
if (message.size() <= inline_buffer_size - error_code_size) {
w.write(message);
w.write(SEP);
}
w.write(ERROR_STR);
w.write(error_code);
error_code_size += detail::to_unsigned(detail::count_digits(abs_value));
auto it = std::back_inserter(out);
if (message.size() <= inline_buffer_size - error_code_size)
format_to(it, "{}{}", message, SEP);
format_to(it, "{}{}", ERROR_STR, error_code);
assert(out.size() <= inline_buffer_size);
}
@@ -168,10 +178,10 @@ FMT_FUNC void fwrite_fully(const void* ptr, size_t size, size_t count,
size_t written = std::fwrite(ptr, size, count, stream);
if (written < count) FMT_THROW(system_error(errno, "cannot write to file"));
}
} // namespace internal
} // namespace detail
#if !defined(FMT_STATIC_THOUSANDS_SEPARATOR)
namespace internal {
namespace detail {
template <typename Locale>
locale_ref::locale_ref(const Locale& loc) : locale_(&loc) {
@@ -194,18 +204,16 @@ template <typename Char> FMT_FUNC Char decimal_point_impl(locale_ref loc) {
return std::use_facet<std::numpunct<Char>>(loc.get<std::locale>())
.decimal_point();
}
} // namespace internal
} // namespace detail
#else
template <typename Char>
FMT_FUNC std::string internal::grouping_impl(locale_ref) {
FMT_FUNC std::string detail::grouping_impl(locale_ref) {
return "\03";
}
template <typename Char>
FMT_FUNC Char internal::thousands_sep_impl(locale_ref) {
template <typename Char> FMT_FUNC Char detail::thousands_sep_impl(locale_ref) {
return FMT_STATIC_THOUSANDS_SEPARATOR;
}
template <typename Char>
FMT_FUNC Char internal::decimal_point_impl(locale_ref) {
template <typename Char> FMT_FUNC Char detail::decimal_point_impl(locale_ref) {
return '.';
}
#endif
@@ -222,9 +230,9 @@ FMT_FUNC void system_error::init(int err_code, string_view format_str,
base = std::runtime_error(to_string(buffer));
}
namespace internal {
namespace detail {
template <> FMT_FUNC int count_digits<4>(internal::fallback_uintptr n) {
template <> FMT_FUNC int count_digits<4>(detail::fallback_uintptr n) {
// fallback_uintptr is always stored in little endian.
int i = static_cast<int>(sizeof(void*)) - 1;
while (i > 0 && n.value[i] == 0) --i;
@@ -233,12 +241,27 @@ template <> FMT_FUNC int count_digits<4>(internal::fallback_uintptr n) {
}
template <typename T>
const char basic_data<T>::digits[] =
"0001020304050607080910111213141516171819"
"2021222324252627282930313233343536373839"
"4041424344454647484950515253545556575859"
"6061626364656667686970717273747576777879"
"8081828384858687888990919293949596979899";
const typename basic_data<T>::digit_pair basic_data<T>::digits[] = {
{'0', '0'}, {'0', '1'}, {'0', '2'}, {'0', '3'}, {'0', '4'},
{'0', '5'}, {'0', '6'}, {'0', '7'}, {'0', '8'}, {'0', '9'},
{'1', '0'}, {'1', '1'}, {'1', '2'}, {'1', '3'}, {'1', '4'},
{'1', '5'}, {'1', '6'}, {'1', '7'}, {'1', '8'}, {'1', '9'},
{'2', '0'}, {'2', '1'}, {'2', '2'}, {'2', '3'}, {'2', '4'},
{'2', '5'}, {'2', '6'}, {'2', '7'}, {'2', '8'}, {'2', '9'},
{'3', '0'}, {'3', '1'}, {'3', '2'}, {'3', '3'}, {'3', '4'},
{'3', '5'}, {'3', '6'}, {'3', '7'}, {'3', '8'}, {'3', '9'},
{'4', '0'}, {'4', '1'}, {'4', '2'}, {'4', '3'}, {'4', '4'},
{'4', '5'}, {'4', '6'}, {'4', '7'}, {'4', '8'}, {'4', '9'},
{'5', '0'}, {'5', '1'}, {'5', '2'}, {'5', '3'}, {'5', '4'},
{'5', '5'}, {'5', '6'}, {'5', '7'}, {'5', '8'}, {'5', '9'},
{'6', '0'}, {'6', '1'}, {'6', '2'}, {'6', '3'}, {'6', '4'},
{'6', '5'}, {'6', '6'}, {'6', '7'}, {'6', '8'}, {'6', '9'},
{'7', '0'}, {'7', '1'}, {'7', '2'}, {'7', '3'}, {'7', '4'},
{'7', '5'}, {'7', '6'}, {'7', '7'}, {'7', '8'}, {'7', '9'},
{'8', '0'}, {'8', '1'}, {'8', '2'}, {'8', '3'}, {'8', '4'},
{'8', '5'}, {'8', '6'}, {'8', '7'}, {'8', '8'}, {'8', '9'},
{'9', '0'}, {'9', '1'}, {'9', '2'}, {'9', '3'}, {'9', '4'},
{'9', '5'}, {'9', '6'}, {'9', '7'}, {'9', '8'}, {'9', '9'}};
template <typename T>
const char basic_data<T>::hex_digits[] = "0123456789abcdef";
@@ -317,6 +340,10 @@ const char basic_data<T>::background_color[] = "\x1b[48;2;";
template <typename T> const char basic_data<T>::reset_color[] = "\x1b[0m";
template <typename T> const wchar_t basic_data<T>::wreset_color[] = L"\x1b[0m";
template <typename T> const char basic_data<T>::signs[] = {0, '-', '+', ' '};
template <typename T>
const char basic_data<T>::left_padding_shifts[] = {31, 31, 0, 1, 0};
template <typename T>
const char basic_data<T>::right_padding_shifts[] = {0, 31, 0, 1, 0};
template <typename T> struct bits {
static FMT_CONSTEXPR_DECL const int value =
@@ -576,9 +603,10 @@ class bigint {
void operator=(const bigint&) = delete;
void assign(const bigint& other) {
bigits_.resize(other.bigits_.size());
auto size = other.bigits_.size();
bigits_.resize(size);
auto data = other.bigits_.data();
std::copy(data, data + other.bigits_.size(), bigits_.data());
std::copy(data, data + size, make_checked(bigits_.data(), size));
exp_ = other.exp_;
}
@@ -594,7 +622,7 @@ class bigint {
int num_bigits() const { return static_cast<int>(bigits_.size()) + exp_; }
bigint& operator<<=(int shift) {
FMT_NOINLINE bigint& operator<<=(int shift) {
assert(shift >= 0);
exp_ += shift / bigit_bits;
shift %= bigit_bits;
@@ -1125,7 +1153,7 @@ int snprintf_float(T value, int precision, float_specs specs,
precision = (precision >= 0 ? precision : 6) - 1;
// Build the format string.
enum { max_format_size = 7 }; // Ths longest format is "%#.*Le".
enum { max_format_size = 7 }; // The longest format is "%#.*Le".
char format[max_format_size];
char* format_ptr = format;
*format_ptr++ = '%';
@@ -1145,13 +1173,13 @@ int snprintf_float(T value, int precision, float_specs specs,
for (;;) {
auto begin = buf.data() + offset;
auto capacity = buf.capacity() - offset;
#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
#ifdef FMT_FUZZ
if (precision > 100000)
throw std::runtime_error(
"fuzz mode - avoid large allocation inside snprintf");
#endif
// Suppress the warning about a nonliteral format string.
// Cannot use auto becase of a bug in MinGW (#1532).
// Cannot use auto because of a bug in MinGW (#1532).
int (*snprintf_ptr)(char*, size_t, const char*, ...) = FMT_SNPRINTF;
int result = precision >= 0
? snprintf_ptr(begin, capacity, format, precision, value)
@@ -1268,14 +1296,14 @@ FMT_FUNC const char* utf8_decode(const char* buf, uint32_t* c, int* e) {
return next;
}
} // namespace internal
} // namespace detail
template <> struct formatter<internal::bigint> {
template <> struct formatter<detail::bigint> {
format_parse_context::iterator parse(format_parse_context& ctx) {
return ctx.begin();
}
format_context::iterator format(const internal::bigint& n,
format_context::iterator format(const detail::bigint& n,
format_context& ctx) {
auto out = ctx.out();
bool first = true;
@@ -1289,12 +1317,12 @@ template <> struct formatter<internal::bigint> {
out = format_to(out, "{:08x}", value);
}
if (n.exp_ > 0)
out = format_to(out, "p{}", n.exp_ * internal::bigint::bigit_bits);
out = format_to(out, "p{}", n.exp_ * detail::bigint::bigit_bits);
return out;
}
};
FMT_FUNC internal::utf8_to_utf16::utf8_to_utf16(string_view s) {
FMT_FUNC detail::utf8_to_utf16::utf8_to_utf16(string_view s) {
auto transcode = [this](const char* p) {
auto cp = uint32_t();
auto error = 0;
@@ -1325,7 +1353,7 @@ FMT_FUNC internal::utf8_to_utf16::utf8_to_utf16(string_view s) {
buffer_.push_back(0);
}
FMT_FUNC void format_system_error(internal::buffer<char>& out, int error_code,
FMT_FUNC void format_system_error(detail::buffer<char>& out, int error_code,
string_view message) FMT_NOEXCEPT {
FMT_TRY {
memory_buffer buf;
@@ -1333,12 +1361,9 @@ FMT_FUNC void format_system_error(internal::buffer<char>& out, int error_code,
for (;;) {
char* system_message = &buf[0];
int result =
internal::safe_strerror(error_code, system_message, buf.size());
detail::safe_strerror(error_code, system_message, buf.size());
if (result == 0) {
internal::writer w(out);
w.write(message);
w.write(": ");
w.write(system_message);
format_to(std::back_inserter(out), "{}: {}", message, system_message);
return;
}
if (result != ERANGE)
@@ -1350,7 +1375,7 @@ FMT_FUNC void format_system_error(internal::buffer<char>& out, int error_code,
format_error_code(out, error_code, message);
}
FMT_FUNC void internal::error_handler::on_error(const char* message) {
FMT_FUNC void detail::error_handler::on_error(const char* message) {
FMT_THROW(format_error(message));
}
@@ -1359,14 +1384,39 @@ FMT_FUNC void report_system_error(int error_code,
report_error(format_system_error, error_code, message);
}
struct stringifier {
template <typename T> FMT_INLINE std::string operator()(T value) const {
return to_string(value);
}
std::string operator()(basic_format_arg<format_context>::handle h) const {
memory_buffer buf;
detail::buffer<char>& base = buf;
format_parse_context parse_ctx({});
format_context format_ctx(std::back_inserter(base), {}, {});
h.format(parse_ctx, format_ctx);
return to_string(buf);
}
};
FMT_FUNC std::string detail::vformat(string_view format_str, format_args args) {
if (format_str.size() == 2 && equal2(format_str.data(), "{}")) {
auto arg = args.get(0);
if (!arg) error_handler().on_error("argument not found");
return visit_format_arg(stringifier(), arg);
}
memory_buffer buffer;
detail::vformat_to(buffer, format_str, args);
return to_string(buffer);
}
FMT_FUNC void vprint(std::FILE* f, string_view format_str, format_args args) {
memory_buffer buffer;
internal::vformat_to(buffer, format_str,
detail::vformat_to(buffer, format_str,
basic_format_args<buffer_context<char>>(args));
#ifdef _WIN32
auto fd = _fileno(f);
if (_isatty(fd)) {
internal::utf8_to_utf16 u16(string_view(buffer.data(), buffer.size()));
detail::utf8_to_utf16 u16(string_view(buffer.data(), buffer.size()));
auto written = DWORD();
if (!WriteConsoleW(reinterpret_cast<HANDLE>(_get_osfhandle(fd)),
u16.c_str(), static_cast<DWORD>(u16.size()), &written,
@@ -1376,15 +1426,15 @@ FMT_FUNC void vprint(std::FILE* f, string_view format_str, format_args args) {
return;
}
#endif
internal::fwrite_fully(buffer.data(), 1, buffer.size(), f);
detail::fwrite_fully(buffer.data(), 1, buffer.size(), f);
}
#ifdef _WIN32
// Print assuming legacy (non-Unicode) encoding.
FMT_FUNC void internal::vprint_mojibake(std::FILE* f, string_view format_str,
FMT_FUNC void detail::vprint_mojibake(std::FILE* f, string_view format_str,
format_args args) {
memory_buffer buffer;
internal::vformat_to(buffer, format_str,
detail::vformat_to(buffer, format_str,
basic_format_args<buffer_context<char>>(args));
fwrite_fully(buffer.data(), 1, buffer.size(), f);
}

File diff suppressed because it is too large Load Diff

View File

@@ -14,15 +14,15 @@
FMT_BEGIN_NAMESPACE
namespace internal {
namespace detail {
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));
using af = arg_formatter<typename buffer_context<Char>::iterator, Char>;
return vformat_to<af>(std::back_inserter(buf), to_string_view(format_str),
args, detail::locale_ref(loc));
}
template <typename Char>
@@ -30,43 +30,43 @@ 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);
detail::vformat_to(loc, buffer, format_str, args);
return fmt::to_string(buffer);
}
} // namespace internal
} // namespace detail
template <typename S, typename Char = char_t<S>>
inline std::basic_string<Char> vformat(
const std::locale& loc, const S& format_str,
basic_format_args<buffer_context<type_identity_t<Char>>> args) {
return internal::vformat(loc, to_string_view(format_str), args);
return detail::vformat(loc, to_string_view(format_str), args);
}
template <typename S, typename... Args, typename Char = char_t<S>>
inline std::basic_string<Char> format(const std::locale& loc,
const S& format_str, Args&&... args) {
return internal::vformat(
return detail::vformat(
loc, to_string_view(format_str),
internal::make_args_checked<Args...>(format_str, args...));
detail::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>>>
detail::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));
using af = detail::arg_formatter<OutputIt, Char>;
return vformat_to<af>(out, to_string_view(format_str), args,
detail::locale_ref(loc));
}
template <typename OutputIt, typename S, typename... Args,
FMT_ENABLE_IF(internal::is_output_iterator<OutputIt>::value&&
internal::is_string<S>::value)>
FMT_ENABLE_IF(detail::is_output_iterator<OutputIt>::value&&
detail::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);
detail::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),

View File

@@ -0,0 +1,450 @@
// 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_
#if defined(__MINGW32__) || defined(__CYGWIN__)
// Workaround MinGW bug https://sourceforge.net/p/mingw/bugs/2024/.
# undef __STRICT_ANSI__
#endif
#include <cerrno>
#include <clocale> // for locale_t
#include <cstddef>
#include <cstdio>
#include <cstdlib> // for strtod_l
#if defined __APPLE__ || defined(__FreeBSD__)
# include <xlocale.h> // for LC_NUMERIC_MASK on OS X
#endif
#include "format.h"
// UWP doesn't provide _pipe.
#if FMT_HAS_INCLUDE("winapifamily.h")
# include <winapifamily.h>
#endif
#if FMT_HAS_INCLUDE("fcntl.h") && \
(!defined(WINAPI_FAMILY) || (WINAPI_FAMILY == WINAPI_FAMILY_DESKTOP_APP))
# include <fcntl.h> // for O_RDONLY
# define FMT_USE_FCNTL 1
#else
# define FMT_USE_FCNTL 0
#endif
#ifndef FMT_POSIX
# if defined(_WIN32) && !defined(__MINGW32__)
// Fix warnings about deprecated symbols.
# define FMT_POSIX(call) _##call
# else
# define FMT_POSIX(call) call
# endif
#endif
// Calls to system functions are wrapped in FMT_SYSTEM for testability.
#ifdef FMT_SYSTEM
# define FMT_POSIX_CALL(call) FMT_SYSTEM(call)
#else
# define FMT_SYSTEM(call) ::call
# ifdef _WIN32
// Fix warnings about deprecated symbols.
# define FMT_POSIX_CALL(call) ::_##call
# else
# define FMT_POSIX_CALL(call) ::call
# endif
#endif
// Retries the expression while it evaluates to error_result and errno
// equals to EINTR.
#ifndef _WIN32
# define FMT_RETRY_VAL(result, expression, error_result) \
do { \
(result) = (expression); \
} while ((result) == (error_result) && errno == EINTR)
#else
# define FMT_RETRY_VAL(result, expression, error_result) result = (expression)
#endif
#define FMT_RETRY(result, expression) FMT_RETRY_VAL(result, expression, -1)
FMT_BEGIN_NAMESPACE
/**
\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>;
// An error code.
class error_code {
private:
int value_;
public:
explicit error_code(int value = 0) FMT_NOEXCEPT : value_(value) {}
int get() const FMT_NOEXCEPT { return value_; }
};
#ifdef _WIN32
namespace detail {
// 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(wstring_view 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(wstring_view s);
};
FMT_API void format_windows_error(buffer<char>& out, int error_code,
string_view message) FMT_NOEXCEPT;
} // namespace detail
/** A Windows error. */
class windows_error : public system_error {
private:
FMT_API void init(int error_code, string_view format_str, format_args args);
public:
/**
\rst
Constructs a :class:`fmt::windows_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 windows_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>
windows_error(int error_code, string_view message, const Args&... args) {
init(error_code, message, 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,
string_view message) FMT_NOEXCEPT;
#endif // _WIN32
// 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, 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.
};
// 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; }
file& operator=(file&& other) FMT_NOEXCEPT {
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, 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();
class direct_buffered_file;
template <typename S, typename... Args>
void print(direct_buffered_file& f, const S& format_str,
const Args&... args);
// A buffered file with a direct buffer access and no synchronization.
class direct_buffered_file {
private:
file file_;
enum { buffer_size = 4096 };
char buffer_[buffer_size];
int pos_;
void flush() {
if (pos_ == 0) return;
file_.write(buffer_, pos_);
pos_ = 0;
}
int free_capacity() const { return buffer_size - pos_; }
public:
direct_buffered_file(cstring_view path, int oflag)
: file_(path, oflag), pos_(0) {}
~direct_buffered_file() {
flush();
}
void close() {
flush();
file_.close();
}
template <typename S, typename... Args>
friend void print(direct_buffered_file& f, const S& format_str,
const Args&... args) {
// We could avoid double buffering.
auto buf = fmt::memory_buffer();
fmt::format_to(std::back_inserter(buf), format_str, args...);
auto remaining_pos = 0;
auto remaining_size = buf.size();
while (remaining_size > detail::to_unsigned(f.free_capacity())) {
auto size = f.free_capacity();
memcpy(f.buffer_ + f.pos_, buf.data() + remaining_pos, size);
f.pos_ += size;
f.flush();
remaining_pos += size;
remaining_size -= size;
}
memcpy(f.buffer_ + f.pos_, buf.data() + remaining_pos, remaining_size);
f.pos_ += static_cast<int>(remaining_size);
}
};
#endif // FMT_USE_FCNTL
#ifdef FMT_LOCALE
// A "C" numeric locale.
class locale {
private:
# ifdef _WIN32
using locale_t = _locale_t;
static void freelocale(locale_t loc) { _free_locale(loc); }
static double strtod_l(const char* nptr, char** endptr, _locale_t loc) {
return _strtod_l(nptr, endptr, loc);
}
# endif
locale_t locale_;
public:
using type = locale_t;
locale(const locale&) = delete;
void operator=(const locale&) = delete;
locale() {
# ifndef _WIN32
locale_ = FMT_SYSTEM(newlocale(LC_NUMERIC_MASK, "C", nullptr));
# else
locale_ = _create_locale(LC_NUMERIC, "C");
# endif
if (!locale_) FMT_THROW(system_error(errno, "cannot create locale"));
}
~locale() { freelocale(locale_); }
type get() const { return locale_; }
// Converts string to floating-point number and advances str past the end
// of the parsed input.
double strtod(const char*& str) const {
char* end = nullptr;
double result = strtod_l(str, &end, locale_);
str = end;
return result;
}
};
using Locale FMT_DEPRECATED_ALIAS = locale;
#endif // FMT_LOCALE
FMT_END_NAMESPACE
#endif // FMT_OS_H_

View File

@@ -14,10 +14,10 @@
FMT_BEGIN_NAMESPACE
template <typename CHar> class basic_printf_parse_context;
template <typename Char> class basic_printf_parse_context;
template <typename OutputIt, typename Char> class basic_printf_context;
namespace internal {
namespace detail {
template <class Char> class formatbuf : public std::basic_streambuf<Char> {
private:
@@ -80,7 +80,7 @@ template <typename T, typename Char> class is_streamable {
// Write the content of buf to os.
template <typename Char>
void write(std::basic_ostream<Char>& os, buffer<Char>& buf) {
void write_buffer(std::basic_ostream<Char>& os, buffer<Char>& buf) {
const Char* buf_data = buf.data();
using unsigned_streamsize = std::make_unsigned<std::streamsize>::type;
unsigned_streamsize size = buf.size();
@@ -101,8 +101,8 @@ void format_value(buffer<Char>& buf, const T& value,
#if !defined(FMT_STATIC_THOUSANDS_SEPARATOR)
if (loc) output.imbue(loc.get<std::locale>());
#endif
output.exceptions(std::ios_base::failbit | std::ios_base::badbit);
output << value;
output.exceptions(std::ios_base::failbit | std::ios_base::badbit);
buf.resize(buf.size());
}
@@ -110,7 +110,8 @@ void format_value(buffer<Char>& buf, const T& value,
template <typename T, typename Char>
struct fallback_formatter<T, Char, enable_if_t<is_streamable<T, Char>::value>>
: private formatter<basic_string_view<Char>, Char> {
auto parse(basic_format_parse_context<Char>& ctx) -> decltype(ctx.begin()) {
FMT_CONSTEXPR auto parse(basic_format_parse_context<Char>& ctx)
-> decltype(ctx.begin()) {
return formatter<basic_string_view<Char>, Char>::parse(ctx);
}
template <typename ParseCtx,
@@ -136,14 +137,14 @@ struct fallback_formatter<T, Char, enable_if_t<is_streamable<T, Char>::value>>
return std::copy(buffer.begin(), buffer.end(), ctx.out());
}
};
} // namespace internal
} // namespace detail
template <typename Char>
void vprint(std::basic_ostream<Char>& os, basic_string_view<Char> format_str,
basic_format_args<buffer_context<type_identity_t<Char>>> args) {
basic_memory_buffer<Char> buffer;
internal::vformat_to(buffer, format_str, args);
internal::write(os, buffer);
detail::vformat_to(buffer, format_str, args);
detail::write_buffer(os, buffer);
}
/**
@@ -156,10 +157,10 @@ void vprint(std::basic_ostream<Char>& os, basic_string_view<Char> format_str,
\endrst
*/
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...));
detail::make_args_checked<Args...>(format_str, args...));
}
FMT_END_NAMESPACE

View File

@@ -14,7 +14,7 @@
#include "ostream.h"
FMT_BEGIN_NAMESPACE
namespace internal {
namespace detail {
// Checks if a value fits in int - used to avoid warnings about comparing
// signed and unsigned integers.
@@ -90,11 +90,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 +102,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 +133,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 +141,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 +162,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;
}
@@ -172,22 +179,20 @@ template <typename Char> class printf_width_handler {
};
template <typename Char, typename Context>
void printf(buffer<Char>& buf, basic_string_view<Char> format,
void vprintf(buffer<Char>& buf, basic_string_view<Char> format,
basic_format_args<Context> args) {
Context(std::back_inserter(buf), format, args).format();
}
} // namespace detail
template <typename OutputIt, typename Char, typename Context>
internal::truncating_iterator<OutputIt> printf(
internal::truncating_iterator<OutputIt> it, basic_string_view<Char> format,
// For printing into memory_buffer.
template <typename Char, typename Context>
FMT_DEPRECATED void printf(detail::buffer<Char>& buf,
basic_string_view<Char> format,
basic_format_args<Context> args) {
return Context(it, format, args).format();
return detail::vprintf(buf, format, args);
}
} // namespace internal
using internal::printf; // For printing into memory_buffer.
template <typename Range> class printf_arg_formatter;
using detail::vprintf;
template <typename Char>
class basic_printf_parse_context : public basic_format_parse_context<Char> {
@@ -200,15 +205,15 @@ template <typename OutputIt, typename Char> class basic_printf_context;
The ``printf`` argument formatter.
\endrst
*/
template <typename Range>
class printf_arg_formatter : public internal::arg_formatter_base<Range> {
template <typename OutputIt, typename Char>
class printf_arg_formatter : public detail::arg_formatter_base<OutputIt, Char> {
public:
using iterator = typename Range::iterator;
using iterator = OutputIt;
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 char_type = Char;
using base = detail::arg_formatter_base<OutputIt, Char>;
using context_type = basic_printf_context<OutputIt, Char>;
context_type& context_;
@@ -233,9 +238,9 @@ class printf_arg_formatter : public internal::arg_formatter_base<Range> {
\endrst
*/
printf_arg_formatter(iterator iter, format_specs& specs, context_type& ctx)
: base(Range(iter), &specs, internal::locale_ref()), context_(ctx) {}
: base(iter, &specs, detail::locale_ref()), context_(ctx) {}
template <typename T, FMT_ENABLE_IF(fmt::internal::is_integral<T>::value)>
template <typename T, FMT_ENABLE_IF(fmt::detail::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.
@@ -250,6 +255,10 @@ class printf_arg_formatter : public internal::arg_formatter_base<Range> {
return (*this)(static_cast<int>(value));
fmt_specs.sign = sign::none;
fmt_specs.alt = false;
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 base::operator()(value);
} else {
@@ -316,12 +325,14 @@ template <typename T> struct printf_formatter {
template <typename FormatContext>
auto format(const T& value, FormatContext& ctx) -> decltype(ctx.out()) {
internal::format_value(internal::get_container(ctx.out()), value);
detail::format_value(detail::get_container(ctx.out()), value);
return ctx.out();
}
};
/** This template formats data and writes the output to a writer. */
/**
This template formats data and writes the output through an output iterator.
*/
template <typename OutputIt, typename Char> class basic_printf_context {
public:
/** The character type for the output. */
@@ -351,9 +362,8 @@ template <typename OutputIt, typename Char> class basic_printf_context {
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.
Constructs a ``printf_context`` object. References to the arguments are
stored in the context object so make sure they have appropriate lifetimes.
\endrst
*/
basic_printf_context(OutputIt out, basic_string_view<char_type> format_str,
@@ -363,7 +373,7 @@ template <typename OutputIt, typename Char> class basic_printf_context {
OutputIt out() { return out_; }
void advance_to(OutputIt it) { out_ = it; }
internal::locale_ref locale() { return {}; }
detail::locale_ref locale() { return {}; }
format_arg arg(int id) const { return args_.get(id); }
@@ -374,7 +384,7 @@ template <typename OutputIt, typename Char> class basic_printf_context {
}
/** Formats stored arguments and writes the output to the range. */
template <typename ArgFormatter = printf_arg_formatter<buffer_range<Char>>>
template <typename ArgFormatter = printf_arg_formatter<OutputIt, Char>>
OutputIt format();
};
@@ -394,7 +404,9 @@ void basic_printf_context<OutputIt, Char>::parse_flags(format_specs& specs,
specs.fill[0] = '0';
break;
case ' ':
if (specs.sign != sign::plus) {
specs.sign = sign::space;
}
break;
case '#':
specs.alt = true;
@@ -412,7 +424,7 @@ basic_printf_context<OutputIt, Char>::get_arg(int arg_index) {
arg_index = parse_ctx_.next_arg_id();
else
parse_ctx_.check_arg_id(--arg_index);
return internal::get_arg(*this, arg_index);
return detail::get_arg(*this, arg_index);
}
template <typename OutputIt, typename Char>
@@ -424,7 +436,7 @@ int basic_printf_context<OutputIt, Char>::parse_header(const Char*& 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;
detail::error_handler eh;
int value = parse_nonnegative_int(it, end, eh);
if (it != end && *it == '$') { // value is an argument index
++it;
@@ -443,12 +455,12 @@ 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;
detail::error_handler eh;
specs.width = parse_nonnegative_int(it, end, eh);
} 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_type>(specs), get_arg()));
}
}
return arg_index;
@@ -476,38 +488,52 @@ OutputIt basic_printf_context<OutputIt, Char>::format() {
// 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");
if (arg_index == 0) 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;
detail::error_handler eh;
specs.precision = parse_nonnegative_int(it, end, eh);
} 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()));
} else {
specs.precision = 0;
}
}
format_arg arg = get_arg(arg_index);
if (specs.alt && visit_format_arg(internal::is_zero_int(), arg))
// 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>(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;
using detail::convert_arg;
switch (c) {
case 'h':
if (t == 'h') {
@@ -531,7 +557,7 @@ OutputIt basic_printf_context<OutputIt, Char>::format() {
convert_arg<intmax_t>(arg, t);
break;
case 'z':
convert_arg<std::size_t>(arg, t);
convert_arg<size_t>(arg, t);
break;
case 't':
convert_arg<std::ptrdiff_t>(arg, t);
@@ -556,7 +582,7 @@ OutputIt basic_printf_context<OutputIt, Char>::format() {
specs.type = 'd';
break;
case 'c':
visit_format_arg(internal::char_converter<basic_printf_context>(arg),
visit_format_arg(detail::char_converter<basic_printf_context>(arg),
arg);
break;
}
@@ -565,15 +591,14 @@ OutputIt basic_printf_context<OutputIt, Char>::format() {
start = it;
// Format argument.
visit_format_arg(ArgFormatter(out, specs, *this), arg);
out = visit_format_arg(ArgFormatter(out, specs, *this), arg);
}
return std::copy(start, it, out);
}
template <typename Char>
using basic_printf_context_t =
basic_printf_context<std::back_insert_iterator<internal::buffer<Char>>,
Char>;
basic_printf_context<std::back_insert_iterator<detail::buffer<Char>>, Char>;
using printf_context = basic_printf_context_t<char>;
using wprintf_context = basic_printf_context_t<wchar_t>;
@@ -610,7 +635,7 @@ inline std::basic_string<Char> vsprintf(
const S& format,
basic_format_args<basic_printf_context_t<type_identity_t<Char>>> args) {
basic_memory_buffer<Char> buffer;
printf(buffer, to_string_view(format), args);
vprintf(buffer, to_string_view(format), args);
return to_string(buffer);
}
@@ -624,7 +649,7 @@ inline std::basic_string<Char> vsprintf(
\endrst
*/
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>>>
inline std::basic_string<Char> sprintf(const S& format, const Args&... args) {
using context = basic_printf_context_t<Char>;
return vsprintf(to_string_view(format), make_format_args<context>(args...));
@@ -635,8 +660,8 @@ inline int vfprintf(
std::FILE* f, const S& format,
basic_format_args<basic_printf_context_t<type_identity_t<Char>>> args) {
basic_memory_buffer<Char> buffer;
printf(buffer, to_string_view(format), args);
std::size_t size = buffer.size();
vprintf(buffer, to_string_view(format), args);
size_t size = buffer.size();
return std::fwrite(buffer.data(), sizeof(Char), size, f) < size
? -1
: static_cast<int>(size);
@@ -652,7 +677,7 @@ inline int vfprintf(
\endrst
*/
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>>>
inline int fprintf(std::FILE* f, const S& format, const Args&... args) {
using context = basic_printf_context_t<Char>;
return vfprintf(f, to_string_view(format),
@@ -676,7 +701,7 @@ inline int vprintf(
\endrst
*/
template <typename S, typename... Args,
FMT_ENABLE_IF(internal::is_string<S>::value)>
FMT_ENABLE_IF(detail::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),
@@ -688,8 +713,8 @@ inline int vfprintf(
std::basic_ostream<Char>& os, const S& format,
basic_format_args<basic_printf_context_t<type_identity_t<Char>>> args) {
basic_memory_buffer<Char> buffer;
printf(buffer, to_string_view(format), args);
internal::write(os, buffer);
vprintf(buffer, to_string_view(format), args);
detail::write_buffer(os, buffer);
return static_cast<int>(buffer.size());
}
@@ -698,7 +723,7 @@ 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,
detail::buffer<Char>& out, basic_string_view<Char> format_str,
basic_format_args<type_identity_t<Context>> args) {
typename ArgFormatter::iterator iter(out);
Context(iter, format_str, args).template format<ArgFormatter>();

View File

@@ -33,7 +33,7 @@ template <typename Char> struct formatting_base {
template <typename Char, typename Enable = void>
struct formatting_range : formatting_base<Char> {
static FMT_CONSTEXPR_DECL const std::size_t range_length_limit =
static FMT_CONSTEXPR_DECL const size_t range_length_limit =
FMT_RANGE_OUTPUT_LENGTH_LIMIT; // output only up to N items from the
// range.
Char prefix;
@@ -54,7 +54,7 @@ struct formatting_tuple : formatting_base<Char> {
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) {
@@ -118,26 +118,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>
@@ -185,11 +183,11 @@ FMT_CONSTEXPR const wchar_t* format_str_quoted(bool add_space, const wchar_t) {
return add_space ? L" '{}'" : L"'{}'";
}
} // namespace internal
} // namespace detail
template <typename T> struct is_tuple_like {
static FMT_CONSTEXPR_DECL const bool value =
internal::is_tuple_like_<T>::value && !internal::is_range_<T>::value;
detail::is_tuple_like_<T>::value && !detail::is_range_<T>::value;
};
template <typename TupleT, typename Char>
@@ -202,17 +200,17 @@ struct formatter<TupleT, Char, enable_if_t<fmt::is_tuple_like<TupleT>::value>> {
if (formatting.add_prepostfix_space) {
*out++ = ' ';
}
out = internal::copy(formatting.delimiter, out);
out = detail::copy(formatting.delimiter, out);
}
out = format_to(out,
internal::format_str_quoted(
detail::format_str_quoted(
(formatting.add_delimiter_spaces && i > 0), v),
v);
++i;
}
formatting_tuple<Char>& formatting;
std::size_t& i;
size_t& i;
typename std::add_lvalue_reference<decltype(
std::declval<FormatContext>().out())>::type out;
};
@@ -228,14 +226,14 @@ struct formatter<TupleT, Char, enable_if_t<fmt::is_tuple_like<TupleT>::value>> {
template <typename FormatContext = format_context>
auto format(const TupleT& values, FormatContext& ctx) -> decltype(ctx.out()) {
auto out = ctx.out();
std::size_t i = 0;
internal::copy(formatting.prefix, out);
size_t i = 0;
detail::copy(formatting.prefix, out);
internal::for_each(values, format_each<FormatContext>{formatting, i, out});
detail::for_each(values, format_each<FormatContext>{formatting, i, out});
if (formatting.add_prepostfix_space) {
*out++ = ' ';
}
internal::copy(formatting.postfix, out);
detail::copy(formatting.postfix, out);
return ctx.out();
}
@@ -243,10 +241,9 @@ struct formatter<TupleT, Char, enable_if_t<fmt::is_tuple_like<TupleT>::value>> {
template <typename T, typename Char> struct is_range {
static FMT_CONSTEXPR_DECL const bool value =
internal::is_range_<T>::value &&
!internal::is_like_std_string<T>::value &&
detail::is_range_<T>::value && !detail::is_like_std_string<T>::value &&
!std::is_convertible<T, std::basic_string<Char>>::value &&
!std::is_constructible<internal::std_string_view<Char>, T>::value;
!std::is_constructible<detail::std_string_view<Char>, T>::value;
};
template <typename RangeT, typename Char>
@@ -262,15 +259,17 @@ struct formatter<RangeT, Char,
template <typename FormatContext>
typename FormatContext::iterator format(const RangeT& values,
FormatContext& ctx) {
auto out = internal::copy(formatting.prefix, ctx.out());
std::size_t i = 0;
for (auto it = values.begin(), end = values.end(); it != end; ++it) {
auto out = detail::copy(formatting.prefix, ctx.out());
size_t i = 0;
auto it = values.begin();
auto end = values.end();
for (; it != end; ++it) {
if (i > 0) {
if (formatting.add_prepostfix_space) *out++ = ' ';
out = internal::copy(formatting.delimiter, out);
out = detail::copy(formatting.delimiter, out);
}
out = format_to(out,
internal::format_str_quoted(
detail::format_str_quoted(
(formatting.add_delimiter_spaces && i > 0), *it),
*it);
if (++i > formatting.range_length_limit) {
@@ -279,11 +278,11 @@ struct formatter<RangeT, Char,
}
}
if (formatting.add_prepostfix_space) *out++ = ' ';
return internal::copy(formatting.postfix, out);
return detail::copy(formatting.postfix, out);
}
};
template <typename Char, typename... T> struct tuple_arg_join : internal::view {
template <typename Char, typename... T> struct tuple_arg_join : detail::view {
const std::tuple<T...>& tuple;
basic_string_view<Char> sep;
@@ -301,14 +300,14 @@ struct formatter<tuple_arg_join<Char, T...>, Char> {
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)>{});
return format(value, ctx, detail::make_index_sequence<sizeof...(T)>{});
}
private:
template <typename FormatContext, size_t... N>
typename FormatContext::iterator format(
const tuple_arg_join<Char, T...>& value, FormatContext& ctx,
internal::index_sequence<N...>) {
detail::index_sequence<N...>) {
return format_args(value, ctx, std::get<N>(value.tuple)...);
}
@@ -371,14 +370,14 @@ 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) {
arg_join<const T*, const 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) {
arg_join<const T*, const T*, wchar_t> join(std::initializer_list<T> list,
wstring_view sep) {
return join(std::begin(list), std::end(list), sep);
}

View File

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

View File

@@ -17,6 +17,8 @@
#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

View File

@@ -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

View File

@@ -75,56 +75,56 @@ public:
// FormatString is a type derived from fmt::compile_string
template<typename FormatString, typename std::enable_if<fmt::is_compile_string<FormatString>::value, int>::type = 0, typename... Args>
void log(source_loc loc, level::level_enum lvl, const FormatString &fmt, const Args &... args)
void log(source_loc loc, level::level_enum lvl, const FormatString &fmt, const Args &...args)
{
log_(loc, lvl, fmt, args...);
}
// FormatString is NOT a type derived from fmt::compile_string but is a string_view_t or can be implicitly converted to one
template<typename... Args>
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, string_view_t fmt, const Args &...args)
{
log_(loc, lvl, fmt, args...);
}
template<typename FormatString, typename... Args>
void log(level::level_enum lvl, const FormatString &fmt, const Args &... args)
void log(level::level_enum lvl, const FormatString &fmt, const Args &...args)
{
log(source_loc{}, lvl, fmt, args...);
}
template<typename FormatString, typename... Args>
void trace(const FormatString &fmt, const Args &... args)
void trace(const FormatString &fmt, const Args &...args)
{
log(level::trace, fmt, args...);
}
template<typename FormatString, typename... Args>
void debug(const FormatString &fmt, const Args &... args)
void debug(const FormatString &fmt, const Args &...args)
{
log(level::debug, fmt, args...);
}
template<typename FormatString, typename... Args>
void info(const FormatString &fmt, const Args &... args)
void info(const FormatString &fmt, const Args &...args)
{
log(level::info, fmt, args...);
}
template<typename FormatString, typename... Args>
void warn(const FormatString &fmt, const Args &... args)
void warn(const FormatString &fmt, const Args &...args)
{
log(level::warn, fmt, args...);
}
template<typename FormatString, typename... Args>
void error(const FormatString &fmt, const Args &... args)
void error(const FormatString &fmt, const Args &...args)
{
log(level::err, fmt, args...);
}
template<typename FormatString, typename... Args>
void critical(const FormatString &fmt, const Args &... args)
void critical(const FormatString &fmt, const Args &...args)
{
log(level::critical, fmt, args...);
}
@@ -225,7 +225,7 @@ public:
#else
template<typename... Args>
void log(source_loc loc, level::level_enum lvl, wstring_view_t fmt, const Args &... 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();
@@ -326,7 +326,7 @@ protected:
// common implementation for after templated public api has been resolved
template<typename FormatString, typename... Args>
void log_(source_loc loc, level::level_enum lvl, const FormatString &fmt, const Args &... args)
void log_(source_loc loc, level::level_enum lvl, const FormatString &fmt, const Args &...args)
{
bool log_enabled = should_log(lvl);
bool traceback_enabled = tracer_.enabled();

View File

@@ -45,12 +45,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;
@@ -1246,9 +1246,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>();
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 +1285,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;
}

View File

@@ -22,7 +22,7 @@ namespace details {
// padding information.
struct padding_info
{
enum pad_side
enum class pad_side
{
left,
right,
@@ -42,7 +42,7 @@ struct padding_info
return enabled_;
}
size_t width_ = 0;
pad_side side_ = left;
pad_side side_ = pad_side::left;
bool truncate_ = false;
bool enabled_ = false;
};
@@ -92,7 +92,7 @@ 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, const Args &...args)
{
custom_handlers_[flag] = details::make_unique<T>(args...);
return *this;

View File

@@ -8,12 +8,13 @@
#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>
// Avoid including windows.h (https://stackoverflow.com/a/30741042)
extern "C" __declspec(dllimport) void __stdcall OutputDebugStringA(const char *lpOutputString);
namespace spdlog {
namespace sinks {
/*
@@ -23,12 +24,11 @@ template<typename Mutex>
class msvc_sink : public base_sink<Mutex>
{
public:
explicit msvc_sink() {}
msvc_sink() = default;
protected:
void sink_it_(const details::log_msg &msg) override
{
memory_buf_t formatted;
base_sink<Mutex>::formatter_->format(msg, formatted);
OutputDebugStringA(fmt::to_string(formatted).c_str());

View File

@@ -11,6 +11,15 @@
#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>
#include <fileapi.h> // WriteFile (..)
#include <io.h> // _get_osfhandle(..)
#include <stdio.h> // _fileno(..)
#endif // WIN32
namespace spdlog {
namespace sinks {
@@ -20,7 +29,16 @@ SPDLOG_INLINE stdout_sink_base<ConsoleMutex>::stdout_sink_base(FILE *file)
: mutex_(ConsoleMutex::mutex())
, file_(file)
, formatter_(details::make_unique<spdlog::pattern_formatter>())
{}
{
#ifdef _WIN32
// get windows handle from the FILE* object
handle_ = (HANDLE)::_get_osfhandle(::_fileno(file_));
if (handle_ == INVALID_HANDLE_VALUE)
{
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)
@@ -28,8 +46,19 @@ SPDLOG_INLINE void stdout_sink_base<ConsoleMutex>::log(const details::log_msg &m
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
#ifdef _WIN32
::fflush(file_); // flush in case there is somthing in this file_ already
auto size = static_cast<DWORD>(formatted.size());
DWORD bytes_written = 0;
bool ok = ::WriteFile(handle_, formatted.data(), size, &bytes_written, nullptr) != 0;
if (!ok)
{
throw_spdlog_ex("stdout_sink_base: WriteFile() failed. GetLastError(): " + std::to_string(::GetLastError()));
}
#else
::fwrite(formatted.data(), sizeof(char), formatted.size(), file_);
::fflush(file_); // flush every line to terminal
#endif // WIN32
}
template<typename ConsoleMutex>

View File

@@ -8,6 +8,10 @@
#include <spdlog/sinks/sink.h>
#include <cstdio>
#ifdef _WIN32
#include <spdlog/details/windows_include.h>
#endif
namespace spdlog {
namespace sinks {
@@ -36,6 +40,9 @@ protected:
mutex_t &mutex_;
FILE *file_;
std::unique_ptr<spdlog::formatter> formatter_;
#ifdef _WIN32
HANDLE handle_;
#endif // WIN32
};
template<typename ConsoleMutex>

View File

@@ -146,22 +146,12 @@ void SPDLOG_INLINE wincolor_sink<ConsoleMutex>::write_to_file_(const memory_buf_
return;
}
auto size = static_cast<DWORD>(formatted.size());
if (size == 0)
{
return;
}
DWORD total_written = 0;
do
{
DWORD bytes_written = 0;
bool ok = ::WriteFile(out_handle_, formatted.data() + total_written, size - total_written, &bytes_written, nullptr) != 0;
if (!ok || bytes_written == 0)
bool ok = ::WriteFile(out_handle_, formatted.data(), size, &bytes_written, nullptr) != 0;
if (!ok)
{
throw_spdlog_ex("wincolor_sink: write_to_file_ failed. GetLastError(): " + std::to_string(::GetLastError()));
throw_spdlog_ex("wincolor_sink: ::WriteFile() failed. GetLastError(): " + std::to_string(::GetLastError()));
}
total_written += bytes_written;
} while (total_written < size);
}
// wincolor_stdout_sink

View File

@@ -47,6 +47,16 @@ SPDLOG_INLINE void dump_backtrace()
default_logger_raw()->dump_backtrace();
}
SPDLOG_INLINE level::level_enum get_level()
{
return default_logger_raw()->level();
}
SPDLOG_INLINE bool should_log(level::level_enum log_level)
{
return default_logger_raw()->should_log(log_level);
}
SPDLOG_INLINE void set_level(level::level_enum log_level)
{
details::registry::instance().set_level(log_level);

View File

@@ -31,7 +31,7 @@ using default_factory = synchronous_factory;
// Example:
// spdlog::create<daily_file_sink_st>("logger_name", "dailylog_filename", 11, 59);
template<typename Sink, typename... SinkArgs>
inline std::shared_ptr<spdlog::logger> create(std::string logger_name, SinkArgs &&... sink_args)
inline std::shared_ptr<spdlog::logger> create(std::string logger_name, SinkArgs &&...sink_args)
{
return default_factory::create<Sink>(std::move(logger_name), std::forward<SinkArgs>(sink_args)...);
}
@@ -67,9 +67,15 @@ SPDLOG_API void disable_backtrace();
// call dump backtrace on default logger
SPDLOG_API void dump_backtrace();
// Get global logging level
SPDLOG_API level::level_enum get_level();
// Set global logging level
SPDLOG_API void set_level(level::level_enum log_level);
// Determine whether the default logger should log messages with a certain level
SPDLOG_API bool should_log(level::level_enum lvl);
// Set global flush level
SPDLOG_API void flush_on(level::level_enum log_level);
@@ -122,49 +128,49 @@ SPDLOG_API spdlog::logger *default_logger_raw();
SPDLOG_API void set_default_logger(std::shared_ptr<spdlog::logger> default_logger);
template<typename FormatString, typename... Args>
inline void log(source_loc source, level::level_enum lvl, const FormatString &fmt, const Args &... args)
inline void log(source_loc source, level::level_enum lvl, const FormatString &fmt, const Args &...args)
{
default_logger_raw()->log(source, lvl, fmt, args...);
}
template<typename FormatString, typename... Args>
inline void log(level::level_enum lvl, const FormatString &fmt, const Args &... args)
inline void log(level::level_enum lvl, const FormatString &fmt, const Args &...args)
{
default_logger_raw()->log(source_loc{}, lvl, fmt, args...);
}
template<typename FormatString, typename... Args>
inline void trace(const FormatString &fmt, const Args &... args)
inline void trace(const FormatString &fmt, const Args &...args)
{
default_logger_raw()->trace(fmt, args...);
}
template<typename FormatString, typename... Args>
inline void debug(const FormatString &fmt, const Args &... args)
inline void debug(const FormatString &fmt, const Args &...args)
{
default_logger_raw()->debug(fmt, args...);
}
template<typename FormatString, typename... Args>
inline void info(const FormatString &fmt, const Args &... args)
inline void info(const FormatString &fmt, const Args &...args)
{
default_logger_raw()->info(fmt, args...);
}
template<typename FormatString, typename... Args>
inline void warn(const FormatString &fmt, const Args &... args)
inline void warn(const FormatString &fmt, const Args &...args)
{
default_logger_raw()->warn(fmt, args...);
}
template<typename FormatString, typename... Args>
inline void error(const FormatString &fmt, const Args &... args)
inline void error(const FormatString &fmt, const Args &...args)
{
default_logger_raw()->error(fmt, args...);
}
template<typename FormatString, typename... Args>
inline void critical(const FormatString &fmt, const Args &... args)
inline void critical(const FormatString &fmt, const Args &...args)
{
default_logger_raw()->critical(fmt, args...);
}

View File

@@ -0,0 +1,61 @@
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
#pragma once
#include <spdlog/fmt/fmt.h>
// Stopwatch support for spdlog (using std::chrono::steady_clock).
// Displays elapsed seconds since construction as double.
//
// Usage:
//
// spdlog::stopwatch sw;
// ...
// spdlog::debug("Elapsed: {} seconds", sw); => "Elapsed 0.005116733 seconds"
// spdlog::info("Elapsed: {:.6} seconds", sw); => "Elapsed 0.005163 seconds"
//
//
// If other units are needed (e.g. millis instead of double), include "fmt/chrono.h" and use "duration_cast<..>(sw.elapsed())":
//
// #include <spdlog/fmt/chrono.h>
//..
// using std::chrono::duration_cast;
// using std::chrono::milliseconds;
// spdlog::info("Elapsed {}", duration_cast<milliseconds>(sw.elapsed())); => "Elapsed 5ms"
namespace spdlog {
class stopwatch
{
using clock = std::chrono::steady_clock;
std::chrono::time_point<clock> start_tp_;
public:
stopwatch()
: start_tp_{clock::now()}
{}
std::chrono::duration<double> elapsed() const
{
return std::chrono::duration<double>(clock::now() - start_tp_);
}
void reset()
{
start_tp_ = clock ::now();
}
};
} // namespace spdlog
// Support for fmt formatting (e.g. "{:012.9}" or just "{}")
namespace fmt {
template<>
struct formatter<spdlog::stopwatch> : formatter<double>
{
template<typename FormatContext>
auto format(const spdlog::stopwatch &sw, FormatContext &ctx) -> decltype(ctx.out())
{
return formatter<double>::format(sw.elapsed().count(), ctx);
}
};
} // namespace fmt

View File

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

View File

@@ -10,12 +10,12 @@
#include <spdlog/fmt/bundled/format-inl.h>
FMT_BEGIN_NAMESPACE
namespace internal {
namespace detail {
template<typename T>
int format_float(char *buf, std::size_t size, const char *format, int precision, T value)
{
#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
#ifdef FMT_FUZZ
if (precision > 100000)
throw std::runtime_error("fuzz mode - avoid large allocation inside snprintf");
#endif
@@ -23,167 +23,41 @@ int format_float(char *buf, std::size_t size, const char *format, int precision,
int (*snprintf_ptr)(char *, size_t, const char *, ...) = FMT_SNPRINTF;
return precision < 0 ? snprintf_ptr(buf, size, format, value) : snprintf_ptr(buf, size, format, precision, value);
}
struct sprintf_specs
{
int precision;
char type;
bool alt : 1;
} // namespace detail
template<typename Char>
constexpr sprintf_specs(basic_format_specs<Char> specs)
: precision(specs.precision)
, type(specs.type)
, alt(specs.alt)
{}
constexpr bool has_precision() const
{
return precision >= 0;
}
};
// This is deprecated and is kept only to preserve ABI compatibility.
template<typename Double>
char *sprintf_format(Double value, internal::buffer<char> &buf, sprintf_specs specs)
{
// Buffer capacity must be non-zero, otherwise MSVC's vsnprintf_s will fail.
FMT_ASSERT(buf.capacity() != 0, "empty buffer");
// Build format string.
enum
{
max_format_size = 10
}; // longest format: %#-*.*Lg
char format[max_format_size];
char *format_ptr = format;
*format_ptr++ = '%';
if (specs.alt || !specs.type)
*format_ptr++ = '#';
if (specs.precision >= 0)
{
*format_ptr++ = '.';
*format_ptr++ = '*';
}
if (std::is_same<Double, long double>::value)
*format_ptr++ = 'L';
char type = specs.type;
if (type == '%')
type = 'f';
else if (type == 0 || type == 'n')
type = 'g';
#if FMT_MSC_VER
if (type == 'F')
{
// MSVC's printf doesn't support 'F'.
type = 'f';
}
#endif
*format_ptr++ = type;
*format_ptr = '\0';
// Format using snprintf.
char *start = nullptr;
char *decimal_point_pos = nullptr;
for (;;)
{
std::size_t buffer_size = buf.capacity();
start = &buf[0];
int result = format_float(start, buffer_size, format, specs.precision, value);
if (result >= 0)
{
unsigned n = internal::to_unsigned(result);
if (n < buf.capacity())
{
// Find the decimal point.
auto p = buf.data(), end = p + n;
if (*p == '+' || *p == '-')
++p;
if (specs.type != 'a' && specs.type != 'A')
{
while (p < end && *p >= '0' && *p <= '9')
++p;
if (p < end && *p != 'e' && *p != 'E')
{
decimal_point_pos = p;
if (!specs.type)
{
// Keep only one trailing zero after the decimal point.
++p;
if (*p == '0')
++p;
while (p != end && *p >= '1' && *p <= '9')
++p;
char *where = p;
while (p != end && *p == '0')
++p;
if (p == end || *p < '0' || *p > '9')
{
if (p != end)
std::memmove(where, p, to_unsigned(end - p));
n -= static_cast<unsigned>(p - where);
}
}
}
}
buf.resize(n);
break; // The buffer is large enough - continue with formatting.
}
buf.reserve(n + 1);
}
else
{
// If result is negative we ask to increase the capacity by at least 1,
// but as std::vector, the buffer grows exponentially.
buf.reserve(buf.capacity() + 1);
}
}
return decimal_point_pos;
}
} // namespace internal
template FMT_API char *internal::sprintf_format(double, internal::buffer<char> &, sprintf_specs);
template FMT_API char *internal::sprintf_format(long double, internal::buffer<char> &, sprintf_specs);
template struct FMT_INSTANTIATION_DEF_API internal::basic_data<void>;
template struct FMT_INSTANTIATION_DEF_API detail::basic_data<void>;
// Workaround a bug in MSVC2013 that prevents instantiation of format_float.
int (*instantiate_format_float)(double, int, internal::float_specs, internal::buffer<char> &) = internal::format_float;
int (*instantiate_format_float)(double, int, detail::float_specs, detail::buffer<char> &) = detail::format_float;
#ifndef FMT_STATIC_THOUSANDS_SEPARATOR
template FMT_API internal::locale_ref::locale_ref(const std::locale &loc);
template FMT_API std::locale internal::locale_ref::get<std::locale>() const;
template FMT_API detail::locale_ref::locale_ref(const std::locale &loc);
template FMT_API std::locale detail::locale_ref::get<std::locale>() const;
#endif
// Explicit instantiations for char.
template FMT_API std::string internal::grouping_impl<char>(locale_ref);
template FMT_API char internal::thousands_sep_impl(locale_ref);
template FMT_API char internal::decimal_point_impl(locale_ref);
template FMT_API std::string detail::grouping_impl<char>(locale_ref);
template FMT_API char detail::thousands_sep_impl(locale_ref);
template FMT_API char detail::decimal_point_impl(locale_ref);
template FMT_API void internal::buffer<char>::append(const char *, const char *);
template FMT_API void detail::buffer<char>::append(const char *, const char *);
template FMT_API void internal::arg_map<format_context>::init(const basic_format_args<format_context> &args);
template FMT_API FMT_BUFFER_CONTEXT(char)::iterator detail::vformat_to(
detail::buffer<char> &, string_view, basic_format_args<FMT_BUFFER_CONTEXT(char)>);
template FMT_API std::string internal::vformat<char>(string_view, basic_format_args<format_context>);
template FMT_API format_context::iterator internal::vformat_to(internal::buffer<char> &, string_view, basic_format_args<format_context>);
template FMT_API int internal::snprintf_float(double, int, internal::float_specs, internal::buffer<char> &);
template FMT_API int internal::snprintf_float(long double, int, internal::float_specs, internal::buffer<char> &);
template FMT_API int internal::format_float(double, int, internal::float_specs, internal::buffer<char> &);
template FMT_API int internal::format_float(long double, int, internal::float_specs, internal::buffer<char> &);
template FMT_API int detail::snprintf_float(double, int, detail::float_specs, detail::buffer<char> &);
template FMT_API int detail::snprintf_float(long double, int, detail::float_specs, detail::buffer<char> &);
template FMT_API int detail::format_float(double, int, detail::float_specs, detail::buffer<char> &);
template FMT_API int detail::format_float(long double, int, detail::float_specs, detail::buffer<char> &);
// Explicit instantiations for wchar_t.
template FMT_API std::string internal::grouping_impl<wchar_t>(locale_ref);
template FMT_API wchar_t internal::thousands_sep_impl(locale_ref);
template FMT_API wchar_t internal::decimal_point_impl(locale_ref);
template FMT_API std::string detail::grouping_impl<wchar_t>(locale_ref);
template FMT_API wchar_t detail::thousands_sep_impl(locale_ref);
template FMT_API wchar_t detail::decimal_point_impl(locale_ref);
template FMT_API void internal::buffer<wchar_t>::append(const wchar_t *, const wchar_t *);
template FMT_API std::wstring internal::vformat<wchar_t>(wstring_view, basic_format_args<wformat_context>);
template FMT_API void detail::buffer<wchar_t>::append(const wchar_t *, const wchar_t *);
FMT_END_NAMESPACE
#endif // !SPDLOG_FMT_EXTERNAL

View File

@@ -1,18 +1,17 @@
cmake_minimum_required(VERSION 3.2)
cmake_minimum_required(VERSION 3.10)
project(spdlog_utests CXX)
if (NOT TARGET spdlog)
if(NOT TARGET spdlog)
# Stand-alone build
find_package(spdlog REQUIRED)
endif ()
endif()
include(../cmake/utils.cmake)
find_package(PkgConfig)
if (PkgConfig_FOUND)
if(PkgConfig_FOUND)
pkg_check_modules(systemd libsystemd)
endif ()
endif()
set(SPDLOG_UTESTS_SOURCES
test_file_helper.cpp
@@ -33,15 +32,16 @@ set(SPDLOG_UTESTS_SOURCES
test_backtrace.cpp
test_create_dir.cpp
test_cfg.cpp
test_time_point.cpp)
test_time_point.cpp
test_stopwatch.cpp)
if (NOT SPDLOG_NO_EXCEPTIONS)
if(NOT SPDLOG_NO_EXCEPTIONS)
list(APPEND SPDLOG_UTESTS_SOURCES test_errors.cpp)
endif ()
endif()
if (systemd_FOUND)
if(systemd_FOUND)
list(APPEND SPDLOG_UTESTS_SOURCES test_systemd.cpp)
endif ()
endif()
enable_testing()
@@ -49,22 +49,22 @@ function(spdlog_prepare_test test_target spdlog_lib)
add_executable(${test_target} ${SPDLOG_UTESTS_SOURCES})
spdlog_enable_warnings(${test_target})
target_link_libraries(${test_target} PRIVATE ${spdlog_lib})
if (systemd_FOUND)
if(systemd_FOUND)
target_link_libraries(${test_target} PRIVATE ${systemd_LIBRARIES})
endif ()
if (SPDLOG_SANITIZE_ADDRESS)
endif()
if(SPDLOG_SANITIZE_ADDRESS)
spdlog_enable_sanitizer(${test_target})
endif ()
endif()
add_test(NAME ${test_target} COMMAND ${test_target})
set_tests_properties(${test_target} PROPERTIES RUN_SERIAL ON)
endfunction()
# The compiled library tests
if (SPDLOG_BUILD_TESTS OR SPDLOG_BUILD_ALL)
if(SPDLOG_BUILD_TESTS OR SPDLOG_BUILD_ALL)
spdlog_prepare_test(spdlog-utests spdlog::spdlog)
endif ()
endif()
# The header-only library version tests
if (SPDLOG_BUILD_TESTS_HO OR SPDLOG_BUILD_ALL)
if(SPDLOG_BUILD_TESTS_HO OR SPDLOG_BUILD_ALL)
spdlog_prepare_test(spdlog-utests-ho spdlog::spdlog_header_only)
endif ()
endif()

View File

@@ -1,3 +1,4 @@
#include "includes.h"
#include "test_sink.h"
@@ -41,21 +42,23 @@ TEST_CASE("argv2", "[cfg]")
auto l1 = spdlog::create<test_sink_st>("l1");
REQUIRE(l1->level() == spdlog::level::warn);
REQUIRE(spdlog::default_logger()->level() == spdlog::level::trace);
spdlog::set_level(spdlog::level::info);
}
TEST_CASE("argv3", "[cfg]")
{
spdlog::set_level(spdlog::level::trace);
spdlog::drop("l1");
const char *argv[] = {"ignore", "SPDLOG_LEVEL="};
const char *argv[] = {"ignore", "SPDLOG_LEVEL=junk_name=warn"};
load_argv_levels(2, argv);
auto l1 = spdlog::create<test_sink_st>("l1");
REQUIRE(l1->level() == spdlog::level::info);
REQUIRE(spdlog::default_logger()->level() == spdlog::level::info);
REQUIRE(l1->level() == spdlog::level::trace);
REQUIRE(spdlog::default_logger()->level() == spdlog::level::trace);
}
TEST_CASE("argv4", "[cfg]")
{
spdlog::set_level(spdlog::level::info);
spdlog::drop("l1");
const char *argv[] = {"ignore", "SPDLOG_LEVEL=junk"};
load_argv_levels(2, argv);
@@ -65,6 +68,7 @@ TEST_CASE("argv4", "[cfg]")
TEST_CASE("argv5", "[cfg]")
{
spdlog::set_level(spdlog::level::info);
spdlog::drop("l1");
const char *argv[] = {"ignore", "ignore", "SPDLOG_LEVEL=l1=warn,trace"};
load_argv_levels(3, argv);
@@ -91,3 +95,89 @@ TEST_CASE("argv7", "[cfg]")
REQUIRE(spdlog::default_logger()->level() == spdlog::level::err);
spdlog::set_level(spdlog::level::info);
}
TEST_CASE("level-not-set-test1", "[cfg]")
{
spdlog::drop("l1");
const char *argv[] = {"ignore", ""};
load_argv_levels(2, argv);
auto l1 = spdlog::create<spdlog::sinks::test_sink_st>("l1");
l1->set_level(spdlog::level::trace);
REQUIRE(l1->level() == spdlog::level::trace);
REQUIRE(spdlog::default_logger()->level() == spdlog::level::info);
}
TEST_CASE("level-not-set-test2", "[cfg]")
{
spdlog::drop("l1");
spdlog::drop("l2");
const char *argv[] = {"ignore", "SPDLOG_LEVEL=l1=trace"};
auto l1 = spdlog::create<spdlog::sinks::test_sink_st>("l1");
l1->set_level(spdlog::level::warn);
auto l2 = spdlog::create<spdlog::sinks::test_sink_st>("l2");
l2->set_level(spdlog::level::warn);
load_argv_levels(2, argv);
REQUIRE(l1->level() == spdlog::level::trace);
REQUIRE(l2->level() == spdlog::level::warn);
REQUIRE(spdlog::default_logger()->level() == spdlog::level::info);
}
TEST_CASE("level-not-set-test3", "[cfg]")
{
spdlog::drop("l1");
spdlog::drop("l2");
const char *argv[] = {"ignore", "SPDLOG_LEVEL=l1=trace"};
load_argv_levels(2, argv);
auto l1 = spdlog::create<spdlog::sinks::test_sink_st>("l1");
auto l2 = spdlog::create<spdlog::sinks::test_sink_st>("l2");
REQUIRE(l1->level() == spdlog::level::trace);
REQUIRE(l2->level() == spdlog::level::info);
REQUIRE(spdlog::default_logger()->level() == spdlog::level::info);
}
TEST_CASE("level-not-set-test4", "[cfg]")
{
spdlog::drop("l1");
spdlog::drop("l2");
const char *argv[] = {"ignore", "SPDLOG_LEVEL=l1=trace,warn"};
load_argv_levels(2, argv);
auto l1 = spdlog::create<spdlog::sinks::test_sink_st>("l1");
auto l2 = spdlog::create<spdlog::sinks::test_sink_st>("l2");
REQUIRE(l1->level() == spdlog::level::trace);
REQUIRE(l2->level() == spdlog::level::warn);
REQUIRE(spdlog::default_logger()->level() == spdlog::level::warn);
}
TEST_CASE("level-not-set-test5", "[cfg]")
{
spdlog::drop("l1");
spdlog::drop("l2");
const char *argv[] = {"ignore", "SPDLOG_LEVEL=l1=junk,warn"};
load_argv_levels(2, argv);
auto l1 = spdlog::create<spdlog::sinks::test_sink_st>("l1");
auto l2 = spdlog::create<spdlog::sinks::test_sink_st>("l2");
REQUIRE(l1->level() == spdlog::level::warn);
REQUIRE(l2->level() == spdlog::level::warn);
REQUIRE(spdlog::default_logger()->level() == spdlog::level::warn);
}
TEST_CASE("restore-to-default", "[cfg]")
{
spdlog::drop("l1");
spdlog::drop("l2");
const char *argv[] = {"ignore", "SPDLOG_LEVEL=info"};
load_argv_levels(2, argv);
REQUIRE(spdlog::default_logger()->level() == spdlog::level::info);
}

View File

@@ -5,7 +5,7 @@ using spdlog::memory_buf_t;
// log to str and return it
template<typename... Args>
static std::string log_to_str(const std::string &msg, const Args &... args)
static std::string log_to_str(const std::string &msg, const Args &...args)
{
std::ostringstream oss;
auto oss_sink = std::make_shared<spdlog::sinks::ostream_sink_mt>(oss);
@@ -227,7 +227,7 @@ TEST_CASE("paddinng_truncate", "[pattern_formatter]")
REQUIRE(log_to_str("123456", "%0!v", spdlog::pattern_time_type::local, "\n") == "\n");
}
TEST_CASE("paddinng_truncate_funcname", "[pattern_formatter]")
TEST_CASE("padding_truncate_funcname", "[pattern_formatter]")
{
spdlog::sinks::test_sink_st test_sink;
@@ -237,13 +237,28 @@ TEST_CASE("paddinng_truncate_funcname", "[pattern_formatter]")
spdlog::details::log_msg msg1{spdlog::source_loc{"ignored", 1, "func"}, "test_logger", spdlog::level::info, "message"};
test_sink.log(msg1);
REQUIRE(test_sink.lines()[0] == "message [ func]");
spdlog::details::log_msg msg2{spdlog::source_loc{"ignored", 1, "function"}, "test_logger", spdlog::level::info, "message"};
test_sink.log(msg2);
REQUIRE(test_sink.lines()[1] == "message [funct]");
}
auto lines = test_sink.lines();
REQUIRE(lines[0] == "message [ func]");
REQUIRE(lines[1] == "message [funct]");
TEST_CASE("padding_funcname", "[pattern_formatter]")
{
spdlog::sinks::test_sink_st test_sink;
const char *pattern = "%v [%10!]";
auto formatter = std::unique_ptr<spdlog::formatter>(new spdlog::pattern_formatter(pattern));
test_sink.set_formatter(std::move(formatter));
spdlog::details::log_msg msg1{spdlog::source_loc{"ignored", 1, "func"}, "test_logger", spdlog::level::info, "message"};
test_sink.log(msg1);
REQUIRE(test_sink.lines()[0] == "message [ func]");
spdlog::details::log_msg msg2{spdlog::source_loc{"ignored", 1, "func567890123"}, "test_logger", spdlog::level::info, "message"};
test_sink.log(msg2);
REQUIRE(test_sink.lines()[1] == "message [func567890123]");
}
TEST_CASE("clone-default-formatter", "[pattern_formatter]")

35
tests/test_stopwatch.cpp Normal file
View File

@@ -0,0 +1,35 @@
#include "includes.h"
#include "test_sink.h"
#include "spdlog/stopwatch.h"
TEST_CASE("stopwatch1", "[stopwatch]")
{
using std::chrono::milliseconds;
milliseconds wait_ms(250);
milliseconds tolerance_ms(250);
spdlog::stopwatch sw;
std::this_thread::sleep_for(wait_ms);
REQUIRE(sw.elapsed() >= wait_ms);
REQUIRE(sw.elapsed() <= wait_ms + tolerance_ms);
}
TEST_CASE("stopwatch2", "[stopwatch]")
{
using spdlog::sinks::test_sink_st;
std::chrono::duration<double> wait_duration(0.250);
std::chrono::duration<double> tolerance_duration(0.250);
auto test_sink = std::make_shared<test_sink_st>();
spdlog::stopwatch sw;
spdlog::logger logger("test-stopwatch", test_sink);
logger.set_pattern("%v");
std::this_thread::sleep_for(wait_duration);
logger.info("{}", sw);
auto val = std::stod(test_sink->lines()[0]);
REQUIRE(val >= wait_duration.count());
REQUIRE(val <= (wait_duration + tolerance_duration).count());
}