1、  问题现象,设计背景 <?xml:namespace prefix = o ns = "urn:schemas-microsoft-com:office:office" />

在我们的软件设计过程中,一般都会设计函数的返回值,根据错误码来判断和定位异常分支的处理流程,为]进一步定位问题提供方便。
错误码的常用设计和管理方法,一般会按照全局设计,错误码中包含了模块号和错误类型,管理上会生成错误信息表,根据错误码可以从错误信息表中查出相应的错误的信息,很快定位问题。
错误码在函数之间传递通常有两种传递方式,我把他们分别定义为:
1) 连续传递

例如:
unsigned long FuncMain()
{
         ……
         ulRet = FuncSon();
         ifOK =  ulRet
         {
                return ulRet;
         }
         ……
}
从例子中可以看出,对于直接传递来看,子函数被调用出现异常后,在处理异常的时候直接将子函数的异常返回值作为主函数的返回值直接返回。

 

2) 间断传递

例如:
unsigned long FuncMain()
{
         ……
         ulRet = FuncSon();
         ifOK =  ulRet
         {
                return ERR_NUM_X;
         }
         ……
}
从例子中可以看出,对于间接传递来看,子函数被调用出现异常后,在处理异常的时候返回了另外定义的返回值ERR_NUM_X

 

3) 两种错误值返回方式的比较

在实际软件维护和开发过程中,经常会遇到这样的尴尬,虽然知道了某函数的返回值,如果是错误码的间接传递,我们无法从返回值来判断子函数的错误返回码;如果是错误码的直接传递,我们无法从返回码中看出传递路径,也就无法知道发生错误的根本错误分支在哪里。两种情况我们没办法很直接的发现问题的核心,只好通过加入大量的调试和打印信息,重新编译版本来定位问题,如果问题难以复现,就惨不忍睹了,在设计中使用断言可以查出问题所在,不幸的是,大多数人都不习惯使用断言,而且在使用大量的断言及函数间调用关系比较深的时候,铺天盖地的断言会使我们眼花潦乱,反而不利于定位问题。
2、  问题分析和处理

根据上面问题的分析,对于我们在设计错误码的时候需要考虑的问题:错误码要有针对性(模块化一一对应,一种错误码对应一种错误),可传承性强(可以包含相应的函数调用关系和信息)。
通过分析发现,通常用来传递错误码的函数返回值,长度一般为unsinged long类型,32bits,我们可以在错误码上做文章,使他携带更多的信息给我们。如果我们使用8bits来记录一层的函数的信息,那么32bits就可以记录4层函数的调用关系和错误码。
基于此想法,以函数为单位设计错误码,错误码从非0数字1开始,将主函数的返回值何调用函数的返回值拼在一起做为新的错误码返回。
例如:
unsigned long FuncMain()
{
         ……
         ulRet = FuncSon();
         ifOK =  ulRet
         {
                return BuildErrNum ( ERR_NUM_X , ulRet ) ;
         }
         ……
}
这样拼在一起实现了错误码的有效传递,根据顶层的错误码,可以找出调用关系和根本错误原因所在。

 

3、  代码上的具体实现

 

# define ERR_NUM_BITS_OFFSET   4

 

unsigned long BuildErrNum ( unsigned long ulErrNoAdd , unsigned long ulErrNoOld )
{
unsigned long ulErrNoNew ;
unsigned char aucMask[] = {0x1, 0x3 , 0x7 ,  0xf , 0x<?xml:namespace prefix = st1 ns = "urn:schemas-microsoft-com:office:smarttags" />1f ,  0x3f , 0x7f , 0xff};

 

ulErrNoNew = (ulErrNoOld << ERR_NUM_BITS_OFFSET) | (ulErrNoAdd & aucMask[ERR_NUM_BITS_OFFSET - 1] ) ;

 

return ulErrNoNew;
}
本人推荐使用4bit来记录信息,这样可以记录8层信息,对于4bit可以区分15种不同的返回码,这样基本上90%的函数不会超过15个不同的返回值,即便超过了也和容易通过4bit的信息找出错误分支所在。
对于宏定义ERR_NUM_BITS_OFFSET 是用来控制偏移信息的,需要程序员根据实际需要灵活使用。

 

4、  此设计使用局限

此设计方法虽然简单,实用,但对于某些场合并不适用;它要求:
1) 所有的函数返回值必须都是单纯的错误码,对于有意义的返回值,此设计方法不适
用,例如:申请资源,比较大小,算法实现等等;
2) 在同一函数中,不同的错误分支或者是不同的错误类型一定要通过不同的错误码加
以区分,否则不能记录调用栈信息。
    3希望具体使用者能将其进一步完善。