# Define the compiler environment
Env = Rscons::Environment.new do |env|
env.build_dir('source/','build/obj/source')
- env["CFLAGS"] += ['--std=c99', '-Wall', '-Wextra', '-Werror']
+ env["CFLAGS"] += ['--std=c99', '--pedantic', '--coverage', '-Wall', '-Wextra', '-Werror']
+ env["LDFLAGS"] += ['--coverage']
env['CPPPATH'] += Dir['source/**/']
end
--- /dev/null
+/**
+ @file exn.c
+ @brief See header for details
+ $Revision$
+ $HeadURL$
+ */
+#include "exn.h"
+#include <stdio.h>
+#include <stdlib.h>
+
+#ifndef EXN_MAX_NUM_HANDLERS
+#define EXN_MAX_NUM_HANDLERS (8)
+#endif
+
+DEFINE_EXCEPTION(RuntimeException, NULL);
+DEFINE_EXCEPTION(NullPointerException, &RuntimeException);
+DEFINE_EXCEPTION(AssertionException, &RuntimeException);
+DEFINE_EXCEPTION(AbortException, &RuntimeException);
+DEFINE_EXCEPTION(ArithmeticException, &RuntimeException);
+DEFINE_EXCEPTION(IllegalInstructionException, &RuntimeException);
+DEFINE_EXCEPTION(SegmentationException, &RuntimeException);
+
+static bool Exn_Handled = true;
+static const exn_t* Exn_Current = NULL;
+static int Exn_Num_Handlers = 0;
+static exn_handler_t Exn_Handlers[EXN_MAX_NUM_HANDLERS];
+
+static void exn_uncaught(const exn_t* p_exn) {
+ (void)p_exn;
+ fprintf(stderr, "Error: Uncaught Exception\n");
+ exit(1);
+}
+
+void exn_prep(void) {
+ if((Exn_Num_Handlers+1) > EXN_MAX_NUM_HANDLERS)
+ exn_throw(&RuntimeException);
+ Exn_Handlers[Exn_Num_Handlers].state = EXN_BEGIN;
+ Exn_Num_Handlers++;
+ Exn_Handled = true;
+}
+
+bool exn_process(void) {
+ bool ret = true;
+ switch(exn_handler()->state) {
+ case EXN_BEGIN:
+ exn_handler()->state = EXN_TRY;
+ break;
+
+ case EXN_TRY:
+ exn_handler()->state = (!Exn_Handled) ? EXN_CATCH : EXN_FINALLY;
+ break;
+
+ case EXN_CATCH:
+ exn_handler()->state = EXN_FINALLY;
+ break;
+
+ case EXN_FINALLY:
+ if (!Exn_Handled)
+ {
+ exn_rethrow();
+ }
+ else
+ {
+ exn_handler()->state = EXN_DONE;
+ }
+ break;
+
+ case EXN_DONE:
+ Exn_Num_Handlers--;
+ ret = false;
+ break;
+
+ default:
+ fprintf(stderr, "Error: Exception handler entered an invalid state\n");
+ exit(1);
+ break;
+ }
+ return ret;
+}
+
+void exn_throw(const exn_t* p_type) {
+ if (Exn_Num_Handlers == 0) {
+ exn_uncaught(Exn_Current);
+ } else {
+ Exn_Current = p_type;
+ Exn_Handled = false;
+ longjmp(exn_handler()->context,1);
+ }
+}
+
+void exn_rethrow(void) {
+ Exn_Num_Handlers--;
+ exn_throw(Exn_Current);
+}
+
+bool exn_catch(const exn_t* p_type) {
+ const exn_t* p_current;
+ for(p_current = exn_current(); p_current != NULL; p_current = p_current->p_parent)
+ {
+ if (p_current == p_type)
+ {
+ Exn_Handled = true;
+ break;
+ }
+ }
+ return Exn_Handled;
+}
+
+const exn_t* exn_current(void) {
+ return Exn_Current;
+}
+
+exn_handler_t* exn_handler(void) {
+ return &(Exn_Handlers[Exn_Num_Handlers-1]);
+}
+
+void exn_assert(bool expr) {
+ if(!expr) {
+ exn_throw(&AssertionException);
+ }
+}
+
--- /dev/null
+/**
+ @file exn.h
+ @brief An implementation of exception handling in pure ANSI C.
+ $Revision$
+ $HeadURL$
+*/
+#ifndef EXN_H
+#define EXN_H
+
+#include <setjmp.h>
+#include <stdbool.h>
+
+typedef struct exn_t {
+ const struct exn_t* p_parent;
+} exn_t;
+
+typedef enum {
+ EXN_BEGIN = 0,
+ EXN_TRY = 1,
+ EXN_CATCH = 2,
+ EXN_FINALLY = 3,
+ EXN_DONE = 4
+} exn_state_t;
+
+typedef struct {
+ jmp_buf context;
+ exn_state_t state;
+} exn_handler_t;
+
+#define DECLARE_EXCEPTION(exname) \
+ extern const exn_t exname
+
+#define DEFINE_EXCEPTION(exname, parent) \
+ const exn_t exname = { parent }
+
+DECLARE_EXCEPTION(RuntimeException);
+DECLARE_EXCEPTION(NullPointerException);
+DECLARE_EXCEPTION(AssertionException);
+DECLARE_EXCEPTION(AbortException);
+DECLARE_EXCEPTION(ArithmeticException);
+DECLARE_EXCEPTION(IllegalInstructionException);
+DECLARE_EXCEPTION(SegmentationException);
+
+void exn_prep(void);
+bool exn_process(void);
+void exn_throw(const exn_t* p_type);
+void exn_rethrow(void);
+bool exn_catch(const exn_t* p_type);
+const exn_t* exn_current(void);
+exn_handler_t* exn_handler(void);
+void exn_assert(bool expr);
+
+#define try \
+ for(exn_prep(), setjmp(exn_handler()->context); exn_process();) \
+ if (exn_handler()->state == EXN_TRY)
+
+#define catch(type) \
+ else if ((exn_handler()->state == EXN_CATCH) && exn_catch(&(type)))
+
+#define finally \
+ else if (exn_handler()->state == EXN_FINALLY)
+
+#define throw(type) exn_throw(&(type))
+
+#define rethrow() exn_rethrow()
+
+#ifdef NDEBUG
+#define assert(expr) ((void)0)
+#else
+#define assert(expr) exn_assert(expr)
+#endif
+
+#endif /* EXN_H */
/** node colors */
typedef enum {
- RED = 0,
- BLACK
+ RED = 0,
+ BLACK
} rbt_color_t;
-
+
/** a function pointer for comparing node contents
should return -1, 0, or 1 if a is <, ==, or > b, respectively */
typedef int (*comparator_t)(void* p_a, void* p_b);
/** a red-black tree node */
typedef struct rbt_node_t {
- /** pointers to immediate relatives */
- struct rbt_node_t* left;
- struct rbt_node_t* right;
- struct rbt_node_t* parent;
- /** node color */
- rbt_color_t color;
- /** pointer to node contents */
- void* contents;
+ /** pointers to immediate relatives */
+ struct rbt_node_t* left;
+ struct rbt_node_t* right;
+ struct rbt_node_t* parent;
+ /** node color */
+ rbt_color_t color;
+ /** pointer to node contents */
+ void* contents;
} rbt_node_t;
/** a red-black tree */
typedef struct {
- /** pointer to the root of the tree */
- rbt_node_t* root;
- /** function pointer for comparing node contents */
- comparator_t comp;
+ /** pointer to the root of the tree */
+ rbt_node_t* root;
+ /** function pointer for comparing node contents */
+ comparator_t comp;
} rbt_t;
/**
* @brief count the number of nodes in a red-black tree
- *
+ *
* @param tree pointer to the tree on which to operate
*
* @return the number of nodes present in the tree
RUN_TEST_SUITE(Buffer);
RUN_TEST_SUITE(String);
RUN_TEST_SUITE(RBT);
+ RUN_TEST_SUITE(Exn);
return PRINT_TEST_RESULTS();
}
--- /dev/null
+
+// Unit Test Framework Includes
+#include "test.h"
+
+// File To Test
+#include "exn.h"
+
+static void test_setup(void) { }
+
+//-----------------------------------------------------------------------------
+// Begin Unit Tests
+//-----------------------------------------------------------------------------
+TEST_SUITE(Exn) {
+ //-------------------------------------------------------------------------
+ // Test catch blocks
+ //-------------------------------------------------------------------------
+ TEST(Verify_a_catch_block_will_catch_a_matching_exception)
+ {
+ int counter = 0;
+ try { throw(RuntimeException); }
+ catch(RuntimeException) { counter++; }
+ CHECK(counter == 1);
+ }
+
+ TEST(Verify_a_catch_block_will_catch_a_matching_exception_by_hierarchy)
+ {
+ int counter = 0;
+ try { throw(AssertionException); }
+ catch(RuntimeException) { counter++; }
+ CHECK(counter == 1);
+ }
+
+ TEST(Verify_the_first_matching_catch_block_is_executed)
+ {
+ int counter = 0;
+ try { throw(AssertionException); }
+ catch(RuntimeException) { counter++; }
+ catch(AssertionException) { counter++; }
+ CHECK(counter == 1);
+ }
+
+ TEST(Verify_non_matching_catch_blocks_are_ignored)
+ {
+ int counter = 0;
+ try { throw(AssertionException); }
+ catch(AbortException) { counter++; }
+ catch(RuntimeException) { counter++; }
+ catch(AssertionException) { counter++; }
+ CHECK(counter == 1);
+ }
+
+ TEST(Verify_an_uncaught_exception_will_be_rethrown)
+ {
+ int counter = 0;
+ try {
+ counter++;
+ try { throw(RuntimeException); }
+ catch(AssertionException) { counter++; }
+ counter++;
+ } catch(RuntimeException) {
+ counter++;
+ }
+ CHECK(counter == 2);
+ }
+
+ //-------------------------------------------------------------------------
+ // Test finally blocks
+ //-------------------------------------------------------------------------
+ TEST(Verify_a_finally_block_is_executed_after_an_uncaught_exception_is_thrown)
+ {
+ int counter = 0;
+ try {
+ counter++;
+ try { throw(RuntimeException); }
+ finally { counter++; }
+ } catch(RuntimeException) {
+ counter++;
+ }
+ CHECK(counter == 3);
+ }
+
+ TEST(Verify_a_finally_block_is_executed_after_a_succesful_try_block)
+ {
+ int counter = 0;
+ try {
+ counter++;
+ }
+ catch(RuntimeException) { counter++; }
+ finally { counter++; }
+ CHECK(counter == 2);
+ }
+}