C/C++工程中错误码定义总结

本文结合实际经验,参考ros2 rclcpp库中错误码定义及使用方式,梳理了一种基于C或C++开发的接口下错误码的定义及处理方式。{本文不涉及跨系统、跨服务的定义}

1.错误码如何定义?

系统一般是按模块划分的,模块与模块之间通过调用与被调用的关系,一般也会划分为多个层次,底层一般对接系统级API或者实现一些算法,上层调用底层的接口来处理业务。那么这些错误码如何定义呢?
首先要明白错误码是什么:

在笔者看来,错误码分为通用的系统级错误码和业务错误码,系统级错误码一般包括指针为空、内存分配失败、无效参数、超时等等,业务错误码和具体的模块业务有关系。

我们一般会定义一个头文件,统一放这些错误码,当然错误码也是要按模块来进行划分的,这样既方便了错误码的追溯,又可以让不同的开发人员方便维护自己模块的错误码。比如下面这样:

/// 通用的错误码定义
/// 成功
#define ERR_OK 0
/// 超时错误
#define ERR_TIMEOUT 1
/// 内存分配失败
#define ERR_BAD_ALLOC 2// A模块,1xx
/// 错误1.
#define ERR_XXX 100
#define ERR_XYX 101// B模块,2xx
/// 错误2.
#define ERR_XXX2 200

【注意】某些模块可能是一些通用的模块,不想纳入某个系统的错误码定义约定中,比如某些通用的算法模块,那么此时错误码的定义可以单独进行定义,维护自己的一套规则即可。

2.错误码处理方法

错误码定义完了,如何处理这些错误码是很关键也很头疼的一件事。这里C和C++处理方式可以按C的方式处理,也可以用不同的方法处理。
上层对底层返回的错误码,一般会有两种方法来处理:

  1. 直接透传到上层
  2. 错误码收敛后返回到上层

2.1 C风格错误码分层处理方法

直接上代码:

