cpp-demo-project/include/plog/Record.h

466 lines
14 KiB
C
Raw Permalink Normal View History

2024-01-22 12:05:59 +01:00
#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;
};
}