c++ 宏方法的理解

关于宏方法的理解

宏方法和

  1. 批量定义方法,和模板比较
  2. 校验,失败并返回
  3. 宏常量,和const常量比较

一、伪函数方法和模板方法

以下是open62541三方库的opcua server中的一段代码

#define UA_ENCODING_HELPERS(TYPE,UPCASE_TYPE)\static UA_INLINE size_t\UA_##TYPE##_calSizeBinary(const UA_##TYPE *src){\return UA_calSizeBinary(src, &UA_TYPES[UA_TYPES_##UPCASE_TYPE])\}\static UA_INLINE UA_StatusCode\UA_##TYPE##_encodeBinary(const UA_##TYPE *src, UA_Byte **bufpPos, const UA_Byte * bufEnd){\return UA_encodeBinaryInternal(src, &UA_TYPES[UA_TYPES_##UPCASE_TYPE],\bufPos,&bufEnd,NULL,NULL)\}\static UA_INLINE UA_StatusCode\UA_##TYPE##_decodeBinary(const UA_##TYPE *src, size_t *offset, UA_##TYPE *dst){\return UA_decodeBinaryInternal(src, offset,dst,\&UA_TYPES[UA_TYPES_##UPCASE_TYPE])\}UA_ENCODING_HELPERS(Boolean,BOOLEAN)
UA_ENCODING_HELPERS(SByte,SBYTE)

这个UA_ENCODING_HELPERS宏可以为boolean类型、SByte类型批量申明定义三个函数。

以boolean为例,生成了UA_Boolean_calSizeBinary、UA_Boolean_encodeBinary、UA_Boolean_DecodeBinary三个方法

以SByte为例,生成了UA_SByte_calSizeBinary、UA_SByte_encodeBinary、UA_SByten_DecodeBinary三个方法

这样做的好处是,

  • 可以为每个类型生成独属于自己计算二进制大小、加密、解密方法,
  • 另外可以减少代码量。
  • 调用处增加可读性

关于减少代码量,这里申明定义了两个类型的各自的三个方法,一共6个方法,宏方法14行,之后每增加一个类型的,只需要多谢一行代码,提高开发速度。如果不是使用这样的方法,需要写的代码量为类型数(n)*宏方法代码行数(m)。但是需要注意的点是,减少的只是开发者需要写的代码量,而对于编译器而言,代码量并没有减少,因为在预编译期,使用宏的地方会被替换。

这样使用是因为UA_calSizeBinary、UA_encodeBinaryInternal、UA_decodeBinaryInternal这三个内层调用的函数,并没有入参类型上的区别。如果在三个内层调用函数中,对于不同类型,也就是UA_TYPES有不同处理方式,那么这样使用确实很方便。但是这三个内层函数并没有根据UA_TYPES的类型不同做不同处理,就显得有点鸡肋。如果直接使用三个内层函数也可以做到一样的效果。所以这里这样使用只是增加了在调用这三个宏定义方法时的可读性。

和模板方法的不同

  • 宏在预处理器中被展开,模板方法在编译时被生成

  • 模板方法需要后去模板参数才能生成对应代码

  • 模板方法灵活性更好,对于需要特殊处理的模板参数,可以特例化模板,而宏需要使用if-else这类的手段

二、频繁校验时使用

在频繁需要进行校验,并且校验失败时直接退出函数时使用。

比如这样的场景,在使用proto数据时,有一个map<string,variant>,需要根据key(stirng)找到value(variant),然后需要校验variant是否是我们需要的类型比如stringvalue、boolvalue、int32value等,如果不等就退出函数,以及获取map中这个variant实际类型值

#define CHECK_RETURN(map,key,type,errorcode,getter,out)\auto iter_##type = map.find(key);\if(iter_##type == map.end()) return errorcode;\if(iter_##type->second.oneof_case() != type) return errorcode;\out = iter_##type->second.getter;\int32 startOpt(){...int32 id = 0;CHECK_RETURN(map,"id",int32,-10001,int32value(),id);string address;CHECK_RETURN(map,"address",string,-10002,stringvalue(),idaddress)return 0;
}

这样写在CHECK_RETURN中校验失败了,会直接返回错误码,结束startOpt函数,如果成功,会重新给id和address赋值。

如果是使用方法来校验

int32 checkAndReturn(mapType map,string key,Type type,Out& out)
{auto iter = map.find(key);if(iter == map.end()) return -10000;if(iter->second.oneof_case() != type) return -10000;out = iter->second.getter;return 0;
}

当在startOpt中用这个checkAndReturn替代CHECK_RETURN后,需要在startOpt再判断一次

int32 startOpt(){...int32 id = 0;auto ret = checkAndReturn(map,"id",int32,id);if(ret != 0) return -10001;string address;ret = checkAndReturn(map,"address",string,idaddress)if(ret != 0) return -10002;return 0;
}

这样会导致可读性变差,代码量增加,更加冗余。

三、宏变量

什么时候使用宏常量和什么时候使用const常量。个人认为使用宏常量的地方都可以替换为const常量,而且使用const常量更好。

