#pragma once #include #include #include #include #include #include #include #include #define LOG_FUNC(...) Compat::LogFunc logFunc(__VA_ARGS__) #define LOG_RESULT(...) logFunc.setResult(__VA_ARGS__) #define LOG_ONCE(msg) \ { \ static bool isAlreadyLogged = false; \ if (!isAlreadyLogged) \ { \ Compat::Log() << msg; \ isAlreadyLogged = true; \ } \ } std::ostream& operator<<(std::ostream& os, std::nullptr_t); std::ostream& operator<<(std::ostream& os, const char* str); std::ostream& operator<<(std::ostream& os, const unsigned char* data); std::ostream& operator<<(std::ostream& os, const WCHAR* wstr); namespace Compat { using ::operator<<; namespace detail { template struct Hex { explicit Hex(T val) : val(val) {} T val; }; template struct Array { Array(const Elem* elem, const unsigned long size) : elem(elem), size(size) {} const Elem* elem; const unsigned long size; }; template struct Out { explicit Out(T val) : val(val) {} T val; }; class LogParams; class LogFirstParam { public: LogFirstParam(std::ostream& os) : m_os(os) {} template LogParams operator<<(const T& val) { m_os << val; return LogParams(m_os); } protected: std::ostream& m_os; }; class LogParams { public: LogParams(std::ostream& os) : m_os(os) {} template LogParams& operator<<(const T& val) { m_os << ',' << val; return *this; } operator std::ostream&() { return m_os; } private: std::ostream& m_os; }; template std::ostream& operator<<(std::ostream& os, Hex hex) { os << "0x" << std::hex << hex.val << std::dec; return os; } template std::ostream& operator<<(std::ostream& os, Array array) { os << '['; if (Log::isPointerDereferencingAllowed()) { if (0 != array.size) { os << array.elem[0]; } for (unsigned long i = 1; i < array.size; ++i) { os << ',' << array.elem[i]; } } return os << ']'; } template std::ostream& operator<<(std::ostream& os, Out out) { ++Log::s_outParamDepth; os << out.val; --Log::s_outParamDepth; return os; } } template detail::Hex hex(T val) { return detail::Hex(val); } template detail::Array array(const Elem* elem, const unsigned long size) { return detail::Array(elem, size); } template detail::Out out(const T& val) { return detail::Out(val); } class Log { public: Log(); ~Log(); template Log& operator<<(const T& t) { s_logFile << t; return *this; } static bool isPointerDereferencingAllowed() { return s_isLeaveLog || 0 == s_outParamDepth; } protected: template Log(const char* prefix, const char* funcName, Params... params) : Log() { s_logFile << prefix << ' ' << funcName << '('; toList(params...); s_logFile << ')'; } private: friend class LogFunc; template friend std::ostream& detail::operator<<(std::ostream& os, detail::Out out); void toList() { } template void toList(Param param) { s_logFile << param; } template void toList(Param firstParam, Params... remainingParams) { s_logFile << firstParam << ", "; toList(remainingParams...); } ScopedCriticalSection m_lock; static thread_local DWORD s_indent; static thread_local DWORD s_outParamDepth; static thread_local bool s_isLeaveLog; static std::ofstream s_logFile; }; class LogStruct : public detail::LogFirstParam { public: LogStruct(std::ostream& os) : detail::LogFirstParam(os) { m_os << '{'; } ~LogStruct() { m_os << '}'; } }; #ifdef DEBUGLOGS typedef Log LogDebug; class LogFunc { public: template LogFunc(const char* funcName, Params... params) : m_printCall([=](Log& log) { log << funcName << '('; log.toList(params...); log << ')'; }) { Log log; log << "> "; m_printCall(log); Log::s_indent += 2; } ~LogFunc() { Log::s_isLeaveLog = true; Log::s_indent -= 2; Log log; log << "< "; m_printCall(log); if (m_printResult) { log << " = "; m_printResult(log); } Log::s_isLeaveLog = false; } template T setResult(T result) { m_printResult = [=](Log& log) { log << std::hex << result << std::dec; }; return result; } private: std::function m_printCall; std::function m_printResult; }; #else class LogDebug { public: template LogDebug& operator<<(const T&) { return *this; } }; class LogFunc { public: template LogFunc(const char* /*funcName*/, Params...) { } template T setResult(T result) { return result; } }; #endif } template typename std::enable_if::value && !std::is_same::value, std::ostream&>::type operator<<(std::ostream& os, const T& t) { return os << static_cast(&t); } template typename std::enable_if::value, std::ostream&>::type operator<<(std::ostream& os, T* t) { if (!t) { return os << "null"; } if (!Compat::Log::isPointerDereferencingAllowed()) { return os << static_cast(t); } return os << *t; } template std::ostream& operator<<(std::ostream& os, T** t) { if (!t) { return os << "null"; } os << static_cast(t); if (Compat::Log::isPointerDereferencingAllowed()) { os << '=' << *t; } return os; }