/** * @brief Writes messages to `debug.log` if `NDEBUG` is not defined. * @author Christian Burger (christian@krikkel.de) * @todo refactor macros */ #ifndef __DEBUG_H__ #define __DEBUG_H__ #ifndef NDEBUG #include #include #include #include namespace krikkel { class Debug { private: std::fstream logFile; static const int MAX_NUMBER_OF_SYSCALLS = 1024; static const char * syscalls[MAX_NUMBER_OF_SYSCALLS]; Debug(); std::string getTimestamp(); std::string getUname(); public: void log(std::string message, std::string fileName, int lineNo , std::string functionName); static Debug *getInstance(); }; } #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 */ #endif // __DEBUG_H__