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_1BASE_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 // 去掉了外面一層括號

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/web/37248.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/37248.shtml
英文地址,請注明出處:http://en.pswp.cn/web/37248.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

安全架構概述_2.安全架構的定義和范圍

安全架構是架構面向安全性方向上的一種細分&#xff0c;比如細分領域含有運維架構、數據庫架構等。如果安全性體現在產品上&#xff0c;那么&#xff0c;通常的產品安全架構、安全技術體系架構和審計架構可組成三道防線。 &#xff08;1&#xff09;產品安全架構&#xff1a;構…

ArtTS系統能力-通知的學習(3.1)

上篇回顧&#xff1a; ArtTS語言基礎類庫-容器類庫內容的學習(2.10.2&#xff09; 本篇內容&#xff1a; ArtTS系統能力-通知的學習&#xff08;3.1&#xff09; 一、 知識儲備 1. 基礎類型通知 按內容分成四類&#xff1a; 類型描述NOTIFICATION_CONTENT_BASIC_TEXT普通文…

2024 Parallels Desktop for Mac 功能介紹

Parallels Desktop的簡介 Parallels Desktop是一款由Parallels公司開發的桌面虛擬化軟件&#xff0c;它允許用戶在Mac上運行Windows和其他操作系統。通過強大的技術支持&#xff0c;用戶無需重新啟動電腦即可在Mac上運行Windows應用程序&#xff0c;實現了真正的無縫切換。 二…

普元EOS學習筆記-創建精簡應用

前言 本文依舊基于EOS8.3進行描述。 在上一篇文章《EOS8.3精簡版安裝》中&#xff0c;我們了解到普元預編譯好的EOS的精簡版壓縮包&#xff0c;安裝后&#xff0c;只能進行低開&#xff0c;而無法高開。 EOS精簡版的高開方式是使用EOS開發工具提供的IDE&#xff0c;創建一個…

東軟睿馳總裁兼CTO杜強受邀出席 CICV 2024智能網聯汽車技術首腦(CTO)閉門峰會

近日&#xff0c;第十一屆國際智能網聯汽車技術年會&#xff08;CICV 2024&#xff09;在北京舉辦&#xff0c;會議期間組織智能網聯汽車技術首腦&#xff08;CTO&#xff09;閉門峰會&#xff0c;邀請40余位技術領袖圍繞智能網聯汽車產業生態建設以及智能網聯汽車數據、算力和…

SQL語句查詢Cache數據庫中數據時對時間怎樣轉換

使用東華醫為HIS系統的用戶都知道&#xff0c;Cache數據庫中對于日期的存儲為幾萬的數字&#xff0c;比如&#xff0c;今天就是相對于1841年1月1日的第多少萬天&#xff0c;以這種形式進行表示&#xff1b;對于時間&#xff0c;也是以數字形式存儲&#xff0c;比如&#xff0c;…

Python的numpy簡單使用

1.可以調用引入numpy里面的函數&#xff0c;如add可以把倆數相加&#xff0c;也可以創建一個數組arr&#xff0c;arr.shape是數組arr的屬性&#xff0c;如果后有跟&#xff08;&#xff09;就是里面的一個函數 type()函數可以知道里面是什么類型 變量.shape可以知道這個變量是…

BLEU和SPICE:機器翻譯與圖像描述的自動評估方法詳解

BLEU和SPICE&#xff1a;機器翻譯與圖像描述的自動評估方法詳解 在機器翻譯和圖像描述領域&#xff0c;評估模型性能是一個重要且復雜的問題。為了有效地評估機器生成的文本質量&#xff0c;我們需要一種自動、快速且可靠的方法。BLEU&#xff08;Bilingual Evaluation Unders…

基于決策樹的旋轉機械故障診斷(Python)

前置文章&#xff1a; 將一維機械振動信號構造為訓練集和測試集&#xff08;Python&#xff09; https://mp.weixin.qq.com/s/DTKjBo6_WAQ7bUPZEdB1TA 旋轉機械振動信號特征提取&#xff08;Python&#xff09; https://mp.weixin.qq.com/s/VwvzTzE-pacxqb9rs8hEVw import…

菲爾茲獎得主測試GPT-4o,經典過河難題未能破解!最強Claude 3.5回答離譜!

