Cxbx-Reloaded/src/common/Logging.h

638 lines
22 KiB
C++

// ******************************************************************
// *
// * This file is part of the Cxbx project.
// *
// * Cxbx and Cxbe are free software; you can redistribute them
// * and/or modify them under the terms of the GNU General Public
// * License as published by the Free Software Foundation; either
// * version 2 of the license, or (at your option) any later version.
// *
// * This program is distributed in the hope that it will be useful,
// * but WITHOUT ANY WARRANTY; without even the implied warranty of
// * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// * GNU General Public License for more details.
// *
// * You should have recieved a copy of the GNU General Public License
// * along with this program; see the file COPYING.
// * If not, write to the Free Software Foundation, Inc.,
// * 59 Temple Place - Suite 330, Bostom, MA 02111-1307, USA.
// *
// * (c) 2016 Patrick van Logchem <pvanlogchem@gmail.com>
// * (c) 2019 ergo720
// *
// * All rights reserved
// *
// ******************************************************************
#ifndef _LOGGING_H
#define _LOGGING_H
#include <windows.h> // For DWORD
#include <sstream> // For std::stringstream
#include <iostream> // For std::cout
#include <iomanip> // For std::setw
#include <atomic> // For atomic_bool and atomic_uint
#include "common\util\CxbxUtil.h" // For g_bPrintfOn and to_underlying
// NOTE: using ERROR2 since windows.h imports an ERROR macro which would conflict otherwise
typedef enum class _LOG_LEVEL {
DEBUG = 0,
INFO,
WARNING,
ERROR2,
FATAL,
MAX,
}LOG_LEVEL;
typedef enum class _CXBXR_MODULE: unsigned int {
// general
CXBXR = 0,
XBE,
INIT,
VMEM,
PMEM,
GUI,
EEPR,
RSA,
POOLMEM,
D3D8,
D3DST,
D3DCVT,
DSOUND,
XAPI,
XACT,
XGRP,
XONLINE,
FS,
PSHB,
PXSH,
VTXSH,
VSHCACHE,
VTXB,
DINP,
XINP,
SDL,
FILE,
X86,
HLE,
NET,
MCPX,
NV2A,
SMC,
OHCI,
USB,
HUB,
XIDCTRL,
ADM,
INPSYS,
DSBUFFER,
DSSTREAM,
DS3DCALC,
XMO,
RINP,
JVS,
LIBUSB,
// kernel
KRNL,
LOG,
XBOX,
XBDM,
AV,
DBG,
EX,
FSC,
HAL,
IO,
KD,
KE,
KI,
MM,
NT,
OB,
PS,
RTL,
XC,
XE,
// max
MAX,
}CXBXR_MODULE;
extern std::atomic_bool g_EnabledModules[to_underlying(CXBXR_MODULE::MAX)];
extern const char* g_EnumModules2String[to_underlying(CXBXR_MODULE::MAX)];
extern std::atomic_int g_CurrentLogLevel;
extern std::atomic_bool g_CurrentLogPopupTestCase;
// print out a log message to the console or kernel debug log file if level is high enough
void EmuLogEx(CXBXR_MODULE cxbxr_module, LOG_LEVEL level, const char *szWarningMessage, ...);
void EmuLogInit(LOG_LEVEL level, const char *szWarningMessage, ...);
#define EmuLog(level, fmt, ...) EmuLogEx(LOG_PREFIX, level, fmt, ##__VA_ARGS__)
extern inline void log_get_settings();
extern inline void log_sync_config();
void log_set_config(int LogLevel, unsigned int* LoggedModules, bool LogPopupTestCase);
void log_generate_active_filter_output(const CXBXR_MODULE cxbxr_module);
// Use emulation environment to manage popup messages
// If log_init_popup_msg is not called at earliest point of emulation.
// Then users will have a chance of popup message appear during start of emulation in full screen.
void log_init_popup_msg();
typedef enum class _PopupIcon {
Unknown = 0,
Question,
Info,
Warning,
Error
} PopupIcon;
typedef enum class _PopupButtons {
Unknown = 0,
Ok,
OkCancel,
AbortRetryIgnore,
YesNoCancel,
YesNo,
RetryCancel
} PopupButtons;
typedef enum class _PopupReturn {
Unknown = 0,
Ok,
Cancel,
Abort,
Retry,
Ignore,
Yes,
No
} PopupReturn;
PopupReturn PopupCustomEx(const void* hwnd, const CXBXR_MODULE cxbxr_module, const LOG_LEVEL level, const PopupIcon icon, const PopupButtons buttons, const PopupReturn ret_default, const char* message, ...);
#define PopupCustom(hwnd, level, icon, buttons, ret_default, fmt, ...) PopupCustomEx(hwnd, LOG_PREFIX, level, icon, buttons, ret_default, fmt, ## __VA_ARGS__)
#define PopupQuestionEx(hwnd, level, buttons, ret_default, fmt, ...) PopupCustom(hwnd, level, PopupIcon::Question, buttons, ret_default, fmt, ## __VA_ARGS__)
#define PopupQuestion(hwnd, fmt, ...) PopupQuestionEx(hwnd, LOG_LEVEL::INFO, PopupButtons::YesNoCancel, PopupReturn::Cancel, fmt, ## __VA_ARGS__)
#define PopupInfoEx(hwnd, buttons, ret_default, fmt, ...) PopupCustom(hwnd, LOG_LEVEL::INFO, PopupIcon::Info, buttons, ret_default, fmt, ## __VA_ARGS__)
#define PopupInfo(hwnd, fmt, ...) (void)PopupInfoEx(hwnd, PopupButtons::Ok, PopupReturn::Ok, fmt, ## __VA_ARGS__)
#define PopupWarningEx(hwnd, buttons, ret_default, fmt, ...) PopupCustom(hwnd, LOG_LEVEL::WARNING, PopupIcon::Warning, buttons, ret_default, fmt, ## __VA_ARGS__)
#define PopupWarning(hwnd, fmt, ...) (void)PopupWarningEx(hwnd, PopupButtons::Ok, PopupReturn::Ok, fmt, ## __VA_ARGS__)
#define PopupErrorEx(hwnd, buttons, ret_default, fmt, ...) PopupCustom(hwnd, LOG_LEVEL::ERROR2, PopupIcon::Error, buttons, ret_default, fmt, ## __VA_ARGS__)
#define PopupError(hwnd, fmt, ...) (void)PopupErrorEx(hwnd, PopupButtons::Ok, PopupReturn::Ok, fmt, ## __VA_ARGS__)
#define PopupFatalEx(hwnd, buttons, ret_default, fmt, ...) PopupCustom(hwnd, LOG_LEVEL::FATAL, PopupIcon::Error, buttons, ret_default, fmt, ## __VA_ARGS__)
#define PopupFatal(hwnd, fmt, ...) (void)PopupFatalEx(hwnd, PopupButtons::Ok, PopupReturn::Ok, fmt, ## __VA_ARGS__)
// For LOG_TEST_CASE
extern inline void EmuLogOutputEx(const CXBXR_MODULE cxbxr_module, const LOG_LEVEL level, const char *szWarningMessage, ...);
//
// __FILENAME__
//
// From https://stackoverflow.com/questions/31050113/how-to-extract-the-source-filename-without-path-and-suffix-at-compile-time
constexpr const char* str_end(const char *str) {
return *str ? str_end(str + 1) : str;
}
constexpr bool str_slant(const char *str) {
return *str == '\\' ? true : (*str ? str_slant(str + 1) : false);
}
constexpr const char* r_slant(const char* str) {
return *str == '\\' ? (str + 1) : r_slant(str - 1);
}
constexpr const char* file_name(const char* str) {
return str_slant(str) ? r_slant(str_end(str)) : str;
}
#define __FILENAME__ file_name(__FILE__)
//
// Character escaping functions
//
extern const bool needs_escape(const wint_t _char);
extern inline void output_char(std::ostream& os, char c);
extern inline void output_wchar(std::ostream& os, wchar_t c);
//
// Data sanitization functions
//
// By default, sanitization functions simply return the given argument
// (type and value) which results in calls to standard output writers.
template<class T>
inline auto _log_sanitize(T value, int ignored_length = 0)
{
// This is necessary because C++20 has deleted the overloaded operator<< of ostream for wchar_t, char8_t, char16_t and char32_t.
// The proper solution is to use wide strings in all our logging macros/functions, see https://github.com/Cxbx-Reloaded/Cxbx-Reloaded/issues/743
if constexpr (std::is_same_v<xbox::wchar_xt, std::remove_cvref_t<std::remove_pointer_t<T>>>) {
if constexpr (std::is_pointer_v<T>) {
const wchar_t *wstr = reinterpret_cast<const wchar_t *>(value);
std::size_t len = std::wcslen(wstr);
std::string dst(len + 1, '\0');
std::mbstate_t ps{};
std::wcsrtombs(dst.data(), &wstr, len, &ps);
return dst;
}
else {
const wchar_t *wstr = reinterpret_cast<const wchar_t *>(&value);
std::string dst(2, '\0');
std::mbstate_t ps{};
std::wcsrtombs(dst.data(), &wstr, 1, &ps);
return dst;
}
}
else {
return value;
}
}
#if 0 // TODO FIXME : Disabled for now, as this is incorrectly called for INT types too
// Convert booleans to strings properly
inline const char * _log_sanitize(BOOL value, int ignored_length = 0)
{
return value ? "TRUE" : "FALSE";
}
#endif
// Macro to ease declaring a _log_sanitize overload (invokeable via C) for type T
#define LOG_SANITIZE_HEADER(C, T) \
std::ostream& operator<<( \
std::ostream& os, \
const Sane##C& container) \
#define LOG_SANITIZE(C, T) \
struct Sane##C \
{ \
T value; \
int max; \
Sane##C(T _v, int _m = 80) : value(_v), max(_m) { } \
}; \
\
inline Sane##C C(T value, int max = 80) \
{ \
return Sane##C(value, max); \
} \
\
inline Sane##C _log_sanitize(T value, int max = 80) \
{ \
return C(value, max); \
} \
\
extern LOG_SANITIZE_HEADER(C, T) \
// Hex output (type safe)
// https://stackoverflow.com/questions/673240/how-do-i-print-an-unsigned-char-as-hex-in-c-using-ostream
LOG_SANITIZE(hex1, uint8_t);
LOG_SANITIZE(hex2, uint16_t);
LOG_SANITIZE(hex4, uint32_t);
// Character output (escaped into a C-string representation)
// https://en.wikipedia.org/wiki/Escape_sequences_in_C
LOG_SANITIZE(sanitized_char, char);
LOG_SANITIZE(sanitized_wchar, wchar_t);
LOG_SANITIZE(sanitized_char_pointer, char *);
LOG_SANITIZE(sanitized_wchar_pointer, wchar_t *);
//
// Function (and argument) logging defines
//
constexpr const int str_length(const char* str) {
return str_end(str) - str;
}
constexpr const char* str_skip_prefix(const char* str, const char *prefix) {
return (*str == *prefix) ? str_skip_prefix(str + 1, prefix + 1) : str;
}
constexpr const char* remove_prefix(const char* str, const char *prefix) {
return (str_skip_prefix(str, prefix) == str + str_length(prefix)) ? str_skip_prefix(str, prefix) : str;
}
static constexpr const char* xbox_prefix = "xbox::";
static constexpr const char* emupatch_prefix = "EmuPatch_"; // See #define EMUPATCH
constexpr const char* remove_emupatch_prefix(const char* str) {
// return an empty string when str isn't given
// skip xbox:: and/or EmuPatch_ prefix if present
return remove_prefix(remove_prefix(str, xbox_prefix), emupatch_prefix);
}
#define LOG_ARG_START "\n " << std::setfill(' ') << std::setw(20) << std::left
#define LOG_ARG_OUT_START "\n OUT " << std::setfill(' ') << std::setw(18) << std::left
#ifndef LOG_PREFIX
#define LOG_PREFIX __FILENAME__
#endif // LOG_PREFIX
// For thread_local, see : https://en.cppreference.com/w/cpp/language/storage_duration
// TODO : Use Boost.Format https://www.boost.org/doc/libs/1_53_0/libs/format/index.html
extern thread_local std::string _logThreadPrefix;
// Checks if this log should be printed or not
#define LOG_CHECK_ENABLED_EX(cxbxr_module, level) \
if (g_EnabledModules[to_underlying(cxbxr_module)] && to_underlying(level) >= g_CurrentLogLevel)
// Checks if this log should be printed or not
#define LOG_CHECK_ENABLED(level) \
LOG_CHECK_ENABLED_EX(LOG_PREFIX, level)
#define LOG_THREAD_INIT \
if (_logThreadPrefix.length() == 0) { \
std::stringstream tmp; \
tmp << "[" << hexstring16 << GetCurrentThreadId() << "] "; \
_logThreadPrefix = tmp.str(); \
}
#define LOG_FUNC_INIT(func) \
static thread_local std::string _logFuncPrefix; \
if (_logFuncPrefix.length() == 0) { \
std::stringstream tmp; \
tmp << g_EnumModules2String[to_underlying(LOG_PREFIX)] << (func != nullptr ? remove_emupatch_prefix(func) : ""); \
_logFuncPrefix = tmp.str(); \
}
#define LOG_INIT \
LOG_THREAD_INIT \
LOG_FUNC_INIT(__func__)
#define LOG_FINIT \
_logFuncPrefix.clear(); // Reset prefix, to show caller changes
#define LOG_FUNC_BEGIN_NO_INIT \
do { if(g_bPrintfOn) { \
bool _had_arg = false; \
std::stringstream msg; \
msg << _logThreadPrefix << _logFuncPrefix << "(";
#define LOG_FUNC_BEGIN \
LOG_INIT \
LOG_CHECK_ENABLED(LOG_LEVEL::DEBUG) { \
LOG_FUNC_BEGIN_NO_INIT
// LOG_FUNC_ARG writes output via all available ostream << operator overloads, sanitizing and adding detail where possible
#define LOG_FUNC_ARG(arg) \
_had_arg = true; \
msg << LOG_ARG_START << #arg << " : " << _log_sanitize(arg);
// LOG_FUNC_ARG_TYPE writes output using the overloaded << operator of the given type
#define LOG_FUNC_ARG_TYPE(type, arg) \
_had_arg = true; \
msg << LOG_ARG_START << #arg << " : " << (type)arg;
// LOG_FUNC_ARG_OUT prevents expansion of types, by only rendering as a pointer
#define LOG_FUNC_ARG_OUT(arg) \
_had_arg = true; \
msg << LOG_ARG_OUT_START << #arg << " : " << hex4((uint32_t)arg);
// LOG_FUNC_END closes off function and optional argument logging
#define LOG_FUNC_END \
if (_had_arg) msg << "\n"; \
msg << ");\n"; \
std::cout << msg.str(); \
} } while (0); \
}
#define LOG_FUNC_BEGIN_ARG_RESULT_NO_INIT \
do { if(g_bPrintfOn) { \
bool _had_arg = false; \
std::stringstream msg; \
msg << _logThreadPrefix << _logFuncPrefix << " returns OUT {";
#define LOG_FUNC_BEGIN_ARG_RESULT \
LOG_CHECK_ENABLED(LOG_LEVEL::DEBUG) { \
LOG_FUNC_BEGIN_ARG_RESULT_NO_INIT
// LOG_FUNC_ARG_RESULT writes output via all available ostream << operator overloads, sanitizing and adding detail where possible
#define LOG_FUNC_ARG_RESULT(arg) \
_had_arg = true; \
msg << LOG_ARG_START << "*"#arg << " : "; \
if (arg != nullptr) { \
msg << _log_sanitize(*arg); \
} else { \
msg << "NOT SET"; \
}
// LOG_FUNC_ARG_RESULT_TYPE writes result output using the overloaded << operator of the given type
#define LOG_FUNC_ARG_RESULT_TYPE(type, arg) \
_had_arg = true; \
msg << LOG_ARG_START << "*"#arg << " : "; \
if (arg != nullptr) { \
msg << (type)*arg; \
} else { \
msg << "NOT SET"; \
}
// LOG_FUNC_END_ARG_RESULT closes off function and optional argument result logging
#define LOG_FUNC_END_ARG_RESULT \
if (_had_arg) msg << "\n"; \
msg << "};\n"; \
std::cout << msg.str(); \
} } while (0); \
}
// LOG_FUNC_RESULT logs the function return result
#define LOG_FUNC_RESULT(r) \
std::cout << _logThreadPrefix << _logFuncPrefix << " returns " << _log_sanitize(r) << "\n";
// LOG_FUNC_RESULT_TYPE logs the function return result using the overloaded << operator of the given type
#define LOG_FUNC_RESULT_TYPE(type, r) \
std::cout << _logThreadPrefix << _logFuncPrefix << " returns " << (type)r << "\n";
// LOG_FORWARD indicates that an api is implemented by a forward to another API
#define LOG_FORWARD(api) \
LOG_INIT \
LOG_CHECK_ENABLED(LOG_LEVEL::DEBUG) { \
do { if(g_bPrintfOn) { \
std::cout << _logThreadPrefix << _logFuncPrefix << " forwarding to "#api"...\n"; \
} } while (0); \
}
// LOG_IGNORED indicates that Cxbx consiously ignores an api
#define LOG_IGNORED() \
do { \
static bool b_echoOnce = true; \
if(g_bPrintfOn && b_echoOnce) { \
LOG_CHECK_ENABLED(LOG_LEVEL::INFO) { \
LOG_THREAD_INIT \
LOG_FUNC_INIT(__func__) \
std::cout << _logThreadPrefix << "WARN: " << _logFuncPrefix << " ignored!\n"; \
b_echoOnce = false; \
} \
} \
} while(0)
// LOG_UNIMPLEMENTED indicates that Cxbx is missing an implementation of an api
#define LOG_UNIMPLEMENTED() \
do { \
static bool b_echoOnce = true; \
if(g_bPrintfOn && b_echoOnce) { \
LOG_CHECK_ENABLED(LOG_LEVEL::INFO) { \
LOG_THREAD_INIT \
LOG_FUNC_INIT(__func__) \
std::cout << _logThreadPrefix << "WARN: " << _logFuncPrefix << " unimplemented!\n"; \
b_echoOnce = false; \
} \
} \
} while(0)
// LOG_INCOMPLETE indicates that Cxbx is missing part of an implementation of an api
#define LOG_INCOMPLETE() \
do { \
static bool b_echoOnce = true; \
if(g_bPrintfOn && b_echoOnce) { \
LOG_CHECK_ENABLED(LOG_LEVEL::INFO) { \
LOG_THREAD_INIT \
LOG_FUNC_INIT(__func__) \
std::cout << _logThreadPrefix << "WARN: " << _logFuncPrefix << " incomplete!\n"; \
b_echoOnce = false; \
} \
} \
} while(0)
// LOG_NOT_SUPPORTED indicates that Cxbx cannot implement (part of) an api
#define LOG_NOT_SUPPORTED() \
do { \
static bool b_echoOnce = true; \
if(g_bPrintfOn && b_echoOnce) { \
LOG_CHECK_ENABLED(LOG_LEVEL::INFO) { \
LOG_THREAD_INIT \
LOG_FUNC_INIT(__func__) \
std::cout << _logThreadPrefix << "WARN: " << _logFuncPrefix << " not supported!\n"; \
b_echoOnce = false; \
} \
} \
} while(0)
//
// Short hand function logging defines :
//
// Log function without arguments
#define LOG_FUNC() LOG_FUNC_BEGIN LOG_FUNC_END
// Log function with one argument
#define LOG_FUNC_ONE_ARG(arg) LOG_FUNC_BEGIN LOG_FUNC_ARG(arg) LOG_FUNC_END
// Log function with one typed argument
#define LOG_FUNC_ONE_ARG_TYPE(type, arg) LOG_FUNC_BEGIN LOG_FUNC_ARG_TYPE(type, arg) LOG_FUNC_END
// Log function with one out argument
#define LOG_FUNC_ONE_ARG_OUT(arg) LOG_FUNC_BEGIN LOG_FUNC_ARG_OUT(arg) LOG_FUNC_END
// RETURN logs the given result and then returns it (so this should appear last in functions)
#define RETURN(r) do { if (g_bPrintfOn) LOG_CHECK_ENABLED(LOG_LEVEL::DEBUG) { LOG_FUNC_RESULT(r) } return r; } while (0)
// RETURN_TYPE logs the given typed result and then returns it (so this should appear last in functions)
#define RETURN_TYPE(type, r) do { if (g_bPrintfOn) LOG_CHECK_ENABLED(LOG_LEVEL::DEBUG) { LOG_FUNC_RESULT_TYPE(type, r) } return r; } while (0)
//
// Headers for rendering enums, flags and (pointer-to-)types :
//
// Macro to ease declaration of a render function per Type:
#define LOGRENDER_HEADER_BY_REF(Type) std::ostream& operator<<(std::ostream& os, const Type& value)
#define LOGRENDER_HEADER_BY_PTR(Type) std::ostream& operator<<(std::ostream& os, const Type *value)
#define TYPE2PCHAR(Type) Type##ToPCHAR
#define TYPE2PCHAR_HEADER(Type) const char * TYPE2PCHAR(Type)(const Type &value)
// Macro for implementation of rendering any Type-ToString :
#define LOGRENDER_TYPE(Type) LOGRENDER_HEADER_BY_REF(Type) \
{ return os << "("#Type")" << hex4((int)value) << " = " << TYPE2PCHAR(Type)(value); }
// Macro's for Enum-ToString conversions :
#define ENUM2STR_HEADER(EnumType) extern TYPE2PCHAR_HEADER(EnumType); LOGRENDER_HEADER_BY_REF(EnumType);
#define ENUM2STR_START(EnumType) TYPE2PCHAR_HEADER(EnumType) { switch (value) {
#define ENUM2STR_CASE(a) case a: return #a;
#define ENUM2STR_END(EnumType) default: return "Unknown_"#EnumType; } }
#define ENUM2STR_END_and_LOGRENDER(EnumType) ENUM2STR_END(EnumType) LOGRENDER_TYPE(EnumType)
// Macro's for Flags-ToString conversions :
#define FLAGS2STR_HEADER(FlagType) LOGRENDER_HEADER_BY_REF(FlagType);
#define FLAGS2STR_START(FlagType) std::string TYPE2PCHAR(FlagType)(const FlagType &value) { std::stringstream ss;
#define FLAG2STR(f) if (((uint32_t)value & f) == f) ss << #f << "|";
#define FLAG2STR_MASK(f) ss << #f"=" << (value & f) << "|";
#define FLAGS2STR_END std::string res = ss.str(); if (!res.empty()) res.pop_back(); return res; }
#define FLAGS2STR_END_and_LOGRENDER(FlagType) FLAGS2STR_END LOGRENDER_TYPE(FlagType)
// Macro's for Struct-member rendering :
#define LOGRENDER_MEMBER_ARRAY_OFFSET(Name, Offset) << LOG_ARG_START << (Name) << "[" << (Offset) << "]: {"
#define LOGRENDER_MEMBER_NAME_VALUE(Name, Value) << LOG_ARG_START << (Name) << " : " << (Value)
#define LOGRENDER_MEMBER_NAME_TYPE_PTR(Name, Type, Value) << LOG_ARG_START << (Name) << " : " << hex4((uint32_t)Value) << " -> " << (Type) << "* {"
#define LOGRENDER_MEMBER_NAME_TYPE_VALUE(Name, Type, Value) << LOG_ARG_START << (Name) << " : " << (Type)(Value)
#define LOGRENDER_MEMBER_NAME(Member) << LOG_ARG_START << "."#Member << " : "
#define LOGRENDER_MEMBER(Member) LOGRENDER_MEMBER_NAME_VALUE("."#Member, value.Member)
#define LOGRENDER_MEMBER_TYPE(Type, Member) LOGRENDER_MEMBER_NAME(Member) << (Type)value.Member
#define LOGRENDER_MEMBER_SANITIZED(Member, MemberType, Length) LOGRENDER_MEMBER_NAME(Member) << _log_sanitize((MemberType)value.Member, Length)
// Macro to ease declaration of two render functions, for type and pointer-to-type :
#define LOGRENDER_HEADER(Type) LOGRENDER_HEADER_BY_PTR(Type); LOGRENDER_HEADER_BY_REF(Type);
// Macro's for dynamic array structure rendering :
template<class A, class B>
static std::string
LogRenderGenerate_Array(std::string MemberStr, A MemberValue, B Count) {
B counter = 0;
std::ostringstream strGenerator;
while (counter < Count) {
strGenerator LOGRENDER_MEMBER_ARRAY_OFFSET(MemberStr, counter)
<< *MemberValue << "}";
MemberValue++;
counter++;
}
strGenerator << "}";
return strGenerator.str();
}
#define LOGRENDER_MEMBER_ARRAY_TYPE(Type, Member, Count) LOGRENDER_MEMBER_NAME_TYPE_PTR("."#Member, #Type, value.Member) << LogRenderGenerate_Array("."#Member, (Type*)value.Member, value.Count)
// Traits to switch ostreams to our preferred rendering of hexadecimal values
template <class _CharT, class _Traits>
std::basic_ostream<_CharT, _Traits>&
hexstring8(std::basic_ostream<_CharT, _Traits>&os)
{
// std::noshowbase is not neccessary to set (it's the default, and we never use std::showbase)
return os << "0x" << std::setfill('0') << std::setw(2) << std::right << std::hex << std::uppercase;
}
template <class _CharT, class _Traits>
std::basic_ostream<_CharT, _Traits>&
hexstring16(std::basic_ostream<_CharT, _Traits>&os)
{
return os << "0x" << std::setfill('0') << std::setw(4) << std::right << std::hex << std::uppercase;
}
template <class _CharT, class _Traits>
std::basic_ostream<_CharT, _Traits>&
hexstring32(std::basic_ostream<_CharT, _Traits>&os)
{
return os << "0x" << std::setfill('0') << std::setw(8) << std::right << std::hex << std::uppercase;
}
// Macro combining pointer-to-type implementation and type rendering header :
#define LOGRENDER(Type) \
LOGRENDER_HEADER_BY_PTR(Type) \
{ \
os << hexstring32 << reinterpret_cast<uint32_t>(value); \
if (value) \
os << " -> "#Type"* {" << *value << "}"; \
\
return os; \
} \
\
LOGRENDER_HEADER_BY_REF(Type) \
//
// An example type rendering, for PVOID
//
LOGRENDER_HEADER_BY_REF(PVOID);
#endif _LOGGING_H