最美情侣中文字幕电影,在线麻豆精品传媒,在线网站高清黄,久久黄色视频

歡迎光臨散文網(wǎng) 會員登陸 & 注冊

C++編譯期反射實踐——以AOP實現(xiàn)為例

2022-11-27 05:02 作者:L__B_  | 我要投稿

編譯期反射實踐

自古以來,C++就一直缺少一個編程語言的重要特性——反射,但如果熟悉C++元模板編程的同學,就知道以C++的風格,肯定是不會在標準庫中添加運行時的反射支持的,從最新的C++版本演進來看,倒是編譯期反射可能得到更好的支持。C++11 -> C++14 -> C++17 -> C++20… 不斷讓元模板編程變得更簡單,更規(guī)范。

本次的編譯期反射實踐,代碼要求的最低C++版本為14,因為用到了 make_shared、decay_t。

本次實踐的完整代碼倉庫:https://github.com/ACking-you/MyUtil/tree/master/aop

獲取類的方法

判斷類是否具有某方法

我們?nèi)绾闻袛嗄硞€類是否具有某個方法呢?

要想在編譯期間實現(xiàn)這樣一個判斷,我們的思路可以是這樣:寫兩個模板,如果這個類型具有這個方法,就匹配到返回?std::true_type()?的模板,如果不具備則匹配到返回?std::false_type()?的模板,最后通過?std::is_same?能夠判斷匹配結(jié)果,也就是實現(xiàn)了在編譯期間判斷類是否有這個方法。

上述過程,利用?SFINAE?的原理可以輕松實現(xiàn),如果不了解 SFINAE 以及對應(yīng)的 enable_if 的運用,可以看看這篇文章:https://zhuanlan.zhihu.com/p/21314708

我們現(xiàn)在就開始動手實現(xiàn)上述代碼,假設(shè)我們需要判斷一個類型是否有?before()?方法。

先講下上述代碼定義后如何使用吧,比如現(xiàn)在有個 Student 類型,我們來判斷是否具有 before 成員函數(shù),則只需要寫下下面的代碼:

上面的代碼重點有三段,已經(jīng)作為標記1、2、3:

代碼1處,利用了?std::declval?在編譯期創(chuàng)建類型U的實例,并調(diào)用其?before?方法,這是在元模板中判斷一個類型是否具有某個方法的常有手段,因為 SFINAE 的存在,即便該處替換出錯,編譯器會去繼續(xù)尋找下一個替換是否能夠正確,直到所有的替換都出錯。

很明顯這里是一定會替換成功的,因為代碼2有一個包容性很強的重載,這個重載的參數(shù)不能和代碼1處的重載參數(shù)一致,否則會算作重復(fù)定義,當然如果你使用?std::enable_if?對參數(shù)一致的模板參數(shù)進行唯一性的限制,那么重復(fù)定義的錯誤也可以避免。但是寫成 C 的可變參數(shù)是最快的解決方式。

代碼1處,有個逗號表達式的細節(jié),如果成功被代碼1處替換,那么返回值類型將會是?decltype()?中的表達式類型,也就是逗號表達式最后的結(jié)果?std::true_type。

代碼3是利用enum類型在編譯期得到具體的常量值。具體是通過調(diào)用?Check<T>(0)?獲取該函數(shù)的返回值類型,這期間模板的匹配替換就會牽扯到前面的代碼1、2。所以一旦模板被實例化,那么該class是否具有該方法的信息也就清楚了。

最后我們可以把該段代碼提取為宏作為通用代碼:

如果想要生成判斷是否有before或者其他方法的代碼,則只需要調(diào)用這個宏。

將類方法轉(zhuǎn)為function保存

直接上代碼,再逐一講解:

以下代碼是將該類的before和after方法包裝成一個function,并返回一個pair。完整代碼:https://github.com/ACking-you/MyUtil/tree/master/aop/reflect_util.hpp

在代碼段1中,通過?enable_if?確保在該類型有before和after方法,同時也可以保證寫其他版本的時候不會出現(xiàn)重復(fù)定義的錯誤。enable_if?第一個參數(shù)是需要滿足的條件,第二個參數(shù)是enable_if內(nèi)部的type類型,默認為void。

代碼段2中,創(chuàng)建一個T類型的實例,并用shread_ptr管理,原因在于before方法和after方法需要共用內(nèi)存,而這兩個方法都要被提取為單獨的function,要保證內(nèi)存安全,故需要使用智能指針。其中?std::decay_t<T>?效果等同于?std::decay<T>::type,作用是消除T類型的const修飾和引用修飾。因為make_shared<>中的模板參數(shù)不能為引用類型。

代碼段3中,利用lamda表達式將fun拷貝一份到其中命名為self,最后返回pair即可。

當前寫的功能是不完整的,需要多幾個模板的重載來實現(xiàn)只有before方法、以及只有after方法的情況。寫法和上述代碼一致,只不過?enable_if?中的條件稍作改變即可。前面也提到過enable_if千萬不能丟,否則會報重復(fù)定義的錯誤,當然如果你是C++17的版本,可以直接使用?if constexpr?來實現(xiàn)更為簡潔的代碼而無需單獨寫三個函數(shù)。

