diff --git a/CMakeLists.txt b/CMakeLists.txt index 5164609b..b4fe429d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -4,7 +4,7 @@ # cmake_minimum_required(VERSION 3.1) -project(spdlog VERSION 0.16.3) +project(spdlog VERSION 0.16.3 LANGUAGES CXX) include(CTest) include(CMakeDependentOption) include(GNUInstallDirs) @@ -73,7 +73,6 @@ configure_file("cmake/spdlog.pc.in" "${pkg_config}" @ONLY) install( TARGETS spdlog EXPORT "${targets_export_name}" - INCLUDES DESTINATION "${include_install_dir}" ) # install headers diff --git a/example/CMakeLists.txt b/example/CMakeLists.txt index 7859e4d5..cc699729 100644 --- a/example/CMakeLists.txt +++ b/example/CMakeLists.txt @@ -1,49 +1,49 @@ -# *************************************************************************/ -# * Copyright (c) 2015 Ruslan Baratov. */ -# * */ -# * Permission is hereby granted, free of charge, to any person obtaining */ -# * a copy of this software and associated documentation files (the */ -# * "Software"), to deal in the Software without restriction, including */ -# * without limitation the rights to use, copy, modify, merge, publish, */ -# * distribute, sublicense, and/or sell copies of the Software, and to */ -# * permit persons to whom the Software is furnished to do so, subject to */ -# * the following conditions: */ -# * */ -# * The above copyright notice and this permission notice shall be */ -# * included in all copies or substantial portions of the Software. */ -# * */ -# * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -# * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -# * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -# * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -# * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -# * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -# * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -# *************************************************************************/ - -cmake_minimum_required(VERSION 3.0) -project(SpdlogExamples) - -if(TARGET spdlog) - # Part of the main project - add_library(spdlog::spdlog ALIAS spdlog) -else() - # Stand-alone build - find_package(spdlog CONFIG REQUIRED) -endif() - -find_package(Threads) - -add_executable(example example.cpp) -target_link_libraries(example spdlog::spdlog ${CMAKE_THREAD_LIBS_INIT}) - -add_executable(benchmark bench.cpp) -target_link_libraries(benchmark spdlog::spdlog ${CMAKE_THREAD_LIBS_INIT}) - -add_executable(multisink multisink.cpp) -target_link_libraries(multisink spdlog::spdlog ${CMAKE_THREAD_LIBS_INIT}) - -enable_testing() -file(MAKE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/logs") -add_test(NAME RunExample COMMAND example) -add_test(NAME RunBenchmark COMMAND benchmark) +# *************************************************************************/ +# * Copyright (c) 2015 Ruslan Baratov. */ +# * */ +# * Permission is hereby granted, free of charge, to any person obtaining */ +# * a copy of this software and associated documentation files (the */ +# * "Software"), to deal in the Software without restriction, including */ +# * without limitation the rights to use, copy, modify, merge, publish, */ +# * distribute, sublicense, and/or sell copies of the Software, and to */ +# * permit persons to whom the Software is furnished to do so, subject to */ +# * the following conditions: */ +# * */ +# * The above copyright notice and this permission notice shall be */ +# * included in all copies or substantial portions of the Software. */ +# * */ +# * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +# * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +# * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +# * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +# * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +# * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +# * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +# *************************************************************************/ + +cmake_minimum_required(VERSION 3.1) +project(SpdlogExamples CXX) + +if(TARGET spdlog) + # Part of the main project + add_library(spdlog::spdlog ALIAS spdlog) +else() + # Stand-alone build + find_package(spdlog CONFIG REQUIRED) +endif() + +find_package(Threads REQUIRED) + +add_executable(example example.cpp) +target_link_libraries(example spdlog::spdlog Threads::Threads) + +add_executable(benchmark bench.cpp) +target_link_libraries(benchmark spdlog::spdlog Threads::Threads) + +add_executable(multisink multisink.cpp) +target_link_libraries(multisink spdlog::spdlog Threads::Threads) + +enable_testing() +file(MAKE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/logs") +add_test(NAME RunExample COMMAND example) +add_test(NAME RunBenchmark COMMAND benchmark) diff --git a/example/example.cpp b/example/example.cpp index c9aee7ad..cf747834 100644 --- a/example/example.cpp +++ b/example/example.cpp @@ -64,9 +64,9 @@ int main(int, char*[]) // Runtime log levels spd::set_level(spd::level::info); //Set global log level to info - console->debug("This message shold not be displayed!"); + console->debug("This message should not be displayed!"); console->set_level(spd::level::debug); // Set specific logger's log level - console->debug("This message shold be displayed.."); + console->debug("This message should be displayed.."); // Compile time log levels // define SPDLOG_DEBUG_ON or SPDLOG_TRACE_ON diff --git a/include/spdlog/common.h b/include/spdlog/common.h index ea0b0567..6c7ab149 100644 --- a/include/spdlog/common.h +++ b/include/spdlog/common.h @@ -5,6 +5,10 @@ #pragma once +#define SPDLOG_VERSION "0.16.3" + +#include "tweakme.h" + #include #include #include @@ -98,6 +102,8 @@ inline const char* to_short_str(spdlog::level::level_enum l) { return short_level_names[l]; } +using level_hasher = std::hash; + } //level diff --git a/include/spdlog/details/async_log_helper.h b/include/spdlog/details/async_log_helper.h index bba9bc0e..82282db9 100644 --- a/include/spdlog/details/async_log_helper.h +++ b/include/spdlog/details/async_log_helper.h @@ -216,9 +216,10 @@ inline spdlog::details::async_log_helper::async_log_helper( _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) -{} + _worker_teardown_cb(worker_teardown_cb) +{ + _worker_thread = std::thread(&async_log_helper::worker_loop, this); +} // Send to the worker thread termination message(level=off) // and wait for it to finish gracefully diff --git a/include/spdlog/details/logger_impl.h b/include/spdlog/details/logger_impl.h index bfb39333..14db8e2c 100644 --- a/include/spdlog/details/logger_impl.h +++ b/include/spdlog/details/logger_impl.h @@ -369,7 +369,7 @@ inline void spdlog::logger::_default_err_handler(const std::string &msg) 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); + err_msg.formatted.write("[*** LOG ERROR ***] [{}] [{}] [{}]{}", name(), msg, date_buf, details::os::default_eol); sinks::stderr_sink_mt::instance()->log(err_msg); _last_err_time = now; } diff --git a/include/spdlog/details/os.h b/include/spdlog/details/os.h index b881cae4..e647e32a 100644 --- a/include/spdlog/details/os.h +++ b/include/spdlog/details/os.h @@ -140,8 +140,7 @@ inline bool operator!=(const std::tm& tm1, const std::tm& tm2) #endif #endif -SPDLOG_CONSTEXPR static const char* eol = SPDLOG_EOL; -SPDLOG_CONSTEXPR static int eol_size = sizeof(SPDLOG_EOL) - 1; +SPDLOG_CONSTEXPR static const char* default_eol = SPDLOG_EOL; @@ -155,10 +154,13 @@ SPDLOG_CONSTEXPR static const char folder_sep = '/'; inline void prevent_child_fd(FILE *f) { + #ifdef _WIN32 +#if !defined(__cplusplus_winrt) auto file_handle = (HANDLE)_get_osfhandle(_fileno(f)); if (!::SetHandleInformation(file_handle, HANDLE_FLAG_INHERIT, 0)) throw spdlog_ex("SetHandleInformation failed", errno); +#endif #else auto fd = fileno(f); if (fcntl(fd, F_SETFD, FD_CLOEXEC) == -1) @@ -352,7 +354,7 @@ inline size_t _thread_id() //Return current thread id as size_t (from thread local storage) inline size_t thread_id() { -#if defined(SPDLOG_DISABLE_TID_CACHING) || (defined(_MSC_VER) && (_MSC_VER < 1900)) || (defined(__clang__) && !__has_feature(cxx_thread_local)) +#if defined(SPDLOG_DISABLE_TID_CACHING) || (defined(_MSC_VER) && (_MSC_VER < 1900)) || defined(__cplusplus_winrt ) || (defined(__clang__) && !__has_feature(cxx_thread_local)) return _thread_id(); #else // cache thread id in tls static thread_local const size_t tid = _thread_id(); @@ -368,7 +370,7 @@ inline size_t thread_id() inline void sleep_for_millis(int milliseconds) { #if defined(_WIN32) - Sleep(milliseconds); + ::Sleep(milliseconds); #else std::this_thread::sleep_for(std::chrono::milliseconds(milliseconds)); #endif @@ -437,7 +439,7 @@ inline int pid() { #ifdef _WIN32 - return ::_getpid(); + return static_cast(::GetCurrentProcessId()); #else return static_cast(::getpid()); #endif diff --git a/include/spdlog/details/pattern_formatter_impl.h b/include/spdlog/details/pattern_formatter_impl.h index f252b227..adfe4d4c 100644 --- a/include/spdlog/details/pattern_formatter_impl.h +++ b/include/spdlog/details/pattern_formatter_impl.h @@ -508,8 +508,8 @@ class full_formatter SPDLOG_FINAL:public flag_formatter /////////////////////////////////////////////////////////////////////////////// // pattern_formatter inline impl /////////////////////////////////////////////////////////////////////////////// -inline spdlog::pattern_formatter::pattern_formatter(const std::string& pattern, pattern_time_type pattern_time) - : _pattern_time(pattern_time) +inline spdlog::pattern_formatter::pattern_formatter(const std::string& pattern, pattern_time_type pattern_time, const std::string& eol) + : _eol(eol), _pattern_time(pattern_time) { compile_pattern(pattern); } @@ -524,7 +524,6 @@ inline void spdlog::pattern_formatter::compile_pattern(const std::string& patter { if (user_chars) //append user chars found so far _formatters.push_back(std::move(user_chars)); - if (++it != end) handle_flag(*it); else @@ -549,130 +548,130 @@ inline void spdlog::pattern_formatter::handle_flag(char flag) { // logger name case 'n': - _formatters.push_back(std::unique_ptr(new details::name_formatter())); + _formatters.emplace_back(new details::name_formatter()); break; case 'l': - _formatters.push_back(std::unique_ptr(new details::level_formatter())); + _formatters.emplace_back(new details::level_formatter()); break; case 'L': - _formatters.push_back(std::unique_ptr(new details::short_level_formatter())); + _formatters.emplace_back(new details::short_level_formatter()); break; case('t'): - _formatters.push_back(std::unique_ptr(new details::t_formatter())); + _formatters.emplace_back(new details::t_formatter()); break; case('v'): - _formatters.push_back(std::unique_ptr(new details::v_formatter())); + _formatters.emplace_back(new details::v_formatter()); break; case('a'): - _formatters.push_back(std::unique_ptr(new details::a_formatter())); + _formatters.emplace_back(new details::a_formatter()); break; case('A'): - _formatters.push_back(std::unique_ptr(new details::A_formatter())); + _formatters.emplace_back(new details::A_formatter()); break; case('b'): case('h'): - _formatters.push_back(std::unique_ptr(new details::b_formatter())); + _formatters.emplace_back(new details::b_formatter()); break; case('B'): - _formatters.push_back(std::unique_ptr(new details::B_formatter())); + _formatters.emplace_back(new details::B_formatter()); break; case('c'): - _formatters.push_back(std::unique_ptr(new details::c_formatter())); + _formatters.emplace_back(new details::c_formatter()); break; case('C'): - _formatters.push_back(std::unique_ptr(new details::C_formatter())); + _formatters.emplace_back(new details::C_formatter()); break; case('Y'): - _formatters.push_back(std::unique_ptr(new details::Y_formatter())); + _formatters.emplace_back(new details::Y_formatter()); break; case('D'): case('x'): - _formatters.push_back(std::unique_ptr(new details::D_formatter())); + _formatters.emplace_back(new details::D_formatter()); break; case('m'): - _formatters.push_back(std::unique_ptr(new details::m_formatter())); + _formatters.emplace_back(new details::m_formatter()); break; case('d'): - _formatters.push_back(std::unique_ptr(new details::d_formatter())); + _formatters.emplace_back(new details::d_formatter()); break; case('H'): - _formatters.push_back(std::unique_ptr(new details::H_formatter())); + _formatters.emplace_back(new details::H_formatter()); break; case('I'): - _formatters.push_back(std::unique_ptr(new details::I_formatter())); + _formatters.emplace_back(new details::I_formatter()); break; case('M'): - _formatters.push_back(std::unique_ptr(new details::M_formatter())); + _formatters.emplace_back(new details::M_formatter()); break; case('S'): - _formatters.push_back(std::unique_ptr(new details::S_formatter())); + _formatters.emplace_back(new details::S_formatter()); break; case('e'): - _formatters.push_back(std::unique_ptr(new details::e_formatter())); + _formatters.emplace_back(new details::e_formatter()); break; case('f'): - _formatters.push_back(std::unique_ptr(new details::f_formatter())); + _formatters.emplace_back(new details::f_formatter()); break; case('F'): - _formatters.push_back(std::unique_ptr(new details::F_formatter())); + _formatters.emplace_back(new details::F_formatter()); break; case('E'): - _formatters.push_back(std::unique_ptr(new details::E_formatter())); + _formatters.emplace_back(new details::E_formatter()); break; case('p'): - _formatters.push_back(std::unique_ptr(new details::p_formatter())); + _formatters.emplace_back(new details::p_formatter()); break; case('r'): - _formatters.push_back(std::unique_ptr(new details::r_formatter())); + _formatters.emplace_back(new details::r_formatter()); break; case('R'): - _formatters.push_back(std::unique_ptr(new details::R_formatter())); + _formatters.emplace_back(new details::R_formatter()); break; case('T'): case('X'): - _formatters.push_back(std::unique_ptr(new details::T_formatter())); + _formatters.emplace_back(new details::T_formatter()); break; case('z'): - _formatters.push_back(std::unique_ptr(new details::z_formatter())); + _formatters.emplace_back(new details::z_formatter()); break; case ('+'): - _formatters.push_back(std::unique_ptr(new details::full_formatter())); + _formatters.emplace_back(new details::full_formatter()); break; case ('P'): - _formatters.push_back(std::unique_ptr(new details::pid_formatter())); + _formatters.emplace_back(new details::pid_formatter()); break; case ('i'): - _formatters.push_back(std::unique_ptr(new details::i_formatter())); + _formatters.emplace_back(new details::i_formatter()); break; default: //Unknown flag appears as is @@ -702,5 +701,5 @@ inline void spdlog::pattern_formatter::format(details::log_msg& msg) f->format(msg, tm_time); } //write eol - msg.formatted.write(details::os::eol, details::os::eol_size); + msg.formatted.write(_eol.data(), _eol.size()); } diff --git a/include/spdlog/details/spdlog_impl.h b/include/spdlog/details/spdlog_impl.h index bcbf3014..251ff204 100644 --- a/include/spdlog/details/spdlog_impl.h +++ b/include/spdlog/details/spdlog_impl.h @@ -16,7 +16,7 @@ #include "../sinks/syslog_sink.h" #endif -#ifdef _WIN32 +#if defined _WIN32 && !defined(__cplusplus_winrt) #include "../sinks/wincolor_sink.h" #else #include "../sinks/ansicolor_sink.h" @@ -107,7 +107,8 @@ inline std::shared_ptr spdlog::stderr_logger_st(const std::strin // // stdout/stderr color loggers // -#ifdef _WIN32 +#if defined _WIN32 && !defined(__cplusplus_winrt) + inline std::shared_ptr spdlog::stdout_color_mt(const std::string& logger_name) { auto sink = std::make_shared(); diff --git a/include/spdlog/fmt/bundled/format.cc b/include/spdlog/fmt/bundled/format.cc index 09d2ea9f..2d236bc6 100644 --- a/include/spdlog/fmt/bundled/format.cc +++ b/include/spdlog/fmt/bundled/format.cc @@ -72,9 +72,11 @@ // Dummy implementations of strerror_r and strerror_s called if corresponding // system functions are not available. +FMT_MAYBE_UNUSED static inline fmt::internal::Null<> strerror_r(int, char *, ...) { return fmt::internal::Null<>(); } +FMT_MAYBE_UNUSED static inline fmt::internal::Null<> strerror_s(char *, std::size_t, ...) { return fmt::internal::Null<>(); } @@ -121,7 +123,7 @@ typedef void (*FormatFunc)(Writer &, int, StringRef); // 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"); + FMT_ASSERT(buffer != FMT_NULL && buffer_size != 0, "invalid buffer"); class StrError { private: @@ -159,6 +161,11 @@ int safe_strerror( ERANGE : result; } +#ifdef __c2__ +# pragma clang diagnostic push +# pragma clang diagnostic ignored "-Wdeprecated-declarations" +#endif + // Fallback to strerror if strerror_r and strerror_s are not available. int fallback(internal::Null<>) { errno = 0; @@ -166,13 +173,15 @@ int safe_strerror( return errno; } +#ifdef __c2__ +# pragma clang diagnostic pop +#endif + public: StrError(int err_code, char *&buf, std::size_t buf_size) : error_code_(err_code), buffer_(buf), buffer_size_(buf_size) {} int run() { - // Suppress a warning about unused strerror_r. - strerror_r(0, FMT_NULL, ""); return handle(strerror_r(error_code_, buffer_, buffer_size_)); } }; @@ -396,51 +405,6 @@ FMT_FUNC void format_system_error( 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 = FMT_NULL; - 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")); @@ -502,8 +466,6 @@ template struct internal::BasicData; template void internal::FixedBuffer::grow(std::size_t); -template void internal::ArgMap::init(const ArgList &args); - template FMT_API int internal::CharTraits::format_float( char *buffer, std::size_t size, const char *format, unsigned width, int precision, double value); @@ -516,8 +478,6 @@ template FMT_API int internal::CharTraits::format_float( template void internal::FixedBuffer::grow(std::size_t); -template void internal::ArgMap::init(const ArgList &args); - template FMT_API int internal::CharTraits::format_float( wchar_t *buffer, std::size_t size, const wchar_t *format, unsigned width, int precision, double value); diff --git a/include/spdlog/fmt/bundled/format.h b/include/spdlog/fmt/bundled/format.h index 91f43481..971c58e2 100644 --- a/include/spdlog/fmt/bundled/format.h +++ b/include/spdlog/fmt/bundled/format.h @@ -28,6 +28,7 @@ #ifndef FMT_FORMAT_H_ #define FMT_FORMAT_H_ +#define FMT_INCLUDE #include #include #include @@ -39,11 +40,26 @@ #include #include #include // for std::pair +#undef FMT_INCLUDE // The fmt library version in the form major * 10000 + minor * 100 + patch. -#define FMT_VERSION 40000 +#define FMT_VERSION 40100 -#ifdef _SECURE_SCL +#if defined(__has_include) +# define FMT_HAS_INCLUDE(x) __has_include(x) +#else +# define FMT_HAS_INCLUDE(x) 0 +#endif + +#if (FMT_HAS_INCLUDE() && __cplusplus > 201402L) || \ + (defined(_MSVC_LANG) && _MSVC_LANG > 201402L && _MSC_VER >= 1910) +# include +# define FMT_HAS_STRING_VIEW 1 +#else +# define FMT_HAS_STRING_VIEW 0 +#endif + +#if defined _SECURE_SCL && _SECURE_SCL # define FMT_SECURE_SCL _SECURE_SCL #else # define FMT_SECURE_SCL 0 @@ -97,7 +113,9 @@ typedef __int64 intmax_t; # define FMT_HAS_GXX_CXX11 1 # endif #else +# define FMT_GCC_VERSION 0 # define FMT_GCC_EXTENSION +# define FMT_HAS_GXX_CXX11 0 #endif #if defined(__INTEL_COMPILER) @@ -135,6 +153,32 @@ typedef __int64 intmax_t; # define FMT_HAS_CPP_ATTRIBUTE(x) 0 #endif +#if FMT_HAS_CPP_ATTRIBUTE(maybe_unused) +# define FMT_HAS_CXX17_ATTRIBUTE_MAYBE_UNUSED +// VC++ 1910 support /std: option and that will set _MSVC_LANG macro +// Clang with Microsoft CodeGen doesn't define _MSVC_LANG macro +#elif defined(_MSVC_LANG) && _MSVC_LANG > 201402 && _MSC_VER >= 1910 +# define FMT_HAS_CXX17_ATTRIBUTE_MAYBE_UNUSED +#endif + +#ifdef FMT_HAS_CXX17_ATTRIBUTE_MAYBE_UNUSED +# define FMT_MAYBE_UNUSED [[maybe_unused]] +// g++/clang++ also support [[gnu::unused]]. However, we don't use it. +#elif defined(__GNUC__) +# define FMT_MAYBE_UNUSED __attribute__((unused)) +#else +# define FMT_MAYBE_UNUSED +#endif + +// Use the compiler's attribute noreturn +#if defined(__MINGW32__) || defined(__MINGW64__) +# define FMT_NORETURN __attribute__((noreturn)) +#elif FMT_HAS_CPP_ATTRIBUTE(noreturn) && __cplusplus >= 201103L +# define FMT_NORETURN [[noreturn]] +#else +# define FMT_NORETURN +#endif + #ifndef FMT_USE_VARIADIC_TEMPLATES // Variadic templates are available in GCC since version 4.4 // (http://gcc.gnu.org/projects/cxx0x.html) and in Visual C++ @@ -156,6 +200,12 @@ typedef __int64 intmax_t; # endif #endif +#if __cplusplus >= 201103L || FMT_MSC_VER >= 1700 +# define FMT_USE_ALLOCATOR_TRAITS 1 +#else +# define FMT_USE_ALLOCATOR_TRAITS 0 +#endif + // Check if exceptions are disabled. #if defined(__GNUC__) && !defined(__EXCEPTIONS) # define FMT_EXCEPTIONS 0 @@ -262,11 +312,14 @@ typedef __int64 intmax_t; // 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 && \ +# if FMT_USE_VARIADIC_TEMPLATES && FMT_USE_RVALUE_REFERENCES && \ (FMT_HAS_FEATURE(cxx_user_literals) || \ (FMT_GCC_VERSION >= 407 && FMT_HAS_GXX_CXX11) || FMT_MSC_VER >= 1900) && \ (!defined(FMT_ICC_VERSION) || FMT_ICC_VERSION >= 1500) +# define FMT_USE_USER_DEFINED_LITERALS 1 +# else +# define FMT_USE_USER_DEFINED_LITERALS 0 +# endif #endif #ifndef FMT_USE_EXTERN_TEMPLATES @@ -307,7 +360,10 @@ namespace fmt { namespace internal { -# pragma intrinsic(_BitScanReverse) +// avoid Clang with Microsoft CodeGen's -Wunknown-pragmas warning +# ifndef __clang__ +# pragma intrinsic(_BitScanReverse) +# endif inline uint32_t clz(uint32_t x) { unsigned long r = 0; @@ -322,7 +378,8 @@ inline uint32_t clz(uint32_t x) } # define FMT_BUILTIN_CLZ(n) fmt::internal::clz(n) -# ifdef _WIN64 +// avoid Clang with Microsoft CodeGen's -Wunknown-pragmas warning +# if defined(_WIN64) && !defined(__clang__) # pragma intrinsic(_BitScanReverse64) # endif @@ -546,6 +603,27 @@ public: const std::basic_string, Allocator> &s) : data_(s.c_str()), size_(s.size()) {} +#if FMT_HAS_STRING_VIEW + /** + \rst + Constructs a string reference from a ``std::basic_string_view`` object. + \endrst + */ + BasicStringRef( + const std::basic_string_view> &s) + : data_(s.data()), size_(s.size()) {} + + /** + \rst + Converts a string reference to an ``std::string_view`` object. + \endrst + */ + explicit operator std::basic_string_view() const FMT_NOEXCEPT + { + return std::basic_string_view(data_, size_); + } +#endif + /** \rst Converts a string reference to an ``std::string`` object. @@ -669,7 +747,7 @@ public: explicit FormatError(CStringRef message) : std::runtime_error(message.c_str()) {} FormatError(const FormatError &ferr) : std::runtime_error(ferr) {} - FMT_API ~FormatError() FMT_DTOR_NOEXCEPT; + FMT_API ~FormatError() FMT_DTOR_NOEXCEPT FMT_OVERRIDE; }; namespace internal @@ -812,7 +890,7 @@ template void Buffer::append(const U *begin, const U *end) { FMT_ASSERT(end >= begin, "negative value"); - std::size_t new_size = size_ + (end - begin); + std::size_t new_size = size_ + static_cast(end - begin); if (new_size > capacity_) grow(new_size); std::uninitialized_copy(begin, end, @@ -843,10 +921,7 @@ protected: public: explicit MemoryBuffer(const Allocator &alloc = Allocator()) : Allocator(alloc), Buffer(data_, SIZE) {} - ~MemoryBuffer() - { - deallocate(); - } + ~MemoryBuffer() FMT_OVERRIDE { deallocate(); } #if FMT_USE_RVALUE_REFERENCES private: @@ -900,7 +975,12 @@ void MemoryBuffer::grow(std::size_t size) std::size_t new_capacity = this->capacity_ + this->capacity_ / 2; if (size > new_capacity) new_capacity = size; +#if FMT_USE_ALLOCATOR_TRAITS + T *new_ptr = + std::allocator_traits::allocate(*this, new_capacity, FMT_NULL); +#else T *new_ptr = this->allocate(new_capacity, FMT_NULL); +#endif // The following code doesn't throw, so the raw pointer above doesn't leak. std::uninitialized_copy(this->ptr_, this->ptr_ + this->size_, make_ptr(new_ptr, new_capacity)); @@ -1050,7 +1130,7 @@ struct IntTraits TypeSelector::digits <= 32>::Type MainType; }; -FMT_API void report_unknown_type(char code, const char *type); +FMT_API FMT_NORETURN void report_unknown_type(char code, const char *type); // Static data is placed in this class template to allow header-only // configuration. @@ -1335,19 +1415,19 @@ T &get(); Yes &convert(fmt::ULongLong); No &convert(...); -template +template struct ConvertToIntImpl { enum { value = ENABLE_CONVERSION }; }; -template +template struct ConvertToIntImpl2 { enum { value = false }; }; -template +template struct ConvertToIntImpl2 { enum @@ -1357,7 +1437,7 @@ struct ConvertToIntImpl2 }; }; -template +template struct ConvertToInt { enum @@ -1376,22 +1456,22 @@ FMT_DISABLE_CONVERSION_TO_INT(float); FMT_DISABLE_CONVERSION_TO_INT(double); FMT_DISABLE_CONVERSION_TO_INT(long double); -template +template struct EnableIf {}; -template +template struct EnableIf { typedef T type; }; -template +template struct Conditional { typedef T type; }; -template +template struct Conditional { typedef F type; @@ -1457,10 +1537,10 @@ inline fmt::StringRef thousands_sep(...) typedef int FMT_CONCAT_(Assert, __LINE__)[(cond) ? 1 : -1] FMT_UNUSED #endif -template -void format_arg(Formatter &, const Char *, const T &) +template +void format_arg(Formatter&, ...) { - FMT_STATIC_ASSERT(FalseType::value, + FMT_STATIC_ASSERT(FalseType::value, "Cannot format argument. To enable the use of ostream " "operator<< include fmt/ostream.h. Otherwise provide " "an overload of format_arg."); @@ -1494,6 +1574,9 @@ private: MakeValue(typename WCharHelper::Unsupported); MakeValue(typename WCharHelper::Unsupported); MakeValue(typename WCharHelper::Unsupported); +#if FMT_HAS_STRING_VIEW + MakeValue(typename WCharHelper::Unsupported); +#endif MakeValue(typename WCharHelper::Unsupported); void set_string(StringRef str) @@ -1570,6 +1653,26 @@ public: FMT_MAKE_VALUE(unsigned char, uint_value, UINT) FMT_MAKE_VALUE(char, int_value, CHAR) +#if __cplusplus >= 201103L + template < + typename T, + typename = typename std::enable_if< + std::is_enum::value && ConvertToInt::value>::type> + MakeValue(T value) + { + int_value = value; + } + + template < + typename T, + typename = typename std::enable_if< + std::is_enum::value && ConvertToInt::value>::type> + static uint64_t type(T) + { + return Arg::INT; + } +#endif + #if !defined(_MSC_VER) || defined(_NATIVE_WCHAR_T_DEFINED) MakeValue(typename WCharHelper::Supported value) { @@ -1592,6 +1695,9 @@ public: 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) +#if FMT_HAS_STRING_VIEW + FMT_MAKE_STR_VALUE(const std::string_view &, STRING) +#endif FMT_MAKE_STR_VALUE(StringRef, STRING) FMT_MAKE_VALUE_(CStringRef, string.value, CSTRING, value.c_str()) @@ -1604,6 +1710,9 @@ public: FMT_MAKE_WSTR_VALUE(wchar_t *, WSTRING) FMT_MAKE_WSTR_VALUE(const wchar_t *, WSTRING) FMT_MAKE_WSTR_VALUE(const std::wstring &, WSTRING) +#if FMT_HAS_STRING_VIEW + FMT_MAKE_WSTR_VALUE(const std::wstring_view &, WSTRING) +#endif FMT_MAKE_WSTR_VALUE(WStringRef, WSTRING) FMT_MAKE_VALUE(void *, pointer, POINTER) @@ -1689,7 +1798,7 @@ class RuntimeError : public std::runtime_error protected: RuntimeError() : std::runtime_error("") {} RuntimeError(const RuntimeError &rerr) : std::runtime_error(rerr) {} - FMT_API ~RuntimeError() FMT_DTOR_NOEXCEPT; + FMT_API ~RuntimeError() FMT_DTOR_NOEXCEPT FMT_OVERRIDE; }; template @@ -2267,7 +2376,7 @@ private: MapType map_; public: - FMT_API void init(const ArgList &args); + void init(const ArgList &args); const internal::Arg *find(const fmt::BasicStringRef &name) const { @@ -2282,6 +2391,61 @@ public: } }; +template +void ArgMap::init(const ArgList &args) +{ + if (!map_.empty()) + return; + typedef internal::NamedArg NamedArg; + const NamedArg *named_arg = FMT_NULL; + 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 class ArgFormatterBase : public ArgVisitor { @@ -2623,7 +2787,8 @@ template template struct ArgArray { - typedef Value Type[N > 0 ? N : 1]; + // '+' is used to silence GCC -Wduplicated-branches warning. + typedef Value Type[N > 0 ? N : +1]; template static Value make(const T &value) @@ -2823,7 +2988,7 @@ public: FMT_DEFAULTED_COPY_CTOR(SystemError) FMT_VARIADIC_CTOR(SystemError, init, int, CStringRef) - FMT_API ~SystemError() FMT_DTOR_NOEXCEPT; + FMT_API ~SystemError() FMT_DTOR_NOEXCEPT FMT_OVERRIDE; int error_code() const { @@ -4057,10 +4222,10 @@ void arg(WStringRef, const internal::NamedArg&) FMT_DELETED_OR_UNDEFINED; #define FMT_GET_ARG_NAME(type, index) arg##index #if FMT_USE_VARIADIC_TEMPLATES -# define FMT_VARIADIC_(Char, ReturnType, func, call, ...) \ +# define FMT_VARIADIC_(Const, Char, ReturnType, func, call, ...) \ template \ ReturnType func(FMT_FOR_EACH(FMT_ADD_ARG_NAME, __VA_ARGS__), \ - const Args & ... args) { \ + const Args & ... args) Const { \ typedef fmt::internal::ArgArray ArgArray; \ typename ArgArray::Type array{ \ ArgArray::template make >(args)...}; \ @@ -4070,35 +4235,35 @@ void arg(WStringRef, const internal::NamedArg&) FMT_DELETED_OR_UNDEFINED; #else // Defines a wrapper for a function taking __VA_ARGS__ arguments // and n additional arguments of arbitrary types. -# define FMT_WRAP(Char, ReturnType, func, call, n, ...) \ +# define FMT_WRAP(Const, Char, ReturnType, func, call, n, ...) \ template \ inline ReturnType func(FMT_FOR_EACH(FMT_ADD_ARG_NAME, __VA_ARGS__), \ - FMT_GEN(n, FMT_MAKE_ARG)) { \ + FMT_GEN(n, FMT_MAKE_ARG)) Const { \ fmt::internal::ArgArray::Type arr; \ FMT_GEN(n, FMT_ASSIGN_##Char); \ call(FMT_FOR_EACH(FMT_GET_ARG_NAME, __VA_ARGS__), fmt::ArgList( \ fmt::internal::make_type(FMT_GEN(n, FMT_MAKE_REF2)), arr)); \ } -# define FMT_VARIADIC_(Char, ReturnType, func, call, ...) \ - inline ReturnType func(FMT_FOR_EACH(FMT_ADD_ARG_NAME, __VA_ARGS__)) { \ +# define FMT_VARIADIC_(Const, Char, ReturnType, func, call, ...) \ + inline ReturnType func(FMT_FOR_EACH(FMT_ADD_ARG_NAME, __VA_ARGS__)) Const { \ call(FMT_FOR_EACH(FMT_GET_ARG_NAME, __VA_ARGS__), fmt::ArgList()); \ } \ - FMT_WRAP(Char, ReturnType, func, call, 1, __VA_ARGS__) \ - FMT_WRAP(Char, ReturnType, func, call, 2, __VA_ARGS__) \ - FMT_WRAP(Char, ReturnType, func, call, 3, __VA_ARGS__) \ - FMT_WRAP(Char, ReturnType, func, call, 4, __VA_ARGS__) \ - FMT_WRAP(Char, ReturnType, func, call, 5, __VA_ARGS__) \ - FMT_WRAP(Char, ReturnType, func, call, 6, __VA_ARGS__) \ - FMT_WRAP(Char, ReturnType, func, call, 7, __VA_ARGS__) \ - FMT_WRAP(Char, ReturnType, func, call, 8, __VA_ARGS__) \ - FMT_WRAP(Char, ReturnType, func, call, 9, __VA_ARGS__) \ - FMT_WRAP(Char, ReturnType, func, call, 10, __VA_ARGS__) \ - FMT_WRAP(Char, ReturnType, func, call, 11, __VA_ARGS__) \ - FMT_WRAP(Char, ReturnType, func, call, 12, __VA_ARGS__) \ - FMT_WRAP(Char, ReturnType, func, call, 13, __VA_ARGS__) \ - FMT_WRAP(Char, ReturnType, func, call, 14, __VA_ARGS__) \ - FMT_WRAP(Char, ReturnType, func, call, 15, __VA_ARGS__) + FMT_WRAP(Const, Char, ReturnType, func, call, 1, __VA_ARGS__) \ + FMT_WRAP(Const, Char, ReturnType, func, call, 2, __VA_ARGS__) \ + FMT_WRAP(Const, Char, ReturnType, func, call, 3, __VA_ARGS__) \ + FMT_WRAP(Const, Char, ReturnType, func, call, 4, __VA_ARGS__) \ + FMT_WRAP(Const, Char, ReturnType, func, call, 5, __VA_ARGS__) \ + FMT_WRAP(Const, Char, ReturnType, func, call, 6, __VA_ARGS__) \ + FMT_WRAP(Const, Char, ReturnType, func, call, 7, __VA_ARGS__) \ + FMT_WRAP(Const, Char, ReturnType, func, call, 8, __VA_ARGS__) \ + FMT_WRAP(Const, Char, ReturnType, func, call, 9, __VA_ARGS__) \ + FMT_WRAP(Const, Char, ReturnType, func, call, 10, __VA_ARGS__) \ + FMT_WRAP(Const, Char, ReturnType, func, call, 11, __VA_ARGS__) \ + FMT_WRAP(Const, Char, ReturnType, func, call, 12, __VA_ARGS__) \ + FMT_WRAP(Const, Char, ReturnType, func, call, 13, __VA_ARGS__) \ + FMT_WRAP(Const, Char, ReturnType, func, call, 14, __VA_ARGS__) \ + FMT_WRAP(Const, Char, ReturnType, func, call, 15, __VA_ARGS__) #endif // FMT_USE_VARIADIC_TEMPLATES /** @@ -4129,10 +4294,16 @@ void arg(WStringRef, const internal::NamedArg&) FMT_DELETED_OR_UNDEFINED; \endrst */ #define FMT_VARIADIC(ReturnType, func, ...) \ - FMT_VARIADIC_(char, ReturnType, func, return func, __VA_ARGS__) + FMT_VARIADIC_(, char, ReturnType, func, return func, __VA_ARGS__) + +#define FMT_VARIADIC_CONST(ReturnType, func, ...) \ + FMT_VARIADIC_(const, char, ReturnType, func, return func, __VA_ARGS__) #define FMT_VARIADIC_W(ReturnType, func, ...) \ - FMT_VARIADIC_(wchar_t, ReturnType, func, return func, __VA_ARGS__) + FMT_VARIADIC_(, wchar_t, ReturnType, func, return func, __VA_ARGS__) + +#define FMT_VARIADIC_CONST_W(ReturnType, func, ...) \ + FMT_VARIADIC_(const, wchar_t, ReturnType, func, return func, __VA_ARGS__) #define FMT_CAPTURE_ARG_(id, index) ::fmt::arg(#id, id) @@ -4179,20 +4350,22 @@ unsigned parse_nonnegative_int(const Char *&s) { assert('0' <= *s && *s <= '9'); unsigned value = 0; + // Convert to unsigned to prevent a warning. + unsigned max_int = (std::numeric_limits::max)(); + unsigned big = max_int / 10; do { - unsigned new_value = value * 10 + (*s++ - '0'); - // Check if value wrapped around. - if (new_value < value) + // Check for overflow. + if (value > big) { - value = (std::numeric_limits::max)(); + value = max_int + 1; break; } - value = new_value; + value = value * 10 + (*s - '0'); + ++s; } while ('0' <= *s && *s <= '9'); // Convert to unsigned to prevent a warning. - unsigned max_int = (std::numeric_limits::max)(); if (value > max_int) FMT_THROW(FormatError("number is too big")); return value; @@ -4392,7 +4565,8 @@ const Char *BasicFormatter::format( default: FMT_THROW(FormatError("width is not integer")); } - if (value > (std::numeric_limits::max)()) + unsigned max_int = (std::numeric_limits::max)(); + if (value > max_int) FMT_THROW(FormatError("number is too big")); spec.width_ = static_cast(value); } @@ -4435,7 +4609,8 @@ const Char *BasicFormatter::format( default: FMT_THROW(FormatError("precision is not integer")); } - if (value > (std::numeric_limits::max)()) + unsigned max_int = (std::numeric_limits::max)(); + if (value > max_int) FMT_THROW(FormatError("number is too big")); spec.precision_ = static_cast(value); } diff --git a/include/spdlog/fmt/bundled/ostream.h b/include/spdlog/fmt/bundled/ostream.h index cfb8e035..f1b09dfc 100644 --- a/include/spdlog/fmt/bundled/ostream.h +++ b/include/spdlog/fmt/bundled/ostream.h @@ -58,13 +58,15 @@ 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<>); + template + typename EnableIf::type operator<<(const T &); }; No &operator<<(std::ostream &, int); -template +template struct ConvertToIntImpl { // Convert to int only if T doesn't have an overloaded operator<<. @@ -87,6 +89,7 @@ void format_arg(BasicFormatter &f, internal::FormatBuf format_buf(buffer); std::basic_ostream output(&format_buf); + output.exceptions(std::ios_base::failbit | std::ios_base::badbit); output << value; BasicStringRef str(&buffer[0], buffer.size()); diff --git a/include/spdlog/fmt/bundled/printf.h b/include/spdlog/fmt/bundled/printf.h index 7861b460..14242143 100644 --- a/include/spdlog/fmt/bundled/printf.h +++ b/include/spdlog/fmt/bundled/printf.h @@ -152,7 +152,7 @@ public: visit_any_int(value); } - void visit_char(char value) + void visit_char(int value) { if (type_ != 's') visit_any_int(value); @@ -170,7 +170,7 @@ public: using internal::Arg; typedef typename internal::Conditional< is_same::value, U, T>::type TargetType; - if (sizeof(TargetType) <= sizeof(int)) + if (const_check(sizeof(TargetType) <= sizeof(int))) { // Extra casts are used to silence warnings. if (is_signed) diff --git a/include/spdlog/formatter.h b/include/spdlog/formatter.h index 8bf0f43f..b69d7eb1 100644 --- a/include/spdlog/formatter.h +++ b/include/spdlog/formatter.h @@ -29,11 +29,12 @@ class pattern_formatter SPDLOG_FINAL : public formatter { public: - explicit pattern_formatter(const std::string& pattern, pattern_time_type pattern_time = pattern_time_type::local); + explicit pattern_formatter(const std::string& pattern, pattern_time_type pattern_time = pattern_time_type::local, const std::string& eol = spdlog::details::os::default_eol); pattern_formatter(const pattern_formatter&) = delete; pattern_formatter& operator=(const pattern_formatter&) = delete; void format(details::log_msg& msg) override; private: + const std::string _eol; const std::string _pattern; const pattern_time_type _pattern_time; std::vector> _formatters; diff --git a/include/spdlog/sinks/ansicolor_sink.h b/include/spdlog/sinks/ansicolor_sink.h index 517003a2..bd0e3222 100644 --- a/include/spdlog/sinks/ansicolor_sink.h +++ b/include/spdlog/sinks/ansicolor_sink.h @@ -10,7 +10,7 @@ #include "../details/os.h" #include -#include +#include namespace spdlog { @@ -104,7 +104,7 @@ protected: } FILE* target_file_; bool should_do_colors_; - std::map colors_; + std::unordered_map colors_; }; diff --git a/include/spdlog/sinks/file_sinks.h b/include/spdlog/sinks/file_sinks.h index e7856172..e13c81a6 100644 --- a/include/spdlog/sinks/file_sinks.h +++ b/include/spdlog/sinks/file_sinks.h @@ -81,7 +81,7 @@ public: // e.g. calc_filename("logs/mylog.txt, 3) => "logs/mylog.3.txt". static filename_t calc_filename(const filename_t& filename, std::size_t index) { - std::conditional::value, fmt::MemoryWriter, fmt::WMemoryWriter>::type w; + typename std::conditional::value, fmt::MemoryWriter, fmt::WMemoryWriter>::type w; if (index) { filename_t basename, ext; diff --git a/include/spdlog/sinks/wincolor_sink.h b/include/spdlog/sinks/wincolor_sink.h index 8ee3d894..d8211fb1 100644 --- a/include/spdlog/sinks/wincolor_sink.h +++ b/include/spdlog/sinks/wincolor_sink.h @@ -11,7 +11,7 @@ #include #include -#include +#include #include namespace spdlog @@ -50,6 +50,13 @@ public: wincolor_sink(const wincolor_sink& other) = delete; wincolor_sink& operator=(const wincolor_sink& other) = delete; + // change the color for the given level + void set_color(level::level_enum level, WORD color) + { + std::lock_guard lock(base_sink::_mutex); + colors_[level] = color; + } + protected: virtual void _sink_it(const details::log_msg& msg) override { @@ -64,16 +71,9 @@ protected: // windows console always flushed? } - // change the color for the given level - void set_color(level::level_enum level, WORD color) - { - std::lock_guard lock(base_sink::_mutex); - colors_[level] = color; - } - private: HANDLE out_handle_; - std::map colors_; + std::unordered_map colors_; // set color and return the orig console attributes (for resetting later) WORD set_console_attribs(WORD attribs) diff --git a/include/spdlog/spdlog.h b/include/spdlog/spdlog.h index 7cf14f2a..e157c4a8 100644 --- a/include/spdlog/spdlog.h +++ b/include/spdlog/spdlog.h @@ -7,9 +7,7 @@ #pragma once -#define SPDLOG_VERSION "0.16.3" -#include "tweakme.h" #include "common.h" #include "logger.h" diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 22329b4e..119fab55 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -1,19 +1,23 @@ -# -# Tests -# - -enable_testing() - -find_package(Threads) - -# Build Catch unit tests -add_library(catch INTERFACE) -target_include_directories(catch INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}) - -file(GLOB catch_tests LIST_DIRECTORIES false RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} *.cpp *.h *.hpp) - -add_executable(catch_tests ${catch_tests}) -target_link_libraries(catch_tests spdlog ${CMAKE_THREAD_LIBS_INIT}) -add_test(NAME catch_tests COMMAND catch_tests) -file(MAKE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/logs") - +project(spdlog-utests CXX) +enable_testing() +find_package(Threads REQUIRED) + +set(SPDLOG_UTESTS_SOURCES + errors.cpp + file_helper.cpp + file_log.cpp + test_misc.cpp + test_pattern_formatter + includes.h + registry.cpp + test_macros.cpp + utils.cpp + utils.h + main.cpp) + +add_executable(${PROJECT_NAME} ${SPDLOG_UTESTS_SOURCES}) +target_link_libraries(${PROJECT_NAME} PRIVATE Threads::Threads) +target_link_libraries(${PROJECT_NAME} PRIVATE spdlog) + +add_test(NAME ${PROJECT_NAME} COMMAND ${PROJECT_NAME}) +file(MAKE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/logs") diff --git a/tests/Makefile b/tests/Makefile index b1935e75..e8333186 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -1,10 +1,10 @@ CXX ?= g++ ifeq ($(STYLE),printf) $(info *** PRINTF STYLE ***) - CXXFLAGS = -DSPDLOG_FMT_PRINTF -Wall -pedantic -std=c++11 -pthread -O2 -I../include + CXXFLAGS = -DSPDLOG_FMT_PRINTF -Wall -pedantic -std=c++11 -pthread -O3 -I../include else $(info *** FORMAT STYLE ***) - CXXFLAGS = -Wall -pedantic -std=c++11 -pthread -O2 -I../include + CXXFLAGS = -Wall -pedantic -std=c++11 -pthread -O3 -I../include endif LDPFALGS = -pthread diff --git a/tests/catch.hpp b/tests/catch.hpp index 925c6bff..5585d1e3 100644 --- a/tests/catch.hpp +++ b/tests/catch.hpp @@ -2913,7 +2913,7 @@ namespace Catch { for( std::vector >::const_iterator it = m_patterns.begin(), itEnd = m_patterns.end(); it != itEnd; ++it ) if( !(*it)->matches( testCase ) ) return false; - return true; + return true; } }; diff --git a/tests/format.cpp b/tests/test_misc.cpp similarity index 92% rename from tests/format.cpp rename to tests/test_misc.cpp index adb8ba8b..4f6ee72c 100644 --- a/tests/format.cpp +++ b/tests/test_misc.cpp @@ -13,7 +13,7 @@ std::string log_info(const T& what, spdlog::level::level_enum logger_level = spd oss_logger.set_pattern("%v"); oss_logger.info(what); - return oss.str().substr(0, oss.str().length() - spdlog::details::os::eol_size); + return oss.str().substr(0, oss.str().length() - strlen(spdlog::details::os::default_eol)); } @@ -39,7 +39,6 @@ TEST_CASE("basic_logging ", "[basic_logging]") //REQUIRE(log_info(some_logged_class("some_val")) == "some_val"); } - TEST_CASE("log_levels", "[log_levels]") { REQUIRE(log_info("Hello", spdlog::level::err) == ""); @@ -54,3 +53,6 @@ TEST_CASE("log_levels", "[log_levels]") + + + diff --git a/tests/test_pattern_formatter.cpp b/tests/test_pattern_formatter.cpp new file mode 100644 index 00000000..e80fd37d --- /dev/null +++ b/tests/test_pattern_formatter.cpp @@ -0,0 +1,69 @@ +#include "includes.h" + +// log to str and return it +static std::string log_to_str(const std::string& msg, std::shared_ptr formatter = nullptr) +{ + std::ostringstream oss; + auto oss_sink = std::make_shared(oss); + spdlog::logger oss_logger("pattern_tester", oss_sink); + oss_logger.set_level(spdlog::level::info); + if (formatter) oss_logger.set_formatter(formatter); + oss_logger.info(msg); + return oss.str(); +} +TEST_CASE("custom eol", "[pattern_formatter]") +{ + std::string msg = "Hello custom eol test"; + std::string eol = ";)"; + auto formatter = std::make_shared("%v", spdlog::pattern_time_type::local, ";)"); + + REQUIRE(log_to_str(msg, formatter) == msg + eol); +} + +TEST_CASE("empty format", "[pattern_formatter]") +{ + auto formatter = std::make_shared("", spdlog::pattern_time_type::local, ""); + REQUIRE(log_to_str("Some message", formatter) == ""); +} + +TEST_CASE("empty format2", "[pattern_formatter]") +{ + auto formatter = std::make_shared("", spdlog::pattern_time_type::local, "\n"); + REQUIRE(log_to_str("Some message", formatter) == "\n"); +} + +TEST_CASE("level", "[pattern_formatter]") +{ + auto formatter = std::make_shared("[%l] %v", spdlog::pattern_time_type::local, "\n"); + REQUIRE(log_to_str("Some message", formatter) == "[info] Some message\n"); +} + + +TEST_CASE("short level", "[pattern_formatter]") +{ + auto formatter = std::make_shared("[%L] %v", spdlog::pattern_time_type::local, "\n"); + REQUIRE(log_to_str("Some message", formatter) == "[I] Some message\n"); +} + +TEST_CASE("name", "[pattern_formatter]") +{ + auto formatter = std::make_shared("[%n] %v", spdlog::pattern_time_type::local, "\n"); + REQUIRE(log_to_str("Some message", formatter) == "[pattern_tester] Some message\n"); +} + + +TEST_CASE("date MM/DD/YY ", "[pattern_formatter]") +{ + using namespace::std::chrono; + auto formatter = std::make_shared("%D %v", spdlog::pattern_time_type::local, "\n"); + auto now_tm = spdlog::details::os::localtime(); + std::stringstream oss; + oss << std::setfill('0') << std::setw(2) << now_tm.tm_mon + 1 << "/" << now_tm.tm_mday << "/" << (now_tm.tm_year + 1900) % 1000 << " Some message\n"; + REQUIRE(log_to_str("Some message", formatter) == oss.str()); +} + + + + + + diff --git a/tests/tests.vcxproj b/tests/tests.vcxproj index ef9d78f9..286c6979 100644 --- a/tests/tests.vcxproj +++ b/tests/tests.vcxproj @@ -21,6 +21,7 @@ {59A07559-5F38-4DD6-A7FA-DB4153690B42} tests + 10.0.16299.0 @@ -128,10 +129,11 @@ - + + diff --git a/tests/tests.vcxproj.filters b/tests/tests.vcxproj.filters index adc86e7b..d1fdf73d 100644 --- a/tests/tests.vcxproj.filters +++ b/tests/tests.vcxproj.filters @@ -18,9 +18,6 @@ Source Files - - Source Files - Source Files @@ -39,6 +36,12 @@ Source Files + + Source Files + + + Source Files +