#define PI 3.14159
和
const float PI = 3.14159
为例

以下是一些区别:

  1. 编译时处理:
    • #define PI 3.14159 是一个预处理指令,在编译前由预处理器进行处理。预处理器会将所有出现的 PI 字符串替换为 3.14159 字面量。
    • const float PI = 3.14159 是一个常量声明,会在编译时被编译器处理。编译器会为这个常量分配内存空间,并在链接时确定它的值。
  2. 类型检查:
    • 使用 #define 定义的 PI 是一个宏,没有任何类型信息。预处理器只是简单地进行文本替换。
    • 使用 const float PI = 3.14159 定义的 PI 是一个类型为 float 的常量。编译器可以进行类型检查,确保它在使用时类型正确。
  3. 作用域:
    • 使用 #define 定义的 PI 在预处理阶段就被替换了,它的作用域仅限于定义它的那个编译单元。
    • 使用 const float PI = 3.14159 定义的 PI 是一个全局变量,它的作用域取决于它被声明的位置。
  4. 调试信息:
    • 使用 #define 定义的 PI 在编译后的二进制文件中不再存在,因此很难在调试过程中查看它的值。
    • 使用 const float PI = 3.14159 定义的 PI 会保留在编译后的二进制文件中,可以在调试时查看它的值。

另外还有一个重要的地方——编译解耦

一般使用宏常量,会直接把宏写在.h文件中,我一般使用const会定义一个.h和.cpp文件。

//errorcode.cpp
const int64 NO_ID = -10001;
const int64 NO_NAME = -10002;//errorcode.h
extern const int64 NO_ID;
extern const int64 NO_NAME;

这样写会显得代码冗余,过多。但是如果这样写,不要errorcode.cpp文件,申明和定义都在errorcode.h文件中

//errorcode.h
const int64 NO_ID = -10001;
const int64 NO_NAME = -10002;

如果这个errorcode.h头文件被多个文件include,那么每次修改errorcode后(不增加和删除errorcode),都会在编译项目的时候,非常耗时。

而如果将其分开,每次errorcode.cpp文件的修改后再编译项目,就会编译很快

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

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章

CPU是如何执行任务的?

你清楚下面这几个问题吗&#xff1f; 有了内存&#xff0c;为什么还需要 CPU Cache&#xff1f; CPU 是怎么读写数据的&#xff1f; 如何让 CPU 能读取数据更快一些&#xff1f; CPU 伪共享是如何发生的&#xff1f;又该如何避免&#xff1f; CPU 是如何调度任务的&#x…

最短路径算法(Dijkstra算法 + Bellman-Ford 算法 + Floyd-Warshall算法)

最短路径算法 完整版万字原文见史上最全详解图数据结构 1. Dijkstra算法&#xff08;单源最短路径&#xff09;&#xff08;无负权边图&#xff09; 算法原理 1. Dijkstra 算法通过 贪心策略 计算从一个源顶点到其他所有 顶点的最短路径。2. 时间复杂度为 O(V^2)&#xff08…

pyqt6事件概要

例子&#xff1a; 利用qtdesigner建立闹钟 python代码 # 导入所需要的文件 from PyQt6.QtGui import QIcon, QPixmap from PyQt6.QtWidgets import QApplication, QMainWindow, QPushButton, QListWidgetItem from PyQt6 import uic from PyQt6.QtCore import Qt, QTime imp…

位运算符I^~

&运算&#xff1a;上下相等才是1&#xff0c;有一个不同就是0 |运算&#xff1a;只要有1返回的就是1 ^(亦或)运算&#xff1a;上下不同是1&#xff0c;相同是0 ~运算&#xff1a;非运算&#xff0c;与数据全相反 cpu核心运算原理&#xff0c;四种cpu底层小电路 例&#xf…

【求助】Tinymce组件异常

版本号 { "tinymce/tinymce-vue": "^3.0.1", "tinymce": "^5.10.9", "vue": "^2.6.10", }问题&#xff1a; 就是红框处点击后没有菜单出现&#xff0c;下面是正常的

大模型分布式训练框架——DeepSpeed

本文的主要内容是阐述DeepSpeed训练模块在跟进大模型技术中的作用&#xff0c;重点解析了RLHF在其中的应用。文中深入探讨了模型训练的关键概念&#xff0c;如显存需求、优化器迭代和混合精度训练。针对大规模模型训练&#xff0c;介绍了模型并行和流水线并行等分布式训练方法&…

跟着AI 学 AI, 开发一个ChatBot, 完整图文版__持续更新中

跟着AI 学 AI&#xff0c; 开发一个ChatBot, 完整图文版__持续更新中.记录步骤以便排查错误。 使用Python 加 Visual Studio Code&#xff0c;开发代码。 使用Flask 套件和 ngrok 工具。 Step 1 下载安装Python &#xff0c;下载完后 在CMD 测试 Python --version. 结果出现p…

Pyside6 --Qt Designer--Qt设计师--了解+运行ui_demo_1.py

