C语言中Try/Catch的实现

C语言中Try/Catch的实现

许多高级语言中都有Try/Catch的实现用于处理异常。例如在C++中

try {
    // ... do something
    // ... throw a exception
} catch (exception &e) {
    // handled the specific exception
} catch (...) {
    // handled other exception.
}

可以通过以上语句块来实现异常的捕获和处理。

C语言中并没有直接提供try/catch/throw的实现。但是C提供了两个跳转函数setjmp与longjmp可以实现

#include <setjmp.h>

int setjmp(jmp_buf env);
void longjmp(jmp_buf env, int val);

1. 以下例子,使用setjmp/longjmp将模拟Try/Catch的机制实现跳转

// try / catch -->setjmp/longjmp
#include <stdio.h>
#include <setjmp.h>

jmp_buf env;
int count = 0;



void sub_func(int idx) {
    printf("sub_func --> idx :%d\n", idx);
    longjmp(env, idx);          // throw idx
}

int main() {

    int idx = 0;
    count = setjmp(env);
    if (count == 0) {           // try
        printf("count : %d\n", count);
        sub_func(++idx);
    } else if (count == 1) {    // catch(1)
        printf("count : %d\n", count);
        sub_func(++idx);
    } else if (count == 2) {    // catch(2)
        printf("count : %d\n", count);
        sub_func(++idx);
    } else if (count == 3) {    // catch(3)
        printf("count : %d\n", count);
        sub_func(++idx);
    } else {                    // catch(...)
        printf("other count \n");
    }

    {                           // finally

    }
}

执行结果如下:

 

2. 可以将setjmp与longjmp的语句块定义成宏。这样可以写成类似以下:

 

// try / catch -->setjmp/longjmp
#include <stdio.h>
#include <setjmp.h>

typedef struct _Exception {
    jmp_buf env;
    int exceptype;

} Exception;

#define Try(excep) if ((excep.exceptype = setjmp(excep.env)) == 0)

#define Catch(excep, ExcepType) else if (excep.exceptype == ExcepType)

#define Throw(excep, ExcepType) longjmp(excep.env, ExcepType)

#define Finally


void throw_func(Exception ex, int idx) {
    printf("sub_func --> idx :%d\n", idx);
    Throw(ex, idx);
}

int main() {

    int idx = 0;
    Exception ex;
    Try(ex) {
        printf("count : %d\n", idx);
        throw_func(ex, ++idx);
    } Catch(ex, 1) {
        printf("count : %d\n", ex.exceptype);
    } Catch(ex, 2) {
        printf("count : %d\n", ex.exceptype);
    } Catch(ex, 3) {
        printf("count : %d\n", ex.exceptype);
    } Finally {
        printf("Finally\n");
    }
}

执行结果如下:

 

3. 如果是多线程或者多层嵌套的情况怎么办? 

1)可以看到在上面的代码中,将异常定义的“帧”数据结构,然后通过函数传入栈内的方法会增加编码复杂度和代码的耦合性,这种方法不可取。有什么办法解决呢?

可以将异常帧的数据结构定义为全局。

2)多层嵌套抛出异常的情况要怎么办?

可以将每一层的异常帧传入一个定义在全局的栈数据结构中,每进入一层“Try”语句块,就创建一个新的“帧”。后续代码每抛出一个异常,就可以在全局的异常帧中pop出栈顶的帧。

每个帧之间才用链表的方式来存储。这样就达到了不需要在所有内层嵌套函数中传递栈帧的操作。

3)多线程怎么办呢?

可以利用posix中的pthread_key_t数据结构,定义每个线程的局部存储。然后利用创建,设置与获取线程局部存储数据。

#include <pthread.h>

int pthread_key_create(pthread_key_t *key, void (*destructor)(void*));
int pthread_setspecific(pthread_key_t key,const void *pointer));
void *pthread_getspecific(pthread_key_t key);

基本使用方法是

使用 pthread_key_t 创建线程局部key并用pthread_key_create对齐初始化

后续每个线程通过pthread_setspecific与pthread_getspecific分别来设置与获取线程局部变量。

完整的测试代码如下:

// try / catch -->setjmp/longjmp
#include <stdio.h>
#include <setjmp.h>
#include <stdarg.h>
#include <string.h>

#include <pthread.h>

#define ThreadLocalData pthread_key_t
#define ThreadLocalDataSet(key, value) pthread_setspecific((key), (value))
#define ThreadLocalDataGet(key) pthread_getspecific((key))
#define ThreadLocalDataCreate(key) pthread_key_create(&(key), NULL)


#define EXCEPTION_MESSAGE_LENGTH        512

ThreadLocalData ExceptionStack;

// Exception Flags
enum {
    ExceptionEntered = 0,
    ExceptionThrown,
    ExceptionHandled,
    ExceptionFinalized
};

// Exception Type
typedef enum {
    ExceptionTypeA = 0,
    ExceptionTypeB,
    ExceptionTypeC,
    ExceptionTypeD
} ExceptionType;

char * exceptionTable[] = {
    "ExceptionTypeA",
    "ExceptionTypeB",
    "ExceptionTypeC",
    "ExceptionTypeD"
};

typedef struct _Exception {
    jmp_buf env;
    int line;
    const char * func;
    const char * file;
    ExceptionType exceptype;
    struct _Exception * prev;
    char msg[EXCEPTION_MESSAGE_LENGTH + 1];
} Exception;


