Next: , Up: conditions   [Contents][Index]


6.1 Introduction to exceptional–condition objects

The purpose of CCExceptions’s exceptional–condition objects module is to allow us to write code like this:

cce_location_t  L[1];

if (cce_location(L)) {
  // handle the exceptional condition
  if (my_condition_is_alpha(cce_condition(L)))
    {
      // react to exception "alpha"
    }
  else if (my_condition_is_beta(cce_condition(L)))
    {
      // react to exception "beta"
    }
  else
    {
      // default reaction to any exception
    }
  cce_run_catch_handlers_final(L);
} else {
  // do something useful
  cce_run_body_handlers(L);
}

in which we use some, previously defined, exceptional–condition object–types ‘alpha’ and ‘beta’.

If we need to hand the exceptional–condition object to an upper level location we can do it as follows:

void
upper_function (void)
{
  cce_location_t        L[1];

  if (cce_location(L)) {
    fprintf(stderr, "log: %s\n", \
        cce_condition_static_message(cce_condition(L)));
    cce_run_catch_handlers_final(L);
  } else {
    lower_function(L);
    cce_run_body_handlers(L);
  }
}

void
lower_function (cce_location_t * upper_L)
{
  cce_location_t        L[1];

  if (cce_location(L)) {
    cce_run_catch_handlers_raise(L, upper_L);
  } else {
    do_something();
    if (an_error_occurred()) {
      cce_raise(L, select_condition_object());
    }
   cce_run_body_handlers(L);
  }
}

From the point of view of our ability to react to an exception at runtime: a flexible situation is the one in which the exceptional–condition object–types are organised in a tree hierarchy. It allows us to select the level of granularity we desire in the branching logic. The tree hierarchy implemented by CCExceptions uses single inheritance; multiple inheritance would be more general, but also more complex.

How the hierarchy is implemented

Exceptional–condition objects are C language structures referencing a type descriptor, which is also a C language structure. Type descriptors are usually statically allocated and mostly have a hard–coded initialisation. Exceptional–condition objects are either dynamically allocated and initialised or statically allocated with hard–coded initialisation.

Every type descriptor data structure encloses a data structure of type cce_descriptor_t, which must be its first field. The instances of cce_descriptor_t hold the pointers that define the tree hierarchy; every type descriptor can be referenced by multiple exceptional–condition objects: it can represent the type of multiple objects; every type descriptor has a single parent and can have zero, one or more children. Each pointer to cce_descriptor_t is a unique runtime value identifying the type of exceptional–condition objects.

Every exceptional–condition object’s data structure encloses a data structure of type cce_condition_t, which must be its first field. The exceptional–condition object holds a pointer to the associated type descriptor.

The root of the tree is represented by the object–type descriptor cce_descriptor_root_t and objects of such type are struct of type cce_condition_root_t. We can think of the root structure types as follows:

typedef struct cce_condition_root_t     cce_descriptor_root_t;
typedef struct cce_condition_root_t     cce_condition_root_t;

struct cce_descriptor_root_t {
  cce_descriptor_t	descriptor;
};

struct cce_condition_root_t {
  cce_condition_t	condition;
};

extern cce_descriptor_root_t const * const cce_descriptor_root_ptr;

We declare the struct types needed to define a new exceptional–condition object–type, child of the root type, as follows:

typedef struct my_descriptor_error_t  my_descriptor_error_t;
typedef struct my_condition_error_t   my_condition_error_t;

struct my_descriptor_error_t {
  cce_descriptor_t  descriptor;
  /* Put some custom fields here. */
};

struct my_condition_error_t {
  cce_condition_root_t  root;
  /* Put some custom fields here. */
};

the field of type cce_descriptor_t is the first in its data structure type; the field of type cce_condition_root_t is the first in its data structure type.

In the following picture: the alpha descriptor is a child of the root descriptor; the beta descriptor is a child of the type ‘alpha’ descriptor. We can say that:

 -----------------
| descriptor root |
 -----------------
         ^
         |
 ------------------       --------------
| descriptor alpha | <-- | object alpha |
 ------------------       --------------
         ^
         |
 -----------------        -------------
| descriptor beta | <--- | object beta |
 -----------------        -------------

The data types that represent such hierarchy look as follows:

typedef struct my_descriptor_alpha_t my_descriptor_alpha_t;
typedef struct my_descriptor_beta_t  my_descriptor_beta_t;
typedef struct my_condition_alpha_t  my_condition_alpha_t;
typedef struct my_condition_beta_t   my_condition_beta_t;

/*** Descriptors types. ***/

struct my_descriptor_alpha_t {
  cce_descriptor_t  descriptor;
};

struct my_descriptor_beta_t {
  cce_descriptor_t  descriptor;
};

/*** Object types. ***/

struct my_condition_alpha_t {
  cce_condition_root_t  root;
};

struct my_condition_beta_t {
  my_condition_alpha_t  alpha;
};

we can statically allocate and initialise the type descriptors as follows:

my_descriptor_alpha_t my_descriptor_alpha = {
  .descriptor.parent = NULL
};

my_descriptor_beta_t  my_descriptor_beta  = {
  .descriptor.parent = &my_descriptor_alpha.descriptor
};

void
initialisation_function (void)
{
  cce_descriptor_set_parent_to(cce_descriptor_root_t) \
    (&my_descriptor_alpha.descriptor);
}

Next: , Up: conditions   [Contents][Index]

This document describes version 0.9.0-devel.3 of CCExceptions.