/** * \file exception.h \brief Exception definitions and assertions. * * Copyright 2007-2011 IMP Inventors. All rights reserved. * */ #ifndef RMF_EXCEPTION_H #define RMF_EXCEPTION_H #include "rmf_config.h" #include #include #include #include #include #include #include namespace rmf { /** \name Error checking and reporting \anchor assert @{ */ //! The general base class for \imp exceptions /** Exceptions should be used to report all errors that occur within \imp. */ class RMFEXPORT Exception { struct refstring { char message_[4096]; int ct_; }; refstring *str_; public: const char *what() const throw() { return str_? str_->message_: NULL; } Exception(const char *message); /* \note By making the destructor virtual and providing an implementation in each derived class, we force a strong definition of the exception object in the kernel DSO. This allows exceptions to be passed between DSOs. */ virtual ~Exception() throw(); Exception(const Exception &o) {copy(o);} Exception &operator=(const Exception &o) { destroy(); copy(o); return *this; } private: void destroy() { if (str_ != NULL) { --str_->ct_; if (str_->ct_==0) delete str_; } } void copy(const Exception &o) { str_=o.str_; if (str_!= NULL) ++str_->ct_; } }; #ifdef IMP_DOXYGEN //! Execute the code block if a certain level checks are on /** The next code block (delimited by { }) is executed if get_check_level() <= level. For example: \code RMF_CHECK_CODE(CHECK_USAGE) { std::vector testp(input.begin(), input.end()); std::sort(testp.begin(), testp.end()); RMF_USAGE_CHECK(std::unique(testp.begin(), testp.end()) == testp.end(), "Duplicate particles found in the input list."); } \endcode */ #define RMF_IF_CHECK(level) //! Only compile the code if checks are enabled /** For example \code RMF_CHECK_CODE({ std::vector testp(input.begin(), input.end()); std::sort(testp.begin(), testp.end()); RMF_USAGE_CHECK(std::unique(testp.begin(), testp.end()) == testp.end(), "Duplicate particles found in the input list."); }); \endcode **/ #define RMF_CHECK_CODE(expr) //! A runtime test for incorrect usage of a class or method. /** \param[in] expr The assertion expression. \param[in] message Write this message if the assertion fails. It should be used to check arguments to function. For example \code RMF_USAGE_CHECK(positive_argument >0, "Argument positive_argument to function my_function " << " must be positive. Instead got " << positive_argument); \endcode */ #define RMF_USAGE_CHECK(expr, message) //! Throw an exception with a message /** The exception thrown must inherit from Exception and not be UsageException or InternalException as those are reserved for disableable checks (the RMF_INTERNAL_CHECK() and RMF_USAGE_CHECK() macros). \code RMF_THROW("Could not open file " << file_name, IOException); \endcode */ #define RMF_THROW(message, exception_name) #else // IMP_DOXYGEN #define RMF_THROW(message, exception_name)do { \ std::ostringstream oss; \ oss << message << std::endl; \ BOOST_STATIC_ASSERT((!(boost::is_base_of< ::rmf::UsageException, \ exception_name>::value) \ && (boost::is_base_of< ::rmf::Exception, \ exception_name>::value))); \ throw exception_name(oss.str().c_str()); \ } while (true) #define RMF_IF_CHECK \ if (true) #define RMF_CHECK_CODE(expr) expr #define RMF_USAGE_CHECK(expr, message) \ do { \ if (!(expr)) { \ std::ostringstream oss; \ oss << "Usage check failure: " << message \ << std::endl; \ throw rmf::UsageException(oss.str().c_str()); \ } \ } while (false) #endif // IMP_DOXYGEN /** @} */ //! An exception for an invalid usage of \imp /** It is thrown by the RMF_USAGE_CHECK() macro. It should never be caught internally to \imp, but it one may be able to recover from it being thrown. \advanceddoc As the usage checks are disabled in fast mode, UsageExceptions are not considered part of the API and hence should not be documented or checked in test cases. */ class RMFEXPORT UsageException : public Exception { public: UsageException(const char *t): Exception(t){} ~UsageException() throw(); }; //! An exception for an invalid value being passed to \imp /** The equivalent Python type also derives from Python's ValueError. */ class RMFEXPORT ValueException : public Exception { public: ValueException(const char *t): Exception(t){} ~ValueException() throw(); }; //! An exception for a request for an invalid member of a container /** The equivalent Python type also derives from Python's IndexError. */ class RMFEXPORT IndexException: public Exception { public: //! Create exception with an error message IndexException(const char *t): Exception(t){} ~IndexException() throw(); }; //! An input/output exception /** This exception should be used when an IO operation fails in a way that leaves the internal state OK. For example, failure to open a file should result in an IOException. It is OK to catch such exceptions in \imp. The equivalent Python type also derives from Python's IOError. */ class RMFEXPORT IOException: public Exception { public: IOException(const char *t): Exception(t){} ~IOException() throw(); }; } #endif /* RMF_EXCEPTION_H */