程序中的错误处理方法

概述

在C/C++程序中,设定了三种处理程序错误/异常的方法,来表达程序出错了,它们在OpenCV库中也有体现。

以下代码在OpenCV 2.4.4g++ 4.6.3编译通过。

C/C++

主要参照C++参考中对各标准库的文字解释和示例代码,完成下述内容。

errno

“errno.h”/“cerrno”头文件中定义了int类型的全局变量errno,初始值是0,所有C/C++标准库函数都有可能将值修改为非0,表示程序出错了。errno会被重复修改,值是最后一次修改的结果,也就表达了最后一次错误的内容。

errno不仅会被标准库函数修改,也能被用户程序修改。一般用来检查标准库函数是否出错,建议的使用方法是:在使用之前先置0,然后调用库函数,接着检查该值。

一般来说,不仅需要检测到错误,还需要将错误提示出来。这就需要用到另外两个标准库函数。

对errno的调用是隐藏起来的,所以不用显式声明库的头文件。

 1 #include <cstdio>
 2 
 3 int main(int argc, char **argv) {
 4     FILE *pFile = fopen("unexist.txt", "rb");
 5     if (NULL == pFile) {
 6         perror("Error");
 7     } else {
 8         fclose(pFile);
 9     }
10     return 0;
11 }

运行结果:

1 Error: No such file or directory

其中,“: No such file or directory”是函数“perror()”添加的。

对errno的调用是显式的,所以需要声明库的头文件。

 1 #include <cerrno>
 2 #include <cstring>
 3 #include <cstdio>
 4 #include <iostream>
 5 
 6 int main(int argc, char **argv) {
 7     FILE *pFile = fopen("unexist.txt", "rb");
 8     if (NULL == pFile) {
 9         std::cout << "Error: " << strerror(errno) << std::endl;
10     } else {
11         fclose(pFile);
12     }
13     return 0;
14 }

运行结果:

1 Error: No such file or directory

其中,“No such file or directory”是函数“strerror()”输出的。

assert

“assert.h”/“cassert”头文件中定义了宏函数 void assert(int expression) 如果表达式“expression”的值为0,则触发错误,在标准错误设备(stderr)上输出一条消息,并立刻调用“abort()”函数,退出程序。

输出内容依赖于库的实现,但一般会包含表达式、源文件名称、行号。

设计该宏函数是为了捕捉编程错误,而不是用户输入错误或者程序运行时错误。当程序依照Release模式编译时,通常需要提前先定义“NDEBUG”宏,则代码中的assert语句将不起作用。

1 //#define NDEBUG
2 #include <cassert>
3 #include <iostream>
4 
5 int main(int argc, char **argv) {
6     const size_t a = 10;
7     assert(a == 100);
8     return 0;
9 }

运行结果:

