From: Michael D. Lowis Date: Fri, 29 Aug 2014 12:43:29 +0000 (-0400) Subject: Added support for exceptions X-Git-Url: https://git.mdlowis.com/?a=commitdiff_plain;h=3c27f828e607f004f1a4a4726fa6190af5ca95ae;p=projs%2Flibcds.git Added support for exceptions --- diff --git a/Rakefile b/Rakefile index 4fce26f..16641e1 100644 --- 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 index 0000000..cd81cc7 --- /dev/null +++ b/source/exn/exn.c @@ -0,0 +1,122 @@ +/** + @file exn.c + @brief See header for details + $Revision$ + $HeadURL$ + */ +#include "exn.h" +#include +#include + +#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 index 0000000..2a6d9c6 --- /dev/null +++ b/source/exn/exn.h @@ -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 +#include + +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 */ diff --git a/source/rbt/rbt.h b/source/rbt/rbt.h index dff00c2..5834e56 100644 --- a/source/rbt/rbt.h +++ b/source/rbt/rbt.h @@ -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 diff --git a/tests/main.c b/tests/main.c index b22a93a..cac9be5 100644 --- a/tests/main.c +++ b/tests/main.c @@ -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 index 0000000..9c705c1 --- /dev/null +++ b/tests/test_exn.c @@ -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); + } +}