466 lines
14 KiB
C
466 lines
14 KiB
C
|
#pragma once
|
||
|
#include <cstdarg>
|
||
|
#include <plog/Severity.h>
|
||
|
#include <plog/Util.h>
|
||
|
|
||
|
#ifdef __cplusplus_cli
|
||
|
#include <vcclr.h> // 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<class T>
|
||
|
inline T& declval()
|
||
|
{
|
||
|
#ifdef __INTEL_COMPILER
|
||
|
# pragma warning(suppress: 327) // NULL reference is not allowed
|
||
|
#endif
|
||
|
return *reinterpret_cast<T*>(0);
|
||
|
}
|
||
|
|
||
|
template<bool B, class T = void>
|
||
|
struct enableIf {};
|
||
|
|
||
|
template<class T>
|
||
|
struct enableIf<true, T> { typedef T type; };
|
||
|
|
||
|
struct No { char a[1]; };
|
||
|
struct Yes { char a[2]; };
|
||
|
|
||
|
template <class From, class To>
|
||
|
struct isConvertible
|
||
|
{
|
||
|
// `+ sizeof(U*)` is required for GCC 4.5-4.7
|
||
|
template<class U>
|
||
|
static typename enableIf<!!(sizeof(static_cast<To>(meta::declval<U>())) + sizeof(U*)), Yes>::type test(int);
|
||
|
|
||
|
template<class U>
|
||
|
static No test(...);
|
||
|
|
||
|
enum { value = sizeof(test<From>(0)) == sizeof(Yes) };
|
||
|
};
|
||
|
|
||
|
template <class T>
|
||
|
struct isConvertibleToString : isConvertible<T, std::string> {};
|
||
|
|
||
|
#if PLOG_ENABLE_WCHAR_INPUT
|
||
|
template <class T>
|
||
|
struct isConvertibleToWString : isConvertible<T, std::wstring> {};
|
||
|
#endif
|
||
|
|
||
|
template <class T>
|
||
|
struct isContainer
|
||
|
{
|
||
|
template<class U>
|
||
|
static typename meta::enableIf<!!(sizeof(
|
||
|
#if defined(_MSC_VER) && _MSC_VER < 1700 // MSVC 2010 doesn't understand `typename T::const_iterator`
|
||
|
meta::declval<U>().begin()) + sizeof(meta::declval<U>().end()
|
||
|
#else
|
||
|
typename U::const_iterator
|
||
|
#endif
|
||
|
)), Yes>::type test(int);
|
||
|
|
||
|
template<class U>
|
||
|
static No test(...);
|
||
|
|
||
|
enum { value = sizeof(test<T>(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 <class T>
|
||
|
struct isFilesystemPath
|
||
|
{
|
||
|
template<class U>
|
||
|
static typename meta::enableIf<!!(sizeof(meta::declval<U>().preferred_separator)), Yes>::type test(int);
|
||
|
|
||
|
template<class U>
|
||
|
static No test(...);
|
||
|
|
||
|
enum { value = sizeof(test<T>(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<const wchar_t*>(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<const char*>(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<const char*>(data));
|
||
|
# else
|
||
|
plog::detail::operator<<(stream, util::toWide(reinterpret_cast<const char*>(data), codePage::kUTF8));
|
||
|
# endif
|
||
|
}
|
||
|
#endif //__cpp_char8_t
|
||
|
|
||
|
// Print `std::pair`
|
||
|
template<class T1, class T2>
|
||
|
inline void operator<<(util::nostringstream& stream, const std::pair<T1, T2>& 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<class T>
|
||
|
inline typename meta::enableIf<meta::isConvertibleToString<T>::value, void>::type operator<<(util::nostringstream& stream, const T& data)
|
||
|
{
|
||
|
plog::detail::operator<<(stream, static_cast<std::string>(data));
|
||
|
}
|
||
|
|
||
|
#if PLOG_ENABLE_WCHAR_INPUT
|
||
|
// Print data that can be casted to `std::wstring`
|
||
|
template<class T>
|
||
|
inline typename meta::enableIf<meta::isConvertibleToWString<T>::value, void>::type operator<<(util::nostringstream& stream, const T& data)
|
||
|
{
|
||
|
plog::detail::operator<<(stream, static_cast<std::wstring>(data));
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
// Print std containers
|
||
|
template<class T>
|
||
|
inline typename meta::enableIf<meta::isContainer<T>::value &&
|
||
|
!meta::isConvertibleToString<T>::value &&
|
||
|
#if PLOG_ENABLE_WCHAR_INPUT
|
||
|
!meta::isConvertibleToWString<T>::value &&
|
||
|
#endif
|
||
|
!meta::isFilesystemPath<T>::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<const System::Char> ptr = PtrToStringChars(data);
|
||
|
plog::detail::operator<<(stream, static_cast<const wchar_t*>(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<bool Value>
|
||
|
struct valueType { enum { value = Value }; };
|
||
|
|
||
|
template<class T, class Stream>
|
||
|
inline No operator<<(Stream&, const T&);
|
||
|
|
||
|
template<class T, class Stream>
|
||
|
struct isStreamable : valueType<sizeof(operator<<(meta::declval<Stream>(), meta::declval<const T>())) != sizeof(No)> {};
|
||
|
|
||
|
template<class Stream>
|
||
|
struct isStreamable<std::ios_base& PLOG_CDECL (std::ios_base&), Stream> : valueType<true> {};
|
||
|
|
||
|
template<class Stream, size_t N>
|
||
|
struct isStreamable<wchar_t[N], Stream> : valueType<false> {};
|
||
|
|
||
|
template<class Stream, size_t N>
|
||
|
struct isStreamable<const wchar_t[N], Stream> : valueType<false> {};
|
||
|
|
||
|
// 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<const wchar_t*, std::ostream> : valueType<false> {};
|
||
|
|
||
|
# 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<class Stream, size_t N>
|
||
|
struct isStreamable<char8_t[N], Stream> : valueType<false> {};
|
||
|
|
||
|
template<class Stream>
|
||
|
struct isStreamable<const char8_t*, Stream> : valueType<false> {};
|
||
|
# 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<class T>
|
||
|
inline typename meta::enableIf<meta::isStreamable<T, std::wostream>::value &&
|
||
|
!meta::isStreamable<T, std::ostream>::value &&
|
||
|
!meta::isConvertibleToWString<T>::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<class T>
|
||
|
inline typename meta::enableIf<meta::isStreamable<T, std::ostream>::value &&
|
||
|
!meta::isStreamable<T, std::wostream>::value &&
|
||
|
!meta::isConvertibleToString<T>::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<typename T>
|
||
|
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<void>(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<void>(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;
|
||
|
};
|
||
|
}
|