Extensible Exception Library (EEL) is a simple framework for C++11 exception handling.
The concepts behind are the following:
Each not final class is virtually derived from an already existing standard one, preserving its semantic and sytnax compatibility.
The semantic of the exception is similar to the standard homologous ones:
Exceptions handling in C++ is a mechanism able to move - when special conditions modify the normal flow of a program - information from the place in which the exception is thrown to the block of code in which the program is resumed. In the standard exception implementation:
These produce the following situations:
The eel::Exception payload implemented is a collection of named attributes so basically:
In the standard library the textual representation of the exception is based on the what() method, usually reporting a free string passed through constructor, so statically defined inside the code throwing the exception. Different instances of the same exception class produce completely different messages, loosing efficacy when dumped on a log. On EEL every class defines a set of IDs actually indexing the string reported by the what() method to a fixed template string, that a 'lazy' what() implementation actually populates with the named attributes present in the payload.
An instance of eel::RuntimeError, thrown with the eel::RuntimeError::Id::RTMERR001 will use the corresponding template string:
RTMERR001 RuntimeError error: ${RTMERR_NUM} - ${RTMERR_DESC}.
Assuming - at throw-time - a value of for errno equal to ERANGE, the constructor will add the corresponding attributes so that a later invocation of the what() method inside a catch block will replace every ${ATTRIBUTE NAME} in the template with the corresponding attribute value:
RTMERR001 RuntimeError error: 34 - Numerical result out of range.
The IDs add coherence and standard notation to displayed message delaying the composition at what() invocation time (so after stack unwinding has occurred, and presumably, released some resources) and reducing the risk of unwanted nested exceptions. Note also that the corresponding replace phase inside eel::Exception::what() is protected with a catch block.
As already mentioned, every catch block can add whatever attributes of whatever type. This can be used to add any diagnostic information in whatever point of the program (source line, file name and so on). The what() method will use only the attributes referred inside the corresponding template, not impacting on the 'canonical' representation of the exception itself. In order to have a dump of all the contained information of the Exception payload, a catch block can eventually use the eel::Exception::dump() method.
The following example shows briefly the forementioned features:
#include <dlfcn.h> #include <iostream> #include <eel/eel.h> using namespace std; using namespace eel; void inner_code(const char *soname,int soflags) { void *p=dlopen(soname,soflags); if (!p) { // throw a DlError instance initializing its attributes with the macros supplied // EELA_API --> eel::attribute::api(__PRETTY_FUNCTION__) throw DlError(EELA_API); } else { dlclose(p); // throw a RuntimeError instance initializing its attributes with the macros supplied // EELA_API --> eel::attribute::api(__PRETTY_FUNCTION__) throw RuntimeError(EELA_API); } } void outer_code() { const char *name="/path/to/impossible.so"; int flags=RTLD_LAZY; try { inner_code(name,flags); } catch (eel::Exception &error) { // in case of error let's add other attributes // EELA_FILE --> eel::attribute::file(__FILE__) // "SONAME" --> custom attribute of type std::string // "SOFLAGS" --> custom attribute of type int throw error << EELA_FILE << eel::make_attribute("SONAME",name) << eel::make_attribute("SOFLAGS",flags); } } int main(int argc,const char *argv[]) { try { outer_code(); } catch (const std::exception &error) { // let's trap standard exception as usual cerr << "*** ERROR ***\n" << error.what() << endl; if (argc>1 && strcmp(argv[1],"--debug")==0) { if (const eel::Exception *eerror=dynamic_cast(&error)) { // use the dump() method for printing the whole exception payload cerr << "\n*** DIAGNOSTIC ***\n" << eerror->dump() << endl; } } } }
The produced output will be:
*** ERROR ***
DLLERR000 /path/to/impossible.so: cannot open shared object file: No such file or directory.
if the example is invoked with the --debug parameter we'll have also:
*** DIAGNOSTIC ***
[EXCEPT_API ] std::string 'void inner_code(const char*, int)'
[EXCEPT_FILE ] std::string '/home/giampa/projects/kdevelop/eel/examples/simple.cpp'
[RTMERR_DESC ] std::string '/path/to/impossible.so: cannot open shared object file: No such file or directory'
[SOFLAGS ] int '1'
[SONAME ] std::string '/path/to/impossible.so'