Extensible Exception Library

Categoria: Projects
Pubblicato Sabato, 15 Febbraio 2014 11:34
Scritto da Giampiero Gabbiani
Visite: 3212

Extensible Exception Library (EEL) is a simple framework for C++11 exception handling.

The concepts behind are the following:

Features:

Class hierarchy

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:

Exception's payload

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:

  1. the payload is statically defined by the exception class definition;
  2. there is no uniform interface able to enrich the payload independently from the type of the exception;

These produce the following situations:

The eel::Exception payload implemented is a collection of named attributes so basically:

Exception IDs

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.

 

Diagnostic

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.

Example code

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'

 

References

Sourceforge download page

Error and Exception Handling

Boost Exception

System error support in C++0x