#define ExceptionPopStack()     \
    ThreadLocalDataSet(ExceptionStack, ((Exception*) ThreadLocalDataGet(ExceptionStack))->prev)

#define ReThrow()				ExceptionThrow(excep.exceptype, excep.func, excep.file, excep.line, NULL)
#define Throw(e, cause, ...) 	ExceptionThrow((e), __func__, __FILE__, __LINE__, cause, ##__VA_ARGS__, NULL)

#define Try do {                           \
            Exception excep;            \
            excep.msg[0] = '\0';        \
            excep.prev = (Exception*)ThreadLocalDataGet(ExceptionStack);   \
            ThreadLocalDataSet(ExceptionStack, &excep);     \
            int Exception_flag = setjmp(excep.env);         \
            if (Exception_flag == ExceptionEntered) { //}

#define Catch(e)    \
            if (Exception_flag == ExceptionEntered) ExceptionPopStack();    \
        } else if (excep.exceptype == (e)) {   \
            Exception_flag = ExceptionHandled;

#define Finally     \
            if (Exception_flag == ExceptionEntered) ExceptionPopStack();    \
        }  {        \
            if (Exception_flag == ExceptionEntered) \
                Exception_flag = ExceptionFinalized;

#define EndTry      \
            if (Exception_flag == ExceptionEntered) ExceptionPopStack();    \
        } if (Exception_flag == ExceptionThrown)  ReThrow();  \
        } while (0)

//
void ExceptionThrow(ExceptionType e, const char * func, const char * file, int line, const char * cause, ...) {

    Exception * excep = (Exception *)ThreadLocalDataGet(ExceptionStack);
    va_list ap;

    if (excep) {
        
        va_start(ap, cause);
        vsnprintf(excep->msg, EXCEPTION_MESSAGE_LENGTH, cause, ap);
        va_end(ap);

        excep->exceptype = e;
        excep->func = func;
        excep->file = file;
        excep->line = line;

        ExceptionPopStack();
        longjmp(excep->env, ExceptionThrown);
    } else {
        char message[EXCEPTION_MESSAGE_LENGTH+1];

        va_start(ap, cause);
        vsnprintf(message, EXCEPTION_MESSAGE_LENGTH, cause, ap);
        va_end(ap);   

        printf("Unhandled %s: %s\n raised in %s at %s:%d\n",exceptionTable[e], message, func ? func : "?", file ? file : "?", line);     
    }   
}


static pthread_once_t once_control = PTHREAD_ONCE_INIT;

static void init_once(void) { 
	ThreadLocalDataCreate(ExceptionStack); 
}

void ExceptionInit(void) {
	pthread_once(&once_control, init_once);
}

#define THREADS     50

void *thread_func(void * args) {
    pthread_t selfid = pthread_self();

    // test different type exception with try catch.
    Try {
        Throw(ExceptionTypeA, "A");
    } Catch(ExceptionTypeA) {
        printf("Handled %s! %ld\n", exceptionTable[ExceptionTypeA], selfid);
    } EndTry;

    Try {
        Throw(ExceptionTypeB, "B");
    } Catch(ExceptionTypeB) {
        printf("Handled %s! %ld\n", exceptionTable[ExceptionTypeB], selfid);
    } EndTry;

    Try {
        Throw(ExceptionTypeC, "C");
    } Catch(ExceptionTypeC) {
        printf("Handled %s! %ld\n", exceptionTable[ExceptionTypeC], selfid);
    } EndTry;

    Try {
        Throw(ExceptionTypeD, "D");
    } Catch(ExceptionTypeD) {
        printf("Handled %s! %ld\n", exceptionTable[ExceptionTypeD], selfid);
    } EndTry;


    // test the control flow of try catch.
    Try {
        Throw(ExceptionTypeA, "A Again");
        Throw(ExceptionTypeB, "B Again");
        Throw(ExceptionTypeC, "C Again");
        Throw(ExceptionTypeD, "D Again");
    } Catch(ExceptionTypeA) {
        printf("Handled %s! %ld\n", exceptionTable[ExceptionTypeA], selfid);
    } Catch(ExceptionTypeB) {
        printf("Handled %s! %ld\n", exceptionTable[ExceptionTypeB], selfid);
    }  Catch(ExceptionTypeC) {
        printf("Handled %s! %ld\n", exceptionTable[ExceptionTypeC], selfid);
    } EndTry;

    // test unhandled exception.
    Try {
        Throw(ExceptionTypeD, "D");
    } Catch(ExceptionTypeA) {
        printf("Handled %s! %ld\n", exceptionTable[ExceptionTypeA], selfid);
    } Catch(ExceptionTypeB) {
        printf("Handled %s! %ld\n", exceptionTable[ExceptionTypeB], selfid);
    } Catch(ExceptionTypeC) {
        printf("Handled %s! %ld\n", exceptionTable[ExceptionTypeC], selfid);
    } EndTry;


}

int main() {

    ExceptionInit();

    printf("test single-thread exception!\n");
    thread_func(NULL);

    printf("test multi-thread exception!\n");

    int i = 0;
    pthread_t threads[THREADS];
    for(i = 0; i < THREADS; i++)
        pthread_create(&threads[i], NULL, thread_func, NULL);
    
    for(i = 0; i < THREADS; i++)
        pthread_join(threads[i], NULL);
    printf("test finished!\n");
    return 0;
}

测试结果如下:

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值