• <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)系 :: 聚合  :: 管理

            GUI框架:消息檢查者

            Posted on 2009-11-22 02:36 cexer 閱讀(3723) 評(píng)論(40)  編輯 收藏 引用 所屬分類(lèi): GUI

            轉(zhuǎn)帖請(qǐng)注明出處 http://www.shnenglu.com/cexer/archive/2009/11/22/101591.html

            1 胸口碎大石

              緊接上話:GUI框架:談?wù)効蚣埽瑢?xiě)寫(xiě)代碼 。廢話是肯定首先要說(shuō)的,既為了承前啟后點(diǎn)明主題,也為了拉攏人心騙取回復(fù)。本來(lái)我想像自己上篇博文寫(xiě)出來(lái)勢(shì)必像胸口碎大石一樣威猛有力,在街邊拉開(kāi)陣勢(shì),大吼一聲舉起錘子正要往下砸的時(shí)候,卻看到幾位神仙手提醬油瓶?jī)?yōu)雅地踏著凌波微步路過(guò),聽(tīng)他們開(kāi)口閉口說(shuō)的都是六脈神劍啊,九陰真級(jí)啊這些高級(jí)東西,我的威猛感一下消失于無(wú)形,取而代之的是小孩子玩水槍的渺小。但是不管怎么樣攤子都鋪開(kāi)了,這一錘子不砸下去,對(duì)不起那涼了半天石頭的胸肌。

              在此之前首先得感謝一下各位醬油眾。無(wú)論你們是看熱鬧的還是砸場(chǎng)子的,你們的圍觀都令我的博文增光不少。特別要感謝那幾位打架的神仙,你們使上篇博文真正變得有思想交鋒的精彩。我覺(jué)得你們的那些想法和爭(zhēng)論都非常有價(jià)值,建議你們不要只讓它們?cè)谶@個(gè)角落里藏著,都寫(xiě)到自己的博客上去讓更多的人看到吧。

              走過(guò)路過(guò)不要錯(cuò)過(guò),有錢(qián)的捧個(gè)錢(qián)場(chǎng),沒(méi)錢(qián)的繼續(xù)揮舞你的醬油瓶加油吶喊,我這一錘要砸下去了!

            2 實(shí)現(xiàn)消息檢查者

              上文將消息框架分為幾個(gè)部分,這篇博文實(shí)現(xiàn)其中的消息檢查者。經(jīng)典的用 API 編寫(xiě) GUI 程序的方式當(dāng)中,消息檢查都是用 if 或者 switch 語(yǔ)句進(jìn)行的:

               1: // 經(jīng)典的 API 方式
            
               2: switch( message )
            
               3: {
            
               4:     case WM_CREATE:
            
               5:         // ......
            
               6:         break;
            
               7:     case WM_PAINT:
            
               8:         // ......
            
               9:         break;
            
              10:     default:
            
              11:         // ......
            
              12: }
            
              13:
            
              14: // MFC 映射宏展開(kāi)
            
              15: if ( message == WM_CREATE )
            
              16: {
            
              17:     // ......
            
              18: }
            
              19: if ( message == WM_PAINT )
            
              20: {
            
              21:     // ......
            
              22: }
            

              見(jiàn)過(guò)的很多的 GUI 框架并沒(méi)有在這原始的方式上進(jìn)步多少,"只是將黑換成暗"。比如 MFC 和 WTL 的消息映射宏,就像是披在 if 語(yǔ)句上的皇帝的新衣。這種消息檢查方式的好處是速度快,不用額外的空間消耗,但壞處更明顯:不容易擴(kuò)充。我覺(jué)得在好處和壞處之間的取舍很容易,有必要單獨(dú)給消息檢查的過(guò)程實(shí)現(xiàn)一個(gè)更具 OO 含義的執(zhí)行者:消息檢查者(MessageChecker )。

            2.1 其實(shí)很簡(jiǎn)單

              要有消息檢查者,首先得有個(gè)消息(Message)。上篇博文中的消息定義雖然非常簡(jiǎn)單,卻完全可以勝任目前需要的工作,因此我們直接復(fù)制過(guò)來(lái)。

               1: typedef LRESULT    MessageResult;
            
               2: typedef UINT       MessageId;
            
               3: typedef WPARAM     MessageWparam;
            
               4: typedef LPARAM     MessageLparam;
            
               5:
            
               6: // 簡(jiǎn)單的消息定義 
            
               7: class Message
            
               8: {
            
               9: public:
            
              10:     Message( MessageId id_=0,MessageWparam wp_=0,MessageLparam lp_=0 )
            
              11:         :id( id_ )
            
              12:         ,wparam( wp_ )
            
              13:         ,lparam( lp_ )
            
              14:         ,result( 0 )
            
              15:     {}
            
              16:
            
              17: public:
            
              18:     MessageResult    result;
            
              19:     MessageId        id;
            
              20:     MessageWparam    wparam;
            
              21:     MessageLparam    lparam;
            
              22: };
            

              有了消息,現(xiàn)在開(kāi)始定義消息檢查者。消息檢查者的職責(zé):根據(jù)某種條件檢查消息并返回(是,否)的檢查結(jié)果,所以它既應(yīng)該有用于檢查的數(shù)據(jù),也應(yīng)該有用于檢查的動(dòng)作函數(shù),并且該函數(shù)檢查返回布爾值。這不是很容易就出來(lái)了嗎:

               1: // 領(lǐng)銜的消息檢查者
            
               2: class MessageChecker
            
               3: {
            
               4: public:
            
               5:     MessageChecker( MessageId id_=0,MessageWparam wp_=0,MessageLparam lp_=0 )
            
               6:         :id( id_ )
            
               7:         ,wparam( wp_ )
            
               8:         ,lparam( lp_ )
            
               9:     {}
            
              10:
            
              11: public:
            
              12:     // 用于檢查消息的函數(shù)
            
              13:     virtual bool    isOk( const Message& message ) const;
            
              14:
            
              15: public:
            
              16:     // 用于檢查消息的數(shù)據(jù)
            
              17:     MessageId        id;
            
              18:     MessageWparam    wparam;
            
              19:     MessageLparam    lparam;
            
              20: };
            

              其中 MessageChecker::isOk 很明顯應(yīng)該可以被后繼者重寫(xiě)以實(shí)現(xiàn)不同的檢查方式,所以它應(yīng)該是虛函數(shù),這樣后繼者可以這樣的形式擴(kuò)充檢查者隊(duì)伍:

               1: // 命令消息檢查者
            
               2: class CommandChecker:public MessageChecker
            
               3: {
            
               4: public:
            
               5:     virtual bool isOk( const Message& message );
            
               6: };
            
               7:
            
               8: // 通知消息檢查者
            
               9: class NotifyChecker:public MessageChecker
            
              10: {
            
              11: public:
            
              12:     virtual bool isOk( const Message& message );
            
              13: };
            

              看著 MessageChecker,CommandChecker,NotifyChecker 這些和諧的名字,感覺(jué)消息檢查者就這樣實(shí)現(xiàn)完成了,我們的 GUI 框架似乎已經(jīng)成功邁出了重要的第一步。但是面對(duì)函數(shù) isOk ,有經(jīng)驗(yàn)的程序員肯定會(huì)有疑問(wèn):真的這樣簡(jiǎn)單就 ok 了?當(dāng)然是 no。要是真有那么簡(jiǎn)單,我何苦還在后面寫(xiě)那么長(zhǎng)的篇符呢,cppblog 又不能多寫(xiě)字騙稿費(fèi)的。

            2.2 堆上生成 & 對(duì)象切割

              看著虛函數(shù) isOk 會(huì)想聯(lián)到兩個(gè)關(guān)鍵詞:多態(tài),指針。多態(tài)意味著要保存為指針,指針意味著要在堆上生成。消息檢查者保存為指針的映射像下面這樣子(假設(shè)消息處理者叫做 MessageHandler ):

               1: // 允許一個(gè)消息對(duì)應(yīng)多個(gè)處理者
            
               2: typedef vector<MessageHandler>                _HandlerVector;
            
               3:
            
               4: // 為了多態(tài)必須保存 MessageChecker 的指針
            
               5: typedef pair<MessageChecker*,_HandlerVector>   _HandlerPair;
            
               6:
            
               7: // 消息映射
            
               8: typedef vector<_HandlerPair>                 _HandlerMap;
            

              堆上生成是萬(wàn)惡之源。誰(shuí)來(lái)負(fù)責(zé)銷(xiāo)毀?何時(shí)銷(xiāo)毀?效率問(wèn)題怎么辦?有的人此時(shí)可能想到了引用計(jì)數(shù),小對(duì)象分配技術(shù),內(nèi)存池。。。只是一個(gè)消息檢查的動(dòng)作就用那么昂貴的實(shí)現(xiàn),就像花兩萬(wàn)塊買(mǎi)張鼠標(biāo)墊一樣讓人難以接受。所以我們想保存消息映射者的對(duì)象 MessageChecker 而不是它的指針,就像這個(gè)樣子:

               1: // 允許一個(gè)消息對(duì)應(yīng)多個(gè)處理者
            
               2: typedef vector<MessageHandler>                _HandlerVector;
            
               3:
            
               4: // 保存 MessageChecker 對(duì)象而不是指針
            
               5: typedef pair<MessageChecker,_HandlerVector>   _HandlerPair;
            
               6:
            
               7: // 消息映射
            
               8: typedef vector<_HandlerPair>                 _HandlerMap;
            

              但這樣的保存方式帶來(lái)了一個(gè)新的問(wèn)題:對(duì)象切割。如果往映射中放入派生類(lèi)的對(duì)象比如 CommandChecker 或者 NotifyChecker,編譯器會(huì)鐵手無(wú)情地對(duì)它們進(jìn)行切割,切割得它們體無(wú)完膚搖搖欲墜,只剩下 MessageChecker 子對(duì)象為止 ??愁^不要緊,只要主義真,但是要是切割過(guò)程中切掉了虛函數(shù)表這個(gè)命根子就完蛋了,在手執(zhí)電鋸的編譯器面前玩耍虛函數(shù),很難說(shuō)會(huì)發(fā)生什么可怕的事情。

              有一種解決方案是我們不使用真正的虛函數(shù),而是自己模擬虛函數(shù)的功能。具體辦法是在 MessageChecker 當(dāng)中保存一個(gè)函數(shù)指針,由子類(lèi)去把它指向自己實(shí)現(xiàn)的函數(shù),非虛的 MessageChecker::isOk 函數(shù)去調(diào)用這個(gè)指針。修改 MessageChecker 的定義:

               1: // 領(lǐng)銜的消息檢查者,用函數(shù)指針模擬虛函數(shù)
            
               2: class MessageChecker
            
               3: {
            
               4:     // 模擬虛函數(shù)的函數(shù)指針類(lèi)型
            
               5:     typedef bool (*_VirtualIsOk)( const MessageChecker* pthis,const Message& message );
            
               6:
            
               7: public:
            
               8:     MessageChecker( MessageId id_=0,MessageWparam wp_=0,MessageLparam lp_=0,_VirtualIsOk is_=&MessageChecker::virtualIsOk )
            
               9:         :id( id_ )
            
              10:         ,wparam( wp_ )
            
              11:         ,lparam( lp_ )
            
              12:         ,m_visok( is_ )
            
              13:     {}
            
              14:
            
              15: public:
            
              16:     // 非虛函數(shù)調(diào)用函數(shù)指針
            
              17:     bool isOk( const Message& message ) const
            
              18:     {
            
              19:         return m_visok( this,message );
            
              20:     }
            
              21:
            
              22: protected:
            
              23:     // 不騙你,我真的是虛函數(shù)
            
              24:     static bool virtualIsOk( const MessageChecker* pthis,const Message& message )
            
              25:     {
            
              26:         return pthis->id == message.id;
            
              27:     }
            
              28:
            
              29: public:
            
              30:     // 用于檢查消息的數(shù)據(jù)
            
              31:     MessageId        id;
            
              32:     MessageWparam    wparam;
            
              33:     MessageLparam    lparam;
            
              34:
            
              35: protected:
            
              36:     // 模擬虛函數(shù)的函數(shù)指針
            
              37:     _VirtualIsOk    m_visok;
            
              38: };
            

              如代碼所示的,MessageChecker::virtualIsOk 的默認(rèn)實(shí)現(xiàn)是只檢查消息id,這也是 MFC 的映射宏 MESSAGE_HANDLER 干的事情?,F(xiàn)在解決了不要堆生成與要求多態(tài)的矛盾關(guān)系,真正完成了消息檢查者的定義。

            2.3 擴(kuò)充隊(duì)伍

              要擴(kuò)展消息檢查者隊(duì)伍,可以從 MessageChecker 派生新類(lèi),定義自己的檢查函數(shù)(virtualIsOk 或者其它名字都可以)并將 MessageChecker::m_visok 指向這個(gè)函數(shù)。要注意的是因?yàn)榇嬖趯?duì)象切割問(wèn)題,所以派生類(lèi)不應(yīng)該定義新的數(shù)據(jù)成員,畢竟切掉花花草草也是非常不好的。舉例說(shuō)明派生方法。

              比如從消息檢查者派生一個(gè)命令消息(Command)的檢查者 CommandChecker:它定義了一個(gè)函數(shù) virtualIsOk ,此函數(shù)檢查消息id是否為 WM_COMMAND ,并進(jìn)一步檢查控件id和命令code,然后將指針 Message::m_visok 指向這個(gè)函數(shù) CommandChecker::virualIsOk,這樣 MessageChecker::isOk 實(shí)際上就是調(diào)用的 CommandChecker::virtualIsOk 了。CommandChecker 的功能類(lèi)似于 MFC 的宏 COMMAND_HANDLER:

               1: // 命令消息檢查者
            
               2: class CommandChecker:public MessageChecker
            
               3: {
            
               4: public:
            
               5:     // 將函數(shù)指針指向自己的函數(shù) CommandChecker;:virtualIsOk
            
               6:     CommandChecker( WORD id,WORD code )
            
               7:         :MessageChecker( WM_COMMAND,MAKEWPARAM(id,code),0,&CommandChecker::virtualIsOk )
            
               8:     {}
            
               9:
            
              10: protected:
            
              11:     // 檢查消息id是否為 WM_COMMAND,并進(jìn)一步檢查控件id和命令code
            
              12:     static bool virtualIsOk( const MessageChecker* pthis,const Message& message )
            
              13:     {
            
              14:         WORD idToCheck   = LOWORD( message.wparam );
            
              15:         WORD codeToCheck = HIWORD( message.wparam );
            
              16:
            
              17:         WORD id   = LOWORD( pthis->wparam );
            
              18:         WORD code = HIWORD( pthis->wparam );
            
              19:
            
              20:         return message.id==WM_COMMAND && idToCheck==id && codeToCheck==code;
            
              21:     }
            
              22: };
            

              同理定義一個(gè)通知消息(Notification)的檢查者 NotifyChecker,其功能類(lèi)似于 MFC 的宏 NOTIFY_HANDLER :

               1: // 通知消息檢查者
            
               2: class NotifyChecker:public MessageChecker
            
               3: {
            
               4: public:
            
               5:     // 將函數(shù)指針指向自己的函數(shù) NotifyChecker;:virtualIsOk
            
               6:     NotifyChecker( WORD id,WORD code )
            
               7:         :MessageChecker( WM_NOTIFY,MAKEWPARAM(id,code),0,&NotifyChecker::virtualIsOk )
            
               8:     {}
            
               9:
            
              10: public:
            
              11:     // 檢查消息的 id 是否為 WM_NOTIFY ,并進(jìn)一步檢查控件 id 和命令 code
            
              12:     static bool virtualIsOk(  const MessageChecker* pthis,const Message& message )
            
              13:     {
            
              14:         WORD idToCheck   = reinterpret_cast<NMHDR*>(message.lparam)->idFrom;
            
              15:         WORD codeToCheck = reinterpret_cast<NMHDR*>(message.lparam)->code;
            
              16:
            
              17:         WORD id   = LOWORD( pthis->wparam );
            
              18:         WORD code = HIWORD( pthis->wparam );
            
              19:
            
              20:         return message.id==WM_NOTIFY && idToCheck==id && codeToCheck==code;
            
              21:     }
            
              22: };
            

              發(fā)揮想像進(jìn)行擴(kuò)展,這個(gè)消息檢查者可以做很多事情。比如定義一個(gè)范圍id內(nèi)命令消息的檢查者 RangeIdCommandChecker,其功能類(lèi)似于 MFC 的 ON_COMMAND_RANGE 宏:

               1: // 范圍 id 命令消息檢查者
            
               2: class RangeIdCommandChecker:public MessageChecker
            
               3: {
            
               4: public:
            
               5:     // 將函數(shù)指針指向自己的函數(shù) RangeIdCommandChecker;:virtualIsOk
            
               6:     RangeIdCommandChecker( WORD idMin,WORD idMax,WORD code )
            
               7:         :MessageChecker( WM_COMMAND,MAKEWPARAM(idMin,idMax),MAKELPARAM(code,0),&RangeIdCommandChecker::virtualIsOk )
            
               8:     {}
            
               9:
            
              10: protected:
            
              11:     // 檢查消息 id 是否為 WM_COMMAND,并進(jìn)一步檢查控件 id 范圍和命令 code
            
              12:     static bool virtualIsOk( const MessageChecker* pthis,const Message& message )
            
              13:     {
            
              14:         WORD idToCheck   = LOWORD( message.wparam );
            
              15:         WORD codeToCheck = HIWORD( message.wparam );
            
              16:
            
              17:         WORD idMin = LOWORD( pthis->wparam );
            
              18:         WORD idMax = HIWORD( pthis->wparam );
            
              19:         WORD code  = LOWORD( pthis->lparam );
            
              20:
            
              21:         return message.id==WM_COMMAND && codeToCheck==code && idToCheck>=idMin && idToCheck<=idMax;
            
              22:     }
            
              23: };
            

              定義一個(gè)按鈕點(diǎn)擊消息的消息檢查者:

               1: // 按鈕點(diǎn)擊的命令消息檢查者
            
               2: class ButtonClickingChecker:public MessageChecker
            
               3: {
            
               4: public:
            
               5:     // 將函數(shù)指針指向自己的函數(shù) ButtonClickChecker;:virtualIsOk
            
               6:     ButtonClickingChecker( WORD id )
            
               7:         :MessageChecker( WM_COMMAND,MAKEWPARAM(id,BN_CLICKED),0,&ButtonClickingChecker::virtualIsOk )
            
               8:     {}
            
               9:
            
              10: protected:
            
              11:     // 檢查消息id是否為 WM_COMMAND,并進(jìn)一步檢查命令code是否為 BN_CLICKED 以及按鈕id
            
              12:     static bool virtualIsOk( const MessageChecker* pthis,const Message& message )
            
              13:     {
            
              14:         WORD idToCheck   = LOWORD( message.wparam );
            
              15:         WORD codeToCheck = HIWORD( message.wparam );
            
              16:
            
              17:         WORD  id   = LOWORD( pthis->wparam );
            
              18:         WORD  code = BN_CLICKED;
            
              19:
            
              20:         return message.id==WM_COMMAND && idToCheck==id && codeToCheck==code;
            
              21:     }
            
              22: };
            

            2.4 數(shù)據(jù)不夠用

              這一節(jié)是新加的。因?yàn)橛腥颂岢鰯?shù)據(jù)容納不下的問(wèn)題:MessageChecker 目前的定義只能容納 WPAWAM,LPARAM 兩個(gè)參數(shù)大小的數(shù)據(jù),而因?yàn)樵诖嬖趯?duì)象切割問(wèn)題,子類(lèi)又不能定義新的數(shù)據(jù),那如果 WPARAM,LPARAM 裝不下需要的數(shù)據(jù)了怎么辦?難道就只能把數(shù)據(jù)在子類(lèi)當(dāng)中定義,然后每次都在堆上生成 MessageChecker 嗎?

              我在這里的解決方案是,在 MessageChecker 里面定義一個(gè)多態(tài)的數(shù)據(jù)成員,這個(gè)成員大多數(shù)時(shí)候是空的,當(dāng)需要的時(shí)候從堆上生成它,用它來(lái)裝下數(shù)據(jù)。如代碼所示:

              先定義一個(gè)多態(tài)的數(shù)據(jù)類(lèi) MessageData:

               1: // 消息數(shù)據(jù)
               2: class MessageData
               3: {
               4: public:
               5:     virtual ~MessageData(){}
               6:     virtual MessageData* clone() const = 0;
               7:     virtual void release(){ delete this; }
               8: };

              在 MessageChecker 當(dāng)中加入這個(gè)數(shù)據(jù)成員:

               1: // 加入多態(tài)的數(shù)據(jù)成員
               2: class MessageChecker
               3: {
               4: //......
               5: public:
               6:     MessageData* data;
               7: }
               8:  
               9: // 拷貝構(gòu)造時(shí)同時(shí)深拷貝數(shù)據(jù)
              10: MessageChecker::MessageChecker( const MessageChecker& other )
              11:     :id( other.id )
              12:     ,wparam( other.wparam )
              13:     ,lparam( other.lparam )
              14:     ,m_visok( other.m_visok )
              15:     ,data( other.data?other.data->clone():NULL )
              16: {}
              17:  
              18: // 拷貝時(shí)同時(shí)深拷貝數(shù)據(jù)
              19: MessageChecker& MessageChecker::operator=( const MessageChecker& other )
              20: {
              21:     if ( this != &other )
              22:     {
              23:         id        = other.id;
              24:         wparam  = other.wparam;
              25:         lparam  = other.lparam;
              26:         m_visok = other.m_visok;
              27:         
              28:         if ( data )
              29:         {
              30:             data->release();
              31:             data = NULL;
              32:         }
              33:  
              34:         if ( other.data )
              35:         {
              36:             data = other.data->clone();
              37:         }
              38:     }
              39:     return *this;
              40: }
              41:  
              42: // 析構(gòu)時(shí)刪除
              43: MessageChecker::~MessageChecker()
              44: {
              45:     if ( data )
              46:     {
              47:         data->release();
              48:         data = NULL;
              49:     }
              50: }

              舉例說(shuō)明怎么樣使用 data 成員。例如要在一個(gè)消息檢查者需要比較字符串,則在其中必須要保存供比較的字符串。先從 MessageData 派生一個(gè)保存字符串的數(shù)據(jù)類(lèi) MessageString:

               1: // 裝字符串多態(tài)數(shù)據(jù)類(lèi)
               2: class MessageString:public MessageData
               3: {
               4: public:
               5:     MessageString( const String& string )
               6:         :content( string )
               7:     {}
               8:  
               9:     virtual MessageData* clone() const
              10:     {
              11:         return new MessageString( content );
              12:     }
              13:  
              14: public:
              15:     String    content;
              16: };

              然后在這個(gè)消息檢查者中可以使用這個(gè)類(lèi)了:

               1: // 檢查字符串的消息檢查者
               2: class StringMessageChecker:public MessageChecker
               3: {
               4: public:
               5:     StringMessageChecker( const String& string )
               6:         :MessageChecker( WM_SETTEXT,0,0,&StringMessageChecker::virtualIsOk )
               7:     {
               8:         data = new MessageString( string );
               9:     }
              10:  
              11: public:
              12:     static bool virtualIsOk( const MessageChecker* pthis,const Message& message )
              13:     {
              14:         if ( message.id != pthis->id )
              15:         {
              16:             return false;
              17:         }
              18:  
              19:         MessageString* data = (MessageString*)pthis->data;
              20:         if ( !data )
              21:         {
              22:             return false;
              23:         }
              24:  
              25:         std::string stirngToCheck = (const Char*)( message.lparam );
              26:         return stirngToCheck == data->content;
              27:     }
              28: };

              為了使用方便可以定義一個(gè)數(shù)據(jù)類(lèi)的模板:

               1: // 數(shù)據(jù)類(lèi)的模板
               2: template <typename TContent>
               3: class MessageDataT:public MessageData
               4: {
               5: public:
               6:     MessageDataT( const TContent& content_ )
               7:         :content( content_ )
               8:     {}
               9:  
              10:     virtual MessageData* clone() const
              11:     {
              12:         return new MessageDataT( content );
              13:     }
              14:  
              15: public:
              16:     TContent content;
              17: };

              然后可以將字符串?dāng)?shù)據(jù)類(lèi)的定義簡(jiǎn)化為:

               1: // 利用模板生成數(shù)據(jù)類(lèi)
               2: typedef MessageDataT<String>    MessageString;

            2.5 龍?zhí)籽輪T

              接下來(lái)可以進(jìn)行消息檢查者的測(cè)試了。但因?yàn)橄z查者的工作牽到其它兩個(gè)角色,所以測(cè)試之前必須要先簡(jiǎn)單模擬出這兩個(gè)龍?zhí)捉巧合⑻幚碚吆拖⒈O(jiān)聽(tīng)者。

              消息處理者(MessageHandler )的作用顧名思義不用多作解釋,它在 GUI 框架當(dāng)中的作用十分重要,實(shí)現(xiàn)起來(lái)也最復(fù)雜,但在這本文中它不是主角,所以可以先隨便拉個(gè)路人甲來(lái)跑跑龍?zhí)祝啡思组L(zhǎng)得像這個(gè)樣子:

               1: // 路人甲表演的消息處理者
            
               2: typedef void    (*MessageHandler)( const Message& message );
            

              消息監(jiān)聽(tīng)者(MessageListener )保存有消息檢查者與消息處理者的映射,并提供接口操作這些映射,當(dāng)監(jiān)聽(tīng)到消息的時(shí)候,消息監(jiān)聽(tīng)者調(diào)用映射中的檢查者進(jìn)行檢查,如果通過(guò)檢查則調(diào)用消息處理者來(lái)進(jìn)行處理。消息監(jiān)聽(tīng)者干的都是添加/刪除/查找這樣的體力活,看似實(shí)現(xiàn)起來(lái)用不著大腦,可是當(dāng)涉及到真正的消息處理時(shí)情況會(huì)變得復(fù)雜,會(huì)遇到消息重入之類(lèi)的問(wèn)題。所以真正的實(shí)現(xiàn)留待日后再說(shuō),這里也先給出一個(gè)簡(jiǎn)單的模擬定義含混過(guò)去:

               1: // 消息監(jiān)聽(tīng)者
            
               2: class MessageListener
            
               3: {
            
               4:     typedef vector<MessageHandler>                 _HandlerVector;
            
               5:     typedef pair<MessageChecker,_HandlerVector>    _HandlerPair;
            
               6:     typedef vector<_HandlerPair>                   _HandlerMap;
            
               7:     typedef _HandlerVector::iterator               _HandlerVectorIter;
            
               8:     typedef _HandlerMap::iterator                  _HandlerMapIter;
            
               9:
            
              10: public:
            
              11:     virtual ~MessageListener(){}
            
              12:
            
              13:     // 消息從這里來(lái)了
            
              14:     virtual void    onMessage( const Message& message );
            
              15:
            
              16: public:
            
              17:     // 操作映射的接口
            
              18:     bool    addHandler( const MessageChecker& checker,const MessageHandler& handler );
            
              19:     bool    removeHandler( const MessageChecker& checker,const MessageHandler& handler );
            
              20:     bool    clearHandlers( const MessageChecker& checker );
            
              21:
            
              22: protected:
            
              23:     // 消息檢查者與消息處理者的映射
            
              24:     _HandlerMap        m_map;
            
              25: };
            

              其中調(diào)用消息檢查者的是關(guān)鍵函數(shù)是 MessageListener::onMessage( const Message& mesage ) ,實(shí)現(xiàn)很簡(jiǎn)單,查找出消息處理者列表然后逐個(gè)調(diào)用其中處理者:

               1: // 調(diào)用檢查者檢查,通過(guò)則調(diào)用處理者處理
            
               2: void MessageListener::onMessage( const Message& message )
            
               3: {
            
               4:     for ( _HandlerMapIter it = m_map.begin(); it!=m_map.end(); ++it )
            
               5:     {
            
               6:         if ( (*it).first.isOk(message) )
            
               7:         {
            
               8:             _HandlerVector* handers = &(*it).second;
            
               9:             if ( handers && !handers->empty() )
            
              10:             {
            
              11:                 for ( _HandlerVectorIter it=handers->begin(); it!=handers->end(); ++it )
            
              12:                 {
            
              13:                     (*it)( message );
            
              14:                 }
            
              15:             }
            
              16:         }
            
              17:     }
            
              18: }
            

            2.6 進(jìn)行測(cè)試

              龍?zhí)锥家丫臀?,?dǎo)演喊:"action!",現(xiàn)在可以寫(xiě)點(diǎn)測(cè)試代碼測(cè)試一下了:

               1: void handleCreated( const Message& message )
               2: {
               3:     cout<<"::handleCreated";
               4:     cout<<"\n"<<endl;
               5: }
               6:  
               7: void handleCommand( const Message& message )
               8: {
               9:     cout<<"::handleCommand\t";
              10:     cout<<"id:"<<LOWORD(message.wparam);
              11:     cout<<"\t";
              12:     cout<<"code:"<<HIWORD(message.wparam);
              13:     cout<<"\n"<<endl;
              14: }
              15:  
              16: void handleClicked( const Message& message )
              17: {
              18:     cout<<"::handleClicked\t";
              19:     cout<<"id:"<<LOWORD(message.wparam);
              20:     cout<<"\n"<<endl;
              21: }
              22:  
              23: void handleRangeCommand( const Message& message )
              24: {
              25:     cout<<"::handleRangeCommand\t";
              26:     cout<<"id:"<<LOWORD(message.wparam);
              27:     cout<<"\t";
              28:     cout<<"code:"<<HIWORD(message.wparam);
              29:     cout<<"\n"<<endl;
              30: }
              31:  
              32: void handleString( const Message& message )
              33: {
              34:     cout<<"::handleString\t";
              35:     cout<<"string:"<<(const char*)message.lparam;
              36:     cout<<"\n"<<endl;
              37: }
              38:  
              39:  
              40: #define ID_BUTTON_1    1
              41: #define ID_BUTTON_2    2
              42: #define ID_BUTTON_3    3
              43: #define ID_BUTTON_4    4
              44:  
              45: int main( int argc,char** argv )
              46: {
              47:     MessageListener listener;
              48:     listener.addHandler( MessageChecker(WM_CREATE),&handleCreated );
              49:     listener.addHandler( CommandChecker(ID_BUTTON_1,BN_CLICKED),&handleCommand );
              50:     listener.addHandler( RangeIdCommandChecker(ID_BUTTON_1,ID_BUTTON_3,BN_CLICKED),&handleRangeCommand );
              51:     listener.addHandler( StringMessageChecker( "I love this game" ),&handleString );
              52:  
              53:     Message message( WM_CREATE );
              54:     listener.onMessage( message );
              55:  
              56:     message.id = WM_COMMAND;
              57:     message.wparam = MAKEWPARAM( ID_BUTTON_2,BN_CLICKED );
              58:     listener.onMessage( message );
              59:  
              60:     message.id = WM_COMMAND;
              61:     message.wparam = MAKEWPARAM( ID_BUTTON_1,BN_CLICKED );
              62:     listener.onMessage( message );
              63:  
              64:     message.id = WM_COMMAND;
              65:     message.wparam = MAKEWPARAM( ID_BUTTON_3,BN_CLICKED );
              66:     listener.onMessage( message );
              67:  
              68:     const char* string = "I love this game";
              69:     message.id = WM_SETTEXT;
              70:     message.lparam = (LPARAM)string;
              71:     listener.onMessage( message );
              72:  
              73:     return 0;
              74: }

            3 收?qǐng)龅脑?/p>

              這第一錘終于砸完了,石頭一裂為二,胸口完好無(wú)損。其實(shí)砸的時(shí)候心想,這一錘的分量砸下去不轟動(dòng)神州也要震驚天府吧。但是回頭看看上面所有的文字,覺(jué)得這個(gè)東西怎么這么簡(jiǎn)單,甚至連模板參數(shù)都沒(méi)有用到一個(gè),更沒(méi)有談到效率,優(yōu)化什么的,肯定是不足以誘惑技術(shù)流的 cpper 們的。

              想起自己曾經(jīng)寫(xiě)過(guò)的幾個(gè)消息框架,可以算是把 C++ 的編譯期技術(shù)發(fā)揮得淋漓盡致了,但是出來(lái)的東西卻并不理想,后來(lái)慢慢領(lǐng)悟到一個(gè)道理:高尖的技術(shù)雖然炫酷,并不是處處都合適用。我的版本的消息檢查者就止于這個(gè)程度了,肯定有比這個(gè)更好的實(shí)現(xiàn),希望走過(guò)路過(guò)的高手們不要吝嗇自己的好想法,提出來(lái)與廣大醬油眾分享。

              消息檢查總算寫(xiě)完了。沒(méi)選上好季節(jié),電腦前坐了大半天手腳都冰涼的。上床睡覺(jué)去了,養(yǎng)足精神希望能看到新一輪的神仙打架。文章涉及的所有代碼項(xiàng)目下載:MessageChecker_200911251055.rar

            Feedback

            # re: GUI框架:消息檢查者  回復(fù)  更多評(píng)論   

            2009-11-22 05:14 by OwnWaterloo
            貌似沙發(fā)又得被我占領(lǐng)……
            作為一個(gè)對(duì)oo不屑一顧的c(pp)er,看到這樣的代碼相當(dāng)不適……




            比如MessageChecker和它的子類(lèi)……
            不適啊……

            1. 切割與多態(tài)
            從msvc和gcc的實(shí)現(xiàn)來(lái)看,虛指針是切割不掉的,它在父類(lèi)中。
            虛表就更不存在切割一說(shuō)……
            不能多態(tài)的原因不是因?yàn)榍懈?,而是因?yàn)?—— 沒(méi)有使用引用或指針類(lèi)型進(jìn)行調(diào)用……
            所以啊,對(duì)這種情況 …… 可以惡心點(diǎn)……
            按值保存(會(huì)被切割),同時(shí)對(duì)每個(gè)值也保存一個(gè)對(duì)應(yīng)的指針,指向該值。
            通過(guò)指針而非值進(jìn)行調(diào)用,誘使編譯器使用虛指針。

            為什么感到惡心呢……
            是因?yàn)檫@樣可以多態(tài)調(diào)用,但不能保證子類(lèi)的不變式……
            調(diào)用的是子類(lèi)的函數(shù),使用的是父類(lèi)的數(shù)據(jù)(被切割了)……


            2. class vs struct
            當(dāng)然,對(duì)這個(gè)問(wèn)題蠻適合的。
            因?yàn)闃侵鞯姆桨冈谧宇?lèi)中同樣不能塞新的數(shù)據(jù),否則一樣會(huì)被切割。
            然后,調(diào)用那個(gè)函數(shù)指針時(shí),就會(huì)訪問(wèn)到無(wú)效內(nèi)容。


            為什么感到不適呢……
            上面說(shuō)了,只要是按值存儲(chǔ),子類(lèi)就不可能添加數(shù)據(jù)成員 —— 否則就會(huì)被切割。
            也就是說(shuō),無(wú)論怎樣繼承,數(shù)據(jù)成員就那么幾個(gè)……
            這里需要的其實(shí)不是class, 而是struct ……
            強(qiáng)行使用class, 會(huì)使得需要定制的時(shí)候,就需要定義一個(gè)類(lèi) —— 其實(shí)僅僅需要定制一個(gè)函數(shù)就可以了。

            struct message_checker
            {

            id;
            wp;
            lp;
            int (*is_ok) (const message_checker&,const message& );

            };

            message_checker ButtonClickingChecker(WORD id);
            message_checker xxxChecker( ... );

            所以,怎么看怎么別扭……
            當(dāng)然,這是口味問(wèn)題…… 不值得爭(zhēng)個(gè)你死我活……


            3. 其他
            有些不需要id,wp,lp的checker該怎么辦呢……
            需要比id,wp,lp更多參數(shù)的checker又該怎么辦呢……


            for (_HandlerMapIter it = m_map.begin(); it!=m_map.end(); ++it)
            這個(gè)效率是很低的……
            _HandlerMapIter的命名也是不符合c/c++規(guī)范的…… 都被Gof帶壞了……

            # re: GUI框架:消息檢查者  回復(fù)  更多評(píng)論   

            2009-11-22 12:33 by cexer
            @OwnWaterloo
            【強(qiáng)行使用class, 會(huì)使得需要定制的時(shí)候,就需要定義一個(gè)類(lèi) —— 其實(shí)僅僅需要定制一個(gè)函數(shù)就可以了。】
            這樣的設(shè)計(jì)也是跟框架的整體設(shè)計(jì)有關(guān)系,以后寫(xiě)了消息映射者你再看看。倒是沒(méi)考慮過(guò)使用函數(shù)來(lái)實(shí)現(xiàn)消息檢查者,我更喜歡它有對(duì)象的感覺(jué)一點(diǎn),OOP語(yǔ)言嘛,而且類(lèi)是肯定比函數(shù)有大得多的靈活性的。

            【有些不需要id,wp,lp的checker該怎么辦呢……需要比id,wp,lp更多參數(shù)的checker又該怎么辦呢……】
            之所以不需要再需要新的成員數(shù)據(jù),是因?yàn)橄⒈旧砭椭挥心菐讉€(gè)數(shù)據(jù)。如果需要檢查更復(fù)雜的情況,比如說(shuō)字符串,這樣的模式也是可以勝任的,以前實(shí)現(xiàn)過(guò)。在 MessageChecker 當(dāng)中加一個(gè)多態(tài)的 Data 成員,它的多態(tài)復(fù)雜度要比 MessageChecker 本身在堆上分配的小得多,目前沒(méi)遇到id,wparam,lparam 三個(gè)參數(shù)解決不了問(wèn)題的情況,暫時(shí)不會(huì)增加這個(gè) Data 成員。

            【從msvc和gcc的實(shí)現(xiàn)來(lái)看,虛指針是切割不掉的,它在父類(lèi)中。虛表就更不存在切割一說(shuō)……】
            這樣走旁門(mén)左道是因?yàn)橐郧按_實(shí)遇到過(guò)虛函數(shù)失效的問(wèn)題,應(yīng)該不會(huì)真是我忘了用指針調(diào)用了吧?更可能是編譯器bug,因?yàn)橹羔樥{(diào)用虛函數(shù)從小就記得。VC71對(duì)于C++標(biāo)準(zhǔn)的支持不是很理想,遇到過(guò)很多編譯器報(bào)“cl.exe 內(nèi)部錯(cuò)誤”,特別是涉及模板的時(shí)候。

            【是因?yàn)檫@樣可以多態(tài)調(diào)用,但不能保證子類(lèi)的不變式……調(diào)用的是子類(lèi)的函數(shù),使用的是父類(lèi)的數(shù)據(jù)(被切割了)……】
            什么不變式要變式哦,聽(tīng)起來(lái)好討厭,能抓貓的就是牛老鼠。寫(xiě)出來(lái)的程序編譯通過(guò)鏈接成功,客戶能運(yùn)行它的時(shí)候覺(jué)得心里爽就行了,所有的規(guī)則應(yīng)該是為這個(gè)最終的目的服務(wù)的,而不應(yīng)該為規(guī)則而規(guī)則。

            【“for (_HandlerMapIter it = m_map.begin(); it!=m_map.end(); ++it)這個(gè)效率是很低的……HandlerMapIter的命名也是不符合c/c++規(guī)范的…… 都被Gof帶壞了……】
            我倒是并不覺(jué)得它的效率很低,因?yàn)橹皇鞘啻蔚难h(huán),不過(guò)也希望看看你有更有效率的方法。命令風(fēng)格是被翻炒了千億次的問(wèn)題了,使程序員從編碼到設(shè)計(jì)都能保持最大的個(gè)性,我覺(jué)得正是C++的一大特色。就算有人腰上圍一圈炸彈手執(zhí)打火機(jī)來(lái)逼我改風(fēng)格,我依然視死如歸地覺(jué)得自己的命名方式美妙絕倫無(wú)以倫比的,堅(jiān)持風(fēng)格得有血可流有頭可斷發(fā)型不能亂的勇氣啊。所以你就將就著看了。

            “規(guī)則,風(fēng)格,效率”,OwnWaterloo兄弟啊,你就是典型的傳說(shuō)中的學(xué)院派 cpper 。很高興又來(lái)占我沙發(fā),同時(shí)很是抱歉,這篇博文可能讓你失望了。但我會(huì)再接再勵(lì),希望后續(xù)的文章能引起你的興趣,不負(fù)你兩占沙發(fā)的厚望。

            # re: GUI框架:消息檢查者[未登錄](méi)  回復(fù)  更多評(píng)論   

            2009-11-22 12:57 by Loaden
            下午還有課!雖然是星期天??!
            唉。
            這么好的文章,竟然只能晚上才能拜讀,遺憾啊!
            多謝cexer分享,期待下文...

            # re: GUI框架:消息檢查者  回復(fù)  更多評(píng)論   

            2009-11-22 13:06 by cexer
            @Loaden
            謝謝老鄧!也希望你的框架再進(jìn)化!

            # re: GUI框架:消息檢查者  回復(fù)  更多評(píng)論   

            2009-11-22 13:53 by 空明流轉(zhuǎn)
            對(duì)GUI我早就煩了。。。
            歸根結(jié)底,GUI是個(gè)適合于自動(dòng)生成的玩意兒。
            實(shí)在不行,還是學(xué)Qt吧,MOC解決所有問(wèn)題。。。

            # re: GUI框架:消息檢查者  回復(fù)  更多評(píng)論   

            2009-11-22 14:04 by OwnWaterloo
            @cexer
            剛醒…… 一邊吃飯一邊先把不涉及風(fēng)格的東西解釋一下……

            不說(shuō)不變式了。我也覺(jué)得這詞太學(xué)究,所以在后面補(bǔ)了一句:
            【調(diào)用的是子類(lèi)的函數(shù),使用的是父類(lèi)的數(shù)據(jù)(被切割了)……】

            而且,我說(shuō)錯(cuò)了……
            編譯器會(huì)在復(fù)制(以及切割)的同時(shí)修正虛指針 —— 忘了……
            要memcpy( &b, &d, sizeof(b) ); 才行……


            關(guān)于【for (_HandlerMapIter it = m_map.begin(); it!=m_map.end(); ++it)】
            我指的不是for each。而是遍歷。
            不明白為什么只需要遍歷,卻用了一個(gè)map。
            然后…… 我再看了看這行代碼之上的代碼,這是一個(gè)vector……
            我又看錯(cuò)了…… 代碼看得不仔細(xì)……



            關(guān)于coding-style。其實(shí)我是最不講究這個(gè)的了……
            但有些東西不屬于coding-style,而是屬于底線 —— 使用c/c++語(yǔ)言必須遵守的 —— 不能使用語(yǔ)言保留的標(biāo)識(shí)符。
            語(yǔ)言保留的標(biāo)識(shí)符還分了幾種, 統(tǒng)一起來(lái)就是以下劃線開(kāi)始的程序員全都不能使用。
            這在很多書(shū)里面都有說(shuō),又在很多書(shū)(比如Gof,不知道算不算經(jīng)典)中犯錯(cuò)。

            # re: GUI框架:消息檢查者  回復(fù)  更多評(píng)論   

            2009-11-22 14:16 by 個(gè)性藝術(shù)簽名
            黃金時(shí)代何健飛道護(hù)膚

            # re: GUI框架:消息檢查者  回復(fù)  更多評(píng)論   

            2009-11-22 14:38 by OwnWaterloo
            @cexer
            說(shuō)說(shuō)我對(duì)gui框架的需求吧……
            以用戶的身份…… 用戶的需求總得聽(tīng)聽(tīng)吧……


            從這里說(shuō)起:
            【目前沒(méi)遇到id,wparam,lparam 三個(gè)參數(shù)解決不了問(wèn)題的情況】
            我編寫(xiě)gui的經(jīng)驗(yàn)不多。所以對(duì)"這3個(gè)參數(shù)是否能解決所有問(wèn)題"是一點(diǎn)概念都沒(méi)有。

            不過(guò)我想了一個(gè)例子,比如我要在xxx-yyy時(shí)間段內(nèi),檢查這個(gè)消息,返回真,這個(gè)時(shí)間段外,就返回假。
            這個(gè)checker就做不到了。 肯定要放到其他地方做,比如hanlder中。


            對(duì),這就是我對(duì)"框架"感到反感的地方之一 —— 它"限制"你做事的方法。
            它會(huì)將可能本來(lái)是一件完整的工作,拆散,然后分散到框架的一些地方去。

            為了完成這個(gè)工作,你要按框架所拆分的那樣 —— 需要順著框架的思路,而非我自己的思路 ——只需要id,wp,lp就能check的,放到checker中。其他即使也是check,也得放到handler中。
            如果框架拆分得恰當(dāng),就很好,比如 CommandChecker等。
            如果框架拆分得不恰當(dāng),就很糟……
            所以我對(duì)mfc一點(diǎn)好感都沒(méi)有。


            作為一個(gè)了解且不排斥win32api的用戶(當(dāng)然,gui框架大多著眼的都不是這種用戶囧……),我需要的僅僅是彌補(bǔ)一下WndProc,使得它可以找到this —— 這個(gè)工作幾乎是必須做的,即使是用C編寫(xiě)gui的家伙。
            然后,就可以從hwnd,msg,wp,lp開(kāi)始干活了 —— 不需要再學(xué)任何關(guān)于框架的知識(shí),比如要將check放在哪,hanlder放在哪。
            我希望框架提供一個(gè),在window上add(掛)多個(gè)listener的機(jī)制就差不多了…… 如何分配checker和handler還有cracker的工作,還是將它們?nèi)嘣谝黄穑脩艨梢宰孕袥Q定。

            所以我對(duì)lambda表達(dá)式特別渴望……
            比如上面的代碼,對(duì)應(yīng)的偽代碼可能是這樣:

            window w;
            w.add_listener( void (const msg& m) { if (m.msg==WM_CREATE) cout<<"create\n"<<endl; } );


            然后,在每天編程閑暇的時(shí)候,發(fā)現(xiàn)庫(kù)(而非框架)里面有command_check,我就可以這樣寫(xiě)代碼了:
            w.add_listener( void (const msg&m) { if (is_command(m,id) cout<<command_crack(m).id<<endl; } );

            如果我沒(méi)發(fā)現(xiàn)command_check,我也可以開(kāi)始我的工作,只是代碼更繁瑣一些。

            這就是庫(kù)和框架的一個(gè)很大的區(qū)別。
            你可以使用熟悉的方式工作,然后慢慢熟悉這個(gè)庫(kù),慢慢用它干善自己的工作,并提高效率。
            而框架,不對(duì)它熟悉到一定程度,就沒(méi)法工作……



            作為程序員……
            我也了解用戶的需求是很惡心的…… 就像你有一篇blog里寫(xiě)的那樣……
            所以,推己及人……
            對(duì)我提出的這個(gè)惡心的需求,聽(tīng)過(guò)之后就當(dāng)是廢話好了~_~

            # re: GUI框架:消息檢查者  回復(fù)  更多評(píng)論   

            2009-11-22 14:48 by cexer
            @OwnWaterloo
            【剛醒…… 一邊吃飯一邊先把不涉及風(fēng)格的東西解釋一下……】
            你們那里沒(méi)有出太陽(yáng)?這么大好時(shí)光不要拿來(lái)睡覺(jué)浪費(fèi)了?;貜?fù)完這個(gè),我就出去曬太陽(yáng)了。

            【調(diào)用的是子類(lèi)的函數(shù),使用的是父類(lèi)的數(shù)據(jù)(被切割了)……】
            函數(shù)指針和虛函數(shù)的唯一區(qū)別就是函數(shù)指針少個(gè)讀表的動(dòng)作。所以如果說(shuō)這樣破壞了“不變式”,那么虛函數(shù)也是一樣的。使用子類(lèi)函數(shù)讀父類(lèi)數(shù)據(jù),這種手法是經(jīng)常被用到各種模式中的,這里也不存在切割問(wèn)題,因?yàn)橐呀?jīng)沒(méi)有什么可以切割的。

            【語(yǔ)言保留的標(biāo)識(shí)符還分了幾種, 統(tǒng)一起來(lái)就是以下劃線開(kāi)始的程序員全都不能使用?!?br>這只專家們一個(gè)很學(xué)究氣的建議,跟“謹(jǐn)慎使用內(nèi)聯(lián)函數(shù)”一樣的警示級(jí)別。因?yàn)闃?biāo)準(zhǔn)庫(kù)的作者確實(shí)使用了一些下劃線的變量,boost 也有很多下劃線開(kāi)始的東西,像占位符 _1,_2,..._n。為了避免名字沖突確實(shí)少用為妙,但用了也不應(yīng)該看作錯(cuò)誤。

            # re: GUI框架:消息檢查者  回復(fù)  更多評(píng)論   

            2009-11-22 15:01 by OwnWaterloo
            @cexer
            好像沒(méi)有,我是宅男……

            boost有自己的苦衷。它需要一個(gè)簡(jiǎn)短的東西來(lái)做占位符。
            這樣寫(xiě)就太繁瑣了:
            bind( f, first, second , ... ) ();
            而且first,second估計(jì)很容易和程序員沖突。
            1、2又不是合法的標(biāo)識(shí)符。 所以干脆搞成_1, _2,至少不會(huì)和程序員沖突了。
            bind( f, _1, _2 ... )( ... ); 直觀

            而_HandlerMapIter改為HandlerMapIter或者HandlerMapIter_并不會(huì)影響什么,而且也符合與C/C++定下的契約。


            boost將_1,_2改為1_,2_是不合法的,不能以數(shù)字開(kāi)頭。
            改為其他,不太簡(jiǎn)潔……



            也不能算完全的學(xué)究。它們不會(huì)永遠(yuǎn)被保留。
            C99已經(jīng)開(kāi)始動(dòng)用這些保留的標(biāo)識(shí)符了。
            _LongLong, _Complex,_Bool等。
            C++也會(huì)跟進(jìn)。

            # re: GUI框架:消息檢查者  回復(fù)  更多評(píng)論   

            2009-11-22 15:06 by OwnWaterloo
            將_HandlerMapIter改為HandlerMapIter或者HandlerMapIter_真的沒(méi)有壞處。
            本來(lái)就是一個(gè)private,庫(kù)用戶不可見(jiàn)的名字。
            屬于Handler,也不會(huì)和其他重名。
            還避免了(可能微乎其微)與_HandlerMapIter宏沖突。

            # re: GUI框架:消息檢查者  回復(fù)  更多評(píng)論   

            2009-11-22 20:07 by cexer
            @OwnWaterloo
            【boost有自己的苦衷。它需要一個(gè)簡(jiǎn)短的東西來(lái)做占位符。】
            你誤會(huì)我的意思了。我是同意你的,我的意思是確實(shí)應(yīng)該避免使用,因?yàn)橄?boost,stl 之類(lèi)的庫(kù)很多地使用了這樣的下劃線。

            【將_HandlerMapIter改為HandlerMapIter或者HandlerMapIter_真的沒(méi)有壞處。
            本來(lái)就是一個(gè)private,庫(kù)用戶不可見(jiàn)的名字。屬于Handler,也不會(huì)和其他重名。還避免了(可能微乎其微)與_HandlerMapIter宏沖突?!?br>嗯,明白你的意思。其實(shí)這是我的習(xí)慣,外部不可見(jiàn)的都加個(gè)下劃線在前頭,沒(méi)考慮到與標(biāo)準(zhǔn)庫(kù)發(fā)生沖突的情況,你想的更細(xì)致一點(diǎn)。

            # re: GUI框架:消息檢查者  回復(fù)  更多評(píng)論   

            2009-11-22 21:05 by cexer
            【不過(guò)我想了一個(gè)例子,比如我要在xxx-yyy時(shí)間段內(nèi),檢查這個(gè)消息,返回真,這個(gè)時(shí)間段外,就返回假。
            這個(gè)checker就做不到了。 肯定要放到其他地方做,比如hanlder中?!?br>是可以的啊,可以像下面這樣實(shí)現(xiàn)。
                class TimeMessageChecker:public MessageChecker
                {
                public:
                    TimeMessageChecker( timeStart,timeTo )
                        :MessageChecker( 0,timeStart,timeTo,&TimeMessageChecker:virtualIsOk )
                    {}
                protected:
                    static bool virtualIsOk( const MessageChecker* pthis,const Message& )
                    {
                        timeNow   = GetCurrentTime();
                        timeStart = pthis->wparam;
                        timeTo    = pthis->lparam;
                        return timeNow>=timeStart && timeNow<=timeTo;
                    }
                }

            可能你的意思是遇到消息參數(shù)裝不下檢查時(shí)需要的信息的情況應(yīng)該怎么辦,這里舉例說(shuō)明一下怎么實(shí)現(xiàn)。比如說(shuō)要檢查是否某個(gè)字符串,這樣的實(shí)現(xiàn)可以裝下任何需要的信息。
            先定義一個(gè)多態(tài)的數(shù)據(jù)類(lèi)型,為了效率這里可以使用引用計(jì)數(shù)之類(lèi)的東西
                class MessageData
                {
                public:
                    virtual ~MessageData()
                    {}
                    virtual void release()
                    {
                        delete this;
                    }
                    virtual MessageData* clone() const = 0;
                };

            MessageChecker 當(dāng)中有這個(gè)成員
                class MessageChecker
                {
                //......
                public:
                    MessageData* data;
                }

            修改拷貝構(gòu)造函數(shù)和析構(gòu)函數(shù)
                MessageChecker::MessageChecker( const MessageChecker& other )
                {
                    if ( data )
                    {
                        data->release();
                        data = NULL;
                    }
                    if ( other.data )
                    {
                        data = other.data->clone();
                    }
                }

                MessageChecker::~MessageChecker()
                {
                    if ( data )
                    {
                        data->release();
                        data = NULL;
                    }
                }

            定義一個(gè)裝字符串的多態(tài)數(shù)據(jù)類(lèi)
                class StringMessageData:public MessageData
                {
                public:
                    StringMessageData( const String& str_ )
                        :string( str_ )
                    {}
                    virtual MessageData* clone() const
                    {
                        return new StringMessageData(string);
                    }
                public:
                    String  string;
                };

            利用數(shù)據(jù)成員 data 所裝的信息可以檢查字符串的消息檢查者
                class StringMessageChecker:public MessageChecker
                {
                public:
                    StringMessageChecker( const String& string )
                        :MessageChecker( 0,0,0,&StringMessageChecker::virutalIsOk )
                    {
                        data = new StringMessageData( string );
                    }
                public:
                    static bool virtualIsOk( const MessageChecker* pthis,const Message& message )
                    {
                        StringMessageData* data = (StringMessageData*)pthis->data;
                        if ( !data )
                        {
                            return false;
                        }
                        std::string stirngToCheck = (const Char*)( message.wparam );
                        return stirngToCheck == data->string;
                    }
                };

            【對(duì),這就是我對(duì)"框架"感到反感的地方之一 —— 它"限制"你做事的方法。不對(duì)它熟悉到一定程度,就沒(méi)法工作……
            作為程序員……我也了解用戶的需求是很惡心的…… 就像你有一篇blog里寫(xiě)的那樣……所以,推己及人……對(duì)我提出的這個(gè)惡心的需求,聽(tīng)過(guò)之后就當(dāng)是廢話好了~_~】
            說(shuō)實(shí)話我一直對(duì)“框架”和“類(lèi)庫(kù)”的概念分不大清楚,我想“框架”從名字上來(lái)說(shuō)多了一個(gè)“可以擴(kuò)展,可以填充”的意思,沒(méi)有你說(shuō)的“限制你做事的方法”這種感覺(jué)。還有你提到了 GUI 框架用戶的需求。要說(shuō)明一下的是,實(shí)現(xiàn)的這個(gè)消息檢查者只是在框架內(nèi)工作的,方便 GUI 框架的開(kāi)發(fā)者和擴(kuò)充者使用,GUI 框架的使用者不會(huì)接觸到這個(gè)東西。最終的用戶只需要使用是像這個(gè)樣子:
                window.onCreated += messageHandler( &::_handleCreated );
                window.onCreated += messageHandler ( this,&Window::_handleCreated );

            # re: GUI框架:消息檢查者  回復(fù)  更多評(píng)論   

            2009-11-22 21:21 by cexer
            cppblg 這爛程序,吃空格太嚴(yán)重了。

            # re: GUI框架:消息檢查者  回復(fù)  更多評(píng)論   

            2009-11-23 02:21 by OwnWaterloo
            @cexer
            確實(shí)…… cppblog的評(píng)論做得不咋嘀……
            所以代碼我也沒(méi)仔細(xì)看…… 意思差不多明白了。
            WndProc可以用這些參數(shù)表達(dá)任意多的參數(shù),所以我們也可以……
            對(duì)不需要多余參數(shù)的,可以直接使用這幾個(gè)。
            對(duì)需要的,再引入多態(tài)與自由存儲(chǔ)的開(kāi)銷(xiāo),并將其包裹在一個(gè)值語(yǔ)意的類(lèi)型中。
            是這樣嗎?

            這思路不錯(cuò)。 預(yù)留幾個(gè)常用的參數(shù),免得需要參數(shù)就要?jiǎng)佑脛?dòng)態(tài)內(nèi)存……



            說(shuō)實(shí)話我一直對(duì)“框架”和“類(lèi)庫(kù)”的概念分不大清楚,我想“框架”從名字上來(lái)說(shuō)多了一個(gè)“可以擴(kuò)展,可以填充”的意思,沒(méi)有你說(shuō)的“限制你做事的方法”這種感覺(jué)。

            哎…… 因果弄反了……
            為什么需要【擴(kuò)展】框架? —— 就是因?yàn)榭蚣芟拗屏俗鍪碌姆绞?,不擴(kuò)展就沒(méi)法做自己需要但框架又不提供的功能了。

            說(shuō)得好聽(tīng)點(diǎn),叫擴(kuò)展了框架;說(shuō)得不好聽(tīng),叫順了框架的意;再難聽(tīng)點(diǎn),就是被框架qj了……


            以你這篇文章里提到框架來(lái)說(shuō)吧:
            1個(gè)listener 有 1個(gè)m_map
            1個(gè)m_map 有若干個(gè)HanlderPair
            1個(gè)HandlerPair 包含一個(gè) Checker和HandlerVector
            1個(gè)HandlerVector 包含若干個(gè)Handler

            這就是一種限制。MessageChecker也算。

            設(shè):從源頭WndProc到實(shí)現(xiàn)某個(gè)功能所需要做的全部事情為S。
            【必須按這個(gè)框架制定的規(guī)則,將S拆分為S1、S2等等,然后安排到框架中的預(yù)定位置?!?br>比如將事情分為監(jiān)聽(tīng)、檢查、映射、處理等工作。

            這就是我說(shuō)的限制的意思?!究蚣芤?guī)定了編程的模型】。

            假設(shè),有一項(xiàng)需求框架沒(méi)有提供,比如Checker需要更多的參數(shù)。
            如果還想繼續(xù)使用這個(gè)框架,就必須設(shè)計(jì)一個(gè)MessageData。
            假設(shè)MessageData用戶而非框架提供的,是為【擴(kuò)展】了框架。
            假設(shè)MessageData是框架自身提供的,但如果它又有另一個(gè)需求沒(méi)有滿足呢? 它必須提供一些【可擴(kuò)展點(diǎn)】讓用戶【大部分按框架的思路編程】,并在需要的時(shí)候,擴(kuò)展一下框架。否則用戶就會(huì)放棄這個(gè)框架了。

            如果框架的規(guī)定在大部分時(shí)候合理,框架就是合理的。
            這個(gè)尺度很難把握……

            總之,用框架編程,框架是主體?;蛘哒f(shuō)底層的實(shí)現(xiàn)不用用戶操心,只需要注重業(yè)務(wù)邏輯。
            用類(lèi)庫(kù)編程,程序員自己是主體。




            倒是沒(méi)考慮過(guò)使用函數(shù)來(lái)實(shí)現(xiàn)消息檢查者,我更喜歡它有對(duì)象的感覺(jué)一點(diǎn),OOP語(yǔ)言嘛,而且類(lèi)是肯定比函數(shù)有大得多的靈活性的。

            這個(gè),其實(shí)也是反的……
            OO是思想,class只是它的一種實(shí)現(xiàn)方式。也許使用會(huì)比較方便。
            但靈活性是不如函數(shù)+數(shù)據(jù)的。

            1.
            比如C++、java、C#都沒(méi)有提供多重分派(multi-method)。
            所以,很明顯的,o.method(...); 只是method( o , ... );在1個(gè)對(duì)象下的簡(jiǎn)便形式。
            如果需要method( o1, o2, ... ) —— 根據(jù)o1和o2的類(lèi)型來(lái)決定做什么事。這3門(mén)語(yǔ)言是不提供支持的,也必須用函數(shù)來(lái)表達(dá)。
            我知道有個(gè)xxx模式…… 但3重分派它又解決不了了…… 治標(biāo)不治本。

            當(dāng)然,某種支持多分派的語(yǔ)言可以繼續(xù)提供 o1&o2.method( ... ); 的語(yǔ)法……

            2. OO將算法埋葬到自己的類(lèi)里。
            如果這個(gè)算法很通用,更好的是將它實(shí)現(xiàn)為一個(gè)函數(shù),而不是委身到一個(gè)class中。
            呃,這種情況在gui中可能不多見(jiàn)。

            # re: GUI框架:消息檢查者  回復(fù)  更多評(píng)論   

            2009-11-23 02:45 by OwnWaterloo
            想起一個(gè)說(shuō)明限制的更通俗的例子:std::for_each。
            它就算一種微型框架。
            它完成了遍歷的步驟,同時(shí)限制了遍歷的方法—— 用一元函數(shù)。

            使用for_each時(shí)
            1. 直接存在現(xiàn)有的一元函數(shù):
            vector<void*> m; // memory blocks
            for_each(m.begin(), m.end(), free );

            2. 可以通過(guò)for_each配套提供的一些設(shè)施完成工作:
            vector<shape*> s;
            for_each(s.begin(), s.end(), bind2nd(mem_fun(&s::draw),canvas) );
            for_each(s.begin(), s.end(), mem_fun(&s::reset) );

            3. 擴(kuò)展for_each —— 添加更多functor
            vector<elem> e;
            for_each(e.begin(), e.end(), boost::bind( &e::f, ... ) );

            但怎么做,都只是個(gè)binder而已。還有一些人做出一些帶有簡(jiǎn)單邏輯的functor,然后繼續(xù)使用std::for_each,語(yǔ)法丑陋得,我都不知道怎么寫(xiě)…… 所以這里就不列了…… toplanguage上可以找到不少……

            4. 當(dāng)這些都失效時(shí)……
            可以說(shuō),for_each這個(gè)框架在大多數(shù)時(shí)候都是雞肋。
            讓人不得不繞過(guò)它的限制(傳遞一元函數(shù)),直接寫(xiě)for循環(huán)。


            而boost.lambda和C++0x的lambda使得for_each變得實(shí)用了不少。所以框架是否實(shí)用,很難把握。
            說(shuō)for_each是框架嘛…… 主要是因?yàn)樗南拗啤?br>但是它規(guī)模有點(diǎn)小……也可以很輕易的被丟掉。反正是函數(shù)模板,不使用就不會(huì)被實(shí)例化。這又有類(lèi)庫(kù)的性質(zhì)。
            算是一個(gè)不太恰當(dāng)?shù)睦影伞?br>


            框架定義了輪廓線,由程序員去填充顏色。
            類(lèi)庫(kù)提供調(diào)色板等工具,由程序員去繪制。


            另外,傳遞給for_each的一元函數(shù)的調(diào)用語(yǔ)法是:
            f( *first ); 而不是 ((*first).*f)();
            也是一個(gè)自由函數(shù)比成員函數(shù)更普適與靈活的例子。

            將成員函數(shù)以自由函數(shù)的語(yǔ)法進(jìn)行調(diào)用:
            bind(&c::f)( o, ... );

            將自由函數(shù)以成員函數(shù)語(yǔ)法調(diào)用…… 好像要定義一個(gè)類(lèi)……

            # re: GUI框架:消息檢查者  回復(fù)  更多評(píng)論   

            2009-11-23 10:17 by cexer
            @OwnWaterloo
            【框架定義了輪廓線,由程序員去填充顏色。類(lèi)庫(kù)提供調(diào)色板等工具,由程序員去繪制?!?br>這句話比喻得很好。你把框架理解成一種束縛是覺(jué)得需要按照它規(guī)定的方式去使用,但類(lèi)庫(kù)甚至 API 何嘗不是也有自己的規(guī)則。我理解的框架和類(lèi)庫(kù)其基本目的都是一樣的:對(duì)繁瑣的細(xì)節(jié)進(jìn)行封裝,提供更為便利的接口。這當(dāng)中肯定會(huì)損失靈活性,畢竟魚(yú)和熊掌很難兼得。但我覺(jué)得框架其實(shí)是類(lèi)庫(kù)的進(jìn)化后的身份,從字面意義上來(lái)講,“框架”看起來(lái)更有彈性,擴(kuò)展的意思。但實(shí)際上大家對(duì)這兩個(gè)詞的概念并沒(méi)有很明白的分辨,比如說(shuō)到 MFC,有人說(shuō)框架有人說(shuō)類(lèi)庫(kù)大家的感覺(jué)都是一樣的。

            【這個(gè),其實(shí)也是反的……OO是思想,class只是它的一種實(shí)現(xiàn)方式。也許使用會(huì)比較方便。但靈活性是不如函數(shù)+數(shù)據(jù)的?!?br>我的想法相反。你說(shuō)類(lèi)的靈活性不如函數(shù)加數(shù)據(jù),但類(lèi)難道不正是建立在函數(shù)和數(shù)據(jù)之上的一個(gè)超強(qiáng)結(jié)合體?之所以用C之類(lèi)的 OP 語(yǔ)言實(shí)現(xiàn)模式不如C++這樣的 OO 語(yǔ)言容易,一大原因正是它缺少類(lèi)的支持。

            【message_checker ButtonClickingChecker(WORD id);
            message_checker xxxChecker( ... );】
            這是你舉例說(shuō)明的用函數(shù)來(lái)實(shí)現(xiàn)檢查者。你可以嘗試真的用函數(shù)來(lái)實(shí)現(xiàn)消息檢查者,這個(gè) ButtonClickingChecker(WORD id) , xxxChecker( ... ) 函數(shù)內(nèi)部你各自要怎么實(shí)現(xiàn)?它既要包含不同的數(shù)據(jù),又要包含不同的操作,它們返回完全相同的 message_checker 對(duì)象,這個(gè) message_checker 又要怎么實(shí)現(xiàn)才能以一已之身完成眾多不同的功能?由于不同參數(shù)列表的函數(shù)實(shí)際上是完全不同的東西,你甚至不能以統(tǒng)一的方式保存它們管理它們。

            # re: GUI框架:消息檢查者  回復(fù)  更多評(píng)論   

            2009-11-23 16:05 by 陳梓瀚(vczh)
            繼續(xù)攪局:你憑什么認(rèn)為MessageChecker父類(lèi)的成員變量一定夠用呢?如果夠用的話,就證明你的邏輯已經(jīng)是固定的了,為什么要子類(lèi)?就一個(gè)MessageChecker好了,不用繼承。你矛盾了。

            # re: GUI框架:消息檢查者  回復(fù)  更多評(píng)論   

            2009-11-23 16:10 by 陳梓瀚(vczh)
            @cexer
            你說(shuō)類(lèi)的靈活性不如函數(shù)加數(shù)據(jù),但類(lèi)難道不正是建立在函數(shù)和數(shù)據(jù)之上的一個(gè)超強(qiáng)結(jié)合體?

            類(lèi)的靈活性在于,你使用shared_ptr<Base>保存了一個(gè)Derived*,然后調(diào)用虛函數(shù)。代價(jià)非常低,我經(jīng)常把智能指針?lè)胚M(jìn)容器,也可以不管誰(shuí)去釋放,總之會(huì)被釋放。而且我這種寫(xiě)compiler的人對(duì)循環(huán)引用十分敏感所以我基本不會(huì)犯這種錯(cuò)誤……

            # re: GUI框架:消息檢查者  回復(fù)  更多評(píng)論   

            2009-11-23 16:12 by 陳梓瀚(vczh)
            @cexer
            【這是你舉例說(shuō)明的用函數(shù)來(lái)實(shí)現(xiàn)檢查者。你可以嘗試真的用函數(shù)來(lái)實(shí)現(xiàn)消息檢查者,這個(gè) ButtonClickingChecker(WORD id) , xxxChecker( ... ) 函數(shù)內(nèi)部你各自要怎么實(shí)現(xiàn)?它既要包含不同的數(shù)據(jù),又要包含不同的操作,它們返回完全相同的 message_checker 對(duì)象,這個(gè) message_checker 又要怎么實(shí)現(xiàn)才能以一已之身完成眾多不同的功能?由于不同參數(shù)列表的函數(shù)實(shí)際上是完全不同的東西,你甚至不能以統(tǒng)一的方式保存它們管理它們?!?

            TR1有functor給你用,解決了這些問(wèn)題。無(wú)論是函數(shù)指針,成員函數(shù)指針,有operator()的類(lèi),統(tǒng)統(tǒng)都可以放進(jìn)去。所以我覺(jué)得你的listener應(yīng)該是
            vector<function<bool(Message&)>>,然后處理了返回true,沒(méi)處理返回false。function自己可以有自己的成員,你可以把有operator()的對(duì)象放進(jìn)去,把成員函數(shù)放進(jìn)去什么的,統(tǒng)統(tǒng)都可以。要堅(jiān)定不移地使用這些東西。

            # re: GUI框架:消息檢查者  回復(fù)  更多評(píng)論   

            2009-11-23 16:13 by 陳梓瀚(vczh)
            @cexer
            而且,千萬(wàn)不要去考慮標(biāo)準(zhǔn)庫(kù)里面什么類(lèi)的性能如何的問(wèn)題,你永遠(yuǎn)假定他們是用魔法完成的,不需要任何CPU周期。不然你一行代碼都寫(xiě)不出來(lái)。

            # re: GUI框架:消息檢查者  回復(fù)  更多評(píng)論   

            2009-11-23 16:14 by 陳梓瀚(vczh)
            @cexer
            至于什么是框架什么是類(lèi)庫(kù),有一個(gè)很容易的判斷標(biāo)準(zhǔn):template method就是框架。

            # re: GUI框架:消息檢查者  回復(fù)  更多評(píng)論   

            2009-11-23 16:15 by 陳梓瀚(vczh)
            @cexer
            所以框架和類(lèi)庫(kù)可以互相包含,這也是很常見(jiàn)的。

            # re: GUI框架:消息檢查者  回復(fù)  更多評(píng)論   

            2009-11-23 16:57 by cexer
            @陳梓瀚(vczh)
            【繼續(xù)攪局:你憑什么認(rèn)為MessageChecker父類(lèi)的成員變量一定夠用呢?如果夠用的話,就證明你的邏輯已經(jīng)是固定的了,為什么要子類(lèi)?就一個(gè)MessageChecker好了,不用繼承。你矛盾了?!?br>歡迎攪局!你提出一問(wèn)題確實(shí)有“以子之矛攻子之盾”的殺傷力,不過(guò)幸好我不是既賣(mài)矛又賣(mài)盾的。關(guān)于數(shù)據(jù)不夠用的情況 OwnWaterloo 的也提出過(guò),你可以倒著往上看給 OwnWaterloo 的回復(fù),在 MessageChecker 當(dāng)中加一個(gè)多態(tài)的 Data 成員可以放下所有東西,我還是把這東西更新到博文里去吧免得又有人問(wèn)?!斑壿嫛钡年P(guān)鍵顯然不是數(shù)據(jù),而是那個(gè)檢查的動(dòng)作。就像銀行存了五千萬(wàn),不拿出去揮霍也住不上豪宅一樣,成員變量夠用成員函數(shù)不夠用也是白搭,必須得要改寫(xiě)。要想一個(gè)類(lèi),不繼承,不改寫(xiě),而要滿足不斷出現(xiàn)的需求,這肯定是不能完成的任務(wù)?!皵?shù)據(jù)夠用”和“函數(shù)改寫(xiě)”并矛盾。

            【類(lèi)的靈活性在于,你使用shared_ptr<Base>保存了一個(gè)Derived*,然后調(diào)用虛函數(shù)。代價(jià)非常低,我經(jīng)常把智能指針?lè)胚M(jìn)容器,也可以不管誰(shuí)去釋放,總之會(huì)被釋放。而且我這種寫(xiě)compiler的人對(duì)循環(huán)引用十分敏感所以我基本不會(huì)犯這種錯(cuò)誤……】
            你是建議我把 MessageChecker 在堆上生成用智能指針管理起來(lái)吧。我也覺(jué)得這樣確實(shí)可以使用真正強(qiáng)大的虛函數(shù),但這樣要付出每次都在堆上構(gòu)造的效率成本和管理上的復(fù)雜性,而實(shí)際上大多數(shù)情況下這樣的付出是不必的:在 Windows 的消息機(jī)制下,消息檢查大多數(shù)時(shí)候需要的數(shù)據(jù)是很少的,用 WPARAM 和 LPARAM 就可以裝下,但確實(shí)有需要在堆上保存數(shù)據(jù)的情況,所以我在 MessgaeChecker 增加了一個(gè)多態(tài)的 Data 成員來(lái)保存,它只有極少時(shí)候的才會(huì)從堆上生成,這樣盡量避免了堆上生成的復(fù)雜性和效率損失,而完成的功能又絲毫不減。

            # re: GUI框架:消息檢查者  回復(fù)  更多評(píng)論   

            2009-11-23 17:03 by 陳梓瀚(vczh)
            @cexer
            使用tr1::function<bool(Message&)>,毫無(wú)管理復(fù)雜性,幾乎沒(méi)有效率成本。

            # re: GUI框架:消息檢查者  回復(fù)  更多評(píng)論   

            2009-11-23 17:04 by 陳梓瀚(vczh)
            @陳梓瀚(vczh)
            但是實(shí)際上windows的消息很亂,事件跟消息并不是一一對(duì)上號(hào)的。因此MessageChecker之類(lèi)的東西最終用戶是看不到的,你要根據(jù)每一個(gè)控件的具體情況,重新整理出一系列事件,才能讓用的時(shí)候真正爽快起來(lái)。這里沒(méi)有技術(shù)問(wèn)題。

            # re: GUI框架:消息檢查者  回復(fù)  更多評(píng)論   

            2009-11-23 17:31 by cexer
            @陳梓瀚(vczh)
            【要堅(jiān)定不移地使用這些東西。 使用tr1::function<bool(Message&)>,毫無(wú)管理復(fù)雜性,幾乎沒(méi)有效率成本?!?br>把 MessageChecker::isOk(const Message&) 的實(shí)現(xiàn)放到 bool MessageChecker::operator()(const Message&) 中去,函數(shù)體MessageChecker 就變成了和 function 類(lèi)似的東西,但這樣又有什么本質(zhì)區(qū)別呢。tr1::function 的功能雖然強(qiáng)大,用它去實(shí)現(xiàn)消息檢查者,該寫(xiě)的邏輯還得寫(xiě),省不了功夫,擴(kuò)展性也是個(gè)問(wèn)題。

            【至于什么是框架什么是類(lèi)庫(kù),有一個(gè)很容易的判斷標(biāo)準(zhǔn):template method就是框架。】
            謝謝,很多書(shū)對(duì)這個(gè)也言之不詳,只是叫著爽就行了,所以我自己也就不大明白。我從此以后照你這個(gè)標(biāo)準(zhǔn)去評(píng)判好了。

            【而且,千萬(wàn)不要去考慮標(biāo)準(zhǔn)庫(kù)里面什么類(lèi)的性能如何的問(wèn)題,你永遠(yuǎn)假定他們是用魔法完成的,不需要任何CPU周期。不然你一行代碼都寫(xiě)不出來(lái)?!?br>我是從不考慮那些的:標(biāo)準(zhǔn)庫(kù)的效率再差,也不會(huì)到我放棄使用它們的地步的。另外以我的水平寫(xiě)出來(lái)的東西絕對(duì)比它的慢,哪有資格嫌棄人家。

            【但是實(shí)際上windows的消息很亂,事件跟消息并不是一一對(duì)上號(hào)的。因此MessageChecker之類(lèi)的東西最終用戶是看不到的,你要根據(jù)每一個(gè)控件的具體情況,重新整理出一系列事件,才能讓用的時(shí)候真正爽快起來(lái)。這里沒(méi)有技術(shù)問(wèn)題。】
            你說(shuō)得對(duì),框架用戶是看不到 MessageChecker 的,他們看到的是 onCreated,onClosed,onClicked 之類(lèi)的東西。

            # re: GUI框架:消息檢查者  回復(fù)  更多評(píng)論   

            2009-11-23 18:25 by 陳梓瀚(vczh)
            @cexer
            function的好處就是你可以使用子類(lèi)的同時(shí)不用管delete啊

            # re: GUI框架:消息檢查者  回復(fù)  更多評(píng)論   

            2009-11-23 20:09 by cexer
            @陳梓瀚(vczh)
            【function的好處就是你可以使用子類(lèi)的同時(shí)不用管delete啊】
            想了下用 tr1::function 來(lái)實(shí)現(xiàn)確實(shí)要比手寫(xiě)類(lèi)簡(jiǎn)單得多,函數(shù)和參數(shù)綁定功能在這里很有用處,有多少參數(shù)都不用搞個(gè)多態(tài)的 Data 了,也不用自己去 delete 。確實(shí)很強(qiáng)大。

            # re: GUI框架:消息檢查者  回復(fù)  更多評(píng)論   

            2009-11-24 00:52 by OwnWaterloo
            @cexer
            【我的想法相反。你說(shuō)類(lèi)的靈活性不如函數(shù)加數(shù)據(jù),但類(lèi)難道不正是建立在函數(shù)和數(shù)據(jù)之上的一個(gè)超強(qiáng)結(jié)合體?之所以用C之類(lèi)的 OP 語(yǔ)言實(shí)現(xiàn)模式不如C++這樣的 OO 語(yǔ)言容易,一大原因正是它缺少類(lèi)的支持?!?br>
            是的,類(lèi)是函數(shù)和數(shù)據(jù)的結(jié)合,還加上數(shù)據(jù)隱藏。
            超強(qiáng)到談不上……
            我也說(shuō)了的,對(duì)單個(gè)object,這3門(mén)語(yǔ)言的OO實(shí)現(xiàn)確實(shí)是方便。

            靈活與范化的東西,通常比較難用和不方便。



            這是你舉例說(shuō)明的用函數(shù)來(lái)實(shí)現(xiàn)檢查者。你可以嘗試真的用函數(shù)來(lái)實(shí)現(xiàn)消息檢查者,這個(gè) ButtonClickingChecker(WORD id) , xxxChecker( ... ) 函數(shù)內(nèi)部你各自要怎么實(shí)現(xiàn)?它既要包含不同的數(shù)據(jù),又要包含不同的操作,它們返回完全相同的 message_checker 對(duì)象,這個(gè) message_checker 又要怎么實(shí)現(xiàn)才能以一已之身完成眾多不同的功能?由于不同參數(shù)列表的函數(shù)實(shí)際上是完全不同的東西,你甚至不能以統(tǒng)一的方式保存它們管理它們。

            你那個(gè)MessageChecker怎么完成以一己之身完成,眾多不同功能的?
            同樣可以應(yīng)用到message_checker 上,兩者是相通的,都是靠結(jié)構(gòu)體中嵌的一個(gè)函數(shù)指針。


            -------- -------- -------- --------

            關(guān)于那個(gè)id,wp,lp,我覺(jué)得還不錯(cuò)。

            Windows確實(shí)需要將系統(tǒng)與用戶之間使用【一條統(tǒng)一的渠道【來(lái)通信。
            這條渠道演化到【極端】就是這種形式:
            void* (*)(void* all_parameter);

            但如果所有消息都需要使用動(dòng)態(tài)內(nèi)存,就有點(diǎn)浪費(fèi)。
            添加一些其他比較常用的值參數(shù) vs 和一個(gè)值參數(shù)都不用,就是一種折衷。
            完全不用,肯定每次都需要?jiǎng)討B(tài)內(nèi)存分配。
            值參數(shù)多了,又可能在一些情況用不上。

            所以,Windows最后選擇了
            LRESULT (CALLBACK*)(HWND, UINT, WPARAM, LPARAM);
            參數(shù)少,直接傳遞,參數(shù)多,將某個(gè)參數(shù)理解為指針……
            (此處純猜測(cè)……)


            所以,迎合WndProc的設(shè)計(jì)還不錯(cuò)。


            -------- -------- -------- --------

            其實(shí)我是被這段語(yǔ)法吸引的:
            window.onCreated += messageHandler( &::_handleCreated );
            window.onCreated += messageHandler ( this,&Window::_handleCreated );

            很像boost.signal。


            而且真正吸引人的是樓主說(shuō)它是廉價(jià)工人~_~


            boost.function如果不用支持bind,可能可以不動(dòng)用動(dòng)態(tài)存儲(chǔ)。
            要支持bind…… 而且不使用動(dòng)態(tài)存儲(chǔ)…… 好像不行……
            boost.signal肯定是要?jiǎng)佑脛?dòng)態(tài)存儲(chǔ)的。


            等著樓主這部分的實(shí)現(xiàn)了~_~

            # re: GUI框架:消息檢查者  回復(fù)  更多評(píng)論   

            2009-11-24 10:38 by cexer
            【你那個(gè)MessageChecker怎么完成以一己之身完成,眾多不同功能的?
            同樣可以應(yīng)用到message_checker 上,兩者是相通的,都是靠結(jié)構(gòu)體中嵌的一個(gè)函數(shù)指針。】
            我的意思是函數(shù)本身不容易實(shí)現(xiàn),只用函數(shù)主要有數(shù)據(jù)保存的問(wèn)題。加上 function 來(lái)實(shí)現(xiàn)就容易了,直接綁定一些檢查函數(shù)和檢查的數(shù)據(jù):
                typedef boost::function<bool (const Message&)> MessageChecker

                bool checkCommand( const Message& message,WORD id,WORD code );
                MessageChecker checkYesClicked = boost::bind( &checkCommand,_1,IDYES,BN_CLICKED );

                bool checkMessage( const Message& message,UINT messageId, );
                MessageChecker checkCreated = boost::bind( &checkMessage,_1,WM_CREATE );

            【其實(shí)我是被這段語(yǔ)法吸引的:
                window.onCreated += messageHandler( &::_handleCreated );
                window.onCreated += messageHandler ( this,&Window::_handleCreated );
            很像boost.signal。
            而且真正吸引人的是樓主說(shuō)它是廉價(jià)工人~_~】
            += 之前的和之后的是兩個(gè)不同的東西,onCreated 是幫助消息映射的一個(gè)東西,其實(shí)就是一個(gè)轉(zhuǎn)發(fā)調(diào)用,所以成本很低。messageHandler() 是消息處理者和消息分解者共同組成的東西。在這里 ::_handleCreated 和 Window::_handleCreated 參數(shù)列表可以是不同的,這里和 boost.signal 不大一樣,因?yàn)橐粋€(gè) signal 只能對(duì)應(yīng)一種 signautre 的 functor 的。

            【boost.function如果不用支持bind,可能可以不動(dòng)用動(dòng)態(tài)存儲(chǔ)。
            要支持bind…… 而且不使用動(dòng)態(tài)存儲(chǔ)…… 好像不行……
            boost.signal肯定是要?jiǎng)佑脛?dòng)態(tài)存儲(chǔ)的?!?br>嗯。主要是管理起來(lái)很容易了,任何的參數(shù)直接綁定進(jìn)去就行了,不用自己弄個(gè)堆對(duì)象來(lái)保存,然后還要記得刪除。

            # re: GUI框架:消息檢查者  回復(fù)  更多評(píng)論   

            2009-11-24 16:47 by 陳梓瀚(vczh)
            @cexer
            所以說(shuō)嘛,用標(biāo)準(zhǔn)庫(kù)的時(shí)候要假設(shè)他們是0CPU消耗,這樣你就不會(huì)想太多多余的事情了。

            # re: GUI框架:消息檢查者  回復(fù)  更多評(píng)論   

            2009-11-26 14:59 by lch
            GUI 框架固然重要,
            但漂亮的控件,和所見(jiàn)即所得的GUI設(shè)計(jì)器更加重要

            Mac OS, IPhone, Andriod都是如此

            # re: GUI框架:消息檢查者  回復(fù)  更多評(píng)論   

            2009-11-26 17:28 by cexer
            @lch
            一個(gè)是手段,一個(gè)是目的,何來(lái)重要和更重要之說(shuō)。

            # re: GUI框架:消息檢查者  回復(fù)  更多評(píng)論   

            2009-11-27 00:10 by lch
            我的意思是原生的框架已經(jīng)夠用的情況下,控件的成熟度顯得更重要。

            # re: GUI框架:消息檢查者  回復(fù)  更多評(píng)論   

            2009-11-30 10:54 by 俠客西風(fēng)
            高手討論吧,

            新手路過(guò),學(xué)習(xí)中...

            以后水平高了再和你們討論...

            又見(jiàn) OwnWaterloo 兄,
            還認(rèn)識(shí)了cexer,陳梓瀚(vczh) 高手...

            # re: GUI框架:消息檢查者  回復(fù)  更多評(píng)論   

            2009-12-01 10:10 by cexer
            @lch
            這個(gè)我同意。不過(guò)到后來(lái)的豐富控件,已經(jīng)基本上是體力活了。

            # re: GUI框架:消息檢查者  回復(fù)  更多評(píng)論   

            2009-12-19 16:51 by Goteet
            我想問(wèn)個(gè)問(wèn)題,假如消息接受者在收到消失的時(shí)候要把消息發(fā)送者刪除怎么辦

            # re: GUI框架:消息檢查者  回復(fù)  更多評(píng)論   

            2010-01-05 12:30 by 赤腳流浪人
            怎么加你好友啊,很喜歡你寫(xiě)的這些東西

            # re: GUI框架:消息檢查者[未登錄](méi)  回復(fù)  更多評(píng)論   

            2011-05-22 21:35 by Neo
            關(guān)于GUI的文章停了嘛,我在恭候呢。

            # re: GUI框架:消息檢查者  回復(fù)  更多評(píng)論   

            2012-09-04 09:57 by Richard Wei
            mark下
            色综合久久88色综合天天| yy6080久久| 久久偷看各类wc女厕嘘嘘| 99久久精品费精品国产一区二区| 久久精品国产清高在天天线| 欧美精品一本久久男人的天堂| 久久99精品久久久久久水蜜桃| 日韩精品无码久久一区二区三| 久久久无码精品亚洲日韩京东传媒 | 老色鬼久久亚洲AV综合| 国产成人无码精品久久久性色| 日本强好片久久久久久AAA| A狠狠久久蜜臀婷色中文网| 久久精品国产亚洲Aⅴ香蕉| 久久久久亚洲AV片无码下载蜜桃| 97久久精品人人做人人爽| 青青草原精品99久久精品66| 久久综合九色综合精品| 日日狠狠久久偷偷色综合免费| 国产美女久久久| 蜜臀久久99精品久久久久久| 久久综合欧美成人| 久久人人爽人人人人爽AV | 久久99精品久久久久久噜噜| 久久精品黄AA片一区二区三区| 久久亚洲精品国产亚洲老地址| 国产免费久久久久久无码| 久久国产精品久久精品国产| 久久国产精品77777| 色婷婷综合久久久中文字幕| 国内精品久久久久影院老司| 久久精品成人欧美大片| 国产精品xxxx国产喷水亚洲国产精品无码久久一区 | 久久精品国产久精国产一老狼| 久久精品无码一区二区三区免费| 久久超乳爆乳中文字幕| 国产精品99久久久精品无码| 久久电影网| 久久一区二区三区99| 污污内射久久一区二区欧美日韩| 久久天天躁狠狠躁夜夜2020老熟妇|