cgul_exception.h File Reference

cgul c-style exception More...

#include "cgul_common.h"
Include dependency graph for cgul_exception.h:
This graph shows which files directly or indirectly include this file:

Typedefs

typedef typedefCGUL_BEGIN_C struct cgul_exception * cgul_exception_t
 

Functions

CGUL_EXPORT cgul_exception_t cgul_exception__get_out_of_memory_exception (void)
 
CGUL_EXPORT cgul_exception_t cgul_exception__new (const char *format,...)
 
CGUL_EXPORT cgul_exception_t cgul_exception__new_from_va_list (const char *format, va_list args)
 
CGUL_EXPORT void cgul_exception__delete (cgul_exception_t cex)
 
CGUL_EXPORT const char * cgul_exception__get_message (cgul_exception_t cex)
 

Detailed Description

Basics:

This class provides a generic object that you can use to unwind the stack similar to throwing an exception in C++.

The basic idea is for the main program to initialize a cgul_exception_t variable to NULL. You then pass in a pointer to the cgul_exception_t object to every function you write. If you get an error that you want to throw, you allocate a cgul_exception_t object using cgul_exception__new() and return to the caller. Very simple; except ... for this to work, every function you write must first check to see if the cgul_exception_t object it is passed is not NULL before it begins doing its work.

If the cgul_exception_t is not NULL, it means an error has occurred, and the stack is unwinding. Thus, your function must return immediately without doing any work in order to allow the stack to unwind. Lastly, unlike with Java, there is no garbage collection; so, the code that catches the exception needs to free the memory associated with the exception by calling cgul_exception__delete().

It should also be noted that the same issues regarding throwing exceptions in your destructors that pertain to C++ also pertain to these exceptions. Specifically, don't do it, but it is not the end of the world if you do. It only means that the exception you throw from your destructor will mask the original exception. Typically, there is no reason to even pass in the thread's cgul_exception_t pointer to your destructors.

It should be noted that cgul_exception__new() is unlike the other constructors in this library in that it does not participate in the C-style exception mechanism. You can clearly see this because it does not accept a cgul_exception_t pointer as its first argument. The reason for this is that unwinding the stack has to start somewhere, and, in this scheme, it starts by assigning the value returned by cgul_exception__new().

When you are reading the doxygen comments for other classes that use this class, a function with param[in] cex only tests the exception and returns if the stack is unwinding. A function with param[in,out] cex both tests the exception and, if an error occurs, throws an exception by allocating a new cgul_exception object.

Usage Patterns:

A typical main() will look something like the following:

     int main(int argc, char* argv[])
     {
         int rv = 0;
         // When an exception is thrown, the lower-level function will
         // create a new cgul_exception class and set "local" to point
         // to it.
         cgul_exception_t local = NULL;
         // Because each function returns immediately if "local" is set,
         // we can write the functional part of the code clean of most
         // (if not all) in-line error checking.
         func1(&local, ...);
         func2(&local, ...);
         func3(&local, ...);
         func4(&local, ...);
         // After the stack unwinds, we check for errors.
         if (local) {
             const char* errmsg = cgul_exception__get_message(local);
             fprintf(stderr, "*** %s: Error: %s\n", progname, errmsg);
             cgul_exception__delete(local);
             local = NULL;
             rv = 1;
         }
         return rv;
     }

A typical constructor that throws only one or two exceptions will look like the following:

    my_obj_t
    my_obj_new(cgul_exception_t* cex)
    {
        my_obj_t rv = NULL;
        assert(cex);
        if (*cex) {
            // An exception has already been thrown.  We need to allow the
            // stack to unwind by not doing anything substantive.
            goto out;
        }
        rv = calloc(1, sizeof(*rv));
        if (rv == NULL) {
            // Notice that these lower-level functions still have to check
            // for errors and throw exceptions.
            *cex = cgul_exception__get_out_of_memory_exception();
            goto out;
        }
        // Initialize your object here.
     out:
        if (*cex && rv) {
            // Clean up a partially instantiated object.  Notice that your
            // destructors (here my_obj_delete()) are not passed the "cex"
            // pointer.  The reason for this is the same reason why C++
            // destructors should not throw exceptions.  Your destructors
            // always need to be able to clean up partially instantiated
            // objects which means they need to be callable from a context
            // in which an exception has already been raised and must not
            // themselves throw an exception.
            my_obj_delete(rv);
            rv = NULL;
        }
        return rv;
    }
Author
Paul Serice

Typedef Documentation

§ cgul_exception_t

typedef typedefCGUL_BEGIN_C struct cgul_exception* cgul_exception_t

Opaque pointer to a cgul_exception instance.

Function Documentation

§ cgul_exception__get_out_of_memory_exception()

CGUL_EXPORT cgul_exception_t cgul_exception__get_out_of_memory_exception ( void  )

A statically allocated cgul_exception_t object exists to handle the special case of running out of memory because once you run out of memory you will obviously have problems allocating a cgul_exception object at that point. If your code runs out of memory, this is the exception that it should throw.

It is perfectly legal for the code that catches the exception to call cgul_exception__delete() on the object returned despite the object being statically allocated. This works because the cgul_exception class knows not to actually call free() on this object.

Returns
out of memory exception

§ cgul_exception__new()

CGUL_EXPORT cgul_exception_t cgul_exception__new ( const char *  format,
  ... 
)

Create a new cgul_exception object using the printf()-style format string format. This method is a very thin wrapper around cgul_exception__new_from_va_list().

In the special case where memory cannot be allocated for a new cgul_exception instance, cgul_exception__get_out_of_memory_exception() will be returned in place of the exception that would otherwise be returned.

Parameters
[in]formatprintf()-style format string
[in]...variable number of arguments that satisfy the format string
Returns
new cgul_exception object

Referenced by cgul_exception_cxx::operator=(), cgul_wstring_cxx::operator[](), cgul_string_cxx::operator[](), and cgul_hstring_cxx::operator[]().

§ cgul_exception__new_from_va_list()

CGUL_EXPORT cgul_exception_t cgul_exception__new_from_va_list ( const char *  format,
va_list  args 
)

Create a new cgul_exception object using the printf()-style format string format and the variable argument list args.

In the special case where memory cannot be allocated for a new cgul_exception instance, cgul_exception__get_out_of_memory_exception() will be returned in place of the exception that would otherwise be returned.

Parameters
[in]formatprintf()-style format string
argsvariable number of arguments that satisfy format
Returns
new cgul_exception object

Referenced by cgul_exception_cxx::cgul_exception_cxx().

§ cgul_exception__delete()

CGUL_EXPORT void cgul_exception__delete ( cgul_exception_t  cex)

Free all internally allocated memory. Do not try to access cex after calling this method.

Parameters
[in]cexcgul_exception object

Referenced by cgul_exception_cxx::operator=(), cgul_exception_cxx::set_obj(), and cgul_exception_cxx::~cgul_exception_cxx().

§ cgul_exception__get_message()

CGUL_EXPORT const char* cgul_exception__get_message ( cgul_exception_t  cex)

Get the message associated with the exception.

Parameters
[in]cexrecently caught exception
Returns
message associated with the exception

Referenced by cgul_exception_cxx::get_message().