(Lazy, i.e. non-compiling, near-C code...)
// lispy macros
#define CAR(first,rest...) first
#define CDR(first,rest...) rest
// TRY macro easing the implementation of the RAII paradigm in C
// This implementation just scratches the surface...
if (TRY_trace) printf(#_cond ": " CAR(_msg) "\n",CDR(_msg));
printf("Failed" CAR(_msg) ", status=%d\n",CDR(_msg),status);
"cond" is any expression that returns anything that can be interpreted as a boolean (i.e. "if (cond)..."),
"status" identifies the error if "cond" evaluates to non-zero/true,
"exitpoint" refers to a code label which will be goto'ed if "cond" is non-zero/true, and
"msg" documents what "cond" is supposed to do.
int foo(int bufsize,char *filename)
TRY(!(buf=malloc(bufsize)), -1, done, "allocating %d byte buf",bufsize);
TRY(!(file=fopen(filename,"r")), -1, deallocate_buf, "opening file %s",filename);
TRY(status=bar(buf,file), status, close_file, "bar'ing buf[%d], file %s",bufsize,filename);
// could "goto done" here if not releasing resources immediately.
* RAII-like* paradigm, in a very compact and concise form
* Focus on what's important/unique, forget about boilerplate
* Every important line's purpose is auto-documented, as a side-effect of design
But most importantly, every important line of code is wrapped in a macro(!). An entire application can be altered, instrumented, re-interpreted, or otherwise arbitrarily processed simply by changing the definition of TRY. Trivial examples would be changing how the messages are printed/logged, or only "tracing" TRY calls to a certain depth. A less-trivial example would be implementing a debugging feature that allowed you single-step through TRY-instrumented code on-demand.
*I've never called this RAII myself, although it does make dealing with resource allocation/release consistently very easy. It's more of a general control-flow method I use; a controlled way of using goto in a way that makes nested, vulnerable-to-failure resource allocations easy to control and clean up.
Post a Comment