Linux系统C/C++通用错误码实现模板

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://latelee.blog.csdn.net/article/details/82286517

背景

公司C++项目初期是安排不同的人编写不同的模块,有嵌入式ARM的,有socket协议的,有mysql的,有redis的,不同人风格不同。由于当时我还在运维小组搞docker,没参与规则的制定,后来接手时,开始确定编码规范,也建立了Git管理(Git尝试过sub modules,不过太过繁琐,最终弃用了),由于历史原因,后面要不断重构代码。
错误码大型项目中都会使用到,不同模块虽然有不同的错误返回值,但还是有必要建立一套基本的错误码使用系统,让大家都遵守。否则,错误码随便定义、使用,重构起来十分麻烦。
本文是在重构项目错误码之前进行的自测,与项目正式使用的代码有些许不同,但本质上是一致的。参考Linux内核和系统调用,确定了一些基本的通用类错误码。至于与业务有关的,则自由定制,对外形式、接口保持不同。

代码

闲话不提,直接上代码。

头文件

头文件my_errorcode.h如下:

/**
 * @file   my_errorcode.h
 * @author Late Lee
 * @date   2018.9.1
 *
 * @brief
 *         通用错误码定义及实现
 *
 * @note
 *         1、错误码从0开始计算,顺序添加。
 *         2、实际使用时,除E_OK外,错误码可以是负数(需要手工添加'-'),也可以是正数。
 *         3、错误码可以是任何数字,但不在枚举之列则显示未知错误码。
 *
 * @log
 *        
 */
#ifndef _MY_ERRORCODE_H
#define _MY_ERRORCODE_H

enum errorcode{
    E_OK = 0,         ///< 成功
    E_FAIL,           ///< 一般性错误
    E_INNER,          ///< 内部错误(一般在同一个模块内使用,不对外公开
    E_POINTER,        ///< 非法指针
    E_INVALARG,       ///< 非法参数
    E_NOTIMPL = 5,    ///< 功能未实现
    E_OUTOFMEM,       ///< 内存申请失败/内存不足
    E_BUFERROR,       ///< 缓冲区错误(不足,错乱)
    E_PERM,           ///< 权限不足,访问未授权的对象
    E_TIMEOUT,        ///< 超时
    E_NOTINIT = 10,        ///< 未初始化
    E_INITFAIL,       ///< 初始化失败
    E_ALREADY,        ///< 已初始化,已经在运行
    E_INPROGRESS,     ///< 已在运行、进行状态
    E_EXIST,          ///< 申请资源对象(如文件或目录)已存在
    E_NOTEXIST,       ///< 资源对象(如文件或目录)、命令、设备等不存在
    E_BUSY,           ///< 设备或资源忙(资源不可用)
    E_FULL,           ///< 设备/资源已满
    E_EMPTY,          ///< 对象/内存/内容为空
    E_OPENFAIL,       ///< 资源对象(如文件或目录、socket)打开失败
    E_READFAIL,       ///< 资源对象(如文件或目录、socket)读取、接收失败
    E_WRITEFAIL,      ///< 资源对象(如文件或目录、socket)写入、发送失败
    E_DELFAIL,        ///< 资源对象(如文件或目录、socket)删除、关闭失败
    E_CODECFAIL,      ///< 加解密、编码解密失败
    E_CRC_FAIL,       ///< crc校验错误
    E_TOOMANY,        ///< 消息、缓冲区、内容太多
    E_TOOSMALL,       ///< 消息、缓冲区、内容太少
    E_NETNOTREACH,    ///< 网络不可达(无路由,网关错误)
    E_NETDOWN,        ///< 网络不可用(断网)

    // more...

    E_END,            ///< 占位,无实际作用
};

const char* get_errorcode(int ec);
#endif

实现文件

my_errorcode.c文件内容,注意,使用2种方法实现:

#include "my_errorcode.h"

#if 01
///////////// 推荐方式
static const char* faults[] = {
    [E_OK] = "Success",
    [E_FAIL] = "General Failed",
    [E_INNER] = "Internal error",
    [E_POINTER] = "Invalid Pointer",
    [E_INVALARG] = "Invalid argument",
    [E_NOTIMPL] = "Not implemented",
    [E_OUTOFMEM] = "Out of memory",
    [E_BUFERROR] = "Buffer error",
    [E_PERM] = "Permission denied",
    [E_TIMEOUT] = "Timed out",
    [E_NOTINIT] = "Object not init",
    [E_INITFAIL] = "Object init failed",
    [E_ALREADY] = "Operation already in progress",
    [E_INPROGRESS] = "Operation now in progress",
    [E_EXIST] = "Object exist",
    [E_NOTEXIST] = "Object not exist",
    [E_BUSY] = "Device or resource busy",
    [E_FULL] = "Device or resource full",
    [E_EMPTY] = "Device or resource empty",
    [E_OPENFAIL] = "Device or resource open failed",
    [E_READFAIL] = "Device or resource read failed",
    [E_WRITEFAIL] = "Device or resource write failed",
    [E_DELFAIL] = "Device or resource delete failed",
    [E_CODECFAIL] = "Encode or decode failed",
    [E_CRC_FAIL] = "CRC failed",
    [E_TOOMANY] = "Object too many",
    [E_TOOSMALL] = "Object too small",
    [E_NETNOTREACH] = "Network is unreachable",
    [E_NETDOWN] = "Network is down",


    // more...
};

