• <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>
            posts - 297,  comments - 15,  trackbacks - 0
            出處 http://www.shnenglu.com/cexer/archive/2008/07/06/55484.html

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

              借用 CCTV10《走近科學》的語氣:那么這個神秘的關鍵關鍵字到底是什么呢?它又實現了什么神奇的功能呢?帶著這一連串的疑問,讓我們先來看一個具體的例子。

              我在自己曾經寫的一個GUI框架當中,為了實現消息與處理函數自動映射的,就需要求助于這種功能。比如說有一個窗口類,它包含若干消息處理函數和一個消息與處理函數的映射 map:(請無視當中的 show() 和 create() 函數,與主題無關)

                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  進行循環檢測,檢測 Window 類是否存在該消息對應的處理函數。如果消息對應的處理函數存在,那么就將消息與函數的映射放進 m_handlerMap 當中。比如說消息 WM_CREATE,我檢測類 Window是否存在 LRESULT onEvent( WindowEvent<WM_CREATE> ) 成員函數,在上例代碼中是存在的,于是我將這樣一個映射放進m_handlerMap:(真正實現的時候,還要考慮函數的類型。不同類型的函數,是不能直 接裝進 map 當中的。不過在這里請無視例子當中涉及的所有類型轉換,與主題無關)

                pair<WM_CREATE,&Window::onEvent>


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

              要實現以上的自動映射功能就引出了這樣一個難題:如何編譯期檢測類的某特定名字的成員是否存在。

              功能不負有心人,經過爬山涉水翻山越嶺,我終于在 MSDN 一個偏遠角落里找著了傳說當中那個神秘的關鍵字:__if_exists(其實還有一個 __if_not_exists)。MSDN 當中這樣說明:__if_exists (__if_not_exists)允許你針對某符號的存在與否條件性地執行語句。使用語法:(注意檢測的是“存在性”,而不是值)

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


              MSDN當中的示例代碼如下:
                // 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;
            }


              以上代碼的輸出如下:(未測試,此輸出為MSDN的說明文檔當中的)

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


              大概很少人見過這個關鍵字吧。雖然它們的功能與我的需求是如此的接近,但是面對如此強憾的關鍵字,我還是只能搖頭嘆息。我傷心地在文檔 里看到說明,__if_exists(__if_not_exists)關鍵字用于函數的時候,只能根據函數名字進行檢測,而會忽略對參數列表的檢測,因 此沒有對重載函數的分辨能力,而正是我需要的。比如類 Window 有一個函數:

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


              我用以下代碼來檢測 WM_CREATE 消息是否存在處理函數:

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


              即使 Window 類當中不存在 LRESULT onEvent ( WindowEvent<WM_CREATE> ),以上測試也能通過。這是因為 __if_exists 關鍵字是不管函數重載的,如果存在一個 onEvent ,那么所有的檢測都能通過。這不是我想要的。我需要比 __if_exists 更強憾的檢測功能,強憾到能夠針對不同參數列表的同名函數(重載函數)做出正確的存在性測試。

              于是我繼續翻山越嶺地尋找,從 CSDN 到 MSDN,從 SourceForge 到 CodeProject。要相信那句老話:“有心人天不負”。最后我在 CodeProject 上面看到一篇讓我醍醐灌頂的文章:

              Interface Detection by Alexandre Courpron

              這篇文章從原理到實現,很詳細地說明地一種編譯期檢測技術,先說明一下,由于VC7.1數千個bug當中的一個,以下技術不能在VC++7.1或更低版本上使用。具體的實現在那篇文章當中說得很詳盡了,還是在這兒贅述一下。

              Alexandre Courpron的實現方式基于C++的這樣一個規則:Substitution Failure Is Not An Error (簡稱SFINAE)。它的含義我也理解得比較含糊,不過它作用于重載函數的時候,可以這樣理解:對于一個函數調用,在匹配函數的過程當中,如果最終能夠 有一個函數匹配成功,那么對其余函數的匹配如果失敗,編譯器也不會視為錯誤。聽起來有些麻煩,看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

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

                void f( typename int::Type ) {}


              這顯然是不正確的。不過編譯器并沒有編譯失敗報告錯誤,因為下面的 definition#2 匹配成功,根據 SFINAE的 規則,編譯器有權保持沉默 。

              雖然是個小小的規則,在平時幾乎不會注意它。然而在這兒,我們卻可以利用它實現編譯期檢測的強大功能了,一個最簡單的示例:

                #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;
            }


              上面的代碼會輸出 1。說明一下檢測的過程:

            1. 編譯器遇到 isExists<TestClass>(0) 這一句,會去匹配 isExists 的兩個重載函數。不定長的參數優先級更低,因此先匹配第一個函數。
            2. 第一個函數參數類型為 Param<&T::testFun>*,在這里是 Param<&TestClass::testFun>,編譯器在匹配這個參數類型的時候會嘗試實例化模板類 Param。
            3. 編 譯器嘗試用 &TestClass::testFun 去實例化 Param,因為 TestClass 確實存在一個 void (TestClass::*)() 類型,且名為 testFun 的成員函數。所以 Param 的實例化成功,因此參數匹配成功。
            4. 匹配第一個函數成功。編譯器決定 isExists<TestClass>(0) 這一句調用就是調用的第一個函數。
            5. 因為第一個函數返回的類型為 Exists,用 sizeof 取大小就是 1。

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

                struct TestClass
            {
            void testFun(int);
            };


              這一次代碼會輸出 2。因為在第3步的時候,由于 TestClass 沒有類型為 void (TestClass::*)(),且名為 testFun 的函數,所以實例化 Param 會失敗,因此匹配第一個函數失敗。然后編譯器去匹配第二個函數。因為其參數類型是任意的,自然會匹配成功。結果會輸出 2。

              當然這只是個最簡單的示例,通過模板包裝類。可以實現更靈活更強大的功能。比如回到那個自動消息映射的例子,用以下代碼就能夠實現了:

            //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;
            }




              以上代碼會輸出:

                1
            0
            0
            1


              以上的示例代碼再加上模板元編程,可以很輕易地實現消息的自動映射,具體實現這個已不在本貼的討論范圍并且這種自動映射的實現,太過復雜,在編譯期沒有效率,且不夠靈活。不過在消息映射機制上來說,已稱得上是一種革命性的嘗試。

              在說完了這所有一切之后,再告訴你一個我最近才知道的秘密(不準笑我孤陋寡聞):其實 boost 庫當中已有相關功能的 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

            posted on 2008-07-19 13:50 chatler 閱讀(468) 評論(0)  編輯 收藏 引用 所屬分類: VC_MFC
            <2025年5月>
            27282930123
            45678910
            11121314151617
            18192021222324
            25262728293031
            1234567

            常用鏈接

            留言簿(10)

            隨筆分類(307)

            隨筆檔案(297)

            algorithm

            Books_Free_Online

            C++

            database

            Linux

            Linux shell

            linux socket

            misce

            • cloudward
            • 感覺這個博客還是不錯,雖然做的東西和我不大相關,覺得看看還是有好處的

            network

            OSS

            • Google Android
            • Android is a software stack for mobile devices that includes an operating system, middleware and key applications. This early look at the Android SDK provides the tools and APIs necessary to begin developing applications on the Android platform using the Java programming language.
            • os161 file list

            overall

            搜索

            •  

            最新評論

            閱讀排行榜

            評論排行榜

            久久国产精品久久精品国产| 国产精品成人久久久| 99久久久精品| 狠狠久久综合伊人不卡| 久久亚洲欧洲国产综合| 亚洲日韩中文无码久久| 久久精品一区二区| 久久国产亚洲精品| 青青草国产精品久久| 色天使久久综合网天天| 99久久精品国产麻豆| 最新久久免费视频| 99久久精品无码一区二区毛片| 伊人久久成人成综合网222| 久久九九有精品国产23百花影院| 亚洲欧美日韩久久精品| 国产精品久久国产精品99盘| 久久久久久久久久免免费精品| 久久久久免费视频| 国产成人久久精品激情| 久久精品国产亚洲av麻豆蜜芽| 久久99热国产这有精品| 无码人妻精品一区二区三区久久| 国产午夜福利精品久久| 69SEX久久精品国产麻豆| 久久大香萑太香蕉av| 国产精品伊人久久伊人电影| 久久久无码一区二区三区| 亚洲国产精品无码久久九九| 久久综合成人网| 99久久精品九九亚洲精品| 国产精品久久久久影视不卡| 精品久久久无码21p发布| 久久精品国产亚洲AV忘忧草18| 色8激情欧美成人久久综合电| 狠狠色丁香婷婷久久综合不卡| 亚洲中文字幕无码久久2017 | 一本久道久久综合狠狠爱| 国产成人综合久久精品红| 亚洲欧美国产日韩综合久久| 99久久这里只精品国产免费 |