chrome-base源码分析(1)macros模块
Chrome-base源码分析(2)之Macros模块
Author:Once Day Date:2024年6月29日
漫漫长路,才刚刚开始…
全系列文章请查看专栏: 源码分析_Once-Day的博客-CSDN博客
参考文档:
- macros - Chromium Code Search
- Chrome base 库详解:工具类和常用类库_base::repeatingcallback-CSDN博客
- For Developers (chromium.org)
- 手动代码中包含C++20 __VA_OPT__错误的类函数宏-腾讯云开发者社区-腾讯云 (tencent.com)
文章目录
- Chrome-base源码分析(2)之Macros模块
- 1. 概述
- 2. concat.h
- 3. if.h
- 4. is_empty.h
- 5. remove_parens.h
1. 概述
chrome-base源码中macros模块是一个比较简单的模块,定义了一些简单的宏,有五个源文件,下面一一介绍。
2. concat.h
源码如下:
// A macro that expands to the concatenation of its arguments. If the arguments
// are themselves macros, they are first expanded (due to the indirection
// through a second macro). This can be used to construct tokens.
#define BASE_CONCAT(a, b) BASE_INTERNAL_CONCAT(a, b)// Implementation details: do not use directly.
#define BASE_INTERNAL_CONCAT(a, b) a##b
这个非常基础的关键字拼接宏,例如下面所示:
TEST(MacrosTest, Concat) {auto a = BASE_CONCAT(10, 20);std::cout << "a: " << a << std::endl;EXPECT_EQ(a, 1020);auto b = BASE_CONCAT(5000, 6000);std::cout << "b: " << b << std::endl;EXPECT_EQ(b, 50006000);
}
>>> a: 1020
>>> b: 50006000
BASE_CONCAT(10, 20)会输出1020,这是可以作为源码字面量的值,并不是"10" + "20" = "1020"这种字符串拼接。
3. if.h
源码如下:
// Given a `_Cond` that evaluates to exactly 0 or 1, this macro evaluates to
// either the `_Then` or `_Else` args. Unlike a real conditional expression,
// this does not support conditions other than `0` and `1`.
#define BASE_IF(_Cond, _Then, _Else) \BASE_CONCAT(BASE_INTERNAL_IF_, _Cond)(_Then, _Else)// Implementation details: do not use directly.
#define BASE_INTERNAL_IF_1(_Then, _Else) _Then
#define BASE_INTERNAL_IF_0(_Then, _Else) _Else
这段代码定义了一个名为BASE_IF的宏,用于实现编译期的条件选择功能。
宏接受三个参数:_Cond、_Then和_Else。_Cond必须是一个计算结果为0或1的表达式。
根据_Cond的值,宏会将其展开为_Then或_Else参数的内容。
宏的实现依赖于两个内部宏BASE_INTERNAL_IF_1和BASE_INTERNAL_IF_0,它们分别选择_Then和_Else参数。
通过巧妙的宏拼接,BASE_IF能够在编译期根据条件选择代码,而不会产生运行时开销。
例如下面所示:
TEST(MacrosTest, If) {auto a = BASE_IF(1, 10, 20);std::cout << "a: " << a << std::endl;EXPECT_EQ(a, 10);auto b = BASE_IF(0, 100, 200);std::cout << "b: " << b << std::endl;EXPECT_EQ(b, 200);
}
>>> a: 10
>>> b: 200
4. is_empty.h
源码如下:
// A macro that substitutes with 1 if called without arguments, otherwise 0.
#define BASE_IS_EMPTY(...) BASE_INTERNAL_IS_EMPTY_EXPANDED(__VA_ARGS__)
#define BASE_INTERNAL_IS_EMPTY_EXPANDED(...) \BASE_INTERNAL_IS_EMPTY_INNER(_, ##__VA_ARGS__)
#define BASE_INTERNAL_IS_EMPTY_INNER(...) \BASE_INTERNAL_IS_EMPTY_INNER_EXPANDED(__VA_ARGS__, 0, 1)
#define BASE_INTERNAL_IS_EMPTY_INNER_EXPANDED(e0, e1, is_empty, ...) is_empty
这段代码定义了一个名为BASE_IS_EMPTY的宏,用于检查宏是否被传递了参数。如果宏调用时没有传递任何参数,则展开为1,否则展开为0。
宏的实现依赖于几个内部宏:
- BASE_INTERNAL_IS_EMPTY_EXPANDED: 对传入的参数进行展开,并在前面添加一个下划线。
- BASE_INTERNAL_IS_EMPTY_INNER: 在展开后的参数列表前添加固定的参数。
- BASE_INTERNAL_IS_EMPTY_INNER_EXPANDED: 根据参数个数选择结果,如果只有固定参数则说明原宏调用时没传参数,返回1,否则返回0。
通过这种巧妙的宏展开和参数匹配,BASE_IS_EMPTY能够在编译期判断宏调用时是否传递了参数。下面是一些示例:
BASE_IS_EMPTY() // 展开为 1
BASE_IS_EMPTY(a) // 展开为 0
BASE_IS_EMPTY(a, b) // 展开为 0
这个宏常用于其他宏定义中,用于根据传参情况生成不同的代码,或者进行静态断言检查宏参数等。比如可以写一个字符串连接的宏:
#define CONCAT(a, ...) \BASE_IF(BASE_IS_EMPTY(__VA_ARGS__), a, CONCAT_INNER(a, __VA_ARGS__))// 当只传一个参数时直接返回,多个参数时递归拼接
在windows上编译时,需要传入/Zc:preprocessor参数,以确保C++预编译器的行为和GCC一致。
windows上默认行为比较特殊,如下:
// windows行为
BASE_IS_EMPTY(a, b, c, d)
>>> BASE_INTERNAL_IS_EMPTY_EXPANDED(a, b, c, d)
>>> BASE_INTERNAL_IS_EMPTY_INNER(_, a, b, c, d)
>>> BASE_INTERNAL_IS_EMPTY_INNER_EXPANDED(_, a, b, c, d, 0, 1)
e0 = _, a, b, c, d //因为windows默认把__VA_ARGS__当成整体了,这与GCC行为存在差异
e1 = 0
e2 = 1
GCC的默认行为如下:
// GCC行为
BASE_IS_EMPTY(a, b, c, d)
>>> BASE_INTERNAL_IS_EMPTY_EXPANDED(a, b, c, d)
>>> BASE_INTERNAL_IS_EMPTY_INNER(_, a, b, c, d)
>>> BASE_INTERNAL_IS_EMPTY_INNER_EXPANDED(_, a, b, c, d, 0, 1)
e0 = _
e1 = a # GCC上, 默认是按照展开位置来抉择
e2 = b
......
虽然GCC的行为是正常展开了变量,参数和位置能一一对应,但是依旧不满足逻辑,如下:
// BASE_IS_EMPTY() 应该返回 1
BASE_IS_EMPTY() => BASE_INTERNAL_IS_EMPTY_INNER(_) => BASE_INTERNAL_IS_EMPTY_INNER_EXPANDED(_, 0, 1)
>>> 所以is_empty == 1
// BASE_IS_EMPTY(a, b, c, d) 应该返回0
BASE_IS_EMPTY(a, b, c, d) => BASE_INTERNAL_IS_EMPTY_INNER_EXPANDED(_, a, b, c, d, 0, 1)
>>> 所以is_empty == b, 这并不符合预期
所以需要对命令稍加改造:
// 在Windows上使用msvc时, 需要/Zc:preprocessor参数, 否则宏处理会有问题
// reference: https://stackoverflow.com/questions/77700691/getting-va-opt-to-be-recognized-by-visual-studio
// 对于多个参数的情况, __VA_OPT__会将逗号和参数一起处理, 然后通过#__VA_ARGS__转换成一个字符串// A macro that substitutes with 1 if called without arguments, otherwise 0.
#define BASE_IS_EMPTY(...) BASE_INTERNAL_IS_EMPTY_EXPANDED(__VA_ARGS__)
#define BASE_INTERNAL_IS_EMPTY_EXPANDED(...) \BASE_INTERNAL_IS_EMPTY_INNER("_" __VA_OPT__(,) #__VA_ARGS__)
#define BASE_INTERNAL_IS_EMPTY_INNER(...) \BASE_INTERNAL_IS_EMPTY_INNER_EXPANDED(__VA_ARGS__, 0, 1)
#define BASE_INTERNAL_IS_EMPTY_INNER_EXPANDED(e0, e1, is_empty, ...) is_empty
这里通过"_" __VA_OPT__(,) #__VA_ARGS__来操作,将__VA_ARGS__当成整体变成字符串,那么就有:
BASE_IS_EMPTY(a, b, c, d) => BASE_INTERNAL_IS_EMPTY_INNER_EXPANDED("_", "a, b, c, d", 0, 1)
>>> is_empty == 0, 符合预期
5. remove_parens.h
源码如下:
// A macro that removes at most one outer set of parentheses from its arguments.
// If the arguments are not surrounded by parentheses, this expands to the
// arguments unchanged. For example:
// `BASE_REMOVE_PARENS()` -> ``
// `BASE_REMOVE_PARENS(foo)` -> `foo`
// `BASE_REMOVE_PARENS(foo(1))` -> `foo(1)`
// `BASE_REMOVE_PARENS((foo))` -> `foo`
// `BASE_REMOVE_PARENS((foo(1)))` -> `foo(1)`
// `BASE_REMOVE_PARENS((foo)[1])` -> `(foo)[1]`
// `BASE_REMOVE_PARENS(((foo)))` -> `(foo)`
// `BASE_REMOVE_PARENS(foo, bar, baz)` -> `foo, bar, baz`
// `BASE_REMOVE_PARENS(foo, (bar), baz)` -> `foo, (bar), baz`
#define BASE_REMOVE_PARENS(...) \BASE_IF(BASE_INTERNAL_IS_PARENTHESIZED(__VA_ARGS__), BASE_INTERNAL_ECHO, \BASE_INTERNAL_EMPTY()) \__VA_ARGS__#define BASE_INTERNAL_IS_PARENTHESIZED(...) \BASE_IS_EMPTY(BASE_INTERNAL_EAT __VA_ARGS__)
#define BASE_INTERNAL_EAT(...)
#define BASE_INTERNAL_ECHO(...) __VA_ARGS__
#define BASE_INTERNAL_EMPTY()
这段代码定义了一个名为BASE_REMOVE_PARENS的宏,用于移除宏参数最外层的一对括号(如果有的话)。如果参数没有被括号包围,则宏展开后的结果与原参数相同。
宏的实现利用了之前提到的BASE_IS_EMPTY和BASE_IF宏,以及一些辅助的内部宏:
- BASE_INTERNAL_IS_PARENTHESIZED: 判断参数是否被括号包围。它将参数传递给BASE_INTERNAL_EAT宏,如果参数有括号,那么括号内的内容会被BASE_INTERNAL_EAT"吃掉",导致BASE_IS_EMPTY的结果为1,否则为0。
- BASE_INTERNAL_EAT: 接受任意参数,但不做任何事情。
- BASE_INTERNAL_ECHO: 原样返回传入的参数。
- BASE_INTERNAL_EMPTY: 不接受任何参数,也不返回任何内容。
BASE_REMOVE_PARENS的实现可以分为两步:
- 使用BASE_INTERNAL_IS_PARENTHESIZED判断参数是否有括号。
- 根据第一步的结果,使用BASE_IF选择BASE_INTERNAL_ECHO(有括号)或BASE_INTERNAL_EMPTY(无括号),并将其展开。
最后,将原始的__VA_ARGS__附加在展开的结果之后。如果参数有括号,那么展开的空宏会移除最外层括号,否则原始参数不变。
下面是一个示例演示:
BASE_REMOVE_PARENS((foo))
>>> BASE_IF(BASE_INTERNAL_IS_PARENTHESIZED((foo))), BASE_INTERNAL_ECHO, BASE_INTERNAL_EMPTY()) (foo)
>>> BASE_IF(BASE_IS_EMPTY(BASE_INTERNAL_EAT ((foo))), BASE_INTERNAL_ECHO, BASE_INTERNAL_EMPTY()) (foo)
//这里__VA_ARGS__外面存在括号,则会执行BASE_INTERNAL_EAT宏,从而变成空,条件选择BASE_INTERNAL_ECHO
>>> BASE_INTERNAL_ECHO (foo)
>>> foo // 去掉了外面一层括号
相关文章:
chrome-base源码分析(1)macros模块
Chrome-base源码分析(2)之Macros模块 Author:Once Day Date:2024年6月29日 漫漫长路,才刚刚开始… 全系列文章请查看专栏: 源码分析_Once-Day的博客-CSDN博客 参考文档: macros - Chromium Code SearchChrome base 库详解:工…...
玩转springboot之springboot定制嵌入式的servlet
springboot定制嵌入式的servlet容器 修改容器配置 有两种方式可以修改容器的配置 可以直接在配置文件中修改和server有关的配置 server.port8081 server.tomcat.uri-encodingUTF-8//通用的Servlet容器设置 server.xxx //指定Tomcat的设置 server.tomcat.xxx编写一个EmbeddedSer…...
dell服务器RAID5磁盘阵列出现故障的解决过程二——热备盘制作与坏盘替换过程
目录 背景方案概念全局热备(Global Hot Spare):独立热备(Dedicated Hot Spare): 过程8号制作成热备清除配置制作独立热备热备顶替坏盘直接rebuild 更换2号盘2号热备 注意注意事项foreign状态要先清除配置 背…...
Elasticsearch开启认证|为ES设置账号密码|ES账号密码设置|ES单机开启认证|ES集群开启认证
文章目录 前言单节点模式开启认证生成节点证书修改ES配置文件为内置账号添加密码Kibana修改配置验证 ES集群开启认证验证 前言 ES安装完成并运行,默认情况下是允许任何用户访问的,这样并不安全,可以为ES开启认证,设置账号密码。 …...
Excel 数据筛选难题解决
人不走空 🌈个人主页:人不走空 💖系列专栏:算法专题 ⏰诗词歌赋:斯是陋室,惟吾德馨 目录 🌈个人主页:人不走空 💖系列专栏:算法专题 ⏰诗词歌…...
Web实时通信的学习之旅:WebSocket入门指南及示例演示
文章目录 WebSocket的特点1、工作原理2、特点3、WebSocket 协议介绍4、安全性 WebSocket的使用一、服务端1、创建实例:创建一个webScoket实例对象1.1、WebSocket.Server(options[,callback])方法中options对象所支持的参数1.2、同样也有一个加密的 wss:/…...
分治精炼宝库-----快速排序运用(⌯꒪꒫꒪)੭
目录 一.基本概念: 一.颜色分类: 二.排序数组: 三.数组中的第k个最大元素: 解法一:快速选择算法 解法二:简单粗暴优先级队列 四.库存管理Ⅲ: 解法一:快速选择 解法二:简单粗…...
快速修复mfc100u.dll丢失解决方案
相连文章:SecureCRT的安装破解 [详细过程2024] 有小伙伴向我反馈在打开SecureFX注册机之后显示【mfc100u.dll找不到】重装之后也没有用,这个是因为Microsoft Visual C的运行时组件和库出现了错误,直接选择重新安装就可以 出现这种情况的原因…...
【C++深度探索】继承机制详解(一)
hello hello~ ,这里是大耳朵土土垚~💖💖 ,欢迎大家点赞🥳🥳关注💥💥收藏🌹🌹🌹 💥个人主页:大耳朵土土垚的博客 …...
力扣第218题“天际线问题”
在本篇文章中,我们将详细解读力扣第218题“天际线问题”。通过学习本篇文章,读者将掌握如何使用扫描线算法和堆来解决这一问题,并了解相关的复杂度分析和模拟面试问答。每种方法都将配以详细的解释,以便于理解。 问题描述 力扣第…...
帝国cms未审核文章可视化预览效果
有时候为了让编辑更加清楚的看到别人审核之后的效果,同时文章有需要下一级审核才能在前端展示出来,今天就来展示一个未审核文章预览审核后的效果 这次给某出版社开发的时候,他们需要实现编辑能够预览自己发布之后的审核效果,所以就…...
医院管理系统带万字文档医院预约挂号管理系统基于spingboot和vue的前后端分离java项目java课程设计java毕业设计
文章目录 仓库管理系统一、项目演示二、项目介绍三、万字项目文档四、部分功能截图五、部分代码展示六、底部获取项目源码带万字文档(9.9¥带走) 仓库管理系统 一、项目演示 医院管理系统 二、项目介绍 基于springbootvue的前后端分离医院管…...
爬虫技术在物联网数据采集中的应用
爬虫技术在物联网数据采集中的应用案例主要包括以下几个方面: 电商平台数据采集:例如,使用Python编写的网络爬虫可以用于爬取京东网页相关数据,如品牌、标题、价格、店铺等,并进行数据处理及可视化展示。这种方法不仅可…...
spring boot初始化的几个总结
spring intializr File->New->Project 注意:Spring Initializer中 Java版本选择模块已经不支持1.8了。 Spring Boot 3.x要求 Java最低版本为17, 最新的SpringBoot版本已经要求Java22了 所以,你可以升级Java版本,使用Spri…...
springcloud第4季 seata报could not find any implementation for class
一 问题说明 1.1 描述 在使用seata2.0alibaba-cloud 2022.0.0.0-RC2nacos 2.2.3 模拟下订单分布式事务场景,出现如下问题:java.lang.ArrayIndexOutOfBoundsException: Index 0 out of bounds for length 0 查看服务端:java.util.ServiceCo…...
IT之家最新科技热点
人不走空 🌈个人主页:人不走空 💖系列专栏:算法专题 ⏰诗词歌赋:斯是陋室,惟吾德馨 目录 🌈个人主页:人不走空 💖系列专栏:算法专题 ⏰诗词歌…...
对象实例化过程
目录 一、Java对象实例化在JVM中的过程: 类加载与初始化 分配内存 初始化对象内存 设置对象头 执行初始化方法 构造方法执行 二、对象的创建过程 一、Java对象实例化在JVM中的过程: 类加载与初始化: 当JVM需要实例化一个对象时,它…...
常见漏洞之XSS
一、XSS简介 XSS(Cross-Site Scripting,跨站脚本攻击)是一种常见的网络攻击方式,通过在网页中注入恶意脚本,当其他用户浏览这些网页时,这些嵌入的恶意脚本会在其浏览器上执行,从而进行各种恶意…...
Python变量的命名规则与赋值方式
第二章:Python 基础语法 第一节:变量的命名规则与赋值方式 2.1.1 引言 在编程中,变量是存储数据的基本单元。变量的命名和赋值是编程语言中表达和操作数据的基础。了解和遵循变量命名规则对于编写清晰、可维护的代码至关重要。 2.1.2 变量…...
昇思25天学习打卡营第7天|网络构建
昇思25天学习打卡营第7天|网络构建 前言函数式自动微分函数与计算图微分函数与梯度计算Stop GradientAuxiliary data神经网络梯度计算 个人任务打卡(读者请忽略)个人理解与总结 前言 非常感谢华为昇思大模型平台和CSDN邀请体验昇思大模型!从今…...
接口测试中缓存处理策略
在接口测试中,缓存处理策略是一个关键环节,直接影响测试结果的准确性和可靠性。合理的缓存处理策略能够确保测试环境的一致性,避免因缓存数据导致的测试偏差。以下是接口测试中常见的缓存处理策略及其详细说明: 一、缓存处理的核…...
在软件开发中正确使用MySQL日期时间类型的深度解析
在日常软件开发场景中,时间信息的存储是底层且核心的需求。从金融交易的精确记账时间、用户操作的行为日志,到供应链系统的物流节点时间戳,时间数据的准确性直接决定业务逻辑的可靠性。MySQL作为主流关系型数据库,其日期时间类型的…...
2025年能源电力系统与流体力学国际会议 (EPSFD 2025)
2025年能源电力系统与流体力学国际会议(EPSFD 2025)将于本年度在美丽的杭州盛大召开。作为全球能源、电力系统以及流体力学领域的顶级盛会,EPSFD 2025旨在为来自世界各地的科学家、工程师和研究人员提供一个展示最新研究成果、分享实践经验及…...
前端导出带有合并单元格的列表
// 导出async function exportExcel(fileName "共识调整.xlsx") {// 所有数据const exportData await getAllMainData();// 表头内容let fitstTitleList [];const secondTitleList [];allColumns.value.forEach(column > {if (!column.children) {fitstTitleL…...
苍穹外卖--缓存菜品
1.问题说明 用户端小程序展示的菜品数据都是通过查询数据库获得,如果用户端访问量比较大,数据库访问压力随之增大 2.实现思路 通过Redis来缓存菜品数据,减少数据库查询操作。 缓存逻辑分析: ①每个分类下的菜品保持一份缓存数据…...
Qt Http Server模块功能及架构
Qt Http Server 是 Qt 6.0 中引入的一个新模块,它提供了一个轻量级的 HTTP 服务器实现,主要用于构建基于 HTTP 的应用程序和服务。 功能介绍: 主要功能 HTTP服务器功能: 支持 HTTP/1.1 协议 简单的请求/响应处理模型 支持 GET…...
2025盘古石杯决赛【手机取证】
前言 第三届盘古石杯国际电子数据取证大赛决赛 最后一题没有解出来,实在找不到,希望有大佬教一下我。 还有就会议时间,我感觉不是图片时间,因为在电脑看到是其他时间用老会议系统开的会。 手机取证 1、分析鸿蒙手机检材&#x…...
听写流程自动化实践,轻量级教育辅助
随着智能教育工具的发展,越来越多的传统学习方式正在被数字化、自动化所优化。听写作为语文、英语等学科中重要的基础训练形式,也迎来了更高效的解决方案。 这是一款轻量但功能强大的听写辅助工具。它是基于本地词库与可选在线语音引擎构建,…...
搭建DNS域名解析服务器(正向解析资源文件)
正向解析资源文件 1)准备工作 服务端及客户端都关闭安全软件 [rootlocalhost ~]# systemctl stop firewalld [rootlocalhost ~]# setenforce 0 2)服务端安装软件:bind 1.配置yum源 [rootlocalhost ~]# cat /etc/yum.repos.d/base.repo [Base…...
Unity UGUI Button事件流程
场景结构 测试代码 public class TestBtn : MonoBehaviour {void Start(){var btn GetComponent<Button>();btn.onClick.AddListener(OnClick);}private void OnClick(){Debug.Log("666");}}当添加事件时 // 实例化一个ButtonClickedEvent的事件 [Formerl…...