如下:

下面我簡單解釋下代碼:

  1. ST_ASSERT宏的作用是,通過static_assert在編譯期拋出提示,T類型必須有before或after兩個方法之一。

  2. 通過該類型擁有的情況不同,給出不同的返回值。

很明顯去除了enable_if后,我們代碼清爽了許多。

AOP的實現(xiàn)

關(guān)于AOP,大家可以去搜一搜,這里就不過多贅述。我的簡單理解就是一個事件回調(diào),可以嵌入到業(yè)務(wù)的執(zhí)行前后,把這個事件的概念換成一個切面,把業(yè)務(wù)代碼看作一個橫向坐標軸上的面,那么AOP就是在這個面的前后添加其他切面來實現(xiàn)常用的業(yè)務(wù)復(fù)用。比如用戶的身份驗證,可以在業(yè)務(wù)之前添加身份驗證的切面,比如需要測試該業(yè)務(wù)的性能,那么可以在業(yè)務(wù)切面的前后添加開始計時和終止計時的邏輯。

Invoke調(diào)用實現(xiàn)AOP

根據(jù)上述對AOP的描述,我們要切入的代碼主要是前和后兩個邏輯,故每個要切入的類可以規(guī)定他必須定義Before或者After方法。然后通過可變參模板遞歸實現(xiàn)任意個參數(shù)的切面調(diào)用。

可以把整個切面調(diào)用過程看作一個洋蔥圈層,比如添加s1類型的before和after作為切片,s2類型的before和after作為切片,s3類型的before作為切片。把業(yè)務(wù)代碼邏輯作為foo函數(shù)。

則他們的調(diào)用過程如下:

?s1->before =>?

s2->before =>?

s3->before =>

foo業(yè)務(wù)邏輯 =>?

s1->after =>?

s2->after。

如果稍微學過點數(shù)據(jù)結(jié)構(gòu),這個調(diào)用就能想到前中后序遍歷上去了。

代碼實現(xiàn)如下(C++11需要使用eable_if來實現(xiàn),代碼量很多,所以這里就直接用C++17的?if constexpr?來實現(xiàn)了):

上述完整代碼:https://github.com/ACking-you/MyUtil/tree/master/aop/aspect.hpp

上述代碼是根據(jù)C++變參模板實現(xiàn)的通用性操作,可以同時添加多個切片 ,他們都是Aspect類的兩個方法,具體實現(xiàn)邏輯就是:通過之前得到的編譯期常量(?has_member_Before<T,Args...>::value?)判斷 T 是否具有Before或者After方法,分三種情況:

  1. 同時又Before和After:利用中序進行遞歸。

  2. 只有Before:利用前序進行遞歸。

  3. 只有After:利用后序進行遞歸。

為了簡化調(diào)用過程,繼續(xù)封裝如下:

最后記得定義一個終止模板遞歸的最終形態(tài)。

最終如果像最開始講的要拓展s1、s2、s3的方法上去,那么簡單的使用如下代碼即可:

統(tǒng)一轉(zhuǎn)function存儲并實現(xiàn)AOP調(diào)用順序

統(tǒng)一轉(zhuǎn)function存儲

將任意類的before和after方法集體裝箱為function的關(guān)鍵代碼邏輯如下,完整代碼請看:

由于所有的獲取before和after的邏輯在前面獲取類的方法已經(jīng)講到,所以單個類型直接調(diào)用?GetMemberFunc?函數(shù)即可得出結(jié)果,并放入vector中,最后通過模板實例化的遞歸將所有的類型都裝箱。

具體的使用方式也很簡單,如下代碼:

AOP的調(diào)用順序?qū)崿F(xiàn)

完整測試代碼:https://github.com/ACking-you/MyUtil/tree/master/aop/test_aspect.cc

參考鏈接:
C++模板進階指南:SFINAE:https://zhuanlan.zhihu.com/p/21314708

SFINAE:https://en.cppreference.com/w/cpp/language/sfinae

C++11實現(xiàn)一個輕量級的AOP框架:https://www.cnblogs.com/qicosmos/p/4772389.html


C++編譯期反射實踐——以AOP實現(xiàn)為例的評論 (共 條)

分享到微博請遵守國家法律
高台县| 津南区| 云龙县| 新野县| 喀什市| 信宜市| 苏州市| 浙江省| 利辛县| 锡林浩特市| 临猗县| 民权县| 通江县| 玉溪市| 黑河市| 台北县| 左云县| 竹山县| 卫辉市| 海伦市| 寿光市| 永寿县| 望都县| 湄潭县| 太谷县| 遵义市| 浦江县| 平乐县| 长治县| 安康市| 广东省| 灵寿县| 奉化市| 随州市| 墨竹工卡县| 灌云县| 安塞县| 屯昌县| 简阳市| 浦东新区| 通河县|