HOME> 世界杯比利时> C语言中Try/Catch的实现

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

int setjmp(jmp_buf env);

void longjmp(jmp_buf env, int val);

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

// try / catch -->setjmp/longjmp

#include

#include

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

#include

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

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

#include

#include

#include

#include

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

}

测试结果如下: