Ist das ein guter Ansatz um flöten gegangene Funktionen zu finden?
-
Hi,
ich habe in meinen Projekten oft Probleme, das mir eine Funktion flöten geht, wegen eines undefinierten verhaltens und co.
Jetzt hab ich mir den Kopf zerbrochen und neulich auch was interessantes im Netz gefunden es sind 2 Klassen:
#ifndef _CDEBUG_H_ #define _CDEBUG_H_ #ifdef _DEBUG # ifndef _WIN32 # define __FUNCTION__ __func__ # endif // __FUNCTION__ # define PUSH_FUNCTION CDebug::getInstance().pushFunctionOnStack(__FUNCTION__); # define POP_FUNCTION CDebug::getInstance().popFunctionFromStack(__FUNCTION__); # ifdef assert # undef assert # endif // assert # define assert(exp) CDebug::getInstance().assertx(exp, __FILE__, __LINE__); #else # define PUSH_FUNCTION # define POP_FUNCTION # define assert(x); #endif // debug class CDebug { public: static CDebug& getInstance (void); void pushFunctionOnStack (const std::string &Function); void popFunctionFromStack (const std::string &Function); std::string getCrashedFunctionPath (void); std::string getCrashedFunction (void); void assertx (bool exp, const std::string &FileName, unsigned int line); private: // Constructor CDebug (void); // Destructor ~CDebug (void); std::stack<std::string> m_FunctionStack; }; #endif
#include "CDebug.h" CDebug::CDebug (void) { } CDebug::~CDebug(void) { } CDebug& CDebug::getInstance (void) { static CDebug Instanz; return Instanz; } void CDebug::pushFunctionOnStack (const std::string &Function) { this->m_FunctionStack.push(Function); } void CDebug::popFunctionFromStack (const std::string &Function) { if (this->m_FunctionStack.top () == Function) this->m_FunctionStack.pop (); // else // Print("ERROR: Error in StackTrace"); } std::string CDebug::getCrashedFunctionPath (void) { int size = static_cast<int>(this->m_FunctionStack.size()); std::stringstream result (""); if (size > 0) { std::stack<std::string> tempStack; std::stack<std::string> tempStack2 = this->m_FunctionStack; for (int i=0; i < size; ++i) { tempStack.push (tempStack2.top()); tempStack2.pop (); } for (int i=0; i < size; ++i) { std::string output = tempStack.top(); tempStack.pop(); result << "-> " << output << "();" << std::endl; } } return result.str(); } std::string CDebug::getCrashedFunction (void) { std::stringstream Buffer (""); Buffer << this->m_FunctionStack.top(); Buffer << "();"; return (Buffer.str()); } void CDebug::assertx (bool exp, const std::string &FileName, unsigned int line) { if (exp == false) { std::stringstream Buffer; Buffer.str ("Engine Assertion failed!\n\n"); Buffer << "File : " << FileName << std::endl; Buffer << "Line :" << line << std::endl << std::endl; Buffer << "Crashed Functionpath .... " << std::endl; Buffer << getCrashedFunctionPath (); Buffer << "Crashed Function: " << this->m_FunctionStack.top(); Buffer << "();" << std::endl; #ifdef _WIN32 ::MessageBox(NULL, Buffer.str().c_str(), "Engine Assertion Error!", MB_ICONEXCLAMATION | MB_OK); #else std::cout << Buffer.str().c_str() << std::endl; #endif // Werfe eine "Zero-Exception" throw ""; } }
und ne Exceptionklasse dafür:
#ifndef _CEXCEPTION_H_ #define _CEXCEPTION_H_ #include "CDebug.h" #define EXCEPTION(x) throw CException (__FILE__, __LINE__, (x)); class CException { public: // Constructor CException (const std::string &FileName, unsigned int Line, const std::string &ErrorText); // Destructor ~CException (void); // Gibt uns einen schönen Exception Text wieder std::string getText (void); protected: std::string m_ErrorText; // Meldung std::string m_FileName; // Dateiname unsigned int m_Line; // Zeile in der Datei }; #endif
#include "CException.h" CException::CException (const std::string &FileName, unsigned int Line, const std::string &ErrorText): m_FileName (FileName), m_Line (Line), m_ErrorText (ErrorText) { PUSH_FUNCTION POP_FUNCTION } CException::~CException (void) { PUSH_FUNCTION POP_FUNCTION } std::string CException::getText (void) { std::stringstream Buffer (""); Buffer << "Engine Error!" << std::endl << std::endl; Buffer << this->m_ErrorText << std::endl << std::endl; Buffer << "File : " << this->m_FileName << std::endl; Buffer << "Line :" << this->m_Line << std::endl << std::endl; Buffer << "Crashed Functionpath .... " << std::endl; Buffer << CDebug::getInstance().getCrashedFunctionPath(); Buffer << "Crashed Function: " << CDebug::getInstance().getCrashedFunction() << std::endl; return (Buffer.str()); };
und benutzt wird das dingen so:
#include "CException.h" void func1 (void) { PUSH_FUNCTION EXCEPTION ("TEST!!!!"); POP_FUNCTION } void func2 (void) { PUSH_FUNCTION func1 (); POP_FUNCTION } int main (int argc, char *argv[]) { PUSH_FUNCTION try { } catch (CException &ex) { MessageBoxA (NULL, ex.getText().c_str(), "Engine Error!", MB_ICONEXCLAMATION | MB_OK); } catch (...) { } POP_FUNCTION return 0; }
funzt auch sehr gut, doch nun meine Frage: ist dieser Ansatz sehr gut oder eher nur so 5 von 10 punkten?
-
achja vergessen, im try-block von main wird func2 (); aufgerufen
-
ich würde was simpleres vorschlagen.
ich hab den code nicht bei der hand, aber ich habe eine klasse die etwa so arbeitet:class Guard { private: string name; public: Guard(string const& name) : name(name) { Singleton::EnterFunction(name); } ~Guard() { Singletion::LeaveFunction(name, uncaught_exception()); } }; #define GUARD Guard functionGuard(__FUNCTION__)
Wobei __FUNCTION__ nicht standardisiert ist, aber jeder compiler bietet ein ähnliches makro an, dass den funktionsnamen beinhaltet.
EnterFunction und LeaveFunction speichern die infos dann zB in einem Logfile. Mittels uncaught_exception stelle ich fest, ob die Funktion normal beendet wurde, oder per exception.
Der große Vorteil hierbei ist, dass man nur einmal
GUARD
am anfang der Funktion schreiben mussund in der release version kann man es ohne probleme ausblenden.