I think that there are quite some occasions when the try/catch mechanism offered by C++ (and other languages) can make code clearer.
Let me go back to what I believe is the the #1 job of programmers:
To write code as simple and as clean as possible so that the code itself conveys its purpose.
We all know how much this is easier said than done. I believe that anything that could help in expressing oneself clearer is beneficial.
The first scenario that comes to mind is the need of acquire a set of resources (e.g. memory, files, connections,...). If a resource is not available, the ones previously acquired should be released. This is usually solved using nested if or using a goto to a specific cleanup section of the code. Personally I don't like neither one or the other and I believe that using try/catch would make the intent clearer.
If C had them, that is!
Actually, it turns out it's not so difficult to put together few macros to do it. C-libutl has them, they are very easy to use and only require a C99 compiler. Here is an example:
#define EXCEPTION_NOMEMORY 1 #define EXCEPTION_NORESOURCE 2 #define EXCEPTION_NOFILE 3 #define EXCEPTION_FATALERROR 5 int main(int argc, char *argv[]) { ... try { printf("Do something\n"); f = fopen("myfile","r"); if (f == NULL) throw(EXCEPTION_NOFILE); printf("Continue doing\n"); } catch (EXCEPTION_NOMEMORY) { printf("Not enough memory\n"); } catch (EXCEPTION_NOFILE) { printf("Unable to find file\n"); } ... }
I'm sure you guessed that if "myfile" doesn't exist this fragment of code will print:
Do Something Unable to find file
So far it doesn't seem too useful: it's just a sort of goto in disguise! Or, even better, this could be handled with a simple if.
Actually, having try/catch blocks become more useful when you consider that you can throw an exception from a function and having it handled in the calling function:
void do_it(void) { ... m = malloc(128); if ( m == NULL) throw(EXCEPTION_NOMEMORY); ... } int main(int argc, char *argv[]) { ... try { printf("Do something\n"); do_it(); printf("Completed\n"); } catch (EXCEPTION_NOMEMORY) { printf("Not enough memory\n"); } catch (EXCEPTION_NOFILE) { printf("Unable to find file\n"); } catchall { printf("Something unexpected happened\n"); } ... }
Now, if whitin the do_it() function the system runs out of memory, the fragment will print:
Do Something Not enough memory
A rather useful mechanism to handle herrors neatly ... or for messing everything up! As a C programmer you're used to sharp tools, I'm sure you'll find a responsible way of using it.
The catchall block at the end will be executed if an exception is thrown but is not managed earlier.
In a catch block, you can retrieve the exception code through the function thrown() (actually this is useful only in a catchall block.
Try/catch blocks can be nested but, usually, this is a rather bad idea. What is useful, instead, is the ability to pass the exception up to have it handled from the calling function like in this example:
void do_it(void) { ... try { ... m = malloc(128); if ( m == NULL) throw(EXCEPTION_NOMEMORY); ... f = fopen("myfile","r"); if (f == NULL) throw(EXCEPTION_NOFILE); ... } catch (EXCEPTION_NOFILE) { f = stdin; free(m); // Continue in this function } catchall { rethrow(); // Excpetion will be handled in the calling function } ... } int main(int argc, char *argv[]) { ... try { printf("Do something\n"); do_it(); printf("Completed\n"); } catch (EXCEPTION_NOMEMORY) { printf("Not enough memory\n"); } catch (EXCEPTION_NOFILE) { printf("Unable to find file\n"); } ... }In the do_it() function, the exception NOFILE is handled directly but the exception NOMEMORY is passed to the upper try/catch block via the rethrow() function.
* * * *
The only thing that is left is to see how it is implemented. You can have a look at the utl_try.h and associated test cases ut_try.c file or read a more detailed post that explain the inner working mechanism of this flavour of try/catch.
0 comments :
Post a Comment