I will be adding functionality and most likely additional refactoring, however a stringent review would be welcome before I build it further. I know documentation is probably a bit sparse but, you know how it is. If you feel anything significantly warrants documentation, let me know. I know I understand the code; I don't know about others.
Please review code correctness, best practices, design and code formatting.
//File debug.h
//Release 1.2.0.0
//Copyright Michael Mercer
/* Include code
#ifdef NDEBUG
#include <debug.h>
#else
#undef OBJECT_NAME
#define OBJECT_NAME debugDefault //Name object by file or project
#include <debug.h>
using namespace debug_1_2_0; //Required to debug main() and NDEBUG ForwardCall
debug OBJECT_NAME; //debug OBJECT_NAME(std::initializer_list<std::ostream*>); to set ostreams
#endif
DEBUG_MAIN //Wrap main with debug diagnostics
{
//DEBUG(Args...);
//DEBUG_FUNC("Function name", function, function args...);
//DEBUG_FUNC(function, function args...);
return 0;
}
*/
#ifdef NDEBUG
#undef DEBUG
#undef DEBUG_FUNC
#undef DEBUG_MAIN
#define DEBUG(...)
#define DEBUG_FUNC ForwardCall
#define DEBUG_MAIN int main(int argc, char** argv, char** envp)
#else
#undef DEBUG
#undef DEBUG_FUNC
#undef DEBUG_MAIN
#define DEBUG OBJECT_NAME.debugOutput
#define DEBUG_FUNC DEBUG
#define DEBUG_MAIN \
int main(int argc, char** argv, char** envp) \
{ \
OBJECT_NAME.debugDiagnostics(argc, argv, envp); \
} \
int debug::main(int argc, char** argv, char** envp)
#endif
#ifndef DEBUG_H //Include guard
#define DEBUG_H
#include <exception> //for std::exception
#include <iostream> //for std::ostream
#include <stdexcept> //for std::logic_error
#include <windows.h> //for GetStdHandle etc
#include <forward_list> //for std::forward_list
namespace debug_1_2_0
{
//Forward calls when NDEBUG
template<typename... T, typename... Args>
void ForwardCall(void funcToTry(T... Tp), Args&&... parameters)
{
(funcToTry)(static_cast<Args&&>(parameters)...);
}
//Forward calls when NDEBUG
template<typename F, typename... T, typename... Args>
auto ForwardCall(F funcToTry(T... Tp), Args&&... parameters) -> F
{
return (funcToTry)(static_cast<Args&&>(parameters)...);
}
//Forward calls when NDEBUG
template<typename... T, typename... Args>
void ForwardCall(const char* const functionName,void funcToTry(T... Tp), Args&&... parameters)
{
(funcToTry)(static_cast<Args&&>(parameters)...);
}
//Forward calls when NDEBUG
template<typename F, typename... T, typename... Args>
auto ForwardCall(const char* const functionName,F funcToTry(T... Tp), Args&&... parameters) -> F
{
return (funcToTry)(static_cast<Args&&>(parameters)...);
}
#ifndef NDEBUG
enum struct debugState {ENTER, EXIT, PROCESS};
//Retreive file size
__int64 FileSize(std::string name);
class oStreams
{
private:
std::forward_list<std::ostream*> listStreams;
public:
oStreams(std::initializer_list<std::ostream*> initOutStreams);
typedef std::ostream& (manip)(std::ostream&);
void operator<<(manip& m);
template <typename T>
void operator<<(T&& obj)
{
for (auto& x: listStreams)
(*x) << static_cast<T&&>(obj);
}
};
class debug
{
private:
int debugIndentCount=0;
const char* const enterRoutine = "Enter ";
const char* const exitRoutine = "Exit ";
oStreams debugStreams;
//Output last remaining element of args...
template <typename T>
void logText(T&& firstOut)
{
debugStreams << static_cast<T&&>(firstOut);
}
//Output first element of args..., call recursively
template <typename T, typename... Args>
void logText(T&& firstOut, Args&&... remainder)
{
debugStreams << static_cast<T&&>(firstOut);
logText(static_cast<Args&&>(remainder)...);
}
//Format and output args... to ostream
template <typename... Args>
void indentX(Args&&... debugText)
{
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), FOREGROUND_RED);
debugStreams << "# ";
for(int x = 0; x < debugIndentCount; ++x)
debugStreams << " ";
logText(static_cast<Args&&>(debugText)...);
debugStreams << std::endl;
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), FOREGROUND_RED|FOREGROUND_GREEN|FOREGROUND_BLUE);
}
//Output custom state and args...
template <typename... Args>
void debugOutput(debugState state, Args&&... debugText)
{
switch(state)
{
case debugState::ENTER:
indentX(enterRoutine, static_cast<Args&&>(debugText)...);
debugIndentCount++;
break;
case debugState::EXIT:
if (debugIndentCount>0)
debugIndentCount--;
indentX(exitRoutine, static_cast<Args&&>(debugText)...);
break;
case debugState::PROCESS:
indentX(static_cast<Args&&>(debugText)...);
break;
default:
throw std::logic_error("Debugging error: Incorrect enum debugState");
}
}
public:
debug(std::initializer_list<std::ostream*> oStreams = {&std::clog})
: debugStreams(oStreams)
{};
~debug(){};
//Output args...
template <typename... Args>
void debugOutput(Args&&... debugText)
{
debugOutput(debugState::PROCESS,static_cast<Args&&>(debugText)...);
}
//Call funcToTry with args..., catch exception
template<typename... T, typename... Args>
void debugOutput(void funcToTry(T... Tp), Args&&... parameters)
{
try
{
(funcToTry)(static_cast<Args&&>(parameters)...);
}
catch(std::exception& e)
{
debugOutput("EXCEPTION THROWN! ", e.what());
throw;
}
}
//Call funcToTry with args... and return F, catch exception
template<typename F, typename... T, typename... Args>
auto debugOutput(F funcToTry(T... Tp), Args&&... parameters) -> F
{
try
{
return (funcToTry)(static_cast<Args&&>(parameters)...);
}
catch(std::exception& e)
{
debugOutput("EXCEPTION THROWN! ", e.what());
throw;
}
}
//Output function name
//Call void funcToTry with args..., catch exception
template<typename... T, typename... Args>
void debugOutput(const char* const functionName,void funcToTry(T... Tp), Args&&... parameters)
{
try
{
debugOutput(debugState::ENTER,functionName);
(funcToTry)(static_cast<Args&&>(parameters)...);
debugOutput(debugState::EXIT,functionName);
}
catch(std::exception& e)
{
debugOutput("EXCEPTION THROWN! ", e.what());
throw;
}
}
//Output function name
//Call funcToTry with args... and return F, catch exception
template<typename F, typename... T, typename... Args>
auto debugOutput(const char* const functionName,F funcToTry(T... Tp), Args&&... parameters) -> F
{
try
{
debugOutput(debugState::ENTER,functionName);
F returnValue = (funcToTry)(static_cast<Args&&>(parameters)...);
debugOutput(debugState::EXIT,functionName," with ", returnValue);
return returnValue;
}
catch(std::exception& e)
{
debugOutput("EXCEPTION THROWN! ", e.what());
throw;
}
}
int main(int argc, char** argv, char** envp);
void debugDiagnostics(int argc, char** argv, char** envp)
{
SYSTEMTIME stStart, stEnd;
FILETIME ftStart,ftEnd;
debugOutput(debugState::ENTER,"Debug Diagnostics");
double fileSize = FileSize(argv[0]);
debugOutput("file size in bytes = ",fileSize);
debugOutput("file size in kilobytes = ",fileSize/1024);
debugOutput(debugState::EXIT,"Debug Diagnostics");
GetSystemTime(&stStart);
debugOutput(debugState::ENTER,"Main");
this->main(argc, argv, envp);
debugOutput(debugState::EXIT,"Main");
GetSystemTime(&stEnd);
SystemTimeToFileTime (&stStart,&ftStart);
SystemTimeToFileTime (&stEnd,&ftEnd);
unsigned __int64 timeStart=ftStart.dwLowDateTime;
unsigned __int64 timeEnd=ftEnd.dwLowDateTime;
debugOutput(debugState::ENTER,"Debug Diagnostics");
debugOutput("execution time is ", (timeEnd-timeStart)/10000000, " seconds");
debugOutput("execution time is ", (timeEnd-timeStart)/10000, " milliseconds");
debugOutput(debugState::EXIT,"Debug Diagnostics");
}
}; //class
#endif //#ifndef NDEBUG
} //namespace
#endif //DEBUG_H