#pragma once #include #include #include #ifdef __cplusplus_cli #include // For PtrToStringChars #endif namespace plog { namespace detail { #if !defined(_MSC_VER) || _MSC_VER > 1400 // MSVC 2005 doesn't understand `enableIf`, so drop all `meta` namespace meta { template inline T& declval() { #ifdef __INTEL_COMPILER # pragma warning(suppress: 327) // NULL reference is not allowed #endif return *reinterpret_cast(0); } template struct enableIf {}; template struct enableIf { typedef T type; }; struct No { char a[1]; }; struct Yes { char a[2]; }; template struct isConvertible { // `+ sizeof(U*)` is required for GCC 4.5-4.7 template static typename enableIf(meta::declval())) + sizeof(U*)), Yes>::type test(int); template static No test(...); enum { value = sizeof(test(0)) == sizeof(Yes) }; }; template struct isConvertibleToString : isConvertible {}; #if PLOG_ENABLE_WCHAR_INPUT template struct isConvertibleToWString : isConvertible {}; #endif template struct isContainer { template static typename meta::enableIf().begin()) + sizeof(meta::declval().end() #else typename U::const_iterator #endif )), Yes>::type test(int); template static No test(...); enum { value = sizeof(test(0)) == sizeof(Yes) }; }; // Detects `std::filesystem::path` and `boost::filesystem::path`. They look like containers // but we don't want to treat them as containers, so we use this detector to filter them out. template struct isFilesystemPath { template static typename meta::enableIf().preferred_separator)), Yes>::type test(int); template static No test(...); enum { value = sizeof(test(0)) == sizeof(Yes) }; }; } #endif ////////////////////////////////////////////////////////////////////////// // Stream output operators as free functions #if PLOG_ENABLE_WCHAR_INPUT inline void operator<<(util::nostringstream& stream, const wchar_t* data) { data = data ? data : L"(null)"; # ifdef _WIN32 # if PLOG_CHAR_IS_UTF8 std::operator<<(stream, util::toNarrow(data, codePage::kUTF8)); # else std::operator<<(stream, data); # endif # else std::operator<<(stream, util::toNarrow(data)); # endif } inline void operator<<(util::nostringstream& stream, wchar_t* data) { plog::detail::operator<<(stream, const_cast(data)); } inline void operator<<(util::nostringstream& stream, const std::wstring& data) { plog::detail::operator<<(stream, data.c_str()); } #endif inline void operator<<(util::nostringstream& stream, const char* data) { data = data ? data : "(null)"; #if defined(_WIN32) && defined(__BORLANDC__) # if PLOG_CHAR_IS_UTF8 stream << data; # else stream << util::toWide(data); # endif #elif defined(_WIN32) # if PLOG_CHAR_IS_UTF8 std::operator<<(stream, data); # else std::operator<<(stream, util::toWide(data)); # endif #else std::operator<<(stream, data); #endif } inline void operator<<(util::nostringstream& stream, char* data) { plog::detail::operator<<(stream, const_cast(data)); } inline void operator<<(util::nostringstream& stream, const std::string& data) { plog::detail::operator<<(stream, data.c_str()); } #ifdef __cpp_char8_t inline void operator<<(util::nostringstream& stream, const char8_t* data) { # if PLOG_CHAR_IS_UTF8 plog::detail::operator<<(stream, reinterpret_cast(data)); # else plog::detail::operator<<(stream, util::toWide(reinterpret_cast(data), codePage::kUTF8)); # endif } #endif //__cpp_char8_t // Print `std::pair` template inline void operator<<(util::nostringstream& stream, const std::pair& data) { stream << data.first; stream << ":"; stream << data.second; } #if defined(__clang__) || !defined(__GNUC__) || (__GNUC__ * 100 + __GNUC_MINOR__) >= 405 // skip for GCC < 4.5 due to https://gcc.gnu.org/bugzilla/show_bug.cgi?id=38600 #if !defined(_MSC_VER) || _MSC_VER > 1400 // MSVC 2005 doesn't understand `enableIf`, so drop all `meta` // Print data that can be casted to `std::string` template inline typename meta::enableIf::value, void>::type operator<<(util::nostringstream& stream, const T& data) { plog::detail::operator<<(stream, static_cast(data)); } #if PLOG_ENABLE_WCHAR_INPUT // Print data that can be casted to `std::wstring` template inline typename meta::enableIf::value, void>::type operator<<(util::nostringstream& stream, const T& data) { plog::detail::operator<<(stream, static_cast(data)); } #endif // Print std containers template inline typename meta::enableIf::value && !meta::isConvertibleToString::value && #if PLOG_ENABLE_WCHAR_INPUT !meta::isConvertibleToWString::value && #endif !meta::isFilesystemPath::value, void>::type operator<<(util::nostringstream& stream, const T& data) { stream << "["; for (typename T::const_iterator it = data.begin(); it != data.end();) { stream << *it; if (++it == data.end()) { break; } stream << ", "; } stream << "]"; } #endif #endif #ifdef __cplusplus_cli // Print managed C++ `System::String^` inline void operator<<(util::nostringstream& stream, System::String^ data) { cli::pin_ptr ptr = PtrToStringChars(data); plog::detail::operator<<(stream, static_cast(ptr)); } #endif #if PLOG_ENABLE_WCHAR_INPUT && (!defined(_MSC_VER) || _MSC_VER > 1400) // MSVC 2005 doesn't understand `enableIf`, so drop all `meta` namespace meta { template struct valueType { enum { value = Value }; }; template inline No operator<<(Stream&, const T&); template struct isStreamable : valueType(), meta::declval())) != sizeof(No)> {}; template struct isStreamable : valueType {}; template struct isStreamable : valueType {}; template struct isStreamable : valueType {}; // meta doesn't work well for deleted functions and C++20 has `operator<<(std::ostream&, const wchar_t*) = delete` so exlicitly define it template<> struct isStreamable : valueType {}; # ifdef __cpp_char8_t // meta doesn't work well for deleted functions and C++20 has `operator<<(std::ostream&, const char8_t*) = delete` so exlicitly define it template struct isStreamable : valueType {}; template struct isStreamable : valueType {}; # endif //__cpp_char8_t } # if PLOG_CHAR_IS_UTF8 // Print types that can be streamed into `std::owstringstream` but not into `std::ostringstream` when we use UTF-8 on Windows template inline typename meta::enableIf::value && !meta::isStreamable::value && !meta::isConvertibleToWString::value, void>::type operator<<(std::ostringstream& stream, const T& data) { std::wostringstream ss; ss << data; stream << ss.str(); } # else // Print types that can be streamed into `std::ostringstream` but not into `std::owstringstream` when we use `wchar_t` on Windows template inline typename meta::enableIf::value && !meta::isStreamable::value && !meta::isConvertibleToString::value, void>::type operator<<(std::wostringstream& stream, const T& data) { std::ostringstream ss; ss << data; stream << ss.str(); } # endif #endif } class Record { public: Record(Severity severity, const char* func, size_t line, const char* file, const void* object, int instanceId) : m_severity(severity), m_tid(util::gettid()), m_object(object), m_line(line), m_func(func), m_file(file), m_instanceId(instanceId) { util::ftime(&m_time); } Record& ref() { return *this; } ////////////////////////////////////////////////////////////////////////// // Stream output operators Record& operator<<(char data) { char str[] = { data, 0 }; return *this << str; } #if PLOG_ENABLE_WCHAR_INPUT Record& operator<<(wchar_t data) { wchar_t str[] = { data, 0 }; return *this << str; } #endif Record& operator<<(util::nostream& (PLOG_CDECL *data)(util::nostream&)) { m_message << data; return *this; } #ifdef QT_VERSION Record& operator<<(const QString& data) { # if PLOG_CHAR_IS_UTF8 return *this << data.toStdString(); # else return *this << data.toStdWString(); # endif } # if QT_VERSION < 0x060000 Record& operator<<(const QStringRef& data) { return *this << data.toString(); } # endif # ifdef QSTRINGVIEW_H Record& operator<<(QStringView data) { return *this << data.toString(); } # endif #endif template Record& operator<<(const T& data) { using namespace plog::detail; m_message << data; return *this; } #ifndef __cplusplus_cli Record& printf(const char* format, ...) { using namespace util; char* str = NULL; va_list ap; va_start(ap, format); int len = vasprintf(&str, format, ap); static_cast(len); va_end(ap); *this << str; free(str); return *this; } #ifdef _WIN32 Record& printf(const wchar_t* format, ...) { using namespace util; wchar_t* str = NULL; va_list ap; va_start(ap, format); int len = vaswprintf(&str, format, ap); static_cast(len); va_end(ap); *this << str; free(str); return *this; } #endif #endif //__cplusplus_cli ////////////////////////////////////////////////////////////////////////// // Getters virtual const util::Time& getTime() const { return m_time; } virtual Severity getSeverity() const { return m_severity; } virtual unsigned int getTid() const { return m_tid; } virtual const void* getObject() const { return m_object; } virtual size_t getLine() const { return m_line; } virtual const util::nchar* getMessage() const { m_messageStr = m_message.str(); return m_messageStr.c_str(); } virtual const char* getFunc() const { m_funcStr = util::processFuncName(m_func); return m_funcStr.c_str(); } virtual const char* getFile() const { return m_file; } virtual ~Record() // virtual destructor to satisfy -Wnon-virtual-dtor warning { } virtual int getInstanceId() const { return m_instanceId; } private: util::Time m_time; const Severity m_severity; const unsigned int m_tid; const void* const m_object; const size_t m_line; util::nostringstream m_message; const char* const m_func; const char* const m_file; const int m_instanceId; mutable std::string m_funcStr; mutable util::nstring m_messageStr; }; }