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 muss 🙂 und in der release version kann man es ohne probleme ausblenden.


Anmelden zum Antworten