/*! \file qpms_error.h * * \brief QPMS miscellanous internal error handling functions and macros. * */ #ifndef QPMS_ERROR_H #define QPMS_ERROR_H #include "optim.h" /// Print error message and abort(); QPMS_NORETURN void qpms_pr_error(const char *fmt, ...); //void qpms_error(const char *fmt, ...); /// Print an error message, indicating source, function name and line number, and abort(). /** * Usually not used directly, but rather via some of the macros * that fill the first arguments automatically. * * \see QPMS_PR_ERROR */ QPMS_NORETURN void qpms_pr_error_at_flf(const char *filename, unsigned int linenum, const char *func, const char *fmt, ...); /// Print a warning message to stderr and flush the buffer. Don't call this directly, use QPMS_WARN(). void qpms_warn_at_flf(const char *filename, unsigned int linenum, const char *func, const char *fmt, ...); /// Classification of debugging messages. /** * \see qpms_dbgmsg_enabled, qpms_dbgmsg_enable(), qpms_dbgmsg_disable() */ typedef enum { QPMS_DBGMSG_MISC = 1, QPMS_DBGMSG_THREADS = 2, ///< Multithreading-related debug messages. QPMS_DBGMSG_INTEGRATION = 4 ///< Quadrature-related debug messages. } qpms_dbgmsg_flags; /// Print a debugging message to stderr and flush the buffer. Don't call this directly, use QPMS_DEBUG(). void qpms_debug_at_flf(const char *filename, unsigned int linenum, const char *func, qpms_dbgmsg_flags type, const char *fmt, ...); /// Global variable determining which types of debug messages shall be printed with QPMS_DEBUG(). /** * Use qpms_dbgmsg_enable() and qpms_dbgmsg_disable() to manipulate * this variable. * * \see QPMS_DEBUG() */ extern qpms_dbgmsg_flags qpms_dbgmsg_enabled; /// Enable debugging messages of given \a types. /** \see qpms_dbgmsg_disable() */ qpms_dbgmsg_flags qpms_dbgmsg_disable(qpms_dbgmsg_flags types); /// Disable debugging messages of given \a types. /** \see qpms_dbgmsg_enable() */ qpms_dbgmsg_flags qpms_dbgmsg_enable(qpms_dbgmsg_flags types); /// Print a warning to stderr and flush the buffer. /** * The arguments are the same as in standard printf(). */ #define QPMS_WARN(msg, ...) qpms_warn_at_flf(__FILE__,__LINE__,__func__,msg, ##__VA_ARGS__) /// Print a warning to stderr and flush the buffer – only once per run (or current thread). /** * The arguments are the same as in standard printf(). */ #define QPMS_WARN_ONCE(msg, ...) {\ static _Bool already_bitched = 0; \ if (QPMS_UNLIKELY(!already_bitched)) {\ qpms_warn_at_flf(__FILE__,__LINE__,__func__,msg, ##__VA_ARGS__);\ already_bitched = 1;\ }\ } /// Print a debugging message to stderr and flush the buffer. /** * The arguments after \a type are the same as in standard printf(). * * The debugging message is printed only if the corresponding \a type flag * is set in qpms_dbgmsg_enabled. * * \see qpms_dbgmsg_enabled */ #define QPMS_DEBUG(type /**< Debugging message type flag, see qpms_dbgmsg_flags */ , msg, ...) qpms_debug_at_flf(__FILE__,__LINE__,__func__,type,msg, ##__VA_ARGS__) /// Wrapper macro of standard malloc(), crashing on failure. /** * Normally corresponds to a `pointer = malloc(size)` * statement; however, if NULL is returned, this prints an * error message and abort()s the program. * * The arguments are expanded several times. * * Note that this macro expands to a code block, to be kept in mind when using * together with if/else etc. * * Assigned memory block is to be deallocated with standard free(). * * \see QPMS_CRASHING_CALLOC. */ #define QPMS_CRASHING_MALLOC(pointer, size) {\ (pointer) = malloc(size);\ if(QPMS_UNLIKELY(!(pointer) && (size)))\ qpms_pr_error_at_flf(__FILE__,__LINE__,__func__,\ "Allocation of %zd bytes for " #pointer " failed.",\ (size_t) (size));\ } /// Allocate and copy. /** * Behaves as QPMS_CRASHING_MALLOC(dest, size), but * additionaly copies a chunk of memory from \a src to \a dest. * * \see QPMS_CRASHING_MALLOC() */ #define QPMS_CRASHING_MALLOCPY(dest, src, size) {\ (dest) = malloc(size);\ if(QPMS_UNLIKELY(!(dest) && (size)))\ qpms_pr_error_at_flf(__FILE__,__LINE__,__func__,\ "Allocation of %zd bytes for " #dest " failed.",\ (size_t) (size));\ memcpy((dest), (src), (size));\ } /// Wrapper macro of standard calloc(), crashing on failure. /** * Normally corresponds to a `pointer = calloc(nmemb, size)` * statement; however, if NULL is returned, this prints an * error message and abort()s the program. * * The arguments are expanded several times. * * Note that this macro expands to a code block, to be kept in mind when using * together with if/else etc. * * Assigned memory block is to be deallocated with standard free(). * * \see QPMS_CRASHING_MALLOC, QPMS_CRASHING_REALLOC. */ #define QPMS_CRASHING_CALLOC(pointer, nmemb, size) {\ (pointer) = calloc((nmemb), (size));\ if(QPMS_UNLIKELY(!(pointer) && (nmemb) && (size)))\ qpms_pr_error_at_flf(__FILE__,__LINE__,__func__,\ "Allocation of %zd bytes for " #pointer " failed.",\ (size_t)((nmemb)*(size)));\ } /// Wrapper macro of standard realloc(), crashing on failure. /** * Normally corresponds to a `pointer = realloc(pointer, size)` * statement; however, if NULL is returned, this prints an * error message and abort()s the program. * * The arguments are expanded several times. * * Note that this macro expands to a code block, to be kept in mind when using * together with if/else etc. * * Assigned memory block is to be deallocated with standard free(). * * \see QPMS_CRASHING_MALLOC. */ #define QPMS_CRASHING_REALLOC(pointer, size) {\ (pointer) = realloc((pointer), size);\ if(QPMS_UNLIKELY(!(pointer) && (size)))\ qpms_pr_error_at_flf(__FILE__,__LINE__,__func__,\ "Rellocation of %zd bytes for " #pointer " failed.",\ (size_t) (size));\ } /// Prints an "unexpected error" message and aborts the program. /** Usually only put to presumably unreachable places in the code and similar. */ #define QPMS_WTF qpms_pr_error_at_flf(__FILE__,__LINE__,__func__,"Unexpected error.") /// Aborts the program with "invalid enumerator" error message. #define QPMS_INVALID_ENUM(x) qpms_pr_error_at_flf(__FILE__,__LINE__,__func__,"Invalid enum value (" #x " == %d)", (int) (x)) /// Prints an "untested function/feature" warning once when reached in the code. #define QPMS_UNTESTED {\ static _Bool already_bitched = 0; \ if (QPMS_UNLIKELY(!already_bitched)) {\ qpms_warn_at_flf(__FILE__,__LINE__,__func__,"Warning: untested function/feature!");\ already_bitched = 1;\ }\ } /// Prints a given error message and aborts the program. /** The arguments are as in standard printf(). */ #define QPMS_PR_ERROR(msg, ...) qpms_pr_error_at_flf(__FILE__,__LINE__,__func__,msg, ##__VA_ARGS__) /// Raises an error if \a x is not zero. #define QPMS_ENSURE_SUCCESS(x) { \ int errorcode = (x); /* evaluate x only once */ \ if(QPMS_UNLIKELY(errorcode)) \ qpms_pr_error_at_flf(__FILE__,__LINE__,__func__,"Unexpected error (%d)", errorcode); \ } /// Raises an error if \a x is not zero, with custom error message. #define QPMS_ENSURE_SUCCESS_M(x, msg, ...) { \ int errorcode = (x); \ if(QPMS_UNLIKELY(errorcode)) \ qpms_pr_error_at_flf(__FILE__,__LINE__,__func__,msg, ##__VA_ARGS__); \ } /// Raises an error if \a x is not zero or one of the values listed in arguments. #define QPMS_ENSURE_SUCCESS_OR(x, ...) { \ int errorcode = (x); /* evaluate x only once */ \ static const int allowed_errorcodes[] = {0, ##__VA_ARGS__};\ static const int n_allowed = sizeof(allowed_errorcodes) / sizeof(int); \ int i; \ for(i = 0; i < n_allowed; ++i) \ if (errorcode == allowed_errorcodes[i]) break; \ if (QPMS_UNLIKELY(i >= n_allowed)) \ qpms_pr_error_at_flf(__FILE__,__LINE__,__func__,"Unexpected error (%d)", errorcode); \ } /// Raises an error if \a x is not true, with custom error message. #define QPMS_ENSURE(x, msg, ...) {if(QPMS_UNLIKELY(!(x))) qpms_pr_error_at_flf(__FILE__,__LINE__,__func__,msg, ##__VA_ARGS__); } /// Raises an error if \a x is false. /** * Currently, this is always expanded, ignoring the possible NDEBUG macro. * In places where the evaluation could have significant performance impact, * consider using QPMS_PARANOID_ASSERT() instead. */ #define QPMS_ASSERT(x) {\ if(QPMS_UNLIKELY(!(x)))\ qpms_pr_error_at_flf(__FILE__,__LINE__,__func__,\ "Unexpected error. This is most certainly a bug.");\ } #ifdef QPMS_EVALUATE_PARANOID_ASSERTS /** \brief Raises an error if \a x is false. * * Expanded only if QPMS_EVALUATE_PARANOID_ASSERTS macro is defined. */ #define QPMS_PARANOID_ASSERT(x) {\ if(QPMS_UNLIKELY(!(x)))\ qpms_pr_error_at_flf(__FILE__,__LINE__,__func__,\ "Unexpected error. This is most certainly a bug.");\ } #else #define QPMS_PARANOID_ASSERT(x) {;} #endif /// Raises a "not implemented" error with additional custom message. /** Serves also as a label/placeholder of not implemented parts of the code. */ #define QPMS_NOT_IMPLEMENTED(msg, ...) qpms_pr_error_at_flf(__FILE__,__LINE__,__func__, \ "Not implemented:" msg, ##__VA_ARGS__) /// Prints an "incomplete implementation" warning once with a custom message. /** Serves mainly as a label/placeholder of incomplete parts of the code. */ #define QPMS_INCOMPLETE_IMPLEMENTATION(msg, ...) {\ static _Bool already_bitched = 0; \ if (QPMS_UNLIKELY(!already_bitched)) {\ qpms_warn_at_flf(__FILE__,__LINE__,__func__,msg, ##__VA_ARGS__);\ already_bitched = 1;\ }\ } #endif