/** \file \brief Defines the Exception base class. \who \kpr */ #ifndef MUTILS_EXCEPTION_H #define MUTILS_EXCEPTION_H #include #include "mutils/Log.h" /// Throws an exception using uniform messaging. /// \param[in] ex Exception object (must be lvalue). /// \param[in] src Error source (function or method). /// \param[in] msg Error message format string. /// \param[in] ... Optional arguments to satisfy sprintf(\a msg, ...) call. /// \note If mutils::Exception::errors is set the exception is not thrown, only /// an error message is printed. /// \warning The exception \a ex must be or derive from mutils::Exception. /// This \b must be defined as a macro to get proper line information. #define MU_THROW(ex, src, msg, ...) do { \ char _mu_buf[4096]; \ bool db = (mutils::Log::level >= mutils::Log::DEBUG); \ mutils::cxxsnprintf(_mu_buf, sizeof(_mu_buf), "%s:%0*d: " msg "\n", \ (db ? (src) : ""), (db ? 1 : 4), __LINE__, ## __VA_ARGS__); \ if (mutils::Exception::errors) { \ mutils::Log::error(_mu_buf); \ } else { \ (ex).message(_mu_buf); \ throw (ex); \ } \ } while (0) /// Rethrows an exception with a new message (after logging the original one). /// \param[in] ex Exception object (must be lvalue). /// \param[in] src Error source (function or method). /// \param[in] msg Error message format string. /// \param[in] ... Optional arguments to satisfy sprintf(\a msg, ...) call. /// \warning The exception \a ex must be or derive from mutils::Exception. /// This \b must be defined as a macro to get proper line information. #define MU_RETHROW(ex, src, msg, ...) do { \ (ex).print(); MU_THROW(ex, src, msg, ## __VA_ARGS__); \ } while (0) /// Conditionally throws an exception. /// \param[in] cond Failure test condition. /// \param[in] ex Exception object (must be lvalue). /// \param[in] src Error source (function or method). /// \param[in] msg Error message format string. /// \param[in] ... Optional arguments to satisfy sprintf(\a msg, ...) call. /// \warning The exception \a ex must be or derive from mutils::Exception. /// This \b must be defined as a macro to get proper line information. #define MU_THROW_IF(cond, ex, src, msg, ...) do { \ if (cond) { MU_THROW(ex, src, msg, ## __VA_ARGS__); } \ } while (0) /// Throws an exception using uniform messaging (including \c errno text). /// \param[in] ex Exception object (must be lvalue). /// \param[in] src Error source (function or method). /// \param[in] msg Error message format string. /// \param[in] ... Optional arguments to satisfy sprintf(\a msg, ...) call. /// \warning The exception \a ex must be or derive from mutils::Exception. /// This \b must be defined as a macro to get proper line information. /// This macro is not thread-safe. #define MU_THROW_ERRNO(ex, src, msg, ...) \ MU_THROW(ex, src, msg ": %s", ## __VA_ARGS__, strerror(errno)) /// Conditionally throws an exception (including \c errno text). /// \param[in] cond Failure test condition. /// \param[in] ex Exception object (must be lvalue). /// \param[in] src Error source (function or method). /// \param[in] msg Error message format string. /// \param[in] ... Optional arguments to satisfy sprintf(\a msg, ...) call. /// \warning The exception \a ex must be or derive from mutils::Exception. /// This \b must be defined as a macro to get proper line information. /// This macro is not thread-safe. #define MU_THROW_ERRNO_IF(cond, ex, src, msg, ...) \ MU_THROW_IF(cond, ex, src, msg ": %s", ## __VA_ARGS__, strerror(errno)) /// Throws mutils::Exception using uniform messaging. /// \param[in] src Error source (function or method). /// \param[in] msg Error message format string. /// \param[in] ... Optional arguments to satisfy sprintf(\a msg, ...) call. /// \warning This \b must be defined as a macro to get proper line information. #define MU_EXCEPTION(src, msg, ...) do { \ mutils::Exception _mu_ex; \ MU_THROW(_mu_ex, src, msg, ## __VA_ARGS__); \ } while (0) /// Conditionally throws mutils::Exception. /// \param[in] cond Failure test condition. /// \param[in] src Error source (function or method). /// \param[in] msg Error message format string. /// \param[in] ... Optional arguments to satisfy sprintf(\a msg, ...) call. /// \warning This \b must be defined as a macro to get proper line information. #define MU_EXCEPTION_IF(cond, src, msg, ...) do { \ mutils::Exception _mu_ex; \ MU_THROW_IF(cond, _mu_ex, src, msg, ## __VA_ARGS__); \ } while (0) /// Throws mutils::Exception using uniform messaging (including \c errno text). /// \param[in] src Error source (function or method). /// \param[in] msg Error message format string. /// \param[in] ... Optional arguments to satisfy sprintf(\a msg, ...) call. /// \warning This \b must be defined as a macro to get proper line information. /// This macro is not thread-safe. #define MU_EXCEPTION_ERRNO(src, msg, ...) do { \ mutils::Exception _mu_ex; \ MU_THROW_ERRNO(_mu_ex, src, msg, ## __VA_ARGS__); \ } while (0) /// Conditionally throws mutils::Exception (including \c errno text). /// \param[in] cond Failure test condition. /// \param[in] src Error source (function or method). /// \param[in] msg Error message format string. /// \param[in] ... Optional arguments to satisfy sprintf(\a msg, ...) call. /// \warning This \b must be defined as a macro to get proper line information. /// This macro is not thread-safe. #define MU_EXCEPTION_ERRNO_IF(cond, src, msg, ...) do { \ mutils::Exception _mu_ex; \ MU_THROW_ERRNO_IF(cond, _mu_ex, src, msg, ## __VA_ARGS__); \ } while (0) namespace mutils { /** \headerfile Exception.h \brief \mu exception (base) class. This class serves as the base for all \mu exceptions. In practice, \mu code uses this class to propagate error messages for *fatal* errors. Non-fatal errors should use either a derived sub-class or one of the existing C++ exception types, *not* this class; alternatively, MU_ERROR_RTN() can be used instead of an exception if a chain of return status values is present. \who \kpr */ class Exception: public std::exception { public: /// Default constructor. /// \param[in] level Log level. Exception(int level = Log::ERROR) : level(level) { message("Default exception"); } /// Destructor. virtual ~Exception() noexcept { } /// Assigns error message. /// \param[in] msg Error message. void message(const char *msg) { msg_ = Log::name(level); msg_ += ": "; msg_ += msg; } /// Prints error message to log. void print() const { if (level <= Log::level) { fputs(what(), Log::stream); } fflush(Log::stream); } /// Returns error string. virtual const char *what() const noexcept { return msg_.c_str(); } /// Condition log level. int level; /// Whether to print error messages (else throw exceptions). /// \details /// Enabling this results in error messages instead of exceptions /// being thrown, allowing execution to continue (to the point of /// catastrophic failure). For diagnostic use only. static bool errors; protected: /// Error message. std::string msg_; }; /// Prints exception message. /// \details /// If \a ex is an mutils::Exception, calls mutils::Exception::print(); /// otherwise, prints ex.what() (to stderr). /// \param[in] ex Input exception. inline void printException(std::exception &ex) { auto mex = dynamic_cast(&ex); if (mex) { mex->print(); } else { fprintf(stderr, "%s\n", ex.what()); } } /// Exits program with exception message. /// Exits the program (with \a status) after /// displaying the exception \a ex. /// \note This method does not return. /// \param[in] ex Input exception. /// \param[in] status Exit status. /// \see printException() inline void exitException(std::exception &ex, int status = EXIT_FAILURE) { printException(ex); fprintf(stderr, "Exception caught (Log::file = %s); exiting...\n", Log::file.c_str()); exit(status); } } // namespace mutils #endif // MUTILS_EXCEPTION_H