mirror of https://github.com/jrbrtsn/ban2fail
129 lines
4.9 KiB
C
129 lines
4.9 KiB
C
/*****************************************************************************
|
|
* In an effort to make C programming both easier and more secure, I present
|
|
* the EZ method.
|
|
*
|
|
* Reasoning for this is simple; Many function calls can fail for a variety of
|
|
* reasons, some or even most of which are obscure. However, it is both
|
|
* insecure and counterproductive not to check for said failures. Unfortunately
|
|
* coding proper checks and/or recovery from failures will involve (often
|
|
* complex) logic constructs, and detracts from understanding the primary logic
|
|
* of your source code.
|
|
*
|
|
* Sometimes your app needs to recover, and so you must supply this code, but
|
|
* more often than not a "boilplate" approach is sufficient. The EZ method is a
|
|
* consistent convention to supply centralized and reusable "boilerplate" error
|
|
* handling on an _optional_ basis, both for existing library functions and
|
|
* your own.
|
|
*
|
|
* Modern programming languages address this problem by throwing "exceptions",
|
|
* which by default result in your program getting terminated. To avoid
|
|
* termination you must write code to "catch" such exceptions, but this code
|
|
* exists separated in source code from the function call, and tends to be both
|
|
* confusing to read and hideous to look at. If you thought "goto" statements
|
|
* were in poor taste, then this will make you vomit.
|
|
*
|
|
* Enter the EZ method; from a practical coding standpoint you merely prepend
|
|
* "ez_" to any function call for which you desire default error handling. When
|
|
* an error occurs a very helpful error message is printed (in DEBUG mode this
|
|
* includes the source code location from which the function was called), and
|
|
* your program is terminated. If your app needs to recover from such an error,
|
|
* simply erase the "ez_" prefix from the function call and supply the recovery
|
|
* code, where God intended it to be - right where you made the function call!
|
|
*
|
|
* In effect the EZ method provides the utility of throwing exceptions, but
|
|
* without the catch - Woohoo!
|
|
*****************************************************************************/
|
|
|
|
#ifndef EZ_H
|
|
#define EZ_H
|
|
|
|
|
|
/*****************************************************************************
|
|
* These macros ease the drudgery of writing prototypes for the _ez_XXX()
|
|
* wrappers of functions, and reduce the liklihood of bugs due to typing
|
|
* errors.
|
|
*
|
|
* The elipses is for your function's argument list.
|
|
*****************************************************************************/
|
|
|
|
#ifdef DEBUG
|
|
|
|
#define ez_proto(rtn, funcname, ...) \
|
|
rtn _ez_##funcname( \
|
|
const char *fileName, \
|
|
int lineNo, \
|
|
const char *funcName, \
|
|
##__VA_ARGS__ \
|
|
)
|
|
|
|
#define ez_hdr_proto(rtn, funcname, ...) \
|
|
ez_proto(rtn, funcname, ##__VA_ARGS__); \
|
|
rtn funcname(__VA_ARGS__)
|
|
|
|
#else
|
|
|
|
#define ez_proto(rtn, funcname, ...) \
|
|
rtn _ez_##funcname(__VA_ARGS__)
|
|
|
|
#define ez_hdr_proto(rtn, funcname, ...) \
|
|
ez_proto(rtn, funcname, ##__VA_ARGS__); \
|
|
rtn funcname(__VA_ARGS__)
|
|
|
|
#endif
|
|
|
|
/*****************************************************************************
|
|
* For example, if you have the following function prototype:
|
|
*
|
|
* int myfunc (struct foo *f, double bar)
|
|
*
|
|
* In the header file where the function prototype goes,
|
|
* you will need this:
|
|
*
|
|
* ez_hdr_proto (int, myfunc, struct foo *f, double bar);
|
|
*
|
|
* #ifdef DEBUG
|
|
* # define ez_myfunc(...) \
|
|
* _ez_myfunc(__FILE__, __LINE__, __func__, ##__VA_ARGS__)
|
|
* #else
|
|
* # define ez_myfunc(...) \
|
|
* _ez_myfunc(__VA_ARGS__)
|
|
* #endif
|
|
*
|
|
* This will expand to the standard function prototype as listed above, as well
|
|
* as the _ez_myfunc() prototype correct for either DEBUG version or production
|
|
* version, depending on whether or not DEBUG is #define'd. Also, there is now
|
|
* a macro ez_myfunc() which will expand to the correct _ez_myfunc()
|
|
* function call.
|
|
*
|
|
* In the implmentation (*.c) file, you will need to supply code for
|
|
* _ez_myfunc(), which looks like so:
|
|
*
|
|
* ez_proto(int, myfunc, struct foo *f, double bar)
|
|
* {
|
|
* // implementation goes here
|
|
* }
|
|
*
|
|
* Notice that the prototype section is is the same as what you placed in the
|
|
* corresponding header file, minus "_hdr" in the ez_hdr_proto() macro.
|
|
*
|
|
* For the case where you are supplying only the ez_XXX() version of an
|
|
* existing function found in some library 'liba2z', you can place the
|
|
* following in your own header file, "ez_liba2z.h":
|
|
*
|
|
* ez_proto(struct rtnType*, liba2z_funcname, struct foo *f, double bar);
|
|
*
|
|
* Everything else works similar to the previous examples.
|
|
* Now you can use the EZ error handling like so:
|
|
*
|
|
* #include "ez_liba2z.h"
|
|
* int main(int argc, char **argv)
|
|
* {
|
|
* struct foo Foo;
|
|
* double bar;
|
|
* struct rtnType *rtn= ez_liba2z_funcname(&Foo, bar);
|
|
* }
|
|
*
|
|
*****************************************************************************/
|
|
|
|
#endif
|