Chapter 1. Throwing exceptions

Index

Throwing customized exceptions
#include <x/exception.H>

try {

// ...

    if (!parse_file(filename))
    {
        throw EXCEPTION("File parsing error: " << filename);
    }

// ...

    if (rmdir("data") < 0)
    {
        throw SYSEXCEPTION("rmdir(\"data\") failed: ");
    }

// ...

} catch (const x::sysexception &e)
{
    LOG_ERROR(e);
    LOG_TRACE(e->backtrace);
} catch (const x::exception &e)
{
    std::cerr << e.what();
}

// ...

std::cerr << e << std::endl;

This library throws x::exception exceptions, but applications should use the EXCEPTION or a SYSEXCEPTION macro. These macros take an argument that can generally appear on the right hand side of a std::ostream <<. The exception text gets formatted using an output stream formatter.

x::exception can be passed to a std::ostream via the << operator. It is also a subclass of std::exception that implements what() by returning a pointer to a C string that includes both the error message text and the backtrace.

Alternatively, x::exceptions can also get also logged by the logging macros. Internally, it's a reference-counted object, with a backtrace member that describes the stack backtrace at the time the exception object was constructed (which is usually just before it gets thrown).

Note

The application must be compiled with g++'s -fno-omit-frame-pointer option, and linked with -export-dynamic. The absence of -fno-omit-frame-pointer may result in thrown exceptions causing a segfault, with some versions of g++, and without -export-dynamic backtrace() can't resolve symbols from the ELF objects that were linked directly into the executable.

SYSEXCEPTION constructs a x::sysexception, a subclass of x::exception. This macro appends the system error message text, based on the value of errno to the error message that's passed as an argument to the macro. The value of errno is also saved, and may be retrieved by the getErrorCode() method.

Throwing customized exceptions

class custom_exception {

public:
    int errcode;

    custom_exception(int n) : errcode(n)
    {
    }
};

// ...

try {

    throw CUSTOM_EXCEPTION(custom_exception, 100);
} catch (const x::custom_exception<custom_exception> &c)
{
    doSomething(c->errcode);
    // ...
}

The CUSTOM_EXCEPTION macro constructs a temporary class that multiply derives from x::custom_exception<argument>, with argument given as the first macro parameter, and x:;exception. The remaining macro parameters get forwarded to its constructor.

The temporary class is a subclass of x::exception, and it's catchable by a const x::exception &, but passing it to a std::ostream via the << operator results in a single string, the name of the custom subclass. Dereferencing x::custom_exception<argument>, produces an instance argument, instantiated using the arguments to the CUSTOM_EXCEPTION.

class custom_exception {

public:
    int errcode;

    custom_exception(int n) : errcode(n)
    {
    }

    void describe(x::exception &e)
    {
        e << "Internal error " << errcode;
    }
};

Use a describe() method, that takes a native reference to x::exception, to provide a generic error message text, that gets written to a std::ostream by this x::exception, instead of just a class name. describe() gets invoked to provide the exception's description just after its constructor gets invoked, and before the actual exception gets thrown.

class custom_exception {

public:
    int errcode;

    class base {
    public:
        static void base_function();
    };

    custom_exception(int n) : errcode(n)
    {
    }
};

typedef x::custom_exception<custom_exception> custom_thrown_exception;
// ...

try {

    throw CUSTOM_EXCEPTION(custom_exception, 100);
} catch (const custom_thrown_exception &c)
{
    custom_thrown_exception::base::base_function(c->errcode);
}

If a custom exception class defines an inner class named base, the x::custom_exception template contains a member typedef alias for this class. Although a x::custom_exception is not an x::ref (it subclasses it), this allows code to treat it similarly, with a defined base class.