1、申 请 I D :fox95963
2、个人邮箱:yingxiang.huo@gmail.com
回想起来本人使用VC已经有好些年头了,风格是半吊子C+,(用class却少用诸如继承,try catch等)。最近有机会和一位写java的朋友一起合作做项目,发现java的try catch finally 在做文件操作的时候很好用--close放在finally中,不用担心忘记关闭。于是萌生了能否用C++来实现try catch finally的想法。要强调的是,并不是很推崇这样的做法,因为复杂的宏定义实际上不利于调试--如果挂在宏里面的话将会比较费神--而且可能平台迁移性不好,本文纯属娱乐折腾,练练手宏定义和内联汇编
简单而言就是实现对以下写法的支持:
[C++] 纯文本查看 复制代码 try_{
}catch_(int e){
}
....
catch_(...){
}finally_{
}
设计上主要有以下问题需要解决
1.我希望使用的时候严格按照上面的语法,而不用任何额外的语句辅助,但由于a{}b{}c{}这样的语句结构在C++语法中并没有直接支持,所以要考虑变通方法
2.宏定义中大概不能出现直接的jmp <标号>这样的内容,因为如果有形如try{}catch(e){}finally{};try{}catch(e){}finally{}这样的调用,这藏在宏定义里的标号就会重复
3.Release问题,这大概是最头痛的问题。C++和内联asm混编,如果asm中使用了过多的改动,例如如企图手动去平衡堆栈,debug的时候好好的,release的时候由于自动优化,就会有各种问题,比如,你预想会call的地方说不定优化成jmp。这样一来,debug能跑通的,release就会挂。所以需要注意处理。
4.如何在用户return前将代码劫持到finally,和在throw后劫持到finally
对于问题1
一眼看上去,可能if(a){}else if(b){}else{}这样的形式和try{}catch(e){}finally{}有点像,但是它们之间却有一个明显区别:try{}catch(e){}finally{}中各个大括号都有可能会进入,但是if(a){}else if(b){}else{}仅有一个大括号会进入。所以需要对其进行形如下面的改造:
for(int i=0;i<3;i++){
if(!i){}
else if(i==1){}
else{}
}
然而这样有一个问题,就是如红色部分所示,最终有两个右括号,这不符合finally{}这样的格式要求,所以--虽然不是习惯写法,但是上面其实可以改成如下形式:
for(int i=0;i<3;i++)
if(!i){}
else if(i==1){}
else{}
对于问题2
不单是标号,其实临时变量的定义也要小心,因为它们也有可能由于宏的重复调用而发生重定义。而这个问题,刚好能用1的方法解决:使用for-if-else if-else的形式进行跳转,而不用__asm{jmp}和goto,可以躲过标号重名,而将临时变量定义在for(int i=0;;)中,可以限制其作用域,让其不至于被其他地方误用。
对于问题3
一开始时候走了点弯路:
1)企图通过一些办法阻止优化,包括但不限于,调整编译器设置--关了优化的release没有灵魂
2)企图通过printf(),time(NULL)之类的操作,让编译器不能在编译时确定分支走向,从而阻止优化--效率和稳健性差,并且,有的时候编译器宁愿不print你的内容也要剪掉分支。失败告终
3)尽量多使用__asm,因为VC不会asm部分进行优化--有一定效果,但是跳转时由于标号问题貌似躲不过,所以又混进C,然后又被优化掉...
4)用#ifdef _DEBUG之类的来做调整,分别对debug和release做处理--吃力不讨好,可读性较差。
鉴于上面问题搞了我一整晚都没解决,天都要亮了,一怒之下将所有内容删掉了,推倒重试
最终得出解决方案: 将汇编部分压缩到最少,不得已才用,用假的递归调用来阻止编译器将我的某些函数内联
对于问题4
可简化为:如何能在函数退出前做额外的操作?答案是,借用变量的使用析构函数
假设在代码块里面定义了A a();那么在退出代码块的时候,~A()会被触发,无论是return退出,throw退出,还是自然退出,~A()都会被触发,因此可以在里面做点额外的操作.
直接上代码:
//头文件===============================================================================
[C++] 纯文本查看 复制代码 struct killer{
int i;
void* p[1];
bool (*kill)(void* p);
killer(void* p_,bool (*kill_)(void* p));
killer();
~killer();
};
void* getEip2();
#define RNDNAME aNUOgbujigYIKGOVYv4v1k08//避免名字碰撞
#define try_ for(killer kl##RNDNAME;kl##RNDNAME.i<1;kl##RNDNAME.i++)\
for(int k##RNDNAME=(kl##RNDNAME=killer(NULL,ThrowSpc)).i;k##RNDNAME<2;k##RNDNAME++)\
if(k##RNDNAME==1)\
try{
#define catch_(x) k##RNDNAME=-2;\
kl##RNDNAME.kill=0;\
*(kl##RNDNAME.p)=0;\
}catch(x){\
k##RNDNAME=-2;\
kl##RNDNAME.kill=0;\
*(kl##RNDNAME.p)=0;
#define finally_ \
}\
else\
for (int j##RNDNAME=-1;j##RNDNAME<1;j##RNDNAME++)\
if(j##RNDNAME==-1){\
if(k##RNDNAME==-1){\
k##RNDNAME=1;\
continue;\
}\
void* pos;\
__asm{__asm push 0}\
*kl##RNDNAME.p=(void*)getEip2();\
__asm{\
__asm mov eax,[esp]\
__asm mov j##RNDNAME,eax\
}\
if(!j##RNDNAME){\
__asm{__asm pop eax}\
break;\
}else{\
j##RNDNAME=-3;\
}\
continue;\
}else for(volatile int i##RNDNAME=j##RNDNAME;i##RNDNAME<1;i##RNDNAME++)\
if (i##RNDNAME-j##RNDNAME==1){\
__asm{__asm ret}\
}\
else
函数实现cpp:========================================================================================
[C++] 纯文本查看 复制代码 killer::killer(){
*p=0;
i=0;
kill=NULL;
}
killer::killer(void* p_,bool (*kill_)(void* p)){
i=0;
*p=p_;kill=kill_;
}
killer::~killer(){
if(*p==0&&kill==NULL){return;}
bool ans=true;
if(kill){
ans=kill(this);
//printf("kill %s\r\n",ans?"success":"failed");
}
}
void* getEip2(){
void* ans;
__asm{
mov eax,[ebp+4];
mov ans,eax;
pop edi
pop esi
pop ebx
mov esp,ebp
pop ebp
ret //←真正返回的位置!
}
return getEip2();//←防止release内联!!实际永远不会执行到这里
}
bool ThrowSpc(void* p_){
killer* kl=(killer*)p_;
void* p=kl->p[0];
kl->kill=0;
*kl->p=0;
if(p){
__asm sub esp,400
__asm call p;
__asm add esp,400
}
//throw SPC();
}
//测试用例===========================================================
[C++] 纯文本查看 复制代码 int test1(int cnt){
try_
{
printf("main\r\n");
//throw 0;
if(cnt<10){
int ret=test1(cnt+1);
printf("ret%d\r\n",ret);
}
if(rand()%2){
throw cnt;
}
return cnt;
}
catch_(int a)
{
printf("catch%d\r\n",a);
}
finally_{
printf("FINAL\r\n");
}
return 0;
}
int _tmain(int argc, _TCHAR* argv[])
{
test1(0);
return 0;
}
//运行结果:=============================================================
main
main
main
main
main
main
main
main
main
main
main
catch10
FINAL
ret0
catch9
FINAL
ret0
FINAL
ret8
FINAL
ret7
catch6
FINAL
ret0
FINAL
ret5
FINAL
ret4
FINAL
ret3
FINAL
ret2
FINAL
ret1
catch0
FINAL
写在最后:
这是第一次发帖,如果有不合要求处,望包含,敬请指正。说不定这个实现是一个笨办法,希望指出更好的办法。仍存一个小问题:现在若不写final,是编译不过的,可以改进。没经过完整测试,bug未知 |