轉(zhuǎn)帖請注明出處 http://www.shnenglu.com/cexer/archive/2009/11/22/101591.html
1 胸口碎大石
緊接上話:GUI框架:談?wù)効蚣埽瑢憣懘a 。廢話是肯定首先要說的,既為了承前啟后點明主題,也為了拉攏人心騙取回復(fù)。本來我想像自己上篇博文寫出來勢必像胸口碎大石一樣威猛有力,在街邊拉開陣勢,大吼一聲舉起錘子正要往下砸的時候,卻看到幾位神仙手提醬油瓶優(yōu)雅地踏著凌波微步路過,聽他們開口閉口說的都是六脈神劍啊,九陰真級啊這些高級東西,我的威猛感一下消失于無形,取而代之的是小孩子玩水槍的渺小。但是不管怎么樣攤子都鋪開了,這一錘子不砸下去,對不起那涼了半天石頭的胸肌。
在此之前首先得感謝一下各位醬油眾。無論你們是看熱鬧的還是砸場子的,你們的圍觀都令我的博文增光不少。特別要感謝那幾位打架的神仙,你們使上篇博文真正變得有思想交鋒的精彩。我覺得你們的那些想法和爭論都非常有價值,建議你們不要只讓它們在這個角落里藏著,都寫到自己的博客上去讓更多的人看到吧。
走過路過不要錯過,有錢的捧個錢場,沒錢的繼續(xù)揮舞你的醬油瓶加油吶喊,我這一錘要砸下去了!
2 實現(xiàn)消息檢查者
上文將消息框架分為幾個部分,這篇博文實現(xiàn)其中的消息檢查者。經(jīng)典的用 API 編寫 GUI 程序的方式當中,消息檢查都是用 if 或者 switch 語句進行的:
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 映射宏展開
15: if ( message == WM_CREATE )
16: {
17: // ......
18: }
19: if ( message == WM_PAINT )
20: {
21: // ......
22: }
見過的很多的 GUI 框架并沒有在這原始的方式上進步多少,"只是將黑換成暗"。比如 MFC 和 WTL 的消息映射宏,就像是披在 if 語句上的皇帝的新衣。這種消息檢查方式的好處是速度快,不用額外的空間消耗,但壞處更明顯:不容易擴充。我覺得在好處和壞處之間的取舍很容易,有必要單獨給消息檢查的過程實現(xiàn)一個更具 OO 含義的執(zhí)行者:消息檢查者(MessageChecker )。
2.1 其實很簡單
要有消息檢查者,首先得有個消息(Message)。上篇博文中的消息定義雖然非常簡單,卻完全可以勝任目前需要的工作,因此我們直接復(fù)制過來。
1: typedef LRESULT MessageResult;
2: typedef UINT MessageId;
3: typedef WPARAM MessageWparam;
4: typedef LPARAM MessageLparam;
5:
6: // 簡單的消息定義
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)在開始定義消息檢查者。消息檢查者的職責:根據(jù)某種條件檢查消息并返回(是,否)的檢查結(jié)果,所以它既應(yīng)該有用于檢查的數(shù)據(jù),也應(yīng)該有用于檢查的動作函數(shù),并且該函數(shù)檢查返回布爾值。這不是很容易就出來了嗎:
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àn)不同的檢查方式,所以它應(yīng)該是虛函數(shù),這樣后繼者可以這樣的形式擴充檢查者隊伍:
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 這些和諧的名字,感覺消息檢查者就這樣實現(xiàn)完成了,我們的 GUI 框架似乎已經(jīng)成功邁出了重要的第一步。但是面對函數(shù) isOk ,有經(jīng)驗的程序員肯定會有疑問:真的這樣簡單就 ok 了?當然是 no。要是真有那么簡單,我何苦還在后面寫那么長的篇符呢,cppblog 又不能多寫字騙稿費的。
2.2 堆上生成 & 對象切割
看著虛函數(shù) isOk 會想聯(lián)到兩個關(guān)鍵詞:多態(tài),指針。多態(tài)意味著要保存為指針,指針意味著要在堆上生成。消息檢查者保存為指針的映射像下面這樣子(假設(shè)消息處理者叫做 MessageHandler ):
1: // 允許一個消息對應(yīng)多個處理者
2: typedef vector<MessageHandler> _HandlerVector;
3:
4: // 為了多態(tài)必須保存 MessageChecker 的指針
5: typedef pair<MessageChecker*,_HandlerVector> _HandlerPair;
6:
7: // 消息映射
8: typedef vector<_HandlerPair> _HandlerMap;
堆上生成是萬惡之源。誰來負責銷毀?何時銷毀?效率問題怎么辦?有的人此時可能想到了引用計數(shù),小對象分配技術(shù),內(nèi)存池。。。只是一個消息檢查的動作就用那么昂貴的實現(xiàn),就像花兩萬塊買張鼠標墊一樣讓人難以接受。所以我們想保存消息映射者的對象 MessageChecker 而不是它的指針,就像這個樣子:
1: // 允許一個消息對應(yīng)多個處理者
2: typedef vector<MessageHandler> _HandlerVector;
3:
4: // 保存 MessageChecker 對象而不是指針
5: typedef pair<MessageChecker,_HandlerVector> _HandlerPair;
6:
7: // 消息映射
8: typedef vector<_HandlerPair> _HandlerMap;
但這樣的保存方式帶來了一個新的問題:對象切割。如果往映射中放入派生類的對象比如 CommandChecker 或者 NotifyChecker,編譯器會鐵手無情地對它們進行切割,切割得它們體無完膚搖搖欲墜,只剩下 MessageChecker 子對象為止 。砍頭不要緊,只要主義真,但是要是切割過程中切掉了虛函數(shù)表這個命根子就完蛋了,在手執(zhí)電鋸的編譯器面前玩耍虛函數(shù),很難說會發(fā)生什么可怕的事情。
有一種解決方案是我們不使用真正的虛函數(shù),而是自己模擬虛函數(shù)的功能。具體辦法是在 MessageChecker 當中保存一個函數(shù)指針,由子類去把它指向自己實現(xiàn)的函數(shù),非虛的 MessageChecker::isOk 函數(shù)去調(diào)用這個指針。修改 MessageChecker 的定義:
1: // 領(lǐng)銜的消息檢查者,用函數(shù)指針模擬虛函數(shù)
2: class MessageChecker
3: {
4: // 模擬虛函數(shù)的函數(shù)指針類型
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 的默認實現(xiàn)是只檢查消息id,這也是 MFC 的映射宏 MESSAGE_HANDLER 干的事情。現(xiàn)在解決了不要堆生成與要求多態(tài)的矛盾關(guān)系,真正完成了消息檢查者的定義。
2.3 擴充隊伍
要擴展消息檢查者隊伍,可以從 MessageChecker 派生新類,定義自己的檢查函數(shù)(virtualIsOk 或者其它名字都可以)并將 MessageChecker::m_visok 指向這個函數(shù)。要注意的是因為存在對象切割問題,所以派生類不應(yīng)該定義新的數(shù)據(jù)成員,畢竟切掉花花草草也是非常不好的。舉例說明派生方法。
比如從消息檢查者派生一個命令消息(Command)的檢查者 CommandChecker:它定義了一個函數(shù) virtualIsOk ,此函數(shù)檢查消息id是否為 WM_COMMAND ,并進一步檢查控件id和命令code,然后將指針 Message::m_visok 指向這個函數(shù) CommandChecker::virualIsOk,這樣 MessageChecker::isOk 實際上就是調(diào)用的 CommandChecker::virtualIsOk 了。CommandChecker 的功能類似于 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,并進一步檢查控件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: };
同理定義一個通知消息(Notification)的檢查者 NotifyChecker,其功能類似于 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 ,并進一步檢查控件 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ā)揮想像進行擴展,這個消息檢查者可以做很多事情。比如定義一個范圍id內(nèi)命令消息的檢查者 RangeIdCommandChecker,其功能類似于 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,并進一步檢查控件 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: };
定義一個按鈕點擊消息的消息檢查者:
1: // 按鈕點擊的命令消息檢查者
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,并進一步檢查命令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é)是新加的。因為有人提出數(shù)據(jù)容納不下的問題:MessageChecker 目前的定義只能容納 WPAWAM,LPARAM 兩個參數(shù)大小的數(shù)據(jù),而因為在存在對象切割問題,子類又不能定義新的數(shù)據(jù),那如果 WPARAM,LPARAM 裝不下需要的數(shù)據(jù)了怎么辦?難道就只能把數(shù)據(jù)在子類當中定義,然后每次都在堆上生成 MessageChecker 嗎?
我在這里的解決方案是,在 MessageChecker 里面定義一個多態(tài)的數(shù)據(jù)成員,這個成員大多數(shù)時候是空的,當需要的時候從堆上生成它,用它來裝下數(shù)據(jù)。如代碼所示:
先定義一個多態(tài)的數(shù)據(jù)類 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 當中加入這個數(shù)據(jù)成員:
1: // 加入多態(tài)的數(shù)據(jù)成員
2: class MessageChecker
3: {
4: //......
5: public:
6: MessageData* data;
7: }
8:
9: // 拷貝構(gòu)造時同時深拷貝數(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ù)據(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)時刪除
43: MessageChecker::~MessageChecker()
44: {
45: if ( data )
46: {
47: data->release();
48: data = NULL;
49: }
50: }
舉例說明怎么樣使用 data 成員。例如要在一個消息檢查者需要比較字符串,則在其中必須要保存供比較的字符串。先從 MessageData 派生一個保存字符串的數(shù)據(jù)類 MessageString:
1: // 裝字符串多態(tài)數(shù)據(jù)類
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: };
然后在這個消息檢查者中可以使用這個類了:
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: };
為了使用方便可以定義一個數(shù)據(jù)類的模板:
1: // 數(shù)據(jù)類的模板
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: };
然后可以將字符串數(shù)據(jù)類的定義簡化為:
1: // 利用模板生成數(shù)據(jù)類
2: typedef MessageDataT<String> MessageString;
2.5 龍?zhí)籽輪T
接下來可以進行消息檢查者的測試了。但因為消息檢查者的工作牽到其它兩個角色,所以測試之前必須要先簡單模擬出這兩個龍?zhí)捉巧合⑻幚碚吆拖⒈O(jiān)聽者。
消息處理者(MessageHandler )的作用顧名思義不用多作解釋,它在 GUI 框架當中的作用十分重要,實現(xiàn)起來也最復(fù)雜,但在這本文中它不是主角,所以可以先隨便拉個路人甲來跑跑龍?zhí)祝啡思组L得像這個樣子:
1: // 路人甲表演的消息處理者
2: typedef void (*MessageHandler)( const Message& message );
消息監(jiān)聽者(MessageListener )保存有消息檢查者與消息處理者的映射,并提供接口操作這些映射,當監(jiān)聽到消息的時候,消息監(jiān)聽者調(diào)用映射中的檢查者進行檢查,如果通過檢查則調(diào)用消息處理者來進行處理。消息監(jiān)聽者干的都是添加/刪除/查找這樣的體力活,看似實現(xiàn)起來用不著大腦,可是當涉及到真正的消息處理時情況會變得復(fù)雜,會遇到消息重入之類的問題。所以真正的實現(xiàn)留待日后再說,這里也先給出一個簡單的模擬定義含混過去:
1: // 消息監(jiān)聽者
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: // 消息從這里來了
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 ) ,實現(xiàn)很簡單,查找出消息處理者列表然后逐個調(diào)用其中處理者:
1: // 調(diào)用檢查者檢查,通過則調(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 進行測試
龍?zhí)锥家丫臀唬瑢?dǎo)演喊:"action!",現(xiàn)在可以寫點測試代碼測試一下了:
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 收場的話
這第一錘終于砸完了,石頭一裂為二,胸口完好無損。其實砸的時候心想,這一錘的分量砸下去不轟動神州也要震驚天府吧。但是回頭看看上面所有的文字,覺得這個東西怎么這么簡單,甚至連模板參數(shù)都沒有用到一個,更沒有談到效率,優(yōu)化什么的,肯定是不足以誘惑技術(shù)流的 cpper 們的。
想起自己曾經(jīng)寫過的幾個消息框架,可以算是把 C++ 的編譯期技術(shù)發(fā)揮得淋漓盡致了,但是出來的東西卻并不理想,后來慢慢領(lǐng)悟到一個道理:高尖的技術(shù)雖然炫酷,并不是處處都合適用。我的版本的消息檢查者就止于這個程度了,肯定有比這個更好的實現(xiàn),希望走過路過的高手們不要吝嗇自己的好想法,提出來與廣大醬油眾分享。
消息檢查總算寫完了。沒選上好季節(jié),電腦前坐了大半天手腳都冰涼的。上床睡覺去了,養(yǎng)足精神希望能看到新一輪的神仙打架。文章涉及的所有代碼項目下載:MessageChecker_200911251055.rar