]> git.mdlowis.com Git - projs/libcds.git/commitdiff
Added support for exceptions
authorMichael D. Lowis <mike.lowis@gentex.com>
Fri, 29 Aug 2014 12:43:29 +0000 (08:43 -0400)
committerMichael D. Lowis <mike.lowis@gentex.com>
Fri, 29 Aug 2014 12:43:29 +0000 (08:43 -0400)
Rakefile
source/exn/exn.c [new file with mode: 0755]
source/exn/exn.h [new file with mode: 0755]
source/rbt/rbt.h
tests/main.c
tests/test_exn.c [new file with mode: 0644]

index 4fce26f2bef97653e183e6b85cb6be5c235d2261..16641e1379f7f4f11ebce28e14b05a0c0df8b860 100644 (file)
--- a/Rakefile
+++ b/Rakefile
@@ -16,7 +16,8 @@ require 'rbconfig'
 # 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
 
diff --git a/source/exn/exn.c b/source/exn/exn.c
new file mode 100755 (executable)
index 0000000..cd81cc7
--- /dev/null
@@ -0,0 +1,122 @@
+/**
+  @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);
+    }
+}
+
diff --git a/source/exn/exn.h b/source/exn/exn.h
new file mode 100755 (executable)
index 0000000..2a6d9c6
--- /dev/null
@@ -0,0 +1,73 @@
+/**
+  @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 */
index dff00c232dd91185fb4640bfc0cd2ab22c3968f8..5834e563f89d2692e0d5ea045f7dbf3e5767d21e 100644 (file)
@@ -7,32 +7,32 @@ extern "C" {
 
 /** 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;
 
 
@@ -80,7 +80,7 @@ rbt_node_t* rbt_lookup(rbt_t* tree, void* value);
 
 /**
  * @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
index b22a93a3d045a7a365ca4b9169e00d2d6d549e26..cac9be5956e11a2e55ef027b5eea577b16ce23c2 100644 (file)
@@ -9,5 +9,6 @@ int main(int argc, char** argv)
     RUN_TEST_SUITE(Buffer);
     RUN_TEST_SUITE(String);
     RUN_TEST_SUITE(RBT);
+    RUN_TEST_SUITE(Exn);
     return PRINT_TEST_RESULTS();
 }
diff --git a/tests/test_exn.c b/tests/test_exn.c
new file mode 100644 (file)
index 0000000..9c705c1
--- /dev/null
@@ -0,0 +1,92 @@
+
+// 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);
+    }
+}