前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >try-catch 的实现

try-catch 的实现

原创
作者头像
ge3m0r
发布2024-06-01 12:04:09
810
发布2024-06-01 12:04:09

try-catch

基本所有的编程语言都会有异常捕捉的语法,try-catch 基本是所有编程语言都会有的信息,他会捕捉 try 中语法错误,如果存在语法错误就会执行 catch 的内容。

在上代码之前,我们首先需要确定,如果我们自己实现一个 try-catch 我们需要的是什么?

首先我们代码执行在我们看来是一行代码一行代码执行,操作系统看来就是一个线程,一个进程的执行,所以发生错误的时候,除了执行 catch 信息,我们需要让操作系统对线程或者进行进行操作。

怎么做呢?如果进行过代码调试,我们都会看到在代码执行到断点的时候发现,IDE 会返回进行堆栈调用以及各个变量的值,当然这个在我们的编程语言中有一个专门的术语叫做上下文信息。

线程数据键

在 c 语言中,我们对于线程操作除了多线程创建线程,设置回调函数,还可以对线程进行设置,例如线程数据键的设置。

代码语言:c
复制
/*
线程数据键是用来标识线程特定数据的访问和管理,他可以让每个线程都有自己的数据副本不会相互干扰
可以使用在线程隔离,数据独立,简化代码 
使用1、pthread_key_create 创建数据键
2、pthread_setspecific 设置特定数据
3、pthread_getspecific 获取特定数据    
数据键的数据类型是 pthread_key_t
内核层面:
内核层面依赖于 TLS (线程局部存储)机制
内核关键实现:
1、线程控制块 TCB 内核存储线程的所有信息
2、TLS 机制用于独立内存访问
3、硬件支持相关访问机制
用户可以通过 arch_prctl 进行系统调用,设置 TLS 基地址
使用 ARCH_SET_FS ARCH_SET_GS 设置 fs 或者 gs 基地址
*/
#include <pthread.h>
#define ntyThreadData pthread_key_t  //pthread_key_t 是用于标识线程特定数据键的数据类型。
#define ntyThreadDataSet(key,value)  pthread_setspecific((key), (value)) //pthread_setspecific 函数用于将特定的数据值与线程特定数据键相关联。
#define ntyThreadDataGet(key)  pthread_getspecific((key)) //pthread_getspecific 函数用于获取与线程特定数据键相关联的值。
#define ntyThreadDataCreate(key) pthread_key_create(&(key), NULL) //pthread_key_create 函数用于创建一个新的线程特定数据键

上述大概对于数据键信息进行了描述,简单来说就是对线程进行标记,然后希望线程之间进行隔离的机制。

上下文信息保存

c 语言使用了jmp_buf 是 c 语言中用于实现非本地跳转,setjmp 函数可以用于保存调用环境信息

longjmp 函数可以回复保存的调用环境

setjmp 和 longjmp 函数

setjmp 函数:

int setjmp(jmp_buf env);

该函数保存当前的调用环境到 env 中,并返回 0。当通过 longjmp 跳转到此环境时,setjmp 返回一个非零值。

longjmp 函数:

void longjmp(jmp_buf env, int val);

该函数恢复 env 保存的调用环境,并导致 setjmp 返回 val。如果 val 为 0,setjmp 返回 1。

这两个函数都保存在头文件#include <setjmp.h> 中。

代码实现

有了上述的背景铺垫,那么 try-catch 实现相对容易理解一点。

定义数据结构

代码语言:c
复制
//对不同异常类型定义
typedef struct _ntyException{
     const char* name;
} ntyException;

ntyException SQLException = {"SQLException"};
ntyException TimeoutException = {"TimeoutException"};
//声明了一个pthread_key_t 的线程数据键的类型
ntyThreadData ExceptionStack;

typedef struct _ntyExceptionFrame{
	jmp_buf env;  //用于保存环境调用信息
	int line;
	const char* func;  //回调函数
	const char* file;

	ntyException *exception; //异常的名字
	struct _ntyException *prev; //上一个异常的指针
	char message[ EXCEPTION_MESSAGE_LENGTH + 1]; //异常信息
}ntyExceptionFrame;

上述内容应该较为简单,就是设置异常的基本信息,回调函数加上异常本身的信息。

部分宏定义:

代码语言:c
复制
#define ntyExceptionPopStack \
	ntyThreadDataSet(ExceptionStack, (ntyExceptionFrame*)ntyThreadDataGet(ExceptionStack)->prev)  //将上一个异常的的值设置在 pthread_key_t 上

#define ReThrow   ntyExceptionThrow(frame.exception, frame.func, frame.file, frame.line, NULL) //抛出有上下文的异常
#define Throw(e, cause, ...) 	ntyExceptionThrow(&(e), __func__, __FILE__, __LINE__, cause, ##__VA_ARGS__, NULL) //多种类型异常抛出

