• <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++博客 :: 首頁 :: 新隨筆 :: 聯(lián)系 :: 聚合  :: 管理

            GUI框架:談?wù)効蚣?,寫寫代碼

            Posted on 2009-11-15 18:09 cexer 閱讀(12314) 評論(176)  編輯 收藏 引用 所屬分類: GUI 、pattern

            1 開篇廢話

              我喜歡用C++寫 GUI 框架,因?yàn)槟欠N成就感是實(shí)實(shí)在在地能看到的。從畢業(yè)到現(xiàn)在寫了好多個(gè)了,都是實(shí)驗(yàn)性質(zhì)的。什么拳腳飛刀毒暗器,激光核能反物質(zhì),不論是旁門左道的陰暗伎倆,還是名門正派的高明手段,只要是 C++ 里有的技術(shù)都試過了。這當(dāng)中接觸過很多底層或是高級的技術(shù),像編譯時(shí)類型檢測,運(yùn)行時(shí)代碼修改等等,按實(shí)現(xiàn)的不同 GUI 涉及的東西是沒有邊際的。從最開始模仿 MFC,ATL 那樣的實(shí)現(xiàn)學(xué)到很多東西,然后開始看一些開源的著名的 GUI 框架,像 MFC,WTL,SmartWin++,win32gui,jlib2 ,VCF 獲得很多啟發(fā),到現(xiàn)在似乎有一種已看盡天下 GUI 的感覺。在學(xué)習(xí)別人的框架和自己的實(shí)現(xiàn)過程中,真真實(shí)實(shí)地感覺自己成長了不少,也有很多感悟。

              寫到這,我作為輪子制造愛好者,在這里向那些喊著"不要重復(fù)制造輪子的"批評家們承認(rèn)錯(cuò)誤。在有那么多好的輪子的情況下,我不值得浪費(fèi)地球資源,浪費(fèi)時(shí)間精力來自己手工重復(fù)打造。但是不值得歸不值得,在值得和喜歡之間我還是選擇后者。并且人生在世,什么才是值得?我覺得不是拯救人類,為世界和平做貢獻(xiàn),也不是努力奮斗,為地球人民謀福利,而是簡單地做自己喜歡的事。

              寫過的那些代碼很多都消失在硬盤的海洋里了,但那些挑燈苦想來的感悟還在。在它們也消失之前,我想利用空閑時(shí)間把這些覺得有點(diǎn)用處的經(jīng)驗(yàn)寫出來,正好這個(gè)博客也已經(jīng)快一年沒更新了。另外也算是對那些發(fā)我郵件的朋友的回應(yīng)。

              我的想法是用一系列日志,按照實(shí)現(xiàn)一個(gè) GUI 框架的具體思維遞進(jìn)過程來闡述實(shí)現(xiàn)一個(gè) GUI 框架的具體思維遞進(jìn)過程。這樣說好像有點(diǎn)遞歸,簡單地解釋就是這一系列日志不是想用《記憶碎片》那樣錯(cuò)亂的敘述方式來說明一個(gè)多有意思的故事,而是盡量簡單自然地記錄一下寫 GUI 框架過程中我的思考。這個(gè)遞進(jìn)過程也就是實(shí)現(xiàn)一個(gè) GUI 框架的過程,一系列日志之后,我們將會(huì)看到一個(gè)長得漂亮眼,極富彈性,能干又節(jié)約的 GUI 框架。

              雖然寫的內(nèi)容都是在 Windows 的 GUI 系統(tǒng)之上,但其原理是觸類旁通的,其它基于消息的 GUI 系統(tǒng)也都大同小異。所用的代碼也都是闡述原理的,自知絕對達(dá)不到商業(yè)巨作的水準(zhǔn),所以請不要一上來就批判,要知道我只是想分享而已。之所以先這樣說一下,是很害怕那種一上來就"怎么不跨平臺啊?","怎么都還看得到HWND?。?,"怎么不能用成員函數(shù)處理消息???"的同志。不喜歡站在高處指著別人的天靈蓋說話的人。要知道車輪也是一步步造出來的,不要一開始就想載著MM在高速路上飆豪車像少年啦飛馳。

              我認(rèn)為寫技術(shù)博客有三種境界,一種是一直在那繪聲繪色地描述自己的魚有多可口多美味,讓讀者只能垂涎興嘆,一種是授人以魚的人,悶頭就擺出來各種生猛海鮮,讓讀者難以消化,還有一種境界是授人以漁,怎么釣魚怎么煮魚都細(xì)細(xì)地教給讀者。讀博客的人有兩個(gè)境界,一種是只吃魚的,一上來就只要代碼,一種是學(xué)打魚的,想知其然更想知其所以然。讀博客時(shí)我努力做學(xué)打魚的類型,自己寫博客時(shí)我會(huì)努力做到授人以漁的境界。

              另外要說明的是,同樣作為塵世中的一個(gè)渺小個(gè)體,我大多數(shù)時(shí)候也是在為生存而奔波勞累著的。除此之外剩余的大多時(shí)候,更是要玩游戲,K歌,看電影,陪MM,吃喝玩樂。再剩余用來寫這個(gè)的時(shí)候不是很多,有可能這一系列日志一夜寫就,也有可能增刪五年披閱十載,孩子都叫爸了還沒完成。所以請大家不要對這個(gè)博客抱很大的期待,就當(dāng)我是路邊街頭的表演,你打醬油經(jīng)過時(shí)偶爾瞟過來一眼就好了。

              要說的廢話終于說完了,下面開始正題。

            2 基本概念

              基于消息的 GUI 框架的封裝,一切都圍繞消息展開。復(fù)雜的框架設(shè)計(jì),明確了需求之后,第一步首先是劃分模塊。所以,要闡述一個(gè)設(shè)計(jì)過程,第一步也應(yīng)該是先說清最基本的概念和模塊劃分,而不是一上來就用廣義相對論把讀者全部放倒。GUI 框架是干什么的當(dāng)然是地球人都知道的,但 GUI 框架沒有什么已經(jīng)劃分的標(biāo)準(zhǔn)概念,我是按照設(shè)計(jì)的需要來劃分的。如果把 GUI 框架看作一個(gè)單位,那么這個(gè)單位里最重要的角色有這幾個(gè):

            • 消息發(fā)送者(message sender)
            • 消息監(jiān)聽者(message listener)
            • 消息檢查者(message checker)
            • 消息處理者(message handler)
            • 消息分解者(message cracker)
            • 消息映射者(message mapper)

              下面分別說明。

            2.1 消息發(fā)送者和消息(message sender,message) 

              消息發(fā)送者其實(shí)只是在這里友情客串一下,它不在框架設(shè)計(jì)之內(nèi),由操作系統(tǒng)扮演這個(gè)勞苦功高的角色,它的工作是將消息發(fā)送到消息監(jiān)聽者。在這里面隱含了一下最重要的角色,消息。其實(shí)剩余的所有角色說到底也只是死跑龍?zhí)椎?,真正領(lǐng)銜的是消息本身,比如窗口大小改變了的消息,按鈕被點(diǎn)擊了的消息等等,所有人都高舉旗幟緊密團(tuán)結(jié)在它周圍進(jìn)行工作。但消息本身只是一個(gè)很簡單的數(shù)據(jù)結(jié)構(gòu),因?yàn)樵購?fù)雜的 GUI 系統(tǒng),它的消息也不過是幾個(gè)參數(shù),所以框架的實(shí)現(xiàn)重點(diǎn)在其它的角色。在此之前簡單地封裝一下消息,一個(gè)最簡單的封裝可能是這樣:

               1: // 消息封裝類
            
               2: class Message
            
               3: {
            
               4: public:
            
               5:     Message( UINT id_=0,WPARAM wparam_=0,LPARAM lparam_=0 )
            
               6:         :id( id_ )
            
               7:         ,wparam ( wparam_ )
            
               8:         ,lparam ( lparam_ )
            
               9:         ,result ( 0 )
            
              10:     {}
            
              11:
            
              12:     UINT      id;
            
              13:     WPARAM    wparam;
            
              14:     LPARAM    lparam;
            
              15:     LRESULT   result;
            
              16: };
            

              就這樣的我們的公司已經(jīng)有了核心角色了。從概念上講,我們的這個(gè)基于消息的 GUI 框架已經(jīng)完成了 99% 。然后我們可以以它為中心,按功能劃分進(jìn)行詳細(xì)討論,一步步完成那剩余的 1% 的極富創(chuàng)意和挑戰(zhàn)的工作。在此之前,先得簡單解釋一下這幾個(gè)角色都各是什么概念。消息傳送者如上所述,將不在討論范圍內(nèi)。

            2.2 消息監(jiān)聽者(message listener) 

              消息監(jiān)聽者完成的工作是從操作系統(tǒng)接收到消息,消息是從這里真正到達(dá)了框架之內(nèi)。最簡單的消息監(jiān)聽者是一個(gè)提供給操作系統(tǒng)的回調(diào)函數(shù),比如在 Windows 平臺上這個(gè)函數(shù)的樣子是這樣:

               1: //我是最質(zhì)樸的消息接收者
            
               2: LRESULT CALLBACK windowProc( HWND window,UINT id,WPARAM wparam,LPARAM lparam );
            

              一個(gè)好 GUI 框架當(dāng)然不能赤祼祼地使用這個(gè)東西,我們要在此之上進(jìn)行面向?qū)ο蟮姆庋b。消息監(jiān)聽者能想到的最自然的封裝模式是觀察者模式(Observer),這樣的模式下的監(jiān)聽者實(shí)現(xiàn)看起來像這個(gè)樣子:

               1: //我是一個(gè)漂亮的觀察者模式的消息監(jiān)聽者
            
               2: class MessageListener
            
               3: {
            
               4: public:
            
               5:     virtual LRESULT onMessage( Message* message ) = 0;
            
               6: };
            
               7:
            
               8: //監(jiān)聽者這樣工作
            
               9: MessageListener* listener;
            
              10: window->addListener( listener );
            
              11:
            

              jlib2 和 VCF 的實(shí)現(xiàn)就是這種模式。但現(xiàn)實(shí)當(dāng)中大多數(shù)框架沒有使用這種模式,比如 SmartWin++ 和 win32gui ,甚至沒有使用任何模式比如 MFC 和 WTL 。我想它們所以不采用觀察者模式,有些是因?yàn)榭蚣苷w實(shí)現(xiàn)的牽制,有的則可能是因?yàn)闆]能解決某些技術(shù)問題。我們的 GUI 框架將實(shí)現(xiàn)觀察者模式的消息監(jiān)聽者,所以這些問題我們后面也會(huì)遇到,到時(shí)候再詳述。

            2.3 消息檢查者(message checker)

              消息檢查者完成的工作很簡單。當(dāng)收到消息的時(shí)候,框架調(diào)用消息檢查者檢查這個(gè)消息是否符合某種條件,如果符合,則框架再調(diào)用消息處理者來處理這個(gè)消息,所以有點(diǎn)類似一個(gè)轉(zhuǎn)換者,輸入(消息),輸出一個(gè)(是/否)的值。最簡單的檢查者可能就是一個(gè)消息值的比較,比如:

               1:
            
               2: /最簡單的消息檢查者
            
               3: essage.id == /*消息值*/
            
               4:
            
               5: /比如
            
               6: essage.id == WM_CREATE
            

              展開MFC 和 ATL 的消息映射宏,可以看到它們的消息檢查就是用堆積起來的消息值比較語句完成。這就是消息檢查者最原始最自然最簡單的實(shí)現(xiàn)方式,但這種方式缺陷太多。我們的框架將實(shí)現(xiàn)一個(gè)自動(dòng)化,具有擴(kuò)展性的消息檢查者,后文詳細(xì)討論。

            2.4 消息處理者(message handler)

              消息處理者是我們最終的目的。GUI 框架所做的一切努力都只是前期的準(zhǔn)備,直到消息處理者運(yùn)行起來那一刻,整個(gè)公司才算是真正地運(yùn)轉(zhuǎn)起來了。消息處理者的具體實(shí)現(xiàn)可能是自由函數(shù),成員函數(shù)或者其它可調(diào)用體,甚至可以是外部腳本,處理完畢可能需要給操作系統(tǒng)返回一個(gè)結(jié)果。最簡單的消息處理者可以就是條語句,比如:

               1: //消息處理
            
               2: alert( "窗口創(chuàng)建成功了!" );
            
               3:
            
               4: //返回結(jié)果
            
               5: message.result = TRUE;
            

              上面代碼中"顯示消息框"的動(dòng)作就是一個(gè)消息處理,以上兩行代碼可視為消息處理者。最常見的消息處理者是函數(shù),比如:

               1: //消息處理
            
               2: _handleCreated( message );
            

              代碼中的函數(shù) _handleCreated 就是一個(gè)典型的消息處理者。消息處理者的實(shí)現(xiàn)難處在于,既要支持多樣性的調(diào)用接口,又要支持統(tǒng)一的處理方式。我們的框架將實(shí)現(xiàn)一個(gè)支持自由函數(shù),成員函數(shù),函數(shù)對象,或者其它可調(diào)用體的消息處理者,并且這些可調(diào)用體可以具有不同參數(shù)列表。后文將進(jìn)行消息處理者的詳細(xì)討論。

              在這里有必要再說明一下。一個(gè)判斷語句的大括號之前(判斷部分)是消息檢查的動(dòng)作,大括號之內(nèi)(執(zhí)行部分)是實(shí)際的消息處理。因此一個(gè)判斷語句雖簡單,卻包含消息檢查者和消息處理者,以及另外一個(gè)神秘的部分(見后文),一共三個(gè)部分。代碼像這樣:

               1: if ( //消息檢查者 )
            
               2: {
            
               3:     //消息處理者
            
               4: }
            

              比如下面的代碼:

               1: // message.id == WM_CREATE 是消息檢查者
            
               2: // _handleCreated( message )是消息處理者
            
               3:
            
               4: if ( message.id == WM_CREATE )
            
               5: {
            
               6:     _handleCreated( message );
            
               7: }
            
               8:
            

            2.5 消息分解者(message cracker)

              消息分解者是為消息處理者服務(wù)的。不同的消息處理者需要的信息肯定不一樣,比如一個(gè)繪制消息(WM_PAINT)的消息處理者可能需要的是一個(gè)圖形設(shè)備的上下文句柄(HDC),而一個(gè)按鈕點(diǎn)擊消息(BN_CLICK)的消息處理者則可能需要的是按鈕的ID,它們都不想看到一個(gè)赤祼祼的消息杵在那里。從消息中分解出消息攜帶的具體信息,這就是消息分解者的工作。最簡單的消息分解者可能是一個(gè)強(qiáng)制轉(zhuǎn)換,比如:

               1: // WM_CREATE 消息參數(shù)分解
            
               2: CREATESTRUCT* createStruct = (CREATESTRUCT*)message.lparam;
            
               3:
            
               4: // WM_SIZE 消息參數(shù)分解
            
               5: long width  = LOWORD( message.lparam );
            
               6: long height = HIWORD( message.lparam );
            

              上面的的代碼雖然簡單但 100% 完成了消息分解的任務(wù),所以它也是合格的消息分解者。我的框架將實(shí)現(xiàn)一個(gè)自動(dòng)化,可擴(kuò)展的消息分解者。后文將以此為目標(biāo)進(jìn)行詳細(xì)討論。

            2.6 消息映射者(message mapper)

              消息映射者是最直接與框架外部打交道的部分,顧名思義,它的工作就是負(fù)責(zé)將消息檢查者與消息處理者映射起來。最簡單的映射者可以是一條判斷語句,這個(gè)判斷語句,如代碼所示:

               1: // if 語句的框架就是一個(gè)消息映射者
            
               2:
            
               3: // 消息映射者
            
               4: if ( /*消息檢查者*/ )
            
               5: {
            
               6:     /*消息處理者*/
            
               7: }
            
               1: // if 語句將消息檢查者 message.id==WM_CREATE 和消息處理者 _handleCreated(message) 聯(lián)系起來了
            
               2: if ( message.id == WM_CREATE )
            
               3: {
            
               4:     _handleCreated( message );
            
               5: }
            

              上面的代碼 的if 語句中,判斷的部分是消息檢查者,執(zhí)行的部分是消息處理者。if 語句把這兩個(gè)部分組成了一個(gè)映射,這是最簡單的消息映射者。到這里可以發(fā)現(xiàn),這個(gè)簡單的 if 語句有多不簡單。它低調(diào)謙遜但獨(dú)自地完成了很多工作,就像公司的小張既要寫程序,又要掃地倒茶,還義務(wù)地給女同事講笑話。MFC 和 WTL 的消息映射宏展開就是這樣的 if 語句。像 jlib2 那樣的框架,雖然處理者都虛函數(shù),但在底層也是用 if 語句判斷消息然后來進(jìn)行調(diào)用的。當(dāng)然還有華麗一點(diǎn)的消息映射者,像這樣:

               1: // 華麗一點(diǎn)的消息映射者
            
               2: window.onCreated( &_handledCreated );
            

              這個(gè) onCreated 也是一個(gè)消息映射者,在它的內(nèi)部把 WM_CREAE 消息和 _handleCreated 函數(shù)映射到一起,這種方式最有彈性,但實(shí)現(xiàn)起來也比宏和虛函數(shù)都要困難得多。SmarWin++ 就是使用的這種方式,它的消息映射者版本看起來一樣的陽光帥氣,但內(nèi)部實(shí)現(xiàn)有些細(xì)節(jié)稍嫌猥瑣。我們的 GUI 框架將實(shí)現(xiàn)一個(gè)看起來更美,用起來很爽的消息映射者像這個(gè)樣子:

               1: // 將消息處理者列表清空,設(shè)置為某個(gè)處理者
            
               2: // 可以這樣
            
               3: window.onCreated  = &_handleCreated;
            
               4: // 或者這樣
            
               5: window.onCreated.add( &_handleCreated );
            
               6:
            
               7: // 在消息處理者列表中添加一個(gè)處理者
            
               8: // 可以這樣
            
               9: window.onCreated += &_handleCreated;
            
              10: // 或者這樣
            
              11: window.onCreated.add( &_handleCreated );
            
              12:
            
              13: // 清空消息處理者列表
            
              14: // 可以這樣
            
              15: window.onCreated --;
            
              16: // 或者這樣
            
              17: window.onCreated.clear();
            

              值得說一下,這種神奇的映射者是接近零成本的,它沒有數(shù)據(jù)成員沒有虛函數(shù)什么都沒有,就是一個(gè)簡單的空對象。就像傳說中的工作能力超強(qiáng),但卻不拿工資,不泡公司MM,甚至午間盒飯也不要的理想職員。在后文當(dāng)中會(huì)具體詳述這個(gè)消息映射者的實(shí)現(xiàn)。

            3 結(jié)尾廢話

              到目前為止我們的框架已經(jīng)完成了 99% 。下篇準(zhǔn)備開始寫最簡單的消息檢查者,但說實(shí)話我也不知道下一篇什么時(shí)候開始??纯瓷弦黄罩荆谷皇且荒昵皩懙?,這一年內(nèi)發(fā)生的事情很多,但自己渾渾噩噩地的好像一眨眼就到了現(xiàn)在,看著 CPPBLOG 上的好多其它兄弟出的很多很有水準(zhǔn)的東西,心里真是慚愧。昨天看了《2012》,現(xiàn)在心里還殘留有那種全世界在一間瞬間灰飛煙滅的震撼,2012年也不遠(yuǎn)了,我也趕緊在地球毀滅之前加把油把這些日志寫完了吧。不管怎么樣,今天哥先走了,請不要迷戀哥。

            評論共2頁: 1 2 

            Feedback

            # re: GUI框架:談?wù)効蚣?,寫寫代碼  回復(fù)  更多評論   

            2009-11-15 18:21 by OwnWaterloo
            占個(gè)沙發(fā),對這個(gè)很好奇:

            "值得說一下,這種神奇的映射者是接近零成本的,它沒有數(shù)據(jù)成員沒有虛函數(shù)什么都沒有,就是一個(gè)簡單的空對象。就像傳說中的工作能力超強(qiáng),但卻不拿工資,不泡公司MM,甚至午間盒飯也不要的理想職員。"

            # re: GUI框架:談?wù)効蚣埽瑢憣懘a  回復(fù)  更多評論   

            2009-11-15 18:24 by cexer
            @OwnWaterloo
            謝謝沙發(fā),后面會(huì)詳細(xì)說說這個(gè)消息映射者的實(shí)現(xiàn)

            # re: GUI框架:談?wù)効蚣埽瑢憣懘a  回復(fù)  更多評論   

            2009-11-15 18:38 by WXX
            如果單就消息的派發(fā),那么一個(gè)gui框架可以很容易實(shí)現(xiàn),但是要很好的管理窗口之間的關(guān)系,很好的處理鍵盤加速鍵,鼠標(biāo)等這些交互就麻煩了,考慮消息的過濾等。

            # re: GUI框架:談?wù)効蚣?,寫寫代碼  回復(fù)  更多評論   

            2009-11-15 18:40 by OwnWaterloo
            從這2行代碼來說:
            window.onCreated.add( &_handleCreated );
            window.onCreated.clear();


            從UINT message 到 onCreate 的這個(gè)分派(dispatch)過程,無論怎樣都必定是一個(gè)運(yùn)行時(shí)解析過程。
            無論使用何種C++高級技巧,也不可能讓它變?yōu)榫幾g時(shí)dispatch。
            因?yàn)閙essage是一個(gè)運(yùn)行時(shí)才能得到的值……
            只是,這個(gè)dispatch的工作,可能不是由window所在的class完成,而是其他某個(gè)層次完成。
            總之,這個(gè)層次一定存在,這個(gè)工作(運(yùn)行時(shí)dispatch)一定免不了。
            我猜得對嗎……


            接下來,框架將message 分派到onXXX之后,客戶再將onXXX轉(zhuǎn)發(fā)(forwarding)自己的handler這個(gè)過程,我相信是可以編譯時(shí)確定的。
            ——因?yàn)槲铱催^你以前的一些實(shí)現(xiàn)~_~

            但是,編譯時(shí)確定,就意味著運(yùn)行時(shí)不可更改。
            如果要運(yùn)行時(shí)可更改 —— 如上面2行代碼 —— 就一定需要一個(gè)"可調(diào)用體"(callable)來作handler的占位符(placeholder)。
            是這樣嗎?
            而C/C++里面,完全不占用一點(diǎn)空間的callable…… 是不存在的,對吧?


            所以我很好奇window是如何零成本完成映射與轉(zhuǎn)發(fā),并且是一個(gè)空對象的。
            映射肯定需要在某個(gè)地方做,可能不是window。
            運(yùn)行時(shí)可更改轉(zhuǎn)發(fā)目的地而不使用數(shù)據(jù), 好像…… 太不可思議了……


            # re: GUI框架:談?wù)効蚣埽瑢憣懘a  回復(fù)  更多評論   

            2009-11-15 18:48 by cexer
            @OwnWaterloo
            這個(gè)消息映射者的實(shí)現(xiàn)沒有數(shù)據(jù)成員,沒有虛函數(shù)存在,它其實(shí)就是一個(gè)調(diào)用,所以它也是有時(shí)間成本的。是的,所以我說是接近0成本,而不是真正的0成本。畢竟世界上沒有那樣傳說的員工。但如果好的編譯期可以輕松優(yōu)化掉這個(gè)小小調(diào)用。

            你說不可思議,那倒沒到那個(gè)境界哈。你可以看看《Imperfect C++》當(dāng)中的method_property的實(shí)現(xiàn),跟那個(gè)很類似,不過他的實(shí)現(xiàn)不符合C++標(biāo)準(zhǔn),應(yīng)范圍有限。

            用這種消息映射者的方式,我也實(shí)現(xiàn)了值主義的屬性,比如:
            window.size = Size( 100,100 );
            Size size = window.size;

            從理論上來說,也接近0成本的。

            # re: GUI框架:談?wù)効蚣?,寫寫代碼  回復(fù)  更多評論   

            2009-11-15 18:53 by cexer
            @OwnWaterloo
            你說的“接下來,框架將message 分派到onXXX之后,客戶再將onXXX轉(zhuǎn)發(fā)(forwarding)自己的handler這個(gè)過程,我相信是可以編譯時(shí)確定的。
            ——因?yàn)槲铱催^你以前的一些實(shí)現(xiàn)~_~”

            你說的這個(gè)是以前另一個(gè)版本的框架了,跟這個(gè)完全不一樣了哦。你看那份代碼,它的消息映射是編譯期自動(dòng)進(jìn)行的,映射者,檢查者,分解者三個(gè)角色都是由一個(gè)東西全部完成的。我將寫的這個(gè)版本不一樣,是完全分離的實(shí)現(xiàn),沒有那種編譯期映射的功能,但運(yùn)行時(shí)映射可以獲得更大的擴(kuò)展性。

            可惜啊,以前那個(gè)版本的框架代碼我已經(jīng)找不到了。你那里有?

            你說的“以我很好奇window是如何零成本完成映射與轉(zhuǎn)發(fā),并且是一個(gè)空對象的。映射肯定需要在某個(gè)地方做,可能不是window。運(yùn)行時(shí)可更改轉(zhuǎn)發(fā)目的地而不使用數(shù)據(jù), 好像…… 太不可思議了……”

            看來你有點(diǎn)誤會(huì)我的意思了,肯定內(nèi)存當(dāng)中是有一個(gè) std:map 之類的映射數(shù)據(jù)存在的。我說的0成本指的是 window.onCreated 這個(gè)成員的實(shí)現(xiàn)。

            # re: GUI框架:談?wù)効蚣?,寫寫代碼  回復(fù)  更多評論   

            2009-11-15 18:57 by OwnWaterloo
            @cexer
            哦 原來是property模擬。

            window.size
            window.onCreated
            是一個(gè),呃,代理(proxy)對象?

            size是一個(gè)代理,有一個(gè)=操作符和轉(zhuǎn)換函數(shù):
            operator=(const Size& s) {
            this->window->setSize(s);
            }
            operator Size() const {
            return this->window->getSize();
            }

            這個(gè)代理對象也是要占空間的吧?


            呃…… 還是等你的源代碼與分析吧…… 不瞎猜了……
            看來你也是難得抓到一個(gè)更新blog的閑暇……

            # re: GUI框架:談?wù)効蚣埽瑢憣懘a  回復(fù)  更多評論   

            2009-11-15 18:58 by OwnWaterloo
            @cexer
            沒有完整的,就是你blog上發(fā)的一些片段……

            # re: GUI框架:談?wù)効蚣?,寫寫代碼  回復(fù)  更多評論   

            2009-11-15 19:01 by cexer
            @OwnWaterloo
            你說“哦 原來是property模擬。”
            嗯就是這樣的。

            你說“這個(gè)代理對象也是要占空間的吧?”
            理論上來說不要占空間的,但實(shí)際是占用一個(gè)字節(jié)的。因?yàn)镃++標(biāo)準(zhǔn)規(guī)定不允許存在0空間占用的成員變量,因?yàn)槟菚?huì)造成 &object.member1,&object.memeber2 兩個(gè)地址完全相同的情況。

            # re: GUI框架:談?wù)効蚣埽瑢憣懘a  回復(fù)  更多評論   

            2009-11-15 19:07 by cexer
            @WXX
            你說“如果單就消息的派發(fā),那么一個(gè)gui框架可以很容易實(shí)現(xiàn),但是要很好的管理窗口之間的關(guān)系,很好的處理鍵盤加速鍵,鼠標(biāo)等這些交互就麻煩了,考慮消息的過濾等?!?
            各有難處,但我覺得后者更容易一點(diǎn),以前我也都做過一些。我覺得好的框架應(yīng)該是在所有東西之前的。先有骨架,然后再說高矮胖瘦。長得是否高大健壯,陽光帥氣,就先看骨架如何了。

            # re: GUI框架:談?wù)効蚣?,寫寫代碼  回復(fù)  更多評論   

            2009-11-15 19:08 by cexer
            @OwnWaterloo
            你說“沒有完整的,就是你blog上發(fā)的一些片段……”
            我找找看,找到了也給你一分。那些片段隱藏了實(shí)現(xiàn)的,看不出什么有價(jià)值的東西。

            # re: GUI框架:談?wù)効蚣?,寫寫代碼  回復(fù)  更多評論   

            2009-11-15 19:43 by OwnWaterloo
            我去看了看Imperfect C++。因?yàn)槭怯⑽牡?,以前只看了一部分就停了…?br>我覺得前面都寫得不錯(cuò),ABI、mangling這些問題確實(shí)是很困擾……

            但property…… 我覺得這并不算C++的Imperfection。
            完全是被其他語言給蒙蔽了。

            一門語言就應(yīng)該有一門語言的樣子。
            比如用C,如無絕對必要,就不應(yīng)該模擬出虛函數(shù)與異常機(jī)制。
            書里面提到的7種優(yōu)勢,除了最后一種值得考慮,其他全都不值得稱作優(yōu)勢。

            property,C#中的,或者imperfect cpp 里面看起來的樣子,無非就是讓如下語法:
            o.p( value ); o.set_p( value );
            value = o.p(); value = o.get_p();

            變成如下語法:
            o.p = value;
            value = o.p;

            如果C++直接提供property的語法,那當(dāng)然是好事。
            但不提供,也不見得編程就進(jìn)行不下去了,也沒有絕對必要去模擬出這種東西。


            書中提到的7個(gè)好處,除了最后1個(gè),其他都不是property帶來的好處。

            1. 讀寫控制
            非property一樣可以實(shí)現(xiàn)讀寫控制

            2. 內(nèi)外表示轉(zhuǎn)換
            這依然是由Date的算法實(shí)現(xiàn),而不是property的功勞。
            不通過property,一樣可以實(shí)現(xiàn)time_t作為內(nèi)部表示,對外提供int get_xxx()

            3. 驗(yàn)證
            這個(gè)更搞笑了。
            就在書中這句話的下面列出的代碼,就明顯說明對day的驗(yàn)證是通過:
            void Date::set_DayOfMonth(int day);
            完成的。

            完全沒有property的半分功勞。

            4. 沒看太明白
            書中的意思是, Date.Now是一個(gè)緩存,Date::get_Now()是一個(gè)計(jì)算?
            其實(shí),這里真正需要的是"兩種語意",而不是property。
            這兩種語意是:
            Date::getNow();
            Date::updateNow();

            書中只是將getNow的語意,用property來表現(xiàn);get_Now同時(shí)完成updateNow和getNow。


            5. 可用性?
            We didn't have to write date.get_Month(), just date.Month.
            少敲點(diǎn)鍵盤而已……
            如果利用重載,用void Month(value)代表set,用value Month(void)代表get,少敲的,就是一個(gè)調(diào)用操作符……

            6. 不變式?
            沒看太明白。
            沒有property,也可以維護(hù)object的不變式,是吧?
            只要將數(shù)據(jù)hide,通過public interface去操作,就可以了。

            7. 范型編程
            我覺得就只有這個(gè)說到點(diǎn)子上了。 而這個(gè)也就是模擬property的真正的作用:改變語法。
            template<typename P>
            void distance(const P& p1, const P& p2 ) {
            假設(shè)這是一個(gè)計(jì)算2個(gè)點(diǎn)歐式距離的函數(shù)。 它已經(jīng)通過:
            p1.x;
            這種語法,而不是p1.x(); 這種語法寫成了。
            }

            如果新實(shí)現(xiàn)一個(gè)class, 想通過p1.x 這種語法訪問,同時(shí)還要有讀寫控制等, 就需要模擬出property。
            這可能是property真正的用處。




            換句話說,真的存在很多情況,使得我們"不得不"使用如下語法:
            o.p = value;
            value = o.p;

            來代替如下語法:
            o.set_p( value );
            value = o.get_p();
            嗎?

            對這種情況,property模擬才是有價(jià)值的。


            對很多情況,setter/getter的語法和property的語法都是可行的。
            如果本身提供了property語法的語言,那使用2者之一都沒關(guān)系。
            但C++沒有提供。對這種情況也非要去模擬出property的語法,就有點(diǎn)…… 為了property而property的味道了。


            我繼續(xù)去看書中怎么實(shí)現(xiàn)的……
            我以前了解的實(shí)現(xiàn)方式,每一個(gè)proxy至少得帶一個(gè)指針……
            這消耗還是蠻嚴(yán)重的……


            你怎么看?

            # re: GUI框架:談?wù)効蚣埽瑢憣懘a  回復(fù)  更多評論   

            2009-11-15 19:57 by OwnWaterloo
            我暈……
            侵入式的property……

            強(qiáng)大……

            # re: GUI框架:談?wù)効蚣埽瑢憣懘a  回復(fù)  更多評論   

            2009-11-15 20:13 by cexer
            @OwnWaterloo
            你說“我去看了看Imperfect C++。因?yàn)槭怯⑽牡?,以前只看了一部分就停了……我覺得前面都寫得不錯(cuò),ABI、mangling這些問題確實(shí)是很困擾……我繼續(xù)去看書中怎么實(shí)現(xiàn)的……”
            在C++里面屬性只是看起來很美的東西,其實(shí)用性和想像的有差距。你說得對,用方法能實(shí)現(xiàn)所有的功能,所以我的GUI框架已經(jīng)去掉了屬性支持。

            你說“我以前了解的實(shí)現(xiàn)方式,每一個(gè)proxy至少得帶一個(gè)指針……這消耗還是蠻嚴(yán)重的……”
            不一定得帶指針的。比如《C++ Imperfect》里實(shí)現(xiàn)的屬性是不需要帶指針的,但它不被C++標(biāo)準(zhǔn)支持,應(yīng)用有限制。我也實(shí)現(xiàn)了不需要帶指針的屬性,同時(shí)也是跟C++標(biāo)準(zhǔn)相容的,以后會(huì)說一下這個(gè)實(shí)現(xiàn)。

            # re: GUI框架:談?wù)効蚣?,寫寫代碼  回復(fù)  更多評論   

            2009-11-15 20:31 by OwnWaterloo
            @cexer
            Imperfect C++中的差不多看明白了……
            proxy是包含在object中,通過proxy在object中的offset得到object的地址……


            # re: GUI框架:談?wù)効蚣?,寫寫代碼  回復(fù)  更多評論   

            2009-11-15 20:48 by 空明流轉(zhuǎn)
            你還是講講2012比較好。

            # re: GUI框架:談?wù)効蚣埽瑢憣懘a  回復(fù)  更多評論   

            2009-11-15 22:02 by OwnWaterloo
            @cexer
            我想到一個(gè)幾乎沒有消耗的方案……
            我再完善一下……

            # re: GUI框架:談?wù)効蚣?,寫寫代碼  回復(fù)  更多評論   

            2009-11-15 22:07 by cexer
            @OwnWaterloo
            你說“我想到一個(gè)幾乎沒有消耗的方案……我再完善一下……”
            加油,出來了別忘了寫出來大家分享。

            # re: GUI框架:談?wù)効蚣?,寫寫代碼  回復(fù)  更多評論   

            2009-11-15 22:08 by cexer
            @空明流轉(zhuǎn)
            好的,是這樣的,《2012》說的是地球毀滅的故事,好了我說完了。
            哈哈!

            # re: GUI框架:談?wù)効蚣?,寫寫代碼  回復(fù)  更多評論   

            2009-11-15 22:33 by OwnWaterloo
            @cexer
            關(guān)鍵點(diǎn)在于:"如何通過proxy地址,得到object地址"。
             
            1. 可以給proxy放入一個(gè)object的指針。每個(gè)proxy消耗一個(gè)指針的空間。
            太浪費(fèi)了……
             
            2. Imperfect C++中利用offset來計(jì)算object的地址,使得每個(gè)proxy不含任何域。
            但C++為了區(qū)別各個(gè)proxy,會(huì)給每個(gè)proxy進(jìn)行填充,依然會(huì)造成消耗。
             
             
            3. 避免proxy在Record中被填充
            假設(shè)有如下記錄:
            Record
            {
                    proxy1 property1;
                    proxy2 property2;
                    proxy3 property3;
                    ...
            }
            其中proxy1,proxy2,proxy3, ...  是空結(jié)構(gòu)。
             
            Record r;
            r.property1;
            r.property2;
            r.property3;
            ...
             
            在C/C++中,如果想要property1、2、3、... 不占空間,也就是它們必須擁有相同的地址。
            也就是說…… Record 是union……
             
             
            4. 避免Record在class中被填充
            即使這樣,整個(gè)union依然是會(huì)被填充……
            所以,可以將class的數(shù)據(jù)和union放在一起,就真的不消耗任何空間了……
            (如果類本身不需要數(shù)據(jù),那這個(gè)類也是會(huì)被填充的,proxys依然沒有消耗多余的空間)
             
             
            代碼:
            class date
            {
            private:
                    
            struct second_proxy
                    {
                            
            operator int() const
                            {
                                    
            const void* o = this;
                                    
            int sec = static_cast<const date*>(o)->get_second();
                                    printf(
            "%d second_proxy::operator int(%p);\n",sec,o);
                                    
            return sec;
                            }
                            date
            & operator=(int sec)
                            {
                                    
            void* o = this;
                                    printf(
            "second_proxy::operator=(%p,%d);\n",o,sec);
                                    date
            * d = static_cast<date*>(o);
                                    d
            ->set_second(sec);
                                    
            return *d;
                            }
                    };

            public:
                    union
                    {
                            time_t time_;
                            second_proxy second;
                    };

                    date()
                    {
                            printf(
            "date::date(%p);\n",(void*)this);
                            time(
            &time_);
                    }

                    
            int get_second() const
                    {
                            
            int sec = localtime(&time_)->tm_sec;
                            printf(
            "%d date::get_second(%p);\n",sec,(void*)this);
                            
            return sec;
                    }

                    
            void set_second(int sec)
                    {
                            printf(
            "date::set_second(%p,%d);\n",(void*)this,sec);
                            tm t 
            = *localtime(&time_);
                            t.tm_sec 
            = sec;
                            time_ 
            = mktime(&t);
                    }
            };

            second_proxy hour和date的數(shù)據(jù)time_t time_放在同一個(gè)union中。
            這樣,它們擁有相同的地址。
            在second_proxy::operator int() const; 中,可以直接將this轉(zhuǎn)換到const date* ……
            就這樣得到了…… object的地址……
             
             
            在C中,必須給那個(gè)union取一個(gè)名字:
            struct date
            {
                    union
                    {
                            time_t time_;
                            second_proxy second;
                    } must_have_a_name;
            };
            struct date date;
            date.must_have_a_name.second; 必須這樣訪問……
             
            C++中有匿名聯(lián)合,所以可以直接:
            date d;
            d.second;
             
             
            這樣的壞處也很明顯……
            客戶代碼可以訪問 d.time_;   什么都完蛋了……
             
             
            我再想想怎么隱藏……
             

            # re: GUI框架:談?wù)効蚣埽瑢憣懘a  回復(fù)  更多評論   

            2009-11-16 02:48 by OwnWaterloo
            關(guān)于隱藏time_成員……
             
            1.
            class C
            {
            public:
                    union
                    {
                            proxy1 property1;
                            proxy2 property2;
                            proxy3 property3;
                            // private: // msvc和gcc這里是不能加入private的(具體要查標(biāo)準(zhǔn))
                    }
            };
             
             
            2.
            class C
            {
            public:
                    union
                    {
                            proxy1 property1;
                            proxy2 property2;
                            proxy3 property3;
                    }
            private:
                    struct impl impl_; // 一旦不放在union中
                    // properties就可能會(huì)被填充
            };
             
             
            仔細(xì)選擇union的位置,可能會(huì)起到減少總大小的效果,比如:
            class C1 {
                    char c_;
            public:
                    union { /* properties */ };
            private:
                    int i_;
            };
            class C2 {
                    int i_;
                    char c_;
            public:
                    union { /* properties */ };
            };
             
            因?yàn)閏har c_;的附近本來就會(huì)被填充,properties剛好占據(jù)那個(gè)位置。
             
             
            如果這樣,就可能會(huì)很糟糕:
            class C3 {
                    char c_;
                    int i_;
            public:
                    union { /* properties */ };
            };
             
            C++標(biāo)準(zhǔn)沒有說class的layout應(yīng)該怎么安排。
            每個(gè)access secion中的data,必須按聲明順序。
            但不同access secion之間,可以打亂順序 —— 這個(gè)也許會(huì)有一些幫助。
             
            第2種方案依然需要計(jì)算offsetof…… 這是很不靠譜的……
             
             
            3.
            作為base。
             
            struct properties
            {
                    union
                    {
                            proxy1 property1;
                            proxy2 property2;
                            proxy3 property3;
                            // ...
                    }
            };
             
            class C : public properties
            {
                    // data
            };
             
            這樣,可以很容易計(jì)算出C的位置:
            void property_proxy::operator=( ... ) {
                    void* p = this;
                    C* c = static_cast<C*>( static_cast<properties*>(p) );
            }
             
            編譯器會(huì)正確計(jì)算出C*,即使在多繼承下也行。比如:
            class C : other , public properties {};
             
            但是,在msvc和gcc上,properties 都不會(huì)執(zhí)行空基類優(yōu)化…… 很囧……
            仔細(xì)安排data的布局(在這2款編譯器上,將小對齊的數(shù)據(jù)放前面),也會(huì)影響整個(gè)類的大小。
             
             
            2和3的方案,如果能鉆到空子,就可以幾乎沒有代價(jià)的實(shí)現(xiàn)property。
            空子就是說,那個(gè)class本來就有一些需要被填充的空洞……
             
            如果沒有空可鉆……  或者沒有正確安排位置…… 會(huì)多占用一些大小。
            額外大小不超過class中所有域中需要最大對齊那個(gè)成員的大小。
            額外大小和properties的數(shù)量是無關(guān)的, 多少個(gè)properties都占用這么多額外大小, union嘛……
             
             
            4.
            還有一種方案。
            其實(shí)上面的date,缺陷在于time_t time_; 這個(gè)成員會(huì)被外界訪問。
             
            可以這樣:
            class date {
                    class impl {
                            time_t time_;
                            friend class date;
                    };
            public:
                    union
                    {
                            impl impl_;
                            proxy1 property1;
                            proxy2 property2;
                            proxy3 property3;
                    }
                    // ...
            };
             
            這樣,客戶代碼可以訪問的,只有impl_這個(gè)名字。
            但是幾乎不能拿它作任何事情。
            連它的類型名 —— date::impl —— 都是不可訪問的。
             
             
            這個(gè)方案的缺陷就是 —— 通常必須從頭設(shè)計(jì)一個(gè)類型。
            union中不能放非pod。只能從內(nèi)建類型開始,構(gòu)造一些類。
             
            比如已經(jīng)有某個(gè)非pod的類,C,想將C作為一個(gè)成員,實(shí)現(xiàn)Cex:
             
            class Cex
            {
                    union
                    {
                            C impl_; // 不行
                    }
            };
             
            只能用上面的property base或者property member……
             
             
            5.
            范化工作 
            這個(gè)…… 再說吧……
             如果能有更好的布局方案…… 范化工作就白做了……
             

            # re: GUI框架:談?wù)効蚣埽瑢憣懘a[未登錄]  回復(fù)  更多評論   

            2009-11-16 09:42 by foxriver
            針對游戲的每幀更新UI,這種消息結(jié)構(gòu)是沒有什么太大價(jià)值,一堆回調(diào)繼承函數(shù)會(huì)迅速膨脹代碼量。

            微軟用消息做UI最大的好處,是效率。可是當(dāng)顯卡,CPU如此快速,為什么不尋找另外一種UI解決方式?

            # re: GUI框架:談?wù)効蚣埽瑢憣懘a  回復(fù)  更多評論   

            2009-11-16 12:45 by 陳梓瀚(vczh)
            之前我已經(jīng)做過一個(gè)發(fā)在我的頁面上了,其實(shí)最麻煩的事,Windows所要求的標(biāo)準(zhǔn)界面行為API幾乎都沒實(shí)現(xiàn),而且很雜,舉個(gè)例子:

            如果有一個(gè)存在Default Button的窗口,你需要:
            在沒有其他按鈕獲得焦點(diǎn)的情況下,將Default屬性設(shè)置給那個(gè)按鈕
            在有按鈕獲得焦點(diǎn)的情況下,將Default屬性設(shè)置給有焦點(diǎn)的那個(gè)按鈕
            按下Enter的時(shí)候,窗口自動(dòng)激活帶Default屬性的按鈕的事件

            還有譬如如何消除task bar上面的窗口按鈕等等,或者往菜單加上圖片(API絕對沒支持,全部都是owner draw上去的?。。。?,或者shortcut(菜單的Ctrl + C什么的,也是你自己寫上去而不是系統(tǒng)自帶的,而且你負(fù)責(zé)關(guān)聯(lián)他們),或者Tab轉(zhuǎn)移焦點(diǎn)啦(WS_TABSTOP完全沒用,那是給你實(shí)現(xiàn)轉(zhuǎn)移焦點(diǎn)的時(shí)候去獲取控件的屬性然后決定要不要把焦點(diǎn)給它),或者容器被disable之后上面的控件也要跟著顯示成disable一樣啦,還有Vista以下版本你把一個(gè)checkbox放在groupbox里面是黑掉的,微軟證實(shí)了這個(gè)是bug,不過他只在Vista修了。諸如此類,林林總總,你慢慢處理……

            # re: GUI框架:談?wù)効蚣?,寫寫代碼  回復(fù)  更多評論   

            2009-11-16 12:56 by 陳梓瀚(vczh)
            @OwnWaterloo
            光有屬性還不夠,一般來說一個(gè)完整的屬性應(yīng)該是:

            a=Object.Property;
            Object.Property=b;
            Object.Property.OnChanging+=functor;
            Object.Property.OnChanged+=functor;

            //僅本類可用
            Object.Property.Self.OnChanging+=functor;
            Object.Property.Self.OnChanged+=functor;

            當(dāng)一個(gè)Property是int的時(shí)候,你要重載完所有操作符,才能讓Object.Property1 + Object.Property2這樣子的表達(dá)式成立,這一點(diǎn)不知道你想到了沒。

            所以為了提供更加好用的Property,你還得實(shí)現(xiàn)一個(gè)Ref<int>,讓Ref<int>同時(shí)兼容int&和&Object.Property,并且Ref<int>提供所有操作符重載。同時(shí),如果Property和Ref<X>是一個(gè)類的話,最好帶上->以便訪問成員函數(shù)。

            在C++提供property是一件吃力不討好的事情。還是罷了,之前試過,不值得。

            # re: GUI框架:談?wù)効蚣?,寫寫代碼  回復(fù)  更多評論   

            2009-11-16 12:59 by 陳梓瀚(vczh)
            @foxriver
            請使用.NET的WPF,這就是“當(dāng)顯卡,CPU如此快速”的時(shí)候的“另外一種UI解決方式”。

            # re: GUI框架:談?wù)効蚣?,寫寫代碼  回復(fù)  更多評論   

            2009-11-16 13:12 by OwnWaterloo
            @陳梓瀚(vczh)
            我在上面的評論也說了,在C++中沒有絕對必要,還是不要模擬這個(gè)東西了。
            沒有property,生活一樣可以繼續(xù)。


            至于 o.p1 + p.p2, &p.p1, 等模擬,并不是重點(diǎn)。
            proxy本來就和被proxy的對象有所區(qū)別。
            視你需要模擬的程度來決定編程的復(fù)雜度,或者發(fā)現(xiàn)某種特性無法實(shí)現(xiàn)。

            proxy的重點(diǎn)在于如何高效的在proxy中得到被proxy對象。
            實(shí)現(xiàn)了這個(gè)功能點(diǎn),才是其他op=,op T(), op +, ref proxy的前提 —— 這些技術(shù)基本屬于濫大街了 —— 去看vector<bool> —— 所以我對這些沒什么興趣。

            之所以重新開始研究這個(gè)技巧,是因?yàn)镮mperfect C++中提到了一種代價(jià)很低的方案。
            我覺得這個(gè)有點(diǎn)意思,所以繼續(xù)深入了一下。

            # re: GUI框架:談?wù)効蚣?,寫寫代碼  回復(fù)  更多評論   

            2009-11-16 13:19 by OwnWaterloo
            @陳梓瀚(vczh)
            還是用代碼說話吧……

            上面的date的代碼,重點(diǎn)在于這2行:
            union { /* ... */ };

            哦,還有一行在我的測試代碼中,并沒有抄出來:
            typedef int assume[sizeof(date)==sizeof(time_t)?1:-1];
            這2行才是我想表達(dá)的重點(diǎn) —— 沒有額外的空間消耗,實(shí)現(xiàn)property。


            寫上面那些代碼,為的是展示上面那個(gè)重點(diǎn),而不是下面這種爛大街的:
            operator = (int);
            operator int() const;
            這些技術(shù)早就被人討論得七七八八了,相當(dāng)無聊……


            這一點(diǎn)不知道你看出來了沒?

            # re: GUI框架:談?wù)効蚣埽瑢憣懘a  回復(fù)  更多評論   

            2009-11-16 13:35 by 陳梓瀚(vczh)
            @OwnWaterloo
            當(dāng)且僅當(dāng)好用,才有繼續(xù)考慮有沒有消耗的必要。一個(gè)高效的、會(huì)制造麻煩的解決方案一般是不被接受的。

            # re: GUI框架:談?wù)効蚣埽瑢憣懘a  回復(fù)  更多評論   

            2009-11-16 13:45 by cexer
            @OwnWaterloo
            謝謝你分享你的思考,真的很有創(chuàng)意。建議你可以把你的思考寫到你的博客里,讓更多的人看到。

            @陳梓瀚(vczh)
            高效的,不會(huì)制造麻煩的東西,也是從不高效的,會(huì)制造麻煩的東西進(jìn)化來的。所以我覺得只要在思考都是有價(jià)值的。

            # re: GUI框架:談?wù)効蚣埽瑢憣懘a  回復(fù)  更多評論   

            2009-11-16 13:50 by OwnWaterloo
            @陳梓瀚(vczh)
            1.
            如果一個(gè)property需要消耗一個(gè)指針的代價(jià),我認(rèn)為實(shí)現(xiàn)得再好用都是不可接受的…… 高得有點(diǎn)離譜。
            很容易一個(gè)類的property的消耗比類本身還要高。
            [data of instance]
            [pointer]
            [pointer]
            [pointer]
            [pointer]
            ....
            而且所有pointer都指向同一個(gè)地方……

            而且上面也提到,真正需要模擬出property的,是類似于point、vector、matrix這種類。
            p.x比p.x()更自然。
            讓這種類附加額外數(shù)據(jù),也是讓人很難接受的。


            2.
            是否好用,你可以另外開文章討論。
            只是你提到的那些使得proxy好用的技術(shù),都提不起我的興趣。
            理由很簡單 —— 這種文章太多,寫出來都不好意思和人打招呼。

            我關(guān)心的僅僅是proxy如何高效得到被proxy對象 —— 實(shí)現(xiàn)proxy的前提。

            我以前見過的,包括我自己想到的,都是上面one property one pointer的方案。imp cpp中提到的讓我確實(shí)有眼前一亮的感覺。
            而 op T(), op= (T), ref T op + 這些技術(shù),只讓我覺得漲眼睛 —— 看太多,早膩歪了。


            如果你的重點(diǎn)依然在proxy如何更仿真上,可能很難提起我的興趣。
            如果你有興趣思考proxy如何得到被proxy的object上,或者有一些方案,想法,歡迎分享出來~_~



            "當(dāng)且僅當(dāng)好用,才有繼續(xù)考慮有沒有消耗的必要。一個(gè)高效的、會(huì)制造麻煩的解決方案一般是不被接受的。"
            這句話絕對說得過于絕對了。
            如果你用的是C++、而不是C#,效率絕對不是可以不掛在心上的事情。
            為什么你要用C++,而不是用編程更方便,語法糖更多的C#?好好想想吧。

            # re: GUI框架:談?wù)効蚣?,寫寫代碼  回復(fù)  更多評論   

            2009-11-16 14:02 by OwnWaterloo
            @cexer
            我再想想如何做得更范化一些。
            減少需要編寫property的人的重復(fù)勞動(dòng)。

            但是遇到了困難……

            手工寫union base, union member沒什么問題。
            要將其做成模板…… 會(huì)遇到先有雞還是先有蛋的問題……

            比如:
            proxy< ..., getter , ... >

            實(shí)例化一個(gè)property:
            proxy< ..., &date::get_second, ... >; 這里需要date的定義?

            而date的定義無論是union base,union member又需要先實(shí)例化proxy< ... &date::get_second, ... >;……
            很囧……


            手工寫沒問題是因?yàn)椋?br>struct second_proxy {
            這里僅僅聲明其布局就可以了
            };

            然后定義date類

            然后實(shí)現(xiàn)proxy::op ...
            這里才需要引用到date::&getter


            我再想想……


            # re: GUI框架:談?wù)効蚣?,寫寫代碼  回復(fù)  更多評論   

            2009-11-16 14:50 by 俠客西風(fēng)
            準(zhǔn)備把你的博客加到我的友情鏈接里,

            很喜歡您的風(fēng)格...

            呵呵,

            是不是好多好多程序員都很像啊,

            還是好多本來就很像的人都選擇了程序員這個(gè)

            獨(dú)特的
            浪漫的
            理性的
            (傾向于)創(chuàng)意的
            ...

            職業(yè)啊

            # re: GUI框架:談?wù)効蚣?,寫寫代碼  回復(fù)  更多評論   

            2009-11-16 15:26 by 陳梓瀚(vczh)
            @OwnWaterloo
            效率不是一個(gè)可以忽略可用性的理由。我們需要的是一個(gè)在效率和外觀上都達(dá)到一定高度的想法,而不是一個(gè)因?yàn)樾识斐陕闊┑南敕?。即使是C++,我們也要把它變得更方便。而且剛才的那些操作符重載其實(shí)跟你那個(gè)union沒有沖突,這無論有興趣與否,都是一個(gè)要解決的問題。如果別人解決了一個(gè)帶多余指針的好用的屬性,你解決了一個(gè)不帶多余指針的不好用的屬性,那都是沒意義的。

            # re: GUI框架:談?wù)効蚣?,寫寫代碼  回復(fù)  更多評論   

            2009-11-16 15:28 by 陳梓瀚(vczh)
            @OwnWaterloo
            不過用了union,而跟你剛才那樣一個(gè)屬性就要定義一個(gè)類,而不是一種類型的屬性定義一個(gè)類(或者干脆就只有少數(shù)幾個(gè)屬性的類然后到處使用——這個(gè)要求比較高了一點(diǎn)),我覺得比多一個(gè)指針更加不能接受。運(yùn)行時(shí)代價(jià)跟開發(fā)代價(jià)都是需要一并考慮的。

            # re: GUI框架:談?wù)効蚣埽瑢憣懘a  回復(fù)  更多評論   

            2009-11-16 15:29 by 陳梓瀚(vczh)
            @OwnWaterloo
            在關(guān)心程序的質(zhì)量的同時(shí),也要同時(shí)關(guān)心一下如何將廣大程序猿在C++的水深火熱之中解放出來。

            # re: GUI框架:談?wù)効蚣埽瑢憣懘a  回復(fù)  更多評論   

            2009-11-16 15:59 by OwnWaterloo
            @陳梓瀚(vczh)
            "如果別人解決了一個(gè)帶多余指針的好用的屬性,你解決了一個(gè)不帶多余指針的不好用的屬性,那都是沒意義的。"

            我那個(gè)是沒有意義的?
            我那個(gè)不可以繼續(xù)實(shí)現(xiàn)你所說的那些花哨的用法嗎?(為什么說花哨,我下面會(huì)說明)

            我已經(jīng)說過很多次了,"通過proxy得到被proxy的對象"是實(shí)現(xiàn)proxy的前提。
            必須先滿足這個(gè)前提才能繼續(xù)實(shí)現(xiàn)proxy的其他技巧。

            用union會(huì)影響實(shí)現(xiàn)其他技巧嗎? 依然可以。
            難道我要把所有我會(huì)的技巧都展現(xiàn)一下嗎?
            我不喜歡這樣[color=red]顯擺[/color]。
            或者說,我也喜歡顯擺;但[b]我不喜歡顯擺[color=red]眾人皆會(huì)[/color]的技巧[/b]。
            分享一些別人明顯了解的技術(shù),只會(huì)[b][color=red]漲[/color]別人眼睛[/b]。


            我只想展示如何高效實(shí)現(xiàn)proxy所需前提這一技巧,如是而已。



            btw:為什么我說你那些是花哨的技巧。

            首先引入一個(gè)概念:"總線式的設(shè)計(jì)"。

            假設(shè)你需要實(shí)現(xiàn)一個(gè)函數(shù)f,f需要一個(gè)字符串輸入(暫不考慮wchar_t)。
            如何設(shè)計(jì)這個(gè)輸入?yún)?shù)???

            1. f(const char* c_style_string);
            2. f(const char* first,ptrdiff_t length);

            兩者取其一,可能1用得更多一些。

            然后,其他所有的String,都向這個(gè)"總線" —— const char*靠攏。
            比如std::basic_string要提供c_str(), CString要提供operator const char*()。

            為什么要這么做?
            1. 避免API的暴增。 直連線是平方增長,而總線是線性增長。
            2. 有些時(shí)候,必須這樣
            對fopen(const char*)你可以繼續(xù)直連線:
            fopen(const std::string& );
            fopen(const CString& );

            對一個(gè)類:
            class C {
            open(const char* );
            };

            C不是你寫的,你不可能為C加入一個(gè)直連線。
            你只能繞彎:
            open( C& o, const std::string& );
            open( C& o, const CStirng& );


            回到你說的ref<T>。

            假設(shè)有一個(gè)函數(shù):
            f(int& );
            你可以為它直連線。


            假設(shè)有一個(gè)類,依然不是你寫的,它只接受int &
            class C
            {
            f(int & );
            }

            C2有一個(gè)屬性i。
            class C2
            {
            property int i;
            }

            你要怎么給你的用戶解釋:
            C c;
            C2 c2;
            c.f( c2.i ); 是行不通的,必須
            f( c, c2.i ); ???

            你能將所有api的匯合點(diǎn)都直連線嗎? 你做不到。


            本來模擬屬性就是一個(gè)很無聊的事情了。
            你告訴用戶,這就是一個(gè)C#中的 get; set; 就ok了,別想著要其他怎么用。
            C#中可以 &o.p嗎???



            "運(yùn)行時(shí)代價(jià)跟開發(fā)代價(jià)都是需要一并考慮的。"
            你看你是否自相矛盾了。
            將線性開發(fā)代價(jià)的總線形式,轉(zhuǎn)化為平方開發(fā)代價(jià)的直連線形式。

            # re: GUI框架:談?wù)効蚣?,寫寫代碼  回復(fù)  更多評論   

            2009-11-16 16:07 by OwnWaterloo
            @陳梓瀚(vczh)
            "不過用了union,而跟你剛才那樣一個(gè)屬性就要定義一個(gè)類,而不是一種類型的屬性定義一個(gè)類(或者干脆就只有少數(shù)幾個(gè)屬性的類然后到處使用——這個(gè)要求比較高了一點(diǎn)),我覺得比多一個(gè)指針更加不能接受。運(yùn)行時(shí)代價(jià)跟開發(fā)代價(jià)都是需要一并考慮的。"

            先說運(yùn)行代價(jià)和開發(fā)代價(jià)。
            C程序員可能會(huì)因?yàn)樾?,不使用qsort。 他們都是傻子嗎?

            "實(shí)現(xiàn)屬性占用的額外空間大小與屬性的數(shù)量無關(guān)",這難道真的不值得你為其多花點(diǎn)功夫嗎?
            這是O(n)到O(1)的優(yōu)化!是不值得的?!


            而且我比較懷疑你所說的"一種類型的屬性定義一個(gè)類"是一種[color=red]侵入式[/color]的實(shí)現(xiàn)方式。


            需要實(shí)現(xiàn)屬性的類本來就不應(yīng)該很多。
            我也想如何做得更范化一些, 但目前成效不大。

            就像cexer說的"高效的,不會(huì)制造麻煩的東西,也是從不高效的,會(huì)制造麻煩的東西進(jìn)化來的。"
            如果一開始就采用指針保存,那是一輩子就沒辦法優(yōu)化了。

            # re: GUI框架:談?wù)効蚣?,寫寫代碼  回復(fù)  更多評論   

            2009-11-16 16:08 by 陳梓瀚(vczh)
            @OwnWaterloo
            舉個(gè)簡單的例子,總線形式的開發(fā)會(huì)讓string有一個(gè)c_str(),而不是讓string消失。我不知道你知不知道解決方案是什么樣子的,但是舉個(gè)例子出來總是要告訴別人說,“哦,這個(gè)東西可以這么這么做,只是我懶的寫完整”而不是沒說完。

            模擬屬性一點(diǎn)都不無聊,因?yàn)橐粋€(gè)可以監(jiān)聽和撤銷屬性變化的系統(tǒng)非常容易擴(kuò)展,總比你一個(gè)類都來一個(gè)listener或者就像api一樣所有的消息的格式都很混亂的好。特別對于開發(fā)GUI的庫。當(dāng)然這就不僅僅是如何響應(yīng)operator=的問題了。允許阻止變化的listener總是有開銷的,所以你無法杜絕它。這也是我第一次舉例子的時(shí)候就說出來的一個(gè)可以認(rèn)為是“需求”的東西。

            # re: GUI框架:談?wù)効蚣埽瑢憣懘a  回復(fù)  更多評論   

            2009-11-16 16:09 by 陳梓瀚(vczh)
            @OwnWaterloo
            至于說“如果一開始就采用指針保存,那是一輩子就沒辦法優(yōu)化了”,這是不對的。這跟接口無關(guān),也就是你一個(gè)property的實(shí)現(xiàn)怎么變化,this指針放的地方換掉了,調(diào)用的代碼都是不用改的。

            # re: GUI框架:談?wù)効蚣埽瑢憣懘a  回復(fù)  更多評論   

            2009-11-16 16:14 by 陳梓瀚(vczh)
            @OwnWaterloo
            “需要實(shí)現(xiàn)屬性的類本來就不應(yīng)該很多?!币膊皇呛苷_,當(dāng)然這要看情況了。在我看來,每一個(gè)控件都要實(shí)現(xiàn)可監(jiān)聽的屬性,這些屬性用事件暴露出來。你愿意設(shè)計(jì)完屬性之后從頭設(shè)計(jì)一次事件的命名和參數(shù)什么的好,還是愿意把它們綁定到屬性里面去?這樣的話開發(fā)控件也不需要考慮提供的事件完整不完整的問題了。因?yàn)榭丶臓顟B(tài)都在屬性里面,屬性變更就是狀態(tài)變更,也就都有事件了。這樣做的話設(shè)計(jì)出來的控件就不會(huì)有winapi那個(gè)消息(我們都知道很難清洗化)的影子了,會(huì)有一個(gè)十分漂亮的設(shè)計(jì)出來。一般winapi封裝出來的東西都是在PC上跑的,你會(huì)在意一個(gè)textbox占了1k還是1.1k內(nèi)存嗎?在這里多一個(gè)指針少一個(gè)指針根本不是問題。

            特別是對于point,那就是個(gè)變量好了,point沒有行為,所以不需要屬性。

            # re: GUI框架:談?wù)効蚣?,寫寫代碼  回復(fù)  更多評論   

            2009-11-16 16:15 by OwnWaterloo
            @陳梓瀚(vczh) "在關(guān)心程序的質(zhì)量的同時(shí),也要同時(shí)關(guān)心一下如何將廣大程序猿在C++的水深火熱之中解放出來" 我展示的,只是"proxy得到object"的技術(shù)。 你可以繼續(xù)展示proxy得到object之后的"解救廣大C++程序員于水深火熱之中"的其他技術(shù)。 但說實(shí)話,我不覺得那些技術(shù)真的能救人于水火。
            真正救人于水火的,不是玩弄高級技巧,而是設(shè)計(jì)清晰的接口。
            還是上面的例子: C c; C2 c2; c.f( c2.i ); 不能這樣做,只會(huì)讓人感到困擾。 如果i不是屬性,而是成員: c.f( c2.i() ); 不行是很容易理解事情 —— 臨時(shí)對象不能綁定到非const引用上。

            # re: GUI框架:談?wù)効蚣?,寫寫代碼  回復(fù)  更多評論   

            2009-11-16 16:21 by 陳梓瀚(vczh)
            @OwnWaterloo
            所以這就是Ref<>的意義了嘛,【在你的系統(tǒng)或由其擴(kuò)展的系統(tǒng)里面】讓你的參數(shù)的引用都能接受屬性,同時(shí)也能接受舊的引用。

            # re: GUI框架:談?wù)効蚣埽瑢憣懘a  回復(fù)  更多評論   

            2009-11-16 16:27 by OwnWaterloo
            "舉個(gè)簡單的例子,總線形式的開發(fā)會(huì)讓string有一個(gè)c_str(),而不是讓string消失。我不知道你知不知道解決方案是什么樣子的,但是舉個(gè)例子出來總是要告訴別人說,“哦,這個(gè)東西可以這么這么做,只是我懶的寫完整”而不是沒說完。"

            我相信看的人能明白我代碼中的重點(diǎn)在那里。
            我不明白的是你為何看不明白,即使在我說了之后。

            我再說一下吧,如果要讓我說"這個(gè)property"還可以這樣玩、又可以那樣玩,我會(huì)覺得很掉價(jià)……
            一方面因?yàn)槲椰F(xiàn)在本來就不喜歡過分玩弄語言特性。
            另一方面,這些技術(shù)實(shí)在是說得太多……


            如果一定要把所有細(xì)節(jié)都展現(xiàn)出來,不知道評論的字?jǐn)?shù)會(huì)不會(huì)夠。
            而且,展現(xiàn)到什么程度才可以停止?

            # re: GUI框架:談?wù)効蚣?,寫寫代碼  回復(fù)  更多評論   

            2009-11-16 16:32 by OwnWaterloo
            "模擬屬性一點(diǎn)都不無聊,因?yàn)橐粋€(gè)可以監(jiān)聽和撤銷屬性變化的系統(tǒng)非常容易擴(kuò)展,總比你一個(gè)類都來一個(gè)listener或者就像api一樣所有的消息的格式都很混亂的好。特別對于開發(fā)GUI的庫。當(dāng)然這就不僅僅是如何響應(yīng)operator=的問題了。允許阻止變化的listener總是有開銷的,所以你無法杜絕它。這也是我第一次舉例子的時(shí)候就說出來的一個(gè)可以認(rèn)為是“需求”的東西。"

            模擬屬性就是很無聊的工作。
            你試著回答一個(gè)問題:”這里一定需要operator=嗎? 可以使用命名函數(shù)嗎?
            如果回答是“是” ,那這里就寧可采用命名函數(shù),而不是屬性。
            絕大部分回答都應(yīng)該是“是”。所以這根本不算一個(gè)需求。


            從我的直覺上來說,模擬屬性使用的價(jià)值并不大。沒有多少場合非屬性不可。
            如果你可以想到一個(gè)“不是”的例子,請列舉出來。
            也許我的直覺是錯(cuò)的。

            # re: GUI框架:談?wù)効蚣?,寫寫代碼  回復(fù)  更多評論   

            2009-11-16 16:32 by OwnWaterloo
            "模擬屬性一點(diǎn)都不無聊,因?yàn)橐粋€(gè)可以監(jiān)聽和撤銷屬性變化的系統(tǒng)非常容易擴(kuò)展,總比你一個(gè)類都來一個(gè)listener或者就像api一樣所有的消息的格式都很混亂的好。特別對于開發(fā)GUI的庫。當(dāng)然這就不僅僅是如何響應(yīng)operator=的問題了。允許阻止變化的listener總是有開銷的,所以你無法杜絕它。這也是我第一次舉例子的時(shí)候就說出來的一個(gè)可以認(rèn)為是“需求”的東西。"

            模擬屬性就是很無聊的工作。
            你試著回答一個(gè)問題:”這里一定需要operator=嗎? 可以使用命名函數(shù)嗎?
            如果回答是“是” ,那這里就寧可采用命名函數(shù),而不是屬性。
            絕大部分回答都應(yīng)該是“是”。所以這根本不算一個(gè)需求。


            從我的直覺上來說,模擬屬性使用的價(jià)值并不大。沒有多少場合非屬性不可
            如果你可以想到一個(gè)“不是”的例子,請列舉出來。
            也許我的直覺是錯(cuò)的。

            # re: GUI框架:談?wù)効蚣埽瑢憣懘a  回復(fù)  更多評論   

            2009-11-16 16:34 by 矩陣操作
            終于能看到一篇提起興趣的文章了。

            我也寫過一個(gè)類似GUI框架,很lite,相當(dāng)lite。主要也是因?yàn)橐嫘枰⌒鸵粋€(gè)窗口系統(tǒng)。
            風(fēng)格上也是模擬了NET的方式,實(shí)現(xiàn)了一個(gè)Application::run(&win);這樣的玩意。window的消息映射上沒有去追求用標(biāo)準(zhǔn)CPP,直接用了VC特有的__event,__hook這些東西。
            期待博主進(jìn)一步討論!

            # re: GUI框架:談?wù)効蚣埽瑢憣懘a  回復(fù)  更多評論   

            2009-11-16 16:41 by 陳梓瀚(vczh)
            @OwnWaterloo
            其實(shí)舉過了嘛,textBox.Text.OnChanging+=a_functor_that_want_to_check_the_input;你覺得在一個(gè)【上百個(gè)】屬性的Form(你不要告訴我一個(gè)窗口的狀態(tài)很少)里面,你打算怎么處理屬性和事件之間的關(guān)系,我一直都在強(qiáng)調(diào)這一點(diǎn),而不是語法上的問題,operator=其實(shí)也是個(gè)例子,我沒有強(qiáng)調(diào)說要不要有個(gè)名字之類的東西。

            另外,不要覺得什么掉價(jià)不掉價(jià)的,技術(shù)沒有貴賤之分,不要搞三六九等。話說我自己在搞語言設(shè)計(jì)和實(shí)現(xiàn)。從來跟這些property什么的關(guān)系不大,這估計(jì)是職業(yè)病。

            # re: GUI框架:談?wù)効蚣?,寫寫代碼  回復(fù)  更多評論   

            2009-11-16 16:42 by OwnWaterloo
            "當(dāng)然這要看情況了。在我看來,每一個(gè)控件都要實(shí)現(xiàn)可監(jiān)聽的屬性,這些屬性用事件暴露出來。你愿意設(shè)計(jì)完屬性之后從頭設(shè)計(jì)一次事件的命名和參數(shù)什么的好,還是愿意把它們綁定到屬性里面去?這樣的話開發(fā)控件也不需要考慮提供的事件完整不完整的問題了。因?yàn)榭丶臓顟B(tài)都在屬性里面,屬性變更就是狀態(tài)變更,也就都有事件了。這樣做的話設(shè)計(jì)出來的控件就不會(huì)有winapi那個(gè)消息(我們都知道很難清洗化)的影子了,會(huì)有一個(gè)十分漂亮的設(shè)計(jì)出來。"

            消除winapi的影子,一定需要屬性嗎?命名函數(shù)不可以嗎?
            如果語言本身提供屬性,那使用兩者之一都沒什么問題。
            如果語言本身不提供,為何在可以使用命名函數(shù)的地方,非要去使用屬性???



            "一般winapi封裝出來的東西都是在PC上跑的,你會(huì)在意一個(gè)textbox占了1k還是1.1k內(nèi)存嗎?在這里多一個(gè)指針少一個(gè)指針根本不是問題。 "
            在gui上,你的說法是正確的。你甚至可以使用boost::any + boost::tuple來得到一個(gè)類似python的開發(fā)方式。

            我討論的是"proxy得到object", 請不要將討論窄化gui上。
            作為一個(gè)庫(非gui庫)的提供者,效率不是最主要的,但無端的損害(大量損害)就是不可接受的了。
            假設(shè)某天你真的需要將proxy應(yīng)用到critical的場景,你就會(huì)后悔當(dāng)初沒有去考慮這個(gè)問題了。


            "特別是對于point,那就是個(gè)變量好了,point沒有行為,所以不需要屬性。"
            需要的。我舉個(gè)例子:
            point
            {
                    property double x { get;set; }
                    property double y { get;set; }
                    property double radius { get; set; }
                    property double seta { get; set; }
            };

            客戶可以用直角坐標(biāo)或者極坐標(biāo)去操作這個(gè)點(diǎn)。
            點(diǎn)的內(nèi)部表示客戶無須關(guān)心。
            這樣的點(diǎn),就需要行為。

            # re: GUI框架:談?wù)効蚣?,寫寫代碼  回復(fù)  更多評論   

            2009-11-16 16:43 by 陳梓瀚(vczh)
            @陳梓瀚(vczh)
            至于其他的例子,你可以參考WPF強(qiáng)大的布局功能是怎么【under control】的,用傳統(tǒng)的方法根本沒法讓程序變得清晰。

            # re: GUI框架:談?wù)効蚣埽瑢憣懘a  回復(fù)  更多評論   

            2009-11-16 16:45 by 陳梓瀚(vczh)
            @OwnWaterloo
            【窄化】不是問題。GUI是一個(gè)龐大的東西,一旦需要了,那就有做的必要,就算除了GUI以外的所有系統(tǒng)都不能從這個(gè)property機(jī)制上獲得好處,也不是不搞的理由。而且讓一個(gè)例子舉現(xiàn)在一個(gè)范圍里面可以讓我們都少想很多東西。

            # re: GUI框架:談?wù)効蚣埽瑢憣懘a  回復(fù)  更多評論   

            2009-11-16 16:46 by 陳梓瀚(vczh)
            @OwnWaterloo
            行為指的是受改變的時(shí)候發(fā)生的事情,而不是你能從一個(gè)數(shù)據(jù)里面計(jì)算出什么東西。坐標(biāo)轉(zhuǎn)換是映射,點(diǎn)在任何情況下都不可能因?yàn)槟阍O(shè)置了一個(gè)什么坐標(biāo)而做了一些什么事情,因此沒有行為。當(dāng)然cache不同坐標(biāo)系下的結(jié)果例外,這不是重點(diǎn)。

            # re: GUI框架:談?wù)効蚣?,寫寫代碼  回復(fù)  更多評論   

            2009-11-16 16:49 by 陳梓瀚(vczh)
            @OwnWaterloo
            其實(shí)舉過了嘛,textBox.Text.OnChanging+=a_functor_that_want_to_check_the_input;你覺得在一個(gè)【上百個(gè)】屬性的Form(你不要告訴我一個(gè)窗口的狀態(tài)很少)里面,你打算怎么處理屬性和事件之間的關(guān)系,我一直都在強(qiáng)調(diào)這一點(diǎn),而不是語法上的問題,operator=其實(shí)也是個(gè)例子,我沒有強(qiáng)調(diào)說要不要有個(gè)名字之類的東西。

            另外,不要覺得什么掉價(jià)不掉價(jià)的,技術(shù)沒有貴賤之分,不要搞三六九等。話說我自己在搞語言設(shè)計(jì)和實(shí)現(xiàn)。從來跟這些property什么的關(guān)系不大,這估計(jì)是職業(yè)病。

            【剛才看你在我之后刷了,也不知道看到?jīng)]有,所以再復(fù)制一次。如果你看過了就忽略】

            # re: GUI框架:談?wù)効蚣?,寫寫代碼  回復(fù)  更多評論   

            2009-11-16 16:56 by OwnWaterloo
            【其實(shí)舉過了嘛,textBox.Text.OnChanging+=a_functor_that_want_to_check_the_input;你覺得在一個(gè)【上百個(gè)】屬性的Form(你不要告訴我一個(gè)窗口的狀態(tài)很少)里面,你打算怎么處理屬性和事件之間的關(guān)系,我一直都在強(qiáng)調(diào)這一點(diǎn),而不是語法上的問題,operator=其實(shí)也是個(gè)例子,我沒有強(qiáng)調(diào)說要不要有個(gè)名字之類的東西。 】

            textBox.Text.OnChanging+=a_functor_that_want_to_check_the_input;
            你覺得改成這樣,可接受不?
            textBox.Text.OnChanging.add( a_functor_that_want_to_check_the_input );
            textBox.Text.OnChangingSet( a_functor_that_want_to_check_the_input );
            textBox.Text.OnChangingAdd( a_functor_that_want_to_check_the_input );

            C++就是C++,沒有必要模擬.net那一套。


            【另外,不要覺得什么掉價(jià)不掉價(jià)的,技術(shù)沒有貴賤之分,不要搞三六九等。話說我自己在搞語言設(shè)計(jì)和實(shí)現(xiàn)。從來跟這些property什么的關(guān)系不大,這估計(jì)是職業(yè)病?!?/div>
            因?yàn)槲矣X得通過union來達(dá)到property的額外空間與property數(shù)量無關(guān)這個(gè)技巧可能是新奇的。我沒有在其他地方看到過,也是我自己想出來的。

            而proxy的那些技巧,我已經(jīng)看太多,說不定大家都知道,我又何必多次一舉?
            而且很多都不是我獨(dú)立想出來的。
            所以我覺得說出來很掉價(jià)……

            # re: GUI框架:談?wù)効蚣?,寫寫代碼  回復(fù)  更多評論   

            2009-11-16 17:01 by OwnWaterloo
            【至于其他的例子,你可以參考WPF強(qiáng)大的布局功能是怎么【under control】的,用傳統(tǒng)的方法根本沒法讓程序變得清晰?!?div>
            我不懂wpf。
            不通過屬性,而是命名方法,可以做到讓程序清晰么?

            如果只是從:
            textBox.Text.OnChanging+=a_functor_that_want_to_check_the_input;

            變化到:
            textBox.Text.OnChanging.add( a_functor_that_want_to_check_the_input );
            textBox.Text.OnChangingSet( a_functor_that_want_to_check_the_input );
            textBox.Text.OnChangingAdd( a_functor_that_want_to_check_the_input );

            我覺得依然很清晰。
            也許前者對C#轉(zhuǎn)到C++的程序員來說,前者會(huì)很親切。
            但后者才是大部分C++程序員的每天都在使用形式。

            你不需要給你的用戶培訓(xùn)“C++中如何模擬property”這種細(xì)節(jié),他就可以使用你的api。

            # re: GUI框架:談?wù)効蚣埽瑢憣懘a  回復(fù)  更多評論   

            2009-11-16 17:01 by cexer
            @OwnWaterloo
            @陳梓瀚(vczh)
            討論了那么多,突然發(fā)現(xiàn)怎么兩位同志的觀點(diǎn)都扭轉(zhuǎn)了?
            “C++中實(shí)現(xiàn)屬性”這個(gè)問題也是討論過千百遍的了,而且注定是誰都無法說服誰的問題,因?yàn)樗患兇馐且粋€(gè)技術(shù)問題。很大程序上和性格喜歡相關(guān)。就好像實(shí)用主義點(diǎn)的程序員,肯定覺得這樣費(fèi)盡力氣去模仿不值得,但藝術(shù)氣質(zhì)一點(diǎn)的程序員,覺得屬性的語法看起來很漂亮。然后各自用自己的偏好去說服對方,肯定不能成功。
            我個(gè)人的觀點(diǎn)是,有總比沒有好。存在即合理,要不然怎么會(huì)那么多的語言提供語言級的屬性支持了。C++當(dāng)中比這個(gè)急迫要解決的問題還很多,但可以預(yù)見,屬性這東西在未來的某個(gè)C++標(biāo)準(zhǔn)當(dāng)中一定會(huì)出現(xiàn)的,它確實(shí)有著不可磨滅的價(jià)值。

            但你們的討論里各自的觀點(diǎn)都很有道理,里面包含的有技術(shù)含量的思考很多,我看過也很有收獲。所以請你們在友好和諧的氣氛當(dāng)中繼續(xù)嘗試說服對方吧,無論是什么樣的討論都是思想的碰撞,也是學(xué)習(xí)的很好的方式。

            # re: GUI框架:談?wù)効蚣埽瑢憣懘a  回復(fù)  更多評論   

            2009-11-16 17:03 by cexer
            @矩陣操作
            謝謝,很高興又認(rèn)識志同道合的朋友。以后請多指教!

            # re: GUI框架:談?wù)効蚣?,寫寫代碼  回復(fù)  更多評論   

            2009-11-16 17:06 by OwnWaterloo
            【【窄化】不是問題。GUI是一個(gè)龐大的東西,一旦需要了,那就有做的必要,就算除了GUI以外的所有系統(tǒng)都不能從這個(gè)property機(jī)制上獲得好處,也不是不搞的理由。而且讓一個(gè)例子舉現(xiàn)在一個(gè)范圍里面可以讓我們都少想很多東西。】

            就算這種O(n)到O(1)的優(yōu)化因?yàn)樗珡?fù)雜而永遠(yuǎn)用不上,【也不是不搞的理由】。
            我這么說對嗎?

            討論如何優(yōu)化就是討論如何優(yōu)化。
            沒有必要將這種討論限制到某個(gè)范圍內(nèi),然后說在這個(gè)范圍內(nèi)不需要這種優(yōu)化。

            你不能預(yù)見所有場合都不需要這種優(yōu)化。

            # re: GUI框架:談?wù)効蚣?,寫寫代碼  回復(fù)  更多評論   

            2009-11-16 17:07 by cexer
            @俠客西風(fēng)
            呵呵謝謝你能喜歡,其實(shí)我對技術(shù)的探求不是很深,只是迷戀于一些更簡單更表面的東西。做技術(shù)的人其實(shí)這樣不是很好。
            你就不要準(zhǔn)備了,直接把我加進(jìn)去吧。但我不知道怎么加友情鏈接,在后臺加過好像沒成功。
            很高興認(rèn)識你!

            # re: GUI框架:談?wù)効蚣?,寫寫代碼  回復(fù)  更多評論   

            2009-11-16 17:10 by 陳梓瀚(vczh)
            @OwnWaterloo
            我并沒有反對你優(yōu)化,我只是說“你覺得在一個(gè)【上百個(gè)】屬性的Form里面,你打算怎么處理屬性和事件之間的關(guān)系”。正好本篇是談GUI,因此舉這么個(gè)例子我覺得挺合適的。那我就這么問吧,我以為你是知道我在說什么的:在一個(gè)包含請求回調(diào)doing和響應(yīng)回調(diào)one(+=也好,.Add也好)的屬性,怎么避免開銷?

            # re: GUI框架:談?wù)効蚣埽瑢憣懘a  回復(fù)  更多評論   

            2009-11-16 17:11 by OwnWaterloo
            【行為指的是受改變的時(shí)候發(fā)生的事情,而不是你能從一個(gè)數(shù)據(jù)里面計(jì)算出什么東西。坐標(biāo)轉(zhuǎn)換是映射,點(diǎn)在任何情況下都不可能因?yàn)槟阍O(shè)置了一個(gè)什么坐標(biāo)而做了一些什么事情,因此沒有行為。當(dāng)然cache不同坐標(biāo)系下的結(jié)果例外,這不是重點(diǎn)?!?br>

            可能我們對“行為”這個(gè)詞有不同的理解。 那我們不用這個(gè)詞了。

            對剛才說的點(diǎn)的例子。半徑絕對不能直接讀寫對吧?
            所以必須將實(shí)現(xiàn)隱藏起來(實(shí)現(xiàn)可能是以直角坐標(biāo)存儲), 通過setter/getter去操作,以維護(hù)點(diǎn)的不變式 —— 半徑不可能小于0。

            我說的就是這個(gè)意思。

            如果你需要可按極坐標(biāo)操作的點(diǎn),就不能直接暴露數(shù)據(jù),必須使用setter/getter或者property。

            # re: GUI框架:談?wù)効蚣埽瑢憣懘a  回復(fù)  更多評論   

            2009-11-16 17:13 by 陳梓瀚(vczh)
            @陳梓瀚(vczh)
            話說回來,其實(shí)我覺得你后面的重點(diǎn)已經(jīng)變成了operator=和operator+=與set_和.Add的關(guān)系了,這個(gè)我不想討論,因?yàn)槲乙灿X得無所謂,用哪個(gè)都好,只要

            1:先做的事情寫在前面,后做的事情寫在后面,要花時(shí)間看清楚。
            2:不要造成括號的嵌套,難以維護(hù)。
            舉個(gè)例子,挑選列表里面所有偶數(shù)的數(shù)字然后平方,如果你寫成square(even(a_list))就不滿足1和2。
            【注意,上面兩點(diǎn)你看過就算了,我不討論好跟不好,因?yàn)楦黝}沒關(guān)系】

            # re: GUI框架:談?wù)効蚣?,寫寫代碼  回復(fù)  更多評論   

            2009-11-16 17:15 by 陳梓瀚(vczh)
            @OwnWaterloo
            對于你的點(diǎn)的例子,我傾向于CalculateRadius(your_point)。你不能否認(rèn)人們總是下意識地認(rèn)為獲取屬性比調(diào)用函數(shù)的效率要高,所以那些真的做了事情,又不改變狀態(tài),又只讀的屬性變成函數(shù)比較好。不過我還是想跟你討論“在一個(gè)包含請求回調(diào)doing和響應(yīng)回調(diào)one(+=也好,.Add也好)的屬性,怎么避免開銷”而不是在這里要不要用只讀的屬性。

            # re: GUI框架:談?wù)効蚣?,寫寫代碼  回復(fù)  更多評論   

            2009-11-16 17:17 by 陳梓瀚(vczh)
            @cexer
            我跟OwnWaterloo就借著你的地盤版聊了哈

            # re: GUI框架:談?wù)効蚣埽瑢憣懘a  回復(fù)  更多評論   

            2009-11-16 17:19 by OwnWaterloo
            【我并沒有反對你優(yōu)化,我只是說“你覺得在一個(gè)【上百個(gè)】屬性的Form里面,你打算怎么處理屬性和事件之間的關(guān)系”。正好本篇是談GUI,因此舉這么個(gè)例子我覺得挺合適的。那我就這么問吧,我以為你是知道我在說什么的:在一個(gè)包含請求回調(diào)doing和響應(yīng)回調(diào)one(+=也好,.Add也好)的屬性,怎么避免開銷?】

            首先,上百個(gè)屬性是否可以改為上百個(gè)×2的getter和setter?
            setter不一定難用,setter還可以鏈?zhǔn)奖磉_(dá)呢。

            對比:
            1. w.p1(v1).p2(v2).p3(v3) ...
            2. ( (w.p1 = v1).p2 = v2 ).p3 = v3 ...
            3.
            w.p1 = v1;
            w.p2 = v2;
            w.p3 = v3;


            其次,如果真要模擬property。
            我真的沒看明白你說的那個(gè)需求是什么…… 囧


            # re: GUI框架:談?wù)効蚣?,寫寫代碼  回復(fù)  更多評論   

            2009-11-16 17:23 by OwnWaterloo
            【我跟OwnWaterloo就借著你的地盤版聊了哈】

            要不我們開一個(gè)論壇吧?google groups或者群?
            群的聊天資料可能不容易搜出來……

            toplangauge越來越水了……

            把cppblog上的人都招集起來?

            很高興和你們討論問題~_~
            如果我語氣有過激之處,請不吝指出,或者多多包涵一下。

            有人響應(yīng)沒……?

            # re: GUI框架:談?wù)効蚣埽瑢憣懘a  回復(fù)  更多評論   

            2009-11-16 17:26 by 陳梓瀚(vczh)
            @OwnWaterloo
            我覺得這里挺好的,其他地方都會(huì)水- -b就是cppblog還沒那么多亂七八糟的帖子。

            # re: GUI框架:談?wù)効蚣埽瑢憣懘a  回復(fù)  更多評論   

            2009-11-16 17:28 by 陳梓瀚(vczh)
            @OwnWaterloo
            屬性變化就是狀態(tài)變化嘛,你要監(jiān)聽一個(gè)狀態(tài)的變化,還要控制一個(gè)狀態(tài)變化的權(quán)限,用屬性+事件的組合是很常見的。也就是說,一個(gè)屬性都要自動(dòng)提供Changing和Changed兩個(gè)事件。嘗試把它做得高效,就是我之前的需求了。

            這才是屬性的本質(zhì)概念,跟語法什么的關(guān)系并不大。

            # re: GUI框架:談?wù)効蚣?,寫寫代碼  回復(fù)  更多評論   

            2009-11-16 17:35 by cexer
            @陳梓瀚(vczh)
            你說“我跟OwnWaterloo就借著你的地盤版聊了哈”

            絕沒問題,歡迎有自己思考的同志來此版聊,如果能切緊GUI框架的主題就感謝了哈,你們的討論能使我的博文增色不少。聊完不要走,此處管飯。

            # re: GUI框架:談?wù)効蚣?,寫寫代碼  回復(fù)  更多評論   

            2009-11-16 17:43 by 溪流
            哈,關(guān)注~

            # re: GUI框架:談?wù)効蚣?,寫寫代碼  回復(fù)  更多評論   

            2009-11-16 17:43 by OwnWaterloo
            1:先做的事情寫在前面,后做的事情寫在后面,要花時(shí)間看清楚。 
            2:不要造成括號的嵌套,難以維護(hù)。 
            舉個(gè)例子,挑選列表里面所有偶數(shù)的數(shù)字然后平方,如果你寫成square(even(a_list))就不滿足1和2。 
            【注意,上面兩點(diǎn)你看過就算了,我不討論好跟不好,因?yàn)楦黝}沒關(guān)系】

            不能算了……
            text.onchange_set( handler1 ).onchange_add( handler2 ).onchange_add( handler3 );
            滿足上面的2點(diǎn)嗎?


            對于你的點(diǎn)的例子,我傾向于CalculateRadius(your_point)。你不能否認(rèn)人們總是下意識地認(rèn)為獲取屬性比調(diào)用函數(shù)的效率要高,所以那些真的做了事情,又不改變狀態(tài),又只讀的屬性變成函數(shù)比較好。
            點(diǎn)的例子是因?yàn)槟阏fpoint不需要行為而舉出的反例。

            我也傾向于:
            point
            {
                    double x() const;
                    void x(double xx);
                    double r() const;
                    void r(double rr);
                    // ..
            };

            我也沒有假設(shè)get_r( p ); 和 p.r() 或者p.r到底何種效率更高。
            這取決與內(nèi)部表達(dá)是直角坐標(biāo)還是極坐標(biāo)。

            這些都不是重點(diǎn)。重點(diǎn)在于:
            1. r絕對不可以是裸域 —— 它需要維護(hù)不變式
            2. 如果你打算將x、y、r、a由函數(shù)變?yōu)閷傩裕?個(gè)指針的開銷你覺得是否合適?

            btw:這4個(gè)屬性都可以是讀寫的,并不存在x、y讀寫,r、a只讀一說。


            不過我還是想跟你討論“在一個(gè)包含請求回調(diào)doing和響應(yīng)回調(diào)one(+=也好,.Add也好)的屬性,怎么避免開銷”而不是在這里要不要用只讀的屬性。
            我沒看明白,能否再詳細(xì)一點(diǎn)?

            # re: GUI框架:談?wù)効蚣?,寫寫代碼  回復(fù)  更多評論   

            2009-11-16 17:46 by cexer
            @OwnWaterloo
            他少打一個(gè)字母!done

            # re: GUI框架:談?wù)効蚣埽瑢憣懘a  回復(fù)  更多評論   

            2009-11-16 17:57 by OwnWaterloo
            @cexer
            絕沒問題,歡迎有自己思考的同志來此版聊,如果能切緊GUI框架的主題就感謝了哈,你們的討論能使我的博文增色不少。聊完不要走,此處管飯。

            別管飯了……  正好有一個(gè)懸了很久的關(guān)于ATL/WTL的疑問,你管管吧~_~

            是這樣的,ATL/WTL中提到一種atl-style繼承。
            template<typename D>
            class B
            {
                    void f1() { static_cast<D*>(this)->f1_do(); }
                    void f2() { static_cast<D*>(this)->f2_do(); }
                    void f1_do() { printf("B\n"); }
                    void f2_do() { printf("B\n"); }
                    
            };

            class D : public B<D>
            {
                    void f1_do() { printf("D\n"); }
            };

            D d;
            d.f1();
            D::f1不存在 -> 調(diào)用B::f1 -> 轉(zhuǎn)型為D* -> D覆蓋了f1_do -> 調(diào)用D::f1_do ->輸出"D"
            d.f2();
            D::f2不存在 -> 調(diào)用B::f1 -> 轉(zhuǎn)型為D* -> D::f2_do不存在 -> 調(diào)用B::f1_do->輸出"B"

            是這樣嗎?


            然后,問題就來了 …… 這個(gè)樣子,除了將代碼寫得更復(fù)雜以外,相比下面的寫法,有什么區(qū)別么??

            class B
            {
                    void f1() { printf("B\n"); }
                    void f2() { printf("B\n"); }
            };

            class D : public B
            {
                    void f1() { printf("D\n"); }
            };

            D d;
            d.f1(); "D"
            d.f2(); "B"


            # re: GUI框架:談?wù)効蚣?,寫寫代碼  回復(fù)  更多評論   

            2009-11-16 18:02 by OwnWaterloo
            @cexer
            ATL-style繼承,提供的語意是:
            基類提供默認(rèn)實(shí)現(xiàn),派生類繼并覆蓋自己需要的函數(shù)。
            是這樣嗎?
            除了這個(gè),還有其他功能嗎?

            但這個(gè)功能,不正好就是非ATL-style繼承 —— 普通繼承提供的語意嗎……
            為什么要繞這么大一個(gè)圈子?
            能得到什么好處? header-only?

            # re: GUI框架:談?wù)効蚣?,寫寫代碼  回復(fù)  更多評論   

            2009-11-16 18:06 by OwnWaterloo
            @陳梓瀚(vczh)
            屬性變化就是狀態(tài)變化嘛,你要監(jiān)聽一個(gè)狀態(tài)的變化,還要控制一個(gè)狀態(tài)變化的權(quán)限,用屬性+事件的組合是很常見的。也就是說,一個(gè)屬性都要自動(dòng)提供Changing和Changed兩個(gè)事件。嘗試把它做得高效,就是我之前的需求了。 

            這才是屬性的本質(zhì)概念,跟語法什么的關(guān)系并不大。

            我覺得你又開始繼續(xù)扯遠(yuǎn)了……
            本來不是在討論“proxy的實(shí)現(xiàn)技術(shù)”“property和命名函數(shù)”么……

            anyway,換個(gè)話題也好。
            不過我真沒看明白你的需求是怎樣的……

            這個(gè)需求是否高效,與“是否使用union”有關(guān)系嗎?
            與“property vs 命名函數(shù)”有關(guān)系嗎?
            是一個(gè)新話題吧?


            能詳細(xì)說說你的需求么?

            # re: GUI框架:談?wù)効蚣?,寫寫代碼  回復(fù)  更多評論   

            2009-11-16 18:08 by cexer
            @OwnWaterloo
            我的理解是所謂的ATL-Style,其實(shí)是用模板在編譯期手工模擬的一種多態(tài),ATL 的實(shí)現(xiàn)當(dāng)中大量地使用了這種方式,目的就是為了輕量級,幾乎沒用過虛函數(shù)。
            我覺得這種手法的作用不僅僅限于此,因?yàn)榭梢越Y(jié)合其它的編譯期技術(shù),實(shí)現(xiàn)很多虛函數(shù)難以達(dá)到的功能,我實(shí)現(xiàn) GUI 框架的時(shí)候也用到很多這種東西,以后的說明中應(yīng)該會(huì)遇到。

            評論共2頁: 1 2 
            国产精品久久一区二区三区| 伊人伊成久久人综合网777| 国产精品久久久久…| 久久综合给合久久国产免费| 久久亚洲sm情趣捆绑调教| 久久影视综合亚洲| 久久久99精品成人片中文字幕| 久久久综合九色合综国产| 精品久久久久久久久午夜福利| 久久精品国产乱子伦| 久久青青草视频| 久久99国产精品久久99小说| 久久久精品久久久久久| 久久精品二区| 久久久精品波多野结衣| 人妻中文久久久久| 香蕉久久夜色精品国产2020| 亚洲精品tv久久久久| 一本久道久久综合狠狠躁AV | 久久亚洲国产精品五月天婷| 国产成人久久777777| 久久精品亚洲欧美日韩久久| 色欲综合久久躁天天躁| 中文精品久久久久人妻| 国产成人久久精品一区二区三区 | 久久精品国产色蜜蜜麻豆| 亚洲国产精品久久久天堂| 精品久久久久久久无码 | 久久精品国产99久久久香蕉| 人人狠狠综合88综合久久| 亚洲精品tv久久久久久久久| 久久精品国产亚洲av日韩| 久久综合综合久久97色| 狠狠色综合网站久久久久久久| 久久久无码精品亚洲日韩软件| 偷偷做久久久久网站| 国产精品久久精品| 久久久久亚洲国产| 国产精品久久成人影院| 午夜福利91久久福利| 久久精品99久久香蕉国产色戒|