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.
 | 
