-->

ANSI C equivalent of try/catch?

2019-01-22 12:35发布

问题:

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();
}

回答1:

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.



回答2:

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.



回答3:

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



回答4:

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;
}


回答5:

This is my implementation of an exception handling system in C: exceptions4c.

It's powered by macros, built on top of setjmp and longjmp and it is 100% portable ANSI C.

There you can also find a list of all the different implementations I know of.



回答6:

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.



回答7:

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.



回答8:

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.