From fd800dbca804f4a6f81bf83e1049476df2f1cde9 Mon Sep 17 00:00:00 2001 From: Christian Burger Date: Fri, 8 Apr 2022 21:00:10 +0200 Subject: [PATCH] extended debug facilities for output * Output strings with unprintable characters either as a table or as continuing string of characters; the byte value of unprintable characters (`isprint(, ) == false`) is shown. * Output readable string of `key` -> `value` pairs with an enumerated type for the key and union type for the value. --- Debug.cpp | 10 ++++-- Debug.hpp | 93 ++++++++++++++++++++++++++++++++++++++++++++++++------- 2 files changed, 89 insertions(+), 14 deletions(-) diff --git a/Debug.cpp b/Debug.cpp index 0656570..f05fee3 100644 --- a/Debug.cpp +++ b/Debug.cpp @@ -22,7 +22,13 @@ namespace krikkel { + using std::string; + using std::fstream; using std::filesystem::is_directory; + using std::endl; + using std::time; + using std::localtime; + using std::strftime; Debug::Debug() { @@ -30,7 +36,7 @@ namespace krikkel if(is_directory("sandbox")) directory = "./sandbox"; logFile = fstream(directory + "/debug.log", fstream::out | fstream::app);//| fstream::trunc); - logFile << endl << endl << "New instance of class Debug." << endl; + logFile << endl << endl << endl << endl << "New instance of class Debug." << endl; logFile << getUname(); logFile.flush(); } @@ -50,7 +56,7 @@ namespace krikkel string Debug::getTimestamp() { - time_t now = std::time(NULL); + time_t now = time(NULL); tm *localNow = localtime(&now); static char formattedLocalNow[32]; strftime(formattedLocalNow, 32, "%c", localNow); diff --git a/Debug.hpp b/Debug.hpp index 4687622..8c965de 100644 --- a/Debug.hpp +++ b/Debug.hpp @@ -1,6 +1,7 @@ /** * @brief Writes messages to `debug.log` if `NDEBUG` is not defined. * @author Christian Burger (christian@krikkel.de) + * @todo refactor macros */ #ifndef __DEBUG_H__ @@ -11,30 +12,24 @@ #include #include #include +#include namespace krikkel { - using std::fstream; - using std::string; - using std::endl; - using std::strftime; - using std::localtime; - using std::time; - class Debug { private: - fstream logFile; + std::fstream logFile; static const int MAX_NUMBER_OF_SYSCALLS = 1024; static const char * syscalls[MAX_NUMBER_OF_SYSCALLS]; Debug(); - string getTimestamp(); - string getUname(); + std::string getTimestamp(); + std::string getUname(); public: - void log(string message, string fileName, int lineNo - , string functionName); + void log(std::string message, std::string fileName, int lineNo + , std::string functionName); static Debug *getInstance(); }; } @@ -42,10 +37,84 @@ namespace krikkel #define __debug_log(message) krikkel::Debug::getInstance()\ ->log(message, __FILE__, __LINE__, __func__) + +/// @todo need an explanation on why and how to use this +/// this is the complete opposite of self-explanatory +#define __debug_stringify_switch_begin(closureName, __prop, __value) \ + auto __debug_stringify__##closureName = [__prop, __value]() -> std::string \ + { \ + std::string propertyName, valueString; \ + switch(__prop) \ + { +#define __debug_stringify_switch_case(caseTestValue) \ + case caseTestValue: \ + propertyName = (propertyName.empty() ? #caseTestValue : propertyName) +#define __debug_stringify_switch_case_end_bool(value) \ + valueString = (value ? "true" : "false"); \ + break +#define __debug_stringify_switch_case_end_string(value) \ + valueString = "\"" + std::string(value) + "\""; \ + break +#define __debug_stringify_switch_case_end_number(value) \ + valueString = std::to_string(value); \ + break; +#define __debug_stringify_switch_end(__prop) \ + default: \ + propertyName = "default case triggerd for " + std::to_string(__prop); \ + break; \ + } \ + return propertyName + " = " + valueString; \ + } +#define __debug_stringify_get_string(closureName) __debug_stringify__##closureName() + +#define __debug_bytes_printable_table(unprintable, numberOfBytes) ([](const char *bytes, size_t length) \ + { \ + std::locale loc; \ + size_t index, targetIndex = 0; \ + size_t targetLength = (length / 16 + 1) * (3 + 3 * 16 + 1) + 1; \ + char targetBytes[targetLength]; \ + for(index = 0; index < length; index++) \ + { \ + if(std::isprint(bytes[index], loc)) \ + targetIndex += snprintf(targetBytes + targetIndex, targetLength - targetIndex, index % 16 == 0 ? " | %c" : " %c", bytes[index]); \ + else \ + targetIndex += snprintf(targetBytes + targetIndex, targetLength - targetIndex, index % 16 == 0 ? " | %02x" : " %02x", bytes[index]); \ + if(index % 16 == 15) \ + targetIndex += snprintf(targetBytes + targetIndex, targetLength - targetIndex, "\n"); \ + } \ + if(index % 16) \ + snprintf(targetBytes + targetIndex, targetLength - targetIndex, "\n"); \ + return std::string(targetBytes); \ + } \ + )(unprintable, numberOfBytes) + +#define __debug_bytes_printable(bytes, length) ([](std::string unprintable, size_t numberOfBytes) -> std::string \ + { \ + std::locale loc; \ + std::string result; \ + for(char character : unprintable) \ + { \ + if(!numberOfBytes--) \ + break; \ + if(std::isprint(character, loc) && character != '<') \ + result += character; \ + else \ + result += "<" + std::to_string(character) + ">"; \ + } \ + return result; \ + })(bytes, length) + #else /// @todo check what's the business with this "((void)0)" instead of empty macro #define __debug_log(message) +#define __debug_stringify_switch_begin(closureName, __prop, __value) +#define __debug_stringify_switch_case(caseTestValue) +#define __debug_stringify_switch_case_end_bool(value) +#define __debug_stringify_switch_case_end_string(value) +#define __debug_stringify_switch_case_end_number(value) +#define __debug_stringify_switch_end() +#define __debug_bytes_printable(bytes) #endif /* NDEBUG */