以c++的方式实现error类型的定义

一、背景

以前大量使用c语言进行嵌入式软件开发的时候,对于错误类型比较常见的方式大概有:

  • 使用define的宏定义
  • 使用enum的枚举

比如这里需要定义四个错误类型,成功,失败,严重错误,未定义错误。

如果使用宏定义的方式来定义,形如:

#define ERR_OK        0
#define ERR_FAIL      1
#define ERR_FATAL     2
#define ERR_UNDEFINED 3

如果使用枚举的方式来定义,形如:

typedef enum _eError
{
    ERR_Ok = 0,
    ERR_Fail,
    ERR_Fatal,
    ERR_Undefined,
}ErrorType;

从形式上来看,它们都非常的简洁轻量,但是在进行Debug的时候,如果输出的log中只是这些数值错误号的话,就非常的麻烦,还必须得时刻与定义的错误类型进行对比已确认到底是什么错误。

所以如果在输出log时,就能直接输出错误信息的字符串,那就会方便很多了。比如c语言当中的 errno就是错误号(数值),可以通过strerror(errno)的方式获取某错误号对应的错误信息的字符串。

当然如果我们自定义的错误类型也要实现这样的功能,那么除了以上的错误类型定义之外,还必须专门定制实现一些函数接口来让我们获取错误信息的字符串。

有时候,如果我们开发的软件是一个library的方式发布,很有可能我们会用到不同类别的错误类型,比如

  • 用于library接口的API的外部错误类型,供library的用户使用
  • 用于library内部的错误类型,供library的开发人员使用

这时,有可能需要开发两个获取错误信息的接口,也是比较麻烦的。

二、c++解决方案

如果你是像c一样使用c++的话,那这部分你就不需要看了,自行忽略吧,当我没写。

虽然c++被很多人说这样不好,那样不足,但不可否认,它还是有些好东西的。比如封装,重载等等。

这里我们需要做的就是使用c++的封装,重载等特性来实现错误类型的定义,并能方便的获取错误信息。

  • 首先定义错误类型的结构体:
struct ErrorType
{
    int code = 0;  /** error code( or number) */
    const char* desc = ""; /** the describe of error, aways not null */
};

你可能要说,你这里不也是像c一样使用字符串指针吗? 这里之所以要这样用,是为了节省空间,一个string对象的size有二三十个字节,略大了一点。

  • 其次是重载操作运算符,因为错误类型定义出来,其目的就是拿来使用的,赋值,对比等都是常用的操作
/** error type */
struct ErrorType
{
    int code = 0;  /** error code( or number) */
    const char* desc = ""; /** the describe of error, aways not null */

    ErrorType(int cd, const char* dsc)
        :code(cd), desc(dsc)
    {
    }

    inline bool operator==(const ErrorType& et) const
    {
        return (code == et.code);
    }

    inline bool operator!=(const ErrorType& et) const
    {
        return (code != et.code);
    }

    inline ErrorType& operator=(const ErrorType& et)
    {
        code = et.code;
        desc = et.desc;

        return *this;
    }


};

这里的对比包括了,相等和不相等两种,就以上实现绝大多数情况下就已经够用了。

2.1 用c++实现错误类型定义

还是以第一章为例,定义四种错误类型。 这里和第一章有一点不一样的是,声明是在头文件中进行的,但具体的定义是在cpp源文件中实现的。

先来看头文件(假设为:errorType.h)

/************************************************************************/
/** 这部分是为了兼容visual studio和gcc对于动态库类型导出修饰符 */
/** macro define for special tool chain. */
#if defined(__GNUC__) /** !!!for gcc */
////////////////////////////////////////////////////////////////////////////
#define G_GCC_VERSION(maj,min) \
        ((__GNUC__ > (maj)) || (__GNUC__ == (maj) && __GNUC_MINOR__ >= (min)))

/** define API export  macro */
#if G_GCC_VERSION(4,0) //for gcc(version >= 4.0)
#define G_DLL_EXPORT __attribute__((visibility("default")))
#else
#define G_DLL_EXPORT
#endif

#elif defined(_MSC_VER) /** !!!for visual studio */
////////////////////////////////////////////////////////////////////////////

/** auto define API export macro */
#if !defined(DLL_EXPORT) && defined(_WINDLL) //todo,process _USRDLL,_AFXDLL
#define DLL_EXPORT
#endif

#if defined (DLL_EXPORT)
#define G_DLL_EXPORT __declspec(dllexport)
#else
#define G_DLL_EXPORT __declspec(dllimport)

#endif

#else /** !!!for unknown tool chain */
///////////////////////////////////////////////////////////////////////////
#error "!!unspport this toolchain at now!!"
#endif /** !!! end of tool chain define */


/** general global variable decorate macro(for external reference ) */
#define G_VAR  extern G_DLL_EXPORT 

/** general const global variable decorate macro(for external reference ) */
#define G_CVAR G_VAR const 
/************************************************************************/

G_CVAR ErrorType ERR_OK;
G_CVAR ErrorType ERR_FAIL;
G_CVAR ErrorType ERR_FATAL;
G_CVAR ErrorType ERR_UNDEFINED;

源文件的具体实现(假设为:ErrorType.cpp):

G_CVAR ErrorType ERR_OK                         = { 0, "Success" }; 
G_CVAR ErrorType ERR_FAIL                       = { 1, "Fail" };
G_CVAR ErrorType ERR_FATAL                      = { 2, "Fatal" };
G_CVAR ErrorType ERR_UNDEFINED                  = { 3, "undefined" };

2.2 使用方式

这里用c++方式实现的错误类型定义和c语言的实现在使用方式上是差不多的。形如:

#include "ErrorType.h"


ErrorType func1()
{
    /** todo something */
    return ERR_OK;
}

ErrorType func2()
{
    ErrorType ret = ERR_OK;

    ret = func1();

    if (ret != ERR_OK)
    {
        /**这里就可以直接使用错误类型的 ‘desc’ 成员变量来获得错误信息的字符串,相当方便 */
        LOG("do func1 failed(%d, %s)\n", ret.code, ret.desc);
    }

    return ret;
}

猜你喜欢

转载自blog.csdn.net/xuanwolanxue/article/details/80583997