From ffc2fe8eefa092efa4c851e603aaaa64b146d4bb Mon Sep 17 00:00:00 2001 From: Nicolas Schneider Date: Mon, 23 Jan 2017 01:23:51 +0100 Subject: [PATCH 1/3] print defaults for cipher and blocksize in cli help (#124) Closes #94. --- src/cryfs-cli/program_options/Parser.cpp | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/cryfs-cli/program_options/Parser.cpp b/src/cryfs-cli/program_options/Parser.cpp index c5833cf0..0391f20b 100644 --- a/src/cryfs-cli/program_options/Parser.cpp +++ b/src/cryfs-cli/program_options/Parser.cpp @@ -2,11 +2,13 @@ #include "utils.h" #include #include +#include #include namespace po = boost::program_options; namespace bf = boost::filesystem; using namespace cryfs::program_options; +using cryfs::CryConfigConsole; using std::pair; using std::vector; using std::cerr; @@ -118,12 +120,16 @@ vector Parser::_to_const_char_vector(const vector &options) void Parser::_addAllowedOptions(po::options_description *desc) { po::options_description options("Allowed options"); + string cipher_description = "Cipher to use for encryption. See possible values by calling cryfs with --show-ciphers. Default: "; + cipher_description += CryConfigConsole::DEFAULT_CIPHER; + string blocksize_description = "The block size used when storing ciphertext blocks (in bytes). Default: "; + blocksize_description += std::to_string(CryConfigConsole::DEFAULT_BLOCKSIZE_BYTES); options.add_options() ("help,h", "show help message") ("config,c", po::value(), "Configuration file") ("foreground,f", "Run CryFS in foreground.") - ("cipher", po::value(), "Cipher to use for encryption. See possible values by calling cryfs with --show-ciphers.") - ("blocksize", po::value(), "The block size used when storing ciphertext blocks (in bytes).") + ("cipher", po::value(), cipher_description.c_str()) + ("blocksize", po::value(), blocksize_description.c_str()) ("show-ciphers", "Show list of supported ciphers.") ("unmount-idle", po::value(), "Automatically unmount after specified number of idle minutes.") ("logfile", po::value(), "Specify the file to write log messages to. If this is not specified, log messages will go to stdout, or syslog if CryFS is running in the background.") From 7267bb5be47a9f1849da55a55c61999402f2ed84 Mon Sep 17 00:00:00 2001 From: Sebastian Messmer Date: Sat, 4 Feb 2017 14:21:00 +0000 Subject: [PATCH 2/3] Fix buid on Mac OS X --- src/cpp-utils/system/clock_gettime.h | 4 ++-- src/cryfs/filesystem/fsblobstore/utils/DirEntry.h | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/cpp-utils/system/clock_gettime.h b/src/cpp-utils/system/clock_gettime.h index 7c7d9765..42ce5775 100644 --- a/src/cpp-utils/system/clock_gettime.h +++ b/src/cpp-utils/system/clock_gettime.h @@ -2,11 +2,11 @@ #ifndef MESSMER_CPPUTILS_SYSTEM_CLOCKGETTIME_H #define MESSMER_CPPUTILS_SYSTEM_CLOCKGETTIME_H -// Implements clock_gettime for Mac OS X (where it is not implemented by in the standard library) +// Implements clock_gettime for Mac OS X before 10.12 (where it is not implemented by in the standard library) // Source: http://stackoverflow.com/a/9781275/829568 // Caution: The returned value is less precise than the returned value from a linux clock_gettime would be. -#ifdef __MACH__ +#if defined(__MACH__) && !defined(CLOCK_REALTIME) #include #define CLOCK_REALTIME 0 inline int clock_gettime(int /*clk_id*/, struct timespec *result) { diff --git a/src/cryfs/filesystem/fsblobstore/utils/DirEntry.h b/src/cryfs/filesystem/fsblobstore/utils/DirEntry.h index 2ff8b617..16a000a1 100644 --- a/src/cryfs/filesystem/fsblobstore/utils/DirEntry.h +++ b/src/cryfs/filesystem/fsblobstore/utils/DirEntry.h @@ -5,6 +5,7 @@ #include #include #include +#include // TODO Implement (and test) atime, noatime, strictatime, relatime mount options From 9a304fc52bdbcded602d7570dc71d5d3d0d1e612 Mon Sep 17 00:00:00 2001 From: Sebastian Messmer Date: Sat, 4 Feb 2017 19:03:20 +0000 Subject: [PATCH 3/3] Upgrade spdlog to 0.11.0 --- .../encrypted/EncryptedBlock.h | 4 +- .../implementations/ondisk/OnDiskBlock.cpp | 2 +- src/cpp-utils/assert/assert.h | 4 +- src/cpp-utils/assert/backtrace.cpp | 2 +- src/cpp-utils/crypto/RandomPadding.cpp | 2 +- src/cpp-utils/logging/Logger.h | 1 + src/cpp-utils/logging/logging.h | 33 +- src/cpp-utils/process/daemonize.cpp | 4 +- src/cpp-utils/tempfile/TempDir.cpp | 2 +- src/cpp-utils/tempfile/TempFile.cpp | 2 +- src/cpp-utils/thread/ThreadSystem.cpp | 4 +- src/cryfs-cli/Cli.cpp | 4 +- src/cryfs-cli/VersionChecker.cpp | 2 +- src/cryfs/config/CryConfigFile.cpp | 4 +- .../crypto/inner/ConcreteInnerEncryptor.h | 4 +- src/cryfs/config/crypto/inner/InnerConfig.cpp | 4 +- src/cryfs/config/crypto/outer/OuterConfig.cpp | 4 +- src/cryfs/filesystem/CryDevice.cpp | 6 +- src/fspp/fuse/Fuse.cpp | 112 +- src/fspp/impl/FilesystemImpl.cpp | 2 +- test/cpp-utils/logging/LoggingLevelTest.cpp | 16 +- test/cpp-utils/logging/LoggingTest.cpp | 58 +- .../cpp-utils/logging/testutils/LoggingTest.h | 1 + vendor/README | 4 +- vendor/spdlog/CMakeLists.txt | 2 +- vendor/spdlog/spdlog/async_logger.h | 27 +- vendor/spdlog/spdlog/common.h | 82 +- .../spdlog/spdlog/details/async_log_helper.h | 124 +- .../spdlog/spdlog/details/async_logger_impl.h | 42 +- vendor/spdlog/spdlog/details/file_helper.h | 62 +- vendor/spdlog/spdlog/details/format.cc | 949 -------- vendor/spdlog/spdlog/details/line_logger.h | 201 -- vendor/spdlog/spdlog/details/log_msg.h | 69 +- vendor/spdlog/spdlog/details/logger_impl.h | 320 ++- vendor/spdlog/spdlog/details/mpmc_bounded_q.h | 25 +- vendor/spdlog/spdlog/details/null_mutex.h | 23 +- vendor/spdlog/spdlog/details/os.h | 237 +- .../spdlog/details/pattern_formatter_impl.h | 185 +- vendor/spdlog/spdlog/details/registry.h | 71 +- vendor/spdlog/spdlog/details/spdlog_impl.h | 107 +- vendor/spdlog/spdlog/fmt/bundled/format.cc | 557 +++++ .../spdlog/{details => fmt/bundled}/format.h | 2150 +++++++++-------- vendor/spdlog/spdlog/fmt/bundled/ostream.cc | 36 + vendor/spdlog/spdlog/fmt/bundled/ostream.h | 118 + vendor/spdlog/spdlog/fmt/bundled/printf.h | 642 +++++ vendor/spdlog/spdlog/fmt/fmt.h | 28 + vendor/spdlog/spdlog/fmt/ostr.h | 17 + vendor/spdlog/spdlog/formatter.h | 9 +- vendor/spdlog/spdlog/logger.h | 111 +- vendor/spdlog/spdlog/sinks/android_sink.h | 53 +- vendor/spdlog/spdlog/sinks/ansicolor_sink.h | 116 + vendor/spdlog/spdlog/sinks/base_sink.h | 14 +- vendor/spdlog/spdlog/sinks/dist_sink.h | 47 +- vendor/spdlog/spdlog/sinks/file_sinks.h | 129 +- vendor/spdlog/spdlog/sinks/msvc_sink.h | 50 + vendor/spdlog/spdlog/sinks/null_sink.h | 7 +- vendor/spdlog/spdlog/sinks/ostream_sink.h | 7 +- vendor/spdlog/spdlog/sinks/sink.h | 28 +- vendor/spdlog/spdlog/sinks/stdout_sinks.h | 37 +- vendor/spdlog/spdlog/sinks/syslog_sink.h | 16 +- vendor/spdlog/spdlog/spdlog.h | 91 +- vendor/spdlog/spdlog/tweakme.h | 58 +- 62 files changed, 4054 insertions(+), 3074 deletions(-) delete mode 100644 vendor/spdlog/spdlog/details/format.cc delete mode 100644 vendor/spdlog/spdlog/details/line_logger.h create mode 100644 vendor/spdlog/spdlog/fmt/bundled/format.cc rename vendor/spdlog/spdlog/{details => fmt/bundled}/format.h (74%) create mode 100644 vendor/spdlog/spdlog/fmt/bundled/ostream.cc create mode 100644 vendor/spdlog/spdlog/fmt/bundled/ostream.h create mode 100644 vendor/spdlog/spdlog/fmt/bundled/printf.h create mode 100644 vendor/spdlog/spdlog/fmt/fmt.h create mode 100644 vendor/spdlog/spdlog/fmt/ostr.h create mode 100644 vendor/spdlog/spdlog/sinks/ansicolor_sink.h create mode 100644 vendor/spdlog/spdlog/sinks/msvc_sink.h diff --git a/src/blockstore/implementations/encrypted/EncryptedBlock.h b/src/blockstore/implementations/encrypted/EncryptedBlock.h index 47632a1e..b0c447d7 100644 --- a/src/blockstore/implementations/encrypted/EncryptedBlock.h +++ b/src/blockstore/implementations/encrypted/EncryptedBlock.h @@ -105,12 +105,12 @@ boost::optional>> EncryptedBlock plaintextWithHeader = Cipher::decrypt((byte*)baseBlock->data() + sizeof(FORMAT_VERSION_HEADER), baseBlock->size() - sizeof(FORMAT_VERSION_HEADER), encKey); if(plaintextWithHeader == boost::none) { //Decryption failed (e.g. an authenticated cipher detected modifications to the ciphertext) - cpputils::logging::LOG(cpputils::logging::WARN) << "Decrypting block " << baseBlock->key().ToString() << " failed. Was the block modified by an attacker?"; + cpputils::logging::LOG(cpputils::logging::WARN, "Decrypting block {} failed. Was the block modified by an attacker?", baseBlock->key().ToString()); return boost::none; } if(!_keyHeaderIsCorrect(baseBlock->key(), *plaintextWithHeader)) { //The stored key in the block data is incorrect - an attacker might have exchanged the contents with the encrypted data from a different block - cpputils::logging::LOG(cpputils::logging::WARN) << "Decrypting block " << baseBlock->key().ToString() << " failed due to invalid block key. Was the block modified by an attacker?"; + cpputils::logging::LOG(cpputils::logging::WARN, "Decrypting block {} failed due to invalid block key. Was the block modified by an attacker?", baseBlock->key().ToString()); return boost::none; } return cpputils::make_unique_ref>(std::move(baseBlock), encKey, std::move(*plaintextWithHeader)); diff --git a/src/blockstore/implementations/ondisk/OnDiskBlock.cpp b/src/blockstore/implementations/ondisk/OnDiskBlock.cpp index cbae87fa..4bc39dad 100644 --- a/src/blockstore/implementations/ondisk/OnDiskBlock.cpp +++ b/src/blockstore/implementations/ondisk/OnDiskBlock.cpp @@ -91,7 +91,7 @@ void OnDiskBlock::RemoveFromDisk(const bf::path &rootdir, const Key &key) { ASSERT(bf::is_regular_file(filepath), "Block not found on disk"); bool retval = bf::remove(filepath); if (!retval) { - LOG(ERROR) << "Couldn't find block " << key.ToString() << " to remove"; + LOG(ERROR, "Couldn't find block {} to remove", key.ToString()); } if (bf::is_empty(filepath.parent_path())) { bf::remove(filepath.parent_path()); diff --git a/src/cpp-utils/assert/assert.h b/src/cpp-utils/assert/assert.h index 52f1b868..644b6ae1 100644 --- a/src/cpp-utils/assert/assert.h +++ b/src/cpp-utils/assert/assert.h @@ -23,13 +23,13 @@ namespace cpputils { inline void assert_fail_release [[noreturn]] (const char *expr, const std::string &message, const char *file, int line) { auto msg = format(expr, message, file, line); using namespace logging; - LOG(ERROR) << msg; + LOG(ERROR, msg); throw AssertFailed(msg); } inline void assert_fail_debug [[noreturn]] (const char *expr, const std::string &message, const char *file, int line) { using namespace logging; - LOG(ERROR) << format(expr, message, file, line); + LOG(ERROR, format(expr, message, file, line)); abort(); } } diff --git a/src/cpp-utils/assert/backtrace.cpp b/src/cpp-utils/assert/backtrace.cpp index 24885380..b2f030fa 100644 --- a/src/cpp-utils/assert/backtrace.cpp +++ b/src/cpp-utils/assert/backtrace.cpp @@ -58,7 +58,7 @@ namespace cpputils { } void sigsegv_handler(int) { - LOG(ERROR) << "SIGSEGV\n" << backtrace(); + LOG(ERROR, "SIGSEGV\n{}", backtrace()); exit(1); } diff --git a/src/cpp-utils/crypto/RandomPadding.cpp b/src/cpp-utils/crypto/RandomPadding.cpp index 8852ea9d..42a1adbc 100644 --- a/src/cpp-utils/crypto/RandomPadding.cpp +++ b/src/cpp-utils/crypto/RandomPadding.cpp @@ -24,7 +24,7 @@ namespace cpputils { uint32_t size; std::memcpy(&size, reinterpret_cast(data.data()), sizeof(size)); if(sizeof(size) + size >= data.size()) { - LOG(ERROR) << "Config file is invalid: Invalid padding."; + LOG(ERROR, "Config file is invalid: Invalid padding."); return boost::none; }; Data result(size); diff --git a/src/cpp-utils/logging/Logger.h b/src/cpp-utils/logging/Logger.h index a6fa0e34..45e515c0 100644 --- a/src/cpp-utils/logging/Logger.h +++ b/src/cpp-utils/logging/Logger.h @@ -2,6 +2,7 @@ #ifndef MESSMER_CPPUTILS_LOGGING_LOGGER_H #define MESSMER_CPPUTILS_LOGGING_LOGGER_H +#define SPDLOG_ENABLE_SYSLOG #include #include "../macros.h" diff --git a/src/cpp-utils/logging/logging.h b/src/cpp-utils/logging/logging.h index 4ffcc376..0388732d 100644 --- a/src/cpp-utils/logging/logging.h +++ b/src/cpp-utils/logging/logging.h @@ -4,14 +4,15 @@ #include "Logger.h" #include +#include namespace cpputils { namespace logging { - extern struct ERROR_TYPE {} ERROR; - extern struct WARN_TYPE {} WARN; - extern struct INFO_TYPE {} INFO; - extern struct DEBUG_TYPE {} DEBUG; + constexpr struct ERROR_TYPE {} ERROR; + constexpr struct WARN_TYPE {} WARN; + constexpr struct INFO_TYPE {} INFO; + constexpr struct DEBUG_TYPE {} DEBUG; inline void setLogger(std::shared_ptr newLogger) { logger().setLogger(newLogger); @@ -37,20 +38,28 @@ namespace cpputils { logger().setLevel(spdlog::level::debug); } - inline spdlog::details::line_logger LOG(ERROR_TYPE) { - return logger()->error(); + template inline void LOG(LogType logType, const std::string &msg) { + LOG(logType, msg.c_str()); } - inline spdlog::details::line_logger LOG(WARN_TYPE) { - return logger()->warn(); + template + inline void LOG(ERROR_TYPE, const char* fmt, const Args&... args) { + logger()->error(fmt, args...); } - inline spdlog::details::line_logger LOG(INFO_TYPE) { - return logger()->info(); + template + inline void LOG(WARN_TYPE, const char* fmt, const Args&... args) { + logger()->warn(fmt, args...); } - inline spdlog::details::line_logger LOG(DEBUG_TYPE) { - return logger()->debug(); + template + inline void LOG(INFO_TYPE, const char* fmt, const Args&... args) { + logger()->info(fmt, args...); + } + + template + inline void LOG(DEBUG_TYPE, const char* fmt, const Args&... args) { + logger()->debug(fmt, args...); } } } diff --git a/src/cpp-utils/process/daemonize.cpp b/src/cpp-utils/process/daemonize.cpp index 5207f803..fbc25ec6 100644 --- a/src/cpp-utils/process/daemonize.cpp +++ b/src/cpp-utils/process/daemonize.cpp @@ -34,13 +34,13 @@ namespace cpputils { // Create a new SID for the child process pid_t sid = setsid(); if (sid < 0) { - LOG(ERROR) << "Failed to get SID for daemon process"; + LOG(ERROR, "Failed to get SID for daemon process"); exit(EXIT_FAILURE); } // Change the current working directory to a directory that's always existin if ((chdir("/")) < 0) { - LOG(ERROR) << "Failed to change working directory for daemon process"; + LOG(ERROR, "Failed to change working directory for daemon process"); exit(EXIT_FAILURE); } diff --git a/src/cpp-utils/tempfile/TempDir.cpp b/src/cpp-utils/tempfile/TempDir.cpp index 7d4d0838..9de918cb 100644 --- a/src/cpp-utils/tempfile/TempDir.cpp +++ b/src/cpp-utils/tempfile/TempDir.cpp @@ -21,7 +21,7 @@ void TempDir::remove() { bf::remove_all(_path); } } catch (const boost::filesystem::filesystem_error &e) { - LOG(ERROR) << "Could not delete tempfile."; + LOG(ERROR, "Could not delete tempfile."); } } diff --git a/src/cpp-utils/tempfile/TempFile.cpp b/src/cpp-utils/tempfile/TempFile.cpp index ea2e3cf4..61710538 100644 --- a/src/cpp-utils/tempfile/TempFile.cpp +++ b/src/cpp-utils/tempfile/TempFile.cpp @@ -28,7 +28,7 @@ TempFile::~TempFile() { bf::remove(_path); } } catch (const boost::filesystem::filesystem_error &e) { - LOG(ERROR) << "Could not delete tempfile."; + LOG(ERROR, "Could not delete tempfile."); } } diff --git a/src/cpp-utils/thread/ThreadSystem.cpp b/src/cpp-utils/thread/ThreadSystem.cpp index 02b75b97..81522704 100644 --- a/src/cpp-utils/thread/ThreadSystem.cpp +++ b/src/cpp-utils/thread/ThreadSystem.cpp @@ -75,9 +75,9 @@ namespace cpputils { } catch (const boost::thread_interrupted &e) { //Do nothing, exit thread. } catch (const std::exception &e) { - LOG(ERROR) << "LoopThread crashed: " << e.what(); + LOG(ERROR, "LoopThread crashed: {}", e.what()); } catch (...) { - LOG(ERROR) << "LoopThread crashed"; + LOG(ERROR, "LoopThread crashed"); } //TODO We should remove the thread from _runningThreads here, not in stop(). } diff --git a/src/cryfs-cli/Cli.cpp b/src/cryfs-cli/Cli.cpp index 92ebe961..f675172c 100644 --- a/src/cryfs-cli/Cli.cpp +++ b/src/cryfs-cli/Cli.cpp @@ -252,9 +252,9 @@ namespace cryfs { #endif fuse.run(options.mountDir(), options.fuseOptions()); } catch (const std::exception &e) { - LOG(ERROR) << "Crashed: " << e.what(); + LOG(ERROR, "Crashed: {}", e.what()); } catch (...) { - LOG(ERROR) << "Crashed"; + LOG(ERROR, "Crashed"); } } diff --git a/src/cryfs-cli/VersionChecker.cpp b/src/cryfs-cli/VersionChecker.cpp index 6e0f81a3..ec5ba989 100644 --- a/src/cryfs-cli/VersionChecker.cpp +++ b/src/cryfs-cli/VersionChecker.cpp @@ -65,7 +65,7 @@ namespace cryfs { read_json(input, pt); return pt; } catch (const json_parser_error &e) { - LOG(WARN) << "Error parsing version information json object"; + LOG(WARN, "Error parsing version information json object"); return none; } } diff --git a/src/cryfs/config/CryConfigFile.cpp b/src/cryfs/config/CryConfigFile.cpp index ba60d569..523b0927 100644 --- a/src/cryfs/config/CryConfigFile.cpp +++ b/src/cryfs/config/CryConfigFile.cpp @@ -28,7 +28,7 @@ CryConfigFile::~CryConfigFile() { optional CryConfigFile::load(const bf::path &path, const string &password) { auto encryptedConfigData = Data::LoadFromFile(path); if (encryptedConfigData == none) { - LOG(ERROR) << "Config file not found"; + LOG(ERROR, "Config file not found"); return none; } auto encryptor = CryConfigEncryptorFactory::loadKey(*encryptedConfigData, password); @@ -41,7 +41,7 @@ optional CryConfigFile::load(const bf::path &path, const string & } CryConfig config = CryConfig::load(decrypted->data); if (config.Cipher() != decrypted->cipherName) { - LOG(ERROR) << "Inner cipher algorithm used to encrypt config file doesn't match config value"; + LOG(ERROR, "Inner cipher algorithm used to encrypt config file doesn't match config value"); return none; } auto configFile = CryConfigFile(path, std::move(config), std::move(*encryptor)); diff --git a/src/cryfs/config/crypto/inner/ConcreteInnerEncryptor.h b/src/cryfs/config/crypto/inner/ConcreteInnerEncryptor.h index 3f1c6e19..9c2be128 100644 --- a/src/cryfs/config/crypto/inner/ConcreteInnerEncryptor.h +++ b/src/cryfs/config/crypto/inner/ConcreteInnerEncryptor.h @@ -33,12 +33,12 @@ namespace cryfs { template boost::optional ConcreteInnerEncryptor::decrypt(const InnerConfig &innerConfig) const { if (innerConfig.cipherName != Cipher::NAME) { - cpputils::logging::LOG(cpputils::logging::ERROR) << "Initialized ConcreteInnerEncryptor with wrong cipher"; + cpputils::logging::LOG(cpputils::logging::ERROR, "Initialized ConcreteInnerEncryptor with wrong cipher"); return boost::none; } auto decrypted = Cipher::decrypt(static_cast(innerConfig.encryptedConfig.data()), innerConfig.encryptedConfig.size(), _key); if (decrypted == boost::none) { - cpputils::logging::LOG(cpputils::logging::ERROR) << "Failed decrypting configuration file"; + cpputils::logging::LOG(cpputils::logging::ERROR, "Failed decrypting configuration file"); return boost::none; } auto configData = cpputils::RandomPadding::remove(*decrypted); diff --git a/src/cryfs/config/crypto/inner/InnerConfig.cpp b/src/cryfs/config/crypto/inner/InnerConfig.cpp index 1bea763a..e7226bb9 100644 --- a/src/cryfs/config/crypto/inner/InnerConfig.cpp +++ b/src/cryfs/config/crypto/inner/InnerConfig.cpp @@ -23,7 +23,7 @@ namespace cryfs { serializer.writeTailData(encryptedConfig); return serializer.finished(); } catch (const exception &e) { - LOG(ERROR) << "Error serializing inner configuration: " << e.what(); + LOG(ERROR, "Error serializing inner configuration: {}", e.what()); throw; // This is a programming logic error, pass through exception. } } @@ -37,7 +37,7 @@ namespace cryfs { deserializer.finished(); return InnerConfig {cipherName, std::move(result)}; } catch (const exception &e) { - LOG(ERROR) << "Error deserializing inner configuration: " << e.what(); + LOG(ERROR, "Error deserializing inner configuration: {}", e.what()); return none; // This can be caused by invalid input data and does not have to be a programming error. Don't throw exception. } } diff --git a/src/cryfs/config/crypto/outer/OuterConfig.cpp b/src/cryfs/config/crypto/outer/OuterConfig.cpp index 7690687d..c6285247 100644 --- a/src/cryfs/config/crypto/outer/OuterConfig.cpp +++ b/src/cryfs/config/crypto/outer/OuterConfig.cpp @@ -38,7 +38,7 @@ namespace cryfs { serializer.writeTailData(encryptedInnerConfig); return serializer.finished(); } catch (const exception &e) { - LOG(ERROR) << "Error serializing CryConfigEncryptor: " << e.what(); + LOG(ERROR, "Error serializing CryConfigEncryptor: {}", e.what()); throw; // This is a programming logic error. Pass through exception. } } @@ -60,7 +60,7 @@ namespace cryfs { _deserializeNewFormat(&deserializer); #endif } catch (const exception &e) { - LOG(ERROR) << "Error deserializing outer configuration: " << e.what(); + LOG(ERROR, "Error deserializing outer configuration: {}", e.what()); return none; // This can be caused by invalid input data and does not have to be a programming error. Don't throw exception. } } diff --git a/src/cryfs/filesystem/CryDevice.cpp b/src/cryfs/filesystem/CryDevice.cpp index 1f1c4178..5362f807 100644 --- a/src/cryfs/filesystem/CryDevice.cpp +++ b/src/cryfs/filesystem/CryDevice.cpp @@ -148,7 +148,7 @@ CryDevice::BlobWithParent CryDevice::LoadBlobWithParent(const bf::path &path) { optional> parentBlob = none; optional> currentBlobOpt = _fsBlobStore->load(_rootKey); if (currentBlobOpt == none) { - LOG(ERROR) << "Could not load root blob. Is the base directory accessible?"; + LOG(ERROR, "Could not load root blob. Is the base directory accessible?"); throw FuseErrnoException(EIO); } unique_ref currentBlob = std::move(*currentBlobOpt); @@ -211,7 +211,7 @@ unique_ref CryDevice::CreateSymlinkBlob(const bf::path &target) unique_ref CryDevice::LoadBlob(const blockstore::Key &key) { auto blob = _fsBlobStore->load(key); if (blob == none) { - LOG(ERROR) << "Could not load blob " << key.ToString() << ". Is the base directory accessible?"; + LOG(ERROR, "Could not load blob {}. Is the base directory accessible?", key.ToString()); throw FuseErrnoException(EIO); } return std::move(*blob); @@ -220,7 +220,7 @@ unique_ref CryDevice::LoadBlob(const blockstore::Key &key) { void CryDevice::RemoveBlob(const blockstore::Key &key) { auto blob = _fsBlobStore->load(key); if (blob == none) { - LOG(ERROR) << "Could not load blob " << key.ToString() << ". Is the base directory accessible?"; + LOG(ERROR, "Could not load blob. Is the base directory accessible?", key.ToString()); throw FuseErrnoException(EIO); } _fsBlobStore->remove(std::move(*blob)); diff --git a/src/fspp/fuse/Fuse.cpp b/src/fspp/fuse/Fuse.cpp index e5209ffb..56508871 100644 --- a/src/fspp/fuse/Fuse.cpp +++ b/src/fspp/fuse/Fuse.cpp @@ -222,11 +222,11 @@ Fuse::Fuse(Filesystem *fs, const std::string &fstype, const boost::optional &fuseOptions) { @@ -290,19 +290,19 @@ void Fuse::stop() { int ret = system(("fusermount -z -u " + _mountdir.native()).c_str()); // "-z" takes care that if the filesystem can't be unmounted right now because something is opened, it will be unmounted as soon as it can be. #endif if (ret != 0) { - LOG(ERROR) << "Could not unmount filesystem"; + LOG(ERROR, "Could not unmount filesystem"); } } int Fuse::getattr(const bf::path &path, struct stat *stbuf) { #ifdef FSPP_LOG - LOG(DEBUG) << "getattr(" << path << ", _, _)"; + LOG(DEBUG, "getattr({}, _, _)", path); #endif try { _fs->lstat(path, stbuf); return 0; } catch(const cpputils::AssertFailed &e) { - LOG(ERROR) << "AssertFailed in Fuse::getattr: " << e.what(); + LOG(ERROR, "AssertFailed in Fuse::getattr: {}", e.what()); return -EIO; } catch(fspp::fuse::FuseErrnoException &e) { return -e.getErrno(); @@ -317,7 +317,7 @@ int Fuse::getattr(const bf::path &path, struct stat *stbuf) { int Fuse::fgetattr(const bf::path &path, struct stat *stbuf, fuse_file_info *fileinfo) { #ifdef FSPP_LOG - LOG(DEBUG) << "fgetattr(" << path << ", _, _)\n"; + LOG(DEBUG, "fgetattr({}, _, _)\n", path); #endif // On FreeBSD, trying to do anything with the mountpoint ends up @@ -333,7 +333,7 @@ int Fuse::fgetattr(const bf::path &path, struct stat *stbuf, fuse_file_info *fil _fs->fstat(fileinfo->fh, stbuf); return 0; } catch(const cpputils::AssertFailed &e) { - LOG(ERROR) << "AssertFailed in Fuse::fgetattr: " << e.what(); + LOG(ERROR, "AssertFailed in Fuse::fgetattr: {}", e.what()); return -EIO; } catch(fspp::fuse::FuseErrnoException &e) { return -e.getErrno(); @@ -348,13 +348,13 @@ int Fuse::fgetattr(const bf::path &path, struct stat *stbuf, fuse_file_info *fil int Fuse::readlink(const bf::path &path, char *buf, size_t size) { #ifdef FSPP_LOG - LOG(DEBUG) << "readlink(" << path << ", _, " << size << ")"; + LOG(DEBUG, "readlink({}, _, {})", path, size); #endif try { _fs->readSymlink(path, buf, size); return 0; } catch(const cpputils::AssertFailed &e) { - LOG(ERROR) << "AssertFailed in Fuse::readlink: " << e.what(); + LOG(ERROR, "AssertFailed in Fuse::readlink: {}", e.what()); return -EIO; } catch (fspp::fuse::FuseErrnoException &e) { return -e.getErrno(); @@ -371,20 +371,20 @@ int Fuse::mknod(const bf::path &path, mode_t mode, dev_t rdev) { UNUSED(rdev); UNUSED(mode); UNUSED(path); - LOG(WARN) << "Called non-implemented mknod(" << path << ", " << mode << ", _)"; + LOG(WARN, "Called non-implemented mknod({}, {}, _)", path, mode); return ENOSYS; } int Fuse::mkdir(const bf::path &path, mode_t mode) { #ifdef FSPP_LOG - LOG(DEBUG) << "mkdir(" << path << ", " << mode << ")"; + LOG(DEBUG, "mkdir({}, {})", path, mode); #endif try { auto context = fuse_get_context(); _fs->mkdir(path, mode, context->uid, context->gid); return 0; } catch(const cpputils::AssertFailed &e) { - LOG(ERROR) << "AssertFailed in Fuse::mkdir: " << e.what(); + LOG(ERROR, "AssertFailed in Fuse::mkdir: {}", e.what()); return -EIO; } catch(fspp::fuse::FuseErrnoException &e) { return -e.getErrno(); @@ -399,13 +399,13 @@ int Fuse::mkdir(const bf::path &path, mode_t mode) { int Fuse::unlink(const bf::path &path) { #ifdef FSPP_LOG - LOG(DEBUG) << "unlink(" << path << ")"; + LOG(DEBUG, "unlink({})", path); #endif try { _fs->unlink(path); return 0; } catch(const cpputils::AssertFailed &e) { - LOG(ERROR) << "AssertFailed in Fuse::unlink: " << e.what(); + LOG(ERROR, "AssertFailed in Fuse::unlink: {}", e.what()); return -EIO; } catch(fspp::fuse::FuseErrnoException &e) { return -e.getErrno(); @@ -420,13 +420,13 @@ int Fuse::unlink(const bf::path &path) { int Fuse::rmdir(const bf::path &path) { #ifdef FSPP_LOG - LOG(DEBUG) << "rmdir(" << path << ")"; + LOG(DEBUG, "rmdir({})", path); #endif try { _fs->rmdir(path); return 0; } catch(const cpputils::AssertFailed &e) { - LOG(ERROR) << "AssertFailed in Fuse::rmdir: " << e.what(); + LOG(ERROR, "AssertFailed in Fuse::rmdir: {}", e.what()); return -EIO; } catch(fspp::fuse::FuseErrnoException &e) { return -e.getErrno(); @@ -441,14 +441,14 @@ int Fuse::rmdir(const bf::path &path) { int Fuse::symlink(const bf::path &from, const bf::path &to) { #ifdef FSPP_LOG - LOG(DEBUG) << "symlink(" << from << ", " << to << ")"; + LOG(DEBUG, "symlink({}, {})", from, to); #endif try { auto context = fuse_get_context(); _fs->createSymlink(from, to, context->uid, context->gid); return 0; } catch(const cpputils::AssertFailed &e) { - LOG(ERROR) << "AssertFailed in Fuse::symlink: " << e.what(); + LOG(ERROR, "AssertFailed in Fuse::symlink: {}", e.what()); return -EIO; } catch(fspp::fuse::FuseErrnoException &e) { return -e.getErrno(); @@ -463,7 +463,7 @@ int Fuse::symlink(const bf::path &from, const bf::path &to) { int Fuse::rename(const bf::path &from, const bf::path &to) { #ifdef FSPP_LOG - LOG(DEBUG) << "rename(" << from << ", " << to << ")"; + LOG(DEBUG, "rename({}, {})", from, to); #endif try { ASSERT(from.is_absolute(), "from has to be an absolute path"); @@ -471,7 +471,7 @@ int Fuse::rename(const bf::path &from, const bf::path &to) { _fs->rename(from, to); return 0; } catch(const cpputils::AssertFailed &e) { - LOG(ERROR) << "AssertFailed in Fuse::rename: " << e.what(); + LOG(ERROR, "AssertFailed in Fuse::rename: {}", e.what()); return -EIO; } catch(fspp::fuse::FuseErrnoException &e) { return -e.getErrno(); @@ -486,7 +486,7 @@ int Fuse::rename(const bf::path &from, const bf::path &to) { //TODO int Fuse::link(const bf::path &from, const bf::path &to) { - LOG(WARN) << "NOT IMPLEMENTED: link(" << from << ", " << to << ")"; + LOG(WARN, "NOT IMPLEMENTED: link({}, {})", from, to); //auto real_from = _impl->RootDir() / from; //auto real_to = _impl->RootDir() / to; //int retstat = ::link(real_from.c_str(), real_to.c_str()); @@ -496,13 +496,13 @@ int Fuse::link(const bf::path &from, const bf::path &to) { int Fuse::chmod(const bf::path &path, mode_t mode) { #ifdef FSPP_LOG - LOG(DEBUG) << "chmod(" << path << ", " << mode << ")"; + LOG(DEBUG, "chmod({}, {})", path, mode); #endif try { _fs->chmod(path, mode); return 0; } catch(const cpputils::AssertFailed &e) { - LOG(ERROR) << "AssertFailed in Fuse::chmod: " << e.what(); + LOG(ERROR, "AssertFailed in Fuse::chmod: {}", e.what()); return -EIO; } catch (fspp::fuse::FuseErrnoException &e) { return -e.getErrno(); @@ -517,13 +517,13 @@ int Fuse::chmod(const bf::path &path, mode_t mode) { int Fuse::chown(const bf::path &path, uid_t uid, gid_t gid) { #ifdef FSPP_LOG - LOG(DEBUG) << "chown(" << path << ", " << uid << ", " << gid << ")"; + LOG(DEBUG, "chown({}, {}, {})", path, uid, gid); #endif try { _fs->chown(path, uid, gid); return 0; } catch(const cpputils::AssertFailed &e) { - LOG(ERROR) << "AssertFailed in Fuse::chown: " << e.what(); + LOG(ERROR, "AssertFailed in Fuse::chown: {}", e.what()); return -EIO; } catch (fspp::fuse::FuseErrnoException &e) { return -e.getErrno(); @@ -538,13 +538,13 @@ int Fuse::chown(const bf::path &path, uid_t uid, gid_t gid) { int Fuse::truncate(const bf::path &path, off_t size) { #ifdef FSPP_LOG - LOG(DEBUG) << "truncate(" << path << ", " << size << ")"; + LOG(DEBUG, "truncate({}, {})", path, size); #endif try { _fs->truncate(path, size); return 0; } catch(const cpputils::AssertFailed &e) { - LOG(ERROR) << "AssertFailed in Fuse::truncate: " << e.what(); + LOG(ERROR, "AssertFailed in Fuse::truncate: {}", e.what()); return -EIO; } catch (FuseErrnoException &e) { return -e.getErrno(); @@ -559,14 +559,14 @@ int Fuse::truncate(const bf::path &path, off_t size) { int Fuse::ftruncate(const bf::path &path, off_t size, fuse_file_info *fileinfo) { #ifdef FSPP_LOG - LOG(DEBUG) << "ftruncate(" << path << ", " << size << ")"; + LOG(DEBUG, "ftruncate({}, {})", path, size); #endif UNUSED(path); try { _fs->ftruncate(fileinfo->fh, size); return 0; } catch(const cpputils::AssertFailed &e) { - LOG(ERROR) << "AssertFailed in Fuse::ftruncate: " << e.what(); + LOG(ERROR, "AssertFailed in Fuse::ftruncate: {}", e.what()); return -EIO; } catch (FuseErrnoException &e) { return -e.getErrno(); @@ -581,13 +581,13 @@ int Fuse::ftruncate(const bf::path &path, off_t size, fuse_file_info *fileinfo) int Fuse::utimens(const bf::path &path, const timespec times[2]) { #ifdef FSPP_LOG - LOG(DEBUG) << "utimens(" << path << ", _)"; + LOG(DEBUG, "utimens({}, _)", path); #endif try { _fs->utimens(path, times[0], times[1]); return 0; } catch(const cpputils::AssertFailed &e) { - LOG(ERROR) << "AssertFailed in Fuse::utimens: " << e.what(); + LOG(ERROR, "AssertFailed in Fuse::utimens: {}", e.what()); return -EIO; } catch (FuseErrnoException &e) { return -e.getErrno(); @@ -602,13 +602,13 @@ int Fuse::utimens(const bf::path &path, const timespec times[2]) { int Fuse::open(const bf::path &path, fuse_file_info *fileinfo) { #ifdef FSPP_LOG - LOG(DEBUG) << "open(" << path << ", _)"; + LOG(DEBUG, "open({}, _)", path); #endif try { fileinfo->fh = _fs->openFile(path, fileinfo->flags); return 0; } catch(const cpputils::AssertFailed &e) { - LOG(ERROR) << "AssertFailed in Fuse::open: " << e.what(); + LOG(ERROR, "AssertFailed in Fuse::open: {}", e.what()); return -EIO; } catch (FuseErrnoException &e) { return -e.getErrno(); @@ -623,14 +623,14 @@ int Fuse::open(const bf::path &path, fuse_file_info *fileinfo) { int Fuse::release(const bf::path &path, fuse_file_info *fileinfo) { #ifdef FSPP_LOG - LOG(DEBUG) << "release(" << path << ", _)"; + LOG(DEBUG, "release({}, _)", path); #endif UNUSED(path); try { _fs->closeFile(fileinfo->fh); return 0; } catch(const cpputils::AssertFailed &e) { - LOG(ERROR) << "AssertFailed in Fuse::release: " << e.what(); + LOG(ERROR, "AssertFailed in Fuse::release: {}", e.what()); return -EIO; } catch (FuseErrnoException &e) { return -e.getErrno(); @@ -645,13 +645,13 @@ int Fuse::release(const bf::path &path, fuse_file_info *fileinfo) { int Fuse::read(const bf::path &path, char *buf, size_t size, off_t offset, fuse_file_info *fileinfo) { #ifdef FSPP_LOG - LOG(DEBUG) << "read(" << path << ", _, " << size << ", " << offset << ", _ )"; + LOG(DEBUG, "read({}, _, {}, {}, _)", path, size, offset); #endif UNUSED(path); try { return _fs->read(fileinfo->fh, buf, size, offset); } catch(const cpputils::AssertFailed &e) { - LOG(ERROR) << "AssertFailed in Fuse::read: " << e.what(); + LOG(ERROR, "AssertFailed in Fuse::read: {}", e.what()); return -EIO; } catch (FuseErrnoException &e) { return -e.getErrno(); @@ -666,14 +666,14 @@ int Fuse::read(const bf::path &path, char *buf, size_t size, off_t offset, fuse_ int Fuse::write(const bf::path &path, const char *buf, size_t size, off_t offset, fuse_file_info *fileinfo) { #ifdef FSPP_LOG - LOG(DEBUG) << "write(" << path << ", _, " << size << ", " << offset << ", _)"; + LOG(DEBUG, "write({}, _, {}, {}, _)", path, size, offsset); #endif UNUSED(path); try { _fs->write(fileinfo->fh, buf, size, offset); return size; } catch(const cpputils::AssertFailed &e) { - LOG(ERROR) << "AssertFailed in Fuse::write: " << e.what(); + LOG(ERROR, "AssertFailed in Fuse::write: {}", e.what()); return -EIO; } catch (FuseErrnoException &e) { return -e.getErrno(); @@ -689,13 +689,13 @@ int Fuse::write(const bf::path &path, const char *buf, size_t size, off_t offset //TODO int Fuse::statfs(const bf::path &path, struct statvfs *fsstat) { #ifdef FSPP_LOG - LOG(DEBUG) << "statfs(" << path << ", _)"; + LOG(DEBUG, "statfs({}, _)", path); #endif try { _fs->statfs(path, fsstat); return 0; } catch(const cpputils::AssertFailed &e) { - LOG(ERROR) << "AssertFailed in Fuse::statfs: " << e.what(); + LOG(ERROR, "AssertFailed in Fuse::statfs: {}", e.what()); return -EIO; } catch (FuseErrnoException &e) { return -e.getErrno(); @@ -710,14 +710,14 @@ int Fuse::statfs(const bf::path &path, struct statvfs *fsstat) { int Fuse::flush(const bf::path &path, fuse_file_info *fileinfo) { #ifdef FSPP_LOG - LOG(WARN) << "flush(" << path << ", _)"; + LOG(WARN, "flush({}, _)", path); #endif UNUSED(path); try { _fs->flush(fileinfo->fh); return 0; } catch(const cpputils::AssertFailed &e) { - LOG(ERROR) << "AssertFailed in Fuse::flush: " << e.what(); + LOG(ERROR, "AssertFailed in Fuse::flush: {}", e.what()); return -EIO; } catch (FuseErrnoException &e) { return -e.getErrno(); @@ -732,7 +732,7 @@ int Fuse::flush(const bf::path &path, fuse_file_info *fileinfo) { int Fuse::fsync(const bf::path &path, int datasync, fuse_file_info *fileinfo) { #ifdef FSPP_LOG - LOG(DEBUG) << "fsync(" << path << ", " << datasync << ", _)"; + LOG(DEBUG, "fsync({}, {}, _)", path, datasync); #endif UNUSED(path); try { @@ -743,7 +743,7 @@ int Fuse::fsync(const bf::path &path, int datasync, fuse_file_info *fileinfo) { } return 0; } catch(const cpputils::AssertFailed &e) { - LOG(ERROR) << "AssertFailed in Fuse::fsync: " << e.what(); + LOG(ERROR, "AssertFailed in Fuse::fsync: {}", e.what()); return -EIO; } catch (FuseErrnoException &e) { return -e.getErrno(); @@ -759,14 +759,14 @@ int Fuse::fsync(const bf::path &path, int datasync, fuse_file_info *fileinfo) { int Fuse::opendir(const bf::path &path, fuse_file_info *fileinfo) { UNUSED(path); UNUSED(fileinfo); - //LOG(DEBUG) << "opendir(" << path << ", _)"; + //LOG(DEBUG, "opendir({}, _)", path); //We don't need opendir, because readdir works directly on the path return 0; } int Fuse::readdir(const bf::path &path, void *buf, fuse_fill_dir_t filler, off_t offset, fuse_file_info *fileinfo) { #ifdef FSPP_LOG - LOG(DEBUG) << "readdir(" << path << ", _, _, " << offset << ", _)"; + LOG(DEBUG, "readdir({}, _, _, {}, _)", path, offest); #endif UNUSED(fileinfo); UNUSED(offset); @@ -793,7 +793,7 @@ int Fuse::readdir(const bf::path &path, void *buf, fuse_fill_dir_t filler, off_t } return 0; } catch(const cpputils::AssertFailed &e) { - LOG(ERROR) << "AssertFailed in Fuse::readdir: " << e.what(); + LOG(ERROR, "AssertFailed in Fuse::readdir: {}", e.what()); return -EIO; } catch (FuseErrnoException &e) { return -e.getErrno(); @@ -809,7 +809,7 @@ int Fuse::readdir(const bf::path &path, void *buf, fuse_fill_dir_t filler, off_t int Fuse::releasedir(const bf::path &path, fuse_file_info *fileinfo) { UNUSED(path); UNUSED(fileinfo); - //LOG(DEBUG) << "releasedir(" << path << ", _)"; + //LOG(DEBUG, "releasedir({}, _)", path); //We don't need releasedir, because readdir works directly on the path return 0; } @@ -819,13 +819,13 @@ int Fuse::fsyncdir(const bf::path &path, int datasync, fuse_file_info *fileinfo) UNUSED(fileinfo); UNUSED(datasync); UNUSED(path); - //LOG(WARN) << "Called non-implemented fsyncdir(" << path << ", " << datasync << ", _)"; + //LOG(WARN, "Called non-implemented fsyncdir({}, {}, _)", path, datasync); return 0; } void Fuse::init(fuse_conn_info *conn) { UNUSED(conn); - LOG(INFO) << "Filesystem started."; + LOG(INFO, "Filesystem started."); _running = true; @@ -835,19 +835,19 @@ void Fuse::init(fuse_conn_info *conn) { } void Fuse::destroy() { - LOG(INFO) << "Filesystem stopped."; + LOG(INFO, "Filesystem stopped."); _running = false; } int Fuse::access(const bf::path &path, int mask) { #ifdef FSPP_LOG - LOG(DEBUG) << "access(" << path << ", " << mask << ")"; + LOG(DEBUG, "access({}, {})", path, mask); #endif try { _fs->access(path, mask); return 0; } catch(const cpputils::AssertFailed &e) { - LOG(ERROR) << "AssertFailed in Fuse::access: " << e.what(); + LOG(ERROR, "AssertFailed in Fuse::access: {}", e.what()); return -EIO; } catch (FuseErrnoException &e) { return -e.getErrno(); @@ -862,14 +862,14 @@ int Fuse::access(const bf::path &path, int mask) { int Fuse::create(const bf::path &path, mode_t mode, fuse_file_info *fileinfo) { #ifdef FSPP_LOG - LOG(DEBUG) << "create(" << path << ", " << mode << ", _)"; + LOG(DEBUG, "create({}, {}, _)", path, mode); #endif try { auto context = fuse_get_context(); fileinfo->fh = _fs->createAndOpenFile(path, mode, context->uid, context->gid); return 0; } catch(const cpputils::AssertFailed &e) { - LOG(ERROR) << "AssertFailed in Fuse::create: " << e.what(); + LOG(ERROR, "AssertFailed in Fuse::create: {}", e.what()); return -EIO; } catch (FuseErrnoException &e) { return -e.getErrno(); diff --git a/src/fspp/impl/FilesystemImpl.cpp b/src/fspp/impl/FilesystemImpl.cpp index 74d7a472..14de38ec 100644 --- a/src/fspp/impl/FilesystemImpl.cpp +++ b/src/fspp/impl/FilesystemImpl.cpp @@ -88,7 +88,7 @@ FilesystemImpl::~FilesystemImpl() { << std::setw(40) << "CreateSymlink (without loading): " << static_cast(_createSymlinkNanosec_withoutLoading)/1000000000 << "\n" << std::setw(40) << "ReadSymlink: " << static_cast(_readSymlinkNanosec)/1000000000 << "\n" << std::setw(40) << "ReadSymlink (without loading): " << static_cast(_readSymlinkNanosec_withoutLoading)/1000000000 << "\n"; - LOG(INFO) << profilerInformation.str(); + LOG(INFO, profilerInformation.str()); #endif } diff --git a/test/cpp-utils/logging/LoggingLevelTest.cpp b/test/cpp-utils/logging/LoggingLevelTest.cpp index d5da126d..8a9982e9 100644 --- a/test/cpp-utils/logging/LoggingLevelTest.cpp +++ b/test/cpp-utils/logging/LoggingLevelTest.cpp @@ -7,42 +7,42 @@ using testing::MatchesRegex; class LoggingLevelTest: public LoggingTest { public: void EXPECT_DEBUG_LOG_ENABLED() { - LOG(DEBUG) << "My log message"; + LOG(DEBUG, "My log message"); EXPECT_THAT(mockLogger.capturedLog(), MatchesRegex(".*\\[MockLogger\\].*\\[debug\\].*My log message.*")); } void EXPECT_DEBUG_LOG_DISABLED() { - LOG(DEBUG) << "My log message"; + LOG(DEBUG, "My log message"); EXPECT_EQ("", mockLogger.capturedLog()); } void EXPECT_INFO_LOG_ENABLED() { - LOG(INFO) << "My log message"; + LOG(INFO, "My log message"); EXPECT_THAT(mockLogger.capturedLog(), MatchesRegex(".*\\[MockLogger\\].*\\[info\\].*My log message.*")); } void EXPECT_INFO_LOG_DISABLED() { - LOG(INFO) << "My log message"; + LOG(INFO, "My log message"); EXPECT_EQ("", mockLogger.capturedLog()); } void EXPECT_WARNING_LOG_ENABLED() { - LOG(WARN) << "My log message"; + LOG(WARN, "My log message"); EXPECT_THAT(mockLogger.capturedLog(), MatchesRegex(".*\\[MockLogger\\].*\\[warning\\].*My log message.*")); } void EXPECT_WARNING_LOG_DISABLED() { - LOG(WARN) << "My log message"; + LOG(WARN, "My log message"); EXPECT_EQ("", mockLogger.capturedLog()); } void EXPECT_ERROR_LOG_ENABLED() { - LOG(ERROR) << "My log message"; + LOG(ERROR, "My log message"); EXPECT_THAT(mockLogger.capturedLog(), MatchesRegex(".*\\[MockLogger\\].*\\[error\\].*My log message.*")); } void EXPECT_ERROR_LOG_DISABLED() { - LOG(ERROR) << "My log message"; + LOG(ERROR, "My log message"); EXPECT_EQ("", mockLogger.capturedLog()); } }; diff --git a/test/cpp-utils/logging/LoggingTest.cpp b/test/cpp-utils/logging/LoggingTest.cpp index 6e26280e..8bc3d134 100644 --- a/test/cpp-utils/logging/LoggingTest.cpp +++ b/test/cpp-utils/logging/LoggingTest.cpp @@ -2,7 +2,7 @@ /* * Contains test cases for the following logging interface: - * LOG(INFO) << "My log message" + * LOG(INFO, "My log message)" */ using namespace cpputils::logging; @@ -11,7 +11,7 @@ using testing::MatchesRegex; TEST_F(LoggingTest, DefaultLoggerIsStderr) { string output = captureStderr([]{ - LOG(INFO) << "My log message"; + LOG(INFO, "My log message"); }); EXPECT_THAT(output, MatchesRegex(".*\\[Log\\].*\\[info\\].*My log message.*")); } @@ -19,52 +19,52 @@ TEST_F(LoggingTest, DefaultLoggerIsStderr) { TEST_F(LoggingTest, SetLogger_NewLoggerIsUsed) { setLogger(spdlog::stderr_logger_mt("MyTestLog2")); string output = captureStderr([]{ - LOG(INFO) << "My log message"; + LOG(INFO, "My log message"); }); EXPECT_THAT(output, MatchesRegex(".*\\[MyTestLog2\\].*\\[info\\].*My log message.*")); } TEST_F(LoggingTest, SetNonStderrLogger_LogsToNewLogger) { setLogger(mockLogger.get()); - logger()->info() << "My log message"; + logger()->info("My log message"); EXPECT_THAT(mockLogger.capturedLog(), MatchesRegex(".*\\[MockLogger\\].*\\[info\\].*My log message.*")); } TEST_F(LoggingTest, SetNonStderrLogger_DoesNotLogToStderr) { setLogger(mockLogger.get()); string output = captureStderr([] { - logger()->info() << "My log message"; + logger()->info("My log message"); }); EXPECT_EQ("", output); } TEST_F(LoggingTest, InfoLog) { setLogger(mockLogger.get()); - LOG(INFO) << "My log message"; + LOG(INFO, "My log message"); EXPECT_THAT(mockLogger.capturedLog(), MatchesRegex(".*\\[MockLogger\\].*\\[info\\].*My log message.*")); } TEST_F(LoggingTest, WarningLog) { setLogger(mockLogger.get()); - LOG(WARN) << "My log message"; + LOG(WARN, "My log message"); EXPECT_THAT(mockLogger.capturedLog(), MatchesRegex(".*\\[MockLogger\\].*\\[warning\\].*My log message.*")); } TEST_F(LoggingTest, DebugLog) { setLevel(DEBUG); setLogger(mockLogger.get()); - LOG(DEBUG) << "My log message"; + LOG(DEBUG, "My log message"); EXPECT_THAT(mockLogger.capturedLog(), MatchesRegex(".*\\[MockLogger\\].*\\[debug\\].*My log message.*")); } TEST_F(LoggingTest, ErrorLog) { setLogger(mockLogger.get()); - LOG(ERROR) << "My log message"; + LOG(ERROR, "My log message"); EXPECT_THAT(mockLogger.capturedLog(), MatchesRegex(".*\\[MockLogger\\].*\\[error\\].*My log message.*")); } void logAndExit(const string &message) { - LOG(INFO) << message; + LOG(INFO, message); exit(1); } @@ -78,3 +78,41 @@ TEST_F(LoggingTest, LoggingAlsoWorksAfterFork) { "My log message" ); } + +TEST_F(LoggingTest, MessageIsConstChar) { + setLogger(mockLogger.get()); + LOG(INFO, "My log message"); + EXPECT_THAT(mockLogger.capturedLog(), MatchesRegex(".*\\[MockLogger\\].*\\[info\\].*My log message.*")); +} + +TEST_F(LoggingTest, MessageIsString) { + setLogger(mockLogger.get()); + string msg = "My log message"; + LOG(INFO, msg); + EXPECT_THAT(mockLogger.capturedLog(), MatchesRegex(".*\\[MockLogger\\].*\\[info\\].*My log message.*")); +} + +TEST_F(LoggingTest, FormatWithStringPlaceholder) { + setLogger(mockLogger.get()); + string str = "placeholder"; + LOG(INFO, "My log message: {}", str); + EXPECT_THAT(mockLogger.capturedLog(), MatchesRegex(".*\\[MockLogger\\].*\\[info\\].*My log message: placeholder.*")); +} + +TEST_F(LoggingTest, FormatWithConstCharPlaceholder) { + setLogger(mockLogger.get()); + LOG(INFO, "My log message: {}", "placeholder"); + EXPECT_THAT(mockLogger.capturedLog(), MatchesRegex(".*\\[MockLogger\\].*\\[info\\].*My log message: placeholder.*")); +} + +TEST_F(LoggingTest, FormatWithIntPlaceholder) { + setLogger(mockLogger.get()); + LOG(INFO, "My log message: {}", 4); + EXPECT_THAT(mockLogger.capturedLog(), MatchesRegex(".*\\[MockLogger\\].*\\[info\\].*My log message: 4.*")); +} + +TEST_F(LoggingTest, FormatWithMultiplePlaceholders) { + setLogger(mockLogger.get()); + LOG(INFO, "My log message: {}, {}, {}", 4, "then", true); + EXPECT_THAT(mockLogger.capturedLog(), MatchesRegex(".*\\[MockLogger\\].*\\[info\\].*My log message: 4, then, true.*")); +} diff --git a/test/cpp-utils/logging/testutils/LoggingTest.h b/test/cpp-utils/logging/testutils/LoggingTest.h index 42d0a593..a684d6cb 100644 --- a/test/cpp-utils/logging/testutils/LoggingTest.h +++ b/test/cpp-utils/logging/testutils/LoggingTest.h @@ -5,6 +5,7 @@ #include #include #include "cpp-utils/logging/logging.h" +#include class MockLogger final { public: diff --git a/vendor/README b/vendor/README index 0d01bd03..c3056600 100644 --- a/vendor/README +++ b/vendor/README @@ -1,6 +1,4 @@ This directory contains external projects, taken from the following locations: scrypt: http://www.tarsnap.com/scrypt.html googletest: https://github.com/google/googletest/tree/release-1.8.0 -spdlog: https://github.com/gabime/spdlog/commit/0c7beb2e36008598cf80d0e8eb8635ac403febb9 -- with own fix: https://github.com/cryfs/spdlog/commit/7b8d507615b8075fc6c8793a0965a32a708288c4 -gitversion: https://github.com/smessmer/gitversion \ No newline at end of file +spdlog: https://github.com/gabime/spdlog/tree/v0.11.0/include/spdlog diff --git a/vendor/spdlog/CMakeLists.txt b/vendor/spdlog/CMakeLists.txt index 2367c565..41be9fa6 100644 --- a/vendor/spdlog/CMakeLists.txt +++ b/vendor/spdlog/CMakeLists.txt @@ -5,4 +5,4 @@ set(SOURCES ) add_library(${PROJECT_NAME} STATIC ${SOURCES}) -target_include_directories(${PROJECT_NAME} SYSTEM PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}) \ No newline at end of file +target_include_directories(${PROJECT_NAME} SYSTEM PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}) diff --git a/vendor/spdlog/spdlog/async_logger.h b/vendor/spdlog/spdlog/async_logger.h index a68b60ed..1c42fd9c 100644 --- a/vendor/spdlog/spdlog/async_logger.h +++ b/vendor/spdlog/spdlog/async_logger.h @@ -13,14 +13,15 @@ // 1. Checks if its log level is enough to log the message // 2. Push a new copy of the message to a queue (or block the caller until space is available in the queue) // 3. will throw spdlog_ex upon log exceptions -// Upong destruction, logs all remaining messages in the queue before destructing.. +// Upon destruction, logs all remaining messages in the queue before destructing.. + +#include +#include #include #include -#include "common.h" -#include "logger.h" -#include "spdlog.h" - +#include +#include namespace spdlog { @@ -40,26 +41,30 @@ public: size_t queue_size, const async_overflow_policy overflow_policy = async_overflow_policy::block_retry, const std::function& worker_warmup_cb = nullptr, - const std::chrono::milliseconds& flush_interval_ms = std::chrono::milliseconds::zero()); + const std::chrono::milliseconds& flush_interval_ms = std::chrono::milliseconds::zero(), + const std::function& worker_teardown_cb = nullptr); async_logger(const std::string& logger_name, sinks_init_list sinks, size_t queue_size, const async_overflow_policy overflow_policy = async_overflow_policy::block_retry, const std::function& worker_warmup_cb = nullptr, - const std::chrono::milliseconds& flush_interval_ms = std::chrono::milliseconds::zero()); + const std::chrono::milliseconds& flush_interval_ms = std::chrono::milliseconds::zero(), + const std::function& worker_teardown_cb = nullptr); async_logger(const std::string& logger_name, sink_ptr single_sink, size_t queue_size, const async_overflow_policy overflow_policy = async_overflow_policy::block_retry, const std::function& worker_warmup_cb = nullptr, - const std::chrono::milliseconds& flush_interval_ms = std::chrono::milliseconds::zero()); - + const std::chrono::milliseconds& flush_interval_ms = std::chrono::milliseconds::zero(), + const std::function& worker_teardown_cb = nullptr); + //Wait for the queue to be empty, and flush synchronously + //Warning: this can potentialy last forever as we wait it to complete void flush() override; protected: - void _log_msg(details::log_msg& msg) override; + void _sink_it(details::log_msg& msg) override; void _set_formatter(spdlog::formatter_ptr msg_formatter) override; void _set_pattern(const std::string& pattern) override; @@ -69,4 +74,4 @@ private: } -#include "./details/async_logger_impl.h" +#include diff --git a/vendor/spdlog/spdlog/common.h b/vendor/spdlog/spdlog/common.h index bae8b2b1..490deec7 100644 --- a/vendor/spdlog/spdlog/common.h +++ b/vendor/spdlog/spdlog/common.h @@ -9,14 +9,36 @@ #include #include #include +#include +#include +#include -//visual studio does not support noexcept yet -#ifndef _MSC_VER -#define SPDLOG_NOEXCEPT noexcept -#else -#define SPDLOG_NOEXCEPT throw() +#if defined(_WIN32) && defined(SPDLOG_WCHAR_FILENAMES) +#include +#include #endif +#include + +//visual studio upto 2013 does not support noexcept nor constexpr +#if defined(_MSC_VER) && (_MSC_VER < 1900) +#define SPDLOG_NOEXCEPT throw() +#define SPDLOG_CONSTEXPR +#else +#define SPDLOG_NOEXCEPT noexcept +#define SPDLOG_CONSTEXPR constexpr +#endif + +#if defined(__GNUC__) || defined(__clang__) +#define DEPRECATED __attribute__((deprecated)) +#elif defined(_MSC_VER) +#define DEPRECATED __declspec(deprecated) +#else +#define DEPRECATED +#endif + + +#include namespace spdlog { @@ -28,12 +50,17 @@ namespace sinks class sink; } -// Common types across the lib using log_clock = std::chrono::system_clock; using sink_ptr = std::shared_ptr < sinks::sink >; using sinks_init_list = std::initializer_list < sink_ptr >; using formatter_ptr = std::shared_ptr; +#if defined(SPDLOG_NO_ATOMIC_LEVELS) +using level_t = details::null_atomic_int; +#else +using level_t = std::atomic_int; +#endif +using log_err_handler = std::function; //Log level enum namespace level @@ -43,18 +70,15 @@ typedef enum trace = 0, debug = 1, info = 2, - notice = 3, - warn = 4, - err = 5, - critical = 6, - alert = 7, - emerg = 8, - off = 9 + warn = 3, + err = 4, + critical = 5, + off = 6 } level_enum; -static const char* level_names[] { "trace", "debug", "info", "notice", "warning", "error", "critical", "alert", "emerg", "off"}; +static const char* level_names[] { "trace", "debug", "info", "warning", "error", "critical", "off" }; -static const char* short_level_names[] { "T", "D", "I", "N", "W", "E", "C", "A", "M", "O"}; +static const char* short_level_names[] { "T", "D", "I", "W", "E", "C", "O" }; inline const char* to_str(spdlog::level::level_enum l) { @@ -81,10 +105,22 @@ enum class async_overflow_policy // // Log exception // -class spdlog_ex : public std::exception +namespace details +{ +namespace os +{ +std::string errno_str(int err_num); +} +} +class spdlog_ex: public std::exception { public: - spdlog_ex(const std::string& msg) :_msg(msg) {} + spdlog_ex(const std::string& msg):_msg(msg) + {} + spdlog_ex(const std::string& msg, int last_errno) + { + _msg = msg + ": " + details::os::errno_str(last_errno); + } const char* what() const SPDLOG_NOEXCEPT override { return _msg.c_str(); @@ -94,4 +130,14 @@ private: }; -} //spdlog \ No newline at end of file +// +// wchar support for windows file names (SPDLOG_WCHAR_FILENAMES must be defined) +// +#if defined(_WIN32) && defined(SPDLOG_WCHAR_FILENAMES) +using filename_t = std::wstring; +#else +using filename_t = std::string; +#endif + + +} //spdlog diff --git a/vendor/spdlog/spdlog/details/async_log_helper.h b/vendor/spdlog/spdlog/details/async_log_helper.h index 3179d31f..7e9b3eb1 100644 --- a/vendor/spdlog/spdlog/details/async_log_helper.h +++ b/vendor/spdlog/spdlog/details/async_log_helper.h @@ -14,17 +14,21 @@ #pragma once +#include +#include +#include +#include +#include +#include + #include -#include +#include #include - -#include "../common.h" -#include "../sinks/sink.h" -#include "./mpmc_bounded_q.h" -#include "./log_msg.h" -#include "./format.h" -#include "./os.h" - +#include +#include +#include +#include +#include namespace spdlog { @@ -82,21 +86,22 @@ async_msg(async_msg&& other) SPDLOG_NOEXCEPT: // construct from log_msg async_msg(const details::log_msg& m) : - logger_name(m.logger_name), level(m.level), time(m.time), thread_id(m.thread_id), txt(m.raw.data(), m.raw.size()), msg_type(async_msg_type::log) - {} - + { +#ifndef SPDLOG_NO_NAME + logger_name = *m.logger_name; +#endif + } // copy into log_msg void fill_log_msg(log_msg &msg) { - msg.clear(); - msg.logger_name = logger_name; + msg.logger_name = &logger_name; msg.level = level; msg.time = time; msg.thread_id = thread_id; @@ -115,9 +120,11 @@ public: async_log_helper(formatter_ptr formatter, const std::vector& sinks, size_t queue_size, + const log_err_handler err_handler, const async_overflow_policy overflow_policy = async_overflow_policy::block_retry, const std::function& worker_warmup_cb = nullptr, - const std::chrono::milliseconds& flush_interval_ms = std::chrono::milliseconds::zero()); + const std::chrono::milliseconds& flush_interval_ms = std::chrono::milliseconds::zero(), + const std::function& worker_teardown_cb = nullptr); void log(const details::log_msg& msg); @@ -126,7 +133,7 @@ public: void set_formatter(formatter_ptr); - void flush(); + void flush(bool wait_for_q); private: @@ -136,14 +143,13 @@ private: // queue of messages to log q_type _q; + log_err_handler _err_handler; + bool _flush_requested; bool _terminate_requested; - // last exception thrown from the worker thread - std::shared_ptr _last_workerthread_ex; - // overflow policy const async_overflow_policy _overflow_policy; @@ -153,13 +159,13 @@ private: // auto periodic sink flush parameter const std::chrono::milliseconds _flush_interval_ms; + // worker thread teardown callback + const std::function _worker_teardown_cb; + // worker thread std::thread _worker_thread; void push_msg(async_msg&& new_msg); - // throw last worker thread exception or if worker thread is not active - - void throw_if_bad_worker(); // worker thread main loop void worker_loop(); @@ -173,6 +179,9 @@ private: // sleep,yield or return immediatly using the time passed since last message as a hint static void sleep_or_yield(const spdlog::log_clock::time_point& now, const log_clock::time_point& last_op_time); + // wait until the queue is empty + void wait_empty_q(); + }; } } @@ -184,17 +193,21 @@ inline spdlog::details::async_log_helper::async_log_helper( formatter_ptr formatter, const std::vector& sinks, size_t queue_size, + log_err_handler err_handler, const async_overflow_policy overflow_policy, const std::function& worker_warmup_cb, - const std::chrono::milliseconds& flush_interval_ms): + const std::chrono::milliseconds& flush_interval_ms, + const std::function& worker_teardown_cb): _formatter(formatter), _sinks(sinks), _q(queue_size), + _err_handler(err_handler), _flush_requested(false), _terminate_requested(false), _overflow_policy(overflow_policy), _worker_warmup_cb(worker_warmup_cb), _flush_interval_ms(flush_interval_ms), + _worker_teardown_cb(worker_teardown_cb), _worker_thread(&async_log_helper::worker_loop, this) {} @@ -212,7 +225,7 @@ inline spdlog::details::async_log_helper::~async_log_helper() } -//Try to push and block until succeeded +//Try to push and block until succeeded (if the policy is not to discard when the queue is full) inline void spdlog::details::async_log_helper::log(const details::log_msg& msg) { push_msg(async_msg(msg)); @@ -220,10 +233,8 @@ inline void spdlog::details::async_log_helper::log(const details::log_msg& msg) } -//Try to push and block until succeeded inline void spdlog::details::async_log_helper::push_msg(details::async_log_helper::async_msg&& new_msg) { - throw_if_bad_worker(); if (!_q.enqueue(std::move(new_msg)) && _overflow_policy != async_overflow_policy::discard_log_msg) { auto last_op_time = details::os::now(); @@ -238,9 +249,12 @@ inline void spdlog::details::async_log_helper::push_msg(details::async_log_helpe } -inline void spdlog::details::async_log_helper::flush() +// optionally wait for the queue be empty and request flush from the sinks +inline void spdlog::details::async_log_helper::flush(bool wait_for_q) { push_msg(async_msg(async_msg_type::flush)); + if(wait_for_q) + wait_empty_q(); //return only make after the above flush message was processed } inline void spdlog::details::async_log_helper::worker_loop() @@ -251,24 +265,25 @@ inline void spdlog::details::async_log_helper::worker_loop() auto last_pop = details::os::now(); auto last_flush = last_pop; while(process_next_msg(last_pop, last_flush)); + if (_worker_teardown_cb) _worker_teardown_cb(); } - catch (const std::exception& ex) + catch (const std::exception &ex) { - _last_workerthread_ex = std::make_shared(std::string("async_logger worker thread exception: ") + ex.what()); + _err_handler(ex.what()); } catch (...) { - _last_workerthread_ex = std::make_shared("async_logger worker thread exception"); + _err_handler("Unknown exception"); } } // process next message in the queue -// return true if this thread should still be active (no msg with level::off was received) +// return true if this thread should still be active (while no terminate msg was received) inline bool spdlog::details::async_log_helper::process_next_msg(log_clock::time_point& last_pop, log_clock::time_point& last_flush) { async_msg incoming_async_msg; - log_msg incoming_log_msg; + if (_q.dequeue(incoming_async_msg)) { @@ -285,10 +300,16 @@ inline bool spdlog::details::async_log_helper::process_next_msg(log_clock::time_ break; default: + log_msg incoming_log_msg; incoming_async_msg.fill_log_msg(incoming_log_msg); _formatter->format(incoming_log_msg); for (auto &s : _sinks) - s->log(incoming_log_msg); + { + if(s->should_log( incoming_log_msg.level)) + { + s->log(incoming_log_msg); + } + } } return true; } @@ -301,10 +322,10 @@ inline bool spdlog::details::async_log_helper::process_next_msg(log_clock::time_ handle_flush_interval(now, last_flush); sleep_or_yield(now, last_pop); return !_terminate_requested; - } } +// flush all sinks if _flush_interval_ms has expired inline void spdlog::details::async_log_helper::handle_flush_interval(log_clock::time_point& now, log_clock::time_point& last_flush) { auto should_flush = _flush_requested || (_flush_interval_ms != std::chrono::milliseconds::zero() && now - last_flush >= _flush_interval_ms); @@ -316,44 +337,48 @@ inline void spdlog::details::async_log_helper::handle_flush_interval(log_clock:: _flush_requested = false; } } + inline void spdlog::details::async_log_helper::set_formatter(formatter_ptr msg_formatter) { _formatter = msg_formatter; } -// sleep,yield or return immediatly using the time passed since last message as a hint +// spin, yield or sleep. use the time passed since last message as a hint inline void spdlog::details::async_log_helper::sleep_or_yield(const spdlog::log_clock::time_point& now, const spdlog::log_clock::time_point& last_op_time) { - using std::chrono::milliseconds; using namespace std::this_thread; + using std::chrono::milliseconds; + using std::chrono::microseconds; auto time_since_op = now - last_op_time; - // spin upto 1 ms - if (time_since_op <= milliseconds(1)) + // spin upto 50 micros + if (time_since_op <= microseconds(50)) return; - // yield upto 10ms - if (time_since_op <= milliseconds(10)) + // yield upto 150 micros + if (time_since_op <= microseconds(100)) return yield(); - // sleep for half of duration since last op - if (time_since_op <= milliseconds(100)) - return sleep_for(time_since_op / 2); + // sleep for 20 ms upto 200 ms + if (time_since_op <= milliseconds(200)) + return sleep_for(milliseconds(20)); - return sleep_for(milliseconds(100)); + // sleep for 200 ms + return sleep_for(milliseconds(200)); } -// throw if the worker thread threw an exception or not active -inline void spdlog::details::async_log_helper::throw_if_bad_worker() +// wait for the queue to be empty +inline void spdlog::details::async_log_helper::wait_empty_q() { - if (_last_workerthread_ex) + auto last_op = details::os::now(); + while (_q.approx_size() > 0) { - auto ex = std::move(_last_workerthread_ex); - throw *ex; + sleep_or_yield(details::os::now(), last_op); } + } @@ -362,3 +387,4 @@ inline void spdlog::details::async_log_helper::throw_if_bad_worker() + diff --git a/vendor/spdlog/spdlog/details/async_logger_impl.h b/vendor/spdlog/spdlog/details/async_logger_impl.h index 83ec41b0..736d2e31 100644 --- a/vendor/spdlog/spdlog/details/async_logger_impl.h +++ b/vendor/spdlog/spdlog/details/async_logger_impl.h @@ -8,8 +8,13 @@ // Async Logger implementation // Use an async_sink (queue per logger) to perform the logging in a worker thread -#include "./async_log_helper.h" +#include +#include +#include +#include +#include +#include template inline spdlog::async_logger::async_logger(const std::string& logger_name, @@ -18,9 +23,10 @@ inline spdlog::async_logger::async_logger(const std::string& logger_name, size_t queue_size, const async_overflow_policy overflow_policy, const std::function& worker_warmup_cb, - const std::chrono::milliseconds& flush_interval_ms) : + const std::chrono::milliseconds& flush_interval_ms, + const std::function& worker_teardown_cb) : logger(logger_name, begin, end), - _async_log_helper(new details::async_log_helper(_formatter, _sinks, queue_size, overflow_policy, worker_warmup_cb, flush_interval_ms)) + _async_log_helper(new details::async_log_helper(_formatter, _sinks, queue_size, _err_handler, overflow_policy, worker_warmup_cb, flush_interval_ms, worker_teardown_cb)) { } @@ -29,25 +35,26 @@ inline spdlog::async_logger::async_logger(const std::string& logger_name, size_t queue_size, const async_overflow_policy overflow_policy, const std::function& worker_warmup_cb, - const std::chrono::milliseconds& flush_interval_ms) : - async_logger(logger_name, sinks.begin(), sinks.end(), queue_size, overflow_policy, worker_warmup_cb, flush_interval_ms) {} + const std::chrono::milliseconds& flush_interval_ms, + const std::function& worker_teardown_cb) : + async_logger(logger_name, sinks.begin(), sinks.end(), queue_size, overflow_policy, worker_warmup_cb, flush_interval_ms, worker_teardown_cb) {} inline spdlog::async_logger::async_logger(const std::string& logger_name, sink_ptr single_sink, size_t queue_size, const async_overflow_policy overflow_policy, const std::function& worker_warmup_cb, - const std::chrono::milliseconds& flush_interval_ms) : + const std::chrono::milliseconds& flush_interval_ms, + const std::function& worker_teardown_cb) : async_logger(logger_name, { single_sink -}, queue_size, overflow_policy, worker_warmup_cb, flush_interval_ms) {} +}, queue_size, overflow_policy, worker_warmup_cb, flush_interval_ms, worker_teardown_cb) {} inline void spdlog::async_logger::flush() { - - _async_log_helper->flush(); + _async_log_helper->flush(true); } inline void spdlog::async_logger::_set_formatter(spdlog::formatter_ptr msg_formatter) @@ -63,7 +70,20 @@ inline void spdlog::async_logger::_set_pattern(const std::string& pattern) } -inline void spdlog::async_logger::_log_msg(details::log_msg& msg) +inline void spdlog::async_logger::_sink_it(details::log_msg& msg) { - _async_log_helper->log(msg); + try + { + _async_log_helper->log(msg); + if (_should_flush_on(msg)) + _async_log_helper->flush(false); // do async flush + } + catch (const std::exception &ex) + { + _err_handler(ex.what()); + } + catch (...) + { + _err_handler("Unknown exception"); + } } diff --git a/vendor/spdlog/spdlog/details/file_helper.h b/vendor/spdlog/spdlog/details/file_helper.h index 0a544ca1..2e6ce9d2 100644 --- a/vendor/spdlog/spdlog/details/file_helper.h +++ b/vendor/spdlog/spdlog/details/file_helper.h @@ -10,13 +10,14 @@ // Can be set to auto flush on every line // Throw spdlog_ex exception on errors +#include +#include + +#include +#include #include #include -#include -#include "os.h" -#include "log_msg.h" - - +#include namespace spdlog { @@ -25,13 +26,13 @@ namespace details class file_helper { + public: const int open_tries = 5; const int open_interval = 10; - explicit file_helper(bool force_flush) : - _fd(nullptr), - _force_flush(force_flush) + explicit file_helper() : + _fd(nullptr) {} file_helper(const file_helper&) = delete; @@ -43,11 +44,11 @@ public: } - void open(const std::string& fname, bool truncate = false) + void open(const filename_t& fname, bool truncate = false) { close(); - const char* mode = truncate ? "wb" : "ab"; + auto *mode = truncate ? SPDLOG_FILENAME_T("wb") : SPDLOG_FILENAME_T("ab"); _filename = fname; for (int tries = 0; tries < open_tries; ++tries) { @@ -57,7 +58,7 @@ public: std::this_thread::sleep_for(std::chrono::milliseconds(open_interval)); } - throw spdlog_ex("Failed opening file " + fname + " for writing"); + throw spdlog_ex("Failed opening file " + os::filename_to_str(_filename) + " for writing", errno); } void reopen(bool truncate) @@ -88,55 +89,30 @@ public: size_t msg_size = msg.formatted.size(); auto data = msg.formatted.data(); if (std::fwrite(data, 1, msg_size, _fd) != msg_size) - throw spdlog_ex("Failed writing to file " + _filename); - - if (_force_flush) - std::fflush(_fd); - + throw spdlog_ex("Failed writing to file " + os::filename_to_str(_filename), errno); } - long size() + size_t size() { if (!_fd) - throw spdlog_ex("Cannot use size() on closed file " + _filename); - - auto pos = ftell(_fd); - if (fseek(_fd, 0, SEEK_END) != 0) - throw spdlog_ex("fseek failed on file " + _filename); - - auto file_size = ftell(_fd); - - if(fseek(_fd, pos, SEEK_SET) !=0) - throw spdlog_ex("fseek failed on file " + _filename); - - if (file_size == -1) - throw spdlog_ex("ftell failed on file " + _filename); - - - return file_size; - - + throw spdlog_ex("Cannot use size() on closed file " + os::filename_to_str(_filename)); + return os::filesize(_fd); } - const std::string& filename() const + const filename_t& filename() const { return _filename; } - static bool file_exists(const std::string& name) + static bool file_exists(const filename_t& name) { return os::file_exists(name); } - - private: FILE* _fd; - std::string _filename; - bool _force_flush; - - + filename_t _filename; }; } } diff --git a/vendor/spdlog/spdlog/details/format.cc b/vendor/spdlog/spdlog/details/format.cc deleted file mode 100644 index c77e1efa..00000000 --- a/vendor/spdlog/spdlog/details/format.cc +++ /dev/null @@ -1,949 +0,0 @@ -/* -Formatting library for C++ - -Copyright (c) 2012 - 2015, Victor Zverovich -All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are met: - -1. Redistributions of source code must retain the above copyright notice, this -list of conditions and the following disclaimer. -2. Redistributions in binary form must reproduce the above copyright notice, -this list of conditions and the following disclaimer in the documentation -and/or other materials provided with the distribution. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR -ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -*/ - -#include "format.h" - -#include - -#include -#include -#include -#include -#include -#include // for std::ptrdiff_t - -#if defined(_WIN32) && defined(__MINGW32__) -# include -#endif - -#if FMT_USE_WINDOWS_H -# if defined(NOMINMAX) || defined(FMT_WIN_MINMAX) -# include -# else -# define NOMINMAX -# include -# undef NOMINMAX -# endif -#endif - -using fmt::internal::Arg; - -#if FMT_EXCEPTIONS -# define FMT_TRY try -# define FMT_CATCH(x) catch (x) -#else -# define FMT_TRY if (true) -# define FMT_CATCH(x) if (false) -#endif - -#ifdef FMT_HEADER_ONLY -# define FMT_FUNC inline -#else -# define FMT_FUNC -#endif - -#ifdef _MSC_VER -# pragma warning(push) -# pragma warning(disable: 4127) // conditional expression is constant -# pragma warning(disable: 4702) // unreachable code -// Disable deprecation warning for strerror. The latter is not called but -// MSVC fails to detect it. -# pragma warning(disable: 4996) -#endif - -// Dummy implementations of strerror_r and strerror_s called if corresponding -// system functions are not available. -static inline fmt::internal::Null<> strerror_r(int, char *, ...) { - return fmt::internal::Null<>(); -} -static inline fmt::internal::Null<> strerror_s(char *, std::size_t, ...) { - return fmt::internal::Null<>(); -} - -namespace fmt { -namespace { - -#ifndef _MSC_VER -# define FMT_SNPRINTF snprintf -#else // _MSC_VER -inline int fmt_snprintf(char *buffer, size_t size, const char *format, ...) { - va_list args; - va_start(args, format); - int result = vsnprintf_s(buffer, size, _TRUNCATE, format, args); - va_end(args); - return result; -} -# define FMT_SNPRINTF fmt_snprintf -#endif // _MSC_VER - -#if defined(_WIN32) && defined(__MINGW32__) && !defined(__NO_ISOCEXT) -# define FMT_SWPRINTF snwprintf -#else -# define FMT_SWPRINTF swprintf -#endif // defined(_WIN32) && defined(__MINGW32__) && !defined(__NO_ISOCEXT) - -// Checks if a value fits in int - used to avoid warnings about comparing -// signed and unsigned integers. -template -struct IntChecker { - template - static bool fits_in_int(T value) { - unsigned max = INT_MAX; - return value <= max; - } - static bool fits_in_int(bool) { - return true; - } -}; - -template <> -struct IntChecker { - template - static bool fits_in_int(T value) { - return value >= INT_MIN && value <= INT_MAX; - } - static bool fits_in_int(int) { - return true; - } -}; - -const char RESET_COLOR[] = "\x1b[0m"; - -typedef void(*FormatFunc)(fmt::Writer &, int, fmt::StringRef); - -// Portable thread-safe version of strerror. -// Sets buffer to point to a string describing the error code. -// This can be either a pointer to a string stored in buffer, -// or a pointer to some static immutable string. -// Returns one of the following values: -// 0 - success -// ERANGE - buffer is not large enough to store the error message -// other - failure -// Buffer should be at least of size 1. -int safe_strerror( - int error_code, char *&buffer, std::size_t buffer_size) FMT_NOEXCEPT{ - FMT_ASSERT(buffer != 0 && buffer_size != 0, "invalid buffer"); - - class StrError { - private: - int error_code_; - char *&buffer_; - std::size_t buffer_size_; - - // A noop assignment operator to avoid bogus warnings. - void operator=(const StrError &) {} - - // Handle the result of XSI-compliant version of strerror_r. - int handle(int result) { - // glibc versions before 2.13 return result in errno. - return result == -1 ? errno : result; - } - - // Handle the result of GNU-specific version of strerror_r. - int handle(char *message) { - // If the buffer is full then the message is probably truncated. - if (message == buffer_ && strlen(buffer_) == buffer_size_ - 1) - return ERANGE; - buffer_ = message; - return 0; - } - - // Handle the case when strerror_r is not available. - int handle(fmt::internal::Null<>) { - return fallback(strerror_s(buffer_, buffer_size_, error_code_)); - } - - // Fallback to strerror_s when strerror_r is not available. - int fallback(int result) { - // If the buffer is full then the message is probably truncated. - return result == 0 && strlen(buffer_) == buffer_size_ - 1 ? - ERANGE : result; - } - - // Fallback to strerror if strerror_r and strerror_s are not available. - int fallback(fmt::internal::Null<>) { - errno = 0; - buffer_ = strerror(error_code_); - return errno; - } - - public: - StrError(int err_code, char *&buf, std::size_t buf_size) - : error_code_(err_code), buffer_(buf), buffer_size_(buf_size) {} - - int run() { - strerror_r(0, 0, ""); // Suppress a warning about unused strerror_r. - return handle(strerror_r(error_code_, buffer_, buffer_size_)); - } - }; - return StrError(error_code, buffer, buffer_size).run(); -} - -void format_error_code(fmt::Writer &out, int error_code, - fmt::StringRef message) FMT_NOEXCEPT{ - // Report error code making sure that the output fits into - // INLINE_BUFFER_SIZE to avoid dynamic memory allocation and potential - // bad_alloc. - out.clear(); - static const char SEP[] = ": "; - static const char ERROR_STR[] = "error "; - fmt::internal::IntTraits::MainType ec_value = error_code; - // 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; - error_code_size += fmt::internal::count_digits(ec_value); - if (message.size() <= fmt::internal::INLINE_BUFFER_SIZE - error_code_size) - out << message << SEP; - out << ERROR_STR << error_code; - assert(out.size() <= fmt::internal::INLINE_BUFFER_SIZE); -} - -void report_error(FormatFunc func, - int error_code, fmt::StringRef message) FMT_NOEXCEPT{ - fmt::MemoryWriter full_message; - func(full_message, error_code, message); - // Use Writer::data instead of Writer::c_str to avoid potential memory - // allocation. - std::fwrite(full_message.data(), full_message.size(), 1, stderr); - std::fputc('\n', stderr); -} - -// IsZeroInt::visit(arg) returns true iff arg is a zero integer. -class IsZeroInt : public fmt::internal::ArgVisitor { -public: - template - bool visit_any_int(T value) { - return value == 0; - } -}; - -// Checks if an argument is a valid printf width specifier and sets -// left alignment if it is negative. -class WidthHandler : public fmt::internal::ArgVisitor { -private: - fmt::FormatSpec &spec_; - - FMT_DISALLOW_COPY_AND_ASSIGN(WidthHandler); - -public: - explicit WidthHandler(fmt::FormatSpec &spec) : spec_(spec) {} - - void report_unhandled_arg() { - FMT_THROW(fmt::FormatError("width is not integer")); - } - - template - unsigned visit_any_int(T value) { - typedef typename fmt::internal::IntTraits::MainType UnsignedType; - UnsignedType width = value; - if (fmt::internal::is_negative(value)) { - spec_.align_ = fmt::ALIGN_LEFT; - width = 0 - width; - } - if (width > INT_MAX) - FMT_THROW(fmt::FormatError("number is too big")); - return static_cast(width); - } -}; - -class PrecisionHandler : - public fmt::internal::ArgVisitor { -public: - void report_unhandled_arg() { - FMT_THROW(fmt::FormatError("precision is not integer")); - } - - template - int visit_any_int(T value) { - if (!IntChecker::is_signed>::fits_in_int(value)) - FMT_THROW(fmt::FormatError("number is too big")); - return static_cast(value); - } -}; - -// Converts an integer argument to an integral type T for printf. -template -class ArgConverter : public fmt::internal::ArgVisitor, void> { -private: - fmt::internal::Arg &arg_; - wchar_t type_; - - FMT_DISALLOW_COPY_AND_ASSIGN(ArgConverter); - -public: - ArgConverter(fmt::internal::Arg &arg, wchar_t type) - : arg_(arg), type_(type) {} - - void visit_bool(bool value) { - if (type_ != 's') - visit_any_int(value); - } - - template - void visit_any_int(U value) { - bool is_signed = type_ == 'd' || type_ == 'i'; - using fmt::internal::Arg; - if (sizeof(T) <= sizeof(int)) { - // Extra casts are used to silence warnings. - if (is_signed) { - arg_.type = Arg::INT; - arg_.int_value = static_cast(static_cast(value)); - } - else { - arg_.type = Arg::UINT; - arg_.uint_value = static_cast( - static_cast::Type>(value)); - } - } - else { - if (is_signed) { - arg_.type = Arg::LONG_LONG; - arg_.long_long_value = - static_cast::Type>(value); - } - else { - arg_.type = Arg::ULONG_LONG; - arg_.ulong_long_value = - static_cast::Type>(value); - } - } - } -}; - -// Converts an integer argument to char for printf. -class CharConverter : public fmt::internal::ArgVisitor { -private: - fmt::internal::Arg &arg_; - - FMT_DISALLOW_COPY_AND_ASSIGN(CharConverter); - -public: - explicit CharConverter(fmt::internal::Arg &arg) : arg_(arg) {} - - template - void visit_any_int(T value) { - arg_.type = Arg::CHAR; - arg_.int_value = static_cast(value); - } -}; -} // namespace - -namespace internal { - -template -class PrintfArgFormatter : - public ArgFormatterBase, Char> { - - void write_null_pointer() { - this->spec().type_ = 0; - this->write("(nil)"); - } - - typedef ArgFormatterBase, Char> Base; - -public: - PrintfArgFormatter(BasicWriter &w, FormatSpec &s) - : ArgFormatterBase, Char>(w, s) {} - - void visit_bool(bool value) { - FormatSpec &fmt_spec = this->spec(); - if (fmt_spec.type_ != 's') - return this->visit_any_int(value); - fmt_spec.type_ = 0; - this->write(value); - } - - void visit_char(int value) { - const FormatSpec &fmt_spec = this->spec(); - BasicWriter &w = this->writer(); - if (fmt_spec.type_ && fmt_spec.type_ != 'c') - w.write_int(value, fmt_spec); - typedef typename BasicWriter::CharPtr CharPtr; - CharPtr out = CharPtr(); - if (fmt_spec.width_ > 1) { - Char fill = ' '; - out = w.grow_buffer(fmt_spec.width_); - if (fmt_spec.align_ != ALIGN_LEFT) { - std::fill_n(out, fmt_spec.width_ - 1, fill); - out += fmt_spec.width_ - 1; - } - else { - std::fill_n(out + 1, fmt_spec.width_ - 1, fill); - } - } - else { - out = w.grow_buffer(1); - } - *out = static_cast(value); - } - - void visit_cstring(const char *value) { - if (value) - Base::visit_cstring(value); - else if (this->spec().type_ == 'p') - write_null_pointer(); - else - this->write("(null)"); - } - - void visit_pointer(const void *value) { - if (value) - return Base::visit_pointer(value); - this->spec().type_ = 0; - write_null_pointer(); - } - - void visit_custom(Arg::CustomValue c) { - BasicFormatter formatter(ArgList(), this->writer()); - const Char format_str[] = { '}', 0 }; - const Char *format = format_str; - c.format(&formatter, c.value, &format); - } -}; -} // namespace internal -} // namespace fmt - -FMT_FUNC void fmt::SystemError::init( - int err_code, CStringRef format_str, ArgList args) { - error_code_ = err_code; - MemoryWriter w; - internal::format_system_error(w, err_code, format(format_str, args)); - std::runtime_error &base = *this; - base = std::runtime_error(w.str()); -} - -template -int fmt::internal::CharTraits::format_float( - char *buffer, std::size_t size, const char *format, - unsigned width, int precision, T value) { - if (width == 0) { - return precision < 0 ? - FMT_SNPRINTF(buffer, size, format, value) : - FMT_SNPRINTF(buffer, size, format, precision, value); - } - return precision < 0 ? - FMT_SNPRINTF(buffer, size, format, width, value) : - FMT_SNPRINTF(buffer, size, format, width, precision, value); -} - -template -int fmt::internal::CharTraits::format_float( - wchar_t *buffer, std::size_t size, const wchar_t *format, - unsigned width, int precision, T value) { - if (width == 0) { - return precision < 0 ? - FMT_SWPRINTF(buffer, size, format, value) : - FMT_SWPRINTF(buffer, size, format, precision, value); - } - return precision < 0 ? - FMT_SWPRINTF(buffer, size, format, width, value) : - FMT_SWPRINTF(buffer, size, format, width, precision, value); -} - -template -const char fmt::internal::BasicData::DIGITS[] = - "0001020304050607080910111213141516171819" - "2021222324252627282930313233343536373839" - "4041424344454647484950515253545556575859" - "6061626364656667686970717273747576777879" - "8081828384858687888990919293949596979899"; - -#define FMT_POWERS_OF_10(factor) \ - factor * 10, \ - factor * 100, \ - factor * 1000, \ - factor * 10000, \ - factor * 100000, \ - factor * 1000000, \ - factor * 10000000, \ - factor * 100000000, \ - factor * 1000000000 - -template -const uint32_t fmt::internal::BasicData::POWERS_OF_10_32[] = { - 0, FMT_POWERS_OF_10(1) -}; - -template -const uint64_t fmt::internal::BasicData::POWERS_OF_10_64[] = { - 0, - FMT_POWERS_OF_10(1), - FMT_POWERS_OF_10(fmt::ULongLong(1000000000)), - // Multiply several constants instead of using a single long long constant - // to avoid warnings about C++98 not supporting long long. - fmt::ULongLong(1000000000) * fmt::ULongLong(1000000000) * 10 -}; - -FMT_FUNC void fmt::internal::report_unknown_type(char code, const char *type) { - (void)type; - if (std::isprint(static_cast(code))) { - FMT_THROW(fmt::FormatError( - fmt::format("unknown format code '{}' for {}", code, type))); - } - FMT_THROW(fmt::FormatError( - fmt::format("unknown format code '\\x{:02x}' for {}", - static_cast(code), type))); -} - -#if FMT_USE_WINDOWS_H - -FMT_FUNC fmt::internal::UTF8ToUTF16::UTF8ToUTF16(fmt::StringRef s) { - static const char ERROR_MSG[] = "cannot convert string from UTF-8 to UTF-16"; - if (s.size() > INT_MAX) - FMT_THROW(WindowsError(ERROR_INVALID_PARAMETER, ERROR_MSG)); - int s_size = static_cast(s.size()); - int length = MultiByteToWideChar( - CP_UTF8, MB_ERR_INVALID_CHARS, s.data(), s_size, 0, 0); - if (length == 0) - FMT_THROW(WindowsError(GetLastError(), ERROR_MSG)); - buffer_.resize(length + 1); - length = MultiByteToWideChar( - CP_UTF8, MB_ERR_INVALID_CHARS, s.data(), s_size, &buffer_[0], length); - if (length == 0) - FMT_THROW(WindowsError(GetLastError(), ERROR_MSG)); - buffer_[length] = 0; -} - -FMT_FUNC fmt::internal::UTF16ToUTF8::UTF16ToUTF8(fmt::WStringRef s) { - if (int error_code = convert(s)) { - FMT_THROW(WindowsError(error_code, - "cannot convert string from UTF-16 to UTF-8")); - } -} - -FMT_FUNC int fmt::internal::UTF16ToUTF8::convert(fmt::WStringRef s) { - if (s.size() > INT_MAX) - return ERROR_INVALID_PARAMETER; - int s_size = static_cast(s.size()); - int length = WideCharToMultiByte(CP_UTF8, 0, s.data(), s_size, 0, 0, 0, 0); - if (length == 0) - return GetLastError(); - buffer_.resize(length + 1); - length = WideCharToMultiByte( - CP_UTF8, 0, s.data(), s_size, &buffer_[0], length, 0, 0); - if (length == 0) - return GetLastError(); - buffer_[length] = 0; - return 0; -} - -FMT_FUNC void fmt::WindowsError::init( - int err_code, CStringRef format_str, ArgList args) { - error_code_ = err_code; - MemoryWriter w; - internal::format_windows_error(w, err_code, format(format_str, args)); - std::runtime_error &base = *this; - base = std::runtime_error(w.str()); -} - -FMT_FUNC void fmt::internal::format_windows_error( - fmt::Writer &out, int error_code, - fmt::StringRef message) FMT_NOEXCEPT{ - class String { - private: - LPWSTR str_; - - public: - String() : str_() {} - ~String() { - LocalFree(str_); - } - LPWSTR *ptr() { - return &str_; - } - LPCWSTR c_str() const { return str_; } - }; - FMT_TRY{ - String system_message; - if (FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER | - FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, 0, - error_code, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), - reinterpret_cast(system_message.ptr()), 0, 0)) { - UTF16ToUTF8 utf8_message; - if (utf8_message.convert(system_message.c_str()) == ERROR_SUCCESS) { - out << message << ": " << utf8_message; - return; - } - } - } FMT_CATCH(...) {} - fmt::format_error_code(out, error_code, message); // 'fmt::' is for bcc32. -} - -#endif // FMT_USE_WINDOWS_H - -FMT_FUNC void fmt::internal::format_system_error( - fmt::Writer &out, int error_code, - fmt::StringRef message) FMT_NOEXCEPT{ - FMT_TRY{ - MemoryBuffer buffer; - buffer.resize(INLINE_BUFFER_SIZE); - for (;;) { - char *system_message = &buffer[0]; - int result = safe_strerror(error_code, system_message, buffer.size()); - if (result == 0) { - out << message << ": " << system_message; - return; - } - if (result != ERANGE) - break; // Can't get error message, report error code instead. - buffer.resize(buffer.size() * 2); - } - } FMT_CATCH(...) {} - fmt::format_error_code(out, error_code, message); // 'fmt::' is for bcc32. -} - -template -void fmt::internal::ArgMap::init(const ArgList &args) { - if (!map_.empty()) - return; - typedef internal::NamedArg NamedArg; - const NamedArg *named_arg = 0; - bool use_values = - args.type(ArgList::MAX_PACKED_ARGS - 1) == internal::Arg::NONE; - if (use_values) { - for (unsigned i = 0;/*nothing*/; ++i) { - internal::Arg::Type arg_type = args.type(i); - switch (arg_type) { - case internal::Arg::NONE: - return; - case internal::Arg::NAMED_ARG: - named_arg = static_cast(args.values_[i].pointer); - map_.insert(Pair(named_arg->name, *named_arg)); - break; - default: - /*nothing*/ - ; - } - } - return; - } - for (unsigned i = 0; i != ArgList::MAX_PACKED_ARGS; ++i) { - internal::Arg::Type arg_type = args.type(i); - if (arg_type == internal::Arg::NAMED_ARG) { - named_arg = static_cast(args.args_[i].pointer); - map_.insert(Pair(named_arg->name, *named_arg)); - } - } - for (unsigned i = ArgList::MAX_PACKED_ARGS;/*nothing*/; ++i) { - switch (args.args_[i].type) { - case internal::Arg::NONE: - return; - case internal::Arg::NAMED_ARG: - named_arg = static_cast(args.args_[i].pointer); - map_.insert(Pair(named_arg->name, *named_arg)); - break; - default: - /*nothing*/ - ; - } - } -} - -template -void fmt::internal::FixedBuffer::grow(std::size_t) { - FMT_THROW(std::runtime_error("buffer overflow")); -} - -FMT_FUNC Arg fmt::internal::FormatterBase::do_get_arg( - unsigned arg_index, const char *&error) { - Arg arg = args_[arg_index]; - switch (arg.type) { - case Arg::NONE: - error = "argument index out of range"; - break; - case Arg::NAMED_ARG: - arg = *static_cast(arg.pointer); - default: - /*nothing*/ - ; - } - return arg; -} - -template -void fmt::internal::PrintfFormatter::parse_flags( - FormatSpec &spec, const Char *&s) { - for (;;) { - switch (*s++) { - case '-': - spec.align_ = ALIGN_LEFT; - break; - case '+': - spec.flags_ |= SIGN_FLAG | PLUS_FLAG; - break; - case '0': - spec.fill_ = '0'; - break; - case ' ': - spec.flags_ |= SIGN_FLAG; - break; - case '#': - spec.flags_ |= HASH_FLAG; - break; - default: - --s; - return; - } - } -} - -template -Arg fmt::internal::PrintfFormatter::get_arg( - const Char *s, unsigned arg_index) { - (void)s; - const char *error = 0; - Arg arg = arg_index == UINT_MAX ? - next_arg(error) : FormatterBase::get_arg(arg_index - 1, error); - if (error) - FMT_THROW(FormatError(!*s ? "invalid format string" : error)); - return arg; -} - -template -unsigned fmt::internal::PrintfFormatter::parse_header( - const Char *&s, FormatSpec &spec) { - unsigned arg_index = UINT_MAX; - Char c = *s; - if (c >= '0' && c <= '9') { - // Parse an argument index (if followed by '$') or a width possibly - // preceded with '0' flag(s). - unsigned value = parse_nonnegative_int(s); - if (*s == '$') { // value is an argument index - ++s; - arg_index = value; - } - else { - if (c == '0') - spec.fill_ = '0'; - if (value != 0) { - // Nonzero value means that we parsed width and don't need to - // parse it or flags again, so return now. - spec.width_ = value; - return arg_index; - } - } - } - parse_flags(spec, s); - // Parse width. - if (*s >= '0' && *s <= '9') { - spec.width_ = parse_nonnegative_int(s); - } - else if (*s == '*') { - ++s; - spec.width_ = WidthHandler(spec).visit(get_arg(s)); - } - return arg_index; -} - -template -void fmt::internal::PrintfFormatter::format( - BasicWriter &writer, BasicCStringRef format_str) { - const Char *start = format_str.c_str(); - const Char *s = start; - while (*s) { - Char c = *s++; - if (c != '%') continue; - if (*s == c) { - write(writer, start, s); - start = ++s; - continue; - } - write(writer, start, s - 1); - - FormatSpec spec; - spec.align_ = ALIGN_RIGHT; - - // Parse argument index, flags and width. - unsigned arg_index = parse_header(s, spec); - - // Parse precision. - if (*s == '.') { - ++s; - if ('0' <= *s && *s <= '9') { - spec.precision_ = parse_nonnegative_int(s); - } - else if (*s == '*') { - ++s; - spec.precision_ = PrecisionHandler().visit(get_arg(s)); - } - } - - Arg arg = get_arg(s, arg_index); - if (spec.flag(HASH_FLAG) && IsZeroInt().visit(arg)) - spec.flags_ &= ~HASH_FLAG; - if (spec.fill_ == '0') { - if (arg.type <= Arg::LAST_NUMERIC_TYPE) - spec.align_ = ALIGN_NUMERIC; - else - spec.fill_ = ' '; // Ignore '0' flag for non-numeric types. - } - - // Parse length and convert the argument to the required type. - switch (*s++) { - case 'h': - if (*s == 'h') - ArgConverter(arg, *++s).visit(arg); - else - ArgConverter(arg, *s).visit(arg); - break; - case 'l': - if (*s == 'l') - ArgConverter(arg, *++s).visit(arg); - else - ArgConverter(arg, *s).visit(arg); - break; - case 'j': - ArgConverter(arg, *s).visit(arg); - break; - case 'z': - ArgConverter(arg, *s).visit(arg); - break; - case 't': - ArgConverter(arg, *s).visit(arg); - break; - case 'L': - // printf produces garbage when 'L' is omitted for long double, no - // need to do the same. - break; - default: - --s; - ArgConverter(arg, *s).visit(arg); - } - - // Parse type. - if (!*s) - FMT_THROW(FormatError("invalid format string")); - spec.type_ = static_cast(*s++); - if (arg.type <= Arg::LAST_INTEGER_TYPE) { - // Normalize type. - switch (spec.type_) { - case 'i': - case 'u': - spec.type_ = 'd'; - break; - case 'c': - // TODO: handle wchar_t - CharConverter(arg).visit(arg); - break; - } - } - - start = s; - - // Format argument. - internal::PrintfArgFormatter(writer, spec).visit(arg); - } - write(writer, start, s); -} - -FMT_FUNC void fmt::report_system_error( - int error_code, fmt::StringRef message) FMT_NOEXCEPT{ - // 'fmt::' is for bcc32. - fmt::report_error(internal::format_system_error, error_code, message); -} - -#if FMT_USE_WINDOWS_H -FMT_FUNC void fmt::report_windows_error( - int error_code, fmt::StringRef message) FMT_NOEXCEPT{ - // 'fmt::' is for bcc32. - fmt::report_error(internal::format_windows_error, error_code, message); -} -#endif - -FMT_FUNC void fmt::print(std::FILE *f, CStringRef format_str, ArgList args) { - MemoryWriter w; - w.write(format_str, args); - std::fwrite(w.data(), 1, w.size(), f); -} - -FMT_FUNC void fmt::print(CStringRef format_str, ArgList args) { - print(stdout, format_str, args); -} - -FMT_FUNC void fmt::print(std::ostream &os, CStringRef format_str, ArgList args) { - MemoryWriter w; - w.write(format_str, args); - os.write(w.data(), w.size()); -} - -FMT_FUNC void fmt::print_colored(Color c, CStringRef format, ArgList args) { - char escape[] = "\x1b[30m"; - escape[3] = static_cast('0' + c); - std::fputs(escape, stdout); - print(format, args); - std::fputs(RESET_COLOR, stdout); -} - -FMT_FUNC int fmt::fprintf(std::FILE *f, CStringRef format, ArgList args) { - MemoryWriter w; - printf(w, format, args); - std::size_t size = w.size(); - return std::fwrite(w.data(), 1, size, f) < size ? -1 : static_cast(size); -} - -#ifndef FMT_HEADER_ONLY - -template struct fmt::internal::BasicData; - -// Explicit instantiations for char. - -template void fmt::internal::FixedBuffer::grow(std::size_t); - -template void fmt::internal::ArgMap::init(const fmt::ArgList &args); - -template void fmt::internal::PrintfFormatter::format( - BasicWriter &writer, CStringRef format); - -template int fmt::internal::CharTraits::format_float( - char *buffer, std::size_t size, const char *format, - unsigned width, int precision, double value); - -template int fmt::internal::CharTraits::format_float( - char *buffer, std::size_t size, const char *format, - unsigned width, int precision, long double value); - -// Explicit instantiations for wchar_t. - -template void fmt::internal::FixedBuffer::grow(std::size_t); - -template void fmt::internal::ArgMap::init(const fmt::ArgList &args); - -template void fmt::internal::PrintfFormatter::format( - BasicWriter &writer, WCStringRef format); - -template int fmt::internal::CharTraits::format_float( - wchar_t *buffer, std::size_t size, const wchar_t *format, - unsigned width, int precision, double value); - -template int fmt::internal::CharTraits::format_float( - wchar_t *buffer, std::size_t size, const wchar_t *format, - unsigned width, int precision, long double value); - -#endif // FMT_HEADER_ONLY - -#ifdef _MSC_VER -# pragma warning(pop) -#endif \ No newline at end of file diff --git a/vendor/spdlog/spdlog/details/line_logger.h b/vendor/spdlog/spdlog/details/line_logger.h deleted file mode 100644 index 0d8a535e..00000000 --- a/vendor/spdlog/spdlog/details/line_logger.h +++ /dev/null @@ -1,201 +0,0 @@ -// -// Copyright(c) 2015 Gabi Melman. -// Distributed under the MIT License (http://opensource.org/licenses/MIT) -// -#pragma once -#include -#include "../common.h" -#include "../logger.h" - -// Line logger class - aggregates operator<< calls to fast ostream -// and logs upon destruction - -namespace spdlog -{ -namespace details -{ -class line_logger -{ -public: - line_logger(logger* callback_logger, level::level_enum msg_level, bool enabled): - _callback_logger(callback_logger), - _log_msg(msg_level), - _enabled(enabled) - {} - - // No copy intended. Only move - line_logger(const line_logger& other) = delete; - line_logger& operator=(const line_logger&) = delete; - line_logger& operator=(line_logger&&) = delete; - - - line_logger(line_logger&& other) : - _callback_logger(other._callback_logger), - _log_msg(std::move(other._log_msg)), - _enabled(other._enabled) - { - other.disable(); - } - - //Log the log message using the callback logger - ~line_logger() - { - if (_enabled) - { -#ifndef SPDLOG_NO_NAME - _log_msg.logger_name = _callback_logger->name(); -#endif -#ifndef SPDLOG_NO_DATETIME - _log_msg.time = os::now(); -#endif - -#ifndef SPDLOG_NO_THREAD_ID - _log_msg.thread_id = os::thread_id(); -#endif - _callback_logger->_log_msg(_log_msg); - } - } - - // - // Support for format string with variadic args - // - - - void write(const char* what) - { - if (_enabled) - _log_msg.raw << what; - } - - template - void write(const char* fmt, const Args&... args) - { - if (!_enabled) - return; - try - { - _log_msg.raw.write(fmt, args...); - } - catch (const fmt::FormatError& e) - { - throw spdlog_ex(fmt::format("formatting error while processing format string '{}': {}", fmt, e.what())); - } - } - - - // - // Support for operator<< - // - line_logger& operator<<(const char* what) - { - if (_enabled) - _log_msg.raw << what; - return *this; - } - - line_logger& operator<<(const std::string& what) - { - if (_enabled) - _log_msg.raw << what; - return *this; - } - - line_logger& operator<<(int what) - { - if (_enabled) - _log_msg.raw << what; - return *this; - } - - line_logger& operator<<(unsigned int what) - { - if (_enabled) - _log_msg.raw << what; - return *this; - } - - - line_logger& operator<<(long what) - { - if (_enabled) - _log_msg.raw << what; - return *this; - } - - line_logger& operator<<(unsigned long what) - { - if (_enabled) - _log_msg.raw << what; - return *this; - } - - line_logger& operator<<(long long what) - { - if (_enabled) - _log_msg.raw << what; - return *this; - } - - line_logger& operator<<(unsigned long long what) - { - if (_enabled) - _log_msg.raw << what; - return *this; - } - - line_logger& operator<<(double what) - { - if (_enabled) - _log_msg.raw << what; - return *this; - } - - line_logger& operator<<(long double what) - { - if (_enabled) - _log_msg.raw << what; - return *this; - } - - line_logger& operator<<(float what) - { - if (_enabled) - _log_msg.raw << what; - return *this; - } - - line_logger& operator<<(char what) - { - if (_enabled) - _log_msg.raw << what; - return *this; - } - - //Support user types which implements operator<< - template - line_logger& operator<<(const T& what) - { - if (_enabled) - _log_msg.raw.write("{}", what); - return *this; - } - - - void disable() - { - _enabled = false; - } - - bool is_enabled() const - { - return _enabled; - } - - -private: - logger* _callback_logger; - log_msg _log_msg; - bool _enabled; -}; -} //Namespace details -} // Namespace spdlog diff --git a/vendor/spdlog/spdlog/details/log_msg.h b/vendor/spdlog/spdlog/details/log_msg.h index bae2fb2a..ecdc73d7 100644 --- a/vendor/spdlog/spdlog/details/log_msg.h +++ b/vendor/spdlog/spdlog/details/log_msg.h @@ -5,9 +5,12 @@ #pragma once -#include -#include "../common.h" -#include "./format.h" +#include +#include + + +#include +#include namespace spdlog { @@ -16,59 +19,23 @@ namespace details struct log_msg { log_msg() = default; - log_msg(level::level_enum l): - logger_name(), - level(l), - raw(), - formatted() {} - - - log_msg(const log_msg& other) : - logger_name(other.logger_name), - level(other.level), - time(other.time), - thread_id(other.thread_id) + log_msg(const std::string *loggers_name, level::level_enum lvl) : logger_name(loggers_name), level(lvl) { - if (other.raw.size()) - raw << fmt::BasicStringRef(other.raw.data(), other.raw.size()); - if (other.formatted.size()) - formatted << fmt::BasicStringRef(other.formatted.data(), other.formatted.size()); +#ifndef SPDLOG_NO_DATETIME + time = os::now(); +#endif + +#ifndef SPDLOG_NO_THREAD_ID + thread_id = os::thread_id(); +#endif } - log_msg(log_msg&& other) : - logger_name(std::move(other.logger_name)), - level(other.level), - time(std::move(other.time)), - thread_id(other.thread_id), - raw(std::move(other.raw)), - formatted(std::move(other.formatted)) - { - other.clear(); - } + log_msg(const log_msg& other) = delete; + log_msg& operator=(log_msg&& other) = delete; + log_msg(log_msg&& other) = delete; - log_msg& operator=(log_msg&& other) - { - if (this == &other) - return *this; - logger_name = std::move(other.logger_name); - level = other.level; - time = std::move(other.time); - thread_id = other.thread_id; - raw = std::move(other.raw); - formatted = std::move(other.formatted); - other.clear(); - return *this; - } - - void clear() - { - level = level::off; - raw.clear(); - formatted.clear(); - } - - std::string logger_name; + const std::string *logger_name; level::level_enum level; log_clock::time_point time; size_t thread_id; diff --git a/vendor/spdlog/spdlog/details/logger_impl.h b/vendor/spdlog/spdlog/details/logger_impl.h index 7f0171e6..a337b359 100644 --- a/vendor/spdlog/spdlog/details/logger_impl.h +++ b/vendor/spdlog/spdlog/details/logger_impl.h @@ -5,32 +5,43 @@ #pragma once -#include "./line_logger.h" +#include +#include + +#include +#include + // create logger with given name, sinks and the default pattern formatter // all other ctors will call this one template -inline spdlog::logger::logger(const std::string& logger_name, const It& begin, const It& end) : +inline spdlog::logger::logger(const std::string& logger_name, const It& begin, const It& end): _name(logger_name), _sinks(begin, end), _formatter(std::make_shared("%+")) { - - // no support under vs2013 for member initialization for std::atomic _level = level::info; + _flush_level = level::off; + _last_err_time = 0; + _err_handler = [this](const std::string &msg) + { + this->_default_err_handler(msg); + }; } // ctor with sinks as init list -inline spdlog::logger::logger(const std::string& logger_name, sinks_init_list sinks_list) : - logger(logger_name, sinks_list.begin(), sinks_list.end()) {} +inline spdlog::logger::logger(const std::string& logger_name, sinks_init_list sinks_list): + logger(logger_name, sinks_list.begin(), sinks_list.end()) +{} // ctor with single sink -inline spdlog::logger::logger(const std::string& logger_name, spdlog::sink_ptr single_sink) : +inline spdlog::logger::logger(const std::string& logger_name, spdlog::sink_ptr single_sink): logger(logger_name, { single_sink -}) {} +}) +{} inline spdlog::logger::~logger() = default; @@ -46,210 +57,149 @@ inline void spdlog::logger::set_pattern(const std::string& pattern) _set_pattern(pattern); } -// -// log only if given level>=logger's log level -// - template -inline spdlog::details::line_logger spdlog::logger::_log_if_enabled(level::level_enum lvl, const char* fmt, const Args&... args) +inline void spdlog::logger::log(level::level_enum lvl, const char* fmt, const Args&... args) { - bool msg_enabled = should_log(lvl); - details::line_logger l(this, lvl, msg_enabled); - l.write(fmt, args...); - return l; + if (!should_log(lvl)) return; + + try + { + details::log_msg log_msg(&_name, lvl); + log_msg.raw.write(fmt, args...); + _sink_it(log_msg); + } + catch (const std::exception &ex) + { + _err_handler(ex.what()); + } + catch (...) + { + _err_handler("Unknown exception"); + } } -inline spdlog::details::line_logger spdlog::logger::_log_if_enabled(level::level_enum lvl) +template +inline void spdlog::logger::log(level::level_enum lvl, const char* msg) { - return details::line_logger(this, lvl, should_log(lvl)); + if (!should_log(lvl)) return; + try + { + details::log_msg log_msg(&_name, lvl); + log_msg.raw << msg; + _sink_it(log_msg); + } + catch (const std::exception &ex) + { + _err_handler(ex.what()); + } + catch (...) + { + _err_handler("Unknown exception"); + } + } template -inline spdlog::details::line_logger spdlog::logger::_log_if_enabled(level::level_enum lvl, const T& msg) +inline void spdlog::logger::log(level::level_enum lvl, const T& msg) { - bool msg_enabled = should_log(lvl); - details::line_logger l(this, lvl, msg_enabled); - l << msg; - return l; + if (!should_log(lvl)) return; + try + { + details::log_msg log_msg(&_name, lvl); + log_msg.raw << msg; + _sink_it(log_msg); + } + catch (const std::exception &ex) + { + _err_handler(ex.what()); + } + catch (...) + { + _err_handler("Unknown exception"); + } } -// -// logger.info(cppformat_string, arg1, arg2, arg3, ...) call style -// + template -inline spdlog::details::line_logger spdlog::logger::trace(const char* fmt, const Args&... args) +inline void spdlog::logger::trace(const char* fmt, const Args&... args) { - return _log_if_enabled(level::trace, fmt, args...); + log(level::trace, fmt, args...); } template -inline spdlog::details::line_logger spdlog::logger::debug(const char* fmt, const Args&... args) +inline void spdlog::logger::debug(const char* fmt, const Args&... args) { - return _log_if_enabled(level::debug, fmt, args...); + log(level::debug, fmt, args...); } template -inline spdlog::details::line_logger spdlog::logger::info(const char* fmt, const Args&... args) +inline void spdlog::logger::info(const char* fmt, const Args&... args) { - return _log_if_enabled(level::info, fmt, args...); + log(level::info, fmt, args...); +} + + +template +inline void spdlog::logger::warn(const char* fmt, const Args&... args) +{ + log(level::warn, fmt, args...); } template -inline spdlog::details::line_logger spdlog::logger::notice(const char* fmt, const Args&... args) +inline void spdlog::logger::error(const char* fmt, const Args&... args) { - return _log_if_enabled(level::notice, fmt, args...); + log(level::err, fmt, args...); } template -inline spdlog::details::line_logger spdlog::logger::warn(const char* fmt, const Args&... args) +inline void spdlog::logger::critical(const char* fmt, const Args&... args) { - return _log_if_enabled(level::warn, fmt, args...); -} - -template -inline spdlog::details::line_logger spdlog::logger::error(const char* fmt, const Args&... args) -{ - return _log_if_enabled(level::err, fmt, args...); -} - -template -inline spdlog::details::line_logger spdlog::logger::critical(const char* fmt, const Args&... args) -{ - return _log_if_enabled(level::critical, fmt, args...); -} - -template -inline spdlog::details::line_logger spdlog::logger::alert(const char* fmt, const Args&... args) -{ - return _log_if_enabled(level::alert, fmt, args...); -} - -template -inline spdlog::details::line_logger spdlog::logger::emerg(const char* fmt, const Args&... args) -{ - return _log_if_enabled(level::emerg, fmt, args...); -} - -// -// logger.info(msg) << ".." call style -// -template -inline spdlog::details::line_logger spdlog::logger::trace(const T& msg) -{ - return _log_if_enabled(level::trace, msg); -} - -template -inline spdlog::details::line_logger spdlog::logger::debug(const T& msg) -{ - return _log_if_enabled(level::debug, msg); + log(level::critical, fmt, args...); } template -inline spdlog::details::line_logger spdlog::logger::info(const T& msg) +inline void spdlog::logger::trace(const T& msg) { - return _log_if_enabled(level::info, msg); + log(level::trace, msg); } template -inline spdlog::details::line_logger spdlog::logger::notice(const T& msg) +inline void spdlog::logger::debug(const T& msg) { - return _log_if_enabled(level::notice, msg); + log(level::debug, msg); +} + + +template +inline void spdlog::logger::info(const T& msg) +{ + log(level::info, msg); +} + + +template +inline void spdlog::logger::warn(const T& msg) +{ + log(level::warn, msg); } template -inline spdlog::details::line_logger spdlog::logger::warn(const T& msg) +inline void spdlog::logger::error(const T& msg) { - return _log_if_enabled(level::warn, msg); + log(level::err, msg); } template -inline spdlog::details::line_logger spdlog::logger::error(const T& msg) +inline void spdlog::logger::critical(const T& msg) { - return _log_if_enabled(level::err, msg); -} - -template -inline spdlog::details::line_logger spdlog::logger::critical(const T& msg) -{ - return _log_if_enabled(level::critical, msg); -} - -template -inline spdlog::details::line_logger spdlog::logger::alert(const T& msg) -{ - return _log_if_enabled(level::alert, msg); -} - -template -inline spdlog::details::line_logger spdlog::logger::emerg(const T& msg) -{ - return _log_if_enabled(level::emerg, msg); + log(level::critical, msg); } -// -// logger.info() << ".." call style -// -inline spdlog::details::line_logger spdlog::logger::trace() -{ - return _log_if_enabled(level::trace); -} - -inline spdlog::details::line_logger spdlog::logger::debug() -{ - return _log_if_enabled(level::debug); -} - -inline spdlog::details::line_logger spdlog::logger::info() -{ - return _log_if_enabled(level::info); -} - -inline spdlog::details::line_logger spdlog::logger::notice() -{ - return _log_if_enabled(level::notice); -} - -inline spdlog::details::line_logger spdlog::logger::warn() -{ - return _log_if_enabled(level::warn); -} - -inline spdlog::details::line_logger spdlog::logger::error() -{ - return _log_if_enabled(level::err); -} - -inline spdlog::details::line_logger spdlog::logger::critical() -{ - return _log_if_enabled(level::critical); -} - -inline spdlog::details::line_logger spdlog::logger::alert() -{ - return _log_if_enabled(level::alert); -} - -inline spdlog::details::line_logger spdlog::logger::emerg() -{ - return _log_if_enabled(level::emerg); -} - - -// always log, no matter what is the actual logger's log level -template -inline spdlog::details::line_logger spdlog::logger::force_log(level::level_enum lvl, const char* fmt, const Args&... args) -{ - details::line_logger l(this, lvl, true); - l.write(fmt, args...); - return l; -} - // // name and level // @@ -263,6 +213,22 @@ inline void spdlog::logger::set_level(spdlog::level::level_enum log_level) _level.store(log_level); } +inline void spdlog::logger::set_error_handler(spdlog::log_err_handler err_handler) +{ + _err_handler = err_handler; +} + +inline spdlog::log_err_handler spdlog::logger::error_handler() +{ + return _err_handler; +} + + +inline void spdlog::logger::flush_on(level::level_enum log_level) +{ + _flush_level.store(log_level); +} + inline spdlog::level::level_enum spdlog::logger::level() const { return static_cast(_level.load(std::memory_order_relaxed)); @@ -276,11 +242,19 @@ inline bool spdlog::logger::should_log(spdlog::level::level_enum msg_level) cons // // protected virtual called at end of each user log call (if enabled) by the line_logger // -inline void spdlog::logger::_log_msg(details::log_msg& msg) +inline void spdlog::logger::_sink_it(details::log_msg& msg) { _formatter->format(msg); for (auto &sink : _sinks) - sink->log(msg); + { + if( sink->should_log( msg.level)) + { + sink->log(msg); + } + } + + if(_should_flush_on(msg)) + flush(); } inline void spdlog::logger::_set_pattern(const std::string& pattern) @@ -296,4 +270,24 @@ inline void spdlog::logger::flush() { for (auto& sink : _sinks) sink->flush(); -} \ No newline at end of file +} + +inline void spdlog::logger::_default_err_handler(const std::string &msg) +{ + auto now = time(nullptr); + if (now - _last_err_time < 60) + return; + auto tm_time = details::os::localtime(now); + char date_buf[100]; + std::strftime(date_buf, sizeof(date_buf), "%Y-%m-%d %H:%M:%S", &tm_time); + details::log_msg err_msg; + err_msg.formatted.write("[*** LOG ERROR ***] [{}] [{}] [{}]{}", name(), msg, date_buf, details::os::eol); + sinks::stderr_sink_mt::instance()->log(err_msg); + _last_err_time = now; +} + +inline bool spdlog::logger::_should_flush_on(const details::log_msg &msg) +{ + const auto flush_level = _flush_level.load(std::memory_order_relaxed); + return (msg.level >= flush_level) && (msg.level != level::off); +} diff --git a/vendor/spdlog/spdlog/details/mpmc_bounded_q.h b/vendor/spdlog/spdlog/details/mpmc_bounded_q.h index 26bda5fa..3a46e8eb 100644 --- a/vendor/spdlog/spdlog/details/mpmc_bounded_q.h +++ b/vendor/spdlog/spdlog/details/mpmc_bounded_q.h @@ -43,8 +43,10 @@ Distributed under the MIT License (http://opensource.org/licenses/MIT) #pragma once +#include + #include -#include "../common.h" +#include namespace spdlog { @@ -58,8 +60,9 @@ public: using item_type = T; mpmc_bounded_queue(size_t buffer_size) - : buffer_(new cell_t [buffer_size]), - buffer_mask_(buffer_size - 1) + :max_size_(buffer_size), + buffer_(new cell_t [buffer_size]), + buffer_mask_(buffer_size - 1) { //queue size must be power of two if(!((buffer_size >= 2) && ((buffer_size & (buffer_size - 1)) == 0))) @@ -130,6 +133,16 @@ public: return true; } + size_t approx_size() + { + size_t first_pos = dequeue_pos_.load(std::memory_order_relaxed); + size_t last_pos = enqueue_pos_.load(std::memory_order_relaxed); + if (last_pos <= first_pos) + return 0; + auto size = last_pos - first_pos; + return size < max_size_ ? size : max_size_; + } + private: struct cell_t { @@ -137,6 +150,8 @@ private: T data_; }; + size_t const max_size_; + static size_t const cacheline_size = 64; typedef char cacheline_pad_t [cacheline_size]; @@ -149,8 +164,8 @@ private: std::atomic dequeue_pos_; cacheline_pad_t pad3_; - mpmc_bounded_queue(mpmc_bounded_queue const&); - void operator = (mpmc_bounded_queue const&); + mpmc_bounded_queue(mpmc_bounded_queue const&) = delete; + void operator= (mpmc_bounded_queue const&) = delete; }; } // ns details diff --git a/vendor/spdlog/spdlog/details/null_mutex.h b/vendor/spdlog/spdlog/details/null_mutex.h index 19e90bfc..67b0aeee 100644 --- a/vendor/spdlog/spdlog/details/null_mutex.h +++ b/vendor/spdlog/spdlog/details/null_mutex.h @@ -5,7 +5,8 @@ #pragma once -// null, no cost mutex +#include +// null, no cost dummy "mutex" and dummy "atomic" int namespace spdlog { @@ -20,5 +21,25 @@ struct null_mutex return true; } }; + +struct null_atomic_int +{ + int value; + null_atomic_int() = default; + + null_atomic_int(int val):value(val) + {} + + int load(std::memory_order) const + { + return value; + } + + void store(int val) + { + value = val; + } +}; + } } diff --git a/vendor/spdlog/spdlog/details/os.h b/vendor/spdlog/spdlog/details/os.h index 30e2f6d9..ed4f45cd 100644 --- a/vendor/spdlog/spdlog/details/os.h +++ b/vendor/spdlog/spdlog/details/os.h @@ -4,29 +4,47 @@ // #pragma once -#include -#include -#include +#include + +#include +#include +#include +#include +#include +#include +#include + #ifdef _WIN32 -# ifndef WIN32_LEAN_AND_MEAN -# define WIN32_LEAN_AND_MEAN -# endif -# include + +#ifndef NOMINMAX +#define NOMINMAX //prevent windows redefining min/max +#endif + +#ifndef WIN32_LEAN_AND_MEAN +#define WIN32_LEAN_AND_MEAN +#endif +#include #ifdef __MINGW32__ #include #endif +#include + #elif __linux__ + #include //Use gettid() syscall under linux to get thread id -#include #include +#include + +#elif __FreeBSD__ +#include //Use thr_self() syscall under FreeBSD to get thread id + #else #include -#endif -#include "../common.h" +#endif namespace spdlog { @@ -105,66 +123,111 @@ inline bool operator!=(const std::tm& tm1, const std::tm& tm2) return !(tm1 == tm2); } +// eol definition +#if !defined (SPDLOG_EOL) #ifdef _WIN32 -inline const char* eol() -{ - return "\r\n"; -} +#define SPDLOG_EOL "\r\n" #else -constexpr inline const char* eol() -{ - return "\n"; -} +#define SPDLOG_EOL "\n" +#endif #endif -#ifdef _WIN32 -inline unsigned short eol_size() -{ - return 2; -} -#else -constexpr inline unsigned short eol_size() -{ - return 1; -} -#endif +SPDLOG_CONSTEXPR static const char* eol = SPDLOG_EOL; +SPDLOG_CONSTEXPR static int eol_size = sizeof(SPDLOG_EOL) - 1; + + //fopen_s on non windows for writing -inline int fopen_s(FILE** fp, const std::string& filename, const char* mode) +inline int fopen_s(FILE** fp, const filename_t& filename, const filename_t& mode) { #ifdef _WIN32 - *fp = _fsopen((filename.c_str()), mode, _SH_DENYWR); +#ifdef SPDLOG_WCHAR_FILENAMES + *fp = _wfsopen((filename.c_str()), mode.c_str(), _SH_DENYWR); +#else + *fp = _fsopen((filename.c_str()), mode.c_str(), _SH_DENYWR); +#endif return *fp == nullptr; #else - *fp = fopen((filename.c_str()), mode); + *fp = fopen((filename.c_str()), mode.c_str()); return *fp == nullptr; #endif +} +inline int remove(const filename_t &filename) +{ +#if defined(_WIN32) && defined(SPDLOG_WCHAR_FILENAMES) + return _wremove(filename.c_str()); +#else + return std::remove(filename.c_str()); +#endif +} + +inline int rename(const filename_t& filename1, const filename_t& filename2) +{ +#if defined(_WIN32) && defined(SPDLOG_WCHAR_FILENAMES) + return _wrename(filename1.c_str(), filename2.c_str()); +#else + return std::rename(filename1.c_str(), filename2.c_str()); +#endif } //Return if file exists -inline bool file_exists(const std::string& filename) +inline bool file_exists(const filename_t& filename) { #ifdef _WIN32 +#ifdef SPDLOG_WCHAR_FILENAMES + auto attribs = GetFileAttributesW(filename.c_str()); +#else auto attribs = GetFileAttributesA(filename.c_str()); +#endif return (attribs != INVALID_FILE_ATTRIBUTES && !(attribs & FILE_ATTRIBUTE_DIRECTORY)); -#elif __linux__ +#else //common linux/unix all have the stat system call struct stat buffer; return (stat (filename.c_str(), &buffer) == 0); -#else - auto *file = fopen(filename.c_str(), "r"); - if (file != nullptr) - { - fclose(file); - return true; - } - return false; +#endif +} + + + +//Return file size according to open FILE* object +inline size_t filesize(FILE *f) +{ + if (f == nullptr) + throw spdlog_ex("Failed getting file size. fd is null"); +#ifdef _WIN32 + int fd = _fileno(f); +#if _WIN64 //64 bits + struct _stat64 st; + if (_fstat64(fd, &st) == 0) + return st.st_size; + +#else //windows 32 bits + struct _stat st; + if (_fstat(fd, &st) == 0) + return st.st_size; #endif +#else // unix + int fd = fileno(f); + //64 bits(but not in osx, where fstat64 is deprecated) +#if !defined(__FreeBSD__) && !defined(__APPLE__) && (defined(__x86_64__) || defined(__ppc64__)) + struct stat64 st; + if (fstat64(fd, &st) == 0) + return st.st_size; +#else // unix 32 bits or osx + struct stat st; + if (fstat(fd, &st) == 0) + return st.st_size; +#endif +#endif + throw spdlog_ex("Failed getting file size from fd", errno); } + + + //Return utc offset in minutes or throw spdlog_ex on failure inline int utc_minutes_offset(const std::tm& tm = details::os::localtime()) { @@ -178,7 +241,7 @@ inline int utc_minutes_offset(const std::tm& tm = details::os::localtime()) auto rv = GetDynamicTimeZoneInformation(&tzinfo); #endif if (rv == TIME_ZONE_ID_INVALID) - throw spdlog::spdlog_ex("Failed getting timezone info. Last error: " + GetLastError()); + throw spdlog::spdlog_ex("Failed getting timezone info. ", errno); int offset = -tzinfo.Bias; if (tm.tm_isdst) @@ -187,7 +250,43 @@ inline int utc_minutes_offset(const std::tm& tm = details::os::localtime()) offset -= tzinfo.StandardBias; return offset; #else - return static_cast(tm.tm_gmtoff / 60); + +#if defined(sun) || defined(__sun) + // 'tm_gmtoff' field is BSD extension and it's missing on SunOS/Solaris + struct helper + { + static long int calculate_gmt_offset(const std::tm & localtm = details::os::localtime(), const std::tm & gmtm = details::os::gmtime()) + { + int local_year = localtm.tm_year + (1900 - 1); + int gmt_year = gmtm.tm_year + (1900 - 1); + + long int days = ( + // difference in day of year + localtm.tm_yday - gmtm.tm_yday + + // + intervening leap days + + ((local_year >> 2) - (gmt_year >> 2)) + - (local_year / 100 - gmt_year / 100) + + ((local_year / 100 >> 2) - (gmt_year / 100 >> 2)) + + // + difference in years * 365 */ + + (long int)(local_year - gmt_year) * 365 + ); + + long int hours = (24 * days) + (localtm.tm_hour - gmtm.tm_hour); + long int mins = (60 * hours) + (localtm.tm_min - gmtm.tm_min); + long int secs = (60 * mins) + (localtm.tm_sec - gmtm.tm_sec); + + return secs; + } + }; + + long int offset_seconds = helper::calculate_gmt_offset(tm); +#else + long int offset_seconds = tm.tm_gmtoff; +#endif + + return static_cast(offset_seconds / 60); #endif } @@ -202,14 +301,60 @@ inline size_t thread_id() # define SYS_gettid __NR_gettid # endif return static_cast(syscall(SYS_gettid)); +#elif __FreeBSD__ + long tid; + thr_self(&tid); + return static_cast(tid); #else //Default to standard C++11 (OSX and other Unix) return static_cast(std::hash()(std::this_thread::get_id())); #endif + +} + + +// wchar support for windows file names (SPDLOG_WCHAR_FILENAMES must be defined) +#if defined(_WIN32) && defined(SPDLOG_WCHAR_FILENAMES) +#define SPDLOG_FILENAME_T(s) L ## s +inline std::string filename_to_str(const filename_t& filename) +{ + std::wstring_convert, wchar_t> c; + return c.to_bytes(filename); +} +#else +#define SPDLOG_FILENAME_T(s) s +inline std::string filename_to_str(const filename_t& filename) +{ + return filename; +} +#endif + + +// Return errno string (thread safe) +inline std::string errno_str(int err_num) +{ + char buf[256]; + SPDLOG_CONSTEXPR auto buf_size = sizeof(buf); + +#ifdef _WIN32 + if(strerror_s(buf, buf_size, err_num) == 0) + return std::string(buf); + else + return "Unkown error"; + +#elif defined(__FreeBSD__) || defined(__APPLE__) || defined(ANDROID) || \ + ((_POSIX_C_SOURCE >= 200112L) && ! _GNU_SOURCE) // posix version + + if (strerror_r(err_num, buf, buf_size) == 0) + return std::string(buf); + else + return "Unkown error"; + +#else // gnu version (might not use the given buf, so its retval pointer must be used) + return std::string(strerror_r(err_num, buf, buf_size)); +#endif } } //os } //details } //spdlog - - diff --git a/vendor/spdlog/spdlog/details/pattern_formatter_impl.h b/vendor/spdlog/spdlog/details/pattern_formatter_impl.h index 92ccc376..73c0db3b 100644 --- a/vendor/spdlog/spdlog/details/pattern_formatter_impl.h +++ b/vendor/spdlog/spdlog/details/pattern_formatter_impl.h @@ -5,16 +5,19 @@ #pragma once -#include +#include +#include +#include +#include + #include +#include #include -#include +#include +#include #include - - -#include "../formatter.h" -#include "./log_msg.h" -#include "./os.h" +#include +#include namespace spdlog { @@ -23,7 +26,8 @@ namespace details class flag_formatter { public: - virtual ~flag_formatter() {} + virtual ~flag_formatter() + {} virtual void format(details::log_msg& msg, const std::tm& tm_time) = 0; }; @@ -32,17 +36,17 @@ public: /////////////////////////////////////////////////////////////////////// namespace { -class name_formatter :public flag_formatter +class name_formatter:public flag_formatter { void format(details::log_msg& msg, const std::tm&) override { - msg.formatted << msg.logger_name; + msg.formatted << *msg.logger_name; } }; } // log level appender -class level_formatter :public flag_formatter +class level_formatter:public flag_formatter { void format(details::log_msg& msg, const std::tm&) override { @@ -51,7 +55,7 @@ class level_formatter :public flag_formatter }; // short log level appender -class short_level_formatter :public flag_formatter +class short_level_formatter:public flag_formatter { void format(details::log_msg& msg, const std::tm&) override { @@ -75,7 +79,7 @@ static int to12h(const tm& t) //Abbreviated weekday name static const std::string days[] { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" }; -class a_formatter :public flag_formatter +class a_formatter:public flag_formatter { void format(details::log_msg& msg, const std::tm& tm_time) override { @@ -85,7 +89,7 @@ class a_formatter :public flag_formatter //Full weekday name static const std::string full_days[] { "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday" }; -class A_formatter :public flag_formatter +class A_formatter:public flag_formatter { void format(details::log_msg& msg, const std::tm& tm_time) override { @@ -95,17 +99,17 @@ class A_formatter :public flag_formatter //Abbreviated month static const std::string months[] { "Jan", "Feb", "Mar", "Apr", "May", "June", "July", "Aug", "Sept", "Oct", "Nov", "Dec" }; -class b_formatter :public flag_formatter +class b_formatter:public flag_formatter { void format(details::log_msg& msg, const std::tm& tm_time) override { - msg.formatted<< months[tm_time.tm_mon]; + msg.formatted << months[tm_time.tm_mon]; } }; //Full month name static const std::string full_months[] { "January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December" }; -class B_formatter :public flag_formatter +class B_formatter:public flag_formatter { void format(details::log_msg& msg, const std::tm& tm_time) override { @@ -130,7 +134,7 @@ static fmt::MemoryWriter& pad_n_join(fmt::MemoryWriter& w, int v1, int v2, int v //Date and time representation (Thu Aug 23 15:35:46 2014) -class c_formatter :public flag_formatter +class c_formatter:public flag_formatter { void format(details::log_msg& msg, const std::tm& tm_time) override { @@ -141,7 +145,7 @@ class c_formatter :public flag_formatter // year - 2 digit -class C_formatter :public flag_formatter +class C_formatter:public flag_formatter { void format(details::log_msg& msg, const std::tm& tm_time) override { @@ -152,7 +156,7 @@ class C_formatter :public flag_formatter // Short MM/DD/YY date, equivalent to %m/%d/%y 08/23/01 -class D_formatter :public flag_formatter +class D_formatter:public flag_formatter { void format(details::log_msg& msg, const std::tm& tm_time) override { @@ -162,7 +166,7 @@ class D_formatter :public flag_formatter // year - 4 digit -class Y_formatter :public flag_formatter +class Y_formatter:public flag_formatter { void format(details::log_msg& msg, const std::tm& tm_time) override { @@ -171,7 +175,7 @@ class Y_formatter :public flag_formatter }; // month 1-12 -class m_formatter :public flag_formatter +class m_formatter:public flag_formatter { void format(details::log_msg& msg, const std::tm& tm_time) override { @@ -180,7 +184,7 @@ class m_formatter :public flag_formatter }; // day of month 1-31 -class d_formatter :public flag_formatter +class d_formatter:public flag_formatter { void format(details::log_msg& msg, const std::tm& tm_time) override { @@ -189,7 +193,7 @@ class d_formatter :public flag_formatter }; // hours in 24 format 0-23 -class H_formatter :public flag_formatter +class H_formatter:public flag_formatter { void format(details::log_msg& msg, const std::tm& tm_time) override { @@ -198,7 +202,7 @@ class H_formatter :public flag_formatter }; // hours in 12 format 1-12 -class I_formatter :public flag_formatter +class I_formatter:public flag_formatter { void format(details::log_msg& msg, const std::tm& tm_time) override { @@ -207,7 +211,7 @@ class I_formatter :public flag_formatter }; // minutes 0-59 -class M_formatter :public flag_formatter +class M_formatter:public flag_formatter { void format(details::log_msg& msg, const std::tm& tm_time) override { @@ -216,7 +220,7 @@ class M_formatter :public flag_formatter }; // seconds 0-59 -class S_formatter :public flag_formatter +class S_formatter:public flag_formatter { void format(details::log_msg& msg, const std::tm& tm_time) override { @@ -225,7 +229,7 @@ class S_formatter :public flag_formatter }; // milliseconds -class e_formatter :public flag_formatter +class e_formatter:public flag_formatter { void format(details::log_msg& msg, const std::tm&) override { @@ -236,7 +240,7 @@ class e_formatter :public flag_formatter }; // microseconds -class f_formatter :public flag_formatter +class f_formatter:public flag_formatter { void format(details::log_msg& msg, const std::tm&) override { @@ -247,7 +251,7 @@ class f_formatter :public flag_formatter }; // nanoseconds -class F_formatter :public flag_formatter +class F_formatter:public flag_formatter { void format(details::log_msg& msg, const std::tm&) override { @@ -258,7 +262,7 @@ class F_formatter :public flag_formatter }; // AM/PM -class p_formatter :public flag_formatter +class p_formatter:public flag_formatter { void format(details::log_msg& msg, const std::tm& tm_time) override { @@ -268,7 +272,7 @@ class p_formatter :public flag_formatter // 12 hour clock 02:55:02 pm -class r_formatter :public flag_formatter +class r_formatter:public flag_formatter { void format(details::log_msg& msg, const std::tm& tm_time) override { @@ -277,7 +281,7 @@ class r_formatter :public flag_formatter }; // 24-hour HH:MM time, equivalent to %H:%M -class R_formatter :public flag_formatter +class R_formatter:public flag_formatter { void format(details::log_msg& msg, const std::tm& tm_time) override { @@ -286,7 +290,7 @@ class R_formatter :public flag_formatter }; // ISO 8601 time format (HH:MM:SS), equivalent to %H:%M:%S -class T_formatter :public flag_formatter +class T_formatter:public flag_formatter { void format(details::log_msg& msg, const std::tm& tm_time) override { @@ -296,12 +300,13 @@ class T_formatter :public flag_formatter // ISO 8601 offset from UTC in timezone (+-HH:MM) -class z_formatter :public flag_formatter +class z_formatter:public flag_formatter { public: const std::chrono::seconds cache_refresh = std::chrono::seconds(5); - z_formatter() :_last_update(std::chrono::seconds(0)) {} + z_formatter():_last_update(std::chrono::seconds(0)) + {} z_formatter(const z_formatter&) = delete; z_formatter& operator=(const z_formatter&) = delete; @@ -314,13 +319,21 @@ public: // it is very fast (already stored in tm.tm_gmtoff) int total_minutes = os::utc_minutes_offset(tm_time); #endif + bool is_negative = total_minutes < 0; + char sign; + if (is_negative) + { + total_minutes = -total_minutes; + sign = '-'; + } + else + { + sign = '+'; + } int h = total_minutes / 60; int m = total_minutes % 60; - if (h >= 0) //minus sign will be printed anyway if negative - { - msg.formatted << '+'; - } + msg.formatted << sign; pad_n_join(msg.formatted, h, m, ':'); } private: @@ -344,7 +357,7 @@ private: //Thread id -class t_formatter :public flag_formatter +class t_formatter:public flag_formatter { void format(details::log_msg& msg, const std::tm&) override { @@ -353,7 +366,7 @@ class t_formatter :public flag_formatter }; -class v_formatter :public flag_formatter +class v_formatter:public flag_formatter { void format(details::log_msg& msg, const std::tm&) override { @@ -361,10 +374,10 @@ class v_formatter :public flag_formatter } }; -class ch_formatter :public flag_formatter +class ch_formatter:public flag_formatter { public: - explicit ch_formatter(char ch) : _ch(ch) + explicit ch_formatter(char ch): _ch(ch) {} void format(details::log_msg& msg, const std::tm&) override { @@ -376,7 +389,7 @@ private: //aggregate user chars to display as is -class aggregate_formatter :public flag_formatter +class aggregate_formatter:public flag_formatter { public: aggregate_formatter() @@ -395,7 +408,7 @@ private: // Full info formatter // pattern: [%Y-%m-%d %H:%M:%S.%e] [%n] [%l] %v -class full_formatter :public flag_formatter +class full_formatter:public flag_formatter { void format(details::log_msg& msg, const std::tm& tm_time) override { @@ -426,13 +439,13 @@ class full_formatter :public flag_formatter << fmt::pad(static_cast(tm_time.tm_sec), 2, '0') << '.' << fmt::pad(static_cast(millis), 3, '0') << "] "; -//no datetime needed + //no datetime needed #else (void)tm_time; #endif #ifndef SPDLOG_NO_NAME - msg.formatted << '[' << msg.logger_name << "] "; + msg.formatted << '[' << *msg.logger_name << "] "; #endif msg.formatted << '[' << level::to_str(msg.level) << "] "; @@ -496,101 +509,101 @@ inline void spdlog::pattern_formatter::handle_flag(char flag) _formatters.push_back(std::unique_ptr(new details::short_level_formatter())); break; - case('t') : + case('t'): _formatters.push_back(std::unique_ptr(new details::t_formatter())); break; - case('v') : + case('v'): _formatters.push_back(std::unique_ptr(new details::v_formatter())); break; - case('a') : + case('a'): _formatters.push_back(std::unique_ptr(new details::a_formatter())); break; - case('A') : + case('A'): _formatters.push_back(std::unique_ptr(new details::A_formatter())); break; - case('b') : - case('h') : + case('b'): + case('h'): _formatters.push_back(std::unique_ptr(new details::b_formatter())); break; - case('B') : + case('B'): _formatters.push_back(std::unique_ptr(new details::B_formatter())); break; - case('c') : + case('c'): _formatters.push_back(std::unique_ptr(new details::c_formatter())); break; - case('C') : + case('C'): _formatters.push_back(std::unique_ptr(new details::C_formatter())); break; - case('Y') : + case('Y'): _formatters.push_back(std::unique_ptr(new details::Y_formatter())); break; - case('D') : - case('x') : + case('D'): + case('x'): _formatters.push_back(std::unique_ptr(new details::D_formatter())); break; - case('m') : + case('m'): _formatters.push_back(std::unique_ptr(new details::m_formatter())); break; - case('d') : + case('d'): _formatters.push_back(std::unique_ptr(new details::d_formatter())); break; - case('H') : + case('H'): _formatters.push_back(std::unique_ptr(new details::H_formatter())); break; - case('I') : + case('I'): _formatters.push_back(std::unique_ptr(new details::I_formatter())); break; - case('M') : + case('M'): _formatters.push_back(std::unique_ptr(new details::M_formatter())); break; - case('S') : + case('S'): _formatters.push_back(std::unique_ptr(new details::S_formatter())); break; - case('e') : + case('e'): _formatters.push_back(std::unique_ptr(new details::e_formatter())); break; - case('f') : + case('f'): _formatters.push_back(std::unique_ptr(new details::f_formatter())); break; - case('F') : + case('F'): _formatters.push_back(std::unique_ptr(new details::F_formatter())); break; - case('p') : + case('p'): _formatters.push_back(std::unique_ptr(new details::p_formatter())); break; - case('r') : + case('r'): _formatters.push_back(std::unique_ptr(new details::r_formatter())); break; - case('R') : + case('R'): _formatters.push_back(std::unique_ptr(new details::R_formatter())); break; - case('T') : - case('X') : + case('T'): + case('X'): _formatters.push_back(std::unique_ptr(new details::T_formatter())); break; - case('z') : + case('z'): _formatters.push_back(std::unique_ptr(new details::z_formatter())); break; @@ -608,18 +621,16 @@ inline void spdlog::pattern_formatter::handle_flag(char flag) inline void spdlog::pattern_formatter::format(details::log_msg& msg) { - try + +#ifndef SPDLOG_NO_DATETIME + auto tm_time = details::os::localtime(log_clock::to_time_t(msg.time)); +#else + std::tm tm_time; +#endif + for (auto &f : _formatters) { - auto tm_time = details::os::localtime(log_clock::to_time_t(msg.time)); - for (auto &f : _formatters) - { - f->format(msg, tm_time); - } - //write eol - msg.formatted << details::os::eol(); - } - catch(const fmt::FormatError& e) - { - throw spdlog_ex(fmt::format("formatting error while processing format string: {}", e.what())); + f->format(msg, tm_time); } + //write eol + msg.formatted.write(details::os::eol, details::os::eol_size); } diff --git a/vendor/spdlog/spdlog/details/registry.h b/vendor/spdlog/spdlog/details/registry.h index a26db799..ee14adfd 100644 --- a/vendor/spdlog/spdlog/details/registry.h +++ b/vendor/spdlog/spdlog/details/registry.h @@ -10,15 +10,17 @@ // If user requests a non existing logger, nullptr will be returned // This class is thread safe -#include -#include -#include -#include +#include +#include +#include +#include -#include "./null_mutex.h" -#include "../logger.h" -#include "../async_logger.h" -#include "../common.h" +#include +#include +#include +#include +#include +#include namespace spdlog { @@ -31,7 +33,9 @@ public: void register_logger(std::shared_ptr logger) { std::lock_guard lock(_mutex); - register_logger_impl(logger); + auto logger_name = logger->name(); + throw_if_exists(logger_name); + _loggers[logger_name] = logger; } @@ -45,25 +49,35 @@ public: template std::shared_ptr create(const std::string& logger_name, const It& sinks_begin, const It& sinks_end) { - - std::shared_ptr new_logger; - std::lock_guard lock(_mutex); - - + throw_if_exists(logger_name); + std::shared_ptr new_logger; if (_async_mode) - new_logger = std::make_shared(logger_name, sinks_begin, sinks_end, _async_q_size, _overflow_policy, _worker_warmup_cb, _flush_interval_ms); + new_logger = std::make_shared(logger_name, sinks_begin, sinks_end, _async_q_size, _overflow_policy, _worker_warmup_cb, _flush_interval_ms, _worker_teardown_cb); else new_logger = std::make_shared(logger_name, sinks_begin, sinks_end); if (_formatter) new_logger->set_formatter(_formatter); + if (_err_handler) + new_logger->set_error_handler(_err_handler); + new_logger->set_level(_level); - register_logger_impl(new_logger); + + + //Add to registry + _loggers[logger_name] = new_logger; return new_logger; } + void apply_all(std::function)> fun) + { + std::lock_guard lock(_mutex); + for (auto &l : _loggers) + fun(l.second); + } + void drop(const std::string& logger_name) { std::lock_guard lock(_mutex); @@ -110,7 +124,14 @@ public: _level = log_level; } - void set_async_mode(size_t q_size, const async_overflow_policy overflow_policy, const std::function& worker_warmup_cb, const std::chrono::milliseconds& flush_interval_ms) + void set_error_handler(log_err_handler handler) + { + for (auto& l : _loggers) + l.second->set_error_handler(handler); + _err_handler = handler; + } + + void set_async_mode(size_t q_size, const async_overflow_policy overflow_policy, const std::function& worker_warmup_cb, const std::chrono::milliseconds& flush_interval_ms, const std::function& worker_teardown_cb) { std::lock_guard lock(_mutex); _async_mode = true; @@ -118,6 +139,7 @@ public: _overflow_policy = overflow_policy; _worker_warmup_cb = worker_warmup_cb; _flush_interval_ms = flush_interval_ms; + _worker_teardown_cb = worker_teardown_cb; } void set_sync_mode() @@ -133,25 +155,26 @@ public: } private: - void register_logger_impl(std::shared_ptr logger) - { - auto logger_name = logger->name(); - if (_loggers.find(logger_name) != std::end(_loggers)) - throw spdlog_ex("logger with name " + logger_name + " already exists"); - _loggers[logger->name()] = logger; - } registry_t() {} registry_t(const registry_t&) = delete; registry_t& operator=(const registry_t&) = delete; + + void throw_if_exists(const std::string &logger_name) + { + if (_loggers.find(logger_name) != _loggers.end()) + throw spdlog_ex("logger with name '" + logger_name + "' already exists"); + } Mutex _mutex; std::unordered_map > _loggers; formatter_ptr _formatter; level::level_enum _level = level::info; + log_err_handler _err_handler; bool _async_mode = false; size_t _async_q_size = 0; async_overflow_policy _overflow_policy = async_overflow_policy::block_retry; std::function _worker_warmup_cb = nullptr; std::chrono::milliseconds _flush_interval_ms; + std::function _worker_teardown_cb = nullptr; }; #ifdef SPDLOG_NO_REGISTRY_MUTEX typedef registry_t registry; diff --git a/vendor/spdlog/spdlog/details/spdlog_impl.h b/vendor/spdlog/spdlog/details/spdlog_impl.h index c8cc2168..bc283c83 100644 --- a/vendor/spdlog/spdlog/details/spdlog_impl.h +++ b/vendor/spdlog/spdlog/details/spdlog_impl.h @@ -8,10 +8,18 @@ // // Global registry functions // -#include "registry.h" -#include "../sinks/file_sinks.h" -#include "../sinks/stdout_sinks.h" -#include "../sinks/syslog_sink.h" +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include inline void spdlog::register_logger(std::shared_ptr logger) { @@ -28,50 +36,68 @@ inline void spdlog::drop(const std::string &name) details::registry::instance().drop(name); } -// Create multi/single threaded rotating file logger -inline std::shared_ptr spdlog::rotating_logger_mt(const std::string& logger_name, const std::string& filename, size_t max_file_size, size_t max_files, bool force_flush) +// Create multi/single threaded simple file logger +inline std::shared_ptr spdlog::basic_logger_mt(const std::string& logger_name, const filename_t& filename, bool truncate) { - return create(logger_name, filename, "txt", max_file_size, max_files, force_flush); + return create(logger_name, filename, truncate); } -inline std::shared_ptr spdlog::rotating_logger_st(const std::string& logger_name, const std::string& filename, size_t max_file_size, size_t max_files, bool force_flush) +inline std::shared_ptr spdlog::basic_logger_st(const std::string& logger_name, const filename_t& filename, bool truncate) { - return create(logger_name, filename, "txt", max_file_size, max_files, force_flush); + return create(logger_name, filename, truncate); +} + +// Create multi/single threaded rotating file logger +inline std::shared_ptr spdlog::rotating_logger_mt(const std::string& logger_name, const filename_t& filename, size_t max_file_size, size_t max_files) +{ + return create(logger_name, filename, SPDLOG_FILENAME_T("txt"), max_file_size, max_files); +} + +inline std::shared_ptr spdlog::rotating_logger_st(const std::string& logger_name, const filename_t& filename, size_t max_file_size, size_t max_files) +{ + return create(logger_name, filename, SPDLOG_FILENAME_T("txt"), max_file_size, max_files); } // Create file logger which creates new file at midnight): -inline std::shared_ptr spdlog::daily_logger_mt(const std::string& logger_name, const std::string& filename, int hour, int minute, bool force_flush) +inline std::shared_ptr spdlog::daily_logger_mt(const std::string& logger_name, const filename_t& filename, int hour, int minute) { - return create(logger_name, filename, "txt", hour, minute, force_flush); -} -inline std::shared_ptr spdlog::daily_logger_st(const std::string& logger_name, const std::string& filename, int hour, int minute, bool force_flush) -{ - return create(logger_name, filename, "txt", hour, minute, force_flush); + return create(logger_name, filename, SPDLOG_FILENAME_T("txt"), hour, minute); } - -// Create stdout/stderr loggers -inline std::shared_ptr spdlog::stdout_logger_mt(const std::string& logger_name) +inline std::shared_ptr spdlog::daily_logger_st(const std::string& logger_name, const filename_t& filename, int hour, int minute) { - return details::registry::instance().create(logger_name, spdlog::sinks::stdout_sink_mt::instance()); + return create(logger_name, filename, SPDLOG_FILENAME_T("txt"), hour, minute); } -inline std::shared_ptr spdlog::stdout_logger_st(const std::string& logger_name) +// Create stdout/stderr loggers (with optinal color support) +inline std::shared_ptr create_console_logger(const std::string& logger_name, spdlog::sink_ptr sink, bool color) { - return details::registry::instance().create(logger_name, spdlog::sinks::stdout_sink_st::instance()); + if (color) //use color wrapper sink + sink = std::make_shared(sink); + return spdlog::details::registry::instance().create(logger_name, sink); } -inline std::shared_ptr spdlog::stderr_logger_mt(const std::string& logger_name) +inline std::shared_ptr spdlog::stdout_logger_mt(const std::string& logger_name, bool color) { - return details::registry::instance().create(logger_name, spdlog::sinks::stderr_sink_mt::instance()); + return create_console_logger(logger_name, sinks::stdout_sink_mt::instance(), color); } -inline std::shared_ptr spdlog::stderr_logger_st(const std::string& logger_name) +inline std::shared_ptr spdlog::stdout_logger_st(const std::string& logger_name, bool color) { - return details::registry::instance().create(logger_name, spdlog::sinks::stderr_sink_st::instance()); + return create_console_logger(logger_name, sinks::stdout_sink_st::instance(), color); } -#if defined(__linux__) || defined(__APPLE__) +inline std::shared_ptr spdlog::stderr_logger_mt(const std::string& logger_name, bool color) +{ + return create_console_logger(logger_name, sinks::stderr_sink_mt::instance(), color); +} + +inline std::shared_ptr spdlog::stderr_logger_st(const std::string& logger_name, bool color) +{ + return create_console_logger(logger_name, sinks::stderr_sink_st::instance(), color); +} + +#ifdef SPDLOG_ENABLE_SYSLOG // Create syslog logger inline std::shared_ptr spdlog::syslog_logger(const std::string& logger_name, const std::string& syslog_ident, int syslog_option) { @@ -79,6 +105,18 @@ inline std::shared_ptr spdlog::syslog_logger(const std::string& } #endif +#if defined(__ANDROID__) +inline std::shared_ptr spdlog::android_logger(const std::string& logger_name, const std::string& tag) +{ + return create(logger_name, tag); +} +#endif + +// Create and register a logger a single sink +inline std::shared_ptr spdlog::create(const std::string& logger_name, const spdlog::sink_ptr& sink) +{ + return details::registry::instance().create(logger_name, sink); +} //Create logger with multiple sinks @@ -117,10 +155,15 @@ inline void spdlog::set_level(level::level_enum log_level) return details::registry::instance().set_level(log_level); } - -inline void spdlog::set_async_mode(size_t queue_size, const async_overflow_policy overflow_policy, const std::function& worker_warmup_cb, const std::chrono::milliseconds& flush_interval_ms) +inline void spdlog::set_error_handler(log_err_handler handler) { - details::registry::instance().set_async_mode(queue_size, overflow_policy, worker_warmup_cb, flush_interval_ms); + return details::registry::instance().set_error_handler(handler); +} + + +inline void spdlog::set_async_mode(size_t queue_size, const async_overflow_policy overflow_policy, const std::function& worker_warmup_cb, const std::chrono::milliseconds& flush_interval_ms, const std::function& worker_teardown_cb) +{ + details::registry::instance().set_async_mode(queue_size, overflow_policy, worker_warmup_cb, flush_interval_ms, worker_teardown_cb); } inline void spdlog::set_sync_mode() @@ -128,8 +171,12 @@ inline void spdlog::set_sync_mode() details::registry::instance().set_sync_mode(); } +inline void spdlog::apply_all(std::function)> fun) +{ + details::registry::instance().apply_all(fun); +} + inline void spdlog::drop_all() { details::registry::instance().drop_all(); } - diff --git a/vendor/spdlog/spdlog/fmt/bundled/format.cc b/vendor/spdlog/spdlog/fmt/bundled/format.cc new file mode 100644 index 00000000..0f7e0aa2 --- /dev/null +++ b/vendor/spdlog/spdlog/fmt/bundled/format.cc @@ -0,0 +1,557 @@ +/* + Formatting library for C++ + + Copyright (c) 2012 - 2016, Victor Zverovich + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +// Commented out by spdlog to use header only +// #include "fmt/format.h" +// #include "fmt/printf.h" + +#include + +#include +#include +#include +#include +#include +#include // for std::ptrdiff_t + +#if defined(_WIN32) && defined(__MINGW32__) +# include +#endif + +#if FMT_USE_WINDOWS_H +# if defined(NOMINMAX) || defined(FMT_WIN_MINMAX) +# include +# else +# define NOMINMAX +# include +# undef NOMINMAX +# endif +#endif + +using fmt::internal::Arg; + +#if FMT_EXCEPTIONS +# define FMT_TRY try +# define FMT_CATCH(x) catch (x) +#else +# define FMT_TRY if (true) +# define FMT_CATCH(x) if (false) +#endif + +#ifdef _MSC_VER +# pragma warning(push) +# pragma warning(disable: 4127) // conditional expression is constant +# pragma warning(disable: 4702) // unreachable code +// Disable deprecation warning for strerror. The latter is not called but +// MSVC fails to detect it. +# pragma warning(disable: 4996) +#endif + +// Dummy implementations of strerror_r and strerror_s called if corresponding +// system functions are not available. +static inline fmt::internal::Null<> strerror_r(int, char *, ...) { + return fmt::internal::Null<>(); +} +static inline fmt::internal::Null<> strerror_s(char *, std::size_t, ...) { + return fmt::internal::Null<>(); +} + +namespace fmt { + +FMT_FUNC internal::RuntimeError::~RuntimeError() throw() {} +FMT_FUNC FormatError::~FormatError() throw() {} +FMT_FUNC SystemError::~SystemError() throw() {} + +namespace { + +#ifndef _MSC_VER +# define FMT_SNPRINTF snprintf +#else // _MSC_VER +inline int fmt_snprintf(char *buffer, size_t size, const char *format, ...) { + va_list args; + va_start(args, format); + int result = vsnprintf_s(buffer, size, _TRUNCATE, format, args); + va_end(args); + return result; +} +# define FMT_SNPRINTF fmt_snprintf +#endif // _MSC_VER + +#if defined(_WIN32) && defined(__MINGW32__) && !defined(__NO_ISOCEXT) +# define FMT_SWPRINTF snwprintf +#else +# define FMT_SWPRINTF swprintf +#endif // defined(_WIN32) && defined(__MINGW32__) && !defined(__NO_ISOCEXT) + +const char RESET_COLOR[] = "\x1b[0m"; + +typedef void (*FormatFunc)(Writer &, int, StringRef); + +// Portable thread-safe version of strerror. +// Sets buffer to point to a string describing the error code. +// This can be either a pointer to a string stored in buffer, +// or a pointer to some static immutable string. +// Returns one of the following values: +// 0 - success +// ERANGE - buffer is not large enough to store the error message +// other - failure +// Buffer should be at least of size 1. +int safe_strerror( + int error_code, char *&buffer, std::size_t buffer_size) FMT_NOEXCEPT { + FMT_ASSERT(buffer != 0 && buffer_size != 0, "invalid buffer"); + + class StrError { + private: + int error_code_; + char *&buffer_; + std::size_t buffer_size_; + + // A noop assignment operator to avoid bogus warnings. + void operator=(const StrError &) {} + + // Handle the result of XSI-compliant version of strerror_r. + int handle(int result) { + // glibc versions before 2.13 return result in errno. + return result == -1 ? errno : result; + } + + // Handle the result of GNU-specific version of strerror_r. + int handle(char *message) { + // If the buffer is full then the message is probably truncated. + if (message == buffer_ && strlen(buffer_) == buffer_size_ - 1) + return ERANGE; + buffer_ = message; + return 0; + } + + // Handle the case when strerror_r is not available. + int handle(internal::Null<>) { + return fallback(strerror_s(buffer_, buffer_size_, error_code_)); + } + + // Fallback to strerror_s when strerror_r is not available. + int fallback(int result) { + // If the buffer is full then the message is probably truncated. + return result == 0 && strlen(buffer_) == buffer_size_ - 1 ? + ERANGE : result; + } + + // Fallback to strerror if strerror_r and strerror_s are not available. + int fallback(internal::Null<>) { + errno = 0; + buffer_ = strerror(error_code_); + return errno; + } + + public: + StrError(int err_code, char *&buf, std::size_t buf_size) + : error_code_(err_code), buffer_(buf), buffer_size_(buf_size) {} + + int run() { + strerror_r(0, 0, ""); // Suppress a warning about unused strerror_r. + return handle(strerror_r(error_code_, buffer_, buffer_size_)); + } + }; + return StrError(error_code, buffer, buffer_size).run(); +} + +void format_error_code(Writer &out, int error_code, + StringRef message) FMT_NOEXCEPT { + // Report error code making sure that the output fits into + // INLINE_BUFFER_SIZE to avoid dynamic memory allocation and potential + // bad_alloc. + out.clear(); + static const char SEP[] = ": "; + static const char ERROR_STR[] = "error "; + // Subtract 2 to account for terminating null characters in SEP and ERROR_STR. + std::size_t error_code_size = sizeof(SEP) + sizeof(ERROR_STR) - 2; + typedef internal::IntTraits::MainType MainType; + MainType abs_value = static_cast(error_code); + if (internal::is_negative(error_code)) { + abs_value = 0 - abs_value; + ++error_code_size; + } + error_code_size += internal::count_digits(abs_value); + if (message.size() <= internal::INLINE_BUFFER_SIZE - error_code_size) + out << message << SEP; + out << ERROR_STR << error_code; + assert(out.size() <= internal::INLINE_BUFFER_SIZE); +} + +void report_error(FormatFunc func, int error_code, + StringRef message) FMT_NOEXCEPT { + MemoryWriter full_message; + func(full_message, error_code, message); + // Use Writer::data instead of Writer::c_str to avoid potential memory + // allocation. + std::fwrite(full_message.data(), full_message.size(), 1, stderr); + std::fputc('\n', stderr); +} +} // namespace + +namespace internal { + +// This method is used to preserve binary compatibility with fmt 3.0. +// It can be removed in 4.0. +FMT_FUNC void format_system_error( + Writer &out, int error_code, StringRef message) FMT_NOEXCEPT { + fmt::format_system_error(out, error_code, message); +} +} // namespace internal + +FMT_FUNC void SystemError::init( + int err_code, CStringRef format_str, ArgList args) { + error_code_ = err_code; + MemoryWriter w; + format_system_error(w, err_code, format(format_str, args)); + std::runtime_error &base = *this; + base = std::runtime_error(w.str()); +} + +template +int internal::CharTraits::format_float( + char *buffer, std::size_t size, const char *format, + unsigned width, int precision, T value) { + if (width == 0) { + return precision < 0 ? + FMT_SNPRINTF(buffer, size, format, value) : + FMT_SNPRINTF(buffer, size, format, precision, value); + } + return precision < 0 ? + FMT_SNPRINTF(buffer, size, format, width, value) : + FMT_SNPRINTF(buffer, size, format, width, precision, value); +} + +template +int internal::CharTraits::format_float( + wchar_t *buffer, std::size_t size, const wchar_t *format, + unsigned width, int precision, T value) { + if (width == 0) { + return precision < 0 ? + FMT_SWPRINTF(buffer, size, format, value) : + FMT_SWPRINTF(buffer, size, format, precision, value); + } + return precision < 0 ? + FMT_SWPRINTF(buffer, size, format, width, value) : + FMT_SWPRINTF(buffer, size, format, width, precision, value); +} + +template +const char internal::BasicData::DIGITS[] = + "0001020304050607080910111213141516171819" + "2021222324252627282930313233343536373839" + "4041424344454647484950515253545556575859" + "6061626364656667686970717273747576777879" + "8081828384858687888990919293949596979899"; + +#define FMT_POWERS_OF_10(factor) \ + factor * 10, \ + factor * 100, \ + factor * 1000, \ + factor * 10000, \ + factor * 100000, \ + factor * 1000000, \ + factor * 10000000, \ + factor * 100000000, \ + factor * 1000000000 + +template +const uint32_t internal::BasicData::POWERS_OF_10_32[] = { + 0, FMT_POWERS_OF_10(1) +}; + +template +const uint64_t internal::BasicData::POWERS_OF_10_64[] = { + 0, + FMT_POWERS_OF_10(1), + FMT_POWERS_OF_10(ULongLong(1000000000)), + // Multiply several constants instead of using a single long long constant + // to avoid warnings about C++98 not supporting long long. + ULongLong(1000000000) * ULongLong(1000000000) * 10 +}; + +FMT_FUNC void internal::report_unknown_type(char code, const char *type) { + (void)type; + if (std::isprint(static_cast(code))) { + FMT_THROW(FormatError( + format("unknown format code '{}' for {}", code, type))); + } + FMT_THROW(FormatError( + format("unknown format code '\\x{:02x}' for {}", + static_cast(code), type))); +} + +#if FMT_USE_WINDOWS_H + +FMT_FUNC internal::UTF8ToUTF16::UTF8ToUTF16(StringRef s) { + static const char ERROR_MSG[] = "cannot convert string from UTF-8 to UTF-16"; + if (s.size() > INT_MAX) + FMT_THROW(WindowsError(ERROR_INVALID_PARAMETER, ERROR_MSG)); + int s_size = static_cast(s.size()); + int length = MultiByteToWideChar( + CP_UTF8, MB_ERR_INVALID_CHARS, s.data(), s_size, 0, 0); + if (length == 0) + FMT_THROW(WindowsError(GetLastError(), ERROR_MSG)); + buffer_.resize(length + 1); + length = MultiByteToWideChar( + CP_UTF8, MB_ERR_INVALID_CHARS, s.data(), s_size, &buffer_[0], length); + if (length == 0) + FMT_THROW(WindowsError(GetLastError(), ERROR_MSG)); + buffer_[length] = 0; +} + +FMT_FUNC internal::UTF16ToUTF8::UTF16ToUTF8(WStringRef s) { + if (int error_code = convert(s)) { + FMT_THROW(WindowsError(error_code, + "cannot convert string from UTF-16 to UTF-8")); + } +} + +FMT_FUNC int internal::UTF16ToUTF8::convert(WStringRef s) { + if (s.size() > INT_MAX) + return ERROR_INVALID_PARAMETER; + int s_size = static_cast(s.size()); + int length = WideCharToMultiByte(CP_UTF8, 0, s.data(), s_size, 0, 0, 0, 0); + if (length == 0) + return GetLastError(); + buffer_.resize(length + 1); + length = WideCharToMultiByte( + CP_UTF8, 0, s.data(), s_size, &buffer_[0], length, 0, 0); + if (length == 0) + return GetLastError(); + buffer_[length] = 0; + return 0; +} + +FMT_FUNC void WindowsError::init( + int err_code, CStringRef format_str, ArgList args) { + error_code_ = err_code; + MemoryWriter w; + internal::format_windows_error(w, err_code, format(format_str, args)); + std::runtime_error &base = *this; + base = std::runtime_error(w.str()); +} + +FMT_FUNC void internal::format_windows_error( + Writer &out, int error_code, StringRef message) FMT_NOEXCEPT { + FMT_TRY { + MemoryBuffer buffer; + buffer.resize(INLINE_BUFFER_SIZE); + for (;;) { + wchar_t *system_message = &buffer[0]; + int result = FormatMessageW(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, + 0, error_code, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + system_message, static_cast(buffer.size()), 0); + if (result != 0) { + UTF16ToUTF8 utf8_message; + if (utf8_message.convert(system_message) == ERROR_SUCCESS) { + out << message << ": " << utf8_message; + return; + } + break; + } + if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) + break; // Can't get error message, report error code instead. + buffer.resize(buffer.size() * 2); + } + } FMT_CATCH(...) {} + fmt::format_error_code(out, error_code, message); // 'fmt::' is for bcc32. +} + +#endif // FMT_USE_WINDOWS_H + +FMT_FUNC void format_system_error( + Writer &out, int error_code, StringRef message) FMT_NOEXCEPT { + FMT_TRY { + internal::MemoryBuffer buffer; + buffer.resize(internal::INLINE_BUFFER_SIZE); + for (;;) { + char *system_message = &buffer[0]; + int result = safe_strerror(error_code, system_message, buffer.size()); + if (result == 0) { + out << message << ": " << system_message; + return; + } + if (result != ERANGE) + break; // Can't get error message, report error code instead. + buffer.resize(buffer.size() * 2); + } + } FMT_CATCH(...) {} + fmt::format_error_code(out, error_code, message); // 'fmt::' is for bcc32. +} + +template +void internal::ArgMap::init(const ArgList &args) { + if (!map_.empty()) + return; + typedef internal::NamedArg NamedArg; + const NamedArg *named_arg = 0; + bool use_values = + args.type(ArgList::MAX_PACKED_ARGS - 1) == internal::Arg::NONE; + if (use_values) { + for (unsigned i = 0;/*nothing*/; ++i) { + internal::Arg::Type arg_type = args.type(i); + switch (arg_type) { + case internal::Arg::NONE: + return; + case internal::Arg::NAMED_ARG: + named_arg = static_cast(args.values_[i].pointer); + map_.push_back(Pair(named_arg->name, *named_arg)); + break; + default: + /*nothing*/; + } + } + return; + } + for (unsigned i = 0; i != ArgList::MAX_PACKED_ARGS; ++i) { + internal::Arg::Type arg_type = args.type(i); + if (arg_type == internal::Arg::NAMED_ARG) { + named_arg = static_cast(args.args_[i].pointer); + map_.push_back(Pair(named_arg->name, *named_arg)); + } + } + for (unsigned i = ArgList::MAX_PACKED_ARGS;/*nothing*/; ++i) { + switch (args.args_[i].type) { + case internal::Arg::NONE: + return; + case internal::Arg::NAMED_ARG: + named_arg = static_cast(args.args_[i].pointer); + map_.push_back(Pair(named_arg->name, *named_arg)); + break; + default: + /*nothing*/; + } + } +} + +template +void internal::FixedBuffer::grow(std::size_t) { + FMT_THROW(std::runtime_error("buffer overflow")); +} + +FMT_FUNC Arg internal::FormatterBase::do_get_arg( + unsigned arg_index, const char *&error) { + Arg arg = args_[arg_index]; + switch (arg.type) { + case Arg::NONE: + error = "argument index out of range"; + break; + case Arg::NAMED_ARG: + arg = *static_cast(arg.pointer); + break; + default: + /*nothing*/; + } + return arg; +} + +FMT_FUNC void report_system_error( + int error_code, fmt::StringRef message) FMT_NOEXCEPT { + // 'fmt::' is for bcc32. + report_error(format_system_error, error_code, message); +} + +#if FMT_USE_WINDOWS_H +FMT_FUNC void report_windows_error( + int error_code, fmt::StringRef message) FMT_NOEXCEPT { + // 'fmt::' is for bcc32. + report_error(internal::format_windows_error, error_code, message); +} +#endif + +FMT_FUNC void print(std::FILE *f, CStringRef format_str, ArgList args) { + MemoryWriter w; + w.write(format_str, args); + std::fwrite(w.data(), 1, w.size(), f); +} + +FMT_FUNC void print(CStringRef format_str, ArgList args) { + print(stdout, format_str, args); +} + +FMT_FUNC void print_colored(Color c, CStringRef format, ArgList args) { + char escape[] = "\x1b[30m"; + escape[3] = static_cast('0' + c); + std::fputs(escape, stdout); + print(format, args); + std::fputs(RESET_COLOR, stdout); +} + +template +void printf(BasicWriter &w, BasicCStringRef format, ArgList args); + +FMT_FUNC int fprintf(std::FILE *f, CStringRef format, ArgList args) { + MemoryWriter w; + printf(w, format, args); + std::size_t size = w.size(); + return std::fwrite(w.data(), 1, size, f) < size ? -1 : static_cast(size); +} + +#ifndef FMT_HEADER_ONLY + +template struct internal::BasicData; + +// Explicit instantiations for char. + +template void internal::FixedBuffer::grow(std::size_t); + +template void internal::ArgMap::init(const ArgList &args); + +template void PrintfFormatter::format(CStringRef format); + +template int internal::CharTraits::format_float( + char *buffer, std::size_t size, const char *format, + unsigned width, int precision, double value); + +template int internal::CharTraits::format_float( + char *buffer, std::size_t size, const char *format, + unsigned width, int precision, long double value); + +// Explicit instantiations for wchar_t. + +template void internal::FixedBuffer::grow(std::size_t); + +template void internal::ArgMap::init(const ArgList &args); + +template void PrintfFormatter::format(WCStringRef format); + +template int internal::CharTraits::format_float( + wchar_t *buffer, std::size_t size, const wchar_t *format, + unsigned width, int precision, double value); + +template int internal::CharTraits::format_float( + wchar_t *buffer, std::size_t size, const wchar_t *format, + unsigned width, int precision, long double value); + +#endif // FMT_HEADER_ONLY + +} // namespace fmt + +#ifdef _MSC_VER +# pragma warning(pop) +#endif diff --git a/vendor/spdlog/spdlog/details/format.h b/vendor/spdlog/spdlog/fmt/bundled/format.h similarity index 74% rename from vendor/spdlog/spdlog/details/format.h rename to vendor/spdlog/spdlog/fmt/bundled/format.h index 1245b9d7..294a686f 100644 --- a/vendor/spdlog/spdlog/details/format.h +++ b/vendor/spdlog/spdlog/fmt/bundled/format.h @@ -1,44 +1,35 @@ /* -Formatting library for C++ + Formatting library for C++ -Copyright (c) 2012 - 2015, Victor Zverovich -All rights reserved. + Copyright (c) 2012 - 2016, Victor Zverovich + All rights reserved. -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are met: + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: -1. Redistributions of source code must retain the above copyright notice, this -list of conditions and the following disclaimer. -2. Redistributions in binary form must reproduce the above copyright notice, -this list of conditions and the following disclaimer in the documentation -and/or other materials provided with the distribution. + 1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR -ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -*/ + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ #ifndef FMT_FORMAT_H_ #define FMT_FORMAT_H_ -#define FMT_HEADER_ONLY //Added by spdlog for header only usage - -#if defined _MSC_VER && _MSC_VER <= 1500 -typedef unsigned int uint32_t; -typedef unsigned long long uint64_t; -typedef long long intmax_t; -#else -#include -#endif - #include +#include #include #include #include @@ -46,15 +37,8 @@ typedef long long intmax_t; #include #include #include -#include - -#ifndef FMT_USE_IOSTREAMS -# define FMT_USE_IOSTREAMS 1 -#endif - -#if FMT_USE_IOSTREAMS -# include -#endif +#include +#include #ifdef _SECURE_SCL # define FMT_SECURE_SCL _SECURE_SCL @@ -66,6 +50,20 @@ typedef long long intmax_t; # include #endif +#ifdef _MSC_VER +# define FMT_MSC_VER _MSC_VER +#else +# define FMT_MSC_VER 0 +#endif + +#if FMT_MSC_VER && FMT_MSC_VER <= 1500 +typedef unsigned __int32 uint32_t; +typedef unsigned __int64 uint64_t; +typedef __int64 intmax_t; +#else +#include +#endif + #if !defined(FMT_HEADER_ONLY) && defined(_WIN32) # ifdef FMT_EXPORT # define FMT_API __declspec(dllexport) @@ -77,46 +75,6 @@ typedef long long intmax_t; # define FMT_API #endif -#ifdef _MSC_VER -# include // _BitScanReverse, _BitScanReverse64 - -namespace fmt -{ -namespace internal -{ -# pragma intrinsic(_BitScanReverse) -inline uint32_t clz(uint32_t x) -{ - unsigned long r = 0; - _BitScanReverse(&r, x); - return 31 - r; -} -# define FMT_BUILTIN_CLZ(n) fmt::internal::clz(n) - -# ifdef _WIN64 -# pragma intrinsic(_BitScanReverse64) -# endif - -inline uint32_t clzll(uint64_t x) -{ - unsigned long r = 0; -# ifdef _WIN64 - _BitScanReverse64(&r, x); -# else - // Scan the high 32 bits. - if (_BitScanReverse(&r, static_cast(x >> 32))) - return 63 - (r + 32); - - // Scan the low 32 bits. - _BitScanReverse(&r, static_cast(x)); -# endif - return 63 - r; -} -# define FMT_BUILTIN_CLZLL(n) fmt::internal::clzll(n) -} -} -#endif - #ifdef __GNUC__ # define FMT_GCC_VERSION (__GNUC__ * 100 + __GNUC_MINOR__) # define FMT_GCC_EXTENSION __extension__ @@ -139,9 +97,16 @@ inline uint32_t clzll(uint64_t x) # define FMT_GCC_EXTENSION #endif -#if defined(__clang__) && !defined(__INTEL_COMPILER) +#if defined(__INTEL_COMPILER) +# define FMT_ICC_VERSION __INTEL_COMPILER +#elif defined(__ICL) +# define FMT_ICC_VERSION __ICL +#endif + +#if defined(__clang__) && !defined(FMT_ICC_VERSION) # pragma clang diagnostic push -# pragma clang diagnostic ignored "-Wdocumentation" +# pragma clang diagnostic ignored "-Wdocumentation-unknown-command" +# pragma clang diagnostic ignored "-Wpadded" #endif #ifdef __GNUC_LIBSTD__ @@ -172,7 +137,7 @@ inline uint32_t clzll(uint64_t x) // since version 2013. # define FMT_USE_VARIADIC_TEMPLATES \ (FMT_HAS_FEATURE(cxx_variadic_templates) || \ - (FMT_GCC_VERSION >= 404 && FMT_HAS_GXX_CXX11) || _MSC_VER >= 1800) + (FMT_GCC_VERSION >= 404 && FMT_HAS_GXX_CXX11) || FMT_MSC_VER >= 1800) #endif #ifndef FMT_USE_RVALUE_REFERENCES @@ -183,7 +148,7 @@ inline uint32_t clzll(uint64_t x) # else # define FMT_USE_RVALUE_REFERENCES \ (FMT_HAS_FEATURE(cxx_rvalue_references) || \ - (FMT_GCC_VERSION >= 403 && FMT_HAS_GXX_CXX11) || _MSC_VER >= 1600) + (FMT_GCC_VERSION >= 403 && FMT_HAS_GXX_CXX11) || FMT_MSC_VER >= 1600) # endif #endif @@ -191,26 +156,11 @@ inline uint32_t clzll(uint64_t x) # include // for std::move #endif -// Define FMT_USE_NOEXCEPT to make C++ Format use noexcept (C++11 feature). -#ifndef FMT_USE_NOEXCEPT -# define FMT_USE_NOEXCEPT 0 -#endif - -#ifndef FMT_NOEXCEPT -# if FMT_USE_NOEXCEPT || FMT_HAS_FEATURE(cxx_noexcept) || \ - (FMT_GCC_VERSION >= 408 && FMT_HAS_GXX_CXX11) || \ - _MSC_VER >= 1900 -# define FMT_NOEXCEPT noexcept -# else -# define FMT_NOEXCEPT throw() -# endif -#endif - // Check if exceptions are disabled. #if defined(__GNUC__) && !defined(__EXCEPTIONS) # define FMT_EXCEPTIONS 0 #endif -#if defined(_MSC_VER) && !_HAS_EXCEPTIONS +#if FMT_MSC_VER && !_HAS_EXCEPTIONS # define FMT_EXCEPTIONS 0 #endif #ifndef FMT_EXCEPTIONS @@ -225,6 +175,25 @@ inline uint32_t clzll(uint64_t x) # endif #endif +// Define FMT_USE_NOEXCEPT to make fmt use noexcept (C++11 feature). +#ifndef FMT_USE_NOEXCEPT +# define FMT_USE_NOEXCEPT 0 +#endif + +#ifndef FMT_NOEXCEPT +# if FMT_EXCEPTIONS +# if FMT_USE_NOEXCEPT || FMT_HAS_FEATURE(cxx_noexcept) || \ + (FMT_GCC_VERSION >= 408 && FMT_HAS_GXX_CXX11) || \ + FMT_MSC_VER >= 1900 +# define FMT_NOEXCEPT noexcept +# else +# define FMT_NOEXCEPT throw() +# endif +# else +# define FMT_NOEXCEPT +# endif +#endif + // A macro to disallow the copy constructor and operator= functions // This should be used in the private: declarations for a class #ifndef FMT_USE_DELETED_FUNCTIONS @@ -232,7 +201,7 @@ inline uint32_t clzll(uint64_t x) #endif #if FMT_USE_DELETED_FUNCTIONS || FMT_HAS_FEATURE(cxx_deleted_functions) || \ - (FMT_GCC_VERSION >= 404 && FMT_HAS_GXX_CXX11) || _MSC_VER >= 1800 + (FMT_GCC_VERSION >= 404 && FMT_HAS_GXX_CXX11) || FMT_MSC_VER >= 1800 # define FMT_DELETED_OR_UNDEFINED = delete # define FMT_DISALLOW_COPY_AND_ASSIGN(TypeName) \ TypeName(const TypeName&) = delete; \ @@ -248,16 +217,82 @@ inline uint32_t clzll(uint64_t x) // All compilers which support UDLs also support variadic templates. This // makes the fmt::literals implementation easier. However, an explicit check // for variadic templates is added here just in case. +// For Intel's compiler both it and the system gcc/msc must support UDLs. # define FMT_USE_USER_DEFINED_LITERALS \ FMT_USE_VARIADIC_TEMPLATES && FMT_USE_RVALUE_REFERENCES && \ (FMT_HAS_FEATURE(cxx_user_literals) || \ - (FMT_GCC_VERSION >= 407 && FMT_HAS_GXX_CXX11) || _MSC_VER >= 1900) + (FMT_GCC_VERSION >= 407 && FMT_HAS_GXX_CXX11) || FMT_MSC_VER >= 1900) && \ + (!defined(FMT_ICC_VERSION) || FMT_ICC_VERSION >= 1500) #endif #ifndef FMT_ASSERT # define FMT_ASSERT(condition, message) assert((condition) && message) #endif +#if FMT_GCC_VERSION >= 400 || FMT_HAS_BUILTIN(__builtin_clz) +# define FMT_BUILTIN_CLZ(n) __builtin_clz(n) +#endif + +#if FMT_GCC_VERSION >= 400 || FMT_HAS_BUILTIN(__builtin_clzll) +# define FMT_BUILTIN_CLZLL(n) __builtin_clzll(n) +#endif + +// Some compilers masquerade as both MSVC and GCC-likes or +// otherwise support __builtin_clz and __builtin_clzll, so +// only define FMT_BUILTIN_CLZ using the MSVC intrinsics +// if the clz and clzll builtins are not available. +#if FMT_MSC_VER && !defined(FMT_BUILTIN_CLZLL) +# include // _BitScanReverse, _BitScanReverse64 + +namespace fmt +{ +namespace internal +{ +# pragma intrinsic(_BitScanReverse) +inline uint32_t clz(uint32_t x) +{ + unsigned long r = 0; + _BitScanReverse(&r, x); + + assert(x != 0); + // Static analysis complains about using uninitialized data + // "r", but the only way that can happen is if "x" is 0, + // which the callers guarantee to not happen. +# pragma warning(suppress: 6102) + return 31 - r; +} +# define FMT_BUILTIN_CLZ(n) fmt::internal::clz(n) + +# ifdef _WIN64 +# pragma intrinsic(_BitScanReverse64) +# endif + +inline uint32_t clzll(uint64_t x) +{ + unsigned long r = 0; +# ifdef _WIN64 + _BitScanReverse64(&r, x); +# else + // Scan the high 32 bits. + if (_BitScanReverse(&r, static_cast(x >> 32))) + return 63 - (r + 32); + + // Scan the low 32 bits. + _BitScanReverse(&r, static_cast(x)); +# endif + + assert(x != 0); + // Static analysis complains about using uninitialized data + // "r", but the only way that can happen is if "x" is 0, + // which the callers guarantee to not happen. +# pragma warning(suppress: 6102) + return 63 - r; +} +# define FMT_BUILTIN_CLZLL(n) fmt::internal::clzll(n) +} +} +#endif + namespace fmt { namespace internal @@ -302,7 +337,7 @@ inline DummyInt _isnan(...) // A helper function to suppress bogus "conditional expression is constant" // warnings. template -inline T check(T value) +inline T const_check(T value) { return value; } @@ -327,8 +362,8 @@ public: using namespace fmt::internal; // The resolution "priority" is: // isinf macro > std::isinf > ::isinf > fmt::internal::isinf - if (check(sizeof(isinf(x)) == sizeof(bool) || - sizeof(isinf(x)) == sizeof(int))) + if (const_check(sizeof(isinf(x)) == sizeof(bool) || + sizeof(isinf(x)) == sizeof(int))) { return isinf(x) != 0; } @@ -340,8 +375,8 @@ public: static bool isnotanumber(T x) { using namespace fmt::internal; - if (check(sizeof(isnan(x)) == sizeof(bool) || - sizeof(isnan(x)) == sizeof(int))) + if (const_check(sizeof(isnan(x)) == sizeof(bool) || + sizeof(isnan(x)) == sizeof(int))) { return isnan(x) != 0; } @@ -352,7 +387,7 @@ public: static bool isnegative(double x) { using namespace fmt::internal; - if (check(sizeof(signbit(x)) == sizeof(int))) + if (const_check(sizeof(signbit(x)) == sizeof(int))) return signbit(x) != 0; if (x < 0) return true; if (!isnotanumber(x)) return false; @@ -383,35 +418,39 @@ typedef BasicWriter Writer; typedef BasicWriter WWriter; template +class ArgFormatter; + +template +class BasicPrintfArgFormatter; + +template > class BasicFormatter; -template -void format(BasicFormatter &f, const Char *&format_str, const T &value); - /** -\rst -A string reference. It can be constructed from a C string or ``std::string``. + \rst + A string reference. It can be constructed from a C string or ``std::string``. -You can use one of the following typedefs for common character types: + You can use one of the following typedefs for common character types: -+------------+-------------------------+ -| Type | Definition | -+============+=========================+ -| StringRef | BasicStringRef | -+------------+-------------------------+ -| WStringRef | BasicStringRef | -+------------+-------------------------+ + +------------+-------------------------+ + | Type | Definition | + +============+=========================+ + | StringRef | BasicStringRef | + +------------+-------------------------+ + | WStringRef | BasicStringRef | + +------------+-------------------------+ -This class is most useful as a parameter type to allow passing -different types of strings to a function, for example:: + This class is most useful as a parameter type to allow passing + different types of strings to a function, for example:: -template -std::string format(StringRef format_str, const Args & ... args); + template + std::string format(StringRef format_str, const Args & ... args); -format("{}", 42); -format(std::string("{}"), 42); -\endrst -*/ + format("{}", 42); + format(std::string("{}"), 42); + \endrst + */ template class BasicStringRef { @@ -424,33 +463,33 @@ public: BasicStringRef(const Char *s, std::size_t size) : data_(s), size_(size) {} /** - \rst - Constructs a string reference object from a C string computing - the size with ``std::char_traits::length``. - \endrst - */ + \rst + Constructs a string reference object from a C string computing + the size with ``std::char_traits::length``. + \endrst + */ BasicStringRef(const Char *s) : data_(s), size_(std::char_traits::length(s)) {} /** - \rst - Constructs a string reference from an ``std::string`` object. - \endrst - */ + \rst + Constructs a string reference from an ``std::string`` object. + \endrst + */ BasicStringRef(const std::basic_string &s) : data_(s.c_str()), size_(s.size()) {} /** - \rst - Converts a string reference to an ``std::string`` object. - \endrst - */ + \rst + Converts a string reference to an ``std::string`` object. + \endrst + */ std::basic_string to_string() const { return std::basic_string(data_, size_); } - /** Returns the pointer to a C string. */ + /** Returns a pointer to the string data. */ const Char *data() const { return data_; @@ -502,30 +541,30 @@ typedef BasicStringRef StringRef; typedef BasicStringRef WStringRef; /** -\rst -A reference to a null terminated string. It can be constructed from a C -string or ``std::string``. + \rst + A reference to a null terminated string. It can be constructed from a C + string or ``std::string``. -You can use one of the following typedefs for common character types: + You can use one of the following typedefs for common character types: -+-------------+--------------------------+ -| Type | Definition | -+=============+==========================+ -| CStringRef | BasicCStringRef | -+-------------+--------------------------+ -| WCStringRef | BasicCStringRef | -+-------------+--------------------------+ + +-------------+--------------------------+ + | Type | Definition | + +=============+==========================+ + | CStringRef | BasicCStringRef | + +-------------+--------------------------+ + | WCStringRef | BasicCStringRef | + +-------------+--------------------------+ -This class is most useful as a parameter type to allow passing -different types of strings to a function, for example:: + This class is most useful as a parameter type to allow passing + different types of strings to a function, for example:: -template -std::string format(CStringRef format_str, const Args & ... args); + template + std::string format(CStringRef format_str, const Args & ... args); -format("{}", 42); -format(std::string("{}"), 42); -\endrst -*/ + format("{}", 42); + format(std::string("{}"), 42); + \endrst + */ template class BasicCStringRef { @@ -537,10 +576,10 @@ public: BasicCStringRef(const Char *s) : data_(s) {} /** - \rst - Constructs a string reference from an ``std::string`` object. - \endrst - */ + \rst + Constructs a string reference from an ``std::string`` object. + \endrst + */ BasicCStringRef(const std::basic_string &s) : data_(s.c_str()) {} /** Returns the pointer to a C string. */ @@ -553,18 +592,44 @@ public: typedef BasicCStringRef CStringRef; typedef BasicCStringRef WCStringRef; -/** -A formatting error such as invalid format string. -*/ +/** A formatting error such as invalid format string. */ class FormatError : public std::runtime_error { public: explicit FormatError(CStringRef message) : std::runtime_error(message.c_str()) {} + ~FormatError() throw(); }; namespace internal { + +// MakeUnsigned::Type gives an unsigned type corresponding to integer type T. +template +struct MakeUnsigned +{ + typedef T Type; +}; + +#define FMT_SPECIALIZE_MAKE_UNSIGNED(T, U) \ + template <> \ + struct MakeUnsigned { typedef U Type; } + +FMT_SPECIALIZE_MAKE_UNSIGNED(char, unsigned char); +FMT_SPECIALIZE_MAKE_UNSIGNED(signed char, unsigned char); +FMT_SPECIALIZE_MAKE_UNSIGNED(short, unsigned short); +FMT_SPECIALIZE_MAKE_UNSIGNED(int, unsigned); +FMT_SPECIALIZE_MAKE_UNSIGNED(long, unsigned long); +FMT_SPECIALIZE_MAKE_UNSIGNED(LongLong, ULongLong); + +// Casts nonnegative integer to unsigned. +template +inline typename MakeUnsigned::Type to_unsigned(Int value) +{ + FMT_ASSERT(value >= 0, "negative value"); + return static_cast::Type>(value); +} + // The number of characters to store in the MemoryBuffer object itself // to avoid dynamic memory allocation. enum { INLINE_BUFFER_SIZE = 500 }; @@ -586,10 +651,10 @@ inline T *make_ptr(T *ptr, std::size_t) } // namespace internal /** -\rst -A buffer supporting a subset of ``std::vector``'s operations. -\endrst -*/ + \rst + A buffer supporting a subset of ``std::vector``'s operations. + \endrst + */ template class Buffer { @@ -605,11 +670,11 @@ protected: : ptr_(ptr), size_(0), capacity_(capacity) {} /** - \rst - Increases the buffer capacity to hold at least *size* elements updating - ``ptr_`` and ``capacity_``. - \endrst - */ + \rst + Increases the buffer capacity to hold at least *size* elements updating + ``ptr_`` and ``capacity_``. + \endrst + */ virtual void grow(std::size_t size) = 0; public: @@ -628,8 +693,8 @@ public: } /** - Resizes the buffer. If T is a POD type new elements may not be initialized. - */ + Resizes the buffer. If T is a POD type new elements may not be initialized. + */ void resize(std::size_t new_size) { if (new_size > capacity_) @@ -638,10 +703,10 @@ public: } /** - \rst - Reserves space to store at least *capacity* elements. - \endrst - */ + \rst + Reserves space to store at least *capacity* elements. + \endrst + */ void reserve(std::size_t capacity) { if (capacity > capacity_) @@ -675,8 +740,7 @@ template template void Buffer::append(const U *begin, const U *end) { - assert(begin <= end); - std::size_t new_size = size_ + (end - begin); + std::size_t new_size = size_ + internal::to_unsigned(end - begin); if (new_size > capacity_) grow(new_size); std::uninitialized_copy(begin, end, @@ -687,8 +751,8 @@ void Buffer::append(const U *begin, const U *end) namespace internal { -// A memory buffer for POD types with the first SIZE elements stored in -// the object itself. +// A memory buffer for trivially copyable/constructible types with the first +// SIZE elements stored in the object itself. template > class MemoryBuffer : private Allocator, public Buffer { @@ -896,24 +960,6 @@ struct IntTraits TypeSelector::digits <= 32>::Type MainType; }; -// MakeUnsigned::Type gives an unsigned type corresponding to integer type T. -template -struct MakeUnsigned -{ - typedef T Type; -}; - -#define FMT_SPECIALIZE_MAKE_UNSIGNED(T, U) \ - template <> \ - struct MakeUnsigned { typedef U Type; } - -FMT_SPECIALIZE_MAKE_UNSIGNED(char, unsigned char); -FMT_SPECIALIZE_MAKE_UNSIGNED(signed char, unsigned char); -FMT_SPECIALIZE_MAKE_UNSIGNED(short, unsigned short); -FMT_SPECIALIZE_MAKE_UNSIGNED(int, unsigned); -FMT_SPECIALIZE_MAKE_UNSIGNED(long, unsigned long); -FMT_SPECIALIZE_MAKE_UNSIGNED(LongLong, ULongLong); - FMT_API void report_unknown_type(char code, const char *type); // Static data is placed in this class template to allow header-only @@ -926,16 +972,18 @@ struct FMT_API BasicData static const char DIGITS[]; }; +#ifndef FMT_USE_EXTERN_TEMPLATES +// Clang doesn't have a feature check for extern templates so we check +// for variadic templates which were introduced in the same version. +# define FMT_USE_EXTERN_TEMPLATES (__clang__ && FMT_USE_VARIADIC_TEMPLATES) +#endif + +#if FMT_USE_EXTERN_TEMPLATES && !defined(FMT_HEADER_ONLY) +extern template struct BasicData; +#endif + typedef BasicData<> Data; -#if FMT_GCC_VERSION >= 400 || FMT_HAS_BUILTIN(__builtin_clz) -# define FMT_BUILTIN_CLZ(n) __builtin_clz(n) -#endif - -#if FMT_GCC_VERSION >= 400 || FMT_HAS_BUILTIN(__builtin_clzll) -# define FMT_BUILTIN_CLZLL(n) __builtin_clzll(n) -#endif - #ifdef FMT_BUILTIN_CLZLL // Returns the number of decimal digits in n. Leading zeros are not counted // except for n == 0 in which case count_digits returns 1. @@ -943,8 +991,8 @@ inline unsigned count_digits(uint64_t n) { // Based on http://graphics.stanford.edu/~seander/bithacks.html#IntegerLog10 // and the benchmark https://github.com/localvoid/cxx-benchmark-count-digits. - unsigned t = (64 - FMT_BUILTIN_CLZLL(n | 1)) * 1233 >> 12; - return t - (n < Data::POWERS_OF_10_64[t]) + 1; + int t = (64 - FMT_BUILTIN_CLZLL(n | 1)) * 1233 >> 12; + return to_unsigned(t) - (n < Data::POWERS_OF_10_64[t]) + 1; } #else // Fallback version of count_digits used when __builtin_clz is not available. @@ -970,14 +1018,47 @@ inline unsigned count_digits(uint64_t n) // Optional version of count_digits for better performance on 32-bit platforms. inline unsigned count_digits(uint32_t n) { - uint32_t t = (32 - FMT_BUILTIN_CLZ(n | 1)) * 1233 >> 12; - return t - (n < Data::POWERS_OF_10_32[t]) + 1; + int t = (32 - FMT_BUILTIN_CLZ(n | 1)) * 1233 >> 12; + return to_unsigned(t) - (n < Data::POWERS_OF_10_32[t]) + 1; } #endif +// A functor that doesn't add a thousands separator. +struct NoThousandsSep +{ + template + void operator()(Char *) {} +}; + +// A functor that adds a thousands separator. +class ThousandsSep +{ +private: + fmt::StringRef sep_; + + // Index of a decimal digit with the least significant digit having index 0. + unsigned digit_index_; + +public: + explicit ThousandsSep(fmt::StringRef sep) : sep_(sep), digit_index_(0) {} + + template + void operator()(Char *&buffer) + { + if (++digit_index_ % 3 != 0) + return; + buffer -= sep_.size(); + std::uninitialized_copy(sep_.data(), sep_.data() + sep_.size(), + internal::make_ptr(buffer, sep_.size())); + } +}; + // Formats a decimal unsigned integer value writing into buffer. -template -inline void format_decimal(Char *buffer, UInt value, unsigned num_digits) +// thousands_sep is a functor that is called after writing each char to +// add a thousands separator if necessary. +template +inline void format_decimal(Char *buffer, UInt value, unsigned num_digits, + ThousandsSep thousands_sep) { buffer += num_digits; while (value >= 100) @@ -988,7 +1069,9 @@ inline void format_decimal(Char *buffer, UInt value, unsigned num_digits) unsigned index = static_cast((value % 100) * 2); value /= 100; *--buffer = Data::DIGITS[index + 1]; + thousands_sep(buffer); *--buffer = Data::DIGITS[index]; + thousands_sep(buffer); } if (value < 10) { @@ -997,9 +1080,16 @@ inline void format_decimal(Char *buffer, UInt value, unsigned num_digits) } unsigned index = static_cast(value * 2); *--buffer = Data::DIGITS[index + 1]; + thousands_sep(buffer); *--buffer = Data::DIGITS[index]; } +template +inline void format_decimal(Char *buffer, UInt value, unsigned num_digits) +{ + return format_decimal(buffer, value, num_digits, NoThousandsSep()); +} + #ifndef _WIN32 # define FMT_USE_WINDOWS_H 0 #elif !defined(FMT_USE_WINDOWS_H) @@ -1073,9 +1163,6 @@ FMT_API void format_windows_error(fmt::Writer &out, int error_code, fmt::StringRef message) FMT_NOEXCEPT; #endif -FMT_API void format_system_error(fmt::Writer &out, int error_code, - fmt::StringRef message) FMT_NOEXCEPT; - // A formatting argument value. struct Value { @@ -1086,7 +1173,7 @@ struct Value std::size_t size; }; - typedef void(*FormatFunc)( + typedef void (*FormatFunc)( void *formatter, const void *arg, void *format_str_ptr); struct CustomValue @@ -1122,8 +1209,8 @@ struct Value }; }; -// A formatting argument. It is a POD type to allow storage in -// internal::MemoryBuffer. +// A formatting argument. It is a trivially copyable/constructible type to +// allow storage in internal::MemoryBuffer. struct Arg : Value { Type type; @@ -1154,37 +1241,17 @@ struct WCharHelper typedef char Yes[1]; typedef char No[2]; -// These are non-members to workaround an overload resolution bug in bcc32. -Yes &convert(fmt::ULongLong); -Yes &convert(std::ostream &); -No &convert(...); - template T &get(); -struct DummyStream : std::ostream -{ - DummyStream(); // Suppress a bogus warning in MSVC. - // Hide all operator<< overloads from std::ostream. - void operator<<(Null<>); -}; - -No &operator<<(std::ostream &, int); +// These are non-members to workaround an overload resolution bug in bcc32. +Yes &convert(fmt::ULongLong); +No &convert(...); template struct ConvertToIntImpl { - enum { value = false }; -}; - -template -struct ConvertToIntImpl -{ - // Convert to int only if T doesn't have an overloaded operator<<. - enum - { - value = sizeof(convert(get() << get())) == sizeof(No) - }; + enum { value = ENABLE_CONVERSION }; }; template @@ -1241,18 +1308,74 @@ struct Conditional }; // For bcc32 which doesn't understand ! in template arguments. -template +template struct Not { enum { value = 0 }; }; -template<> +template <> struct Not { enum { value = 1 }; }; +template +struct False +{ + enum { value = 0 }; +}; + +template struct LConvCheck +{ + LConvCheck(int) {} +}; + +// Returns the thousands separator for the current locale. +// We check if ``lconv`` contains ``thousands_sep`` because on Android +// ``lconv`` is stubbed as an empty struct. +template +inline StringRef thousands_sep( + LConv *lc, LConvCheck = 0) +{ + return lc->thousands_sep; +} + +inline fmt::StringRef thousands_sep(...) +{ + return ""; +} + +#define FMT_CONCAT(a, b) a##b + +#if FMT_GCC_VERSION >= 407 +# define FMT_UNUSED __attribute__((unused)) +#else +# define FMT_UNUSED +#endif + +#ifndef FMT_USE_STATIC_ASSERT +# define FMT_USE_STATIC_ASSERT 0 +#endif + +#if FMT_USE_STATIC_ASSERT || FMT_HAS_FEATURE(cxx_static_assert) || \ + (FMT_GCC_VERSION >= 403 && FMT_HAS_GXX_CXX11) || _MSC_VER >= 1600 +# define FMT_STATIC_ASSERT(cond, message) static_assert(cond, message) +#else +# define FMT_CONCAT_(a, b) FMT_CONCAT(a, b) +# define FMT_STATIC_ASSERT(cond, message) \ + typedef int FMT_CONCAT_(Assert, __LINE__)[(cond) ? 1 : -1] FMT_UNUSED +#endif + +template +void format_arg(Formatter &, const Char *, const T &) +{ + FMT_STATIC_ASSERT(False::value, + "Cannot format argument. To enable the use of ostream " + "operator<< include fmt/ostream.h. Otherwise provide " + "an overload of format_arg."); +} + // Makes an Arg object from any type. template class MakeValue : public Arg @@ -1275,7 +1398,7 @@ private: // characters and strings into narrow strings as in // fmt::format("{}", L"test"); // To fix this, use a wide format string: fmt::format(L"{}", L"test"). -#if !defined(_MSC_VER) || defined(_NATIVE_WCHAR_T_DEFINED) +#if !FMT_MSC_VER || defined(_NATIVE_WCHAR_T_DEFINED) MakeValue(typename WCharHelper::Unsupported); #endif MakeValue(typename WCharHelper::Unsupported); @@ -1300,9 +1423,9 @@ private: static void format_custom_arg( void *formatter, const void *arg, void *format_str_ptr) { - format(*static_cast(formatter), - *static_cast(format_str_ptr), - *static_cast(arg)); + format_arg(*static_cast(formatter), + *static_cast(format_str_ptr), + *static_cast(arg)); } public: @@ -1325,7 +1448,7 @@ public: { // To minimize the number of types we need to deal with, long is // translated either to int or to long long depending on its size. - if (check(sizeof(long) == sizeof(int))) + if (const_check(sizeof(long) == sizeof(int))) int_value = static_cast(value); else long_long_value = value; @@ -1337,7 +1460,7 @@ public: MakeValue(unsigned long value) { - if (check(sizeof(unsigned long) == sizeof(unsigned))) + if (const_check(sizeof(unsigned long) == sizeof(unsigned))) uint_value = static_cast(value); else ulong_long_value = value; @@ -1374,7 +1497,9 @@ public: FMT_MAKE_VALUE(char *, string.value, CSTRING) FMT_MAKE_VALUE(const char *, string.value, CSTRING) + FMT_MAKE_VALUE(signed char *, sstring.value, CSTRING) FMT_MAKE_VALUE(const signed char *, sstring.value, CSTRING) + FMT_MAKE_VALUE(unsigned char *, ustring.value, CSTRING) FMT_MAKE_VALUE(const unsigned char *, ustring.value, CSTRING) FMT_MAKE_STR_VALUE(const std::string &, STRING) FMT_MAKE_STR_VALUE(StringRef, STRING) @@ -1383,7 +1508,7 @@ public: #define FMT_MAKE_WSTR_VALUE(Type, TYPE) \ MakeValue(typename WCharHelper::Supported value) { \ set_string(value); \ - } \ + } \ static uint64_t type(Type) { return Arg::TYPE; } FMT_MAKE_WSTR_VALUE(wchar_t *, WSTRING) @@ -1431,166 +1556,40 @@ public: } }; +template +class MakeArg : public Arg +{ +public: + MakeArg() + { + type = Arg::NONE; + } + + template + MakeArg(const T &value) + : Arg(MakeValue(value)) + { + type = static_cast(MakeValue::type(value)); + } +}; + template struct NamedArg : Arg { BasicStringRef name; - typedef internal::MakeValue< BasicFormatter > MakeValue; - template NamedArg(BasicStringRef argname, const T &value) - : Arg(MakeValue(value)), name(argname) - { - type = static_cast(MakeValue::type(value)); - } -}; - -#define FMT_DISPATCH(call) static_cast(this)->call - -// An argument visitor. -// To use ArgVisitor define a subclass that implements some or all of the -// visit methods with the same signatures as the methods in ArgVisitor, -// for example, visit_int(int). -// Specify the subclass name as the Impl template parameter. Then calling -// ArgVisitor::visit for some argument will dispatch to a visit method -// specific to the argument type. For example, if the argument type is -// double then visit_double(double) method of a subclass will be called. -// If the subclass doesn't contain a method with this signature, then -// a corresponding method of ArgVisitor will be called. -// -// Example: -// class MyArgVisitor : public ArgVisitor { -// public: -// void visit_int(int value) { print("{}", value); } -// void visit_double(double value) { print("{}", value ); } -// }; -// -// ArgVisitor uses the curiously recurring template pattern: -// http://en.wikipedia.org/wiki/Curiously_recurring_template_pattern -template -class ArgVisitor -{ -public: - void report_unhandled_arg() {} - - Result visit_unhandled_arg() - { - FMT_DISPATCH(report_unhandled_arg()); - return Result(); - } - - Result visit_int(int value) - { - return FMT_DISPATCH(visit_any_int(value)); - } - Result visit_long_long(LongLong value) - { - return FMT_DISPATCH(visit_any_int(value)); - } - Result visit_uint(unsigned value) - { - return FMT_DISPATCH(visit_any_int(value)); - } - Result visit_ulong_long(ULongLong value) - { - return FMT_DISPATCH(visit_any_int(value)); - } - Result visit_bool(bool value) - { - return FMT_DISPATCH(visit_any_int(value)); - } - Result visit_char(int value) - { - return FMT_DISPATCH(visit_any_int(value)); - } - template - Result visit_any_int(T) - { - return FMT_DISPATCH(visit_unhandled_arg()); - } - - Result visit_double(double value) - { - return FMT_DISPATCH(visit_any_double(value)); - } - Result visit_long_double(long double value) - { - return FMT_DISPATCH(visit_any_double(value)); - } - template - Result visit_any_double(T) - { - return FMT_DISPATCH(visit_unhandled_arg()); - } - - Result visit_cstring(const char *) - { - return FMT_DISPATCH(visit_unhandled_arg()); - } - Result visit_string(Arg::StringValue) - { - return FMT_DISPATCH(visit_unhandled_arg()); - } - Result visit_wstring(Arg::StringValue) - { - return FMT_DISPATCH(visit_unhandled_arg()); - } - Result visit_pointer(const void *) - { - return FMT_DISPATCH(visit_unhandled_arg()); - } - Result visit_custom(Arg::CustomValue) - { - return FMT_DISPATCH(visit_unhandled_arg()); - } - - Result visit(const Arg &arg) - { - switch (arg.type) - { - default: - FMT_ASSERT(false, "invalid argument type"); - return Result(); - case Arg::INT: - return FMT_DISPATCH(visit_int(arg.int_value)); - case Arg::UINT: - return FMT_DISPATCH(visit_uint(arg.uint_value)); - case Arg::LONG_LONG: - return FMT_DISPATCH(visit_long_long(arg.long_long_value)); - case Arg::ULONG_LONG: - return FMT_DISPATCH(visit_ulong_long(arg.ulong_long_value)); - case Arg::BOOL: - return FMT_DISPATCH(visit_bool(arg.int_value != 0)); - case Arg::CHAR: - return FMT_DISPATCH(visit_char(arg.int_value)); - case Arg::DOUBLE: - return FMT_DISPATCH(visit_double(arg.double_value)); - case Arg::LONG_DOUBLE: - return FMT_DISPATCH(visit_long_double(arg.long_double_value)); - case Arg::CSTRING: - return FMT_DISPATCH(visit_cstring(arg.string.value)); - case Arg::STRING: - return FMT_DISPATCH(visit_string(arg.string)); - case Arg::WSTRING: - return FMT_DISPATCH(visit_wstring(arg.wstring)); - case Arg::POINTER: - return FMT_DISPATCH(visit_pointer(arg.pointer)); - case Arg::CUSTOM: - return FMT_DISPATCH(visit_custom(arg.custom)); - } - } + : Arg(MakeArg< BasicFormatter >(value)), name(argname) {} }; class RuntimeError : public std::runtime_error { protected: RuntimeError() : std::runtime_error("") {} + ~RuntimeError() throw(); }; -template -class PrintfArgFormatter; - template class ArgMap; } // namespace internal @@ -1666,6 +1665,186 @@ public: } }; +#define FMT_DISPATCH(call) static_cast(this)->call + +/** + \rst + An argument visitor based on the `curiously recurring template pattern + `_. + + To use `~fmt::ArgVisitor` define a subclass that implements some or all of the + visit methods with the same signatures as the methods in `~fmt::ArgVisitor`, + for example, `~fmt::ArgVisitor::visit_int()`. + Pass the subclass as the *Impl* template parameter. Then calling + `~fmt::ArgVisitor::visit` for some argument will dispatch to a visit method + specific to the argument type. For example, if the argument type is + ``double`` then the `~fmt::ArgVisitor::visit_double()` method of a subclass + will be called. If the subclass doesn't contain a method with this signature, + then a corresponding method of `~fmt::ArgVisitor` will be called. + + **Example**:: + + class MyArgVisitor : public fmt::ArgVisitor { + public: + void visit_int(int value) { fmt::print("{}", value); } + void visit_double(double value) { fmt::print("{}", value ); } + }; + \endrst + */ +template +class ArgVisitor +{ +private: + typedef internal::Arg Arg; + +public: + void report_unhandled_arg() {} + + Result visit_unhandled_arg() + { + FMT_DISPATCH(report_unhandled_arg()); + return Result(); + } + + /** Visits an ``int`` argument. **/ + Result visit_int(int value) + { + return FMT_DISPATCH(visit_any_int(value)); + } + + /** Visits a ``long long`` argument. **/ + Result visit_long_long(LongLong value) + { + return FMT_DISPATCH(visit_any_int(value)); + } + + /** Visits an ``unsigned`` argument. **/ + Result visit_uint(unsigned value) + { + return FMT_DISPATCH(visit_any_int(value)); + } + + /** Visits an ``unsigned long long`` argument. **/ + Result visit_ulong_long(ULongLong value) + { + return FMT_DISPATCH(visit_any_int(value)); + } + + /** Visits a ``bool`` argument. **/ + Result visit_bool(bool value) + { + return FMT_DISPATCH(visit_any_int(value)); + } + + /** Visits a ``char`` or ``wchar_t`` argument. **/ + Result visit_char(int value) + { + return FMT_DISPATCH(visit_any_int(value)); + } + + /** Visits an argument of any integral type. **/ + template + Result visit_any_int(T) + { + return FMT_DISPATCH(visit_unhandled_arg()); + } + + /** Visits a ``double`` argument. **/ + Result visit_double(double value) + { + return FMT_DISPATCH(visit_any_double(value)); + } + + /** Visits a ``long double`` argument. **/ + Result visit_long_double(long double value) + { + return FMT_DISPATCH(visit_any_double(value)); + } + + /** Visits a ``double`` or ``long double`` argument. **/ + template + Result visit_any_double(T) + { + return FMT_DISPATCH(visit_unhandled_arg()); + } + + /** Visits a null-terminated C string (``const char *``) argument. **/ + Result visit_cstring(const char *) + { + return FMT_DISPATCH(visit_unhandled_arg()); + } + + /** Visits a string argument. **/ + Result visit_string(Arg::StringValue) + { + return FMT_DISPATCH(visit_unhandled_arg()); + } + + /** Visits a wide string argument. **/ + Result visit_wstring(Arg::StringValue) + { + return FMT_DISPATCH(visit_unhandled_arg()); + } + + /** Visits a pointer argument. **/ + Result visit_pointer(const void *) + { + return FMT_DISPATCH(visit_unhandled_arg()); + } + + /** Visits an argument of a custom (user-defined) type. **/ + Result visit_custom(Arg::CustomValue) + { + return FMT_DISPATCH(visit_unhandled_arg()); + } + + /** + \rst + Visits an argument dispatching to the appropriate visit method based on + the argument type. For example, if the argument type is ``double`` then + the `~fmt::ArgVisitor::visit_double()` method of the *Impl* class will be + called. + \endrst + */ + Result visit(const Arg &arg) + { + switch (arg.type) + { + case Arg::NONE: + case Arg::NAMED_ARG: + FMT_ASSERT(false, "invalid argument type"); + break; + case Arg::INT: + return FMT_DISPATCH(visit_int(arg.int_value)); + case Arg::UINT: + return FMT_DISPATCH(visit_uint(arg.uint_value)); + case Arg::LONG_LONG: + return FMT_DISPATCH(visit_long_long(arg.long_long_value)); + case Arg::ULONG_LONG: + return FMT_DISPATCH(visit_ulong_long(arg.ulong_long_value)); + case Arg::BOOL: + return FMT_DISPATCH(visit_bool(arg.int_value != 0)); + case Arg::CHAR: + return FMT_DISPATCH(visit_char(arg.int_value)); + case Arg::DOUBLE: + return FMT_DISPATCH(visit_double(arg.double_value)); + case Arg::LONG_DOUBLE: + return FMT_DISPATCH(visit_long_double(arg.long_double_value)); + case Arg::CSTRING: + return FMT_DISPATCH(visit_cstring(arg.string.value)); + case Arg::STRING: + return FMT_DISPATCH(visit_string(arg.string)); + case Arg::WSTRING: + return FMT_DISPATCH(visit_wstring(arg.wstring)); + case Arg::POINTER: + return FMT_DISPATCH(visit_pointer(arg.pointer)); + case Arg::CUSTOM: + return FMT_DISPATCH(visit_custom(arg.custom)); + } + return Result(); + } +}; + enum Alignment { ALIGN_DEFAULT, ALIGN_LEFT, ALIGN_RIGHT, ALIGN_CENTER, ALIGN_NUMERIC @@ -1830,41 +2009,41 @@ public: }; /** -Returns an integer format specifier to format the value in base 2. -*/ + Returns an integer format specifier to format the value in base 2. + */ IntFormatSpec > bin(int value); /** -Returns an integer format specifier to format the value in base 8. -*/ + Returns an integer format specifier to format the value in base 8. + */ IntFormatSpec > oct(int value); /** -Returns an integer format specifier to format the value in base 16 using -lower-case letters for the digits above 9. -*/ + Returns an integer format specifier to format the value in base 16 using + lower-case letters for the digits above 9. + */ IntFormatSpec > hex(int value); /** -Returns an integer formatter format specifier to format in base 16 using -upper-case letters for the digits above 9. -*/ + Returns an integer formatter format specifier to format in base 16 using + upper-case letters for the digits above 9. + */ IntFormatSpec > hexu(int value); /** -\rst -Returns an integer format specifier to pad the formatted argument with the -fill character to the specified width using the default (right) numeric -alignment. + \rst + Returns an integer format specifier to pad the formatted argument with the + fill character to the specified width using the default (right) numeric + alignment. -**Example**:: + **Example**:: -MemoryWriter out; -out << pad(hex(0xcafe), 8, '0'); -// out.str() == "0000cafe" + MemoryWriter out; + out << pad(hex(0xcafe), 8, '0'); + // out.str() == "0000cafe" -\endrst -*/ + \endrst + */ template IntFormatSpec, Char> pad( int value, unsigned width, Char fill = ' '); @@ -1872,26 +2051,26 @@ IntFormatSpec, Char> pad( #define FMT_DEFINE_INT_FORMATTERS(TYPE) \ inline IntFormatSpec > bin(TYPE value) { \ return IntFormatSpec >(value, TypeSpec<'b'>()); \ - } \ +} \ \ inline IntFormatSpec > oct(TYPE value) { \ return IntFormatSpec >(value, TypeSpec<'o'>()); \ - } \ +} \ \ inline IntFormatSpec > hex(TYPE value) { \ return IntFormatSpec >(value, TypeSpec<'x'>()); \ - } \ +} \ \ inline IntFormatSpec > hexu(TYPE value) { \ return IntFormatSpec >(value, TypeSpec<'X'>()); \ - } \ +} \ \ template \ inline IntFormatSpec > pad( \ IntFormatSpec > f, unsigned width) { \ return IntFormatSpec >( \ f.value(), AlignTypeSpec(width, ' ')); \ - } \ +} \ \ /* For compatibility with older compilers we provide two overloads for pad, */ \ /* one that takes a fill character and one that doesn't. In the future this */ \ @@ -1903,20 +2082,20 @@ inline IntFormatSpec, Char> pad( \ unsigned width, Char fill) { \ return IntFormatSpec, Char>( \ f.value(), AlignTypeSpec(width, fill)); \ - } \ +} \ \ inline IntFormatSpec > pad( \ TYPE value, unsigned width) { \ return IntFormatSpec >( \ value, AlignTypeSpec<0>(width, ' ')); \ - } \ +} \ \ template \ inline IntFormatSpec, Char> pad( \ TYPE value, unsigned width, Char fill) { \ return IntFormatSpec, Char>( \ value, AlignTypeSpec<0>(width, fill)); \ - } +} FMT_DEFINE_INT_FORMATTERS(int) FMT_DEFINE_INT_FORMATTERS(long) @@ -1926,17 +2105,17 @@ FMT_DEFINE_INT_FORMATTERS(LongLong) FMT_DEFINE_INT_FORMATTERS(ULongLong) /** -\rst -Returns a string formatter that pads the formatted argument with the fill -character to the specified width using the default (left) string alignment. + \rst + Returns a string formatter that pads the formatted argument with the fill + character to the specified width using the default (left) string alignment. -**Example**:: + **Example**:: -std::string s = str(MemoryWriter() << pad("abc", 8)); -// s == "abc " + std::string s = str(MemoryWriter() << pad("abc", 8)); + // s == "abc " -\endrst -*/ + \endrst + */ template inline StrFormatSpec pad( const Char *str, unsigned width, Char fill = ' ') @@ -1957,7 +2136,8 @@ template class ArgMap { private: - typedef std::map, internal::Arg> MapType; + typedef std::vector< + std::pair, internal::Arg> > MapType; typedef typename MapType::value_type Pair; MapType map_; @@ -1967,8 +2147,14 @@ public: const internal::Arg* find(const fmt::BasicStringRef &name) const { - typename MapType::const_iterator it = map_.find(name); - return it != map_.end() ? &it->second : 0; + // The list is unsorted, so just return the first matching name. + for (typename MapType::const_iterator it = map_.begin(), end = map_.end(); + it != end; ++it) + { + if (it->first == name) + return &it->second; + } + return 0; } }; @@ -2007,7 +2193,7 @@ protected: void write(const char *value) { - Arg::StringValue str = { value, value != 0 ? std::strlen(value) : 0 }; + Arg::StringValue str = {value, value != 0 ? std::strlen(value) : 0}; writer_.write_str(str, spec_); } @@ -2059,7 +2245,7 @@ public: else if (spec_.align_ == ALIGN_CENTER) { out = writer_.fill_padding(out, spec_.width_, - internal::check(CHAR_WIDTH), fill); + internal::const_check(CHAR_WIDTH), fill); } else { @@ -2101,26 +2287,6 @@ public: } }; -// An argument formatter. -template -class BasicArgFormatter : - public ArgFormatterBase, Char> -{ -private: - BasicFormatter &formatter_; - const Char *format_; - -public: - BasicArgFormatter(BasicFormatter &f, FormatSpec &s, const Char *fmt) - : ArgFormatterBase, Char>(f.writer(), s), - formatter_(f), format_(fmt) {} - - void visit_custom(Arg::CustomValue c) - { - c.format(&formatter_, c.value, &format_); - } -}; - class FormatterBase { private: @@ -2146,7 +2312,7 @@ protected: Arg next_arg(const char *&error) { if (next_arg_index_ >= 0) - return do_get_arg(next_arg_index_++, error); + return do_get_arg(internal::to_unsigned(next_arg_index_++), error); error = "cannot switch from manual to automatic argument indexing"; return Arg(); } @@ -2173,37 +2339,73 @@ protected: void write(BasicWriter &w, const Char *start, const Char *end) { if (start != end) - w << BasicStringRef(start, end - start); + w << BasicStringRef(start, internal::to_unsigned(end - start)); } }; - -// A printf formatter. -template -class PrintfFormatter : private FormatterBase -{ -private: - void parse_flags(FormatSpec &spec, const Char *&s); - - // Returns the argument with specified index or, if arg_index is equal - // to the maximum unsigned value, the next argument. - Arg get_arg(const Char *s, - unsigned arg_index = (std::numeric_limits::max)()); - - // Parses argument index, flags and width and returns the argument index. - unsigned parse_header(const Char *&s, FormatSpec &spec); - -public: - explicit PrintfFormatter(const ArgList &args) : FormatterBase(args) {} - FMT_API void format(BasicWriter &writer, - BasicCStringRef format_str); -}; } // namespace internal -// A formatter. -template +/** + \rst + An argument formatter based on the `curiously recurring template pattern + `_. + + To use `~fmt::BasicArgFormatter` define a subclass that implements some or + all of the visit methods with the same signatures as the methods in + `~fmt::ArgVisitor`, for example, `~fmt::ArgVisitor::visit_int()`. + Pass the subclass as the *Impl* template parameter. When a formatting + function processes an argument, it will dispatch to a visit method + specific to the argument type. For example, if the argument type is + ``double`` then the `~fmt::ArgVisitor::visit_double()` method of a subclass + will be called. If the subclass doesn't contain a method with this signature, + then a corresponding method of `~fmt::BasicArgFormatter` or its superclass + will be called. + \endrst + */ +template +class BasicArgFormatter : public internal::ArgFormatterBase +{ +private: + BasicFormatter &formatter_; + const Char *format_; + +public: + /** + \rst + Constructs an argument formatter object. + *formatter* is a reference to the main formatter object, *spec* contains + format specifier information for standard argument types, and *fmt* points + to the part of the format string being parsed for custom argument types. + \endrst + */ + BasicArgFormatter(BasicFormatter &formatter, + FormatSpec &spec, const Char *fmt) + : internal::ArgFormatterBase(formatter.writer(), spec), + formatter_(formatter), format_(fmt) {} + + /** Formats an argument of a custom (user-defined) type. */ + void visit_custom(internal::Arg::CustomValue c) + { + c.format(&formatter_, c.value, &format_); + } +}; + +/** The default argument formatter. */ +template +class ArgFormatter : public BasicArgFormatter, Char> +{ +public: + /** Constructs an argument formatter object. */ + ArgFormatter(BasicFormatter &formatter, + FormatSpec &spec, const Char *fmt) + : BasicArgFormatter, Char>(formatter, spec, fmt) {} +}; + +/** This template formats data and writes the output to a writer. */ +template class BasicFormatter : private internal::FormatterBase { public: + /** The character type for the output. */ typedef CharType Char; private: @@ -2225,16 +2427,26 @@ private: internal::Arg parse_arg_name(const Char *&s); public: + /** + \rst + Constructs a ``BasicFormatter`` object. References to the arguments and + the writer are stored in the formatter object so make sure they have + appropriate lifetimes. + \endrst + */ BasicFormatter(const ArgList &args, BasicWriter &w) : internal::FormatterBase(args), writer_(w) {} + /** Returns a reference to the writer associated with this formatter. */ BasicWriter &writer() { return writer_; } + /** Formats stored arguments and writes the output to the writer. */ void format(BasicCStringRef format_str); + // Formats a single argument and advances format_str, a format string pointer. const Char *format(const Char *&format_str, const internal::Arg &arg); }; @@ -2270,17 +2482,39 @@ inline uint64_t make_type(const T &arg) return MakeValue< BasicFormatter >::type(arg); } -template -struct ArgArray -{ - // Computes the argument array size by adding 1 to N, which is the number of - // arguments, if N is zero, because array of zero size is invalid, or if N - // is greater than ArgList::MAX_PACKED_ARGS to accommodate for an extra - // argument that marks the end of the list. - enum { SIZE = N + (N == 0 || N >= ArgList::MAX_PACKED_ARGS ? 1 : 0) }; +template + struct ArgArray; - typedef typename Conditional< - (N < ArgList::MAX_PACKED_ARGS), Value, Arg>::type Type[SIZE]; +template +struct ArgArray +{ + typedef Value Type[N > 0 ? N : 1]; + +template +static Value make(const T &value) +{ +#ifdef __clang__ + Value result = MakeValue(value); + // Workaround a bug in Apple LLVM version 4.2 (clang-425.0.28) of clang: + // https://github.com/fmtlib/fmt/issues/276 + (void)result.custom.format; + return result; +#else + return MakeValue(value); +#endif +} + }; + +template +struct ArgArray +{ + typedef Arg Type[N + 1]; // +1 for the list end Arg::NONE + + template + static Arg make(const T &value) + { + return MakeArg(value); + } }; #if FMT_USE_VARIADIC_TEMPLATES @@ -2290,52 +2524,6 @@ inline uint64_t make_type(const Arg &first, const Args & ... tail) return make_type(first) | (make_type(tail...) << 4); } -inline void do_set_types(Arg *) {} - -template -inline void do_set_types(Arg *args, const T &arg, const Args & ... tail) -{ - args->type = static_cast( - MakeValue< BasicFormatter >::type(arg)); - do_set_types(args + 1, tail...); -} - -template -inline void set_types(Arg *array, const Args & ... args) -{ - if (check(sizeof...(Args) > ArgList::MAX_PACKED_ARGS)) - do_set_types(array, args...); - array[sizeof...(Args)].type = Arg::NONE; -} - -template -inline void set_types(Value *, const Args & ...) -{ - // Do nothing as types are passed separately from values. -} - -template -inline void store_args(Value *) {} - -template -inline void store_args(Arg *args, const T &arg, const Args & ... tail) -{ - // Assign only the Value subobject of Arg and don't overwrite type (if any) - // that is assigned by set_types. - Value &value = *args; - value = MakeValue(arg); - store_args(args + 1, tail...); -} - -template -ArgList make_arg_list(typename ArgArray::Type array, - const Args & ... args) -{ - if (check(sizeof...(Args) >= ArgList::MAX_PACKED_ARGS)) - set_types(array, args...); - store_args(array, args...); - return ArgList(make_type(args...), array); -} #else struct ArgType @@ -2358,43 +2546,6 @@ inline uint64_t make_type(FMT_GEN15(FMT_ARG_TYPE_DEFAULT)) (t12.type << 48) | (t13.type << 52) | (t14.type << 56); } #endif - -template -class FormatBuf : public std::basic_streambuf -{ -private: - typedef typename std::basic_streambuf::int_type int_type; - typedef typename std::basic_streambuf::traits_type traits_type; - - Buffer &buffer_; - Char *start_; - -public: - FormatBuf(Buffer &buffer) : buffer_(buffer), start_(&buffer[0]) - { - this->setp(start_, start_ + buffer_.capacity()); - } - - int_type overflow(int_type ch = traits_type::eof()) - { - if (!traits_type::eq_int_type(ch, traits_type::eof())) - { - size_t size = this->pptr() - start_; - buffer_.resize(size); - buffer_.reserve(size * 2); - - start_ = &buffer_[0]; - start_[size] = traits_type::to_char_type(ch); - this->setp(start_ + size + 1, start_ + size * 2); - } - return ch; - } - - size_t size() const - { - return this->pptr() - start_; - } -}; } // namespace internal # define FMT_MAKE_TEMPLATE_ARG(n) typename T##n @@ -2410,19 +2561,21 @@ public: # define FMT_VARIADIC_VOID(func, arg_type) \ template \ void func(arg_type arg0, const Args & ... args) { \ - typename fmt::internal::ArgArray::Type array; \ - func(arg0, fmt::internal::make_arg_list< \ - fmt::BasicFormatter >(array, args...)); \ - } + typedef fmt::internal::ArgArray ArgArray; \ + typename ArgArray::Type array{ \ + ArgArray::template make >(args)...}; \ + func(arg0, fmt::ArgList(fmt::internal::make_type(args...), array)); \ + } // Defines a variadic constructor. # define FMT_VARIADIC_CTOR(ctor, func, arg0_type, arg1_type) \ template \ ctor(arg0_type arg0, arg1_type arg1, const Args & ... args) { \ - typename fmt::internal::ArgArray::Type array; \ - func(arg0, arg1, fmt::internal::make_arg_list< \ - fmt::BasicFormatter >(array, args...)); \ - } + typedef fmt::internal::ArgArray ArgArray; \ + typename ArgArray::Type array{ \ + ArgArray::template make >(args)...}; \ + func(arg0, arg1, fmt::ArgList(fmt::internal::make_type(args...), array)); \ + } #else @@ -2438,7 +2591,7 @@ public: const fmt::internal::ArgArray::Type array = {FMT_GEN(n, FMT_MAKE_REF)}; \ func(arg1, fmt::ArgList( \ fmt::internal::make_type(FMT_GEN(n, FMT_MAKE_REF2)), array)); \ - } + } // Emulates a variadic function returning void on a pre-C++11 compiler. # define FMT_VARIADIC_VOID(func, arg_type) \ @@ -2455,7 +2608,7 @@ public: const fmt::internal::ArgArray::Type array = {FMT_GEN(n, FMT_MAKE_REF)}; \ func(arg0, arg1, fmt::ArgList( \ fmt::internal::make_type(FMT_GEN(n, FMT_MAKE_REF2)), array)); \ - } + } // Emulates a variadic constructor on a pre-C++11 compiler. # define FMT_VARIADIC_CTOR(ctor, func, arg0_type, arg1_type) \ @@ -2494,8 +2647,8 @@ public: FMT_FOR_EACH9(f, x0, x1, x2, x3, x4, x5, x6, x7, x8), f(x9, 9) /** -An error returned by an operating system or a language runtime, -for example a file opening error. + An error returned by an operating system or a language runtime, + for example a file opening error. */ class SystemError : public internal::RuntimeError { @@ -2511,29 +2664,22 @@ protected: public: /** - \rst - Constructs a :class:`fmt::SystemError` object with the description - of the form + \rst + Constructs a :class:`fmt::SystemError` object with a description + formatted with `fmt::format_system_error`. *message* and additional + arguments passed into the constructor are formatted similarly to + `fmt::format`. - .. parsed-literal:: - **: ** + **Example**:: - where ** is the formatted message and ** is - the system message corresponding to the error code. - *error_code* is a system error code as given by ``errno``. - If *error_code* is not a valid error code such as -1, the system message - may look like "Unknown error -1" and is platform-dependent. - - **Example**:: - - // This throws a SystemError with the description - // cannot open file 'madeup': No such file or directory - // or similar (system message may vary). - const char *filename = "madeup"; - std::FILE *file = std::fopen(filename, "r"); - if (!file) - throw fmt::SystemError(errno, "cannot open file '{}'", filename); - \endrst + // This throws a SystemError with the description + // cannot open file 'madeup': No such file or directory + // or similar (system message may vary). + const char *filename = "madeup"; + std::FILE *file = std::fopen(filename, "r"); + if (!file) + throw fmt::SystemError(errno, "cannot open file '{}'", filename); + \endrst */ SystemError(int error_code, CStringRef message) { @@ -2541,6 +2687,8 @@ public: } FMT_VARIADIC_CTOR(SystemError, init, int, CStringRef) + ~SystemError() throw(); + int error_code() const { return error_code_; @@ -2548,23 +2696,42 @@ public: }; /** -\rst -This template provides operations for formatting and writing data into -a character stream. The output is stored in a buffer provided by a subclass -such as :class:`fmt::BasicMemoryWriter`. + \rst + Formats an error returned by an operating system or a language runtime, + for example a file opening error, and writes it to *out* in the following + form: -You can use one of the following typedefs for common character types: + .. parsed-literal:: + **: ** -+---------+----------------------+ -| Type | Definition | -+=========+======================+ -| Writer | BasicWriter | -+---------+----------------------+ -| WWriter | BasicWriter | -+---------+----------------------+ + where ** is the passed message and ** is + the system message corresponding to the error code. + *error_code* is a system error code as given by ``errno``. + If *error_code* is not a valid error code such as -1, the system message + may look like "Unknown error -1" and is platform-dependent. + \endrst + */ +FMT_API void format_system_error(fmt::Writer &out, int error_code, + fmt::StringRef message) FMT_NOEXCEPT; -\endrst -*/ +/** + \rst + This template provides operations for formatting and writing data into + a character stream. The output is stored in a buffer provided by a subclass + such as :class:`fmt::BasicMemoryWriter`. + + You can use one of the following typedefs for common character types: + + +---------+----------------------+ + | Type | Definition | + +=========+======================+ + | Writer | BasicWriter | + +---------+----------------------+ + | WWriter | BasicWriter | + +---------+----------------------+ + + \endrst + */ template class BasicWriter { @@ -2617,7 +2784,8 @@ private: template void write_decimal(Int value) { - typename internal::IntTraits::MainType abs_value = value; + typedef typename internal::IntTraits::MainType MainType; + MainType abs_value = static_cast(value); if (internal::is_negative(value)) { abs_value = 0 - abs_value; @@ -2680,43 +2848,44 @@ private: template friend class internal::ArgFormatterBase; - friend class internal::PrintfArgFormatter; + template + friend class BasicPrintfArgFormatter; protected: /** - Constructs a ``BasicWriter`` object. - */ + Constructs a ``BasicWriter`` object. + */ explicit BasicWriter(Buffer &b) : buffer_(b) {} public: /** - \rst - Destroys a ``BasicWriter`` object. - \endrst - */ + \rst + Destroys a ``BasicWriter`` object. + \endrst + */ virtual ~BasicWriter() {} /** - Returns the total number of characters written. - */ + Returns the total number of characters written. + */ std::size_t size() const { return buffer_.size(); } /** - Returns a pointer to the output buffer content. No terminating null - character is appended. - */ + Returns a pointer to the output buffer content. No terminating null + character is appended. + */ const Char *data() const FMT_NOEXCEPT { return &buffer_[0]; } /** - Returns a pointer to the output buffer content with terminating null - character appended. - */ + Returns a pointer to the output buffer content with terminating null + character appended. + */ const Char *c_str() const { std::size_t size = buffer_.size(); @@ -2726,40 +2895,40 @@ public: } /** - \rst - Returns the content of the output buffer as an `std::string`. - \endrst - */ + \rst + Returns the content of the output buffer as an `std::string`. + \endrst + */ std::basic_string str() const { return std::basic_string(&buffer_[0], buffer_.size()); } /** - \rst - Writes formatted data. + \rst + Writes formatted data. - *args* is an argument list representing arbitrary arguments. + *args* is an argument list representing arbitrary arguments. - **Example**:: + **Example**:: - MemoryWriter out; - out.write("Current point:\n"); - out.write("({:+f}, {:+f})", -3.14, 3.14); + MemoryWriter out; + out.write("Current point:\n"); + out.write("({:+f}, {:+f})", -3.14, 3.14); - This will write the following output to the ``out`` object: + This will write the following output to the ``out`` object: - .. code-block:: none + .. code-block:: none - Current point: - (-3.140000, +3.140000) + Current point: + (-3.140000, +3.140000) - The output can be accessed using :func:`data()`, :func:`c_str` or - :func:`str` methods. + The output can be accessed using :func:`data()`, :func:`c_str` or + :func:`str` methods. - See also :ref:`syntax`. - \endrst - */ + See also :ref:`syntax`. + \endrst + */ void write(BasicCStringRef format, ArgList args) { BasicFormatter(args, *this).format(format); @@ -2791,10 +2960,10 @@ public: } /** - \rst - Formats *value* and writes it to the stream. - \endrst - */ + \rst + Formats *value* and writes it to the stream. + \endrst + */ BasicWriter &operator<<(ULongLong value) { return *this << IntFormatSpec(value); @@ -2807,11 +2976,11 @@ public: } /** - \rst - Formats *value* using the general format for floating-point numbers - (``'g'``) and writes it to the stream. - \endrst - */ + \rst + Formats *value* using the general format for floating-point numbers + (``'g'``) and writes it to the stream. + \endrst + */ BasicWriter &operator<<(long double value) { write_double(value, FormatSpec()); @@ -2819,8 +2988,8 @@ public: } /** - Writes a character to the stream. - */ + Writes a character to the stream. + */ BasicWriter &operator<<(char value) { buffer_.push_back(value); @@ -2835,10 +3004,10 @@ public: } /** - \rst - Writes *value* to the stream. - \endrst - */ + \rst + Writes *value* to the stream. + \endrst + */ BasicWriter &operator<<(fmt::BasicStringRef value) { const Char *str = value.data(); @@ -2871,6 +3040,8 @@ public: } void clear() FMT_NOEXCEPT { buffer_.clear(); } + + Buffer &buffer() FMT_NOEXCEPT { return buffer_; } }; template @@ -2921,12 +3092,11 @@ void BasicWriter::write_str( if (!str_value) { FMT_THROW(FormatError("string pointer is null")); - return; } } - std::size_t precision = spec.precision_; + std::size_t precision = static_cast(spec.precision_); if (spec.precision_ >= 0 && precision < str_size) - str_size = spec.precision_; + str_size = precision; write_str(str_value, str_size, spec); } @@ -2963,7 +3133,8 @@ BasicWriter::prepare_int_buffer( // is specified. if (prefix_size > 0 && prefix[prefix_size - 1] == '0') --prefix_size; - unsigned number_size = prefix_size + spec.precision(); + unsigned number_size = + prefix_size + internal::to_unsigned(spec.precision()); AlignSpec subspec(number_size, '0', ALIGN_NUMERIC); if (number_size >= width) return prepare_int_buffer(num_digits, subspec, prefix, prefix_size); @@ -3030,7 +3201,7 @@ void BasicWriter::write_int(T value, Spec spec) { unsigned prefix_size = 0; typedef typename internal::IntTraits::MainType UnsignedType; - UnsignedType abs_value = value; + UnsignedType abs_value = static_cast(value); char prefix[4] = ""; if (internal::is_negative(value)) { @@ -3049,9 +3220,8 @@ void BasicWriter::write_int(T value, Spec spec) case 'd': { unsigned num_digits = internal::count_digits(abs_value); - CharPtr p = prepare_int_buffer( - num_digits, spec, prefix, prefix_size) + 1 - num_digits; - internal::format_decimal(get(p), abs_value, num_digits); + CharPtr p = prepare_int_buffer(num_digits, spec, prefix, prefix_size) + 1; + internal::format_decimal(get(p), abs_value, 0); break; } case 'x': @@ -3125,6 +3295,19 @@ void BasicWriter::write_int(T value, Spec spec) while ((n >>= 3) != 0); break; } + case 'n': + { + unsigned num_digits = internal::count_digits(abs_value); + fmt::StringRef sep = ""; +#ifndef ANDROID + sep = internal::thousands_sep(std::localeconv()); +#endif + unsigned size = static_cast( + num_digits + sep.size() * ((num_digits - 1) / 3)); + CharPtr p = prepare_int_buffer(size, spec, prefix, prefix_size) + 1; + internal::format_decimal(get(p), abs_value, 0, internal::ThousandsSep(sep)); + break; + } default: internal::report_unknown_type( spec.type(), spec.flag(CHAR_FLAG) ? "char" : "integer"); @@ -3134,8 +3317,7 @@ void BasicWriter::write_int(T value, Spec spec) template template -void BasicWriter::write_double( - T value, const FormatSpec &spec) +void BasicWriter::write_double(T value, const FormatSpec &spec) { // Check type. char type = spec.type(); @@ -3151,11 +3333,11 @@ void BasicWriter::write_double( case 'a': break; case 'F': -#ifdef _MSC_VER +#if FMT_MSC_VER // MSVC's printf doesn't support 'F'. type = 'f'; #endif - // Fall through. + // Fall through. case 'E': case 'G': case 'A': @@ -3224,7 +3406,7 @@ void BasicWriter::write_double( } // Build format string. - enum { MAX_FORMAT_SIZE = 10 }; // longest format: %#-*.*Lg + enum { MAX_FORMAT_SIZE = 10}; // longest format: %#-*.*Lg Char format[MAX_FORMAT_SIZE]; Char *format_ptr = format; *format_ptr++ = '%'; @@ -3254,10 +3436,12 @@ void BasicWriter::write_double( // Format using snprintf. Char fill = internal::CharTraits::cast(spec.fill()); + unsigned n = 0; + Char *start = 0; for (;;) { std::size_t buffer_size = buffer_.capacity() - offset; -#ifdef _MSC_VER +#if FMT_MSC_VER // MSVC's vsnprintf_s doesn't work with zero size, so reserve // space for at least one extra character to make the size non-zero. // Note that the buffer's capacity will increase by more than 1. @@ -3267,84 +3451,89 @@ void BasicWriter::write_double( buffer_size = buffer_.capacity() - offset; } #endif - Char *start = &buffer_[offset]; - int n = internal::CharTraits::format_float( - start, buffer_size, format, width_for_sprintf, spec.precision(), value); - if (n >= 0 && offset + n < buffer_.capacity()) + start = &buffer_[offset]; + int result = internal::CharTraits::format_float( + start, buffer_size, format, width_for_sprintf, spec.precision(), value); + if (result >= 0) { - if (sign) - { - if ((spec.align() != ALIGN_RIGHT && spec.align() != ALIGN_DEFAULT) || - *start != ' ') - { - *(start - 1) = sign; - sign = 0; - } - else - { - *(start - 1) = fill; - } - ++n; - } - if (spec.align() == ALIGN_CENTER && - spec.width() > static_cast(n)) - { - width = spec.width(); - CharPtr p = grow_buffer(width); - std::memmove(get(p) + (width - n) / 2, get(p), n * sizeof(Char)); - fill_padding(p, spec.width(), n, fill); - return; - } - if (spec.fill() != ' ' || sign) - { - while (*start == ' ') - *start++ = fill; - if (sign) - *(start - 1) = sign; - } - grow_buffer(n); - return; + n = internal::to_unsigned(result); + if (offset + n < buffer_.capacity()) + break; // The buffer is large enough - continue with formatting. + buffer_.reserve(offset + 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. + buffer_.reserve(buffer_.capacity() + 1); } - // If n is negative we ask to increase the capacity by at least 1, - // but as std::vector, the buffer grows exponentially. - buffer_.reserve(n >= 0 ? offset + n + 1 : buffer_.capacity() + 1); } + if (sign) + { + if ((spec.align() != ALIGN_RIGHT && spec.align() != ALIGN_DEFAULT) || + *start != ' ') + { + *(start - 1) = sign; + sign = 0; + } + else + { + *(start - 1) = fill; + } + ++n; + } + if (spec.align() == ALIGN_CENTER && spec.width() > n) + { + width = spec.width(); + CharPtr p = grow_buffer(width); + std::memmove(get(p) + (width - n) / 2, get(p), n * sizeof(Char)); + fill_padding(p, spec.width(), n, fill); + return; + } + if (spec.fill() != ' ' || sign) + { + while (*start == ' ') + *start++ = fill; + if (sign) + *(start - 1) = sign; + } + grow_buffer(n); } /** -\rst -This class template provides operations for formatting and writing data -into a character stream. The output is stored in a memory buffer that grows -dynamically. + \rst + This class template provides operations for formatting and writing data + into a character stream. The output is stored in a memory buffer that grows + dynamically. -You can use one of the following typedefs for common character types -and the standard allocator: + You can use one of the following typedefs for common character types + and the standard allocator: -+---------------+-----------------------------------------------------+ -| Type | Definition | -+===============+=====================================================+ -| MemoryWriter | BasicMemoryWriter> | -+---------------+-----------------------------------------------------+ -| WMemoryWriter | BasicMemoryWriter> | -+---------------+-----------------------------------------------------+ + +---------------+-----------------------------------------------------+ + | Type | Definition | + +===============+=====================================================+ + | MemoryWriter | BasicMemoryWriter> | + +---------------+-----------------------------------------------------+ + | WMemoryWriter | BasicMemoryWriter> | + +---------------+-----------------------------------------------------+ -**Example**:: + **Example**:: -MemoryWriter out; -out << "The answer is " << 42 << "\n"; -out.write("({:+f}, {:+f})", -3.14, 3.14); + MemoryWriter out; + out << "The answer is " << 42 << "\n"; + out.write("({:+f}, {:+f})", -3.14, 3.14); -This will write the following output to the ``out`` object: + This will write the following output to the ``out`` object: -.. code-block:: none + .. code-block:: none -The answer is 42 -(-3.140000, +3.140000) + The answer is 42 + (-3.140000, +3.140000) -The output can be converted to an ``std::string`` with ``out.str()`` or -accessed as a C string with ``out.c_str()``. -\endrst -*/ + The output can be converted to an ``std::string`` with ``out.str()`` or + accessed as a C string with ``out.c_str()``. + \endrst + */ template > class BasicMemoryWriter : public BasicWriter { @@ -3357,21 +3546,21 @@ public: #if FMT_USE_RVALUE_REFERENCES /** - \rst - Constructs a :class:`fmt::BasicMemoryWriter` object moving the content - of the other object to it. - \endrst - */ + \rst + Constructs a :class:`fmt::BasicMemoryWriter` object moving the content + of the other object to it. + \endrst + */ BasicMemoryWriter(BasicMemoryWriter &&other) : BasicWriter(buffer_), buffer_(std::move(other.buffer_)) { } /** - \rst - Moves the content of the other ``BasicMemoryWriter`` object to this one. - \endrst - */ + \rst + Moves the content of the other ``BasicMemoryWriter`` object to this one. + \endrst + */ BasicMemoryWriter &operator=(BasicMemoryWriter &&other) { buffer_ = std::move(other.buffer_); @@ -3384,25 +3573,25 @@ typedef BasicMemoryWriter MemoryWriter; typedef BasicMemoryWriter WMemoryWriter; /** -\rst -This class template provides operations for formatting and writing data -into a fixed-size array. For writing into a dynamically growing buffer -use :class:`fmt::BasicMemoryWriter`. + \rst + This class template provides operations for formatting and writing data + into a fixed-size array. For writing into a dynamically growing buffer + use :class:`fmt::BasicMemoryWriter`. -Any write method will throw ``std::runtime_error`` if the output doesn't fit -into the array. + Any write method will throw ``std::runtime_error`` if the output doesn't fit + into the array. -You can use one of the following typedefs for common character types: + You can use one of the following typedefs for common character types: -+--------------+---------------------------+ -| Type | Definition | -+==============+===========================+ -| ArrayWriter | BasicArrayWriter | -+--------------+---------------------------+ -| WArrayWriter | BasicArrayWriter | -+--------------+---------------------------+ -\endrst -*/ + +--------------+---------------------------+ + | Type | Definition | + +==============+===========================+ + | ArrayWriter | BasicArrayWriter | + +--------------+---------------------------+ + | WArrayWriter | BasicArrayWriter | + +--------------+---------------------------+ + \endrst + */ template class BasicArrayWriter : public BasicWriter { @@ -3411,45 +3600,28 @@ private: public: /** - \rst - Constructs a :class:`fmt::BasicArrayWriter` object for *array* of the - given size. - \endrst - */ + \rst + Constructs a :class:`fmt::BasicArrayWriter` object for *array* of the + given size. + \endrst + */ BasicArrayWriter(Char *array, std::size_t size) : BasicWriter(buffer_), buffer_(array, size) {} /** - \rst - Constructs a :class:`fmt::BasicArrayWriter` object for *array* of the - size known at compile time. - \endrst - */ + \rst + Constructs a :class:`fmt::BasicArrayWriter` object for *array* of the + size known at compile time. + \endrst + */ template - explicit BasicArrayWriter(Char(&array)[SIZE]) + explicit BasicArrayWriter(Char (&array)[SIZE]) : BasicWriter(buffer_), buffer_(array, SIZE) {} }; typedef BasicArrayWriter ArrayWriter; typedef BasicArrayWriter WArrayWriter; -// Formats a value. -template -void format(BasicFormatter &f, const Char *&format_str, const T &value) -{ - internal::MemoryBuffer buffer; - - internal::FormatBuf format_buf(buffer); - std::basic_ostream output(&format_buf); - output << value; - - BasicStringRef str(&buffer[0], format_buf.size()); - typedef internal::MakeValue< BasicFormatter > MakeValue; - internal::Arg arg = MakeValue(str); - arg.type = static_cast(MakeValue::type(str)); - format_str = f.format(format_str, arg); -} - // Reports a system error without throwing an exception. // Can be used to report errors from destructors. FMT_API void report_system_error(int error_code, @@ -3465,32 +3637,32 @@ private: public: /** - \rst - Constructs a :class:`fmt::WindowsError` object with the description - of the form + \rst + Constructs a :class:`fmt::WindowsError` object with the description + of the form - .. parsed-literal:: - **: ** + .. parsed-literal:: + **: ** - where ** is the formatted message and ** 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". + where ** is the formatted message and ** 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**:: + **Example**:: - // This throws a WindowsError 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::WindowsError(GetLastError(), - "cannot open file '{}'", filename); - } - \endrst + // This throws a WindowsError 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::WindowsError(GetLastError(), + "cannot open file '{}'", filename); + } + \endrst */ WindowsError(int error_code, CStringRef message) { @@ -3509,21 +3681,21 @@ FMT_API void report_windows_error(int error_code, enum Color { BLACK, RED, GREEN, YELLOW, BLUE, MAGENTA, CYAN, WHITE }; /** -Formats a string and prints it to stdout using ANSI escape sequences -to specify color (experimental). -Example: -print_colored(fmt::RED, "Elapsed time: {0:.2f} seconds", 1.23); -*/ + Formats a string and prints it to stdout using ANSI escape sequences + to specify color (experimental). + Example: + print_colored(fmt::RED, "Elapsed time: {0:.2f} seconds", 1.23); + */ FMT_API void print_colored(Color c, CStringRef format, ArgList args); /** -\rst -Formats arguments and returns the result as a string. + \rst + Formats arguments and returns the result as a string. -**Example**:: + **Example**:: -std::string message = format("The answer is {}", 42); -\endrst + std::string message = format("The answer is {}", 42); + \endrst */ inline std::string format(CStringRef format_str, ArgList args) { @@ -3540,90 +3712,36 @@ inline std::wstring format(WCStringRef format_str, ArgList args) } /** -\rst -Prints formatted data to the file *f*. + \rst + Prints formatted data to the file *f*. -**Example**:: + **Example**:: -print(stderr, "Don't {}!", "panic"); -\endrst -*/ + print(stderr, "Don't {}!", "panic"); + \endrst + */ FMT_API void print(std::FILE *f, CStringRef format_str, ArgList args); /** -\rst -Prints formatted data to ``stdout``. + \rst + Prints formatted data to ``stdout``. -**Example**:: + **Example**:: -print("Elapsed time: {0:.2f} seconds", 1.23); -\endrst -*/ + print("Elapsed time: {0:.2f} seconds", 1.23); + \endrst + */ FMT_API void print(CStringRef format_str, ArgList args); -template -void printf(BasicWriter &w, BasicCStringRef format, ArgList args) -{ - internal::PrintfFormatter(args).format(w, format); -} - /** -\rst -Formats arguments and returns the result as a string. - -**Example**:: - -std::string message = fmt::sprintf("The answer is %d", 42); -\endrst -*/ -inline std::string sprintf(CStringRef format, ArgList args) -{ - MemoryWriter w; - printf(w, format, args); - return w.str(); -} - -inline std::wstring sprintf(WCStringRef format, ArgList args) -{ - WMemoryWriter w; - printf(w, format, args); - return w.str(); -} - -/** -\rst -Prints formatted data to the file *f*. - -**Example**:: - -fmt::fprintf(stderr, "Don't %s!", "panic"); -\endrst -*/ -FMT_API int fprintf(std::FILE *f, CStringRef format, ArgList args); - -/** -\rst -Prints formatted data to ``stdout``. - -**Example**:: - -fmt::printf("Elapsed time: %.2f seconds", 1.23); -\endrst -*/ -inline int printf(CStringRef format, ArgList args) -{ - return fprintf(stdout, format, args); -} - -/** -Fast integer formatter. -*/ + Fast integer formatter. + */ class FormatInt { private: // Buffer should be large enough to hold all digits (digits10 + 1), // a sign and a null character. - enum { BUFFER_SIZE = std::numeric_limits::digits10 + 3 }; + enum {BUFFER_SIZE = std::numeric_limits::digits10 + 3}; mutable char buffer_[BUFFER_SIZE]; char *str_; @@ -3680,27 +3798,25 @@ public: explicit FormatInt(unsigned long value) : str_(format_decimal(value)) {} explicit FormatInt(ULongLong value) : str_(format_decimal(value)) {} - /** - Returns the number of characters written to the output buffer. - */ + /** Returns the number of characters written to the output buffer. */ std::size_t size() const { - return buffer_ - str_ + BUFFER_SIZE - 1; + return internal::to_unsigned(buffer_ - str_ + BUFFER_SIZE - 1); } /** - Returns a pointer to the output buffer content. No terminating null - character is appended. - */ + Returns a pointer to the output buffer content. No terminating null + character is appended. + */ const char *data() const { return str_; } /** - Returns a pointer to the output buffer content with terminating null - character appended. - */ + Returns a pointer to the output buffer content with terminating null + character appended. + */ const char *c_str() const { buffer_[BUFFER_SIZE - 1] = '\0'; @@ -3708,10 +3824,10 @@ public: } /** - \rst - Returns the content of the output buffer as an ``std::string``. - \endrst - */ + \rst + Returns the content of the output buffer as an ``std::string``. + \endrst + */ std::string str() const { return std::string(str_, size()); @@ -3724,7 +3840,8 @@ public: template inline void format_decimal(char *&buffer, T value) { - typename internal::IntTraits::MainType abs_value = value; + typedef typename internal::IntTraits::MainType MainType; + MainType abs_value = static_cast(value); if (internal::is_negative(value)) { *buffer++ = '-'; @@ -3748,15 +3865,15 @@ inline void format_decimal(char *&buffer, T value) } /** -\rst -Returns a named argument for formatting functions. + \rst + Returns a named argument for formatting functions. -**Example**:: + **Example**:: -print("Elapsed time: {s:.2f} seconds", arg("s", 1.23)); + print("Elapsed time: {s:.2f} seconds", arg("s", 1.23)); -\endrst -*/ + \endrst + */ template inline internal::NamedArg arg(StringRef name, const T &arg) { @@ -3795,7 +3912,6 @@ void arg(WStringRef, const internal::NamedArg&) FMT_DELETED_OR_UNDEFINED; #define FMT_ARG_N(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, N, ...) N #define FMT_RSEQ_N() 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0 -#define FMT_CONCAT(a, b) a##b #define FMT_FOR_EACH_(N, f, ...) \ FMT_EXPAND(FMT_CONCAT(FMT_FOR_EACH, N)(f, __VA_ARGS__)) #define FMT_FOR_EACH(f, ...) \ @@ -3809,10 +3925,11 @@ void arg(WStringRef, const internal::NamedArg&) FMT_DELETED_OR_UNDEFINED; template \ ReturnType func(FMT_FOR_EACH(FMT_ADD_ARG_NAME, __VA_ARGS__), \ const Args & ... args) { \ - typename fmt::internal::ArgArray::Type array; \ + typedef fmt::internal::ArgArray ArgArray; \ + typename ArgArray::Type array{ \ + ArgArray::template make >(args)...}; \ call(FMT_FOR_EACH(FMT_GET_ARG_NAME, __VA_ARGS__), \ - fmt::internal::make_arg_list< \ - fmt::BasicFormatter >(array, args...)); \ + fmt::ArgList(fmt::internal::make_type(args...), array)); \ } #else // Defines a wrapper for a function taking __VA_ARGS__ arguments @@ -3849,32 +3966,32 @@ void arg(WStringRef, const internal::NamedArg&) FMT_DELETED_OR_UNDEFINED; #endif // FMT_USE_VARIADIC_TEMPLATES /** -\rst -Defines a variadic function with the specified return type, function name -and argument types passed as variable arguments to this macro. + \rst + Defines a variadic function with the specified return type, function name + and argument types passed as variable arguments to this macro. -**Example**:: + **Example**:: -void print_error(const char *file, int line, const char *format, -fmt::ArgList args) { -fmt::print("{}: {}: ", file, line); -fmt::print(format, args); -} -FMT_VARIADIC(void, print_error, const char *, int, const char *) + void print_error(const char *file, int line, const char *format, + fmt::ArgList args) { + fmt::print("{}: {}: ", file, line); + fmt::print(format, args); + } + FMT_VARIADIC(void, print_error, const char *, int, const char *) -``FMT_VARIADIC`` is used for compatibility with legacy C++ compilers that -don't implement variadic templates. You don't have to use this macro if -you don't need legacy compiler support and can use variadic templates -directly:: + ``FMT_VARIADIC`` is used for compatibility with legacy C++ compilers that + don't implement variadic templates. You don't have to use this macro if + you don't need legacy compiler support and can use variadic templates + directly:: -template -void print_error(const char *file, int line, const char *format, -const Args & ... args) { -fmt::print("{}: {}: ", file, line); -fmt::print(format, args...); -} -\endrst -*/ + template + void print_error(const char *file, int line, const char *format, + const Args & ... args) { + fmt::print("{}: {}: ", file, line); + fmt::print(format, args...); + } + \endrst + */ #define FMT_VARIADIC(ReturnType, func, ...) \ FMT_VARIADIC_(char, ReturnType, func, return func, __VA_ARGS__) @@ -3886,19 +4003,19 @@ fmt::print(format, args...); #define FMT_CAPTURE_ARG_W_(id, index) ::fmt::arg(L###id, id) /** -\rst -Convenient macro to capture the arguments' names and values into several -``fmt::arg(name, value)``. + \rst + Convenient macro to capture the arguments' names and values into several + ``fmt::arg(name, value)``. -**Example**:: + **Example**:: -int x = 1, y = 2; -print("point: ({x}, {y})", FMT_CAPTURE(x, y)); -// same as: -// print("point: ({x}, {y})", arg("x", x), arg("y", y)); + int x = 1, y = 2; + print("point: ({x}, {y})", FMT_CAPTURE(x, y)); + // same as: + // print("point: ({x}, {y})", arg("x", x), arg("y", y)); -\endrst -*/ + \endrst + */ #define FMT_CAPTURE(...) FMT_FOR_EACH(FMT_CAPTURE_ARG_, __VA_ARGS__) #define FMT_CAPTURE_W(...) FMT_FOR_EACH(FMT_CAPTURE_ARG_W_, __VA_ARGS__) @@ -3909,26 +4026,7 @@ FMT_VARIADIC(std::string, format, CStringRef) FMT_VARIADIC_W(std::wstring, format, WCStringRef) FMT_VARIADIC(void, print, CStringRef) FMT_VARIADIC(void, print, std::FILE *, CStringRef) - FMT_VARIADIC(void, print_colored, Color, CStringRef) -FMT_VARIADIC(std::string, sprintf, CStringRef) -FMT_VARIADIC_W(std::wstring, sprintf, WCStringRef) -FMT_VARIADIC(int, printf, CStringRef) -FMT_VARIADIC(int, fprintf, std::FILE *, CStringRef) - -#if FMT_USE_IOSTREAMS -/** -\rst -Prints formatted data to the stream *os*. - -**Example**:: - -print(cerr, "Don't {}!", "panic"); -\endrst -*/ -FMT_API void print(std::ostream &os, CStringRef format_str, ArgList args); -FMT_VARIADIC(void, print, std::ostream &, CStringRef) -#endif namespace internal { @@ -3941,7 +4039,7 @@ inline bool is_name_start(Char c) // Parses an unsigned integer advancing s to the end of the parsed input. // This function assumes that the first character of s is a digit. template -int parse_nonnegative_int(const Char *&s) +unsigned parse_nonnegative_int(const Char *&s) { assert('0' <= *s && *s <= '9'); unsigned value = 0; @@ -3988,8 +4086,8 @@ void check_sign(const Char *&s, const Arg &arg) } } // namespace internal -template -inline internal::Arg BasicFormatter::get_arg( +template +inline internal::Arg BasicFormatter::get_arg( BasicStringRef arg_name, const char *&error) { if (check_no_auto_index(error)) @@ -4003,8 +4101,8 @@ inline internal::Arg BasicFormatter::get_arg( return internal::Arg(); } -template -inline internal::Arg BasicFormatter::parse_arg_index(const Char *&s) +template +inline internal::Arg BasicFormatter::parse_arg_index(const Char *&s) { const char *error = 0; internal::Arg arg = *s < '0' || *s > '9' ? @@ -4017,8 +4115,8 @@ inline internal::Arg BasicFormatter::parse_arg_index(const Char *&s) return arg; } -template -inline internal::Arg BasicFormatter::parse_arg_name(const Char *&s) +template +inline internal::Arg BasicFormatter::parse_arg_name(const Char *&s) { assert(internal::is_name_start(*s)); const Char *start = s; @@ -4035,9 +4133,8 @@ inline internal::Arg BasicFormatter::parse_arg_name(const Char *&s) return arg; } -// Should be after FormatSpec -template -const Char *BasicFormatter::format( +template +const Char *BasicFormatter::format( const Char *&format_str, const internal::Arg &arg) { using internal::Arg; @@ -4159,7 +4256,7 @@ const Char *BasicFormatter::format( default: FMT_THROW(FormatError("width is not integer")); } - if (value >(std::numeric_limits::max)()) + if (value > (std::numeric_limits::max)()) FMT_THROW(FormatError("number is too big")); spec.width_ = static_cast(value); } @@ -4202,7 +4299,7 @@ const Char *BasicFormatter::format( default: FMT_THROW(FormatError("precision is not integer")); } - if (value >(std::numeric_limits::max)()) + if (value > (std::numeric_limits::max)()) FMT_THROW(FormatError("number is too big")); spec.precision_ = static_cast(value); } @@ -4227,12 +4324,12 @@ const Char *BasicFormatter::format( FMT_THROW(FormatError("missing '}' in format string")); // Format argument. - internal::BasicArgFormatter(*this, spec, s - 1).visit(arg); + ArgFormatter(*this, spec, s - 1).visit(arg); return s; } -template -void BasicFormatter::format(BasicCStringRef format_str) +template +void BasicFormatter::format(BasicCStringRef format_str) { const Char *s = format_str.c_str(); const Char *start = s; @@ -4284,7 +4381,7 @@ struct UdlArg template NamedArg operator=(T &&value) const { - return { str, std::forward(value) }; + return {str, std::forward(value)}; } }; @@ -4294,45 +4391,45 @@ inline namespace literals { /** -\rst -C++11 literal equivalent of :func:`fmt::format`. + \rst + C++11 literal equivalent of :func:`fmt::format`. -**Example**:: + **Example**:: -using namespace fmt::literals; -std::string message = "The answer is {}"_format(42); -\endrst -*/ + using namespace fmt::literals; + std::string message = "The answer is {}"_format(42); + \endrst + */ inline internal::UdlFormat operator"" _format(const char *s, std::size_t) { - return { s }; + return {s}; } inline internal::UdlFormat operator"" _format(const wchar_t *s, std::size_t) { - return { s }; + return {s}; } /** -\rst -C++11 literal equivalent of :func:`fmt::arg`. + \rst + C++11 literal equivalent of :func:`fmt::arg`. -**Example**:: + **Example**:: -using namespace fmt::literals; -print("Elapsed time: {s:.2f} seconds", "s"_a=1.23); -\endrst -*/ + using namespace fmt::literals; + print("Elapsed time: {s:.2f} seconds", "s"_a=1.23); + \endrst + */ inline internal::UdlArg operator"" _a(const char *s, std::size_t) { - return { s }; + return {s}; } inline internal::UdlArg operator"" _a(const wchar_t *s, std::size_t) { - return { s }; + return {s}; } } // inline namespace literals @@ -4344,12 +4441,15 @@ operator"" _a(const wchar_t *s, std::size_t) # pragma GCC diagnostic pop #endif -#if defined(__clang__) && !defined(__INTEL_COMPILER) +#if defined(__clang__) && !defined(FMT_ICC_VERSION) # pragma clang diagnostic pop #endif #ifdef FMT_HEADER_ONLY +# define FMT_FUNC inline # include "format.cc" +#else +# define FMT_FUNC #endif -#endif // FMT_FORMAT_H_ \ No newline at end of file +#endif // FMT_FORMAT_H_ diff --git a/vendor/spdlog/spdlog/fmt/bundled/ostream.cc b/vendor/spdlog/spdlog/fmt/bundled/ostream.cc new file mode 100644 index 00000000..4d4a9a4d --- /dev/null +++ b/vendor/spdlog/spdlog/fmt/bundled/ostream.cc @@ -0,0 +1,36 @@ +/* + Formatting library for C++ - std::ostream support + + Copyright (c) 2012 - 2016, Victor Zverovich + All rights reserved. + + For the license information refer to format.h. + */ + +// Commented out by spdlog to use header only +// #include "fmt/ostream.h" + +namespace fmt { + +namespace internal { +FMT_FUNC void write(std::ostream &os, Writer &w) { + const char *data = w.data(); + typedef internal::MakeUnsigned::Type UnsignedStreamSize; + UnsignedStreamSize size = w.size(); + UnsignedStreamSize max_size = + internal::to_unsigned((std::numeric_limits::max)()); + do { + UnsignedStreamSize n = size <= max_size ? size : max_size; + os.write(data, static_cast(n)); + data += n; + size -= n; + } while (size != 0); +} +} + +FMT_FUNC void print(std::ostream &os, CStringRef format_str, ArgList args) { + MemoryWriter w; + w.write(format_str, args); + internal::write(os, w); +} +} // namespace fmt diff --git a/vendor/spdlog/spdlog/fmt/bundled/ostream.h b/vendor/spdlog/spdlog/fmt/bundled/ostream.h new file mode 100644 index 00000000..37e08f3b --- /dev/null +++ b/vendor/spdlog/spdlog/fmt/bundled/ostream.h @@ -0,0 +1,118 @@ +/* + Formatting library for C++ - std::ostream support + + Copyright (c) 2012 - 2016, Victor Zverovich + All rights reserved. + + For the license information refer to format.h. + */ + +#ifndef FMT_OSTREAM_H_ +#define FMT_OSTREAM_H_ + +// Commented out by spdlog to use header only +// #include "fmt/format.h" +#include + +namespace fmt +{ + +namespace internal +{ + +template +class FormatBuf : public std::basic_streambuf +{ +private: + typedef typename std::basic_streambuf::int_type int_type; + typedef typename std::basic_streambuf::traits_type traits_type; + + Buffer &buffer_; + Char *start_; + +public: + FormatBuf(Buffer &buffer) : buffer_(buffer), start_(&buffer[0]) + { + this->setp(start_, start_ + buffer_.capacity()); + } + + int_type overflow(int_type ch = traits_type::eof()) + { + if (!traits_type::eq_int_type(ch, traits_type::eof())) + { + size_t buf_size = size(); + buffer_.resize(buf_size); + buffer_.reserve(buf_size * 2); + + start_ = &buffer_[0]; + start_[buf_size] = traits_type::to_char_type(ch); + this->setp(start_+ buf_size + 1, start_ + buf_size * 2); + } + return ch; + } + + size_t size() const + { + return to_unsigned(this->pptr() - start_); + } +}; + +Yes &convert(std::ostream &); + +struct DummyStream : std::ostream +{ + DummyStream(); // Suppress a bogus warning in MSVC. + // Hide all operator<< overloads from std::ostream. + void operator<<(Null<>); +}; + +No &operator<<(std::ostream &, int); + +template +struct ConvertToIntImpl +{ + // Convert to int only if T doesn't have an overloaded operator<<. + enum + { + value = sizeof(convert(get() << get())) == sizeof(No) + }; +}; + +// Write the content of w to os. +void write(std::ostream &os, Writer &w); +} // namespace internal + +// Formats a value. +template +void format_arg(BasicFormatter &f, + const Char *&format_str, const T &value) +{ + internal::MemoryBuffer buffer; + + internal::FormatBuf format_buf(buffer); + std::basic_ostream output(&format_buf); + output << value; + + BasicStringRef str(&buffer[0], format_buf.size()); + typedef internal::MakeArg< BasicFormatter > MakeArg; + format_str = f.format(format_str, MakeArg(str)); +} + +/** + \rst + Prints formatted data to the stream *os*. + + **Example**:: + + print(cerr, "Don't {}!", "panic"); + \endrst + */ +FMT_API void print(std::ostream &os, CStringRef format_str, ArgList args); +FMT_VARIADIC(void, print, std::ostream &, CStringRef) +} // namespace fmt + +#ifdef FMT_HEADER_ONLY +# include "ostream.cc" +#endif + +#endif // FMT_OSTREAM_H_ diff --git a/vendor/spdlog/spdlog/fmt/bundled/printf.h b/vendor/spdlog/spdlog/fmt/bundled/printf.h new file mode 100644 index 00000000..77e68403 --- /dev/null +++ b/vendor/spdlog/spdlog/fmt/bundled/printf.h @@ -0,0 +1,642 @@ +/* + Formatting library for C++ + + Copyright (c) 2012 - 2016, Victor Zverovich + All rights reserved. + + For the license information refer to format.h. + */ + +#ifndef FMT_PRINTF_H_ +#define FMT_PRINTF_H_ + +#include // std::fill_n +#include // std::numeric_limits + +#include "fmt/ostream.h" + +namespace fmt +{ +namespace internal +{ + +// Checks if a value fits in int - used to avoid warnings about comparing +// signed and unsigned integers. +template +struct IntChecker +{ + template + static bool fits_in_int(T value) + { + unsigned max = std::numeric_limits::max(); + return value <= max; + } + static bool fits_in_int(bool) + { + return true; + } +}; + +template <> +struct IntChecker +{ + template + static bool fits_in_int(T value) + { + return value >= std::numeric_limits::min() && + value <= std::numeric_limits::max(); + } + static bool fits_in_int(int) + { + return true; + } +}; + +class PrecisionHandler : public ArgVisitor +{ +public: + void report_unhandled_arg() + { + FMT_THROW(FormatError("precision is not integer")); + } + + template + int visit_any_int(T value) + { + if (!IntChecker::is_signed>::fits_in_int(value)) + FMT_THROW(FormatError("number is too big")); + return static_cast(value); + } +}; + +// IsZeroInt::visit(arg) returns true iff arg is a zero integer. +class IsZeroInt : public ArgVisitor +{ +public: + template + bool visit_any_int(T value) + { + return value == 0; + } +}; + +template +struct is_same +{ + enum { value = 0 }; +}; + +template +struct is_same +{ + enum { value = 1 }; +}; + +// An argument visitor that converts an integer argument to T for printf, +// if T is an integral type. If T is void, the argument is converted to +// corresponding signed or unsigned type depending on the type specifier: +// 'd' and 'i' - signed, other - unsigned) +template +class ArgConverter : public ArgVisitor, void> +{ +private: + internal::Arg &arg_; + wchar_t type_; + + FMT_DISALLOW_COPY_AND_ASSIGN(ArgConverter); + +public: + ArgConverter(internal::Arg &arg, wchar_t type) + : arg_(arg), type_(type) {} + + void visit_bool(bool value) + { + if (type_ != 's') + visit_any_int(value); + } + + template + void visit_any_int(U value) + { + bool is_signed = type_ == 'd' || type_ == 'i'; + using internal::Arg; + typedef typename internal::Conditional< + is_same::value, U, T>::type TargetType; + if (sizeof(TargetType) <= sizeof(int)) + { + // Extra casts are used to silence warnings. + if (is_signed) + { + arg_.type = Arg::INT; + arg_.int_value = static_cast(static_cast(value)); + } + else + { + arg_.type = Arg::UINT; + typedef typename internal::MakeUnsigned::Type Unsigned; + arg_.uint_value = static_cast(static_cast(value)); + } + } + else + { + if (is_signed) + { + arg_.type = Arg::LONG_LONG; + // 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_.long_long_value = static_cast(value); + } + else + { + arg_.type = Arg::ULONG_LONG; + arg_.ulong_long_value = + static_cast::Type>(value); + } + } + } +}; + +// Converts an integer argument to char for printf. +class CharConverter : public ArgVisitor +{ +private: + internal::Arg &arg_; + + FMT_DISALLOW_COPY_AND_ASSIGN(CharConverter); + +public: + explicit CharConverter(internal::Arg &arg) : arg_(arg) {} + + template + void visit_any_int(T value) + { + arg_.type = internal::Arg::CHAR; + arg_.int_value = static_cast(value); + } +}; + +// Checks if an argument is a valid printf width specifier and sets +// left alignment if it is negative. +class WidthHandler : public ArgVisitor +{ +private: + FormatSpec &spec_; + + FMT_DISALLOW_COPY_AND_ASSIGN(WidthHandler); + +public: + explicit WidthHandler(FormatSpec &spec) : spec_(spec) {} + + void report_unhandled_arg() + { + FMT_THROW(FormatError("width is not integer")); + } + + template + unsigned visit_any_int(T value) + { + typedef typename internal::IntTraits::MainType UnsignedType; + UnsignedType width = static_cast(value); + if (internal::is_negative(value)) + { + spec_.align_ = ALIGN_LEFT; + width = 0 - width; + } + unsigned int_max = std::numeric_limits::max(); + if (width > int_max) + FMT_THROW(FormatError("number is too big")); + return static_cast(width); + } +}; +} // namespace internal + +/** + \rst + A ``printf`` argument formatter based on the `curiously recurring template + pattern `_. + + To use `~fmt::BasicPrintfArgFormatter` define a subclass that implements some + or all of the visit methods with the same signatures as the methods in + `~fmt::ArgVisitor`, for example, `~fmt::ArgVisitor::visit_int()`. + Pass the subclass as the *Impl* template parameter. When a formatting + function processes an argument, it will dispatch to a visit method + specific to the argument type. For example, if the argument type is + ``double`` then the `~fmt::ArgVisitor::visit_double()` method of a subclass + will be called. If the subclass doesn't contain a method with this signature, + then a corresponding method of `~fmt::BasicPrintfArgFormatter` or its + superclass will be called. + \endrst + */ +template +class BasicPrintfArgFormatter : public internal::ArgFormatterBase +{ +private: + void write_null_pointer() + { + this->spec().type_ = 0; + this->write("(nil)"); + } + + typedef internal::ArgFormatterBase Base; + +public: + /** + \rst + Constructs an argument formatter object. + *writer* is a reference to the output writer and *spec* contains format + specifier information for standard argument types. + \endrst + */ + BasicPrintfArgFormatter(BasicWriter &writer, FormatSpec &spec) + : internal::ArgFormatterBase(writer, spec) {} + + /** Formats an argument of type ``bool``. */ + void visit_bool(bool value) + { + FormatSpec &fmt_spec = this->spec(); + if (fmt_spec.type_ != 's') + return this->visit_any_int(value); + fmt_spec.type_ = 0; + this->write(value); + } + + /** Formats a character. */ + void visit_char(int value) + { + const FormatSpec &fmt_spec = this->spec(); + BasicWriter &w = this->writer(); + if (fmt_spec.type_ && fmt_spec.type_ != 'c') + w.write_int(value, fmt_spec); + typedef typename BasicWriter::CharPtr CharPtr; + CharPtr out = CharPtr(); + if (fmt_spec.width_ > 1) + { + Char fill = ' '; + out = w.grow_buffer(fmt_spec.width_); + if (fmt_spec.align_ != ALIGN_LEFT) + { + std::fill_n(out, fmt_spec.width_ - 1, fill); + out += fmt_spec.width_ - 1; + } + else + { + std::fill_n(out + 1, fmt_spec.width_ - 1, fill); + } + } + else + { + out = w.grow_buffer(1); + } + *out = static_cast(value); + } + + /** Formats a null-terminated C string. */ + void visit_cstring(const char *value) + { + if (value) + Base::visit_cstring(value); + else if (this->spec().type_ == 'p') + write_null_pointer(); + else + this->write("(null)"); + } + + /** Formats a pointer. */ + void visit_pointer(const void *value) + { + if (value) + return Base::visit_pointer(value); + this->spec().type_ = 0; + write_null_pointer(); + } + + /** Formats an argument of a custom (user-defined) type. */ + void visit_custom(internal::Arg::CustomValue c) + { + BasicFormatter formatter(ArgList(), this->writer()); + const Char format_str[] = {'}', 0}; + const Char *format = format_str; + c.format(&formatter, c.value, &format); + } +}; + +/** The default printf argument formatter. */ +template +class PrintfArgFormatter + : public BasicPrintfArgFormatter, Char> +{ +public: + /** Constructs an argument formatter object. */ + PrintfArgFormatter(BasicWriter &w, FormatSpec &s) + : BasicPrintfArgFormatter, Char>(w, s) {} +}; + +/** This template formats data and writes the output to a writer. */ +template > +class PrintfFormatter : private internal::FormatterBase +{ +private: + BasicWriter &writer_; + + void parse_flags(FormatSpec &spec, const Char *&s); + + // Returns the argument with specified index or, if arg_index is equal + // to the maximum unsigned value, the next argument. + internal::Arg get_arg( + const Char *s, + unsigned arg_index = (std::numeric_limits::max)()); + + // Parses argument index, flags and width and returns the argument index. + unsigned parse_header(const Char *&s, FormatSpec &spec); + +public: + /** + \rst + Constructs a ``PrintfFormatter`` object. References to the arguments and + the writer are stored in the formatter object so make sure they have + appropriate lifetimes. + \endrst + */ + explicit PrintfFormatter(const ArgList &args, BasicWriter &w) + : FormatterBase(args), writer_(w) {} + + /** Formats stored arguments and writes the output to the writer. */ + FMT_API void format(BasicCStringRef format_str); +}; + +template +void PrintfFormatter::parse_flags(FormatSpec &spec, const Char *&s) +{ + for (;;) + { + switch (*s++) + { + case '-': + spec.align_ = ALIGN_LEFT; + break; + case '+': + spec.flags_ |= SIGN_FLAG | PLUS_FLAG; + break; + case '0': + spec.fill_ = '0'; + break; + case ' ': + spec.flags_ |= SIGN_FLAG; + break; + case '#': + spec.flags_ |= HASH_FLAG; + break; + default: + --s; + return; + } + } +} + +template +internal::Arg PrintfFormatter::get_arg(const Char *s, + unsigned arg_index) +{ + (void)s; + const char *error = 0; + internal::Arg arg = arg_index == std::numeric_limits::max() ? + next_arg(error) : FormatterBase::get_arg(arg_index - 1, error); + if (error) + FMT_THROW(FormatError(!*s ? "invalid format string" : error)); + return arg; +} + +template +unsigned PrintfFormatter::parse_header( + const Char *&s, FormatSpec &spec) +{ + unsigned arg_index = std::numeric_limits::max(); + Char c = *s; + if (c >= '0' && c <= '9') + { + // Parse an argument index (if followed by '$') or a width possibly + // preceded with '0' flag(s). + unsigned value = internal::parse_nonnegative_int(s); + if (*s == '$') // value is an argument index + { + ++s; + arg_index = value; + } + else + { + if (c == '0') + spec.fill_ = '0'; + if (value != 0) + { + // Nonzero value means that we parsed width and don't need to + // parse it or flags again, so return now. + spec.width_ = value; + return arg_index; + } + } + } + parse_flags(spec, s); + // Parse width. + if (*s >= '0' && *s <= '9') + { + spec.width_ = internal::parse_nonnegative_int(s); + } + else if (*s == '*') + { + ++s; + spec.width_ = internal::WidthHandler(spec).visit(get_arg(s)); + } + return arg_index; +} + +template +void PrintfFormatter::format(BasicCStringRef format_str) +{ + const Char *start = format_str.c_str(); + const Char *s = start; + while (*s) + { + Char c = *s++; + if (c != '%') continue; + if (*s == c) + { + write(writer_, start, s); + start = ++s; + continue; + } + write(writer_, start, s - 1); + + FormatSpec spec; + spec.align_ = ALIGN_RIGHT; + + // Parse argument index, flags and width. + unsigned arg_index = parse_header(s, spec); + + // Parse precision. + if (*s == '.') + { + ++s; + if ('0' <= *s && *s <= '9') + { + spec.precision_ = static_cast(internal::parse_nonnegative_int(s)); + } + else if (*s == '*') + { + ++s; + spec.precision_ = internal::PrecisionHandler().visit(get_arg(s)); + } + } + + using internal::Arg; + Arg arg = get_arg(s, arg_index); + if (spec.flag(HASH_FLAG) && internal::IsZeroInt().visit(arg)) + spec.flags_ &= ~internal::to_unsigned(HASH_FLAG); + if (spec.fill_ == '0') + { + if (arg.type <= Arg::LAST_NUMERIC_TYPE) + spec.align_ = ALIGN_NUMERIC; + else + spec.fill_ = ' '; // Ignore '0' flag for non-numeric types. + } + + // Parse length and convert the argument to the required type. + using internal::ArgConverter; + switch (*s++) + { + case 'h': + if (*s == 'h') + ArgConverter(arg, *++s).visit(arg); + else + ArgConverter(arg, *s).visit(arg); + break; + case 'l': + if (*s == 'l') + ArgConverter(arg, *++s).visit(arg); + else + ArgConverter(arg, *s).visit(arg); + break; + case 'j': + ArgConverter(arg, *s).visit(arg); + break; + case 'z': + ArgConverter(arg, *s).visit(arg); + break; + case 't': + ArgConverter(arg, *s).visit(arg); + break; + case 'L': + // printf produces garbage when 'L' is omitted for long double, no + // need to do the same. + break; + default: + --s; + ArgConverter(arg, *s).visit(arg); + } + + // Parse type. + if (!*s) + FMT_THROW(FormatError("invalid format string")); + spec.type_ = static_cast(*s++); + if (arg.type <= Arg::LAST_INTEGER_TYPE) + { + // Normalize type. + switch (spec.type_) + { + case 'i': + case 'u': + spec.type_ = 'd'; + break; + case 'c': + // TODO: handle wchar_t + internal::CharConverter(arg).visit(arg); + break; + } + } + + start = s; + + // Format argument. + AF(writer_, spec).visit(arg); + } + write(writer_, start, s); +} + +template +void printf(BasicWriter &w, BasicCStringRef format, ArgList args) +{ + PrintfFormatter(args, w).format(format); +} + +/** + \rst + Formats arguments and returns the result as a string. + + **Example**:: + + std::string message = fmt::sprintf("The answer is %d", 42); + \endrst +*/ +inline std::string sprintf(CStringRef format, ArgList args) +{ + MemoryWriter w; + printf(w, format, args); + return w.str(); +} +FMT_VARIADIC(std::string, sprintf, CStringRef) + +inline std::wstring sprintf(WCStringRef format, ArgList args) +{ + WMemoryWriter w; + printf(w, format, args); + return w.str(); +} +FMT_VARIADIC_W(std::wstring, sprintf, WCStringRef) + +/** + \rst + Prints formatted data to the file *f*. + + **Example**:: + + fmt::fprintf(stderr, "Don't %s!", "panic"); + \endrst + */ +FMT_API int fprintf(std::FILE *f, CStringRef format, ArgList args); +FMT_VARIADIC(int, fprintf, std::FILE *, CStringRef) + +/** + \rst + Prints formatted data to ``stdout``. + + **Example**:: + + fmt::printf("Elapsed time: %.2f seconds", 1.23); + \endrst + */ +inline int printf(CStringRef format, ArgList args) +{ + return fprintf(stdout, format, args); +} +FMT_VARIADIC(int, printf, CStringRef) + +/** + \rst + Prints formatted data to the stream *os*. + + **Example**:: + + fprintf(cerr, "Don't %s!", "panic"); + \endrst + */ +inline int fprintf(std::ostream &os, CStringRef format_str, ArgList args) +{ + MemoryWriter w; + printf(w, format_str, args); + internal::write(os, w); + return static_cast(w.size()); +} +FMT_VARIADIC(int, fprintf, std::ostream &, CStringRef) +} // namespace fmt + +#endif // FMT_PRINTF_H_ diff --git a/vendor/spdlog/spdlog/fmt/fmt.h b/vendor/spdlog/spdlog/fmt/fmt.h new file mode 100644 index 00000000..dd035fd2 --- /dev/null +++ b/vendor/spdlog/spdlog/fmt/fmt.h @@ -0,0 +1,28 @@ +// +// Copyright(c) 2016 Gabi Melman. +// Distributed under the MIT License (http://opensource.org/licenses/MIT) +// + +#pragma once + +// +// Include a bundled header-only copy of fmtlib or an external one. +// By default spdlog include its own copy. +// + +#if !defined(SPDLOG_FMT_EXTERNAL) + +#ifndef FMT_HEADER_ONLY +#define FMT_HEADER_ONLY +#endif +#ifndef FMT_USE_WINDOWS_H +#define FMT_USE_WINDOWS_H 0 +#endif +#include + +#else //external fmtlib + +#include + +#endif + diff --git a/vendor/spdlog/spdlog/fmt/ostr.h b/vendor/spdlog/spdlog/fmt/ostr.h new file mode 100644 index 00000000..7a651865 --- /dev/null +++ b/vendor/spdlog/spdlog/fmt/ostr.h @@ -0,0 +1,17 @@ +// +// Copyright(c) 2016 Gabi Melman. +// Distributed under the MIT License (http://opensource.org/licenses/MIT) +// + +#pragma once + +// include external or bundled copy of fmtlib's ostream support +// +#if !defined(SPDLOG_FMT_EXTERNAL) +#include +#include +#else +#include +#endif + + diff --git a/vendor/spdlog/spdlog/formatter.h b/vendor/spdlog/spdlog/formatter.h index cf0af012..0ffcec03 100644 --- a/vendor/spdlog/spdlog/formatter.h +++ b/vendor/spdlog/spdlog/formatter.h @@ -5,7 +5,12 @@ #pragma once -#include "details/log_msg.h" +#include + +#include +#include +#include + namespace spdlog { namespace details @@ -36,5 +41,5 @@ private: }; } -#include "details/pattern_formatter_impl.h" +#include diff --git a/vendor/spdlog/spdlog/logger.h b/vendor/spdlog/spdlog/logger.h index a756ea3a..e998999e 100644 --- a/vendor/spdlog/spdlog/logger.h +++ b/vendor/spdlog/spdlog/logger.h @@ -12,19 +12,16 @@ // 2. Format the message using the formatter function // 3. Pass the formatted message to its sinks to performa the actual logging -#include -#include -#include "sinks/base_sink.h" -#include "common.h" +#include +#include + +#include +#include +#include namespace spdlog { -namespace details -{ -class line_logger; -} - class logger { public: @@ -37,77 +34,61 @@ public: logger(const logger&) = delete; logger& operator=(const logger&) = delete; + + template void log(level::level_enum lvl, const char* fmt, const Args&... args); + template void log(level::level_enum lvl, const char* msg); + template void trace(const char* fmt, const Args&... args); + template void debug(const char* fmt, const Args&... args); + template void info(const char* fmt, const Args&... args); + template void warn(const char* fmt, const Args&... args); + template void error(const char* fmt, const Args&... args); + template void critical(const char* fmt, const Args&... args); + + template void log(level::level_enum lvl, const T&); + template void trace(const T&); + template void debug(const T&); + template void info(const T&); + template void warn(const T&); + template void error(const T&); + template void critical(const T&); + + bool should_log(level::level_enum) const; void set_level(level::level_enum); level::level_enum level() const; - const std::string& name() const; - bool should_log(level::level_enum) const; - - // logger.info(cppformat_string, arg1, arg2, arg3, ...) call style - template details::line_logger trace(const char* fmt, const Args&... args); - template details::line_logger debug(const char* fmt, const Args&... args); - template details::line_logger info(const char* fmt, const Args&... args); - template details::line_logger notice(const char* fmt, const Args&... args); - template details::line_logger warn(const char* fmt, const Args&... args); - template details::line_logger error(const char* fmt, const Args&... args); - template details::line_logger critical(const char* fmt, const Args&... args); - template details::line_logger alert(const char* fmt, const Args&... args); - template details::line_logger emerg(const char* fmt, const Args&... args); - - - // logger.info(msg) << ".." call style - template details::line_logger trace(const T&); - template details::line_logger debug(const T&); - template details::line_logger info(const T&); - template details::line_logger notice(const T&); - template details::line_logger warn(const T&); - template details::line_logger error(const T&); - template details::line_logger critical(const T&); - template details::line_logger alert(const T&); - template details::line_logger emerg(const T&); - - - // logger.info() << ".." call style - details::line_logger trace(); - details::line_logger debug(); - details::line_logger info(); - details::line_logger notice(); - details::line_logger warn(); - details::line_logger error(); - details::line_logger critical(); - details::line_logger alert(); - details::line_logger emerg(); - - - - // Create log message with the given level, no matter what is the actual logger's level - template - details::line_logger force_log(level::level_enum lvl, const char* fmt, const Args&... args); - - // Set the format of the log messages from this logger void set_pattern(const std::string&); void set_formatter(formatter_ptr); + // error handler + void set_error_handler(log_err_handler); + log_err_handler error_handler(); + + // automatically call flush() if message level >= log_level + void flush_on(level::level_enum log_level); + virtual void flush(); protected: - virtual void _log_msg(details::log_msg&); + virtual void _sink_it(details::log_msg&); virtual void _set_pattern(const std::string&); virtual void _set_formatter(formatter_ptr); - details::line_logger _log_if_enabled(level::level_enum lvl); - template - details::line_logger _log_if_enabled(level::level_enum lvl, const char* fmt, const Args&... args); - template - inline details::line_logger _log_if_enabled(level::level_enum lvl, const T& msg); + // default error handler: print the error to stderr with the max rate of 1 message/minute + virtual void _default_err_handler(const std::string &msg); - friend details::line_logger; - std::string _name; + // return true if the given message level should trigger a flush + bool _should_flush_on(const details::log_msg&); + + const std::string _name; std::vector _sinks; formatter_ptr _formatter; - std::atomic_int _level; - + spdlog::level_t _level; + spdlog::level_t _flush_level; + log_err_handler _err_handler; + std::atomic _last_err_time; }; } -#include "./details/logger_impl.h" +#include + + diff --git a/vendor/spdlog/spdlog/sinks/android_sink.h b/vendor/spdlog/spdlog/sinks/android_sink.h index d872c1ba..d8c97e03 100644 --- a/vendor/spdlog/spdlog/sinks/android_sink.h +++ b/vendor/spdlog/spdlog/sinks/android_sink.h @@ -7,49 +7,43 @@ #if defined(__ANDROID__) -#include -#include "base_sink.h" -#include "../details/null_mutex.h" +#include +#include +#include #include namespace spdlog { namespace sinks { + /* * Android sink (logging using __android_log_write) +* __android_log_write is thread-safe. No lock is needed. */ -template -class base_android_sink : public base_sink < Mutex > +class android_sink : public sink { public: - explicit base_android_sink(std::string tag="spdlog"): _tag(tag) + explicit android_sink(const std::string& tag = "spdlog"): _tag(tag) {} + + void log(const details::log_msg& msg) override { + const android_LogPriority priority = convert_to_android(msg.level); + // See system/core/liblog/logger_write.c for explanation of return value + const int ret = __android_log_write( + priority, _tag.c_str(), msg.formatted.c_str() + ); + if (ret < 0) + { + throw spdlog_ex("__android_log_write() failed", ret); + } } void flush() override { } -protected: - void _sink_it(const details::log_msg& msg) override - { - const android_LogPriority priority = convert_to_android(msg.level); - const int expected_size = msg.formatted.size(); - const int size = __android_log_write( - priority, _tag.c_str(), msg.formatted.c_str() - ); - if (size > expected_size) - { - // Will write a little bit more than original message - } - else - { - throw spdlog_ex("Send to Android logcat failed"); - } - } - private: static android_LogPriority convert_to_android(spdlog::level::level_enum level) { @@ -61,29 +55,20 @@ private: return ANDROID_LOG_DEBUG; case spdlog::level::info: return ANDROID_LOG_INFO; - case spdlog::level::notice: - return ANDROID_LOG_INFO; case spdlog::level::warn: return ANDROID_LOG_WARN; case spdlog::level::err: return ANDROID_LOG_ERROR; case spdlog::level::critical: return ANDROID_LOG_FATAL; - case spdlog::level::alert: - return ANDROID_LOG_FATAL; - case spdlog::level::emerg: - return ANDROID_LOG_FATAL; default: - throw spdlog_ex("Incorrect level value"); + return ANDROID_LOG_DEFAULT; } } std::string _tag; }; -typedef base_android_sink android_sink_mt; -typedef base_android_sink android_sink_st; - } } diff --git a/vendor/spdlog/spdlog/sinks/ansicolor_sink.h b/vendor/spdlog/spdlog/sinks/ansicolor_sink.h new file mode 100644 index 00000000..a3b4292d --- /dev/null +++ b/vendor/spdlog/spdlog/sinks/ansicolor_sink.h @@ -0,0 +1,116 @@ +// +// Copyright(c) 2016 Kevin M. Godby (a modified version by spdlog). +// Distributed under the MIT License (http://opensource.org/licenses/MIT) +// + +#pragma once + +#include +#include + +#include +#include + +namespace spdlog +{ +namespace sinks +{ + +/** + * @brief The ansi_color_sink is a decorator around another sink and prefixes + * the output with an ANSI escape sequence color code depending on the severity + * of the message. + */ +class ansicolor_sink : public sink +{ +public: + ansicolor_sink(sink_ptr wrapped_sink); + virtual ~ansicolor_sink(); + + ansicolor_sink(const ansicolor_sink& other) = delete; + ansicolor_sink& operator=(const ansicolor_sink& other) = delete; + + virtual void log(const details::log_msg& msg) override; + virtual void flush() override; + + void set_color(level::level_enum level, const std::string& color); + + /// Formatting codes + const std::string reset = "\033[00m"; + const std::string bold = "\033[1m"; + const std::string dark = "\033[2m"; + const std::string underline = "\033[4m"; + const std::string blink = "\033[5m"; + const std::string reverse = "\033[7m"; + const std::string concealed = "\033[8m"; + + // Foreground colors + const std::string grey = "\033[30m"; + const std::string red = "\033[31m"; + const std::string green = "\033[32m"; + const std::string yellow = "\033[33m"; + const std::string blue = "\033[34m"; + const std::string magenta = "\033[35m"; + const std::string cyan = "\033[36m"; + const std::string white = "\033[37m"; + + /// Background colors + const std::string on_grey = "\033[40m"; + const std::string on_red = "\033[41m"; + const std::string on_green = "\033[42m"; + const std::string on_yellow = "\033[43m"; + const std::string on_blue = "\033[44m"; + const std::string on_magenta = "\033[45m"; + const std::string on_cyan = "\033[46m"; + const std::string on_white = "\033[47m"; + + +protected: + sink_ptr sink_; + std::map colors_; +}; + +inline ansicolor_sink::ansicolor_sink(sink_ptr wrapped_sink) : sink_(wrapped_sink) +{ + colors_[level::trace] = cyan; + colors_[level::debug] = cyan; + colors_[level::info] = bold; + colors_[level::warn] = yellow + bold; + colors_[level::err] = red + bold; + colors_[level::critical] = bold + on_red; + colors_[level::off] = reset; +} + +inline void ansicolor_sink::log(const details::log_msg& msg) +{ + // Wrap the originally formatted message in color codes + const std::string& prefix = colors_[msg.level]; + const std::string& s = msg.formatted.str(); + const std::string& suffix = reset; + details::log_msg m; + m.level = msg.level; + m.logger_name = msg.logger_name; + m.time = msg.time; + m.thread_id = msg.thread_id; + m.formatted << prefix << s << suffix; + sink_->log(m); +} + +inline void ansicolor_sink::flush() +{ + sink_->flush(); +} + +inline void ansicolor_sink::set_color(level::level_enum level, const std::string& color) +{ + colors_[level] = color; +} + +inline ansicolor_sink::~ansicolor_sink() +{ + flush(); +} + +} // namespace sinks +} // namespace spdlog + diff --git a/vendor/spdlog/spdlog/sinks/base_sink.h b/vendor/spdlog/spdlog/sinks/base_sink.h index e5874823..7f1a31db 100644 --- a/vendor/spdlog/spdlog/sinks/base_sink.h +++ b/vendor/spdlog/spdlog/sinks/base_sink.h @@ -7,17 +7,15 @@ // // base sink templated over a mutex (either dummy or realy) // concrete implementation should only overrid the _sink_it method. -// all locking is taken care of here so no locking needed by the implementors.. +// all locking is taken care of here so no locking needed by the implementers.. // -#include -#include -#include -#include "./sink.h" -#include "../formatter.h" -#include "../common.h" -#include "../details/log_msg.h" +#include +#include +#include +#include +#include namespace spdlog { diff --git a/vendor/spdlog/spdlog/sinks/dist_sink.h b/vendor/spdlog/spdlog/sinks/dist_sink.h index dec24e39..cef08bfb 100644 --- a/vendor/spdlog/spdlog/sinks/dist_sink.h +++ b/vendor/spdlog/spdlog/sinks/dist_sink.h @@ -5,15 +5,17 @@ #pragma once -#include -#include -#include -#include +#include +#include +#include +#include -#include "../details/log_msg.h" -#include "../details/null_mutex.h" -#include "./base_sink.h" -#include "./sink.h" +#include +#include +#include +#include + +// Distribution sink (mux). Stores a vector of sinks which get called when log is called namespace spdlog { @@ -29,40 +31,37 @@ public: virtual ~dist_sink() = default; protected: + std::vector> _sinks; + void _sink_it(const details::log_msg& msg) override { - for (auto iter = _sinks.begin(); iter != _sinks.end(); iter++) - (*iter)->log(msg); + for (auto &sink : _sinks) + { + if( sink->should_log( msg.level)) + { + sink->log(msg); + } + } } - std::vector> _sinks; - public: void flush() override { std::lock_guard lock(base_sink::_mutex); - for (auto iter = _sinks.begin(); iter != _sinks.end(); iter++) - (*iter)->flush(); + for (auto &sink : _sinks) + sink->flush(); } void add_sink(std::shared_ptr sink) { std::lock_guard lock(base_sink::_mutex); - if (sink && - _sinks.end() == std::find(_sinks.begin(), _sinks.end(), sink)) - { - _sinks.push_back(sink); - } + _sinks.push_back(sink); } void remove_sink(std::shared_ptr sink) { std::lock_guard lock(base_sink::_mutex); - auto pos = std::find(_sinks.begin(), _sinks.end(), sink); - if (pos != _sinks.end()) - { - _sinks.erase(pos); - } + _sinks.erase(std::remove(_sinks.begin(), _sinks.end(), sink), _sinks.end()); } }; diff --git a/vendor/spdlog/spdlog/sinks/file_sinks.h b/vendor/spdlog/spdlog/sinks/file_sinks.h index 136f63e2..fad21fe1 100644 --- a/vendor/spdlog/spdlog/sinks/file_sinks.h +++ b/vendor/spdlog/spdlog/sinks/file_sinks.h @@ -5,28 +5,33 @@ #pragma once +#include +#include +#include +#include + +#include +#include +#include +#include #include -#include "base_sink.h" -#include "../details/null_mutex.h" -#include "../details/file_helper.h" -#include "../details/format.h" +#include +#include namespace spdlog { namespace sinks { /* -* Trivial file sink with single file as target -*/ + * Trivial file sink with single file as target + */ template class simple_file_sink : public base_sink < Mutex > { public: - explicit simple_file_sink(const std::string &filename, - bool force_flush = false) : - _file_helper(force_flush) + explicit simple_file_sink(const filename_t &filename, bool truncate = false) { - _file_helper.open(filename); + _file_helper.open(filename, truncate); } void flush() override { @@ -46,21 +51,20 @@ typedef simple_file_sink simple_file_sink_mt; typedef simple_file_sink simple_file_sink_st; /* -* Rotating file sink based on size -*/ + * Rotating file sink based on size + */ template class rotating_file_sink : public base_sink < Mutex > { public: - rotating_file_sink(const std::string &base_filename, const std::string &extension, - std::size_t max_size, std::size_t max_files, - bool force_flush = false) : + rotating_file_sink(const filename_t &base_filename, const filename_t &extension, + std::size_t max_size, std::size_t max_files ) : _base_filename(base_filename), _extension(extension), _max_size(max_size), _max_files(max_files), _current_size(0), - _file_helper(force_flush) + _file_helper() { _file_helper.open(calc_filename(_base_filename, 0, _extension)); _current_size = _file_helper.size(); //expensive. called only once @@ -84,13 +88,13 @@ protected: } private: - static std::string calc_filename(const std::string& filename, std::size_t index, const std::string& extension) + static filename_t calc_filename(const filename_t& filename, std::size_t index, const filename_t& extension) { - fmt::MemoryWriter w; + std::conditional::value, fmt::MemoryWriter, fmt::WMemoryWriter>::type w; if (index) - w.write("{}.{}.{}", filename, index, extension); + w.write(SPDLOG_FILENAME_T("{}.{}.{}"), filename, index, extension); else - w.write("{}.{}", filename, extension); + w.write(SPDLOG_FILENAME_T("{}.{}"), filename, extension); return w.str(); } @@ -102,28 +106,29 @@ private: void _rotate() { + using details::os::filename_to_str; _file_helper.close(); for (auto i = _max_files; i > 0; --i) { - std::string src = calc_filename(_base_filename, i - 1, _extension); - std::string target = calc_filename(_base_filename, i, _extension); + filename_t src = calc_filename(_base_filename, i - 1, _extension); + filename_t target = calc_filename(_base_filename, i, _extension); if (details::file_helper::file_exists(target)) { - if (std::remove(target.c_str()) != 0) + if (details::os::remove(target) != 0) { - throw spdlog_ex("rotating_file_sink: failed removing " + target); + throw spdlog_ex("rotating_file_sink: failed removing " + filename_to_str(target), errno); } } - if (details::file_helper::file_exists(src) && std::rename(src.c_str(), target.c_str())) + if (details::file_helper::file_exists(src) && details::os::rename(src, target)) { - throw spdlog_ex("rotating_file_sink: failed renaming " + src + " to " + target); + throw spdlog_ex("rotating_file_sink: failed renaming " + filename_to_str(src) + " to " + filename_to_str(target), errno); } } _file_helper.reopen(true); } - std::string _base_filename; - std::string _extension; + filename_t _base_filename; + filename_t _extension; std::size_t _max_size; std::size_t _max_files; std::size_t _current_size; @@ -134,28 +139,56 @@ typedef rotating_file_sink rotating_file_sink_mt; typedef rotating_file_sinkrotating_file_sink_st; /* -* Rotating file sink based on date. rotates at midnight -*/ -template + * Default generator of daily log file names. + */ +struct default_daily_file_name_calculator +{ + // Create filename for the form basename.YYYY-MM-DD_hh-mm.extension + static filename_t calc_filename(const filename_t& basename, const filename_t& extension) + { + std::tm tm = spdlog::details::os::localtime(); + std::conditional::value, fmt::MemoryWriter, fmt::WMemoryWriter>::type w; + w.write(SPDLOG_FILENAME_T("{}_{:04d}-{:02d}-{:02d}_{:02d}-{:02d}.{}"), basename, tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, tm.tm_hour, tm.tm_min, extension); + return w.str(); + } +}; + +/* + * Generator of daily log file names in format basename.YYYY-MM-DD.extension + */ +struct dateonly_daily_file_name_calculator +{ + // Create filename for the form basename.YYYY-MM-DD.extension + static filename_t calc_filename(const filename_t& basename, const filename_t& extension) + { + std::tm tm = spdlog::details::os::localtime(); + std::conditional::value, fmt::MemoryWriter, fmt::WMemoryWriter>::type w; + w.write(SPDLOG_FILENAME_T("{}_{:04d}-{:02d}-{:02d}.{}"), basename, tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, extension); + return w.str(); + } +}; + +/* + * Rotating file sink based on date. rotates at midnight + */ +template class daily_file_sink :public base_sink < Mutex > { public: //create daily file sink which rotates on given time daily_file_sink( - const std::string& base_filename, - const std::string& extension, + const filename_t& base_filename, + const filename_t& extension, int rotation_hour, - int rotation_minute, - bool force_flush = false) : _base_filename(base_filename), + int rotation_minute) : _base_filename(base_filename), _extension(extension), _rotation_h(rotation_hour), - _rotation_m(rotation_minute), - _file_helper(force_flush) + _rotation_m(rotation_minute) { if (rotation_hour < 0 || rotation_hour > 23 || rotation_minute < 0 || rotation_minute > 59) throw spdlog_ex("daily_file_sink: Invalid rotation time in ctor"); _rotation_tp = _next_rotation_tp(); - _file_helper.open(calc_filename(_base_filename, _extension)); + _file_helper.open(FileNameCalc::calc_filename(_base_filename, _extension)); } void flush() override @@ -168,7 +201,7 @@ protected: { if (std::chrono::system_clock::now() >= _rotation_tp) { - _file_helper.open(calc_filename(_base_filename, _extension)); + _file_helper.open(FileNameCalc::calc_filename(_base_filename, _extension)); _rotation_tp = _next_rotation_tp(); } _file_helper.write(msg); @@ -177,8 +210,7 @@ protected: private: std::chrono::system_clock::time_point _next_rotation_tp() { - using namespace std::chrono; - auto now = system_clock::now(); + auto now = std::chrono::system_clock::now(); time_t tnow = std::chrono::system_clock::to_time_t(now); tm date = spdlog::details::os::localtime(tnow); date.tm_hour = _rotation_h; @@ -188,20 +220,11 @@ private: if (rotation_time > now) return rotation_time; else - return system_clock::time_point(rotation_time + hours(24)); + return std::chrono::system_clock::time_point(rotation_time + std::chrono::hours(24)); } - //Create filename for the form basename.YYYY-MM-DD.extension - static std::string calc_filename(const std::string& basename, const std::string& extension) - { - std::tm tm = spdlog::details::os::localtime(); - fmt::MemoryWriter w; - w.write("{}_{:04d}-{:02d}-{:02d}_{:02d}-{:02d}.{}", basename, tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, tm.tm_hour, tm.tm_min, extension); - return w.str(); - } - - std::string _base_filename; - std::string _extension; + filename_t _base_filename; + filename_t _extension; int _rotation_h; int _rotation_m; std::chrono::system_clock::time_point _rotation_tp; diff --git a/vendor/spdlog/spdlog/sinks/msvc_sink.h b/vendor/spdlog/spdlog/sinks/msvc_sink.h new file mode 100644 index 00000000..16342ca2 --- /dev/null +++ b/vendor/spdlog/spdlog/sinks/msvc_sink.h @@ -0,0 +1,50 @@ +// +// Copyright(c) 2016 Alexander Dalshov. +// Distributed under the MIT License (http://opensource.org/licenses/MIT) +// + +#pragma once + +#if defined(_MSC_VER) + +#include +#include + +#include + +#include +#include + +namespace spdlog +{ +namespace sinks +{ +/* +* MSVC sink (logging using OutputDebugStringA) +*/ +template +class msvc_sink : public base_sink < Mutex > +{ +public: + explicit msvc_sink() + { + } + + void flush() override + { + } + +protected: + void _sink_it(const details::log_msg& msg) override + { + OutputDebugStringA(msg.formatted.c_str()); + } +}; + +typedef msvc_sink msvc_sink_mt; +typedef msvc_sink msvc_sink_st; + +} +} + +#endif diff --git a/vendor/spdlog/spdlog/sinks/null_sink.h b/vendor/spdlog/spdlog/sinks/null_sink.h index ea32df48..68bd9c94 100644 --- a/vendor/spdlog/spdlog/sinks/null_sink.h +++ b/vendor/spdlog/spdlog/sinks/null_sink.h @@ -4,10 +4,11 @@ // #pragma once -#include -#include "./base_sink.h" -#include "../details/null_mutex.h" +#include +#include + +#include namespace spdlog { diff --git a/vendor/spdlog/spdlog/sinks/ostream_sink.h b/vendor/spdlog/spdlog/sinks/ostream_sink.h index 318643c9..feb5efa1 100644 --- a/vendor/spdlog/spdlog/sinks/ostream_sink.h +++ b/vendor/spdlog/spdlog/sinks/ostream_sink.h @@ -5,12 +5,11 @@ #pragma once +#include +#include + #include #include -#include - -#include "../details/null_mutex.h" -#include "./base_sink.h" namespace spdlog { diff --git a/vendor/spdlog/spdlog/sinks/sink.h b/vendor/spdlog/spdlog/sinks/sink.h index 09f1021c..d27fdbe0 100644 --- a/vendor/spdlog/spdlog/sinks/sink.h +++ b/vendor/spdlog/spdlog/sinks/sink.h @@ -6,7 +6,7 @@ #pragma once -#include "../details/log_msg.h" +#include namespace spdlog { @@ -15,10 +15,36 @@ namespace sinks class sink { public: + sink(): _level( level::trace ) {} + virtual ~sink() {} virtual void log(const details::log_msg& msg) = 0; virtual void flush() = 0; + + bool should_log(level::level_enum msg_level) const; + void set_level(level::level_enum log_level); + level::level_enum level() const; + +private: + level_t _level; + }; + +inline bool sink::should_log(level::level_enum msg_level) const +{ + return msg_level >= _level.load(std::memory_order_relaxed); +} + +inline void sink::set_level(level::level_enum log_level) +{ + _level.store(log_level); +} + +inline level::level_enum sink::level() const +{ + return static_cast(_level.load(std::memory_order_relaxed)); +} + } } diff --git a/vendor/spdlog/spdlog/sinks/stdout_sinks.h b/vendor/spdlog/spdlog/sinks/stdout_sinks.h index fd09b76a..30c19a54 100644 --- a/vendor/spdlog/spdlog/sinks/stdout_sinks.h +++ b/vendor/spdlog/spdlog/sinks/stdout_sinks.h @@ -5,10 +5,12 @@ #pragma once -#include +#include +#include + +#include +#include #include -#include "./ostream_sink.h" -#include "../details/null_mutex.h" namespace spdlog { @@ -16,16 +18,27 @@ namespace sinks { template -class stdout_sink : public ostream_sink +class stdout_sink : public base_sink { using MyType = stdout_sink; public: - stdout_sink() : ostream_sink(std::cout, true) {} + stdout_sink() {} static std::shared_ptr instance() { static std::shared_ptr instance = std::make_shared(); return instance; } + + void _sink_it(const details::log_msg& msg) override + { + fwrite(msg.formatted.data(), sizeof(char), msg.formatted.size(), stdout); + flush(); + } + + void flush() override + { + fflush(stdout); + } }; typedef stdout_sink stdout_sink_st; @@ -33,17 +46,27 @@ typedef stdout_sink stdout_sink_mt; template -class stderr_sink : public ostream_sink +class stderr_sink : public base_sink { using MyType = stderr_sink; public: - stderr_sink() : ostream_sink(std::cerr, true) {} + stderr_sink() {} static std::shared_ptr instance() { static std::shared_ptr instance = std::make_shared(); return instance; } + void _sink_it(const details::log_msg& msg) override + { + fwrite(msg.formatted.data(), sizeof(char), msg.formatted.size(), stderr); + flush(); + } + + void flush() override + { + fflush(stderr); + } }; typedef stderr_sink stderr_sink_mt; diff --git a/vendor/spdlog/spdlog/sinks/syslog_sink.h b/vendor/spdlog/spdlog/sinks/syslog_sink.h index ed1e2c40..0d8633c5 100644 --- a/vendor/spdlog/spdlog/sinks/syslog_sink.h +++ b/vendor/spdlog/spdlog/sinks/syslog_sink.h @@ -5,16 +5,17 @@ #pragma once -#if defined(__linux__) || defined(__APPLE__) +#include + +#ifdef SPDLOG_ENABLE_SYSLOG + +#include +#include #include #include #include -#include "./sink.h" -#include "../common.h" -#include "../details/log_msg.h" - namespace spdlog { @@ -35,12 +36,9 @@ public: _priorities[static_cast(level::trace)] = LOG_DEBUG; _priorities[static_cast(level::debug)] = LOG_DEBUG; _priorities[static_cast(level::info)] = LOG_INFO; - _priorities[static_cast(level::notice)] = LOG_NOTICE; _priorities[static_cast(level::warn)] = LOG_WARNING; _priorities[static_cast(level::err)] = LOG_ERR; _priorities[static_cast(level::critical)] = LOG_CRIT; - _priorities[static_cast(level::alert)] = LOG_ALERT; - _priorities[static_cast(level::emerg)] = LOG_EMERG; _priorities[static_cast(level::off)] = LOG_INFO; //set ident to be program name if empty @@ -65,7 +63,7 @@ public: private: - std::array _priorities; + std::array _priorities; //must store the ident because the man says openlog might use the pointer as is and not a string copy const std::string _ident; diff --git a/vendor/spdlog/spdlog/spdlog.h b/vendor/spdlog/spdlog/spdlog.h index 0748cd8f..d4f4203e 100644 --- a/vendor/spdlog/spdlog/spdlog.h +++ b/vendor/spdlog/spdlog/spdlog.h @@ -8,21 +8,25 @@ #pragma once -#include "tweakme.h" -#include "common.h" -#include "logger.h" +#include +#include +#include + +#include +#include +#include +#include namespace spdlog { -// Return an existing logger or nullptr if a logger with such name doesn't exist. -// Examples: + +// +// Return an existing logger or nullptr if a logger with such name doesn't exist. +// example: spdlog::get("my_logger")->info("hello {}", "world"); // -// spdlog::get("mylog")->info("Hello"); -// auto logger = spdlog::get("mylog"); -// logger.info("This is another message" , x, y, z); -// logger.info() << "This is another message" << x << y << z; std::shared_ptr get(const std::string& name); + // // Set global formatting // example: spdlog::set_pattern("%Y-%m-%d %H:%M:%S.%e %l : %v"); @@ -35,6 +39,11 @@ void set_formatter(formatter_ptr f); // void set_level(level::level_enum log_level); +// +// Set global error handler +// +void set_error_handler(log_err_handler); + // // Turn on async mode (off by default) and set the queue size for each async_logger. // effective only for loggers created after this call. @@ -48,40 +57,56 @@ void set_level(level::level_enum log_level); // worker_warmup_cb (optional): // callback function that will be called in worker thread upon start (can be used to init stuff like thread affinity) // -void set_async_mode(size_t queue_size, const async_overflow_policy overflow_policy = async_overflow_policy::block_retry, const std::function& worker_warmup_cb = nullptr, const std::chrono::milliseconds& flush_interval_ms = std::chrono::milliseconds::zero()); +// worker_teardown_cb (optional): +// callback function that will be called in worker thread upon exit +// +void set_async_mode(size_t queue_size, const async_overflow_policy overflow_policy = async_overflow_policy::block_retry, const std::function& worker_warmup_cb = nullptr, const std::chrono::milliseconds& flush_interval_ms = std::chrono::milliseconds::zero(), const std::function& worker_teardown_cb = nullptr); // Turn off async mode void set_sync_mode(); + +// +// Create and register multi/single threaded basic file logger. +// Basic logger simply writes to given file without any limitatons or rotations. +// +std::shared_ptr basic_logger_mt(const std::string& logger_name, const filename_t& filename, bool truncate = false); +std::shared_ptr basic_logger_st(const std::string& logger_name, const filename_t& filename, bool truncate = false); + // // Create and register multi/single threaded rotating file logger // -std::shared_ptr rotating_logger_mt(const std::string& logger_name, const std::string& filenameB, size_t max_file_size, size_t max_files, bool force_flush = false); -std::shared_ptr rotating_logger_st(const std::string& logger_name, const std::string& filename, size_t max_file_size, size_t max_files, bool force_flush = false); +std::shared_ptr rotating_logger_mt(const std::string& logger_name, const filename_t& filename, size_t max_file_size, size_t max_files); +std::shared_ptr rotating_logger_st(const std::string& logger_name, const filename_t& filename, size_t max_file_size, size_t max_files); // // Create file logger which creates new file on the given time (default in midnight): // -std::shared_ptr daily_logger_mt(const std::string& logger_name, const std::string& filename, int hour=0, int minute=0, bool force_flush = false); -std::shared_ptr daily_logger_st(const std::string& logger_name, const std::string& filename, int hour=0, int minute=0, bool force_flush = false); - +std::shared_ptr daily_logger_mt(const std::string& logger_name, const filename_t& filename, int hour=0, int minute=0); +std::shared_ptr daily_logger_st(const std::string& logger_name, const filename_t& filename, int hour=0, int minute=0); // // Create and register stdout/stderr loggers // -std::shared_ptr stdout_logger_mt(const std::string& logger_name); -std::shared_ptr stdout_logger_st(const std::string& logger_name); -std::shared_ptr stderr_logger_mt(const std::string& logger_name); -std::shared_ptr stderr_logger_st(const std::string& logger_name); +std::shared_ptr stdout_logger_mt(const std::string& logger_name, bool color = false); +std::shared_ptr stdout_logger_st(const std::string& logger_name, bool color = false); +std::shared_ptr stderr_logger_mt(const std::string& logger_name, bool color = false); +std::shared_ptr stderr_logger_st(const std::string& logger_name, bool color = false); // // Create and register a syslog logger // -#if defined(__linux__) || defined(__APPLE__) +#ifdef SPDLOG_ENABLE_SYSLOG std::shared_ptr syslog_logger(const std::string& logger_name, const std::string& ident = "", int syslog_option = 0); #endif +#if defined(__ANDROID__) +std::shared_ptr android_logger(const std::string& logger_name, const std::string& tag = "spdlog"); +#endif + +// Create and register a logger a single sink +std::shared_ptr create(const std::string& logger_name, const sink_ptr& sink); // Create and register a logger with multiple sinks std::shared_ptr create(const std::string& logger_name, sinks_init_list sinks); @@ -90,7 +115,8 @@ std::shared_ptr create(const std::string& logger_name, const It& sinks_b // Create and register a logger with templated sink type -// Example: spdlog::create("mylog", "dailylog_filename", "txt"); +// Example: +// spdlog::create("mylog", "dailylog_filename", "txt"); template std::shared_ptr create(const std::string& logger_name, Args...); @@ -98,32 +124,41 @@ std::shared_ptr create(const std::string& logger_name, Args...); // Register the given logger with the given name void register_logger(std::shared_ptr logger); +// Apply a user defined function on all registered loggers +// Example: +// spdlog::apply_all([&](std::shared_ptr l) {l->flush();}); +void apply_all(std::function)> fun); + // Drop the reference to the given logger void drop(const std::string &name); -// Drop all references +// Drop all references from the registry void drop_all(); /////////////////////////////////////////////////////////////////////////////// // -// Macros to be display source file & line // Trace & Debug can be switched on/off at compile time for zero cost debug statements. // Uncomment SPDLOG_DEBUG_ON/SPDLOG_TRACE_ON in teakme.h to enable. +// SPDLOG_TRACE(..) will also print current file and line. // // Example: -// spdlog::set_level(spdlog::level::debug); -// SPDLOG_DEBUG(my_logger, "Some debug message {} {}", 1, 3.2); +// spdlog::set_level(spdlog::level::trace); +// SPDLOG_TRACE(my_logger, "some trace message"); +// SPDLOG_TRACE(my_logger, "another trace message {} {}", 1, 2); +// SPDLOG_DEBUG(my_logger, "some debug message {} {}", 3, 4); /////////////////////////////////////////////////////////////////////////////// #ifdef SPDLOG_TRACE_ON -#define SPDLOG_TRACE(logger, ...) logger->trace(__VA_ARGS__) << " (" << __FILE__ << " #" << __LINE__ <<")"; +#define SPDLOG_STR_H(x) #x +#define SPDLOG_STR_HELPER(x) SPDLOG_STR_H(x) +#define SPDLOG_TRACE(logger, ...) logger->trace("[" __FILE__ " line #" SPDLOG_STR_HELPER(__LINE__) "] " __VA_ARGS__) #else #define SPDLOG_TRACE(logger, ...) #endif #ifdef SPDLOG_DEBUG_ON -#define SPDLOG_DEBUG(logger, ...) logger->debug(__VA_ARGS__) << " (" << __FILE__ << " #" << __LINE__ <<")"; +#define SPDLOG_DEBUG(logger, ...) logger->debug(__VA_ARGS__) #else #define SPDLOG_DEBUG(logger, ...) #endif @@ -132,4 +167,4 @@ void drop_all(); } -#include "details/spdlog_impl.h" +#include diff --git a/vendor/spdlog/spdlog/tweakme.h b/vendor/spdlog/spdlog/tweakme.h index f1365147..1af539bf 100644 --- a/vendor/spdlog/spdlog/tweakme.h +++ b/vendor/spdlog/spdlog/tweakme.h @@ -5,21 +5,29 @@ #pragma once +/////////////////////////////////////////////////////////////////////////////// // -// Edit this file to squeeze every last drop of performance out of spdlog. +// Edit this file to squeeze more performance, and to customize supported features // +/////////////////////////////////////////////////////////////////////////////// + /////////////////////////////////////////////////////////////////////////////// // Under Linux, the much faster CLOCK_REALTIME_COARSE clock can be used. // This clock is less accurate - can be off by dozens of millis - depending on the kernel HZ. -// Uncomment to use it instead of the regular (but slower) clock. +// Uncomment to use it instead of the regular clock. +// // #define SPDLOG_CLOCK_COARSE /////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////// -// Uncomment if date/time logging is not needed. +// Uncomment if date/time logging is not needed and never appear in the log pattern. // This will prevent spdlog from quering the clock on each log call. +// +// WARNING: If the log pattern contains any date/time while this flag is on, the result is undefined. +// You must set new pattern(spdlog::set_pattern(..") without any date/time in it +// // #define SPDLOG_NO_DATETIME /////////////////////////////////////////////////////////////////////////////// @@ -27,6 +35,9 @@ /////////////////////////////////////////////////////////////////////////////// // Uncomment if thread id logging is not needed (i.e. no %t in the log pattern). // This will prevent spdlog from quering the thread id on each log call. +// +// WARNING: If the log pattern contains thread id (i.e, %t) while this flag is on, the result is undefined. +// // #define SPDLOG_NO_THREAD_ID /////////////////////////////////////////////////////////////////////////////// @@ -34,12 +45,13 @@ /////////////////////////////////////////////////////////////////////////////// // Uncomment if logger name logging is not needed. // This will prevent spdlog from copying the logger name on each log call. +// // #define SPDLOG_NO_NAME /////////////////////////////////////////////////////////////////////////////// - /////////////////////////////////////////////////////////////////////////////// // Uncomment to enable the SPDLOG_DEBUG/SPDLOG_TRACE macros. +// // #define SPDLOG_DEBUG_ON // #define SPDLOG_TRACE_ON /////////////////////////////////////////////////////////////////////////////// @@ -49,5 +61,43 @@ // Uncomment to avoid locking in the registry operations (spdlog::get(), spdlog::drop() spdlog::register()). // Use only if your code never modifes concurrently the registry. // Note that upon creating a logger the registry is modified by spdlog.. +// // #define SPDLOG_NO_REGISTRY_MUTEX /////////////////////////////////////////////////////////////////////////////// + + +/////////////////////////////////////////////////////////////////////////////// +// Uncomment to avoid spdlog's usage of atomic log levels +// Use only if your code never modifies a logger's log levels concurrently by different threads. +// +// #define SPDLOG_NO_ATOMIC_LEVELS +/////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////// +// Uncomment to enable usage of wchar_t for file names on Windows. +// +// #define SPDLOG_WCHAR_FILENAMES +/////////////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////////////////////////////// +// Uncomment to override default eol ("\n" or "\r\n" under Linux/Windows) +// +// #define SPDLOG_EOL ";-)\n" +/////////////////////////////////////////////////////////////////////////////// + + +/////////////////////////////////////////////////////////////////////////////// +// Uncomment to use your own copy of the fmt library instead of spdlog's copy. +// In this case spdlog will try to include so set your -I flag accordingly. +// +// #define SPDLOG_FMT_EXTERNAL +/////////////////////////////////////////////////////////////////////////////// + + +/////////////////////////////////////////////////////////////////////////////// +// Uncomment to enable syslog (disabled by default) +// +// #define SPDLOG_ENABLE_SYSLOG +/////////////////////////////////////////////////////////////////////////////// + +