Exception Handling
Contents
Exception Handling
Macros are defined for formated error priting:
-
emFF
-
emFLF
-
emFPF
-
emFLPF
-
emFLPFB
Error macros. Each of them expands to a comma-separated string literals.
emFF: file name and current function name.emFLF: file name, line number and current function name.emFPF: file name, “pretty” function name (i.e., a verbose version).emFLPF: file name, line number and ‘pretty’ function name.emFLPFB: same asemFLPF, with a line break at the end.
Example:
We detect the number of arguments passed to
main(). If it is invalid, we print some information into the streamHIPP::perrand throw an instance ofHIPP::ErrLogicwith errnoHIPP::ErrLogic::eLENGTH:#include <hippcntl.h> using namespace HIPP; int main(int argc, char const *argv[]) { if( argc != 3 ){ perr << emFLPFB, " - invalid no. of arguments. \n", " - expected 3, got ", argc, endl; throw ErrLogic(ErrLogic::eLENGTH); } return 0; }
Invoking this program with no argument, we get the message:
[ file ] e_error.cpp [ line ] 7 [ function ] int main(int, const char**) - invalid no. of arguments. - expected 3, got 1 terminate called after throwing an instance of 'HIPP::ErrLogic' what(): eLENGTH Aborted
The following variables, functions and classes are all defined within the namespace HIPP.
Overview of the Exception Layers
One difficulty in the combination of HPC libraries is how to deal with their error/exception systems. In modern C++, exceptions are handled by try-catch clauses. But in libraries writen in C, exceptions are usually hinted by the returned error code.
HIPP defines an unified model for exceptions. An exception in HIPP is represented by a C++ struct, which has an “application” and a “class”. The “application” indicates which library causes the error, and the “class” indicates the type of error.
The “application” and “class” information of an exception struct is encapsulated in two parent structs, i.e., each exception struct has a parent struct which represents its “application”, and has a parent struct which represents its “class”.
For example, the struct MPI::ErrMPI represents the exceptions thrown on calling of the
underlying MPI library. Hence, its “application” is ErrAppMPI and its “class”
is ErrClassDefault (“default” means the error happens in the underlying MPI library,
not in the high-level wrappers).
Once the exception struct is chosen, the detail reason of the exception is represented
by a member integer called error number or “errno”.
In the MPI::ErrMPI, the errno is just the retuned value of the underlying MPI library,
so that its meaning does not change when switching to the high-level wrappers.
All the exception “application” structs are derived from ErrAPP, and a member
integer (also called errno) is used to distinguish different applications. Such a design
enables the users to capture all exceptions by the common parent struct ErrApp,
and dynamically check the “application” by the errno.
For the same logic, all the exception “class” structs are derined from ErrClass, with
a member integer distinguishing different error types.
At the root, all the exception structs have a common ancestor std::exception, which
is a typical strategy in designing C++ exception layers.
The following example shows how the throw an exception and catch it.
Member function whats() is used to get the detail error information,
get_errno() in the exception struct and its two parent structs are used to
get the error numbers:
try {
throw ErrLogic(ErrLogic::eLENGTH);
}catch( const ErrLogic &e ){
/* priting the detail of error */
pout << e.whats(), endl;
/* retrieve its errno for application, errno for class, and errno */
pout << e.ErrApp::get_errno(), ", ",
e.ErrClass::get_errno(), ", ",
e.get_errno(), endl;
}
The output is
Application: default | Class: logic error | Type: eLENGTH
1, 4, 5
Here, errno 1 represents the default “application”, errno 4 represents the logic error “class”, and errno 5 represents the length error.
Applications, Classes and Exceptions
-
class ErrApp : public virtual std::exception
ErrAppis the base class of all “Error in Application” classes. Each of the subclass represents errors in a specific application, e.g., MPI. OpenMP, HDF5, etc.ErrAppuses a single member, called “errno”, typederrno_tto represent the application to which the error belongs. The subclasses have no extra member, and each of them is pinned to a fixed value of errno. Such a design enables the user tocatch all errors with
ErrApp, and dynamically check its host application by using the errno.or, catch errors in a specific application with one of the subclass of
ErrAPP.
ErrAPPcan be copy/move constructed/assigned.-
typedef std::uint16_t errno_t
Type of the errno.
-
enum ERRNOS : errno_t
-
enumerator eDEFAULT = 1
-
enumerator eUNKNOWN = 2
-
enumerator eSYSTEM = 3
-
enumerator eMPI = 4
-
enumerator eOPENMP = 5
-
enumerator eH5 = 6
-
enumerator eGSL = 7
-
enumerator ePY = 8
Predefined errnos for different applications.
-
enumerator eDEFAULT = 1
-
ErrApp(errno_t new_errno = 1) noexcept
Construct the instance by providing an errno - possibly used in the
throwstatement. The errno can be any value defined as the enumerator typedERRNOS.Usually, we do not throw
ErrApp, but we throw a subclass of it. For example, we throwErrAppMPIto hint a general error in the Message Passing Interface (which is derived fromErrApp) when the type of error does not matter. Or we throwHIPP::MPI::ErrMPIto hint the implementation-defined error in MPI. (which is derived fromErrAppMPIandErrClassDefault).
-
virtual const char *what() const noexcept override
-
virtual string whats() const
-
static size_t errmsg_maxsize() noexcept
-
static errno_t errmsg_maxno() noexcept
Retrieve the detail of the exception instance.
what()gives a short report, which is short and quick, with no obvious overhead (because the content returned bywhat()is stored statically). The maximal length of the error message returned bywhat()can be obtained byerrmsg_maxsize()(the NULL-terminate is not counted).whats()reports more details, but with larger overhead because the error message is dynamically constructed.errmsg_maxno()gives the maximal errno that can be thrown withErrApp.
-
class ErrAppDefault : public ErrApp
-
class ErrAppUnknown : public ErrApp
-
class ErrAppSystem : public ErrApp
-
class ErrAppMPI : public ErrApp
-
class ErrAppOpenMP : public ErrApp
-
class ErrAppH5 : public ErrApp
-
class ErrAppGSL : public ErrApp
-
class ErrAppPy : public ErrApp
Each of these classes defines errors in a specific application.
They all have default constructors.
-
class ErrClass : public virtual std::exception
ErrClassis the base class of all “Error Class” classes. Each of the subclass represents a type of error, e.g., runtime error, logic error, cast error, etc.ErrClassuses a single member typederrno_tto represent which type of error it is, which we called “errno” or error number. The subclasses have no extra member, and each of them is pinned to a fixed value of errno. Such a design enables the user tocatch all errors with
ErrClass, and dynamically check its type by using the errno.or, catch a specific type of error with one of its subclass.
ErrClasscan be copy/move constructed/assigned.-
typedef std::uint16_t errno_t
Type of the errno.
-
enum ERRNOS : errno_t
-
enumerator eDEFAULT = 1
-
enumerator eUNKNOWN = 2
-
enumerator eRUNTIME = 3
-
enumerator eLOGIC = 4
-
enumerator eMEMORY = 5
-
enumerator eCAST = 6
-
enumerator eIO = 7
Predefined errnos for different types of error.
-
enumerator eDEFAULT = 1
-
ErrClass(errno_t new_errno = 1) noexcept
Construct the instance by providing an errno - possibly used in the
throwstatement. The errno can be any value defined as the enumerator typedERRNOS.Usually, we do not throw
ErrClass, but we throw a subclass of it. For example, we throwErrClassLogicto hint a general logic error (which is derived fromErrLogic) when the application does not matter. Or we throwErrLogicto hint the logic error in the default application (which is derived fromErrAppDefaultandErrClassLogic).
-
virtual const char *what() const noexcept override
-
virtual string whats() const
-
static size_t errmsg_maxsize() noexcept
-
static errno_t errmsg_maxno() noexcept
Retrieve the detail of the exception instance.
what()gives a short report, which is short and quick, with no obvious overhead (because the content returned bywhat()is stored statically). The maximal length of the error message returned bywhat()can be obtained byerrmsg_maxsize()(the NULL-terminate is not counted).whats()reports more details, but with larger overhead because the error message is dynamically constructed.errmsg_maxno()gives the maximal errno that can be thrown withErrClass.
-
class ErrClassDefault : public ErrClass
-
class ErrClassUnknown : public ErrClass
-
class ErrClassRuntime : public ErrClass
-
class ErrClassLogic : public ErrClass
-
class ErrClassMemory : public ErrClass
-
class ErrClassCast : public ErrClass
-
class ErrClassIO : public ErrClass
Each of these classes defines a specific type of error.
They all have default constructors.
Examples:
We throw exceptions of different classes. They can be catched by the common parent class
ErrClass, with the errno hinting the real error class:try{ throw ErrClassIO(); }catch( const ErrClass &e ){ perr << e.whats(), '\n', "errno=", e.get_errno(), endl; } try{ throw ErrClassLogic(); }catch( const ErrClass &e ){ perr << e.whats(), '\n', "errno=", e.get_errno(), endl; }
The output is:
Class: IO error errno=7 Class: logic error errno=4
-
class ErrSystem : public ErrAppSystem, ErrClassDefault
Exception that is thrown on a failed system call.
-
ErrType(errno_t new_errno) noexcept
Constructor.
Initialize the exception instance with an errno. The errno should be a valid return value of the underlying operating system.
-
virtual const char *what() const noexcept override
-
virtual string whats() const override
-
static size_t errmsg_maxsize() noexcept
-
static errno_t errmsg_maxno() noexcept
Get the exception details.
what()gives a brief description.whats()gives a more detailed description (with larger overhead).The maximal length of the C-style string retuned by
what()can be obtained byerrmsg_maxsize()(NULL-terminate is not counted). The maximal errno can be obtained byerrmsg_maxno().
-
errno_t get_errno() const noexcept
-
void set_errno(errno_t new_errno) noexcept
Retrieve the current errno or set the errno.
-
static flag_t err_cntl_flag() noexcept
-
static void err_cntl_flag(flag_t flag) noexcept
Retrieve or set the thread-local, static error controlling flag. By default the flag is
1- error message is printed to the standard error stream on the throwing of each exception. If the flag is set to0, the exception throwing is silent.
-
template<typename ...Args>
static void check(errno_t new_errno, Args&&... args) -
template<typename ...Args>
static void throw_(errno_t new_errno, Args&&... args) -
template<typename ...Args>
static void abort(errno_t e, Args&&... args) check()checks ifnew_errnoindicates an error. If it does,throw_(new_errno, args...)is invoked.throw_()prints error messageargs...into standard error stream (if the error controlling flag is not disabled), and throws an exceptionErrSystem(new_errno).abort()always prints the error messageargs...into standard error stream, and abort the program with exit codee.
-
ErrType(errno_t new_errno) noexcept
-
class ErrRuntime : public ErrAppDefault, ErrClassRuntime
Exception that is thrown on a run-time error.
-
enum ERRNOS : errno_t
-
enumerator eDEFAULT = 1
-
enumerator eOVERFLOW = 2
-
enumerator eUNDERFLOW = 3
-
enumerator eRANGE = 4
-
enumerator eSTRCONSTRUCT = 5
Predefined errnos for different run-time errors.
-
enumerator eDEFAULT = 1
-
ErrType(errno_t new_errno) noexcept
Constructor.
Initialize the exception instance with an errno. The errno can be any value defined as the enumerator typed
ERRNOS.
-
virtual const char *what() const noexcept override
-
virtual string whats() const override
-
static size_t errmsg_maxsize() noexcept
-
static errno_t errmsg_maxno() noexcept
Get the exception details.
what()gives a brief description.whats()gives a more detailed description (with larger overhead).The maximal length of the C-style string retuned by
what()can be obtained byerrmsg_maxsize()(NULL-terminate is not counted). The maximal errno can be obtained byerrmsg_maxno().
-
errno_t get_errno() const noexcept
-
void set_errno(errno_t new_errno) noexcept
Retrieve the current errno or set the errno.
-
static flag_t err_cntl_flag() noexcept
-
static void err_cntl_flag(flag_t flag) noexcept
Retrieve or set the thread-local, static error controlling flag. By default the flag is
1- error message is printed to the standard error stream on the throwing of each exception. If the flag is set to0, the exception throwing is silent.
-
template<typename ...Args>
static void throw_(errno_t new_errno, Args&&... args) -
template<typename ...Args>
static void abort(errno_t e, Args&&... args) throw_()prints error messageargs...into standard error stream (if the error controlling flag is not disabled), and throws an exceptionErrRuntime(new_errno).abort()always prints the error messageargs...into standard error stream, and abort the program with exit codee.
-
enum ERRNOS : errno_t
-
class ErrLogic : public ErrAppDefault, ErrClassLogic
Exception that is thrown on a logic error.
-
enum ERRNOS : errno_t
-
enumerator eDEFAULT = 1
-
enumerator eDOMAIN = 2
-
enumerator eINVALIDARG = 3
-
enumerator eOUTOFRANGE = 4
-
enumerator eLENGTH = 5
-
enumerator eRECIPE_INCOMPLETE = 6
-
enumerator eRECIPE_INCONSISTENT = 7
Predefined errnos for different logic errors.
-
enumerator eDEFAULT = 1
-
ErrType(errno_t new_errno) noexcept
Constructor.
Initialize the exception instance with an errno. The errno can be any value defined as the enumerator typed
ERRNOS.
-
virtual const char *what() const noexcept override
-
virtual string whats() const override
-
static size_t errmsg_maxsize() noexcept
-
static errno_t errmsg_maxno() noexcept
Get the exception details.
what()gives a brief description.whats()gives a more detailed description (with larger overhead).The maximal length of the C-style string retuned by
what()can be obtained byerrmsg_maxsize()(NULL-terminate is not counted). The maximal errno can be obtained byerrmsg_maxno().
-
errno_t get_errno() const noexcept
-
void set_errno(errno_t new_errno) noexcept
Retrieve the current errno or set the errno.
-
static flag_t err_cntl_flag() noexcept
-
static void err_cntl_flag(flag_t flag) noexcept
Retrieve or set the thread-local, static error controlling flag. By default the flag is
1- error message is printed to the standard error stream on the throwing of each exception. If the flag is set to0, the exception throwing is silent.
-
template<typename ...Args>
static void throw_(errno_t new_errno, Args&&... args) -
template<typename ...Args>
static void abort(errno_t e, Args&&... args) throw_()prints error messageargs...into standard error stream (if the error controlling flag is not disabled), and throws an exceptionErrLogic(new_errno).abort()always prints the error messageargs...into standard error stream, and abort the program with exit codee.
-
enum ERRNOS : errno_t