mirror of
https://github.com/throwtheswitch/cexception.git
synced 2026-10-26 17:23:55 +00:00
switched format of catches to pass in a variable to fill, more like C++.
git-svn-id: http://cexception.svn.sourceforge.net/svnroot/cexception/trunk@5 50f63946-2846-0410-8d77-f904c773002e
This commit is contained in:
+78
-84
@@ -12,6 +12,29 @@ this library should be useable with very little configuration. It
|
|||||||
even supports environments where multiple program flows are in use,
|
even supports environments where multiple program flows are in use,
|
||||||
such as real-time operating systems.
|
such as real-time operating systems.
|
||||||
|
|
||||||
|
There are about a gabillion exception frameworks using a similar
|
||||||
|
setjmp/longjmp method out there... and there will probably be more
|
||||||
|
in the future. Unfortunately, when we started our last embedded
|
||||||
|
project, all those that existed either (a) did not support multiple
|
||||||
|
tasks (therefore multiple stacks) or (b) were way more complex than
|
||||||
|
we really wanted. CException was born.
|
||||||
|
|
||||||
|
Why use CException?
|
||||||
|
|
||||||
|
0. It's ANSI C, and it beats passing error codes around.
|
||||||
|
|
||||||
|
1. You want something simple... CException throws a single id. You can
|
||||||
|
define those ID's to be whatever you like. You might even choose which
|
||||||
|
type that number is for your project. But that's as far as it goes.
|
||||||
|
We weren't interested in passing objects or structs or strings...
|
||||||
|
just simple error codes.
|
||||||
|
|
||||||
|
2. Performance... CException can be configured for single tasking or
|
||||||
|
multitasking. In single tasking, there is very little overhead past
|
||||||
|
the setjmp/longjmp calls (which are already fast). In multitasking,
|
||||||
|
your only additional overhead is the time it takes you to determine
|
||||||
|
a unique task id 0 - num_tasks.
|
||||||
|
|
||||||
For the latest version, go to http://cexception.sourceforge.net
|
For the latest version, go to http://cexception.sourceforge.net
|
||||||
|
|
||||||
--------------------------------------------------------------------
|
--------------------------------------------------------------------
|
||||||
@@ -35,22 +58,19 @@ if any Throws occur, program control is directly transferred to the
|
|||||||
start of the Catch block.
|
start of the Catch block.
|
||||||
|
|
||||||
A numerical exception ID is included with Throw, and is made accessible
|
A numerical exception ID is included with Throw, and is made accessible
|
||||||
from the Catch block. In addition, an optional details field can be
|
from the Catch block.
|
||||||
included, allowing a pointer or additional numerical information to be
|
|
||||||
added.
|
|
||||||
|
|
||||||
Throws can occur from within function calls or directly within the
|
Throws can occur from within function calls (nested as deeply as you
|
||||||
function itself.
|
like) or directly from within the function itself.
|
||||||
|
|
||||||
--------------------------------------------------------------------
|
--------------------------------------------------------------------
|
||||||
Limitations
|
Limitations
|
||||||
--------------------------------------------------------------------
|
--------------------------------------------------------------------
|
||||||
|
|
||||||
This library was made to be as fast as possible, and provide *basic*
|
This library was made to be as fast as possible, and provide basic
|
||||||
exception handling. It is not a full-blown exception library as
|
exception handling. It is not a full-blown exception library. Because
|
||||||
provided by many other operating systems. Because of this, there are
|
of this, there are a few limitations that should be observed in order
|
||||||
a number of limitations that should be observed in order to
|
to successfully utilize this library:
|
||||||
successfully utilize this library:
|
|
||||||
|
|
||||||
1. Do not directly "return" from within a Try block, nor "goto"
|
1. Do not directly "return" from within a Try block, nor "goto"
|
||||||
into or out of a Try block.
|
into or out of a Try block.
|
||||||
@@ -62,11 +82,11 @@ successfully utilize this library:
|
|||||||
Gotos and returns would bypass some of these steps, resulting in
|
Gotos and returns would bypass some of these steps, resulting in
|
||||||
memory leaks or unpredictable behavior.
|
memory leaks or unpredictable behavior.
|
||||||
|
|
||||||
2. If you change stack variables within your Try block (local variables
|
2. If (a) you change local (stack) variables within your Try block,
|
||||||
for example), and wish to make use of the updated values after an
|
AND (b) wish to make use of the updated values after an exception
|
||||||
exception is thrown, those variables should be made volatile. Note
|
is thrown, those variables should be made volatile. Note that this
|
||||||
that this is ONLY for locals and ONLY when you need access to them
|
is ONLY for locals and ONLY when you need access to them after a
|
||||||
after a throw.
|
throw.
|
||||||
|
|
||||||
Why?
|
Why?
|
||||||
|
|
||||||
@@ -74,17 +94,7 @@ successfully utilize this library:
|
|||||||
memory location was updated and not just a register unless the
|
memory location was updated and not just a register unless the
|
||||||
variable is marked volatile.
|
variable is marked volatile.
|
||||||
|
|
||||||
3. While EXCEPTION_ID and EXCEPTION_DETAIL are available outside a
|
3. Memory which is malloc'd or new'd is not automatically released
|
||||||
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
|
when an error is thrown. This will sometimes be desirable, and
|
||||||
othertimes may not. It will be the responsibility of the Catch
|
othertimes may not. It will be the responsibility of the Catch
|
||||||
block to perform this kind of cleanup.
|
block to perform this kind of cleanup.
|
||||||
@@ -95,74 +105,44 @@ successfully utilize this library:
|
|||||||
replacing or wrapping malloc calls or something like that. This
|
replacing or wrapping malloc calls or something like that. This
|
||||||
is a light framework, so these options were not desirable.
|
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
|
API
|
||||||
--------------------------------------------------------------------
|
--------------------------------------------------------------------
|
||||||
|
|
||||||
void Exception_Init(void);
|
|
||||||
|
|
||||||
Try
|
Try
|
||||||
---
|
---
|
||||||
|
|
||||||
Try is a macro which starts a protected block. It MUST be followed by
|
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
|
a pair of braces or a single protected line (similar to an 'if'),
|
||||||
be followed by a Catch block.
|
enclosing the data that is to be protected. It MUST be followed by a
|
||||||
|
Catch block (don't worry, you'll get compiler errors to let you know if
|
||||||
|
you mess any of that up).
|
||||||
|
|
||||||
Catch
|
Catch(e)
|
||||||
-----
|
--------
|
||||||
|
|
||||||
Catch is a macro which ends the Try block and starts the error handling
|
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
|
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
|
while within the Try block. This error was thrown by a Throw call
|
||||||
call to Throw or ThrowDetailed.
|
somewhere within Try (or within a function called within Try, or a function
|
||||||
|
called by a function called within Try, etc).
|
||||||
|
|
||||||
EXCEPTION_ID
|
The single parameter 'e' is filled with the error code which was thrown.
|
||||||
------------
|
This can be used for reporting, conditional cleanup, etc. (or you can just
|
||||||
|
ignore it if you really want... people ignore return codes all the time,
|
||||||
|
right?). 'e' should be of type EXCEPTION_T;
|
||||||
|
|
||||||
EXCEPTION_ID is a macro which returns the ID of the exception which was
|
Throw(e)
|
||||||
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
|
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
|
protected (Try...Catch) block, though it may easily be nested many function
|
||||||
calls deep without an impact on performance or functionality. Try takes
|
calls deep without an impact on performance or functionality. Throw takes
|
||||||
a single argument, which is an exception id to be used in Catch as the
|
a single argument, which is an exception id which will be passed to Catch
|
||||||
reason for the error.
|
as the reason for the error.
|
||||||
|
|
||||||
ThrowDetailed
|
If you wish to Rethrow an error, this can be done by calling Throw(e) with
|
||||||
-------------
|
the error code you just caught. It IS valid to throw from a catch block.
|
||||||
|
|
||||||
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
|
CONFIGURATION
|
||||||
@@ -172,7 +152,8 @@ CException is a mostly portable library. It has one universal
|
|||||||
dependency, and some macros which are required if working in a
|
dependency, and some macros which are required if working in a
|
||||||
multi-tasking environment.
|
multi-tasking environment.
|
||||||
|
|
||||||
1. The standard C library setjmp must be available
|
1. The standard C library setjmp must be available. Since this is part
|
||||||
|
of the standard library, chances are good that you'll be fine.
|
||||||
|
|
||||||
2. If working in a multitasking environment, methods for obtaining an
|
2. If working in a multitasking environment, methods for obtaining an
|
||||||
index into an array of frames and to get the overall number of
|
index into an array of frames and to get the overall number of
|
||||||
@@ -180,22 +161,31 @@ multi-tasking environment.
|
|||||||
ID's, and those Tasks are number 0, 1, 2... you are in an ideal
|
ID's, and those Tasks are number 0, 1, 2... you are in an ideal
|
||||||
situation. Otherwise, a more creative mapping function may be
|
situation. Otherwise, a more creative mapping function may be
|
||||||
required. Note that this function is likely to be called twice
|
required. Note that this function is likely to be called twice
|
||||||
for each protected block. This is the only overhead in the system.
|
for each protected block and once during a throw. This is the
|
||||||
|
only overhead in the system.
|
||||||
|
|
||||||
Exception.h
|
Exception.h
|
||||||
-----------------
|
-----------------
|
||||||
By convention, most projects include Exception.h which defines any
|
By convention, most projects include Exception.h which defines any
|
||||||
further requirements, then calls CException.h to do the gruntwork.
|
further requirements, then calls CException.h to do the gruntwork. All
|
||||||
|
of these are optional. You could directly include CException.h if
|
||||||
|
you wanted and just use the defaults provided.
|
||||||
|
|
||||||
EXCEPTION_NONE - set to a number which will never be an exception id in
|
EXCEPTION_T - Set this to the type you want your exception id's
|
||||||
your system.
|
to be. Defaults to 'unsigned int'.
|
||||||
|
|
||||||
|
EXCEPTION_NONE - Set this to a number which will never be an
|
||||||
|
exception id in your system. Defaults to 0x5a5a5a5a.
|
||||||
|
|
||||||
EXCEPTION_GET_ID - If in a multi-tasking environment, this should be
|
EXCEPTION_GET_ID - If in a multi-tasking environment, this should be
|
||||||
set to be a call to the function described in #2 above.
|
set to be a call to the function described in #2 above.
|
||||||
|
Defaults to just return 0 all the time (good for
|
||||||
|
single tasking environments)
|
||||||
|
|
||||||
EXCEPTION_NUM_ID - If in a multi-tasking environment, this should be set
|
EXCEPTION_NUM_ID - If in a multi-tasking environment, this should be set
|
||||||
to the number of ID's required (usually the number of
|
to the number of ID's required (usually the number of
|
||||||
tasks in the system)
|
tasks in the system). Defaults to 1 (for single
|
||||||
|
tasking environments).
|
||||||
|
|
||||||
You may also want to include any header files which will commonly be
|
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
|
needed by the rest of your application where it uses exception handling
|
||||||
@@ -205,9 +195,13 @@ here. For example, OS header files or exception codes would be useful.
|
|||||||
TESTING
|
TESTING
|
||||||
--------------------------------------------------------------------
|
--------------------------------------------------------------------
|
||||||
|
|
||||||
|
If you want to validate that CException works with your tools or that
|
||||||
|
it works with your custom configuration, you may want to run the test
|
||||||
|
suite.
|
||||||
|
|
||||||
The test suite included makes use of the Unity Test Framework. It will
|
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.
|
require a native C compiler. The example makefile uses MinGW's gcc.
|
||||||
Modify the makefile to include the proper paths to tools, then run make
|
Modify the makefile to include the proper paths to tools, then run 'make'
|
||||||
to compile and run the test application.
|
to compile and run the test application.
|
||||||
|
|
||||||
C_COMPILER - The C compiler to use to perform the tests
|
C_COMPILER - The C compiler to use to perform the tests
|
||||||
|
|||||||
+28
-19
@@ -3,36 +3,45 @@
|
|||||||
volatile EXCEPTION_FRAME_T ExceptionFrames[EXCEPTION_NUM_ID];
|
volatile EXCEPTION_FRAME_T ExceptionFrames[EXCEPTION_NUM_ID];
|
||||||
|
|
||||||
//------------------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------------------
|
||||||
// ThrowDetailed
|
// Throw
|
||||||
//------------------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------------------
|
||||||
//
|
//
|
||||||
// PARAMETERS: None
|
// PARAMETERS: None
|
||||||
//
|
//
|
||||||
// DESCRIPTION: throws a software exception with details
|
// DESCRIPTION: throws a software exception
|
||||||
//
|
//
|
||||||
// RETURNS: None
|
// RETURNS: None
|
||||||
//
|
//
|
||||||
//------------------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------------------
|
||||||
void ThrowDetailed(unsigned int ExceptionID, unsigned int Details)
|
void Throw(EXCEPTION_T ExceptionID)
|
||||||
{
|
{
|
||||||
MY_FRAME.Exception = ExceptionID;
|
unsigned int MY_ID = EXCEPTION_GET_ID;
|
||||||
MY_FRAME.Details = Details;
|
ExceptionFrames[MY_ID].Exception = ExceptionID;
|
||||||
longjmp(*MY_FRAME.pFrame, 1);
|
longjmp(*ExceptionFrames[MY_ID].pFrame, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
//------------------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------------------
|
||||||
// Rethrow
|
// Explaination of what it's all for:
|
||||||
//------------------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------------------
|
||||||
//
|
/*
|
||||||
// PARAMETERS: None
|
#define Try
|
||||||
//
|
{ <- give us some local scope. most compilers are happy with this
|
||||||
// DESCRIPTION: rethrows a software exception that has already been thrown
|
jmp_buf *PrevFrame, NewFrame; <- prev frame points to the last try block's frame. new frame gets created on stack for this Try block
|
||||||
//
|
unsigned int MY_ID = EXCEPTION_GET_ID; <- look up this task's id for use in frame array. always 0 if single-tasking
|
||||||
// RETURNS: None
|
PrevFrame = ExceptionFrames[EXCEPTION_GET_ID].pFrame; <- set pointer to point at old frame (which array is currently pointing at)
|
||||||
//
|
ExceptionFrames[MY_ID].pFrame = &NewFrame; <- set array to point at my new frame instead, now
|
||||||
//------------------------------------------------------------------------------------------
|
ExceptionFrames[MY_ID].Exception = EXCEPTION_NONE; <- initialize my exception id to be NONE
|
||||||
void Rethrow()
|
if (setjmp(NewFrame) == 0) { <- do setjmp. it returns 1 if longjump called, otherwise 0
|
||||||
{
|
if (&PrevFrame) <- this is here to force proper scoping. it requires braces or a single line to be but after Try, otherwise won't compile. This is always true at this point.
|
||||||
longjmp(*MY_FRAME.pFrame, 1);
|
|
||||||
}
|
#define Catch(e)
|
||||||
|
else { } <- this also forces proper scoping. Without this they could stick their own 'else' in and it would get ugly
|
||||||
|
ExceptionFrames[MY_ID].Exception = EXCEPTION_NONE; <- no errors happened, so just set the exception id to NONE (in case it was corrupted)
|
||||||
|
}
|
||||||
|
else <- an exception occurred
|
||||||
|
{ e = ExceptionFrames[MY_ID].Exception; e=e;} <- assign the caught exception id to the variable passed in.
|
||||||
|
ExceptionFrames[MY_ID].pFrame = PrevFrame; <- make the pointer in the array point at the previous frame again, as if NewFrame never existed.
|
||||||
|
} <- finish off that local scope we created to have our own variables
|
||||||
|
if (ExceptionFrames[EXCEPTION_GET_ID].Exception != EXCEPTION_NONE) <- start the actual 'catch' processing if we have an exception id saved away
|
||||||
|
*/
|
||||||
|
|
||||||
|
|||||||
+21
-19
@@ -15,40 +15,42 @@
|
|||||||
#define EXCEPTION_GET_ID (0) //use the first index always because there is only one anyway
|
#define EXCEPTION_GET_ID (0) //use the first index always because there is only one anyway
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifndef EXCEPTION_T
|
||||||
|
#define EXCEPTION_T unsigned int
|
||||||
|
#endif
|
||||||
|
|
||||||
//exception frame structures
|
//exception frame structures
|
||||||
typedef struct {
|
typedef struct {
|
||||||
jmp_buf* pFrame;
|
jmp_buf* pFrame;
|
||||||
volatile unsigned int Exception;
|
volatile EXCEPTION_T Exception;
|
||||||
volatile unsigned int Details;
|
|
||||||
} EXCEPTION_FRAME_T;
|
} EXCEPTION_FRAME_T;
|
||||||
|
|
||||||
|
//actual root frame storage (only one if single-tasking)
|
||||||
extern volatile EXCEPTION_FRAME_T ExceptionFrames[];
|
extern volatile EXCEPTION_FRAME_T ExceptionFrames[];
|
||||||
|
|
||||||
#define MY_FRAME (ExceptionFrames[EXCEPTION_GET_ID])
|
//Try (see C file for explanation)
|
||||||
#define MY_FRAME_FAST (ExceptionFrames[MY_ID])
|
|
||||||
#define EXCEPTION_ID (MY_FRAME.Exception)
|
|
||||||
#define EXCEPTION_DETAILS (MY_FRAME.Details)
|
|
||||||
|
|
||||||
#define Try \
|
#define Try \
|
||||||
{ \
|
{ \
|
||||||
jmp_buf *PrevFrame, NewFrame; \
|
jmp_buf *PrevFrame, NewFrame; \
|
||||||
unsigned int MY_ID = EXCEPTION_GET_ID; \
|
unsigned int MY_ID = EXCEPTION_GET_ID; \
|
||||||
PrevFrame = MY_FRAME.pFrame; \
|
PrevFrame = ExceptionFrames[EXCEPTION_GET_ID].pFrame; \
|
||||||
MY_FRAME_FAST.pFrame = &NewFrame; \
|
ExceptionFrames[MY_ID].pFrame = &NewFrame; \
|
||||||
MY_FRAME_FAST.Details = 0; \
|
ExceptionFrames[MY_ID].Exception = EXCEPTION_NONE; \
|
||||||
MY_FRAME_FAST.Exception = EXCEPTION_NONE; \
|
|
||||||
if (setjmp(NewFrame) == 0) { \
|
if (setjmp(NewFrame) == 0) { \
|
||||||
if (&PrevFrame)
|
if (&PrevFrame)
|
||||||
|
|
||||||
#define Catch \
|
//Catch (see C file for explanation)
|
||||||
|
#define Catch(e) \
|
||||||
else { } \
|
else { } \
|
||||||
MY_FRAME_FAST.Exception = EXCEPTION_NONE; \
|
ExceptionFrames[MY_ID].Exception = EXCEPTION_NONE; \
|
||||||
} \
|
} \
|
||||||
MY_FRAME_FAST.pFrame = PrevFrame; \
|
else \
|
||||||
|
{ e = ExceptionFrames[MY_ID].Exception; e=e; } \
|
||||||
|
ExceptionFrames[MY_ID].pFrame = PrevFrame; \
|
||||||
} \
|
} \
|
||||||
if (MY_FRAME.Exception != EXCEPTION_NONE)
|
if (ExceptionFrames[EXCEPTION_GET_ID].Exception != EXCEPTION_NONE)
|
||||||
|
|
||||||
#define Throw(id) ThrowDetailed(id, __LINE__)
|
//Throw an Error
|
||||||
void ThrowDetailed(unsigned int ExceptionID, unsigned int Details);
|
void Throw(EXCEPTION_T ExceptionID);
|
||||||
void Rethrow(void);
|
|
||||||
|
|
||||||
#endif // _CEXCEPTION_H
|
#endif // _CEXCEPTION_H
|
||||||
|
|||||||
@@ -1,8 +1,11 @@
|
|||||||
#ifndef _EXCEPTION_H
|
#ifndef _EXCEPTION_H
|
||||||
#define _EXCEPTION_H
|
#define _EXCEPTION_H
|
||||||
|
|
||||||
// Define the reserved value representing NO EXCEPTION
|
//Optionally define the exception type (something like an int which can be directly assigned)
|
||||||
#define EXCEPTION_NONE (0x5A5A5A5A)
|
#define EXCEPTION_T int
|
||||||
|
|
||||||
|
// Optionally define the reserved value representing NO EXCEPTION
|
||||||
|
#define EXCEPTION_NONE (1234)
|
||||||
|
|
||||||
// Multi-Tasking environments will need a couple of macros defined to make this library
|
// Multi-Tasking environments will need a couple of macros defined to make this library
|
||||||
// properly handle multiple exception stacks. You will need to include and required
|
// properly handle multiple exception stacks. You will need to include and required
|
||||||
@@ -13,11 +16,15 @@
|
|||||||
// For example, Quadros might include the following implementation:
|
// For example, Quadros might include the following implementation:
|
||||||
#ifndef TEST
|
#ifndef TEST
|
||||||
#include "OSAPI.h"
|
#include "OSAPI.h"
|
||||||
#define EXCEPTION_GET_ID() (KS_GetTaskID())
|
#define EXCEPTION_GET_ID (KS_GetTaskID())
|
||||||
#define EXCEPTION_NUM_ID (NTASKS + 1)
|
#define EXCEPTION_NUM_ID (NTASKS + 1)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// INCLUDE THE ACTUAL CEXCEPTION LIBRARY
|
// INCLUDE THE ACTUAL CEXCEPTION LIBRARY
|
||||||
#include "CException.h"
|
#include "CException.h"
|
||||||
|
|
||||||
|
//This could be a good place to define/include some error ID's:
|
||||||
|
#define ERROR_ID_EVERYTHING_IS_BROKEN (0x88)
|
||||||
|
#define ERROR_ID_ONLY_THIS_IS_BROKEN (0x77)
|
||||||
|
|
||||||
#endif // _EXCEPTION_H
|
#endif // _EXCEPTION_H
|
||||||
+123
-50
@@ -12,36 +12,64 @@ void tearDown(void)
|
|||||||
void test_BasicTryDoesNothingIfNoThrow(void)
|
void test_BasicTryDoesNothingIfNoThrow(void)
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
|
EXCEPTION_T e = 0x5a5a;
|
||||||
|
|
||||||
Try
|
Try
|
||||||
{
|
{
|
||||||
i += 1;
|
i += 1;
|
||||||
}
|
}
|
||||||
Catch
|
Catch(e)
|
||||||
{
|
{
|
||||||
TEST_FAIL("Should Not Enter Catch If Not Thrown")
|
TEST_FAIL("Should Not Enter Catch If Not Thrown")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//verify that e was untouched
|
||||||
|
TEST_ASSERT_EQUAL(0x5a5a, e);
|
||||||
}
|
}
|
||||||
|
|
||||||
void test_BasicThrowAndCatch(void)
|
void test_BasicThrowAndCatch(void)
|
||||||
{
|
{
|
||||||
volatile unsigned int ID = 0;
|
EXCEPTION_T e;
|
||||||
|
|
||||||
Try
|
Try
|
||||||
{
|
{
|
||||||
Throw(0xBEEFBEEF);
|
Throw(0xBEEFBEEF);
|
||||||
TEST_FAIL("Should Have Thrown An Error")
|
TEST_FAIL("Should Have Thrown An Error")
|
||||||
}
|
}
|
||||||
Catch
|
Catch(e)
|
||||||
{
|
{
|
||||||
ID = EXCEPTION_ID;
|
//verify that e has the right data
|
||||||
|
TEST_ASSERT_EQUAL(0xBEEFBEEF, e)
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_ASSERT_EQUAL(0xBEEFBEEF, ID);
|
//verify that e STILL has the right data
|
||||||
|
TEST_ASSERT_EQUAL(0xBEEFBEEF, e);
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_BasicThrowAndCatch_WithMiniSyntax(void)
|
||||||
|
{
|
||||||
|
EXCEPTION_T e;
|
||||||
|
|
||||||
|
//Mini Throw and Catch
|
||||||
|
Try
|
||||||
|
Throw(0xBEEFBEEF);
|
||||||
|
Catch(e)
|
||||||
|
TEST_ASSERT_EQUAL(0xBEEFBEEF, e);
|
||||||
|
TEST_ASSERT_EQUAL(0xBEEFBEEF, e);
|
||||||
|
|
||||||
|
//Mini Passthrough
|
||||||
|
Try
|
||||||
|
e = 0;
|
||||||
|
Catch(e)
|
||||||
|
TEST_FAIL("I shouldn't be caught because there was no throw");
|
||||||
|
|
||||||
|
TEST_ASSERT_EQUAL(0, e);
|
||||||
}
|
}
|
||||||
|
|
||||||
void test_VerifyVolatilesSurviveThrowAndCatch(void)
|
void test_VerifyVolatilesSurviveThrowAndCatch(void)
|
||||||
{
|
{
|
||||||
volatile unsigned int VolVal = 0;
|
volatile unsigned int VolVal = 0;
|
||||||
|
EXCEPTION_T e;
|
||||||
|
|
||||||
Try
|
Try
|
||||||
{
|
{
|
||||||
@@ -49,24 +77,28 @@ void test_VerifyVolatilesSurviveThrowAndCatch(void)
|
|||||||
Throw(0xBEEFBEEF);
|
Throw(0xBEEFBEEF);
|
||||||
TEST_FAIL("Should Have Thrown An Error")
|
TEST_FAIL("Should Have Thrown An Error")
|
||||||
}
|
}
|
||||||
Catch
|
Catch(e)
|
||||||
{
|
{
|
||||||
VolVal += 2;
|
VolVal += 2;
|
||||||
TEST_ASSERT_EQUAL(0xBEEFBEEF, EXCEPTION_ID);
|
TEST_ASSERT_EQUAL(0xBEEFBEEF, e);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_ASSERT_EQUAL(4, VolVal);
|
TEST_ASSERT_EQUAL(4, VolVal);
|
||||||
|
TEST_ASSERT_EQUAL(0xBEEFBEEF, e);
|
||||||
}
|
}
|
||||||
|
|
||||||
void HappyExceptionThrower(unsigned int ID)
|
void HappyExceptionThrower(unsigned int ID)
|
||||||
{
|
{
|
||||||
if (ID != 0)
|
if (ID != 0)
|
||||||
Throw(ID);
|
{
|
||||||
|
Throw(ID);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void test_ThrowFromASubFunctionAndCatchInRootFunc(void)
|
void test_ThrowFromASubFunctionAndCatchInRootFunc(void)
|
||||||
{
|
{
|
||||||
volatile unsigned int ID = 0;
|
volatile unsigned int ID = 0;
|
||||||
|
EXCEPTION_T e;
|
||||||
|
|
||||||
Try
|
Try
|
||||||
{
|
{
|
||||||
@@ -74,26 +106,29 @@ void test_ThrowFromASubFunctionAndCatchInRootFunc(void)
|
|||||||
HappyExceptionThrower(0xBADDF00D);
|
HappyExceptionThrower(0xBADDF00D);
|
||||||
TEST_FAIL("Should Have Thrown An Exception");
|
TEST_FAIL("Should Have Thrown An Exception");
|
||||||
}
|
}
|
||||||
Catch
|
Catch(e)
|
||||||
{
|
{
|
||||||
ID = EXCEPTION_ID;
|
ID = e;
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_ASSERT_EQUAL(0xBADDF00D, ID);
|
//verify that I can pass that value to something else
|
||||||
|
TEST_ASSERT_EQUAL(0xBADDF00D, e);
|
||||||
}
|
}
|
||||||
|
|
||||||
void HappyExceptionRethrower(unsigned int ID)
|
void HappyExceptionRethrower(unsigned int ID)
|
||||||
{
|
{
|
||||||
|
EXCEPTION_T e;
|
||||||
|
|
||||||
Try
|
Try
|
||||||
{
|
{
|
||||||
Throw(ID);
|
Throw(ID);
|
||||||
}
|
}
|
||||||
Catch
|
Catch(e)
|
||||||
{
|
{
|
||||||
switch (EXCEPTION_ID)
|
switch (e)
|
||||||
{
|
{
|
||||||
case 0xBADDF00D:
|
case 0xBADDF00D:
|
||||||
Rethrow();
|
Throw(0xBADDBEEF);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
@@ -104,41 +139,96 @@ void HappyExceptionRethrower(unsigned int ID)
|
|||||||
void test_ThrowAndCatchFromASubFunctionAndRethrowToCatchInRootFunc(void)
|
void test_ThrowAndCatchFromASubFunctionAndRethrowToCatchInRootFunc(void)
|
||||||
{
|
{
|
||||||
volatile unsigned int ID = 0;
|
volatile unsigned int ID = 0;
|
||||||
|
EXCEPTION_T e;
|
||||||
|
|
||||||
Try
|
Try
|
||||||
{
|
{
|
||||||
HappyExceptionRethrower(0xBADDF00D);
|
HappyExceptionRethrower(0xBADDF00D);
|
||||||
TEST_FAIL("Should Have Rethrown Exception");
|
TEST_FAIL("Should Have Rethrown Exception");
|
||||||
}
|
}
|
||||||
Catch
|
Catch(e)
|
||||||
{
|
{
|
||||||
ID = EXCEPTION_ID;
|
ID = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_ASSERT_EQUAL(0xBADDF00D, ID);
|
TEST_ASSERT_EQUAL(0xBADDBEEF, e);
|
||||||
|
TEST_ASSERT_EQUAL(1, ID);
|
||||||
}
|
}
|
||||||
|
|
||||||
void test_ThrowAndCatchFromASubFunctionAndNoRethrowToCatchInRootFunc(void)
|
void test_ThrowAndCatchFromASubFunctionAndNoRethrowToCatchInRootFunc(void)
|
||||||
{
|
{
|
||||||
|
EXCEPTION_T e = 3;
|
||||||
|
|
||||||
Try
|
Try
|
||||||
{
|
{
|
||||||
HappyExceptionRethrower(0xBADDBEEF);
|
HappyExceptionRethrower(0xBADDBEEF);
|
||||||
}
|
}
|
||||||
Catch
|
Catch(e)
|
||||||
{
|
{
|
||||||
TEST_FAIL("Should Not Have Thrown Error");
|
TEST_FAIL("Should Not Have Re-thrown Error (it should have already been caught)");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//verify that THIS e is still untouched, even though subfunction was touched
|
||||||
|
TEST_ASSERT_EQUAL(3, e);
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_ThrowAnErrorThenEnterATryBlockFromWithinCatch_VerifyThisDoesntCorruptExceptionId(void)
|
||||||
|
{
|
||||||
|
EXCEPTION_T e;
|
||||||
|
|
||||||
|
Try
|
||||||
|
{
|
||||||
|
HappyExceptionThrower(0xBADDBEEF);
|
||||||
|
TEST_FAIL("Should Have Thrown Exception");
|
||||||
|
}
|
||||||
|
Catch(e)
|
||||||
|
{
|
||||||
|
TEST_ASSERT_EQUAL(0xBADDBEEF, e);
|
||||||
|
HappyExceptionRethrower(0x12345678);
|
||||||
|
TEST_ASSERT_EQUAL(0xBADDBEEF, e);
|
||||||
|
}
|
||||||
|
TEST_ASSERT_EQUAL(0xBADDBEEF, e);
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_ThrowAnErrorThenEnterATryBlockFromWithinCatch_VerifyThatEachExceptionIdIndependent(void)
|
||||||
|
{
|
||||||
|
EXCEPTION_T e1, e2;
|
||||||
|
|
||||||
|
Try
|
||||||
|
{
|
||||||
|
HappyExceptionThrower(0xBADDBEEF);
|
||||||
|
TEST_FAIL("Should Have Thrown Exception");
|
||||||
|
}
|
||||||
|
Catch(e1)
|
||||||
|
{
|
||||||
|
TEST_ASSERT_EQUAL(0xBADDBEEF, e1);
|
||||||
|
Try
|
||||||
|
{
|
||||||
|
HappyExceptionThrower(0x12345678);
|
||||||
|
}
|
||||||
|
Catch(e2)
|
||||||
|
{
|
||||||
|
TEST_ASSERT_EQUAL(0x12345678, e2);
|
||||||
|
}
|
||||||
|
TEST_ASSERT_EQUAL(0x12345678, e2);
|
||||||
|
TEST_ASSERT_EQUAL(0xBADDBEEF, e1);
|
||||||
|
}
|
||||||
|
TEST_ASSERT_EQUAL(0x12345678, e2);
|
||||||
|
TEST_ASSERT_EQUAL(0xBADDBEEF, e1);
|
||||||
}
|
}
|
||||||
|
|
||||||
void test_CanHaveMultipleTryBlocksInASingleFunction(void)
|
void test_CanHaveMultipleTryBlocksInASingleFunction(void)
|
||||||
{
|
{
|
||||||
|
EXCEPTION_T e;
|
||||||
|
|
||||||
Try
|
Try
|
||||||
{
|
{
|
||||||
HappyExceptionThrower(0x01234567);
|
HappyExceptionThrower(0x01234567);
|
||||||
TEST_FAIL("Should Have Thrown Exception");
|
TEST_FAIL("Should Have Thrown Exception");
|
||||||
}
|
}
|
||||||
Catch
|
Catch(e)
|
||||||
{
|
{
|
||||||
TEST_ASSERT_EQUAL(0x01234567, EXCEPTION_ID);
|
TEST_ASSERT_EQUAL(0x01234567, e);
|
||||||
}
|
}
|
||||||
|
|
||||||
Try
|
Try
|
||||||
@@ -146,15 +236,17 @@ void test_CanHaveMultipleTryBlocksInASingleFunction(void)
|
|||||||
HappyExceptionThrower(0xF00D8888);
|
HappyExceptionThrower(0xF00D8888);
|
||||||
TEST_FAIL("Should Have Thrown Exception");
|
TEST_FAIL("Should Have Thrown Exception");
|
||||||
}
|
}
|
||||||
Catch
|
Catch(e)
|
||||||
{
|
{
|
||||||
TEST_ASSERT_EQUAL(0xF00D8888, EXCEPTION_ID);
|
TEST_ASSERT_EQUAL(0xF00D8888, e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void test_CanHaveNestedTryBlocksInASingleFunction_ThrowInside(void)
|
void test_CanHaveNestedTryBlocksInASingleFunction_ThrowInside(void)
|
||||||
{
|
{
|
||||||
int i = 0;
|
int i = 0;
|
||||||
|
EXCEPTION_T e;
|
||||||
|
|
||||||
Try
|
Try
|
||||||
{
|
{
|
||||||
Try
|
Try
|
||||||
@@ -163,12 +255,12 @@ void test_CanHaveNestedTryBlocksInASingleFunction_ThrowInside(void)
|
|||||||
i = 1;
|
i = 1;
|
||||||
TEST_FAIL("Should Have Rethrown Exception");
|
TEST_FAIL("Should Have Rethrown Exception");
|
||||||
}
|
}
|
||||||
Catch
|
Catch(e)
|
||||||
{
|
{
|
||||||
TEST_ASSERT_EQUAL(0x01234567, EXCEPTION_ID);
|
TEST_ASSERT_EQUAL(0x01234567, e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Catch
|
Catch(e)
|
||||||
{
|
{
|
||||||
TEST_FAIL("Should Have Been Caught By Inside Catch");
|
TEST_FAIL("Should Have Been Caught By Inside Catch");
|
||||||
}
|
}
|
||||||
@@ -177,42 +269,23 @@ void test_CanHaveNestedTryBlocksInASingleFunction_ThrowInside(void)
|
|||||||
void test_CanHaveNestedTryBlocksInASingleFunction_ThrowOutside(void)
|
void test_CanHaveNestedTryBlocksInASingleFunction_ThrowOutside(void)
|
||||||
{
|
{
|
||||||
int i = 0;
|
int i = 0;
|
||||||
|
EXCEPTION_T e;
|
||||||
|
|
||||||
Try
|
Try
|
||||||
{
|
{
|
||||||
Try
|
Try
|
||||||
{
|
{
|
||||||
i = 2;
|
i = 2;
|
||||||
}
|
}
|
||||||
Catch
|
Catch(e)
|
||||||
{
|
{
|
||||||
TEST_FAIL("Should NotBe Caught Here");
|
TEST_FAIL("Should NotBe Caught Here");
|
||||||
}
|
}
|
||||||
HappyExceptionThrower(0x01234567);
|
HappyExceptionThrower(0x01234567);
|
||||||
TEST_FAIL("Should Have Rethrown Exception");
|
TEST_FAIL("Should Have Rethrown Exception");
|
||||||
}
|
}
|
||||||
Catch
|
Catch(e)
|
||||||
{
|
{
|
||||||
TEST_ASSERT_EQUAL(0x01234567, EXCEPTION_ID);
|
TEST_ASSERT_EQUAL(0x01234567, e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ extern void tearDown(void);
|
|||||||
|
|
||||||
extern void test_BasicTryDoesNothingIfNoThrow(void);
|
extern void test_BasicTryDoesNothingIfNoThrow(void);
|
||||||
extern void test_BasicThrowAndCatch(void);
|
extern void test_BasicThrowAndCatch(void);
|
||||||
|
extern void test_BasicThrowAndCatch_WithMiniSyntax(void);
|
||||||
extern void test_VerifyVolatilesSurviveThrowAndCatch(void);
|
extern void test_VerifyVolatilesSurviveThrowAndCatch(void);
|
||||||
extern void test_ThrowFromASubFunctionAndCatchInRootFunc(void);
|
extern void test_ThrowFromASubFunctionAndCatchInRootFunc(void);
|
||||||
extern void test_ThrowAndCatchFromASubFunctionAndRethrowToCatchInRootFunc(void);
|
extern void test_ThrowAndCatchFromASubFunctionAndRethrowToCatchInRootFunc(void);
|
||||||
@@ -14,10 +15,12 @@ extern void test_ThrowAndCatchFromASubFunctionAndNoRethrowToCatchInRootFunc(void
|
|||||||
extern void test_CanHaveMultipleTryBlocksInASingleFunction(void);
|
extern void test_CanHaveMultipleTryBlocksInASingleFunction(void);
|
||||||
extern void test_CanHaveNestedTryBlocksInASingleFunction_ThrowInside(void);
|
extern void test_CanHaveNestedTryBlocksInASingleFunction_ThrowInside(void);
|
||||||
extern void test_CanHaveNestedTryBlocksInASingleFunction_ThrowOutside(void);
|
extern void test_CanHaveNestedTryBlocksInASingleFunction_ThrowOutside(void);
|
||||||
extern void test_CanThrowADetailedExceptionAndCheckOutTheResults(void);
|
extern void test_ThrowAnErrorThenEnterATryBlockFromWithinCatch_VerifyThisDoesntCorruptExceptionId(void);
|
||||||
|
extern void test_ThrowAnErrorThenEnterATryBlockFromWithinCatch_VerifyThatEachExceptionIdIndependent(void);
|
||||||
|
|
||||||
static void runTest(UnityTestFunction test)
|
static void runTest(UnityTestFunction test)
|
||||||
{
|
{
|
||||||
|
EXCEPTION_T e;
|
||||||
if (TEST_PROTECT())
|
if (TEST_PROTECT())
|
||||||
{
|
{
|
||||||
setUp();
|
setUp();
|
||||||
@@ -25,7 +28,7 @@ static void runTest(UnityTestFunction test)
|
|||||||
{
|
{
|
||||||
test();
|
test();
|
||||||
}
|
}
|
||||||
Catch
|
Catch(e)
|
||||||
{
|
{
|
||||||
TEST_FAIL("Unexpected exception!")
|
TEST_FAIL("Unexpected exception!")
|
||||||
}
|
}
|
||||||
@@ -42,6 +45,7 @@ int main(void)
|
|||||||
// RUN_TEST calls runTest
|
// RUN_TEST calls runTest
|
||||||
RUN_TEST(test_BasicTryDoesNothingIfNoThrow);
|
RUN_TEST(test_BasicTryDoesNothingIfNoThrow);
|
||||||
RUN_TEST(test_BasicThrowAndCatch);
|
RUN_TEST(test_BasicThrowAndCatch);
|
||||||
|
RUN_TEST(test_BasicThrowAndCatch_WithMiniSyntax);
|
||||||
RUN_TEST(test_VerifyVolatilesSurviveThrowAndCatch);
|
RUN_TEST(test_VerifyVolatilesSurviveThrowAndCatch);
|
||||||
RUN_TEST(test_ThrowFromASubFunctionAndCatchInRootFunc);
|
RUN_TEST(test_ThrowFromASubFunctionAndCatchInRootFunc);
|
||||||
RUN_TEST(test_ThrowAndCatchFromASubFunctionAndRethrowToCatchInRootFunc);
|
RUN_TEST(test_ThrowAndCatchFromASubFunctionAndRethrowToCatchInRootFunc);
|
||||||
@@ -49,7 +53,8 @@ int main(void)
|
|||||||
RUN_TEST(test_CanHaveMultipleTryBlocksInASingleFunction);
|
RUN_TEST(test_CanHaveMultipleTryBlocksInASingleFunction);
|
||||||
RUN_TEST(test_CanHaveNestedTryBlocksInASingleFunction_ThrowInside);
|
RUN_TEST(test_CanHaveNestedTryBlocksInASingleFunction_ThrowInside);
|
||||||
RUN_TEST(test_CanHaveNestedTryBlocksInASingleFunction_ThrowOutside);
|
RUN_TEST(test_CanHaveNestedTryBlocksInASingleFunction_ThrowOutside);
|
||||||
RUN_TEST(test_CanThrowADetailedExceptionAndCheckOutTheResults);
|
RUN_TEST(test_ThrowAnErrorThenEnterATryBlockFromWithinCatch_VerifyThisDoesntCorruptExceptionId);
|
||||||
|
RUN_TEST(test_ThrowAnErrorThenEnterATryBlockFromWithinCatch_VerifyThatEachExceptionIdIndependent);
|
||||||
|
|
||||||
UnityEnd();
|
UnityEnd();
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user