293 lines
		
	
	
		
			10 KiB
		
	
	
	
		
			Markdown
		
	
	
	
	
	
		
		
			
		
	
	
			293 lines
		
	
	
		
			10 KiB
		
	
	
	
		
			Markdown
		
	
	
	
	
	
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								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.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								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 [ThrowTheSwitch.org](http://throwtheswitch.org)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								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.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								Throws can occur from within function calls (nested as deeply as you
							 | 
						||
| 
								 | 
							
								like) or directly from 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.  Because
							 | 
						||
| 
								 | 
							
								of this, there are a few 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 (a) you change local (stack) variables within your `Try` block,
							 | 
						||
| 
								 | 
							
								   AND (b) 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. 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.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								API
							 | 
						||
| 
								 | 
							
								---
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								###Try
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								`Try` is a macro which starts a protected block.  It MUST be followed by
							 | 
						||
| 
								 | 
							
								a pair of braces or a single protected line (similar to an 'if'),
							 | 
						||
| 
								 | 
							
								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(e)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								`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 was thrown by a `Throw` call
							 | 
						||
| 
								 | 
							
								somewhere within `Try` (or within a function called within `Try`, or a function
							 | 
						||
| 
								 | 
							
								called by a function called within `Try`, etc).
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								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`
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								###Throw(e)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								This is the method of throwing an error. A `Throw` 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. `Throw` takes
							 | 
						||
| 
								 | 
							
								a single argument, which is an exception id which will be passed to `Catch`
							 | 
						||
| 
								 | 
							
								as the reason for the error.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								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.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								###ExitTry()
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								On rare occasion, you might want to immediately exit your current `Try` block
							 | 
						||
| 
								 | 
							
								but **not** treat this as an error. Don't run the `Catch`. Just start executing
							 | 
						||
| 
								 | 
							
								from after the `Catch` as if nothing had happened... That's what `ExitTry` is
							 | 
						||
| 
								 | 
							
								for.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								CONFIGURATION
							 | 
						||
| 
								 | 
							
								-------------
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								CException is a mostly portable library.  It has one universal
							 | 
						||
| 
								 | 
							
								dependency, and some macros which are required if working in a
							 | 
						||
| 
								 | 
							
								multi-tasking environment.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								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
							 | 
						||
| 
								 | 
							
								   index into an array of frames and to get the overall number of
							 | 
						||
| 
								 | 
							
								   id's are 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 and once during a throw.  This is the
							 | 
						||
| 
								 | 
							
								   only overhead in the system.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								Exception.h
							 | 
						||
| 
								 | 
							
								-----------
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								By convention, most projects include `Exception.h` which defines any
							 | 
						||
| 
								 | 
							
								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_T`
							 | 
						||
| 
								 | 
							
								  * Set this to the type you want your exception id's 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
							 | 
						||
| 
								 | 
							
								    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
							 | 
						||
| 
								 | 
							
								    to the number of ID's required (usually the number of
							 | 
						||
| 
								 | 
							
								    tasks in the system).  Defaults to `1` (for single
							 | 
						||
| 
								 | 
							
								    tasking environments).
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								* `CEXCEPTION_NO_CATCH_HANDLER(id)`
							 | 
						||
| 
								 | 
							
								  * This macro can be optionally specified.
							 | 
						||
| 
								 | 
							
								    It allows you to specify code to be called when a Throw
							 | 
						||
| 
								 | 
							
								    is made outside of `Try` ... `Catch` protection.  Consider
							 | 
						||
| 
								 | 
							
								    this the emergency fallback plan for when something has
							 | 
						||
| 
								 | 
							
								    gone terribly wrong.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								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.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								Finally, there are some hook macros which you can implement to inject
							 | 
						||
| 
								 | 
							
								your own target-specific code in particular places. It is a rare instance
							 | 
						||
| 
								 | 
							
								where you will need these, but they are here if you need them:
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								* `CEXCEPTION_HOOK_START_TRY`
							 | 
						||
| 
								 | 
							
								  * called immediately before the Try block
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								* `CEXCEPTION_HOOK_HAPPY_TRY`
							 | 
						||
| 
								 | 
							
								  * called immediately after the Try block if no exception was thrown
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								* `CEXCEPTION_HOOK_AFTER_TRY`
							 | 
						||
| 
								 | 
							
								  * called immediately after the Try block OR before an exception is caught
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								* `CEXCEPTION_HOOK_START_CATCH`
							 | 
						||
| 
								 | 
							
								  * called immediately before the catch
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								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
							 | 
						||
| 
								 | 
							
								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`
							 | 
						||
| 
								 | 
							
								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 [ThrowTheSwitch.org](http://throwtheswitch.org))
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								LICENSE
							 | 
						||
| 
								 | 
							
								-------
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								This software is licensed under the MIT License
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								Copyright (c) 2007-2017 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.
							 |