From 5cbeb1002ea2d3ed187e5108ca3858e496606e3a Mon Sep 17 00:00:00 2001 From: Abdullah Tahiri Date: Fri, 28 Apr 2017 22:39:56 +0200 Subject: [PATCH] Base::Exception extension ========================= 1. Enable automatic storing of information (function, file, line) when throwing the exception via macro: Examples: THROWM(Exception, "BSpline GeoId is out of bounds.") THROWM(ValueError, "BSpline GeoId is out of bounds.") THROW(AbortException) Output: a) Python Console (what()): App.ActiveDocument.Sketch004.modifyBSplineKnotMultiplicity(16,3,0) Traceback (most recent call last): File "", line 1, in Base.FreeCADError: FreeCAD exception thrown (BSpline GeoId is out of bounds.) b) ReportException (report()): Exception (Thu Apr 27 19:15:24 2017): BSpline GeoId is out of bounds. in bool Sketcher::SketchObject::modifyBSplineKnotMultiplicity(int, int, int) in src/Mod/Sketcher/App/SketchObject.cpp:4102 2. Extend the basic framework so as to allow more control over the mangling of the message introduced by the user, setting the basis to allow, where needed, to preserve the original message while allowing full legacy behaviour. 3. Supporting FileDialog reporting as legacy --- src/Base/Exception.cpp | 77 +++++++++++++++++++++++++++++++++++++----- src/Base/Exception.h | 48 ++++++++++++++++++++++++++ 2 files changed, 116 insertions(+), 9 deletions(-) diff --git a/src/Base/Exception.cpp b/src/Base/Exception.cpp index 3bd34efab9..362a7a22d4 100644 --- a/src/Base/Exception.cpp +++ b/src/Base/Exception.cpp @@ -41,7 +41,7 @@ Exception::Exception(void) } Exception::Exception(const Exception &inst) - : BaseClass(),_sErrMsg(inst._sErrMsg) +: BaseClass(),_sErrMsg(inst._sErrMsg), _file(inst._file), _line(inst._line), _function(inst._function) { } @@ -69,7 +69,30 @@ const char* Exception::what(void) const throw() void Exception::ReportException (void) const { - Console().Error("Exception (%s): %s \n",Console().Time(),what()); + std::string str = ""; + + if(!_sErrMsg.empty()) + str+= (_sErrMsg + " "); + + if(!_function.empty()) { + str+="In "; + str+=_function; + str+= " "; + } + + if(!_file.empty() && !_line.empty()) { + // strip absolute path + std::size_t pos = _file.find("src"); + + if (pos!=std::string::npos) { + str+="in "; + str+= _file.substr(pos); + str+= ":"; + str+=_line; + } + } + + Console().Error("Exception (%s): %s \n",Console().Time(),str.c_str()); } // --------------------------------------------------------- @@ -144,33 +167,68 @@ FileException::FileException(const char * sMessage, const char * sFileName) : Exception( sMessage ),file(sFileName) { if (sFileName) { - _sErrMsg += ": "; - _sErrMsg += sFileName; + _sErrMsgAndFileName = _sErrMsg + ": "; + _sErrMsgAndFileName += sFileName; } } FileException::FileException(const char * sMessage, const FileInfo& File) : Exception( sMessage ),file(File) { - _sErrMsg += ": "; - _sErrMsg += File.fileName(); + _sErrMsgAndFileName = _sErrMsg + ": "; + _sErrMsgAndFileName += File.fileName(); } FileException::FileException() - : Exception( "Unknown file exeption happened" ) + : Exception( "Unknown file exception happened" ) { + _sErrMsgAndFileName = _sErrMsg; } FileException::FileException(const FileException &inst) - : Exception( inst._sErrMsg.c_str() ),file(inst.file) +: Exception( inst._sErrMsg.c_str() ), file(inst.file), _sErrMsgAndFileName(inst._sErrMsgAndFileName.c_str()) { } +std::string FileException::getFileName() const +{ + return file.fileName(); +} + const char* FileException::what() const throw() { - return Exception::what(); + return _sErrMsgAndFileName.c_str(); } +void FileException::ReportException (void) const +{ + std::string str = ""; + + if(!_sErrMsgAndFileName.empty()) + str+= (_sErrMsgAndFileName + " "); + + if(!_function.empty()) { + str+="In "; + str+=_function; + str+= " "; + } + + if(!_file.empty() && !_line.empty()) { + // strip absolute path + std::size_t pos = _file.find("src"); + + if (pos!=std::string::npos) { + str+="in "; + str+= _file.substr(pos); + str+= ":"; + str+=_line; + } + } + + Console().Error("Exception (%s): %s \n",Console().Time(),str.c_str()); +} + + // --------------------------------------------------------- FileSystemError::FileSystemError(const char * sMessage) @@ -562,6 +620,7 @@ CADKernelError::CADKernelError(const CADKernelError &inst) { } + // --------------------------------------------------------- #if defined(__GNUC__) && defined (FC_OS_LINUX) diff --git a/src/Base/Exception.h b/src/Base/Exception.h index 4deccee075..f0f6d5ad91 100644 --- a/src/Base/Exception.h +++ b/src/Base/Exception.h @@ -33,6 +33,13 @@ #include "FileInfo.h" #include "BaseClass.h" +/* MACROS FOR THROWING EXCEPTIONS */ + +#define THROW(exception) {exception myexcp; myexcp.setDebugInformation(__FILE__,__LINE__,__PRETTY_FUNCTION__); throw myexcp;} +#define THROWM(exception, message) {exception myexcp(message); myexcp.setDebugInformation(__FILE__,__LINE__,__PRETTY_FUNCTION__); throw myexcp;} + +#define THROWMF_FILEEXCEPTION(message,filenameorfileinfo) {FileException myexcp(message, filenameorfileinfo); myexcp.setDebugInformation(__FILE__,__LINE__,__PRETTY_FUNCTION__); throw myexcp;} + namespace Base { @@ -41,16 +48,32 @@ class BaseExport Exception : public BaseClass TYPESYSTEM_HEADER(); public: + virtual ~Exception() throw() {} Exception &operator=(const Exception &inst); + virtual const char* what(void) const throw(); + virtual void ReportException (void) const; + inline void setMessage(const char * sMessage); inline void setMessage(const std::string& sMessage); + // what may differ from the message given by the user in + // derived classes + inline std::string getMessage() const; + + /// setter methods for including debug information + /// intended to use via macro for autofilling of debugging information + inline void setDebugInformation(const std::string & file, const int line, const std::string & function); protected: public: // FIXME: Remove the public keyword + /* sMessage may be: + * - an UI compliant string subsceptible of being translated and shown to the user in the UI + * - a very technical message not intended to be traslated or shown to the user in the UI + * The preferred way of throwing an exception is using the macros above. This way, the file, + * line and function are automatically inserted. */ Exception(const char * sMessage); Exception(const std::string& sMessage); Exception(void); @@ -58,6 +81,9 @@ public: // FIXME: Remove the public keyword protected: std::string _sErrMsg; + std::string _file; + std::string _line; + std::string _function; }; @@ -74,6 +100,7 @@ public: AbortException(); /// Construction AbortException(const AbortException &inst); + /// Destruction virtual ~AbortException() throw() {} /// Description of the exception @@ -92,6 +119,7 @@ public: XMLBaseException(const std::string& sMessage); /// Construction XMLBaseException(const XMLBaseException &inst); + /// Destruction virtual ~XMLBaseException() throw() {} }; @@ -111,6 +139,7 @@ public: XMLParseException(); /// Construction XMLParseException(const XMLParseException &inst); + /// Destruction virtual ~XMLParseException() throw() {} /// Description of the exception @@ -136,8 +165,14 @@ public: virtual ~FileException() throw() {} /// Description of the exception virtual const char* what() const throw(); + /// Report generation + virtual void ReportException (void) const; + /// Get file name for use with tranlatable message + std::string getFileName() const; protected: FileInfo file; + // necesary for what() legacy behaviour as it returns a buffer that can not be of a temporary object to be destroyed at end of what() + std::string _sErrMsgAndFileName; }; /** @@ -259,6 +294,7 @@ public: ProgramInformation(const std::string& sMessage); /// Construction ProgramInformation(const ProgramInformation &inst); + /// Destruction virtual ~ProgramInformation() throw() {} }; @@ -516,6 +552,18 @@ inline void Exception::setMessage(const std::string& sMessage) _sErrMsg = sMessage; } +inline std::string Exception::getMessage() const +{ + return _sErrMsg; +} + +inline void Exception::setDebugInformation(const std::string & file, const int line, const std::string & function) +{ + _file = file; + _line = std::to_string(line); + _function = function; +} + #if defined(__GNUC__) && defined (FC_OS_LINUX) class SignalException {