Try catch statements in C

前端 未结 13 1412
眼角桃花
眼角桃花 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:56

    Ok, I couldn't resist replying to this. Let me first say I don't think it's a good idea to simulate this in C as it really is a foreign concept to C.

    We can use abuse the preprocessor and local stack variables to give use a limited version of C++ try/throw/catch.

    Version 1 (local scope throws)

    #include <stdbool.h>
    
    #define try bool __HadError=false;
    #define catch(x) ExitJmp:if(__HadError)
    #define throw(x) __HadError=true;goto ExitJmp;
    

    Version 1 is a local throw only (can't leave the function's scope). It does rely on C99's ability to declare variables in code (it should work in C89 if the try is first thing in the function).

    This function just makes a local var so it knows if there was an error and uses a goto to jump to the catch block.

    For example:

    #include <stdio.h>
    #include <stdbool.h>
    
    #define try bool __HadError=false;
    #define catch(x) ExitJmp:if(__HadError)
    #define throw(x) __HadError=true;goto ExitJmp;
    
    int main(void)
    {
        try
        {
            printf("One\n");
            throw();
            printf("Two\n");
        }
        catch(...)
        {
            printf("Error\n");
        }
        return 0;
    }
    

    This works out to something like:

    int main(void)
    {
        bool HadError=false;
        {
            printf("One\n");
            HadError=true;
            goto ExitJmp;
            printf("Two\n");
        }
    ExitJmp:
        if(HadError)
        {
            printf("Error\n");
        }
        return 0;
    }
    

    Version 2 (scope jumping)

    #include <stdbool.h>
    #include <setjmp.h>
    
    jmp_buf *g__ActiveBuf;
    
    #define try jmp_buf __LocalJmpBuff;jmp_buf *__OldActiveBuf=g__ActiveBuf;bool __WasThrown=false;g__ActiveBuf=&__LocalJmpBuff;if(setjmp(__LocalJmpBuff)){__WasThrown=true;}else
    #define catch(x) g__ActiveBuf=__OldActiveBuf;if(__WasThrown)
    #define throw(x) longjmp(*g__ActiveBuf,1);
    

    Version 2 is a lot more complex but basically works the same way. It uses a long jump out of the current function to the try block. The try block then uses an if/else to skip the code block to the catch block which check the local variable to see if it should catch.

    The example expanded again:

    jmp_buf *g_ActiveBuf;
    
    int main(void)
    {
        jmp_buf LocalJmpBuff;
        jmp_buf *OldActiveBuf=g_ActiveBuf;
        bool WasThrown=false;
        g_ActiveBuf=&LocalJmpBuff;
    
        if(setjmp(LocalJmpBuff))
        {
            WasThrown=true;
        }
        else
        {
            printf("One\n");
            longjmp(*g_ActiveBuf,1);
            printf("Two\n");
        }
        g_ActiveBuf=OldActiveBuf;
        if(WasThrown)
        {
            printf("Error\n");
        }
        return 0;
    }
    

    This uses a global pointer so the longjmp() knows what try was last run. We are using abusing the stack so child functions can also have a try/catch block.

    Using this code has a number of down sides (but is a fun mental exercise):

    • It will not free allocated memory as there are no deconstructors being called.
    • You can't have more than 1 try/catch in a scope (no nesting)
    • You can't actually throw exceptions or other data like in C++
    • Not thread safe at all
    • You are setting up other programmers for failure because they will likely not notice the hack and try using them like C++ try/catch blocks.
    0 讨论(0)
提交回复
热议问题