views:

574

answers:

7

I have some C code I'm working with, and I'm finding errors when the code is running but have little info about how to do a proper try/catch (as in C# or C++).

For instance in C++ I'd just do:

try{
//some stuff
}
catch(...)
{
//handle error
}

but in ANSI C I'm a bit lost. I tried some online searches but I don't see enough info about how to make it happen / figured I'd ask here in case anyone can point me in the right direction.

Here's the code I'm working with (fairly simple, recursive method) and would like to wrap with try/catch (or equivalent error-handling structure).

However my main question is simply how to do a try / catch in ANSI C...the implementation / example doesn't have to be recursive.

void getInfo( int offset, myfile::MyItem * item )
{
    ll::String myOtherInfo = item->getOtherInfo();
    if( myOtherInfo.isNull() )
        myOtherInfo = "";
    ll::String getOne = "";
    myfile::Abc * abc = item->getOrig();
    if( abc != NULL )
    {
        getOne = abc->getOne();
    }
    for( int i = 0 ; i < offset ; i++ )
    {
             printf("found: %d", i);
    }
    if( abc != NULL )
        abc->release();
    int childCount = item->getChildCount();
    offset++;
    for( int i = 0 ; i < childCount ; i++ )
        getInfo( offset, item->getChild(i) );
    item->release();
}
+21  A: 

Generally, you don't.

It's possible to use setjmp and longjmp to build something fairly similar to try/catch, although there's no such thing in C as destructors or stack unwinding, so RAII is out of the question. You could even approximate RAII with a so-called "cleanup stack" (see for example Symbian/C++), although it's not a very close approximation, and it's a lot of work.

The usual way to indicate errors or failure in C is to return a value indicating success status. Callers examine the return value and act accordingly. See for example the standard C functions: printf, read, open, for ideas how to specify your functions.

When mixing C and C++ code, you must ensure that a C++ exception never reaches C code. When writing C++ functions that will be called from C, catch everything.

Steve Jessop
IME the usual way to indicate errors or failure in C is to return a value that's ignored by the caller. `:(`
sbi
@sbi: IME, some people still believe the "return a value" is the way to go in C++, Java and C#, too... `:(`
paercebal
@paercebal: I know. `:(`
sbi
+1  A: 

If you want to do a multiple level jump, look up setjmp() and longjmp(). They can be used as a primitive exception throw. The setjmp() function sets up a return-to place, and returns a status value. The longjmp() function goes to the return-to place, and provides the status value. You can create a catch function by having be called after setjmp() depending on the status value.

Do not, for whatever reason, use them in C++. They do not do stack unwinding or call destructors.

David Thornley
+11  A: 

C does not support exception handling.

There is info on one approach to this problem here. This shows the simple setjmp/longjmp approach but also provides a more sophisticated alternative, covered in depth.

Steve Townsend
Hmm, third downvote today. Any particular reason for this one? I thought this looked promising.
Steve Townsend
Is someone out to get you?
John Dibling
+1 because the down vote was wrong... perfect link for the question
Hogan
@John Dibling - that goes without saying... Will keep an eye out, maybe just a bad day.
Steve Townsend
A: 

Since C++ was originally implemented as a C pre-processor and it had Try / Catch you could redo Bjarne Stroustrup's work and write a pre-processor to do it.

Hogan
By the time exceptions were introduced to C++, the C-preprocessor implementation was a history.
Nemanja Trifunovic
@Nemanja: That is indeed true for the original cfront. [Comeau C++](http://www.comeaucomputing.com), however, still outputs C code. (Because that makes for high portability.) But I doubt that this is anymore useful than assembler output of any other compiler: it's machine-generated code, and as such not for human consumption.
sbi
Jonathan Leffler
+2  A: 

There is the classic unwinding gotos pattern:

FILE *if = fopen(...);
FILE *of = NULL;
if (if == NULL) return; 

of = fopen(...);
if (of == NULL) goto close_if;

/* ...code... */
if (something_is_wrong) goto close_of;

/* ... other code... */

close_of:
  fclose(of);
close_if:
  fclose(if);

return state;

Alternately you can fake it in a limited way by isolating the "try" code in another function

int try_code(type *var_we_must_write, othertype var_we_only_read /*, ... */){
  /* ...code... */
  if (!some_condition) return 1;
  /* ...code... */
  if (!another_condition) return 2;
  /* ...code... */
  if (last_way_to_fail) return 4;
  return 0;
}

void calling_routine(){
  /* ... */
  if (try_code(&x,y/*, other state */) ) {
     /* do your finally here */
  }
 /* ... */
}

but neither approach is fully equivalent. You have to manage all the resources yourself, you don't get automatic rollback until a handler is found, and so on...

dmckee
Eek! That looks really messy to me. I prefer my method I suggested below. No need for goto!
James
`goto` is the way to go (excuse the pun) in C. Having multiple exit labels seems like overkill, though. It's cleaner (although slightly less efficient) to have a single exit label and (if necessary) to check which resources were allocated.
jamesdlin
@jamesdlin: The multiple labels buy you something specific: each one corresponds to a resource that may need to be released. You can avoid them with conditionals on the releases but in some cases (not this one) that calls for additional flags. Matter of taste as far as I can tell.
dmckee
@dmckee: Yeah, but it has the cost of some extra complexity, and IMO it's a bit harder to maintain (because order matters). In most cases additional flags (and additional checks) shouldn't be necessary anyway; clean-up functions (not `fclose`) generally should be no-ops if called on dummy values (e.g. `NULL`).
jamesdlin
@jamesdlin: when I've done this style of cleanup (generally in assembler rather than C), I've found the goto version cleaner and simpler in cases where the potential resources are always allocated in the same order. You can easily know which one to `goto`, because at any given point in the code you understand what resources you have (I hope).
Steve Jessop
+2  A: 

You can find a possible implementation with longjmp in this book : C Interfaces and Implementations: Techniques for Creating Reusable Software - David Hanson

And you can find the code here and here as an example of the style used in the book.

Matthieu
+2  A: 

One useful coding style I like to use is the following. I don't know if it has a particular name but I came across it when I was reverse engineering some assembly code into the equivalent C code. You do lose an indentation level but it's not such a big deal to me. Watch out for reviewer who will point out the infinite loop! :)

int SomeFunction() {
    int err = SUCCESS;

    do {
        err = DoSomethingThatMayFail();
        if (err != SUCCESS) {
            printf("DoSomethingThatMayFail() failed with %d", err);
            break;
        }

        err = DoSomethingElse();
        if (err != SUCCESS) {
            printf("DoSomethingElse() failed with %d", err);
            break;
        }

        // ... call as many functions as needed.

        // If execution gets there everything succeeded!
        return SUCCESS;
    while (false);

    // Something went wrong!
    // Close handles or free memory that may have been allocated successfully.

    return err;
}
James
Nice. You may need nested instances if you need to unwind stuff conditionally.
dmckee