目录 一、打开Qt设计师1.1 Terminal终端1.2 打开env&#xff0c;GUI虚拟环境下的scripts文件1.3 不常用文件介绍&#xff08;Scripts下面&#xff09; 二、了解Qt设计师的各个控件作用2.1 点击widget看看效果&#xff01;2.2 点击Main Window看看效果 三、编写一个简易的UI代码…

『大模型笔记』OpenAI 十二天活动第1天:o1和o1 pro

『大模型笔记』OpenAI 十二天活动第1天:o1和o1 pro 文章目录 一. 『大模型笔记』OpenAI 十二天活动第1天:o1和o1 proOpenAI的12天活动o1完整版本的发布o1 Pro模式ChatGPT Proo1的性能提升多模态输入与推理o1 Pro模式的应用模型对话与历史问题示范二. 参考文献一. 『大模型笔记…

SpringBoot 运行发生异常:java: 错误: 不支持发行版本 5

一、异常&#xff1a; 二、原因&#xff1a; 本地运行用的是JDK17&#xff0c;报错应该是项目编译配置使用的Java版本不对&#xff0c;需要检查一下项目及环境使用的Java编译版本配置。 三、解决&#xff1a;

2024.12.2——[极客大挑战 2019]Secret File 1

知识点&#xff1a;抓包 代码审计 filter伪协议 一、解题步骤 step 1 查看源代码中的信息 查看源代码发现一个php文件&#xff1a;[./Archive_room.php](http://72df1f22-85bf-47bb-b23a-efcaf88701d4.node5.buuoj.cn:81/Archive_room.php) 点进去后发现没什么用&#xff0c…

MKS EDGE Series RF Generators Power Solution 软件

MKS EDGE Series RF Generators Power Solution 软件

【汇编语言】标志寄存器(一) —— 标志寄存器中的标志位:ZF、PF、SF、CF、OF 一网打尽

前言 &#x1f4cc; 汇编语言是很多相关课程&#xff08;如数据结构、操作系统、微机原理&#xff09;的重要基础。但仅仅从课程的角度出发就太片面了&#xff0c;其实学习汇编语言可以深入理解计算机底层工作原理&#xff0c;提升代码效率&#xff0c;尤其在嵌入式系统和性能优…

【C++】priority_queue优先队列

大家好&#xff0c;我是苏貝&#xff0c;本篇博客带大家了解C的string类的priority_queue优先队列&#xff0c;如果你觉得我写的还不错的话&#xff0c;可以给我一个赞&#x1f44d;吗&#xff0c;感谢❤️ 目录 1. 介绍2. 仿函数(A) 介绍(B) 控制比较逻辑 3. priority_queue和…

Python3 operator 模块

Python2.x 版本中&#xff0c;使用 cmp() 函数来比较两个列表、数字或字符串等的大小关系。 Python 3.X 的版本中已经没有 cmp() 函数&#xff0c;如果你需要实现比较功能&#xff0c;需要引入 operator 模块&#xff0c;适合任何对象&#xff0c;包含的方法有&#xff1a; o…

短视频矩阵系统开发|技术源代码部署

短视频矩阵系统通过多账号运营管理、多平台视频智能分发等功能&#xff0c;助力企业实现视频引流、粉丝沉淀和转化。 短视频矩阵系统是一种创新的营销工具&#xff0c;它整合了多账号管理、视频智能分发、数据可视化等多种功能&#xff0c;为企业在短视频领域的发展提供了强大…

YOLOV11 快速使用教程

概述 这里主要记录使用NVIDIA GPU pytorch 检测系列模型的快速使用方式&#xff0c;可以快速解决一些工业应用的问题&#xff0c;比如&#xff1a;无网、数据大需要改路径、需要记录不同实验结果等问题。 安装 参考官网&#xff0c;自己安装好Python > 3.8和pytorch >…

git修改某次commit(白痴版)

第一步 在bash窗口运行 git rebase --interactive commitId^ 比如要改的commitId是 abcedf git rebase --interactive abcedf^键盘 按 i 或者 ins 进入编辑状态 进入insert 编辑状态 在bash窗口手动把对应commit前面的pick改为e或edit 按 esc 进入退出程序 输入 :wq 保存退出…

AI 建站:Durable

网址&#xff1a;https://app.durable.co 步骤 1) 登录 2&#xff09;点击创建新业务 3&#xff09;填写信息后&#xff0c;点击创建 4&#xff09;进入业务 5&#xff09;生成网站 6&#xff09;生成完成后不满意的话可以自己调整 7&#xff09;点击保存 8&#xff09;发布 …

网络原理之 TCP 协议

目录 1. TCP 协议格式 2. TCP 原理 (1) 确认应答 (2) 超时重传 (3) 连接管理 a) 三次握手 b) 四次挥手 (4) 滑动窗口 (5) 流量控制 (6) 拥塞控制 (7) 延时应答 (8) 捎带应答 3. TCP 特性 4. 异常情况的处理 1) 进程崩溃 2) 主机关机 (正常流程) 3) 主机掉电 (…