enum {  //枚举异常
  ExceptionEntered = 0,
  ExceptionThrown,
  ExceptionHandled,
  ExceptionFinalized
};

定义异常抛出的函数。

代码语言:c
复制
void ntyExceptionThrow(ntyException* excep, const char* func, const char*file, int line, const char*cause, ...){
    va_list ap;  //不定参数的list
	ntyExceptionFrame *frame = (ntyExceptionFrame*)ntyThreadDataGet(ExceptionStack);  //获取特定线程数据值

	if(frame){
		frame->exception = excep; //将要抛出的各种参数设置在 frame 上
		frame->func = func;
	    frame->file = file;
		frame->line = line;

		if(cause){  //有错误原因
			va_start(ap, cause);
			vsnprintf(frame->message, EXCEPTION_MESSAGE_LENGTH, cause, ap); //输出
		    va_end(ap);
		}

		ntyExceptionPopStack;  //将异常出栈
		longjmp(frame->env, ExceptionThrown); //恢复上下文
	}else if(cause){  //各种 frame 信息不存在,但是有错误信息
		char message[EXCEPTION_MESSAGE_LENGTH + 1]; //只输出错误信息
		va_start(ap, cause);
		vsnprintf(message, EXCEPTION_MESSAGE_LENGTH, cause, ap);
		va_end(ap);

		printf("%s: %s\n raised in %s at %s:%d\n", excep->name, message, func ? func : "?", file ? file : "?", line);
		
	} else { //直接数据错误相关信息

		printf("%s: %p\n raised in %s at %s:%d\n", excep->name, excep, func ? func : "?", file ? file : "?", line);
		
	}
}

抛出异常已经实现,但是初始化这些内容也可以。

代码语言:c
复制
static pthread_once_t once_control = PTHREAD_ONCE_INIT;
//创建数据键
static void init_once(void){
	ntyThreadDataCreate(ExceptionStack);
}
//异常输出啊
static ntyExceptionInit(void){
   pthread_once(&once_control, init_once);
}

我们母亲把异常抛出所需要的机制定义好了,但是真正的机制实现我们需要是心啊 try 和 catch 能够使用这个逻辑,而 C 语言中,只有宏定义才能引入一个外部变量,所以宏定义如下:

代码语言:c
复制
#define Try do{  \
	volatile int Exception_flag; \  //不用优化定义一个int flag
	ntyExceptionFrame frame; \
	frame.message[0] = 0; \
	frame.prev = (ntyExceptionFrame*)ntyThreadDataGet  (ExceptionStack); \ //将frame 上一个指向为获取数据值
	ntyThreadDataSet(ExceptionStack, &frame); \
	Exception_flag = setjmp(frame.env); \ //保存上下文
	if(Exception_flag == ExceptionEntered){

#define Catch(e) \  //发生错误
	if(Exception_flag == ExceptionEntered) ntyExceptionPopStack; \ 
		}else if(frame.exception == &(e)){ \
		Exception_flag = ExceptionHandled;

#define Finally \
	if(Exception_flag == ExceptionEntered) ntyExceptionPopStack;\
			}{ \  //如果不是以上错误进入这个环节
			if(Exception_flag == ExceptionEntered) \
				Exception_flag = ExceptionFinalized;

#define EndTry \ //结束。
	if(Exception_flag == ExceptionEntered) ntyExceptionPopStack;\
		} if (Exception_flag == ExceptionThrown) ReThrow; \
        	} while (0)

最后就是这个使用。

代码语言:c
复制
ntyException A = {"AException"};
ntyException B = {"BException"};
ntyException C = {"CException"};
ntyException D = {"DException"};

void *thread(void *args){

     pthread_t selfid = pthread_self();

	 Try{
	 	Throw(A, "A");
	 } Catch(A){
	 	printf("catch A :%ld\n", selfid);
	 } EndTry;

	 
     Try {

		Throw(C, "C");
		
	 } Catch (C) {

		printf("catch C : %ld\n", selfid);
		
	 } EndTry;

	 Try {

		Throw(D, "D");
		
	 } Catch (D) {

		printf("catch D : %ld\n", selfid);
		
	 } EndTry;

	 Try {

		Throw(A, "A Again");
		Throw(B, "B Again");
		Throw(C, "C Again");
		Throw(D, "D Again");

	 } Catch (A) {

		printf("catch A again : %ld\n", selfid);
	
	 } Catch (B) {

		printf("catch B again : %ld\n", selfid);

	 } Catch (C) {

		printf("catch C again : %ld\n", selfid);
		
	 } Catch (D) {
	
		printf("catch B again : %ld\n", selfid);
		
	} EndTry;
	
}

如果要实验测试,将该函数中内容设置为 pthread 创建 的回调函数就可以。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • try-catch
  • 线程数据键
  • 上下文信息保存
  • 代码实现
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档