亚洲免费av电影一区二区三区,日韩爱爱视频,51精品视频一区二区三区,91视频爱爱,日韩欧美在线播放视频,中文字幕少妇AV,亚洲电影中文字幕,久久久久亚洲av成人网址,久久综合视频网站,国产在线不卡免费播放

        ?

        基于C++17的泛型函數(shù)容器實(shí)現(xiàn)方法研究

        2019-07-03 02:31:14閔軍羅泓
        軟件工程 2019年5期
        關(guān)鍵詞:型函數(shù)調(diào)用容器

        閔軍 羅泓

        摘? 要:泛型函數(shù)容器的使用可以解耦對(duì)象之間的調(diào)用關(guān)系,有利于實(shí)現(xiàn)高內(nèi)聚、低耦合的軟件設(shè)計(jì)原則。C++標(biāo)準(zhǔn)庫(kù)中并沒(méi)有這樣的容器,用C++舊標(biāo)準(zhǔn)實(shí)現(xiàn)也很困難、很低效。C++1x等新標(biāo)準(zhǔn)發(fā)布后,出現(xiàn)了一些更好的實(shí)現(xiàn)方式。本文將在已有設(shè)計(jì)的基礎(chǔ)之上,基于C++17新標(biāo)準(zhǔn),利用if constexpr、fold expression、std::invoke等新技術(shù),提供一種泛型函數(shù)容器的實(shí)現(xiàn)方式。測(cè)試表明該實(shí)現(xiàn)方式簡(jiǎn)潔高效,解決了重載函數(shù)和某些特殊函數(shù)的注冊(cè)調(diào)用問(wèn)題,可以顯著降低耦合性、提高代碼復(fù)用性。

        關(guān)鍵詞:C++17;泛型;函數(shù)容器;高內(nèi)聚;低耦合

        中圖分類號(hào):TP311.1? ? ?文獻(xiàn)標(biāo)識(shí)碼:A

        Abstract:The application of generic function containers can decouple the calling relationships between objects,conducive to the realization of high cohesion and low coupling software design principles.There is no such container in the C++ standard library,and it is very difficult and inefficient to implement with the old C++ standard.The release of new standards,such as C++1x,has brought some better implementation methods.This paper provides a generic function container implementation method based on the existing design and the new C++17 standard,via some new technologies such as if constexpr,fold expression,and std::invoke.Test results show that the simple and efficient implementation method effectively solves the problem of registration and calling of overloaded functions and some special functions,significantly reducing coupling and improving code reusability.

        Keywords:C++17;generic;function container;high aggregation;low coupling

        1? ?引言(Introduction)

        高內(nèi)聚、低耦合是軟件設(shè)計(jì)的基本原則,泛型函數(shù)容器的使用可以解耦對(duì)象之間的調(diào)用關(guān)系,有利于實(shí)現(xiàn)軟件設(shè)計(jì)的這一基本原則[1]。作為一種萬(wàn)能函數(shù)注冊(cè)器,泛型函數(shù)容器可以將任意類型的函數(shù)用一個(gè)key進(jìn)行注冊(cè)以供其他程序調(diào)用,可以注冊(cè)普通函數(shù)、函數(shù)模板、成員函數(shù)、函數(shù)對(duì)象、lambda表達(dá)式、重載函數(shù)和某些特殊函數(shù)等。當(dāng)全局函數(shù)或?qū)ο笾g存在交互調(diào)用需求時(shí),比如需要相互調(diào)用對(duì)方成員函數(shù)或者不適合關(guān)聯(lián)的無(wú)關(guān)對(duì)象之間需要調(diào)用其他對(duì)象的成員函數(shù)或者全局函數(shù)需要調(diào)用成員函數(shù)等類似需求時(shí),我們便可以將需要被調(diào)用的函數(shù)用一個(gè)key注冊(cè)起來(lái)以供其他實(shí)體調(diào)用。函數(shù)的調(diào)用者不必知道被調(diào)用者,二者都依賴于中間的泛型函數(shù)容器,借此便可以解耦對(duì)象之間的調(diào)用關(guān)系[2]。

        C++標(biāo)準(zhǔn)庫(kù)中并沒(méi)有現(xiàn)存的泛型函數(shù)容器,用C++舊標(biāo)準(zhǔn)實(shí)現(xiàn)也很困難、很低效[3]。C++11、C++14、C++17等新標(biāo)準(zhǔn)發(fā)布后,出現(xiàn)了一些更好的實(shí)現(xiàn)方式。本文便是在已有設(shè)計(jì)的基礎(chǔ)之上,基于C++17新標(biāo)準(zhǔn),利用if constexpr、fold expression、std::invoke等新技術(shù),提供一種泛型函數(shù)容器的實(shí)現(xiàn)方式[4,5]。

        2? 泛型函數(shù)容器的結(jié)構(gòu)設(shè)計(jì)(Design of genericfunction container structure)

        泛型函數(shù)容器的基本結(jié)構(gòu)與C++ STL中的容器類似。在把各種不同類型的函數(shù)存入容器時(shí)必須轉(zhuǎn)換為統(tǒng)一的數(shù)據(jù)結(jié)構(gòu),通過(guò)一個(gè)key進(jìn)行注冊(cè),此后用戶便可以借助這個(gè)key和必要信息提取已注冊(cè)的記錄來(lái)執(zhí)行該函數(shù),從而實(shí)現(xiàn)泛型函數(shù)容器的基本功能。

        2.1? ?泛型函數(shù)容器的結(jié)構(gòu)圖

        2.2? ?函數(shù)存取結(jié)構(gòu)的設(shè)計(jì)

        由于泛型函數(shù)容器需要存取各種不同類型的函數(shù),具體包括普通函數(shù)、函數(shù)模板、成員函數(shù)、函數(shù)對(duì)象、lambda表達(dá)式、重載函數(shù)和某些特殊函數(shù)等,所以需要抽象出能夠區(qū)分各種不同函數(shù)的標(biāo)志特征:函數(shù)簽名(Function Signature)。類似于用簽名可以識(shí)別不同人一樣,通過(guò)函數(shù)簽名便可以識(shí)別不同的函數(shù)[6]。從便于注冊(cè)和提取的角度,本項(xiàng)目主要關(guān)注函數(shù)簽名的四個(gè)部分:函數(shù)名f、函數(shù)所屬對(duì)象的指針pObj、參數(shù)包指針pArgsList、返回值指針pRet。若是成員函數(shù),pObj必須明確賦值,否則就必須賦值為空指針nullptr。

        然后設(shè)計(jì)一個(gè)函數(shù)封裝類invoker,用來(lái)保存注冊(cè)函數(shù)的函數(shù)簽名。Fun是注冊(cè)函數(shù)的具體類型(包含函數(shù)簽名),每注冊(cè)一個(gè)函數(shù)就創(chuàng)建一個(gè)以模板參數(shù)Fun區(qū)分的該類對(duì)象,每個(gè)不同對(duì)象保存了注冊(cè)函數(shù)的函數(shù)簽名(包含四個(gè)部分),調(diào)用時(shí)便可以從中提取函數(shù)簽名來(lái)執(zhí)行該函數(shù)。

        2.3? ?函數(shù)注冊(cè)和提取部分的設(shè)計(jì)

        函數(shù)注冊(cè)時(shí)需要完整保存函數(shù)簽名各個(gè)部分的信息,不過(guò)調(diào)用時(shí)并不需要用戶提供完整的函數(shù)簽名。為了簡(jiǎn)化調(diào)用方式,函數(shù)調(diào)用時(shí)用戶只需輸入函數(shù)參數(shù)列表和返回值類型即可,若是無(wú)返回值函數(shù)則只需輸入函數(shù)參數(shù)列表(可以理解為返回值為void類型)。按照以上設(shè)計(jì)思路,注冊(cè)函數(shù)時(shí)需要完整保存函數(shù)簽名的四個(gè)部分,調(diào)用函數(shù)時(shí)用戶只需輸入兩個(gè)參數(shù)即可。因此,設(shè)計(jì)注冊(cè)函數(shù)reg_fun時(shí)需要有四個(gè)參數(shù),設(shè)計(jì)調(diào)用函數(shù)call時(shí)只需有兩個(gè)參數(shù)即可。具體實(shí)現(xiàn)可參見(jiàn)后面完整代碼中的reg_fun、call。

        3? 關(guān)鍵數(shù)據(jù)成員m_mapInvoker的設(shè)計(jì)(Design of the key data member m_mapInvoker)

        3.1? ?關(guān)鍵數(shù)據(jù)成員m_mapInvoker保存的是key與函數(shù)的成對(duì)記錄

        我們可以使用std::map容器來(lái)保存泛型函數(shù)容器的注冊(cè)數(shù)據(jù)。在本項(xiàng)目中,關(guān)鍵數(shù)據(jù)成員m_mapInvoker便是用于保存key與函數(shù)成對(duì)記錄的std::map容器。m_mapInvoker的key字段為std::string類型,是用戶指定名稱或者返回值及參數(shù)類型名稱的累加字符串;data字段則是std::function類模版封裝的特殊函數(shù)類型[7]。

        若用戶注冊(cè)時(shí)指定了注冊(cè)名稱便以此為該條記錄的key,此時(shí)的key用std::string類型,不至于產(chǎn)生混淆。不過(guò)在調(diào)用該注冊(cè)函數(shù)時(shí),若直接使用std::string類型的key,便可能與參數(shù)類型產(chǎn)生混淆,因?yàn)樽?cè)函數(shù)的第一個(gè)參數(shù)也可能是std::string類型。所以,在調(diào)用該注冊(cè)函數(shù)時(shí),必須使用key_fun類型的key,才能避免可能發(fā)生的混淆。fun_key是只含一個(gè)數(shù)據(jù)成員std::string key的簡(jiǎn)單封裝類型,其主要作用就是在調(diào)用時(shí)避免與函數(shù)參數(shù)發(fā)生混淆。

        若用戶注冊(cè)時(shí)未指定注冊(cè)名稱,便使用返回值及參數(shù)類型名稱的累加字符串作為該條記錄的key,提取時(shí)也會(huì)自動(dòng)生成返回值及參數(shù)類型名稱的累加字符串作為key來(lái)查詢調(diào)用該注冊(cè)函數(shù)。這樣的設(shè)計(jì),同時(shí)也解決了參數(shù)列表相同、返回值不同的多個(gè)函數(shù)的注冊(cè)調(diào)用問(wèn)題。

        m_mapInvoker的data字段用于保存函數(shù)簽名的四個(gè)部分,它是以std::function類模版封裝的特殊函數(shù)void(void*,void*)。該封裝函數(shù)無(wú)返回值,第一個(gè)參數(shù)為注冊(cè)函數(shù)的參數(shù)包指針,第二個(gè)參數(shù)為注冊(cè)函數(shù)的返回值指針。

        具體實(shí)現(xiàn)可以參見(jiàn)完整代碼中的fun_key類(其結(jié)構(gòu)參見(jiàn)圖2)、call_impl、get_key_from_fun_args等函數(shù),以及后面的測(cè)試代碼。

        3.2? ?封裝函數(shù)的原始模型

        關(guān)鍵數(shù)據(jù)成員m_mapInvoker的data字段中保存的并非單純的封裝函數(shù),而是通過(guò)std::bind綁定到原始函數(shù)模型上的間接函數(shù)[8]。封裝函數(shù)的原始模型是invoker::apply(…),F(xiàn)un是注冊(cè)函數(shù)的函數(shù)類型(包含函數(shù)簽名),Object是注冊(cè)函數(shù)所屬的對(duì)象類型(若是非成員函數(shù)則為void)。原始函數(shù)invoker::apply(…)有四個(gè)參數(shù),分別用于關(guān)聯(lián)注冊(cè)函數(shù)類型(函數(shù)簽名)的四個(gè)部分:函數(shù)名f、成員函數(shù)的對(duì)象指針pObj、參數(shù)包指針_1、返回值指針_2。_1、_2為C++11新標(biāo)準(zhǔn)的占位符[9],是用戶在提取記錄調(diào)用注冊(cè)函數(shù)時(shí)需要輸入的參數(shù),若注冊(cè)函數(shù)無(wú)返回值,則只需輸入第一個(gè)參數(shù)即可。具體可以參見(jiàn)后面的完整代碼。

        3.3? ?解決重載函數(shù)和某些特殊函數(shù)的注冊(cè)調(diào)用問(wèn)題

        在以上設(shè)計(jì)的泛型函數(shù)容器中,若直接注冊(cè)存在兩個(gè)以上實(shí)例的重載函數(shù),編譯時(shí)就會(huì)報(bào)錯(cuò)。解決該問(wèn)題的思路很簡(jiǎn)單,就是針對(duì)重載函數(shù)的多個(gè)實(shí)例對(duì)應(yīng)地定義多個(gè)不同名稱和類型的函數(shù)指針、并將重載函數(shù)賦值給它們,這就相當(dāng)于將多個(gè)重載函數(shù)的實(shí)例轉(zhuǎn)換成為多個(gè)不同名稱和類型的新函數(shù)指針。使用這些新的函數(shù)指針便能在泛型函數(shù)容器中成功進(jìn)行注冊(cè)和調(diào)用。另外,也可以用lambda表達(dá)式來(lái)消除重載函數(shù)的二義性[10]。對(duì)某些特殊函數(shù)的處理也類似,包括特殊函數(shù)1:參數(shù)列表相同、返回值不同的多個(gè)函數(shù)的注冊(cè)和調(diào)用,這在本項(xiàng)目設(shè)計(jì)中已經(jīng)解決;特殊函數(shù)2:參數(shù)列表相同、返回值相同的多個(gè)函數(shù)的注冊(cè)和調(diào)用,可以用lambda表達(dá)式封裝該函數(shù),增加一個(gè)參數(shù)即可。具體請(qǐng)參見(jiàn)后面的測(cè)試代碼。

        4? ?利用C++17新技術(shù)優(yōu)化代碼設(shè)計(jì)(Optimize code design with C++17 new technology)在本項(xiàng)目中,使用了C++17的if constexpr[11]新技術(shù)在編譯期進(jìn)行判斷,去除enable_if_t,合并許多功能類似的函數(shù)。包括:合并非成員函數(shù)、成員函數(shù)注冊(cè)的兩種reg_fun函數(shù);合并有返回值、無(wú)返回值的兩種call函數(shù);合并Key與fun_key類型相同和不同的兩種call_impl、get_key_from_fun_args函數(shù)等。

        在C++17之前,我們經(jīng)常用逗號(hào)表達(dá)式和std::initializer_list將變參依次傳入一個(gè)函數(shù)。用C++17的fold expression折疊表達(dá)式代替initializer_list,就要簡(jiǎn)潔得多[12]。本項(xiàng)目便使用了C++17的這一新技術(shù)簡(jiǎn)化代碼設(shè)計(jì)。

        利用C++17的invoke調(diào)用器,可以合并非成員函數(shù)、成員函數(shù)的調(diào)用,統(tǒng)一使用std::invoke(f,pArgsList)這種簡(jiǎn)捷形式進(jìn)行調(diào)用[13]。當(dāng)然,成員函數(shù)調(diào)用時(shí),對(duì)象實(shí)例必須放在 pArgsList的首位,作為第一個(gè)參數(shù)。在本項(xiàng)目中,便使用了C++17的這一新技術(shù)簡(jiǎn)化代碼設(shè)計(jì)。

        5? ?C++17泛型函數(shù)容器的完整實(shí)現(xiàn)代碼(Complete implementation code of C++17 generic function container)

        5.1? ?泛型函數(shù)容器的完整實(shí)現(xiàn)代碼

        以下便是本文介紹的泛型函數(shù)容器的完整實(shí)現(xiàn)代碼。用戶需要注意的是,以下代碼是基于C++17新標(biāo)準(zhǔn)實(shí)現(xiàn)的,需要在支持C++17的編譯器中才能夠正常編譯,比如Visual Studio 2017 15.3[14]、CodeBlocks 17.12 with GCC 7.2及以上版本[15]。

        6? 泛型函數(shù)容器的實(shí)際使用(Actual use of generic function container)

        下面代碼測(cè)試了泛型函數(shù)容器的實(shí)際使用。測(cè)試可以分為注冊(cè)函數(shù)時(shí)輸入key和未輸入key兩大類。每一大類都可以實(shí)現(xiàn)無(wú)返回值普通函數(shù)、帶返回值普通函數(shù)、函數(shù)模板、成員函數(shù)、函數(shù)對(duì)象、lambda表達(dá)式、重載函數(shù)、某些特殊函數(shù)等的注冊(cè)和調(diào)用。

        6.1? ?實(shí)際使用的測(cè)試代碼

        6.2? ?重載函數(shù)和某些特殊函數(shù)的注冊(cè)調(diào)用測(cè)試

        前面已經(jīng)提到,本項(xiàng)目解決了重載函數(shù)和某些特殊函數(shù)的注冊(cè)調(diào)用問(wèn)題。具體測(cè)試代碼如下。

        7? ?結(jié)論(Conclusion)

        綜上所述,泛型函數(shù)容器可以將任意類型的函數(shù)用一個(gè)key進(jìn)行注冊(cè)以供其他程序調(diào)用,它的使用可以解耦對(duì)象之間的調(diào)用關(guān)系,有利于實(shí)現(xiàn)高內(nèi)聚、低耦合的軟件設(shè)計(jì)原則。C++標(biāo)準(zhǔn)庫(kù)中并沒(méi)有這樣的容器,用C++舊標(biāo)準(zhǔn)實(shí)現(xiàn)也很困難、很低效。C++1x等新標(biāo)準(zhǔn)發(fā)布后,出現(xiàn)了一些更好的實(shí)現(xiàn)方式。本文便是在已有設(shè)計(jì)的基礎(chǔ)之上,基于C++17新標(biāo)準(zhǔn),利用if constexpr、fold expression、std::invoke等新技術(shù),提供一種泛型函數(shù)容器的實(shí)現(xiàn)方式。測(cè)試結(jié)果表明,該實(shí)現(xiàn)方式簡(jiǎn)潔高效地實(shí)現(xiàn)了任意類型函數(shù)的注冊(cè)和調(diào)用,并且解決了重載函數(shù)和某些特殊函數(shù)的注冊(cè)調(diào)用問(wèn)題,可以顯著降低耦合性、提高代碼復(fù)用性。

        參考文獻(xiàn)(References)

        [1] Ofenbeck G,Rompf T,Püschel M.Staging for generic programming in space and time[C].The ACM SIGPLAN International Conference.ACM,2017:15-28.

        [2] Bemardi ML,Cimitile M,Lucca GD.Design pattem detection using a DSL-driven graph matching approach[J].Journal of Software Evolution&Process,2014,26(12):1233-1266.

        [3] B Rasool G,Mader P.A customizable approach to design pattems recognition based 011 feature types[J].Arabian Journal for Science&Engineering,2014,39(12):8851-8873.

        [4] Chen Yewang,Jiang Zhixiong,Zhao Wenyun,et al.Generic component:a generic programming approach[EB/OL].https://www.computer.org/csdl/proceedings/cit/2007/2983/00/29830087-abs.html,2018 IEEE.

        [5] Yallop J.Staging,generic programming[M].New York:ACM,2016:85-96.

        [6] 符號(hào)修飾(name decoration)與函數(shù)簽名(function signature)[EB/OL].https://blog.csdn.net/weiwangchao_/article/details/7165467,2011-12-30.

        [7] std::function[EB/OL].https://en.cppreference.com/w/cpp/utility/functional/function,2018-06-15.

        [8] Bjarne Stroustrup.The C++ Programming Language Fourth Edition[M].USA:Addison-Wesley Professional,2013:967.

        [9] std::placeholders[EB/OL].https://en.cppreference.com/w/cpp/utility/functional/placeholders,2018-06-15.

        [10] Stanley B,Lippman.C++ Primer 5th Edition[M].USA:Addison-Wesley Professional,2012:572-574.

        [11] if statement,attr(optional)if constexpr(optional)(init-statement(optional)condition)statement-true else statement-false[EB/OL].https://en.cppreference.com/w/cpp/language/if,2018-08-21.

        [12] Fold expression(since C++17)[EB/OL].https://en.cppreference.com/w/cpp/language/fold,2018-07-19.

        [13] std::invoke[EB/OL].https://en.cppreference.com/w/cpp/utility/functional/invoke,2018-07-06.

        [14] C++ conformance improvements in Visual Studio 2017 versions[EB/OL].https://docs.microsoft.com/en-us/cpp/cpp-conformance-improvements-2017?view=vs-2017,2018-08-15.

        [15] C++ Standards Support in GCC[EB/OL].https://gcc.gnu.org/projects/cxx-status.html,2018-09-30.

        [16] 獲得函數(shù)返回值類型、參數(shù)tuple、成員函數(shù)指針中的對(duì)象類型[EB/OL].https://www.cnblogs.com/ybmj/p/9651227.html,2018-09-15.

        猜你喜歡
        型函數(shù)調(diào)用容器
        Different Containers不同的容器
        幾類“對(duì)勾”型函數(shù)最值問(wèn)題的解法
        難以置信的事情
        核電項(xiàng)目物項(xiàng)調(diào)用管理的應(yīng)用研究
        LabWindows/CVI下基于ActiveX技術(shù)的Excel調(diào)用
        基于系統(tǒng)調(diào)用的惡意軟件檢測(cè)技術(shù)研究
        取米
        Orlicz Sylvester Busemann型函數(shù)的極值研究
        V-型函數(shù)的周期點(diǎn)
        用共軛法解Dhombres型函數(shù)方程
        少妇激情一区二区三区久久大香香| 乱人伦视频中文字幕| 国产精品黄网站免费观看| 国产在线精品一区二区不卡| 亚洲国产成人精品激情资源9| 一区二区丝袜美腿视频| 在线观看免费不卡网站| 99热在线观看| 97人人超碰国产精品最新o| 久久国产香蕉一区精品天美| 国产熟女自拍av网站| 国产人成无码视频在线观看| 免费人成视频在线观看视频| 亚洲精品美女久久久久99| 国产精品成人av一区二区三区| 丁香美女社区| 午夜一级韩国欧美日本国产| 日本二区三区视频免费观看| 中文字幕在线亚洲三区| 久久久久亚洲av片无码v| 久久国产成人亚洲精品影院老金| 伊人狼人激情综合影院| 蜜桃传媒网站在线观看| 国产ww久久久久久久久久| 97se在线| 亚洲av性色精品国产| 亚洲国产精品成人天堂| 亚洲自偷自拍熟女另类| 国内精品91久久久久| 男女射精视频在线观看网站| 亚洲春色在线视频| 成全视频高清免费| 经典女同一区二区三区| 日本高清不卡二区三区| 久久精品国产99国产精品亚洲| 性饥渴艳妇性色生活片在线播放| 国产精品国产三级国产三不| 人妻熟女翘屁股中文字幕| 手机看片福利一区二区三区| 亚洲精品天堂av免费看| 国产丝袜美腿中文字幕|