其实想法很简单拉,源于如下的尴尬,假如一个函数返回false,相应是否应该有错误说明呢?假如函数调用等次很深呢,是否应该层层返回false,那么应该在那一层说明错误情况呢,最适合的地方自然是发生错误的地方,只有在这个地方才能真正准确知道到底为什么会发生错误,然而除非规划得很严格,否则很有可能在外层产生说明,而外层只知道哪一个函数发生了错误,到底怎么发生的,恐怕就无能为力了,同样的情况出现在try...catch中,这是最典型的外层报错的情况,除非throw 一个 char*,说明到底为什么出错,假如内层函数返回一个字符串,说明错误原因,然而外层函数在设计时没有考虑到该情况,也返回了一个字符串,那将发生覆盖原始信息情况,而且,一个问题是,不可能每个函数都返回字符串的。
以下均以产生一个字符串报告错误给用户作为讨论基础:
发生错误了,应该如何处理?最简单莫过于弹出一个对话框,然后点击ok后返回false,然而这种方式我自己感觉缺点很大,在那里弹出对话框呢,最适合的地方自然是发生错误的地方,然而以上已讨论过,假如调用层次很深,控制又不怎么严格呢,很容易出现一个对话框弹出返回false后,在外层又弹出一个对话框的情况,而且,不断地AfxMessageBox()也不是然人看得很舒服,在假设,假如在循环中呢,对话框会中断循环直到点击ok才能继续,很让人上火。我同事使用一个其他公司授权使用的技术时,就发生了这种情况,接口在某种情况下竟然跳出一对话框,中断循环,而他的代码是在半夜无人的时候计算机循环处理作业,没办法还得覆盖该动态库调用MessageBox() API的地址。
以前我一直使用的是这样一种方法,定义一个全局缓冲区,每当有错的地方,我写错误信息到缓冲区然后返回false,在合适的地方检索该缓冲区,并提供给用户,如下:
CString g_strMsg;
void SetErrorMsg(CString strMsg){
g_strMsg = strMsg;
}
CString GetErrorMsg(bool bClear){
CString strMsg = g_strMsg;
if(bClear)
g_strMsg.Empty();
return strMsg;
}
然而我在使用时仍有问题,我有一个父类,属于过程类型的那种,就是父类定义了完成一个任务的操作步骤,通过调用虚函数返回的不同子类的信息达到不同的操作那种,在设计时考虑不周,调用一个虚函数后直接在父类内调用了SetErrorMsg(),说明是哪一个虚函数发生了错误,然而该子类实现该虚函数时,在若干个地方都返回了false,并调用了SetErrorMsg(),然而显示给用户的总是父类的粗糙的错误说明,自然假如设计时考虑到这问题就不会发生这种情况,然而谁能保证总能考虑到呢,还有,父类也不能假设子类一定提供错误说明阿,假如不提供,那提供什么给用户呢?
我昨天想到了Java和C#的异常堆栈跟踪机制,假如模拟一套这种机制,很显然可以解决这种尴尬,我的类定义如下:
#ifndef __TRACEAPI_H__
#define __TRACEAPI_H__
#pragma comment(lib,"trace.lib")
class CMessageManager{
CStringArray strArrMsg;
CStringArray& GetMsgArray();
public:
virtual void AddMsg(CString strMsg);
virtual void AddSysMsg();
virtual CString GetMsg();
virtual CString GetSysMsg();
virtual void Report();
virtual void Delete();
};
extern "C"{
CMessageManager* NewObject();
}
#define tracer CMessageManager
#endif
内部使用一个字符串数组保存各层报告的错误,AddMsg类似于SetErrorMsg,只不过内部调用CStringArray的Add方法将字符串加到数组末位,AddSysMsg()用于检索GetLastError() API 函数的错误代码解释,并将该字符串添加到数组末尾,GetMsg()用于返回所有的错误信息,而后清空数组,GetSysMsg()是对通过错误代码寻找windows标准解释的一个封装,Report()用于一个对话框打印错误信息,delete用于删除自己,因为包含一个默认的对话框,因此做成了一个DLL,之所以不导出类是因为想在Vc6 和 Vc7下都可用,只导出一个newObject函数,是因为还想使用类,于是模拟com的导出函数:)。
无论实现还是什么都很简单,抛砖引玉,呵呵