First Checkin. Woohoo!

git-svn-id: http://cexception.svn.sourceforge.net/svnroot/cexception/trunk@1 50f63946-2846-0410-8d77-f904c773002e
This commit is contained in:
mvandervoord
2008-02-21 01:38:49 +00:00
commit 87838b85bc
7 changed files with 651 additions and 0 deletions
+242
View File
@@ -0,0 +1,242 @@
====================================================================
CException
====================================================================
CException is a basic exception framework for C, suitable for use in
embedded applications. It provides an exception framework similar in
use to C++, but with much less overhead.
CException uses C standard library functions setjmp and longjmp to
operate. As long as the target system has these two functions defined,
this library should be useable with very little configuration. It
even supports environments where multiple program flows are in use,
such as real-time operating systems.
For the latest version, go to http://cexception.sourceforge.net
--------------------------------------------------------------------
CONTENTS OF THIS DOCUMENT
--------------------------------------------------------------------
Usage
Limitations
API
Configuration
Testing
License
--------------------------------------------------------------------
Usage
--------------------------------------------------------------------
Code that is to be protected are wrapped in Try { } Catch { } blocks.
The code directly following the Try call is "protected", meaning that
if any Throws occur, program control is directly transferred to the
start of the Catch block.
A numerical exception ID is included with Throw, and is made accessible
from the Catch block. In addition, an optional details field can be
included, allowing a pointer or additional numerical information to be
added.
Throws can occur from within function calls or directly within the
function itself.
--------------------------------------------------------------------
Limitations
--------------------------------------------------------------------
This library was made to be as fast as possible, and provide *basic*
exception handling. It is not a full-blown exception library as
provided by many other operating systems. Because of this, there are
a number of limitations that should be observed in order to
successfully utilize this library:
1. Do not directly "return" from within a Try block, nor "goto"
into or out of a Try block.
Why?
The "Try" macro allocates some local memory and alters a global
pointer. These are cleaned up at the top of the "Catch" macro.
Gotos and returns would bypass some of these steps, resulting in
memory leaks or unpredictable behavior.
2. If you change stack variables within your Try block (local variables
for example), and wish to make use of the updated values after an
exception is thrown, those variables should be made volatile. Note
that this is ONLY for locals and ONLY when you need access to them
after a throw.
Why?
Compilers optimize. There is no way to guarantee that the actual
memory location was updated and not just a register unless the
variable is marked volatile.
3. While EXCEPTION_ID and EXCEPTION_DETAIL are available outside a
Catch, it is good practice to ONLY access them from within the
Catch block.
Why?
EXCEPTION_ID is altered whenever a new Try block is encountered.
Both are obviously altered on Throws. It's best to isolate calls
to these to places where your state is well known.
4. Memory which is malloc'd or new'd is not automatically released
when an error is thrown. This will sometimes be desirable, and
othertimes may not. It will be the responsibility of the Catch
block to perform this kind of cleanup.
Why?
There's just no easy way to track malloc'd memory, etc., without
replacing or wrapping malloc calls or something like that. This
is a light framework, so these options were not desirable.
5. If additional Try blocks are started from within a Catch (either
in the immediate function or from within a function called from
the Catch block), it will alter the EXCEPTION_ID. It is therefore
good practice to NOT call such functions from a Catch block.
Why?
See #3 above.
--------------------------------------------------------------------
API
--------------------------------------------------------------------
void Exception_Init(void);
Try
---
Try is a macro which starts a protected block. It MUST be followed by
a pair of braces, enclosing the data that is to be protected. It MUST
be followed by a Catch block.
Catch
-----
Catch is a macro which ends the Try block and starts the error handling
block. The catch block is called if and only if an exception was thrown
while within the Try block. This error could have been thrown by a
call to Throw or ThrowDetailed.
EXCEPTION_ID
------------
EXCEPTION_ID is a macro which returns the ID of the exception which was
passed to Throw or ThrowDetailed.
EXCEPTION_DETAILS
----------------
EXCEPTION_DETAILS is a macro which returns the detail int of the exception.
If Throw was called, it will contain the line number of the Throw. If
ThrowDetailed was called, it will contain whatever detail information was
provided.
Throw
-----
The method of throwing an error. Throws should only occur from within a
protected (Try...Catch) block, though it may easily be nested many function
calls deep without an impact on performance or functionality. Try takes
a single argument, which is an exception id to be used in Catch as the
reason for the error.
ThrowDetailed
-------------
Operates identically to Throw, with the added ability to send additional
details. This detail is a single int, which can represent whatever the
developer desires. This has been used as a pointer to an error message,
pointer to data which must be freed, an instance id of a problem, etc.
Rethrow
-------
Rethrow can be used in a Catch block and allows the current error to be
pushed back an additional level of protected blocks. Because protected
blocks can be nested, it is sometimes useful to handle some errors in
inner blocks while throwing the rest to outside blocks. This function
does that, allowing the error to be passed on unchanged.
--------------------------------------------------------------------
CONFIGURATION
--------------------------------------------------------------------
CException is a mostly portable library. It has one universal
dependency, and another which is required if working in a multi-tasking
environment.
1. The standard C library setjmp must be available
2. If working in a multitasking environment, a method of obtaining an
index into an array of frames is required. If the OS supports a
method to retrieve Task ID's, and those Tasks are number 0, 1, 2..
you are in an ideal situation. Otherwise, a more creative mapping
function may be required. Note that this function is likely to be
called twice for each protected block. This is the greatest
overhead in the system.
ExceptionConfig.h
-----------------
EXCEPTION_MULTI_STACK - define if you are in a multi-tasking environment
EXCEPTION_NONE - set to a number which will never be an exception id in
your system.
EXCEPTION_GET_ID() - If in a multi-tasking environment, this should be
set to be a call to the function described in #2 above.
EXCEPTION_NUM_ID - If in a multi-tasking environment, this should be set
to the number of ID's required (usually the number of
tasks in the system)
You may also want to include any header files which will commonly be
needed by the rest of your application where it uses exception handling
here. For example, OS header files or exception codes would be useful.
--------------------------------------------------------------------
TESTING
--------------------------------------------------------------------
The test suite included makes use of the Unity Test Framework. It will
require a native C compiler, the example makefile using MinGW's gcc.
Modify the makefile to include the proper paths to tools, then run make
to compile and run the test application.
C_COMPILER - The C compiler to use to perform the tests
C_LIBS - The path to the C libraries (including setjmp)
UNITY_DIR - The path to the Unity framework (required to run tests)
(get it at http://embunity.sourceforge.net)
--------------------------------------------------------------------
LICENSE
--------------------------------------------------------------------
This software is licensed under the MIT License
Copyright (c) 2007 Mark VanderVoord
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
+38
View File
@@ -0,0 +1,38 @@
#include "Exception.h"
volatile EXCEPTION_FRAME_T ExceptionFrames[EXCEPTION_NUM_ID];
//------------------------------------------------------------------------------------------
// ThrowDetailed
//------------------------------------------------------------------------------------------
//
// PARAMETERS: None
//
// DESCRIPTION: throws a software exception with details
//
// RETURNS: None
//
//------------------------------------------------------------------------------------------
void ThrowDetailed(unsigned int ExceptionID, unsigned int Details)
{
MY_FRAME.Exception = ExceptionID;
MY_FRAME.Details = Details;
longjmp(*MY_FRAME.pFrame, 1);
}
//------------------------------------------------------------------------------------------
// Rethrow
//------------------------------------------------------------------------------------------
//
// PARAMETERS: None
//
// DESCRIPTION: rethrows a software exception that has already been thrown
//
// RETURNS: None
//
//------------------------------------------------------------------------------------------
void Rethrow()
{
longjmp(*MY_FRAME.pFrame, 1);
}
+43
View File
@@ -0,0 +1,43 @@
#ifndef _EXCEPTION_H
#define _EXCEPTION_H
#include <setjmp.h>
#include "ExceptionConfig.h"
//exception frame structures
typedef struct {
jmp_buf* pFrame;
volatile unsigned int Exception;
volatile unsigned int Details;
} EXCEPTION_FRAME_T;
extern volatile EXCEPTION_FRAME_T ExceptionFrames[];
#define MY_FRAME (ExceptionFrames[EXCEPTION_GET_ID()])
#define MY_FRAME_FAST (ExceptionFrames[MY_ID])
#define EXCEPTION_ID (MY_FRAME.Exception)
#define EXCEPTION_DETAILS (MY_FRAME.Details)
#define Try \
{ \
jmp_buf *PrevFrame, NewFrame; \
PrevFrame = MY_FRAME.pFrame; \
unsigned int MY_ID = EXCEPTION_GET_ID(); \
MY_FRAME_FAST.pFrame = &NewFrame; \
MY_FRAME_FAST.Details = 0; \
MY_FRAME_FAST.Exception = EXCEPTION_NONE; \
if (setjmp(NewFrame) == 0) { \
if (&PrevFrame)
#define Catch \
else { } \
MY_FRAME_FAST.Exception = EXCEPTION_NONE; \
} \
MY_FRAME_FAST.pFrame = PrevFrame; \
} \
if (MY_FRAME.Exception != EXCEPTION_NONE)
#define Throw(id) ThrowDetailed(id, __LINE__)
void ThrowDetailed(unsigned int ExceptionID, unsigned int Details);
void Rethrow(void);
#endif // _EXCEPTION_H
+26
View File
@@ -0,0 +1,26 @@
#ifndef _EXCEPTION_CONFIG_H
#define _EXCEPTION_CONFIG_H
//#define EXCEPTION_MULTI_STACK
#define EXCEPTION_NONE (0x5A5A5A5A)
//if using multistack support and not currently running a test, must include multistack support
#undef EXCEPTION_INCLUDE_MULTI_STACK_SUPPORT
#ifndef TEST
#ifdef EXCEPTION_MULTI_STACK
#define EXCEPTION_INCLUDE_MULTI_STACK_SUPPORT
#endif
#endif
//define the method of automatically looking up the current frame index (mappable from task ID if the task ID's are consecutive)
// The example below supports the Quadros OS in Multi-Stack Mode. Most RTOS will support a similar function call.
#ifdef EXCEPTION_INCLUDE_MULTI_STACK_SUPPORT
#include "OSAPI.h"
#define EXCEPTION_GET_ID() (KS_GetTaskID())
#define EXCEPTION_NUM_ID (NTASKS + 1)
#else
#define EXCEPTION_GET_ID() (0) //use the first index always because there is only one anyway
#define EXCEPTION_NUM_ID (1) //there is only the one stack
#endif
#endif //_EXCEPTION_CONFIG_H
+24
View File
@@ -0,0 +1,24 @@
#Tool and Lib Locations
C_COMPILER=gcc
C_LIBS=C:/MinGW/lib
UNITY_DIR=../../unity/src
#Test File To Be Created
OUT_FILE=test_cexceptions
ifeq ($(OS),Windows_NT)
OUT_EXTENSION=.exe
else
OUT_EXTENSION=.out
endif
#Options
SRC_FILES=lib/Exception.c test/TestException.c test/TestException_Runner.c $(UNITY_DIR)/unity.c
INC_DIRS=-Ilib -Itest -I$(UNITY_DIR)
LIB_DIRS=-L$(C_LIBS)
SYMBOLS=-DTEST
#Default Task: Compile And Run Tests
default:
$(C_COMPILER) $(INC_DIRS) $(LIB_DIRS) $(SYMBOLS) $(SRC_FILES) -o $(OUT_FILE)$(OUT_EXTENSION)
$(OUT_FILE)$(OUT_EXTENSION)
+218
View File
@@ -0,0 +1,218 @@
#include "unity.h"
#include "Exception.h"
void setUp(void)
{
}
void tearDown(void)
{
}
void test_BasicTryDoesNothingIfNoThrow(void)
{
int i;
Try
{
i += 1;
}
Catch
{
TEST_FAIL("Should Not Enter Catch If Not Thrown")
}
}
void test_BasicThrowAndCatch(void)
{
volatile unsigned int ID = 0;
Try
{
Throw(0xBEEFBEEF);
TEST_FAIL("Should Have Thrown An Error")
}
Catch
{
ID = EXCEPTION_ID;
}
TEST_ASSERT_EQUAL(0xBEEFBEEF, ID);
}
void test_VerifyVolatilesSurviveThrowAndCatch(void)
{
volatile unsigned int VolVal = 0;
Try
{
VolVal = 2;
Throw(0xBEEFBEEF);
TEST_FAIL("Should Have Thrown An Error")
}
Catch
{
VolVal += 2;
TEST_ASSERT_EQUAL(0xBEEFBEEF, EXCEPTION_ID);
}
TEST_ASSERT_EQUAL(4, VolVal);
}
void HappyExceptionThrower(unsigned int ID)
{
if (ID != 0)
Throw(ID);
}
void test_ThrowFromASubFunctionAndCatchInRootFunc(void)
{
volatile unsigned int ID = 0;
Try
{
HappyExceptionThrower(0xBADDF00D);
TEST_FAIL("Should Have Thrown An Exception");
}
Catch
{
ID = EXCEPTION_ID;
}
TEST_ASSERT_EQUAL(0xBADDF00D, ID);
}
void HappyExceptionRethrower(unsigned int ID)
{
Try
{
Throw(ID);
}
Catch
{
switch (EXCEPTION_ID)
{
case 0xBADDF00D:
Rethrow();
break;
default:
break;
}
}
}
void test_ThrowAndCatchFromASubFunctionAndRethrowToCatchInRootFunc(void)
{
volatile unsigned int ID = 0;
Try
{
HappyExceptionRethrower(0xBADDF00D);
TEST_FAIL("Should Have Rethrown Exception");
}
Catch
{
ID = EXCEPTION_ID;
}
TEST_ASSERT_EQUAL(0xBADDF00D, ID);
}
void test_ThrowAndCatchFromASubFunctionAndNoRethrowToCatchInRootFunc(void)
{
Try
{
HappyExceptionRethrower(0xBADDBEEF);
}
Catch
{
TEST_FAIL("Should Not Have Thrown Error");
}
}
void test_CanHaveMultipleTryBlocksInASingleFunction(void)
{
Try
{
HappyExceptionThrower(0x01234567);
TEST_FAIL("Should Have Rethrown Exception");
}
Catch
{
TEST_ASSERT_EQUAL(0x01234567, EXCEPTION_ID);
}
Try
{
HappyExceptionThrower(0xF00D8888);
TEST_FAIL("Should Have Rethrown Exception");
}
Catch
{
TEST_ASSERT_EQUAL(0xF00D8888, EXCEPTION_ID);
}
}
void test_CanHaveNestedTryBlocksInASingleFunction_ThrowInside(void)
{
int i = 0;
Try
{
Try
{
HappyExceptionThrower(0x01234567);
i = 1;
TEST_FAIL("Should Have Rethrown Exception");
}
Catch
{
TEST_ASSERT_EQUAL(0x01234567, EXCEPTION_ID);
}
}
Catch
{
TEST_FAIL("Should Have Been Caught By Inside Catch");
}
}
void test_CanHaveNestedTryBlocksInASingleFunction_ThrowOutside(void)
{
int i = 0;
Try
{
Try
{
i = 2;
}
Catch
{
TEST_FAIL("Should NotBe Caught Here");
}
HappyExceptionThrower(0x01234567);
TEST_FAIL("Should Have Rethrown Exception");
}
Catch
{
TEST_ASSERT_EQUAL(0x01234567, EXCEPTION_ID);
}
}
void HappyDetailedExceptionThrower(unsigned int ID, unsigned int Details)
{
if (ID != 0)
ThrowDetailed(ID, Details);
}
void test_CanThrowADetailedExceptionAndCheckOutTheResults(void)
{
Try
{
HappyDetailedExceptionThrower(0x12345678, 0x90ABCDEF);
TEST_FAIL("Should Have Thrown An Exception");
}
Catch
{
TEST_ASSERT_EQUAL(0x12345678, EXCEPTION_ID);
TEST_ASSERT_EQUAL(0x90ABCDEF, EXCEPTION_DETAILS);
}
}
+60
View File
@@ -0,0 +1,60 @@
/* AUTOGENERATED FILE. DO NOT EDIT. */
#include "unity.h"
#include "Exception.h"
#include <setjmp.h>
jmp_buf AbortFrame;
extern void setUp(void);
extern void tearDown(void);
extern void test_BasicTryDoesNothingIfNoThrow(void);
extern void test_BasicThrowAndCatch(void);
extern void test_VerifyVolatilesSurviveThrowAndCatch(void);
extern void test_ThrowFromASubFunctionAndCatchInRootFunc(void);
extern void test_ThrowAndCatchFromASubFunctionAndRethrowToCatchInRootFunc(void);
extern void test_ThrowAndCatchFromASubFunctionAndNoRethrowToCatchInRootFunc(void);
extern void test_CanHaveMultipleTryBlocksInASingleFunction(void);
extern void test_CanHaveNestedTryBlocksInASingleFunction_ThrowInside(void);
extern void test_CanHaveNestedTryBlocksInASingleFunction_ThrowOutside(void);
extern void test_CanThrowADetailedExceptionAndCheckOutTheResults(void);
static void runTest(UnityTestFunction test)
{
if (setjmp(AbortFrame) == 0)
{
setUp();
Try
{
test();
}
Catch
{
TEST_FAIL("Unexpected exception!")
}
}
tearDown();
}
int main(void)
{
Unity.TestFile = __FILE__;
UnityBegin();
// RUN_TEST calls runTest
RUN_TEST(test_BasicTryDoesNothingIfNoThrow);
RUN_TEST(test_BasicThrowAndCatch);
RUN_TEST(test_VerifyVolatilesSurviveThrowAndCatch);
RUN_TEST(test_ThrowFromASubFunctionAndCatchInRootFunc);
RUN_TEST(test_ThrowAndCatchFromASubFunctionAndRethrowToCatchInRootFunc);
RUN_TEST(test_ThrowAndCatchFromASubFunctionAndNoRethrowToCatchInRootFunc);
RUN_TEST(test_CanHaveMultipleTryBlocksInASingleFunction);
RUN_TEST(test_CanHaveNestedTryBlocksInASingleFunction_ThrowInside);
RUN_TEST(test_CanHaveNestedTryBlocksInASingleFunction_ThrowOutside);
RUN_TEST(test_CanThrowADetailedExceptionAndCheckOutTheResults);
UnityEnd();
return 0;
}