#define OK 0
#define ERR_1 100
#define ERR_2 101
#define ERR_3 102int funTop(){return funMiddle(); //透传
}int funMiddle{int iRet = funBottom();if(iRet == ERR_1 || iRet == ERR_2){return ERR_2; //过滤转换}return OK;
}int funBottom{if (xx){return ERR_1;}else{return ERR_2;}if(yy){return ERR_3;}
}int main(){int iRet = funTop();
}

2.2 C++风格错误码分层处理方法

C++可以和C一样,也可以用try机制进行异常捕获的透传。
上代码:

class MyException : public std::exception{
//定义一个异常处理类,可以透传错误码MyException(int err_code);std::string what();int err_code();
}#define OK 0
#define ERR_1 100
#define ERR_2 101
#define ERR_3 102void funTop(){try{funMiddle();}catch(MyException e){//上层直接捕获到底层的错误码e.err_code();}
}void funMiddle{
//中间层不处理异常,扔给上层funBottom();
}void funBottom{if (xx){//底层抛出异常throw MyException(ERR_1);}else{throw MyException(ERR_2);}return OK;
}

2.3 C++调用C

当然,底层可以用C来封装库,上层用CPP再次封装,如rclc和rclcpp的关系。
此时,调用底层时可以通过throw来抛出异常。

class MyException : public std::exception{
//定义一个异常处理类,可以透传错误码MyException(int err_code);std::string what();int err_code();
}#define OK 0
#define ERR_1 100
#define ERR_2 101
#define ERR_3 102void funTop(){try{funMiddle();}catch(MyException e){e.err_code();}
}void funMiddle{int iRet = funBottom();if(iRet == ERR_1 || iRet == ERR_2){throw MyException(ERR_3); //抛出异常并收敛错误代码}
}int funBottom{if (xx){return ERR_1;}else{return ERR_2;}if(yy){return ERR_3;}
}

2.4 函数返回值注释

如果采用函数返回错误码的方式来实现,那么函数的注释中,一般会标明该函数返回的错误码。如果底层的错误码没被收敛,透传到上层,那么此时应该把所有的可能错误码包括底层的错误码都列上,这样其实特别繁琐。比如rmw中间件封装库中函数的声明:

/// Allocate a rmw_network_flow_endpoint_array_t instance
/*** \param[inout] network_flow_endpoint_array array to be allocated* \param[in] size size of the array to be allocated* \param[in] allocator the allcator for allocating memory* \returns `RMW_RET_OK` on successfull initilization, or* \returns `RMW_RET_INVALID_ARGUMENT` if `network_flow_endpoint_array` or `allocator` is NULL, or* \returns `RMW_RET_BAD_ALLOC` if memory allocation fails, or* \returns `RMW_RET_ERROR` when an unspecified error occurs.* \remark RMW error state is set on failure*/
RMW_PUBLIC
rmw_ret_t
rmw_network_flow_endpoint_array_init(rmw_network_flow_endpoint_array_t * network_flow_endpoint_array,size_t size,rcutils_allocator_t * allocator);

有没有简单的方法呢?有的,比如rclc的函数声明:

/***  Creates an rcl publisher with quality-of-service option best effort* \param[inout] publisher a zero_initialized rcl_publisher_t* \param[in] node the rcl node* \param[in] type_support the message data type* \param[in] topic_name the name of published topic* \return `RCL_RET_OK` if successful* \return `RCL_ERROR` (or other error code) if an error has occurred*/
RCLC_PUBLIC
rcl_ret_t
rclc_publisher_init_best_effort(rcl_publisher_t * publisher,const rcl_node_t * node,const rosidl_message_type_support_t * type_support,const char * topic_name);

这里直接用or other error code直接把其他错误码一起包含了,调用方只能按照true和flase的用法来用这个接口了,如果有些场景符合这种true或者false的使用,也未尝不可。
总之这两种方法各有利弊,重点是在使用时,某一层要统一一个规则。

3. 错误码追溯机制

层之间调用时,底层的接口可能会被多个中间层调用,底层产生错误时,可能有多种调用路径,那么如何跟踪这些调用路径,也就是说产生一个错误,如何知道错误是怎么一层层产生的呢?这个在某些时候还是非常重要的,如果只拿到底层的错误码,还是无法知道是哪个业务层调用时出的问题。
这里可以参考一下rcl的错误码生成机制,注意这里使用c实现的,如果用c++的异常来实现,可能需要某种特殊的办法来保存每一层的异常代码,否则中间层抛新的异常会把底层抛出的异常覆盖掉。
【核心定义】

//存储错误的结构
typedef struct
{//错误消息char chMsg[MAX_LEN]; //文件名char fileName[MAX_LEN];//行号int rowNum;
}TErrInfo;//全局错误定义
TErrInfo g_ErrInfo;//根据当前全局错误,拼接错误串
char* get_global_err_format_string(){//拼接 Msg fileName rowNum//比如:“bad malloce! pro/app/test.c linenum:555!”
}//重置错误信息
void reset_global_err(){//清空g_ErrInfo
}//设置错误信息
void set_err_info(Msg, fileName, rowNum){if (Msg != g_ErrInfo){//【关键处理1】如果错误信息和上次不同,则直接输出打印上次的错误printf("last err: %s", get_global_err_format_string());}//更新全局错误信息 (示意代码)g_ErrInfo.Msg = Msg; g_ErrInfo.fileName;g_ErrInfo.rowNum;
}

【如何使用】

//main中简单使用
int main(){set_err_info("aaa");//下面这句会打印上次的错误信息 aaaset_err_info("bbb");reset_global_err();set_err_info("ccc");//下面这些会在全局错误Msg中拼接错误调用栈信息//比如全局错误Msg执行结束后为:“ccc! pro/app/main.c lineNum:30 pro/app/main.c lineNum:31”set_err_info(get_global_err_format_string());set_err_info(get_global_err_format_string());}
//分层使用
#define OK 0
#define ERR_1 100
#define ERR_2 101
#define ERR_3 102int funTop(){int iRet = funMiddle();if ( iRet != OK){set_err_info(get_global_err_format_string());return iRet;}return OK;
}int funMiddle{int iRet = funBottom();if(iRet == ERR_1 || iRet == ERR_2){set_err_info(get_global_err_format_string());return ERR_2; //过滤转换}return OK;
}int funBottom{if (xx){set_err_info("ccc");return ERR_1;}else{set_err_info("ddd");return ERR_2;}if(yy){set_err_info("eee");return ERR_3;}
}int main(){int iRet = funTop();if ( iRet !=OK){//这里处理类似True和False,出错后直接把错误码和具体的错误调用栈信息写道日志中去LOG_ERR(iRet, get_global_err_format_string());return 0;}else{}
}

【总结】

最后的写日志,还是放到了上层中,底层并没有写日志的功能。
具体的错误码还是通过函数的返回值中得到的,错误的信息和错误栈可以从get_global_err_format_string()拿到,写道日志中。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/238194.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

PMP证书的PDU如何获得?

首先,让我们来了解一下PDU的含义。PDU代表专业发展单元(Professional Development Unit),是指在获得认证后,您可以通过学习、授课或提供志愿服务来积累专业项目管理领域的学习时间。PDU以小时为单位计算,每…

idea 远程调试linux上的代码

背景介绍 开发过程中,我们经常会遇到部署的代码运行出问题、看日志由不是很直观、我们希望可以像调试本地代码一样去调试远程代码; IDEA提供了Remote工具,基于JVM的跨平台能力,我们可以远程调试部署的代码。 前提 保证远程和本地跑的代码是一致的 操…

音视频类App广告变现如何破局,最大化广告变现收益,让应用增收?

音视频App已然成为了我们日常获取、发布和交换信息的重要方式,在音视频行业不断的拓展中,用户的渗透率提升。 据数据显示,我国网络视听用户的规模已达9亿人次,网民使用率也突破了90%。庞大的市场规模和用户需求吸引了大批开发者和…

GBASE南大通用数据库提供的高可用负载均衡功能

GBASE南大通用GBase 8a ODBC 提供的高可用负载均衡功能是指,GBase 8a ODBC 会将客户 端请求的数据库集群连接平均分摊到集群所有可用的节点上。 GBASE南大通用数据库负载均衡的使用方法 GBASE南大通用GBase 8a ODBC 提供两种方式来使用高可用负载均衡。一种是配置数…

4. 行为模式 - 中介者模式

亦称: 调解人、控制器、Intermediary、Controller、Mediator 意图 中介者模式是一种行为设计模式, 能让你减少对象之间混乱无序的依赖关系。 该模式会限制对象之间的直接交互, 迫使它们通过一个中介者对象进行合作。 问题 假如你有一个创建…

Linux ulimit配置

Linux ulimit配置 1. 简介 在Linux系统中,ulimit是一个强大的工具,用于控制用户进程可以使用的资源。然而,有时候我们可能会遇到需要关闭或者调整这些资源限制的情况,以满足特定需求。本文将介绍如何关闭Linux系统中的ulimit限制…

Java:打印当前线程的堆栈信息到错误流(error stream)

使用java.lang.Thread的静态方法dumpStack(),可以打印当前线程的堆栈信息到错误流(error stream)。 代码示例: package com.thb;public class Test5 {public static void main(String[] args) {Thread.dumpStack();}}运行输出&a…

Alpha突触核蛋白A53T 突变型PFF

Alpha 突触核蛋白A53T 突变型PFF Alpha 突触核蛋白 A53T PFFs (目录号 SPR-326) 在培养的原代大鼠神经元中诱导丝氨酸 129 磷酸化 培养基中 1 g/ml 超声处理的 StressMarq’s Alpha 突触核蛋白 A53T 突变 PFF(目录号 SPR-326) 可诱导原代大鼠神经元中 …

金融信贷场景的风险“要素”与主要“风险点”

目录 要素一:贷款对象 风险点1:为不具备主体资格或主体资格有瑕疵的借款人发放贷款 风险表现: 防控措施: 风险点2:向国家限控行业发放贷款 风险表现: 防控措施: 风险点3:受理不符合准入条件的客户申请 风险表现: 防控措施: 要素二:金额 风险点4:过渡授…

【已解决】使用fastjson返回给echarts的时候怎么不忽略null值?

问题复现: 在使用fastjson的将对象序列化成json字符串的时候,如果对象属性为null的话,会被忽略掉的。有时候,这些null值还是很有用的。比如我们在使用echarts的时候,返回给前端就不能因为null而忽略,如果忽…

二分查找法详解(6种变形)

前言 在之前的博客中,我给大家介绍了最基础的二分查找法(没学的话点我点我!) 今天我将带大家学习二分法的六种变形如何使用,小伙伴们,快来开始今天的学习吧! 文章目录 1,查找第一个…

求交错序列前N项和 C语言xdoj149

题目描述&#xff1a;编写程序&#xff0c;计算交错序列1-2/33/5-4/75/9-6/11…的前N项之和。 输入格式&#xff1a;输入一个正整数 输出格式&#xff1a;输出计算结果&#xff0c;结果保留三位小数 示例&#xff1a; 输入&#xff1a;5 输出&#xff1a;0.917 #include <st…

网络基础【网线的制作、OSI七层模型、集线器、交换机介绍、路由器的配置】

目录 一.网线的制作 1.1.网线的标准 1.2.水晶头的做法 二.OSI七层模型、集线器、交换机介绍 集线器&#xff08;Hub&#xff09;&#xff1a; 交换机&#xff08;Switch&#xff09;&#xff1a; 三.路由器的配置 3.1.使用 3.2.常用的功能介绍 1、如何管理路由器 2、家…

【Qt】tr(), qsTr()

1. 作用 Qt语言国际化&#xff0c;用户包裹需要翻译或转换的内容。 2. 区别 tr() : Qt C中使用qsTr() : QML中使用 3. 举例 3.1 Qt QString text tr("hello");3.2 QML Text {id: txt1;text: qsTr("Hello"); }

数据分析:小红书过节“仪式感”营销种草

导语 过年的氛围是越来越浓&#xff0c;走亲访友&#xff0c;过节送礼都准备起来&#xff01;据千瓜数据显示&#xff0c;“轻松买到仪式感”热度攀升&#xff0c;作为站内扶持的新兴话题&#xff0c;11月上线以来浏览量超2.5亿&#xff0c;笔记数超过20万篇。 看来&#xff…

Vue中转换HTML为PDF

1. 安装html2pdf.js库 npm install html2pdf.js2. 组件中引入html2pdf.js库 import html2pdf from html2pdf.js 3. 创建方法来处理HTML转PDF的逻辑 export default {methods: {convertHTMLToPDF() {const element document.getElementById(html-content); // 获取包含HTML…

Linux 磁盘读写io过高影响什么

当Linux系统的磁盘读写IO过高时&#xff0c;可能会对系统的整体性能和响应时间产生一定的影响&#xff0c;包括以下几个方面&#xff1a; 响应时间延迟&#xff1a;磁盘IO过高会导致IO请求积压&#xff0c;使得系统的响应时间变慢。这会影响到用户的交互体验&#xff0c;特别是…

yolov8改进-添加Wise-IoU,yolov8损失改进

1.在ultralytics/utils/metrics.py文件里找到 bbox_iou函数 注释整个函数 2.将注释的函数后面&#xff0c;去添加以下代码&#xff08;替换上面注释的函数&#xff09; class WIoU_Scale: monotonous: {None: origin v1True: monotonic FM v2False: non-monotonic FM v3}mo…

连续运行多个命令

; 如果需要连续运行多个命令&#xff0c;但是其中一些命令运行的时间比较长&#xff0c;而你不想长时间地守候在计算机旁&#xff0c;这个时候应该怎么办呢&#xff1f;例如&#xff0c;如果一个zip压缩文件中有很多John Coltrane的MP3文件&#xff0c;你想先解压缩&#xff0…

【K8s】2# 使用kuboard管理K8s集群(kuboard安装)

文章目录 安装 Kuboard v3部署计划 安装登录测试 安装 Kuboard v3 部署计划 在正式安装 kuboard v3 之前&#xff0c;需做好一个简单的部署计划的设计&#xff0c;在本例中&#xff0c;各组件之间的连接方式&#xff0c;如下图所示&#xff1a; 假设用户通过 http://外网IP:80…