From 838178bb1c702f0ad7e3dfbfc6acd83daef4a61e Mon Sep 17 00:00:00 2001 From: bofdahof <172177156+bofdahof@users.noreply.github.com> Date: Tue, 18 Mar 2025 17:47:10 +1000 Subject: [PATCH] Base: simplify Exception Remove repetition. Add functions to replace macros, but keep macros in use for now as some compilers have broken std::source_location support. --- src/Base/Exception.cpp | 537 +++--------- src/Base/Exception.h | 869 +++++--------------- src/Base/Interpreter.cpp | 32 +- src/Mod/Part/Gui/DlgProjectionOnSurface.cpp | 4 +- 4 files changed, 354 insertions(+), 1088 deletions(-) diff --git a/src/Base/Exception.cpp b/src/Base/Exception.cpp index acbf6aa16c..45bbe65155 100644 --- a/src/Base/Exception.cpp +++ b/src/Base/Exception.cpp @@ -21,13 +21,15 @@ * * ***************************************************************************/ - #include "PreCompiled.h" +#ifndef _PreComp_ +#include +#endif -#include "Exception.h" #include "Console.h" #include "PyObjectBase.h" +#include "Exception.h" FC_LOG_LEVEL_INIT("Exception", true, true) @@ -35,120 +37,96 @@ using namespace Base; TYPESYSTEM_SOURCE(Base::Exception, Base::BaseClass) - - -Exception::Exception() - : _sErrMsg("FreeCAD Exception") - , _line(0) - , _isTranslatable(false) - , _isReported(false) +Exception::Exception(std::string message) + : errorMessage {std::move(message)} {} Exception::Exception(const Exception& inst) = default; Exception::Exception(Exception&& inst) noexcept = default; -Exception::Exception(const char* sMessage) - : _sErrMsg(sMessage) - , _line(0) - , _isTranslatable(false) - , _isReported(false) -{} - -Exception::Exception(std::string sMessage) - : _sErrMsg(std::move(sMessage)) - , _line(0) - , _isTranslatable(false) - , _isReported(false) -{} - Exception& Exception::operator=(const Exception& inst) { - _sErrMsg = inst._sErrMsg; - _file = inst._file; - _line = inst._line; - _function = inst._function; - _isTranslatable = inst._isTranslatable; + errorMessage = inst.errorMessage; + fileName = inst.fileName; + lineNum = inst.lineNum; + functionName = inst.functionName; + isTranslatable = inst.isTranslatable; return *this; } Exception& Exception::operator=(Exception&& inst) noexcept { - _sErrMsg = std::move(inst._sErrMsg); - _file = std::move(inst._file); - _line = inst._line; - _function = std::move(inst._function); - _isTranslatable = inst._isTranslatable; + errorMessage = std::move(inst.errorMessage); + fileName = std::move(inst.fileName); + lineNum = inst.lineNum; + functionName = std::move(inst.functionName); + isTranslatable = inst.isTranslatable; return *this; } const char* Exception::what() const noexcept { - return _sErrMsg.c_str(); + return errorMessage.c_str(); } void Exception::ReportException() const { - if (!_isReported) { - const char* msg {}; - if (_sErrMsg.empty()) { - msg = typeid(*this).name(); - } - else { - msg = _sErrMsg.c_str(); - } -#ifdef FC_DEBUG - if (!_function.empty()) { - _FC_ERR(_file.c_str(), _line, _function << " -- " << msg); - } - else -#endif - { - _FC_ERR(_file.c_str(), _line, msg); - } - _isReported = true; + if (hasBeenReported) { + return; } + + std::string msg = errorMessage.empty() ? typeid(*this).name() : errorMessage; + +#ifdef FC_DEBUG + if (!functionName.empty()) { + msg = functionName + " -- " + msg; + } +#endif + + _FC_ERR(fileName.c_str(), lineNum, msg); + hasBeenReported = true; } PyObject* Exception::getPyObject() { Py::Dict edict; edict.setItem("sclassname", Py::String(typeid(*this).name())); - edict.setItem("sErrMsg", Py::String(this->getMessage())); - edict.setItem("sfile", Py::String(this->getFile())); - edict.setItem("iline", Py::Long(this->getLine())); - edict.setItem("sfunction", Py::String(this->getFunction())); - edict.setItem("swhat", Py::String(this->what())); - edict.setItem("btranslatable", Py::Boolean(this->getTranslatable())); - edict.setItem("breported", Py::Boolean(this->_isReported)); - return Py::new_reference_to(edict); + edict.setItem("sErrMsg", Py::String(getMessage())); + edict.setItem("sfile", Py::String(getFile())); + edict.setItem("iline", Py::Long(getLine())); + edict.setItem("sfunction", Py::String(getFunction())); + edict.setItem("swhat", Py::String(what())); + edict.setItem("btranslatable", Py::Boolean(getTranslatable())); + edict.setItem("breported", Py::Boolean(hasBeenReported)); + return new_reference_to(edict); } void Exception::setPyObject(PyObject* pydict) { try { if (pydict && Py::_Dict_Check(pydict)) { - Py::Dict edict(pydict); + const Py::Dict edict(pydict); if (edict.hasKey("sfile")) { - _file = static_cast(Py::String(edict.getItem("sfile"))); + fileName = Py::String(edict.getItem("sfile")); } if (edict.hasKey("sfunction")) { - _function = static_cast(Py::String(edict.getItem("sfunction"))); + functionName = Py::String(edict.getItem("sfunction")); } if (edict.hasKey("sErrMsg")) { - _sErrMsg = static_cast(Py::String(edict.getItem("sErrMsg"))); + errorMessage = Py::String(edict.getItem("sErrMsg")); } if (edict.hasKey("iline")) { - _line = static_cast(Py::Long(edict.getItem("iline"))); + lineNum = static_cast(Py::Long(edict.getItem("iline"))); } if (edict.hasKey("btranslatable")) { - _isTranslatable = static_cast(Py::Boolean(edict.getItem("btranslatable"))); + isTranslatable = static_cast(Py::Boolean(edict.getItem("btranslatable"))); } if (edict.hasKey("breported")) { - _isReported = static_cast(Py::Boolean(edict.getItem("breported"))); + hasBeenReported = static_cast(Py::Boolean(edict.getItem("breported"))); } } } @@ -176,15 +154,10 @@ void Exception::setPyException() const TYPESYSTEM_SOURCE(Base::AbortException, Base::Exception) -AbortException::AbortException(const char* sMessage) - : Exception(sMessage) +AbortException::AbortException(const std::string& message) + : Exception(message) {} -AbortException::AbortException() -{ - _sErrMsg = "Aborted operation"; -} - const char* AbortException::what() const noexcept { return Exception::what(); @@ -197,15 +170,8 @@ PyObject* AbortException::getPyExceptionType() const // --------------------------------------------------------- - -XMLBaseException::XMLBaseException() = default; - -XMLBaseException::XMLBaseException(const char* sMessage) - : Exception(sMessage) -{} - -XMLBaseException::XMLBaseException(const std::string& sMessage) - : Exception(sMessage) +XMLBaseException::XMLBaseException(const std::string& message) + : Exception(message) {} PyObject* XMLBaseException::getPyExceptionType() const @@ -215,19 +181,10 @@ PyObject* XMLBaseException::getPyExceptionType() const // --------------------------------------------------------- -XMLParseException::XMLParseException(const char* sMessage) - : XMLBaseException(sMessage) +XMLParseException::XMLParseException(const std::string& message) + : XMLBaseException(message) {} -XMLParseException::XMLParseException(const std::string& sMessage) - : XMLBaseException(sMessage) -{} - -XMLParseException::XMLParseException() -{ - _sErrMsg = "XML parse exception"; -} - const char* XMLParseException::what() const noexcept { return XMLBaseException::what(); @@ -240,19 +197,10 @@ PyObject* XMLParseException::getPyExceptionType() const // --------------------------------------------------------- -XMLAttributeError::XMLAttributeError(const char* sMessage) - : XMLBaseException(sMessage) +XMLAttributeError::XMLAttributeError(const std::string& message) + : XMLBaseException(message) {} -XMLAttributeError::XMLAttributeError(const std::string& sMessage) - : XMLBaseException(sMessage) -{} - -XMLAttributeError::XMLAttributeError() -{ - _sErrMsg = "XML attribute error"; -} - const char* XMLAttributeError::what() const noexcept { return XMLBaseException::what(); @@ -265,33 +213,27 @@ PyObject* XMLAttributeError::getPyExceptionType() const // --------------------------------------------------------- - -FileException::FileException(const char* sMessage, const char* sFileName) - : Exception(sMessage) - , file(sFileName) +FileException::FileException(const std::string& message, const std::string& fileName) + : Exception(message) + , file(fileName) { - setFileName(sFileName); + setFileName(fileName); } -FileException::FileException(const char* sMessage, const FileInfo& File) - : Exception(sMessage) +FileException::FileException(const std::string& message, const FileInfo& File) + : Exception(message) , file(File) { - setFileName(File.filePath().c_str()); + setFileName(File.filePath()); } -FileException::FileException() - : Exception("Unknown file exception happened") - , _sErrMsgAndFileName(_sErrMsg) -{} - -void FileException::setFileName(const char* sFileName) +void FileException::setFileName(const std::string& fileName) { - file.setFile(sFileName); - _sErrMsgAndFileName = _sErrMsg; - if (sFileName) { + file.setFile(fileName); + _sErrMsgAndFileName = getMessage(); + if (!getFile().empty()) { _sErrMsgAndFileName += ": "; - _sErrMsgAndFileName += sFileName; + _sErrMsgAndFileName += fileName; } } @@ -307,32 +249,26 @@ const char* FileException::what() const noexcept void FileException::ReportException() const { - if (!_isReported) { - const char* msg {}; - if (_sErrMsgAndFileName.empty()) { - msg = typeid(*this).name(); - } - else { - msg = _sErrMsgAndFileName.c_str(); - } -#ifdef FC_DEBUG - if (!_function.empty()) { - _FC_ERR(_file.c_str(), _line, _function << " -- " << msg); - } - else -#endif - { - _FC_ERR(_file.c_str(), _line, msg); - } - _isReported = true; + if (getReported()) { + return; } + std::string msg = _sErrMsgAndFileName.empty() ? typeid(*this).name() : _sErrMsgAndFileName; + +#ifdef FC_DEBUG + if (!getFunction().empty()) { + msg = getFunction() + " -- " + msg; + } +#endif + + _FC_ERR(getFile().c_str(), getLine(), msg); + setReported(true); } PyObject* FileException::getPyObject() { Py::Dict edict(Exception::getPyObject(), true); edict.setItem("filename", Py::String(this->file.fileName())); - return Py::new_reference_to(edict); + return new_reference_to(edict); } void FileException::setPyObject(PyObject* pydict) @@ -340,9 +276,8 @@ void FileException::setPyObject(PyObject* pydict) if (pydict) { Exception::setPyObject(pydict); - Py::Dict edict(pydict); - if (edict.hasKey("filename")) { - setFileName(Py::String(edict.getItem("filename")).as_std_string("utf-8").c_str()); + if (const Py::Dict edict(pydict); edict.hasKey("filename")) { + setFileName(Py::String(edict.getItem("filename")).as_std_string("utf-8")); } } } @@ -354,15 +289,8 @@ PyObject* FileException::getPyExceptionType() const // --------------------------------------------------------- - -FileSystemError::FileSystemError() = default; - -FileSystemError::FileSystemError(const char* sMessage) - : Exception(sMessage) -{} - -FileSystemError::FileSystemError(const std::string& sMessage) - : Exception(sMessage) +FileSystemError::FileSystemError(const std::string& message) + : Exception(message) {} PyObject* FileSystemError::getPyExceptionType() const @@ -372,15 +300,8 @@ PyObject* FileSystemError::getPyExceptionType() const // --------------------------------------------------------- - -BadFormatError::BadFormatError() = default; - -BadFormatError::BadFormatError(const char* sMessage) - : Exception(sMessage) -{} - -BadFormatError::BadFormatError(const std::string& sMessage) - : Exception(sMessage) +BadFormatError::BadFormatError(const std::string& message) + : Exception(message) {} PyObject* BadFormatError::getPyExceptionType() const @@ -390,47 +311,14 @@ PyObject* BadFormatError::getPyExceptionType() const // --------------------------------------------------------- - -MemoryException::MemoryException() -{ - _sErrMsg = "Not enough memory available"; -} - -MemoryException::MemoryException(const MemoryException& inst) -#if defined(__GNUC__) - : std::bad_alloc() - , Exception(inst) -#else - : Exception(inst) -#endif +MemoryException::MemoryException(const std::string& message) + : Exception(message) // NOLINT(*-throw-keyword-missing) {} -MemoryException::MemoryException(MemoryException&& inst) noexcept -#if defined(__GNUC__) - : std::bad_alloc() - , Exception(inst) -#else - : Exception(inst) -#endif -{} - -MemoryException& MemoryException::operator=(const MemoryException& inst) -{ - Exception::operator=(inst); - return *this; -} - -MemoryException& MemoryException::operator=(MemoryException&& inst) noexcept -{ - Exception::operator=(inst); - return *this; -} - #if defined(__GNUC__) const char* MemoryException::what() const noexcept { - // call what() of Exception, not of std::bad_alloc - return Exception::what(); + return Exception::what(); // from Exception, not std::bad_alloc } #endif @@ -441,17 +329,8 @@ PyObject* MemoryException::getPyExceptionType() const // --------------------------------------------------------- -AccessViolation::AccessViolation() -{ - _sErrMsg = "Access violation"; -} - -AccessViolation::AccessViolation(const char* sMessage) - : Exception(sMessage) -{} - -AccessViolation::AccessViolation(const std::string& sMessage) - : Exception(sMessage) +AccessViolation::AccessViolation(const std::string& message) + : Exception(message) {} PyObject* AccessViolation::getPyExceptionType() const @@ -461,17 +340,8 @@ PyObject* AccessViolation::getPyExceptionType() const // --------------------------------------------------------- -AbnormalProgramTermination::AbnormalProgramTermination() -{ - _sErrMsg = "Abnormal program termination"; -} - -AbnormalProgramTermination::AbnormalProgramTermination(const char* sMessage) - : Exception(sMessage) -{} - -AbnormalProgramTermination::AbnormalProgramTermination(const std::string& sMessage) - : Exception(sMessage) +AbnormalProgramTermination::AbnormalProgramTermination(const std::string& message) + : Exception(message) {} PyObject* AbnormalProgramTermination::getPyExceptionType() const @@ -481,14 +351,8 @@ PyObject* AbnormalProgramTermination::getPyExceptionType() const // --------------------------------------------------------- -UnknownProgramOption::UnknownProgramOption() = default; - -UnknownProgramOption::UnknownProgramOption(const char* sMessage) - : Exception(sMessage) -{} - -UnknownProgramOption::UnknownProgramOption(const std::string& sMessage) - : Exception(sMessage) +UnknownProgramOption::UnknownProgramOption(const std::string& message) + : Exception(message) {} PyObject* UnknownProgramOption::getPyExceptionType() const @@ -498,26 +362,14 @@ PyObject* UnknownProgramOption::getPyExceptionType() const // --------------------------------------------------------- -ProgramInformation::ProgramInformation() = default; - -ProgramInformation::ProgramInformation(const char* sMessage) - : Exception(sMessage) -{} - -ProgramInformation::ProgramInformation(const std::string& sMessage) - : Exception(sMessage) +ProgramInformation::ProgramInformation(const std::string& message) + : Exception(message) {} // --------------------------------------------------------- -TypeError::TypeError() = default; - -TypeError::TypeError(const char* sMessage) - : Exception(sMessage) -{} - -TypeError::TypeError(const std::string& sMessage) - : Exception(sMessage) +TypeError::TypeError(const std::string& message) + : Exception(message) {} PyObject* TypeError::getPyExceptionType() const @@ -527,14 +379,8 @@ PyObject* TypeError::getPyExceptionType() const // --------------------------------------------------------- -ValueError::ValueError() = default; - -ValueError::ValueError(const char* sMessage) - : Exception(sMessage) -{} - -ValueError::ValueError(const std::string& sMessage) - : Exception(sMessage) +ValueError::ValueError(const std::string& message) + : Exception(message) {} PyObject* ValueError::getPyExceptionType() const @@ -544,14 +390,8 @@ PyObject* ValueError::getPyExceptionType() const // --------------------------------------------------------- -IndexError::IndexError() = default; - -IndexError::IndexError(const char* sMessage) - : Exception(sMessage) -{} - -IndexError::IndexError(const std::string& sMessage) - : Exception(sMessage) +IndexError::IndexError(const std::string& message) + : Exception(message) {} PyObject* IndexError::getPyExceptionType() const @@ -561,14 +401,8 @@ PyObject* IndexError::getPyExceptionType() const // --------------------------------------------------------- -NameError::NameError() = default; - -NameError::NameError(const char* sMessage) - : Exception(sMessage) -{} - -NameError::NameError(const std::string& sMessage) - : Exception(sMessage) +NameError::NameError(const std::string& message) + : Exception(message) {} PyObject* NameError::getPyExceptionType() const @@ -578,14 +412,8 @@ PyObject* NameError::getPyExceptionType() const // --------------------------------------------------------- -ImportError::ImportError() = default; - -ImportError::ImportError(const char* sMessage) - : Exception(sMessage) -{} - -ImportError::ImportError(const std::string& sMessage) - : Exception(sMessage) +ImportError::ImportError(const std::string& message) + : Exception(message) {} PyObject* ImportError::getPyExceptionType() const @@ -595,14 +423,8 @@ PyObject* ImportError::getPyExceptionType() const // --------------------------------------------------------- -AttributeError::AttributeError() = default; - -AttributeError::AttributeError(const char* sMessage) - : Exception(sMessage) -{} - -AttributeError::AttributeError(const std::string& sMessage) - : Exception(sMessage) +AttributeError::AttributeError(const std::string& message) + : Exception(message) {} PyObject* AttributeError::getPyExceptionType() const @@ -612,14 +434,8 @@ PyObject* AttributeError::getPyExceptionType() const // --------------------------------------------------------- -PropertyError::PropertyError() = default; - -PropertyError::PropertyError(const char* sMessage) - : AttributeError(sMessage) -{} - -PropertyError::PropertyError(const std::string& sMessage) - : AttributeError(sMessage) +PropertyError::PropertyError(const std::string& message) + : AttributeError(message) {} PyObject* PropertyError::getPyExceptionType() const @@ -629,14 +445,8 @@ PyObject* PropertyError::getPyExceptionType() const // --------------------------------------------------------- -RuntimeError::RuntimeError() = default; - -RuntimeError::RuntimeError(const char* sMessage) - : Exception(sMessage) -{} - -RuntimeError::RuntimeError(const std::string& sMessage) - : Exception(sMessage) +RuntimeError::RuntimeError(const std::string& message) + : Exception(message) {} PyObject* RuntimeError::getPyExceptionType() const @@ -646,16 +456,8 @@ PyObject* RuntimeError::getPyExceptionType() const // --------------------------------------------------------- -BadGraphError::BadGraphError() - : RuntimeError("The graph must be a DAG.") -{} - -BadGraphError::BadGraphError(const char* sMessage) - : RuntimeError(sMessage) -{} - -BadGraphError::BadGraphError(const std::string& sMessage) - : RuntimeError(sMessage) +BadGraphError::BadGraphError(const std::string& message) + : RuntimeError(message) {} PyObject* BadGraphError::getPyExceptionType() const @@ -665,14 +467,8 @@ PyObject* BadGraphError::getPyExceptionType() const // --------------------------------------------------------- -NotImplementedError::NotImplementedError() = default; - -NotImplementedError::NotImplementedError(const char* sMessage) - : Exception(sMessage) -{} - -NotImplementedError::NotImplementedError(const std::string& sMessage) - : Exception(sMessage) +NotImplementedError::NotImplementedError(const std::string& message) + : Exception(message) {} PyObject* NotImplementedError::getPyExceptionType() const @@ -682,14 +478,8 @@ PyObject* NotImplementedError::getPyExceptionType() const // --------------------------------------------------------- -ZeroDivisionError::ZeroDivisionError() = default; - -ZeroDivisionError::ZeroDivisionError(const char* sMessage) - : Exception(sMessage) -{} - -ZeroDivisionError::ZeroDivisionError(const std::string& sMessage) - : Exception(sMessage) +ZeroDivisionError::ZeroDivisionError(const std::string& message) + : Exception(message) {} PyObject* ZeroDivisionError::getPyExceptionType() const @@ -699,14 +489,8 @@ PyObject* ZeroDivisionError::getPyExceptionType() const // --------------------------------------------------------- -ReferenceError::ReferenceError() = default; - -ReferenceError::ReferenceError(const char* sMessage) - : Exception(sMessage) -{} - -ReferenceError::ReferenceError(const std::string& sMessage) - : Exception(sMessage) +ReferenceError::ReferenceError(const std::string& message) + : Exception(message) {} PyObject* ReferenceError::getPyExceptionType() const @@ -716,14 +500,8 @@ PyObject* ReferenceError::getPyExceptionType() const // --------------------------------------------------------- -ExpressionError::ExpressionError() = default; - -ExpressionError::ExpressionError(const char* sMessage) - : Exception(sMessage) -{} - -ExpressionError::ExpressionError(const std::string& sMessage) - : Exception(sMessage) +ExpressionError::ExpressionError(const std::string& message) + : Exception(message) {} PyObject* ExpressionError::getPyExceptionType() const @@ -733,14 +511,8 @@ PyObject* ExpressionError::getPyExceptionType() const // --------------------------------------------------------- -ParserError::ParserError() = default; - -ParserError::ParserError(const char* sMessage) - : Exception(sMessage) -{} - -ParserError::ParserError(const std::string& sMessage) - : Exception(sMessage) +ParserError::ParserError(const std::string& message) + : Exception(message) {} PyObject* ParserError::getPyExceptionType() const @@ -750,14 +522,8 @@ PyObject* ParserError::getPyExceptionType() const // --------------------------------------------------------- -UnicodeError::UnicodeError() = default; - -UnicodeError::UnicodeError(const char* sMessage) - : Exception(sMessage) -{} - -UnicodeError::UnicodeError(const std::string& sMessage) - : Exception(sMessage) +UnicodeError::UnicodeError(const std::string& message) + : Exception(message) {} PyObject* UnicodeError::getPyExceptionType() const @@ -767,14 +533,8 @@ PyObject* UnicodeError::getPyExceptionType() const // --------------------------------------------------------- -OverflowError::OverflowError() = default; - -OverflowError::OverflowError(const char* sMessage) - : Exception(sMessage) -{} - -OverflowError::OverflowError(const std::string& sMessage) - : Exception(sMessage) +OverflowError::OverflowError(const std::string& message) + : Exception(message) {} PyObject* OverflowError::getPyExceptionType() const @@ -784,14 +544,8 @@ PyObject* OverflowError::getPyExceptionType() const // --------------------------------------------------------- -UnderflowError::UnderflowError() = default; - -UnderflowError::UnderflowError(const char* sMessage) - : Exception(sMessage) -{} - -UnderflowError::UnderflowError(const std::string& sMessage) - : Exception(sMessage) +UnderflowError::UnderflowError(const std::string& message) + : Exception(message) {} PyObject* UnderflowError::getPyExceptionType() const @@ -801,14 +555,8 @@ PyObject* UnderflowError::getPyExceptionType() const // --------------------------------------------------------- -UnitsMismatchError::UnitsMismatchError() = default; - -UnitsMismatchError::UnitsMismatchError(const char* sMessage) - : Exception(sMessage) -{} - -UnitsMismatchError::UnitsMismatchError(const std::string& sMessage) - : Exception(sMessage) +UnitsMismatchError::UnitsMismatchError(const std::string& message) + : Exception(message) {} PyObject* UnitsMismatchError::getPyExceptionType() const @@ -818,14 +566,8 @@ PyObject* UnitsMismatchError::getPyExceptionType() const // --------------------------------------------------------- -CADKernelError::CADKernelError() = default; - -CADKernelError::CADKernelError(const char* sMessage) - : Exception(sMessage) -{} - -CADKernelError::CADKernelError(const std::string& sMessage) - : Exception(sMessage) +CADKernelError::CADKernelError(const std::string& message) + : Exception(message) {} PyObject* CADKernelError::getPyExceptionType() const @@ -835,14 +577,8 @@ PyObject* CADKernelError::getPyExceptionType() const // --------------------------------------------------------- -RestoreError::RestoreError() = default; - -RestoreError::RestoreError(const char* sMessage) - : Exception(sMessage) -{} - -RestoreError::RestoreError(const std::string& sMessage) - : Exception(sMessage) +RestoreError::RestoreError(const std::string& message) + : Exception(message) {} PyObject* RestoreError::getPyExceptionType() const @@ -855,6 +591,7 @@ PyObject* RestoreError::getPyExceptionType() const #if defined(__GNUC__) && defined(FC_OS_LINUX) #include #include +#include SignalException::SignalException() { @@ -876,7 +613,7 @@ SignalException::~SignalException() #endif } -void SignalException::throw_signal(int signum) +void SignalException::throw_signal(const int signum) { std::cerr << "SIGSEGV signal raised: " << signum << std::endl; throw std::runtime_error("throw_signal"); diff --git a/src/Base/Exception.h b/src/Base/Exception.h index 2ebdd78744..047ea54cb9 100644 --- a/src/Base/Exception.h +++ b/src/Base/Exception.h @@ -21,172 +21,126 @@ * * ***************************************************************************/ - #ifndef BASE_EXCEPTION_H #define BASE_EXCEPTION_H #include +#include #include + #include "BaseClass.h" #include "FileInfo.h" +using PyObject = struct _object; // NOLINT -using PyObject = struct _object; +// Remove once all used compilers support this +#if defined(__cpp_lib_source_location) +#define HAVE_STD_SOURCE_LOCATION 1 +#else +#undef HAVE_STD_SOURCE_LOCATION +#endif +// std::source_location is implemented, but buggy in Clang 15 +#if defined(__clang__) && __clang_major__ <= 15 +#undef HAVE_STD_SOURCE_LOCATION +#endif -/* MACROS FOR THROWING EXCEPTIONS */ - -/// the macros do NOT mark any message for translation +/// The macros do NOT mark any message for translation /// If you want to mark text for translation, use the QT_TRANSLATE_NOOP macro -/// with the context "Exceptions" and the right throwing macro from below (the one ending in T) +/// with the context "Exceptions" and the right throwing macro from below (the one ending with T) /// example: /// THROWMT(Base::ValueError,QT_TRANSLATE_NOOP("Exceptions","The multiplicity cannot be increased /// beyond the degree of the B-Spline.")); /// /// N.B.: The QT_TRANSLATE_NOOP macro won't translate your string. It will just allow lupdate to /// identify that string for translation so that if you ask for a translation (and the translator -/// have provided one) at that time it gets translated (e.g. in the UI before showing the message of -/// the exception). +/// have provided one) at that time it gets translated (e.g. in the UI before showing the message +/// of the exception). -// NOLINTBEGIN -#ifdef _MSC_VER - -#define THROW(exception) \ - { \ - exception myexcp; \ - myexcp.setDebugInformation(__FILE__, __LINE__, __FUNCSIG__); \ - throw myexcp; \ - } -#define THROWM(exception, message) \ - { \ - exception myexcp(message); \ - myexcp.setDebugInformation(__FILE__, __LINE__, __FUNCSIG__); \ - throw myexcp; \ - } -#define THROWMF_FILEEXCEPTION(message, filenameorfileinfo) \ - { \ - FileException myexcp(message, filenameorfileinfo); \ - myexcp.setDebugInformation(__FILE__, __LINE__, __FUNCSIG__); \ - throw myexcp; \ - } - -#define THROWT(exception) \ - { \ - exception myexcp; \ - myexcp.setDebugInformation(__FILE__, __LINE__, __FUNCSIG__); \ - myexcp.setTranslatable(true); \ - throw myexcp; \ - } -#define THROWMT(exception, message) \ - { \ - exception myexcp(message); \ - myexcp.setDebugInformation(__FILE__, __LINE__, __FUNCSIG__); \ - myexcp.setTranslatable(true); \ - throw myexcp; \ - } -#define THROWMFT_FILEEXCEPTION(message, filenameorfileinfo) \ - { \ - FileException myexcp(message, filenameorfileinfo); \ - myexcp.setDebugInformation(__FILE__, __LINE__, __FUNCSIG__); \ - myexcp.setTranslatable(true); \ - throw myexcp; \ - } - -#elif defined(__GNUC__) - -#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; \ - } - -#define THROWT(exception) \ - { \ - exception myexcp; \ - myexcp.setDebugInformation(__FILE__, __LINE__, __PRETTY_FUNCTION__); \ - myexcp.setTranslatable(true); \ - throw myexcp; \ - } -#define THROWMT(exception, message) \ - { \ - exception myexcp(message); \ - myexcp.setDebugInformation(__FILE__, __LINE__, __PRETTY_FUNCTION__); \ - myexcp.setTranslatable(true); \ - throw myexcp; \ - } -#define THROWMFT_FILEEXCEPTION(message, filenameorfileinfo) \ - { \ - FileException myexcp(message, filenameorfileinfo); \ - myexcp.setDebugInformation(__FILE__, __LINE__, __PRETTY_FUNCTION__); \ - myexcp.setTranslatable(true); \ - throw myexcp; \ - } - -#else - -#define THROW(exception) \ - { \ - exception myexcp; \ - myexcp.setDebugInformation(__FILE__, __LINE__, __func__); \ - throw myexcp; \ - } -#define THROWM(exception, message) \ - { \ - exception myexcp(message); \ - myexcp.setDebugInformation(__FILE__, __LINE__, __func__); \ - throw myexcp; \ - } -#define THROWMF_FILEEXCEPTION(message, filenameorfileinfo) \ - { \ - FileException myexcp(message, filenameorfileinfo); \ - myexcp.setDebugInformation(__FILE__, __LINE__, __func__); \ - throw myexcp; \ - } - -#define THROWT(exception) \ - { \ - exception myexcp; \ - myexcp.setDebugInformation(__FILE__, __LINE__, __func__); \ - myexcp.setTranslatable(true); \ - throw myexcp; \ - } -#define THROWMT(exception, message) \ - { \ - exception myexcp(message); \ - myexcp.setDebugInformation(__FILE__, __LINE__, __func__); \ - myexcp.setTranslatable(true); \ - throw myexcp; \ - } -#define THROWMFT_FILEEXCEPTION(message, filenameorfileinfo) \ - { \ - FileException myexcp(message, filenameorfileinfo); \ - myexcp.setDebugInformation(__FILE__, __LINE__, __func__); \ - myexcp.setTranslatable(true); \ - throw myexcp; \ - } - - -#endif - -#define FC_THROWM(_exception, _msg) \ +#if defined(HAVE_STD_SOURCE_LOCATION) +// NOLINTBEGIN(*-macro-usage) +#define THROWM(exc, msg) Base::setupAndThrowException((msg), std::source_location::current()); +#define THROWMT(exc, msg) \ + Base::setupAndThrowException((msg), std::source_location::current(), true); +#define FC_THROWM(exception, msg) \ do { \ std::stringstream ss; \ - ss << _msg; \ - THROWM(_exception, ss.str().c_str()); \ + ss << msg; \ + THROWM(exception, ss.str()); \ } while (0) -// NOLINTEND +// NOLINTEND(*-macro-usage) + +namespace Base +{ +template +[[noreturn]] void setupAndThrowException(const std::string message, + const std::source_location location, + const bool translatable = false) +{ + ExceptionType exception {message}; + exception.setTranslatable(translatable); + exception.setDebugInformation(location); + throw exception; +} // NOLINT // unreachable +} // namespace Base + +#else // HAVE_STD_SOURCE_LOCATION + +#ifdef _MSC_VER +#define FC_THROW_INFO __FILE__, __LINE__, __FUNCSIG__ +#elif __GNUC__ +#define FC_THROW_INFO __FILE__, __LINE__, __PRETTY_FUNCTION__ +#else +#define FC_THROW_INFO __FILE__, __LINE__, __func__ +#endif + +#define THROWM(exc, msg) Base::setupAndThrowException(msg, FC_THROW_INFO); +#define THROWMT(exc, msg) Base::setupAndThrowException(msg, FC_THROW_INFO, true); +#define FC_THROWM(exception, msg) \ + do { \ + std::stringstream ss; \ + ss << msg; \ + THROWM(exception, ss.str()); \ + } while (0) +namespace Base +{ +template +[[noreturn]] void setupAndThrowException(const std::string message, + const char* file, + const int line, + const char* func, + const bool translatable = false) +{ + ExceptionType exception {message}; + exception.setTranslatable(translatable); + exception.setDebugInformation(file, line, func); + throw exception; +} // NOLINT // unreachable +} // namespace Base + +#endif // HAVE_STD_SOURCE_LOCATION + +//-------------------------------------------------------------------------------------------------- + +template +constexpr void THROWM_(const std::string& msg, + const std::source_location location = std::source_location::current()) +{ + Base::setupAndThrowException(msg, location); +} + +template +constexpr void THROWMT_(const std::string& msg, + const std::source_location location = std::source_location::current()) +{ + Base::setupAndThrowException(msg, location, true); +} + +template +constexpr void FC_THROWM_(const std::string& raw_msg, + const std::source_location location = std::source_location::current()) +{ + THROWM_(raw_msg, location); +} namespace Base { @@ -196,18 +150,16 @@ class BaseExport Exception: public BaseClass TYPESYSTEM_HEADER_WITH_OVERRIDE(); public: + explicit Exception(std::string message = "FreeCAD Exception"); ~Exception() noexcept override = default; Exception& operator=(const Exception& inst); Exception& operator=(Exception&& inst) noexcept; virtual const char* what() const noexcept; + virtual void ReportException() const; // once only - /// Reports exception. It includes a mechanism to only report an exception once. - virtual void ReportException() const; - - inline void setMessage(const char* sMessage); - inline void setMessage(const std::string& sMessage); + inline void setMessage(const std::string& message); // what may differ from the message given by the user in // derived classes inline std::string getMessage() const; @@ -215,212 +167,104 @@ public: inline int getLine() const; inline std::string getFunction() const; inline bool getTranslatable() const; - inline bool getReported() const - { - return _isReported; - } + inline bool getReported() const; + inline void setReported(bool reported) const; - /// setter methods for including debug information - /// intended to use via macro for autofilling of debugging information - inline void setDebugInformation(const std::string& file, int line, const std::string& function); +#if defined(HAVE_STD_SOURCE_LOCATION) + inline void setDebugInformation(const std::source_location& location); +#else + inline void setDebugInformation(const char* file, int line, const char* func); +#endif inline void setTranslatable(bool translatable); - inline void setReported(bool reported) - { - _isReported = reported; - } + PyObject* getPyObject() override; // exception data + void setPyObject(PyObject* pydict) override; // set the exception data - /// returns a Python dictionary containing the exception data - PyObject* getPyObject() override; - /// returns sets the exception data from a Python dictionary - void setPyObject(PyObject* pydict) override; - - /// returns the corresponding python exception type virtual PyObject* getPyExceptionType() const; - /// Sets the Python error indicator and an error message virtual void setPyException() const; protected: - /* sMessage may be: - * - a UI compliant string susceptible to being translated and shown to the user in the UI - * - a very technical message not intended to be translated 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. */ - explicit Exception(const char* sMessage); - explicit Exception(std::string sMessage); - Exception(); Exception(const Exception& inst); Exception(Exception&& inst) noexcept; -protected: - std::string _sErrMsg; - std::string _file; - int _line; - std::string _function; - bool _isTranslatable; - mutable bool _isReported; +private: + std::string errorMessage; + std::string fileName; + int lineNum {0}; + std::string functionName; + bool isTranslatable {false}; + mutable bool hasBeenReported {false}; }; - -/** - * The AbortException is thrown if a pending operation was aborted. - * @author Werner Mayer - */ class BaseExport AbortException: public Exception { TYPESYSTEM_HEADER_WITH_OVERRIDE(); public: - /// Construction - explicit AbortException(const char* sMessage); - /// Construction - AbortException(); - AbortException(const AbortException&) = default; - AbortException(AbortException&&) = default; + explicit AbortException(const std::string& message = "Aborted operation"); - /// Destruction - ~AbortException() noexcept override = default; - AbortException& operator=(const AbortException&) = default; - AbortException& operator=(AbortException&&) = default; - - /// Description of the exception const char* what() const noexcept override; - /// returns the corresponding python exception type PyObject* getPyExceptionType() const override; }; -/** - * The XMLBaseException can be used to indicate any kind of XML related errors. - * @author Werner Mayer - */ class BaseExport XMLBaseException: public Exception { public: - /// Construction - XMLBaseException(); - explicit XMLBaseException(const char* sMessage); - explicit XMLBaseException(const std::string& sMessage); - XMLBaseException(const XMLBaseException&) = default; - XMLBaseException(XMLBaseException&&) = default; - - /// Destruction - ~XMLBaseException() noexcept override = default; - XMLBaseException& operator=(const XMLBaseException&) = default; - XMLBaseException& operator=(XMLBaseException&&) = default; + explicit XMLBaseException(const std::string& message = "XML base exception"); PyObject* getPyExceptionType() const override; }; -/** - * The XMLParseException is thrown if parsing an XML failed. - * @author Werner Mayer - */ class BaseExport XMLParseException: public XMLBaseException { public: - /// Construction - explicit XMLParseException(const char* sMessage); - /// Construction - explicit XMLParseException(const std::string& sMessage); - /// Construction - XMLParseException(); - XMLParseException(const XMLParseException&) = default; - XMLParseException(XMLParseException&&) = default; + explicit XMLParseException(const std::string& message = "XML parse exception"); - /// Destruction - ~XMLParseException() noexcept override = default; - XMLParseException& operator=(const XMLParseException&) = default; - XMLParseException& operator=(XMLParseException&&) = default; - - /// Description of the exception const char* what() const noexcept override; PyObject* getPyExceptionType() const override; }; -/** - * The XMLAttributeError is thrown if a requested attribute doesn't exist. - * @author Werner Mayer - */ class BaseExport XMLAttributeError: public XMLBaseException { public: - /// Construction - explicit XMLAttributeError(const char* sMessage); - /// Construction - explicit XMLAttributeError(const std::string& sMessage); - /// Construction - XMLAttributeError(); - XMLAttributeError(const XMLAttributeError&) = default; - XMLAttributeError(XMLAttributeError&&) = default; + explicit XMLAttributeError(const std::string& message = "XML attribute error"); - /// Destruction - ~XMLAttributeError() noexcept override = default; - XMLAttributeError& operator=(const XMLAttributeError&) = default; - XMLAttributeError& operator=(XMLAttributeError&&) = default; - - /// Description of the exception const char* what() const noexcept override; PyObject* getPyExceptionType() const override; }; -/** File exception handling class - * This class is specialized to go with exception thrown in case of File IO Problems. - * @author Juergen Riegel - */ class BaseExport FileException: public Exception { public: - /// With massage and file name - explicit FileException(const char* sMessage, const char* sFileName = nullptr); - /// With massage and file name - FileException(const char* sMessage, const FileInfo& File); - /// standard construction - FileException(); - FileException(const FileException&) = default; - FileException(FileException&&) = default; - /// Destruction - ~FileException() noexcept override = default; - /// Assignment operator - FileException& operator=(const FileException&) = default; - FileException& operator=(FileException&&) = default; + explicit FileException(const std::string& message = "Unknown file exception happened", + const std::string& fileName = ""); + FileException(const std::string& message, const FileInfo& File); - /// Description of the exception const char* what() const noexcept override; - /// Report generation void ReportException() const override; - /// Get file name for use with translatable message std::string getFileName() const; - /// returns a Python dictionary containing the exception data PyObject* getPyObject() override; - /// returns sets the exception data from a Python dictionary + void setPyObject(PyObject* pydict) override; PyObject* getPyExceptionType() const override; -protected: +private: FileInfo file; // necessary 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; - void setFileName(const char* sFileName = nullptr); + void setFileName(const std::string& fileName); }; -/** - * The FileSystemError can be used to indicate errors on file system - * e.g. if renaming of a file failed. - * @author Werner Mayer - */ class BaseExport FileSystemError: public Exception { public: - /// Construction - FileSystemError(); - explicit FileSystemError(const char* sMessage); - explicit FileSystemError(const std::string& sMessage); + explicit FileSystemError(const std::string& message = "File system error"); FileSystemError(const FileSystemError&) = default; FileSystemError(FileSystemError&&) = default; - /// Destruction + ~FileSystemError() noexcept override = default; FileSystemError& operator=(const FileSystemError&) = default; FileSystemError& operator=(FileSystemError&&) = default; @@ -428,580 +272,267 @@ public: PyObject* getPyExceptionType() const override; }; -/** - * The BadFormatError can be used to indicate errors in a data structure. - * @author Werner Mayer - */ +/** errors in a data structure */ class BaseExport BadFormatError: public Exception { public: - /// Construction - BadFormatError(); - explicit BadFormatError(const char* sMessage); - explicit BadFormatError(const std::string& sMessage); + explicit BadFormatError(const std::string& message = "Bad format error"); BadFormatError(const BadFormatError&) = default; BadFormatError(BadFormatError&&) = default; - /// Destruction + ~BadFormatError() noexcept override = default; BadFormatError& operator=(const BadFormatError&) = default; BadFormatError& operator=(BadFormatError&&) = default; PyObject* getPyExceptionType() const override; }; -/** - * The MemoryException is thrown if not enough memory can be allocated. - * @author Werner Mayer - */ #if defined(__GNUC__) -// It seems that the calling instance of our new handler expects a bad_alloc exception +// calling instance of our new handler expects a bad_alloc exception class BaseExport MemoryException: public Exception, virtual public std::bad_alloc #else class BaseExport MemoryException: public Exception #endif { public: - /// Construction - MemoryException(); - /// Construction - MemoryException(const MemoryException& inst); - MemoryException(MemoryException&& inst) noexcept; - /// Destruction - ~MemoryException() noexcept override = default; - /// Assignment operator - MemoryException& operator=(const MemoryException& inst); - MemoryException& operator=(MemoryException&& inst) noexcept; + explicit MemoryException(const std::string& = "Not enough memory available"); + #if defined(__GNUC__) - /// Description of the exception const char* what() const noexcept override; #endif + PyObject* getPyExceptionType() const override; }; -/** - * The AccessViolation can be used in an own signal handler. - * @author Werner Mayer - */ +/** can be used in an own signal handler */ class BaseExport AccessViolation: public Exception { public: - /// Construction - AccessViolation(); - explicit AccessViolation(const char* sMessage); - explicit AccessViolation(const std::string& sMessage); - AccessViolation(const AccessViolation&) = default; - AccessViolation(AccessViolation&&) = default; - /// Destruction - ~AccessViolation() noexcept override = default; - AccessViolation& operator=(const AccessViolation&) = default; - AccessViolation& operator=(AccessViolation&&) = default; + explicit AccessViolation(const std::string& message = "Access violation"); PyObject* getPyExceptionType() const override; }; -/** - * The AbnormalProgramTermination can be used in an own signal handler. - * @author Werner Mayer - */ +/** can be used in an own signal handler */ class BaseExport AbnormalProgramTermination: public Exception { public: - /// Construction - AbnormalProgramTermination(); - /// Construction - explicit AbnormalProgramTermination(const char* sMessage); - explicit AbnormalProgramTermination(const std::string& sMessage); - AbnormalProgramTermination(const AbnormalProgramTermination&) = default; - AbnormalProgramTermination(AbnormalProgramTermination&&) = default; - /// Destruction - ~AbnormalProgramTermination() noexcept override = default; - AbnormalProgramTermination& operator=(const AbnormalProgramTermination&) = default; - AbnormalProgramTermination& operator=(AbnormalProgramTermination&&) = default; + explicit AbnormalProgramTermination( + const std::string& message = "Abnormal program termination"); PyObject* getPyExceptionType() const override; }; -/** - * The UnknownProgramOption can be used to indicate an unknown program option. - * @author Werner Mayer - */ class BaseExport UnknownProgramOption: public Exception { public: - /// Construction - UnknownProgramOption(); - explicit UnknownProgramOption(const char* sMessage); - explicit UnknownProgramOption(const std::string& sMessage); - UnknownProgramOption(const UnknownProgramOption&) = default; - UnknownProgramOption(UnknownProgramOption&&) = default; - /// Destruction - ~UnknownProgramOption() noexcept override = default; - UnknownProgramOption& operator=(const UnknownProgramOption&) = default; - UnknownProgramOption& operator=(UnknownProgramOption&&) = default; + explicit UnknownProgramOption(const std::string& message = "Unknown program option"); PyObject* getPyExceptionType() const override; }; -/** - * The ProgramInformation can be used to show information about the program. - * @author Werner Mayer - */ class BaseExport ProgramInformation: public Exception { public: - /// Construction - ProgramInformation(); - explicit ProgramInformation(const char* sMessage); - explicit ProgramInformation(const std::string& sMessage); - ProgramInformation(const ProgramInformation&) = default; - ProgramInformation(ProgramInformation&&) = default; - - /// Destruction - ~ProgramInformation() noexcept override = default; - ProgramInformation& operator=(const ProgramInformation&) = default; - ProgramInformation& operator=(ProgramInformation&&) = default; + explicit ProgramInformation(const std::string& message = "Program information"); }; -/** - * The TypeError can be used to indicate the usage of a wrong type. - * @author Werner Mayer - */ class BaseExport TypeError: public Exception { public: - /// Construction - TypeError(); - explicit TypeError(const char* sMessage); - explicit TypeError(const std::string& sMessage); - TypeError(const TypeError&) = default; - TypeError(TypeError&&) = default; - /// Destruction - ~TypeError() noexcept override = default; - TypeError& operator=(const TypeError&) = default; - TypeError& operator=(TypeError&&) = default; + explicit TypeError(const std::string& message = "Type error"); PyObject* getPyExceptionType() const override; }; -/** - * The ValueError can be used to indicate the usage of a wrong value. - * @author Werner Mayer - */ class BaseExport ValueError: public Exception { public: - /// Construction - ValueError(); - explicit ValueError(const char* sMessage); - explicit ValueError(const std::string& sMessage); - ValueError(const ValueError&) = default; - ValueError(ValueError&&) = default; - /// Destruction - ~ValueError() noexcept override = default; - ValueError& operator=(const ValueError&) = default; - ValueError& operator=(ValueError&&) = default; + explicit ValueError(const std::string& message = "Value error"); PyObject* getPyExceptionType() const override; }; -/** - * The IndexError can be used when a sequence subscript is out of range. - * @author Werner Mayer - */ +/** sequence subscript is out of range */ class BaseExport IndexError: public Exception { public: - /// Construction - IndexError(); - explicit IndexError(const char* sMessage); - explicit IndexError(const std::string& sMessage); - IndexError(const IndexError&) = default; - IndexError(IndexError&&) = default; - /// Destruction - ~IndexError() noexcept override = default; - IndexError& operator=(const IndexError&) = default; - IndexError& operator=(IndexError&&) = default; + explicit IndexError(const std::string& message = "Index error"); PyObject* getPyExceptionType() const override; }; class BaseExport NameError: public Exception { public: - /// Construction - NameError(); - explicit NameError(const char* sMessage); - explicit NameError(const std::string& sMessage); - NameError(const NameError&) = default; - NameError(NameError&&) = default; - /// Destruction - ~NameError() noexcept override = default; - NameError& operator=(const NameError&) = default; - NameError& operator=(NameError&&) = default; + explicit NameError(const std::string& message = "Name error"); PyObject* getPyExceptionType() const override; }; class BaseExport ImportError: public Exception { public: - /// Construction - ImportError(); - explicit ImportError(const char* sMessage); - explicit ImportError(const std::string& sMessage); - ImportError(const ImportError&) = default; - ImportError(ImportError&&) = default; - /// Destruction - ~ImportError() noexcept override = default; - ImportError& operator=(const ImportError&) = default; - ImportError& operator=(ImportError&&) = default; + explicit ImportError(const std::string& message = "Import error"); PyObject* getPyExceptionType() const override; }; -/** - * The AttributeError can be used to indicate the usage of a wrong value. - * @author Werner Mayer - */ class BaseExport AttributeError: public Exception { public: - /// Construction - AttributeError(); - explicit AttributeError(const char* sMessage); - explicit AttributeError(const std::string& sMessage); - AttributeError(const AttributeError&) = default; - AttributeError(AttributeError&&) = default; - /// Destruction - ~AttributeError() noexcept override = default; - AttributeError& operator=(const AttributeError&) = default; - AttributeError& operator=(AttributeError&&) = default; + explicit AttributeError(const std::string& message = "Attribute error"); PyObject* getPyExceptionType() const override; }; -/** - * The PropertyError can be used to indicate the usage of a wrong property name or value. - * @author Mario Passaglia - */ class BaseExport PropertyError: public AttributeError { public: - /// Construction - PropertyError(); - explicit PropertyError(const char* sMessage); - explicit PropertyError(const std::string& sMessage); - PropertyError(const PropertyError&) = default; - PropertyError(PropertyError&&) = default; - /// Destruction - ~PropertyError() noexcept override = default; - PropertyError& operator=(const PropertyError&) = default; - PropertyError& operator=(PropertyError&&) = default; + explicit PropertyError(const std::string& message = "Property error"); PyObject* getPyExceptionType() const override; }; -/** - * The RuntimeError can be used to indicate an unknown exception at runtime. - * @author Werner Mayer - */ class BaseExport RuntimeError: public Exception { public: - /// Construction - RuntimeError(); - explicit RuntimeError(const char* sMessage); - explicit RuntimeError(const std::string& sMessage); - RuntimeError(const RuntimeError&) = default; - RuntimeError(RuntimeError&&) = default; - /// Destruction - ~RuntimeError() noexcept override = default; - RuntimeError& operator=(const RuntimeError&) = default; - RuntimeError& operator=(RuntimeError&&) = default; + explicit RuntimeError(const std::string& message = "Runtime error"); PyObject* getPyExceptionType() const override; }; -/** - * The BadGraphError can be used to indicate that a graph is e.g. not a DAG. - * @author Werner Mayer - */ class BaseExport BadGraphError: public RuntimeError { public: - /// Construction - BadGraphError(); - explicit BadGraphError(const char* sMessage); - explicit BadGraphError(const std::string& sMessage); - BadGraphError(const BadGraphError&) = default; - BadGraphError(BadGraphError&&) = default; - /// Destruction - ~BadGraphError() noexcept override = default; - BadGraphError& operator=(const BadGraphError&) = default; - BadGraphError& operator=(BadGraphError&&) = default; + explicit BadGraphError(const std::string& message = "Bad graph error"); PyObject* getPyExceptionType() const override; }; -/** - * The NotImplementedError can be used to indicate that an invoked function is not implemented. - * @author Werner Mayer - */ class BaseExport NotImplementedError: public Exception { public: - /// Construction - NotImplementedError(); - explicit NotImplementedError(const char* sMessage); - explicit NotImplementedError(const std::string& sMessage); - NotImplementedError(const NotImplementedError&) = default; - NotImplementedError(NotImplementedError&&) = default; - /// Destruction - ~NotImplementedError() noexcept override = default; - NotImplementedError& operator=(const NotImplementedError&) = default; - NotImplementedError& operator=(NotImplementedError&&) = default; + explicit NotImplementedError(const std::string& message = "Not implemented error"); PyObject* getPyExceptionType() const override; }; -/** - * The ZeroDivisionError can be used to indicate a division by zero. - * @author Werner Mayer - */ class BaseExport ZeroDivisionError: public Exception { public: - /// Construction - ZeroDivisionError(); - explicit ZeroDivisionError(const char* sMessage); - explicit ZeroDivisionError(const std::string& sMessage); - ZeroDivisionError(const ZeroDivisionError&) = default; - ZeroDivisionError(ZeroDivisionError&&) = default; - /// Destruction - ~ZeroDivisionError() noexcept override = default; - ZeroDivisionError& operator=(const ZeroDivisionError&) = default; - ZeroDivisionError& operator=(ZeroDivisionError&&) = default; + explicit ZeroDivisionError(const std::string& message = "Zero division error"); PyObject* getPyExceptionType() const override; }; -/** - * The ReferenceError can be used to indicate a reference counter has the wrong value. - * @author Werner Mayer - */ class BaseExport ReferenceError: public Exception { public: - /// Construction - ReferenceError(); - explicit ReferenceError(const char* sMessage); - explicit ReferenceError(const std::string& sMessage); - ReferenceError(const ReferenceError&) = default; - ReferenceError(ReferenceError&&) = default; - /// Destruction - ~ReferenceError() noexcept override = default; - ReferenceError& operator=(const ReferenceError&) = default; - ReferenceError& operator=(ReferenceError&&) = default; + explicit ReferenceError(const std::string& message = "Reference error"); PyObject* getPyExceptionType() const override; }; -/** - * The ExpressionError can be used to indicate erroneous.input - * to the expression engine. - * @author Werner Mayer - */ class BaseExport ExpressionError: public Exception { public: - /// Construction - ExpressionError(); - explicit ExpressionError(const char* sMessage); - explicit ExpressionError(const std::string& sMessage); - ExpressionError(const ExpressionError&) = default; - ExpressionError(ExpressionError&&) = default; - /// Destruction - ~ExpressionError() noexcept override = default; - ExpressionError& operator=(const ExpressionError&) = default; - ExpressionError& operator=(ExpressionError&&) = default; + explicit ExpressionError(const std::string& message = "Expression error"); PyObject* getPyExceptionType() const override; }; -/** - * The ParserError can be used to indicate the parsing error. - * @author Werner Mayer - */ class BaseExport ParserError: public Exception { public: - /// Construction - ParserError(); - explicit ParserError(const char* sMessage); - explicit ParserError(const std::string& sMessage); - ParserError(const ParserError&) = default; - ParserError(ParserError&&) = default; - /// Destruction - ~ParserError() noexcept override = default; - ParserError& operator=(const ParserError&) = default; - ParserError& operator=(ParserError&&) = default; + explicit ParserError(const std::string& message = "Parser error"); PyObject* getPyExceptionType() const override; }; -/** - * The UnicodeError can be used to indicate unicode encoding/decoding error. - * @author Werner Mayer - */ class BaseExport UnicodeError: public Exception { public: - /// Construction - UnicodeError(); - explicit UnicodeError(const char* sMessage); - explicit UnicodeError(const std::string& sMessage); - UnicodeError(const UnicodeError&) = default; - UnicodeError(UnicodeError&&) = default; - /// Destruction - ~UnicodeError() noexcept override = default; - UnicodeError& operator=(const UnicodeError&) = default; - UnicodeError& operator=(UnicodeError&&) = default; + explicit UnicodeError(const std::string& message = "Unicode error"); PyObject* getPyExceptionType() const override; }; -/** - * The OverflowError can be used to indicate overflows of numbers. - * @author Werner Mayer - */ class BaseExport OverflowError: public Exception { public: - /// Construction - OverflowError(); - explicit OverflowError(const char* sMessage); - explicit OverflowError(const std::string& sMessage); - OverflowError(const OverflowError&) = default; - OverflowError(OverflowError&&) = default; - /// Destruction - ~OverflowError() noexcept override = default; - OverflowError& operator=(const OverflowError&) = default; - OverflowError& operator=(OverflowError&&) = default; + explicit OverflowError(const std::string& message = "Overflow error"); PyObject* getPyExceptionType() const override; }; -/** - * The UnderflowError can be used to indicate underflows of numbers. - * @author Werner Mayer - */ class BaseExport UnderflowError: public Exception { public: - /// Construction - UnderflowError(); - explicit UnderflowError(const char* sMessage); - explicit UnderflowError(const std::string& sMessage); - UnderflowError(const UnderflowError&) = default; - UnderflowError(UnderflowError&&) = default; - /// Destruction - ~UnderflowError() noexcept override = default; - UnderflowError& operator=(const UnderflowError&) = default; - UnderflowError& operator=(UnderflowError&&) = default; + explicit UnderflowError(const std::string& message = "Underflow error"); PyObject* getPyExceptionType() const override; }; -/** - * The UnitsMismatchError can be used to indicate that quantities with different units are used. - * @author Werner Mayer - */ class BaseExport UnitsMismatchError: public Exception { public: - /// Construction - UnitsMismatchError(); - explicit UnitsMismatchError(const char* sMessage); - explicit UnitsMismatchError(const std::string& sMessage); - UnitsMismatchError(const UnitsMismatchError&) = default; - UnitsMismatchError(UnitsMismatchError&&) = default; - /// Destruction - ~UnitsMismatchError() noexcept override = default; - UnitsMismatchError& operator=(const UnitsMismatchError&) = default; - UnitsMismatchError& operator=(UnitsMismatchError&&) = default; + explicit UnitsMismatchError(const std::string& message = "Units mismatch error"); PyObject* getPyExceptionType() const override; }; -/* The CADKernelError can be used to indicate an exception originating in the CAD Kernel - * allowing to propagate the error messages of, for example, OCC Standard_Failure exception to - * the FreeCAD application without making the FreeCAD application depend on OCC. - * @author Abdullah Tahiri - */ class BaseExport CADKernelError: public Exception { public: - /// Construction - CADKernelError(); - explicit CADKernelError(const char* sMessage); - explicit CADKernelError(const std::string& sMessage); - CADKernelError(const CADKernelError&) = default; - CADKernelError(CADKernelError&&) = default; - /// Destruction - ~CADKernelError() noexcept override = default; - CADKernelError& operator=(const CADKernelError&) = default; - CADKernelError& operator=(CADKernelError&&) = default; + explicit CADKernelError(const std::string& message = "CAD kernel error"); PyObject* getPyExceptionType() const override; }; -/* The RestoreError can be used to try to do a best recovery effort when an error during restoring - * occurs. The best recovery effort may be to ignore the element altogether or to insert a - * placeholder depending on where the actual element being restored is used. - * - * For example, if it is part of an array (e.g. PropertyList) and the order in the array is - * relevant, it is better to have a placeholder than to fail to restore the whole array. - */ class BaseExport RestoreError: public Exception { public: - /// Construction - RestoreError(); - explicit RestoreError(const char* sMessage); - explicit RestoreError(const std::string& sMessage); - RestoreError(const RestoreError&) = default; - RestoreError(RestoreError&&) = default; - /// Destruction - ~RestoreError() noexcept override = default; - RestoreError& operator=(const RestoreError&) = default; - RestoreError& operator=(RestoreError&&) = default; + explicit RestoreError(const std::string& message = "Restore error"); PyObject* getPyExceptionType() const override; }; - -inline void Exception::setMessage(const char* sMessage) +inline void Exception::setMessage(const std::string& message) { - _sErrMsg = sMessage; -} - -inline void Exception::setMessage(const std::string& sMessage) -{ - _sErrMsg = sMessage; + errorMessage = message; } inline std::string Exception::getMessage() const { - return _sErrMsg; + return errorMessage; } inline std::string Exception::getFile() const { - return _file; + return fileName; } inline int Exception::getLine() const { - return _line; + return lineNum; } inline std::string Exception::getFunction() const { - return _function; + return functionName; } inline bool Exception::getTranslatable() const { - return _isTranslatable; + return isTranslatable; } -inline void -Exception::setDebugInformation(const std::string& file, int line, const std::string& function) +inline bool Exception::getReported() const { - _file = file; - _line = line; - _function = function; + return hasBeenReported; } -inline void Exception::setTranslatable(bool translatable) +inline void Exception::setReported(const bool reported) const { - _isTranslatable = translatable; + hasBeenReported = reported; +} + +#if defined(HAVE_STD_SOURCE_LOCATION) +inline void Exception::setDebugInformation(const std::source_location& location) +{ + fileName = location.file_name(); + lineNum = static_cast(location.line()); + functionName = location.function_name(); +} +#else +inline void Exception::setDebugInformation(const char* file, int line, const char* func) +{ + fileName = file; + lineNum = line; + functionName = func; +} +#endif + +inline void Exception::setTranslatable(const bool translatable) +{ + isTranslatable = translatable; } #if defined(__GNUC__) && defined(FC_OS_LINUX) @@ -1014,11 +545,9 @@ public: private: static void throw_signal(int signum); -private: - // clang-format off - struct sigaction new_action {}, old_action {}; + struct sigaction new_action {}; // NOLINT (keep struct) + struct sigaction old_action {}; // NOLINT (keep struct) bool ok {false}; - // clang-format on }; #endif diff --git a/src/Base/Interpreter.cpp b/src/Base/Interpreter.cpp index de026dd469..7cc9765b7d 100644 --- a/src/Base/Interpreter.cpp +++ b/src/Base/Interpreter.cpp @@ -45,7 +45,7 @@ using namespace Base; PyException::PyException(const Py::Object& obj) { - _sErrMsg = obj.as_string(); + setMessage(obj.as_string()); // WARNING: we are assuming that python type object will never be // destroyed, so we don't keep reference here to save book-keeping in // our copy constructor and destructor @@ -64,7 +64,7 @@ PyException::PyException() std::string prefix = PP_last_error_type; /* exception name text */ std::string error = PP_last_error_info; /* exception data text */ - _sErrMsg = error; + setMessage(error); _errorType = prefix; // NOLINTNEXTLINE @@ -106,17 +106,17 @@ void PyException::raiseException() std::string exceptionname; if (_exceptionType == Base::PyExc_FC_FreeCADAbort) { - edict.setItem("sclassname", Py::String(typeid(Base::AbortException).name())); + edict.setItem("sclassname", Py::String(typeid(AbortException).name())); } - if (_isReported) { + if (getReported()) { edict.setItem("breported", Py::True()); } Base::ExceptionFactory::Instance().raiseException(edict.ptr()); } - if (_exceptionType == Base::PyExc_FC_FreeCADAbort) { - Base::AbortException exc(_sErrMsg.c_str()); - exc.setReported(_isReported); + if (_exceptionType == PyExc_FC_FreeCADAbort) { + AbortException exc(getMessage()); + exc.setReported(getReported()); throw exc; } @@ -125,16 +125,16 @@ void PyException::raiseException() void PyException::ReportException() const { - if (!_isReported) { - _isReported = true; + if (!getReported()) { + setReported(true); // set sys.last_vars to make post-mortem debugging work PyGILStateLocker locker; PySys_SetObject("last_traceback", PP_last_traceback); - Base::Console().DeveloperError("pyException", - "%s%s: %s\n", - _stackTrace.c_str(), - _errorType.c_str(), - what()); + Console().DeveloperError("pyException", + "%s%s: %s\n", + _stackTrace.c_str(), + _errorType.c_str(), + what()); } } @@ -187,7 +187,7 @@ SystemExitException::SystemExitException() } } - _sErrMsg = errMsg; + setMessage(errMsg); _exitCode = errCode; } @@ -656,7 +656,7 @@ std::string InterpreterSingleton::init(int argc, char* argv[]) } return getPythonPath(); } - catch (const Base::Exception& e) { + catch (const Exception& e) { e.ReportException(); throw; } diff --git a/src/Mod/Part/Gui/DlgProjectionOnSurface.cpp b/src/Mod/Part/Gui/DlgProjectionOnSurface.cpp index 7267f49703..78c627df01 100644 --- a/src/Mod/Part/Gui/DlgProjectionOnSurface.cpp +++ b/src/Mod/Part/Gui/DlgProjectionOnSurface.cpp @@ -162,13 +162,13 @@ DlgProjectionOnSurface::DlgProjectionOnSurface(QWidget* parent) m_partDocument = App::GetApplication().getActiveDocument(); if (!m_partDocument) { - throw Base::ValueError(QString(tr("Have no active document!!!")).toUtf8()); + throw Base::ValueError(tr("Have no active document!!!").toStdString()); } this->attachDocument(m_partDocument); m_partDocument->openTransaction("Project on surface"); m_projectionObject = m_partDocument->addObject("Projection Object"); if (!m_projectionObject) { - throw Base::ValueError(QString(tr("Can not create a projection object!!!")).toUtf8()); + throw Base::ValueError(tr("Can not create a projection object!!!").toStdString()); } m_projectionObject->Label.setValue(std::string(m_projectionObjectName.toUtf8()).c_str()); onRadioButtonShowAllClicked();