目錄 01 大言模型能否解決「狼-山羊-卷心菜」經典過河難題&#xff1f; 02 加大難度&#xff1a;100只雞、1000只雞如何&#xff1f; 01 大言模型能否解決「狼-山羊-卷心菜」經典過河難題&#xff1f; 最近&#xff0c;菲爾茲獎得主Timothy Gowers分享了他測試GPT-4o的經歷&a…

游戲推薦: 植物大戰僵尸雜交版

下載地址網上一搜就有. 安裝就能玩. 2是顯血. 4顯示植物血, 5是加速. 都是左手主鍵盤的按鈕, 再按是取消. 比較刺激: ps: 設置里面還能打開自動收集陽光和金幣.

視頻融合共享平臺LntonCVS統一視頻接入平臺智慧安防應用方案

安防視頻監控平臺LntonCVS是一款擁有強大拓展性和靈活部署能力的綜合管理平臺。它支持多種主流標準協議&#xff0c;包括國標GB28181、RTSP/Onvif、RTMP等&#xff0c;同時兼容各廠家的私有協議和SDK&#xff0c;如海康Ehome、海大宇等。LntonCVS不僅具備傳統安防視頻監控功能&…

深入解析Tomcat:Java Web服務器(上)

深入解析Tomcat&#xff1a;Java Web服務器&#xff08;上&#xff09; Apache Tomcat是一個開源的Java Web服務器和Servlet容器&#xff0c;用于運行Java Servlets和JavaServer Pages (JSP)。Tomcat在Java Web應用開發中扮演著重要角色。本文將詳細介紹Tomcat的基本概念、安裝…

遙遠星辰中的覺醒:超大質量黑洞的蘇醒

遙遠星辰中的覺醒&#xff1a;超大質量黑洞的蘇醒 在浩渺無垠的宇宙中&#xff0c;星辰的閃爍仿佛是時間的漣漪&#xff0c;穿越億萬年的距離&#xff0c;抵達我們的眼眸。而在這片星辰大海的深處&#xff0c;一個驚人的現象正在悄然上演——距離地球3.6億光年之遙的星系中&am…

【C++】空指針訪問成員函數

空指針訪問成員函數 C中空指針也是可以調用成員函數的&#xff0c;但是也要注意有沒有用到this指針 如果用到this指針&#xff0c;需要加以判斷保證代碼的健壯性 class Animal { public:void fun1() {//正常的成員函數}void fun2() {if (this NULL) {return;//如果沒有這個…

Django 一對一關系

作用&#xff1a; 兩個數據庫表建立外鍵關系當外鍵表的數據被刪除時&#xff0c;主表的數據也會一并刪除。 1&#xff0c;添加表模型 Test/app8/views.pyfrom django.db import modelsclass User(models.Model):username models.CharField(max_length50, uniqueTrue)email …

【代碼隨想錄訓練營】【Day 65】【圖論-2】| 卡碼 99

【代碼隨想錄訓練營】【Day 65】【圖論-2】| 卡碼 99 需強化知識點 深度搜索和廣度搜索 題目 99. 島嶼數量 思想&#xff1a;遍歷到為1的節點&#xff0c;再搜索標記&#xff0c;每遇到新的陸地節點&#xff0c;增加計數 深度搜索廣度搜索&#xff1a;此處用 [] 作為待遍…

前端面試必備:深入解析Vue.js中v-if與v-show的原理與應用

前言 在Vue.js中&#xff0c;條件渲染是一個核心的概念&#xff0c;它允許我們根據數據的狀態來動態地顯示或隱藏元素。v-if和v-show是Vue.js提供的兩個最常用的條件渲染指令&#xff0c;它們在表面上看起來很相似&#xff0c;但實際上在背后的工作原理和適用場景上有著顯著的…

2024年度濰坊市職業技能大賽 —網絡搭建(網絡與信息安全管理員)職業技能競賽賽項規程

2024年度濰坊市職業技能大賽 —網絡搭建&#xff08;網絡與信息安全管理員&#xff09;職業技能競賽賽項技術文件................................ 一、賽項簡介...................................... 3 二、競賽規程...................................... 3 &#xff08…

【Linux系統】進程替換 自主實現shell(簡易版)

1.先看代碼 && 現象 我們用exec*函數執行新的程序&#xff0c; exec*系列的函數&#xff0c;執行完畢后&#xff0c;后續的代碼不見了&#xff0c;因為被替換了。 execl的返回值可以不關心了&#xff0c;只要替換成功&#xff0c;就不會向后繼續運行&#xff0c;只要…