const char* get_errorcode(int ec)
{
    if (ec < 0) ec = -ec;
    printf("ec: %d\n", ec);
    if (ec < E_OK || ec >= E_END) return "Unknown error code";
    return faults[ec];
}

////////////////////////// 另一种方式实现
#else

typedef struct {
    int ec;
    const char *str;
} errorstring;

static const errorstring faults[] = {
    {E_OK, "Success"},
    {E_FAIL, "General Failed"},
    {E_INNER, "Internal error"},
    {E_POINTER, "Invalid Pointer"},
    {E_INVALARG, "Invalid argument"},
    {E_NOTIMPL, "Not implemented"},
    {E_OUTOFMEM, "Out of memory"},
    {E_BUFERROR, "Buffer error"},
    {E_PERM, "Permission denied"},
    {E_TIMEOUT, "Timed out"},
    {E_NOTINIT, "Object not init"},
    {E_INITFAIL, "Object init failed"},
    {E_ALREADY, "Operation already in progress"},
    {E_INPROGRESS, "Operation now in progress"},
    {E_EXIST, "Object exist"},
    {E_NOTEXIST, "Object not exist"},
    {E_BUSY, "Device or resource busy"},
    {E_FULL, "Device or resource full"},
    {E_EMPTY, "Device or resource empty"},
    {E_OPENFAIL, "Device or resource open failed"},
    {E_READFAIL, "Device or resource read failed"},
    {E_WRITEFAIL, "Device or resource write failed"},
    {E_DELFAIL, "Device or resource delete failed"},
    {E_CODECFAIL, "Encode or decode failed"},
    {E_CRC_FAIL, "CRC failed"},
    {E_TOOMANY, "Object too many"},
    {E_TOOSMALL, "Object too small"},
    {E_NETNOTREACH, "Network is unreachable"},
    {E_NETDOWN, "Network is down"},
};


const char* get_errorcode(int ec)
{
    if (ec < 0) ec = -ec;

    for (unsigned int i = 0; i < sizeof(faults)/sizeof(faults[0]); i++)
        if (faults[i].ec == ec)
            return faults[i].str;

    return "Error code unknown";
}
#endif

测试代码

main.c文件内容如下:

/**
打印结果:
Success
Error code unknown
-6(Out of memory)
9(Timed out)
*/

#include <stdlib.h>
#include <stdio.h>

#include "my_errorcode.h"

int foo()
{
    return -E_OUTOFMEM;
}

int bar()
{
    return E_TIMEOUT;
}

int main(void)
{
    printf("%s\n", get_errorcode(0));
    printf("%s\n", get_errorcode(-250));

    int ret = foo();
    printf("%d(%s)\n",  ret, get_errorcode(ret));

    ret = bar();
    printf("%d(%s)\n",  ret, get_errorcode(ret));

    return 0;
}

设计理念

错误码使用枚举类型定义,方便后续的添加,注意,枚举类型默认是累积1的。
另外,如果不想这样设计,也可以使用:

#define E_OK                   0   ///< 成功

这种宏定义。

如果想看到具体的错误码数值,则要在枚举后面手动添加数字。
错误码0表示成功,其它值表示失败,一般使用负数表示,本文所示代码,在实际返回时需要添加负号-,如return -E_OUTOFMEM;。当然,如果不要这么麻烦,就直接返回,那么其值就是正数。但是,为了兼容,也为了枚举类型的定义,获取错误码信息时需要将负数转成正数。

至于获取错误码信息get_errorcode的实现,则使用了GNU C编译器的扩展语法,从代码编写看十分简洁,不过,这个文件必须使用c语法,即gcc编译,g++编译会失败。

小结

通过上面的实现,可以得到一套基本满足应用的错误码模板。本文仅定义通用的错误码,根据需求添加业务相关错误码即可。

李迟 2018.9.1 深夜,与博导、蔡总、刘总夜宵回来

展开阅读全文

没有更多推荐了,返回首页