• <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>

            cexer

            cexer
            posts - 12, comments - 334, trackbacks - 0, articles - 0
              C++博客 :: 首頁(yè) :: 新隨筆 :: 聯(lián)系 :: 聚合  :: 管理

            轉(zhuǎn)帖請(qǐng)注明出處 http://m.shnenglu.com/cexer/archive/2008/07/06/55484.html

              VC當(dāng)中有一個(gè)鮮為人知的關(guān)鍵字,除了微軟自己的代碼,我從未在任何地方看到有人用過(guò)它。雖然它的功能很強(qiáng)大,不過(guò)除非設(shè)計(jì)上的問(wèn)題或是一些無(wú)法排除的困難,否則幾乎從不會(huì)需要用到它的功能。但是有時(shí)候,它確實(shí)能作為一個(gè)最簡(jiǎn)單的解決方案而讓某些設(shè)計(jì)過(guò)程事半功倍。

              借用 CCTV10《走近科學(xué)》的語(yǔ)氣:那么這個(gè)神秘的關(guān)鍵關(guān)鍵字到底是什么呢?它又實(shí)現(xiàn)了什么神奇的功能呢?帶著這一連串的疑問(wèn),讓我們先來(lái)看一個(gè)具體的例子。

              我在自己曾經(jīng)寫(xiě)的一個(gè)GUI框架當(dāng)中,為了實(shí)現(xiàn)消息與處理函數(shù)自動(dòng)映射的,就需要求助于這種功能。比如說(shuō)有一個(gè)窗口類(lèi),它包含若干消息處理函數(shù)和一個(gè)消息與處理函數(shù)的映射 map:(請(qǐng)無(wú)視當(dāng)中的 show() 和 create() 函數(shù),與主題無(wú)關(guān))

                class Window
            {
            typedef UINT _Message;
            typedef LRESULT (Window::*_Handler)(_Message);

            map<_Message,_Handler> m_handlerMap;

            public:
            bool show();
            bool create();

            public:
            LRESULT onEvent( WindowEvent<WM_CREATE> );
            LRESULT onEvent( WindowEvent<WM_DESTROY> );
            };

              
              我需要利用模板元編程 從 0 到 WM_USER  進(jìn)行循環(huán)檢測(cè),檢測(cè) Window 類(lèi)是否存在該消息對(duì)應(yīng)的處理函數(shù)。如果消息對(duì)應(yīng)的處理函數(shù)存在,那么就將消息與函數(shù)的映射放進(jìn) m_handlerMap 當(dāng)中。比如說(shuō)消息 WM_CREATE,我檢測(cè)類(lèi) Window是否存在 LRESULT onEvent( WindowEvent<WM_CREATE> ) 成員函數(shù),在上例代碼中是存在的,于是我將這樣一個(gè)映射放進(jìn)m_handlerMap:(真正實(shí)現(xiàn)的時(shí)候,還要考慮函數(shù)的類(lèi)型。不同類(lèi)型的函數(shù),是不能直接裝進(jìn) map 當(dāng)中的。不過(guò)在這里請(qǐng)無(wú)視例子當(dāng)中涉及的所有類(lèi)型轉(zhuǎn)換,與主題無(wú)關(guān))

                pair<WM_CREATE,&Window::onEvent>


              這樣就達(dá)到了消息自動(dòng)映射的目的。而不用像MFC一樣手寫(xiě)宏去映射。(最后通過(guò)努力的確達(dá)到了我的目的,我的GUI框架能夠進(jìn)行自動(dòng)消息映射了,然而可以預(yù)見(jiàn),由于幾千個(gè)(0-WM_USER)循環(huán),編譯期的速度受到極大影響。所以最終我還是拋棄了這種自動(dòng)映射實(shí)現(xiàn),而采用了更高效神奇的方法,這是后話也與本主題無(wú)關(guān)就先不提)。

              要實(shí)現(xiàn)以上的自動(dòng)映射功能就引出了這樣一個(gè)難題:如何編譯期檢測(cè)類(lèi)的某特定名字的成員是否存在。

              功能不負(fù)有心人,經(jīng)過(guò)爬山涉水翻山越嶺,我終于在 MSDN 一個(gè)偏遠(yuǎn)角落里找著了傳說(shuō)當(dāng)中那個(gè)神秘的關(guān)鍵字:__if_exists(其實(shí)還有一個(gè) __if_not_exists)。MSDN 當(dāng)中這樣說(shuō)明:__if_exists (__if_not_exists)允許你針對(duì)某符號(hào)的存在與否條件性地執(zhí)行語(yǔ)句。使用語(yǔ)法:(注意檢測(cè)的是“存在性”,而不是值)

                __if_exists ( /*你要檢測(cè)存在性的函數(shù)或變量的名字*/ ) { 
             //做些有用的事
            }


              MSDN當(dāng)中的示例代碼如下:
                // the__if_exists_statement.cpp
            // compile with: /EHsc
            #include <iostream>

            template<typename T>
            class X : public T {
            public:
            void Dump() {
            std::cout << "In X<T>::Dump()" << std::endl;

            __if_exists(T::Dump) {
            T::Dump();
            }

            __if_not_exists(T::Dump) {
            std::cout << "T::Dump does not exist" << std::endl;
            }
            }
            };

            class A {
            public:
            void Dump() {
            std::cout << "In A::Dump()" << std::endl;
            }
            };

            class B {};

            bool g_bFlag = true;

            class C {
            public:
            void f(int);
            void f(double);
            };

            int main() {
            X<A> x1;
            X<B> x2;

            x1.Dump();
            x2.Dump();

            __if_exists(::g_bFlag) {
            std::cout << "g_bFlag = " << g_bFlag << std::endl;
            }

            __if_exists(C::f) {
            std::cout << "C::f exists" << std::endl;
            }

            return 0;
            }


              以上代碼的輸出如下:(未測(cè)試,此輸出為MSDN的說(shuō)明文檔當(dāng)中的)

                In X<T>::Dump()
            In A::Dump()
            In X<T>::Dump()
            T::Dump does not exist
            g_bFlag = 1
            C::f exists


              大概很少人見(jiàn)過(guò)這個(gè)關(guān)鍵字吧。雖然它們的功能與我的需求是如此的接近,但是面對(duì)如此強(qiáng)憾的關(guān)鍵字,我還是只能搖頭嘆息。我傷心地在文檔里看到說(shuō)明,__if_exists(__if_not_exists)關(guān)鍵字用于函數(shù)的時(shí)候,只能根據(jù)函數(shù)名字進(jìn)行檢測(cè),而會(huì)忽略對(duì)參數(shù)列表的檢測(cè),因此沒(méi)有對(duì)重載函數(shù)的分辨能力,而正是我需要的。比如類(lèi) Window 有一個(gè)函數(shù):

                LRESULT Window::onEvent( WindowEvent<WM_DESTROY> )
            {
            //做些有用的事
            }


              我用以下代碼來(lái)檢測(cè) WM_CREATE 消息是否存在處理函數(shù):

                __if_exists(Window::onEvent)
              {
                  //添加消息映射
               }


              即使 Window 類(lèi)當(dāng)中不存在 LRESULT onEvent ( WindowEvent<WM_CREATE> ),以上測(cè)試也能通過(guò)。這是因?yàn)?__if_exists 關(guān)鍵字是不管函數(shù)重載的,如果存在一個(gè) onEvent ,那么所有的檢測(cè)都能通過(guò)。這不是我想要的。我需要比 __if_exists 更強(qiáng)憾的檢測(cè)功能,強(qiáng)憾到能夠針對(duì)不同參數(shù)列表的同名函數(shù)(重載函數(shù))做出正確的存在性測(cè)試。

              于是我繼續(xù)翻山越嶺地尋找,從 CSDN 到 MSDN,從 SourceForge 到 CodeProject。要相信那句老話:“有心人天不負(fù)”。最后我在 CodeProject 上面看到一篇讓我醍醐灌頂?shù)奈恼拢?/p>

              Interface Detection by Alexandre Courpron

              這篇文章從原理到實(shí)現(xiàn),很詳細(xì)地說(shuō)明地一種編譯期檢測(cè)技術(shù),先說(shuō)明一下,由于VC7.1數(shù)千個(gè)bug當(dāng)中的一個(gè),以下技術(shù)不能在VC++7.1或更低版本上使用。具體的實(shí)現(xiàn)在那篇文章當(dāng)中說(shuō)得很詳盡了,還是在這兒贅述一下。

              Alexandre Courpron的實(shí)現(xiàn)方式基于C++的這樣一個(gè)規(guī)則:Substitution Failure Is Not An Error(簡(jiǎn)稱(chēng)SFINAE)。它的含義我也理解得比較含糊,不過(guò)它作用于重載函數(shù)的時(shí)候,可以這樣理解:對(duì)于一個(gè)函數(shù)調(diào)用,在匹配函數(shù)的過(guò)程當(dāng)中,如果最終能夠有一個(gè)函數(shù)匹配成功,那么對(duì)其余函數(shù)的匹配如果失敗,編譯器也不會(huì)視為錯(cuò)誤。聽(tīng)起來(lái)有些麻煩,看Alexandre Courpron給出的例子:

                struct Test 
            {
            typedef int Type;
            };

            template < typename T >
            void f(typename T::Type) {} // definition #1

            template<typename T>
            void f(T){} // definition #2

            f<Test>(10); //call #1

            f<int>(10); //call #2

              
              對(duì)于 call#1 編譯器直接匹配 definition#1 成功。對(duì)于 call#2,編譯器先用 definition#1 匹配 如下:

                void f( typename int::Type ) {}


              這顯然是不正確的。不過(guò)編譯器并沒(méi)有編譯失敗報(bào)告錯(cuò)誤,因?yàn)橄旅娴?definition#2 匹配成功,根據(jù) SFINAE的 規(guī)則,編譯器有權(quán)保持沉默 。

              雖然是個(gè)小小的規(guī)則,在平時(shí)幾乎不會(huì)注意它。然而在這兒,我們卻可以利用它實(shí)現(xiàn)編譯期檢測(cè)的強(qiáng)大功能了,一個(gè)最簡(jiǎn)單的示例:

                #include <iostream>
            using namespace std;
            //
            struct TestClass
            {
            void testFun();
            };

            struct Exists { char x;};
            struct NotExists { char x[2]; };

            template <void (TestClass::*)()>
            struct Param ;

            template <class T>
            Exists isExists( Param<&T::testFun>* );

            template <class T>
            NotExists isExists( ... );
            //
            int main()
            {
            cout<<sizeof(isExists<TestClass>(0))<<endl;
            }


              上面的代碼會(huì)輸出 1。說(shuō)明一下檢測(cè)的過(guò)程:

            1. 編譯器遇到 isExists<TestClass>(0) 這一句,會(huì)去匹配 isExists 的兩個(gè)重載函數(shù)。不定長(zhǎng)的參數(shù)優(yōu)先級(jí)更低,因此先匹配第一個(gè)函數(shù)。
            2. 第一個(gè)函數(shù)參數(shù)類(lèi)型為 Param<&T::testFun>*,在這里是 Param<&TestClass::testFun>,編譯器在匹配這個(gè)參數(shù)類(lèi)型的時(shí)候會(huì)嘗試實(shí)例化模板類(lèi) Param。
            3. 編譯器嘗試用 &TestClass::testFun 去實(shí)例化 Param,因?yàn)?TestClass 確實(shí)存在一個(gè) void (TestClass::*)() 類(lèi)型,且名為 testFun 的成員函數(shù)。所以 Param 的實(shí)例化成功,因此參數(shù)匹配成功。
            4. 匹配第一個(gè)函數(shù)成功。編譯器決定 isExists<TestClass>(0) 這一句調(diào)用就是調(diào)用的第一個(gè)函數(shù)。
            5. 因?yàn)榈谝粋€(gè)函數(shù)返回的類(lèi)型為 Exists,用 sizeof 取大小就是 1。

              如果是我們把 TestClass 的定義修改為:(僅把函數(shù)的參數(shù)類(lèi)型改為 int )

                struct TestClass
            {
            void testFun(int);
            };


              這一次代碼會(huì)輸出 2。因?yàn)樵诘冢巢降臅r(shí)候,由于 TestClass 沒(méi)有類(lèi)型為 void (TestClass::*)(),且名為 testFun 的函數(shù),所以實(shí)例化 Param 會(huì)失敗,因此匹配第一個(gè)函數(shù)失敗。然后編譯器去匹配第二個(gè)函數(shù)。因?yàn)槠鋮?shù)類(lèi)型是任意的,自然會(huì)匹配成功。結(jié)果會(huì)輸出 2。

              當(dāng)然這只是個(gè)最簡(jiǎn)單的示例,通過(guò)模板包裝類(lèi)。可以實(shí)現(xiàn)更靈活更強(qiáng)大的功能。比如回到那個(gè)自動(dòng)消息映射的例子,用以下代碼就能夠?qū)崿F(xiàn)了:

            //c++std
            #include <iostream>
            using namespace std;



            //windows
            #include <windows.h>


            //detector
            template<typename TWindow,UINT t_msg>
            struct MessageHandlerDetector
            {
            typedef WindowEvent<t_msg> _Event;

            struct Exists {char x;};
            struct NotExists {char x[2];};

            template<LRESULT (TWindow::*)(_Event)>
            struct Param;

            template<typename T>
            static Exists detect( Param<&T::onEvent>* );

            template<typename T>
            static NotExists detect( ... );

            public:
            enum{isExists=sizeof(detect<TWindow>(0))==sizeof(Exists)};
            };

            //test classes
            struct Window
            {
            LRESULT onEvent( WindowEvent<WM_CREATE> );
            };

            struct Button
            {
            LRESULT onEvent( WindowEvent<WM_DESTROY> );
            };

            //main
            int main()
            {
            cout<<MessageHandlerDetector<Window,WM_CREATE>::isExists<<endl;
            cout<<MessageHandlerDetector<Window,WM_DESTROY>::isExists<<endl;
            cout<<MessageHandlerDetector<Button,WM_CREATE>::isExists<<endl;
            cout<<MessageHandlerDetector<Button,WM_DESTROY>::isExists<<endl;

            return 0;
            }



              以上代碼會(huì)輸出:

                1
            0
            0
            1


              以上的示例代碼再加上模板元編程,可以很輕易地實(shí)現(xiàn)消息的自動(dòng)映射,具體實(shí)現(xiàn)這個(gè)已不在本貼的討論范圍并且這種自動(dòng)映射的實(shí)現(xiàn),太過(guò)復(fù)雜,在編譯期沒(méi)有效率,且不夠靈活。不過(guò)在消息映射機(jī)制上來(lái)說(shuō),已稱(chēng)得上是一種革命性的嘗試。

              在說(shuō)完了這所有一切之后,再告訴你一個(gè)我最近才知道的秘密(不準(zhǔn)笑我孤陋寡聞):其實(shí) boost 庫(kù)當(dāng)中已有相關(guān)功能的 MPL  工具存在,叫做 has_xxx。

              源文件:<boost\mpl\has_xxx.hpp>

              文檔:http://www.boost.org/doc/libs/1_35_0/libs/mpl/doc/refmanual/has-xxx-trait-def.html

            Feedback

            # re: C++編譯期函數(shù)/變量檢測(cè)技術(shù),超越VC的某某關(guān)鍵字  回復(fù)  更多評(píng)論   

            2008-07-06 23:54 by Bill Gates
            不就是traits嗎

            # re: C++編譯期函數(shù)/變量檢測(cè)技術(shù),超越VC的某某關(guān)鍵字  回復(fù)  更多評(píng)論   

            2008-07-06 23:56 by cexer
            不是。并不是涉及模板就能用traits來(lái)說(shuō)事的。

            # re: C++編譯期函數(shù)/變量檢測(cè)技術(shù),仿真VC關(guān)鍵字__if_exists  回復(fù)  更多評(píng)論   

            2008-07-08 18:29 by 周星星
            好方法呀,俺也一直在尋找 __if_exists 的替代方法(用標(biāo)準(zhǔn)C++語(yǔ)法)

            # re: C++編譯期函數(shù)/變量檢測(cè)技術(shù),仿真VC關(guān)鍵字__if_exists  回復(fù)  更多評(píng)論   

            2008-07-08 19:50 by cexer
            你可以看看,boost有更好的實(shí)現(xiàn)。

            # re: C++編譯期函數(shù)/變量檢測(cè)技術(shù),仿真VC關(guān)鍵字__if_exists  回復(fù)  更多評(píng)論   

            2008-07-10 23:13 by 夢(mèng)在天涯
            沒(méi)用過(guò),有難度,長(zhǎng)見(jiàn)識(shí)!

            # re: C++編譯期函數(shù)/變量檢測(cè)技術(shù),仿真VC關(guān)鍵字__if_exists  回復(fù)  更多評(píng)論   

            2008-08-19 12:47 by 螞蟻終結(jié)者
            不錯(cuò),有點(diǎn)意思。

            # re: C++編譯期函數(shù)/變量檢測(cè)技術(shù),仿真VC關(guān)鍵字__if_exists  回復(fù)  更多評(píng)論   

            2009-09-24 11:20 by pipilu
            問(wèn)一下怎么編譯器比較兩個(gè)常數(shù)來(lái)設(shè)置編譯呢

            #define AAA 12

            // 如果 AAA > 12 則編譯下面內(nèi)容
            bool bUse = 134;

            # re: C++編譯期函數(shù)/變量檢測(cè)技術(shù),仿真VC關(guān)鍵字__if_exists  回復(fù)  更多評(píng)論   

            2012-06-01 01:09 by 春秋十二月
            不錯(cuò) 受益了

            # re: C++編譯期函數(shù)/變量檢測(cè)技術(shù),仿真VC關(guān)鍵字__if_exists  回復(fù)  更多評(píng)論   

            2013-05-30 14:01 by zirandeai
            我覺(jué)得這只是語(yǔ)法糖,增加了復(fù)雜度,卻并未讓人看到實(shí)質(zhì)性的好處
            亚洲国产香蕉人人爽成AV片久久 | www.久久99| 久久精品中文字幕无码绿巨人 | 日韩欧美亚洲综合久久| 国产一级持黄大片99久久| 欧美亚洲国产精品久久| 日韩av无码久久精品免费| 久久人人爽人人爽人人片AV东京热| 久久精品国产影库免费看| 久久国产香蕉视频| 久久精品亚洲男人的天堂| 久久精品国产99国产精品亚洲| 久久久久久九九99精品| 久久久久女教师免费一区| av色综合久久天堂av色综合在| 麻豆久久久9性大片| 久久一区二区免费播放| 久久影视综合亚洲| 狼狼综合久久久久综合网| 久久精品中文字幕有码| 久久亚洲精精品中文字幕| 精品国产91久久久久久久a| 久久久久久久久久久免费精品| 性高湖久久久久久久久| 综合网日日天干夜夜久久| 久久香蕉国产线看观看乱码 | 亚洲国产高清精品线久久| 久久久无码人妻精品无码| 欧美成a人片免费看久久| www.久久精品| 午夜人妻久久久久久久久| 久久综合亚洲色HEZYO国产 | 久久久久亚洲av成人网人人软件 | 婷婷久久综合| 久久天天婷婷五月俺也去| 久久综合久久久| 少妇精品久久久一区二区三区| 久久亚洲高清综合| 久久99精品久久久久久不卡| 久久99国内精品自在现线| 久久综合狠狠综合久久激情 |