Try catch statements in C

前端 未结 13 1471
眼角桃花
眼角桃花 2020-11-27 10:06

I was thinking today about the try/catch blocks existent in another languages. Googled for a while this but with no result. From what I know, there is not such a thing as tr

13条回答
  •  無奈伤痛
    2020-11-27 10:40

    While some of the other answers have covered the simple cases using setjmp and longjmp, in a real application there's two concerns that really matter.

    1. Nesting of try/catch blocks. Using a single global variable for your jmp_buf will make these not work.
    2. Threading. A single global variable for you jmp_buf will cause all kinds of pain in this situation.

    The solution to these is to maintain a thread-local stack of jmp_buf that get updated as you go. (I think this is what lua uses internally).

    So instead of this (from JaredPar's awesome answer)

    static jmp_buf s_jumpBuffer;
    
    void Example() { 
      if (setjmp(s_jumpBuffer)) {
        // The longjmp was executed and returned control here
        printf("Exception happened\n");
      } else {
        // Normal code execution starts here
        Test();
      }
    }
    
    void Test() {
      // Rough equivalent of `throw`
      longjump(s_jumpBuffer, 42);
    }
    

    You'd use something like:

    #define MAX_EXCEPTION_DEPTH 10;
    struct exception_state {
      jmp_buf s_jumpBuffer[MAX_EXCEPTION_DEPTH];
      int current_depth;
    };
    
    int try_point(struct exception_state * state) {
      if(current_depth==MAX_EXCEPTION_DEPTH) {
         abort();
      }
      int ok = setjmp(state->jumpBuffer[state->current_depth]);
      if(ok) {
        state->current_depth++;
      } else {
        //We've had an exception update the stack.
        state->current_depth--;
      }
      return ok;
    }
    
    void throw_exception(struct exception_state * state) {
      longjump(state->current_depth-1,1);
    }
    
    void catch_point(struct exception_state * state) {
        state->current_depth--;
    }
    
    void end_try_point(struct exception_state * state) {
        state->current_depth--;
    }
    
    __thread struct exception_state g_exception_state; 
    
    void Example() { 
      if (try_point(&g_exception_state)) {
        catch_point(&g_exception_state);
        printf("Exception happened\n");
      } else {
        // Normal code execution starts here
        Test();
        end_try_point(&g_exception_state);
      }
    }
    
    void Test() {
      // Rough equivalent of `throw`
      throw_exception(g_exception_state);
    }
    

    Again a more realistic version of this would include some way to store error information into the exception_state, better handling of MAX_EXCEPTION_DEPTH (maybe using realloc to grow the buffer, or something like that).

    DISCLAIMER: The above code was written without any testing whatsoever. It is purely so you get an idea of how to structure things. Different systems and different compilers will need to implement the thread local storage differently. The code probably contains both compile errors and logic errors - so while you're free to use it as you choose, TEST it before using it ;)

提交回复
热议问题