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 +/////////////////////////////////////////////////////////////////////////////// + +