1 test: test.cpp:7: int main(int, char**): Assertion `a == 100' failed.
signal

“signal.h”/“csignal”头文件,设计主要用来捕捉程序运行中的信号,包括(担心翻译出来不准确,就直接拷贝过来了):

大概流程是:利用“raise()”函数触发信号,或者程序运行中某种事件触发信号,程序立即进入通过“signal()”函数注册的对应的处理函数,然后程序回来继续运行。

这方面网上详细资料很多,就不多解释了。下面例子是利用SIGINT信号修改signaled值。这里利用了“raise()”函数。

 1 #include <csignal>
 2 #include <iostream>
 3 
 4 sig_atomic_t signaled = 0;
 5 
 6 void my_handler(int param) {
 7     std::cout << "In my_handler() function" << std::endl;
 8     signaled = 1;
 9 }
10 
11 int main(int argc, char **argv) {
12     void (*handler)(int);
13     handler = signal(SIGINT, my_handler);
14     std::cout << "Wait for SIGINT." << std::endl;
15     raise(SIGINT);
16     std::cout << "Continue. Exit." << std::endl;
17     return 0;
18 }

特别指出,在my_handler()中调用标准输入输出函数是不建议的,不应该的,不合适的,应当避免,这里仅仅为了让执行顺序更清晰。请一定注意!

运行结果:

1 Wait for SIGINT.
2 In my_handler() function
3 Continue. Exit.

利用键盘触发:

 1 #include <csignal>
 2 #include <iostream>
 3 
 4 sig_atomic_t signaled = 0;
 5 
 6 void my_handler(int param) {
 7     std::cout << "In my_handler() function" << std::endl;
 8     signaled = 1;
 9 }
10 
11 int main(int argc, char **argv) {
12     void (*handler)(int);
13     handler = signal(SIGINT, my_handler);
14     std::cout << "Wait for SIGINT." << std::endl;
15     while (!signaled);
16     std::cout << "Continue. Exit." << std::endl;
17     return 0;
18 }

运行结果:

1 Wait for SIGINT.
2 ^CIn my_handler() function    <---这里按下了ctrl-c组合键。
3 Continue. Exit.
exception

“exception”头文件定义了基本的异常类和几个异常处理函数。“stdexcept”头文件定义了两个派生类,分别是逻辑错误和运行时错误。“fenv.h”/“cfenv”头文件定义了跟浮点数有关的异常,但该头文件是新的C++标准才有的(C++ 11),所以暂时不讨论。

异常的继承关系(不包括C++ 11的内容):

exception
    |-- bad_alloc
    |-- bad_cast
    |-- bad_exception
    |-- bad_typeid
    |-- ios_base::failure
    |-- logic_error
    |   |-- domain_error
    |   |-- invalid_error
    |   |-- length_error
    |   |-- out_of_range
    |-- runtime_error
        |-- range_error
        |-- overflow_error
        |-- underflow_error

这方面网上详细资料很多,就不多解释了。

OpenCV

主要参照OpenCV参考手册中的文字解释和示例代码,使用C++语法,完成下述内容。

CV_Assert

CV_Assert与前面的assert基本一致。当条件为0的时候,自动触发(raise)一个错误(下面要讲的error)。该表达式在Debug和Release两种编译模式下都有效。另一个CV_DbgAssert仅在Debug编译模式有效。

1 #include <opencv2/opencv.hpp>
2 
3 int main(int argc, char **argv) {
4     CV_DbgAssert(3 == 2);
5     CV_Assert(1 == 0);
6     return 0;
7 }

Debug模式编译,运行结果:

1 OpenCV Error: Assertion failed (3 == 2) in main, file test.cpp, line 4
2 terminate called after throwing an instance of 'cv::Exception'
3   what():  test.cpp:197: error: (-215) 3 == 2 in function main

Release模式编译,运行结果:

1 OpenCV Error: Assertion failed (1 == 0) in main, file test.cpp, line 5
2 terminate called after throwing an instance of 'cv::Exception'
3   what():  test.cpp:198: error: (-215) 1 == 0 in function main
error

具体有三种调用方式。

函数“error()”,参数是一个cv::Exception对象。

1 #include <opencv2/opencv.hpp>
2 
3 int main(int argc, char **argv) {
4     error(cv::Exception(10, "Error description", "main", "test.cpp", 4));
5     return 0;
6 }

运行结果:

1 OpenCV Error: Unknown status code 10 (Error description) in main, file test.cpp, line 4
2 terminate called after throwing an instance of 'cv::Exception'
3   what():  test.cpp:4: error: (10) Error description in function main

函数“CV_Error(error_code, error_msg)”,参数是错误代码和错误描述。

1 #include <opencv2/opencv.hpp>
2 
3 int main(int argc, char **argv) {
4     CV_Error(10, "Error description.");
5     return 0;
6 }

运行结果:

1 OpenCV Error: Unknown status code 10 (Error description.) in main, file test.cpp, line 4
2 terminate called after throwing an instance of 'cv::Exception'
3   what():  test.cpp:4: error: (10) Error description. in function main

函数“CV_Error_(error_code, (args))”,参数是错误代码和错误描述,但错误描述支持动态生成。

1 #include <opencv2/opencv.hpp>
2 
3 int main(int argc, char **argv) {
4     size_t a = 0;
5     size_t b = 255;
6     CV_Error_(10, ("value a = %d, b = %d", a, b));
7     return 0;
8 }

运行结果:

1 OpenCV Error: Unknown status code 10 (value a = 0, b = 255) in main, file test.cpp, line 6
2 terminate called after throwing an instance of 'cv::Exception'
3   what():  test.cpp:6: error: (10) value a = 0, b = 255 in function main

OpenCV内定了如下错误代码,都是int类型。

 1 typedef int CVStatus;
 2 
 3 enum {
 4  CV_StsOk=                       0,  /* everithing is ok                */
 5  CV_StsBackTrace=               -1,  /* pseudo error for back trace     */
 6  CV_StsError=                   -2,  /* unknown /unspecified error      */
 7  CV_StsInternal=                -3,  /* internal error (bad state)      */
 8  CV_StsNoMem=                   -4,  /* insufficient memory             */
 9  CV_StsBadArg=                  -5,  /* function arg/param is bad       */
10  CV_StsBadFunc=                 -6,  /* unsupported function            */
11  CV_StsNoConv=                  -7,  /* iter. didn't converge           */
12  CV_StsAutoTrace=               -8,  /* tracing                         */
13  CV_HeaderIsNull=               -9,  /* image header is NULL            */
14  CV_BadImageSize=              -10, /* image size is invalid           */
15  CV_BadOffset=                 -11, /* offset is invalid               */
16  CV_BadDataPtr=                -12, /**/
17  CV_BadStep=                   -13, /**/
18  CV_BadModelOrChSeq=           -14, /**/
19  CV_BadNumChannels=            -15, /**/
20  CV_BadNumChannel1U=           -16, /**/
21  CV_BadDepth=                  -17, /**/
22  CV_BadAlphaChannel=           -18, /**/
23  CV_BadOrder=                  -19, /**/
24  CV_BadOrigin=                 -20, /**/
25  CV_BadAlign=                  -21, /**/
26  CV_BadCallBack=               -22, /**/
27  CV_BadTileSize=               -23, /**/
28  CV_BadCOI=                    -24, /**/
29  CV_BadROISize=                -25, /**/
30  CV_MaskIsTiled=               -26, /**/
31  CV_StsNullPtr=                -27, /* null pointer */
32  CV_StsVecLengthErr=           -28, /* incorrect vector length */
33  CV_StsFilterStructContentErr= -29, /* incorr. filter structure content */
34  CV_StsKernelStructContentErr= -30, /* incorr. transform kernel content */
35  CV_StsFilterOffsetErr=        -31, /* incorrect filter ofset value */
36  CV_StsBadSize=                -201, /* the input/output structure size is incorrect  */
37  CV_StsDivByZero=              -202, /* division by zero */
38  CV_StsInplaceNotSupported=    -203, /* in-place operation is not supported */
39  CV_StsObjectNotFound=         -204, /* request can't be completed */
40  CV_StsUnmatchedFormats=       -205, /* formats of input/output arrays differ */
41  CV_StsBadFlag=                -206, /* flag is wrong or not supported */
42  CV_StsBadPoint=               -207, /* bad CvPoint */
43  CV_StsBadMask=                -208, /* bad format of mask (neither 8uC1 nor 8sC1)*/
44  CV_StsUnmatchedSizes=         -209, /* sizes of input/output structures do not match */
45  CV_StsUnsupportedFormat=      -210, /* the data format/type is not supported by the function*/
46  CV_StsOutOfRange=             -211, /* some of parameters are out of range */
47  CV_StsParseError=             -212, /* invalid syntax/structure of the parsed file */
48  CV_StsNotImplemented=         -213, /* the requested function/feature is not implemented */
49  CV_StsBadMemBlock=            -214, /* an allocated block has been corrupted */
50  CV_StsAssert=                 -215, /* assertion failed */
51  CV_GpuNotSupported=           -216,
52  CV_GpuApiCallError=           -217,
53  CV_OpenGlNotSupported=        -218,
54  CV_OpenGlApiCallError=        -219
55 };
Exception

OpenCV中定义的标准异常类型。构造函数主要会用到如下形式:

1 Exception(int _code, const string& _err, const string& _func, const string& _file, int _line);

可以用做函数“error()”的参数,也可以在try中作为throw的对象,然后被catch。

 1 #include <opencv2/opencv.hpp>
 2 
 3 int main(int argc, char **argv) {
 4     try {
 5         throw cv::Exception(10, "Error description.", "main", "test.cpp", 5);
 6     } catch (cv::Exception &ex) {
 7         std::cout << ex.what() << std::endl;
 8     }
 9     return 0;
10 }

运行结果:

1 test.cpp:5: error: (10) Error description. in function main