• <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>
            隨筆-341  評論-2670  文章-0  trackbacks-0
             
                之前曾經(jīng)為Parser Combinator寫過一篇教程。這次為了處理Vczh Library++新設(shè)計的ManagedX托管語言,我為Parser Combinator新增了三個組合子。

                第一個是def,第二個是let。它們組合使用。def(pattern, defaultValue)的意思是,如果pattern成功了那么返回pattern的分析結(jié)構(gòu),否則返回defaultValue。let(pattern, value)的意思是,如果pattern成功了則返回value,否則失敗。因此他們可以一起使用。舉個例子,ManagedX跟C#一樣具有5種type accessor:public, protected, protected internal, private, internal。其中四種accessor的文法類型是token,剩下的protected internal則是tuple<token, token>。因此我們無法很方便地為它寫一個記號到語法樹的轉(zhuǎn)換函數(shù)。而且對于缺省情況要返回private的這種行為,在EBNF+handler上直接表達(dá)出來也比較困難。當(dāng)def和let還不存在的時候,我們需要這么寫:

                accessor = (PUBLIC[ToAccessor] | PROTECTED[ToAccessor] | PRIVATE[ToAccessor] | INTERNAL[ToAccessor] | (PROTECTED + INTERNAL)[ToProtectedInternal])[ToAccessorWithDefault];

                這個時候我們需要創(chuàng)建三個函數(shù),分別是ToAccessor、ToProtectedInternal和ToAccessorWithDefault。因為accessor本身不是一個重要的語法元素,所以我們不需要為accessor記錄一些源代碼的位置信息。表達(dá)式則需要位置信息,這可以在我們產(chǎn)生錯誤信息的時候知道錯誤發(fā)生在源代碼中的位置。而accessor總是直接屬于某一個重要的語法元素的,所以不需要保存。如果不需要保存位置信息的話,那么一個ToXXX的函數(shù)其實(shí)就是沒有必要的。這個時候可以讓def和let來簡化操作:

                accessor = def(let(PUBLIC, acc::Public) | let(PROTECTED, acc::Protected) | let(PRIVATE, acc::Private) | let(INTERNAL, acc::Internal) | let(PROTECTED+INTERNAL, acc::ProtectedInternal), acc::Private);

                看起來好像差不多,但實(shí)際上我們已經(jīng)減少了那三個不需要存在的函數(shù)。

            ============================無恥的分割線====================================

                第三個是binop。做這個主要是因為那個通用的lrec(左遞歸組合子)在對付帶大量括號的表達(dá)式的時候性能表現(xiàn)不好。這里稍微解釋一下原因。假設(shè)我們的語言有>、+、*和()四種操作符,那文法一般都寫成:

                exp0 = NUMBER | '(' exp3 ')'
                exp1 = exp1 '*' exp0 | exp0
                exp2 = exp2 '+' exp1 | exp1
                exp3 = exp3 '>' exp2 | exp2

                因此可以很容易的知道,當(dāng)我們分析1*2*3的時候,走的是下面的路子:
                exp3
                = exp2
                = exp1
                = exp1 '*' exp0
                = exp1 '*' exp1 '*' exp0
                = '1' '*' '2' '*' '3'

                現(xiàn)在我們做一個簡單的變換,把1*2*3變成((1*2)*3)。意義不變,但是分析的路徑卻完全改變了:
                exp3
                = exp2
                = exp1
                = exp0
                = '(' exp3 ')'
                = '(' exp2 ')'
                = '(' exp1 ')'
                = '(' exp1 '*' exp0 ')'
                = '(' exo0 '*' exp0 ')'
                = '(' '(' exp3 ')' '*' exp0 ')'
                = '(' '(' exp2 ')' '*' exp0 ')'
                = '(' '(' exp1 ')' '*' exp0 ')'
                = '(' '(' exp1 '*' exp0 ')' '*' exp0 ')'
                = '(' '(' exp0 '*' exp0 ')' '*' exp0 ')'
                = '(' '(' '1' '*' '2' ')' '*' '3' ')'

                咋一看好像沒什么區(qū)別,但是對于ManagedX這種有十幾個優(yōu)先級的操作符的語言來說,如果給一個復(fù)雜的表達(dá)式的每一個節(jié)點(diǎn)都加上括號,等于一下子增加了上千層文法的遞歸分析。由于Parser Combinator是遞歸向下分析器,因此路徑有這么長,那么遞歸的層次也會有這么長。而且為了避免boost::Spirit那個天殺的超慢編譯速度的問題,這里犧牲了一點(diǎn)點(diǎn)性能,將組合字的Parse函數(shù)做成了虛函數(shù),所以編譯速度提高了超多。一般來說一個需要編譯一個半小時的boost::Spirit語法分析器用我的庫只需要幾秒鐘就可以編譯完了。不過現(xiàn)在卻帶來了問題。括號一多,性能下降的比較明顯。但是我們顯然不能因噎廢食,因此我決定往Parser Combinator提供一個手寫的帶優(yōu)先級的左右結(jié)合一二元操作符語法分析器。為了將這個手寫的分析器插入框架并變得通用,我決定采用下面的結(jié)構(gòu)。下面的代碼是從ManagedX的語法分析器中截取出來的:
             1      expression    = binop(exp0)
             2             .pre(ADD_SUB, ToPreUnary).pre(NOT_BITNOT, ToPreUnary).pre(INC_DEC, ToPreUnary).precedence()
             3             .lbin(MUL_DIV_MOD, ToBinary).precedence()
             4             .lbin(ADD_SUB, ToBinary).precedence()
             5             .lbin(LT << LT, ToBinaryShift).lbin(GT >> GT, ToBinaryShift).precedence()
             6             .lbin(LT, ToBinary).lbin(LE, ToBinary).lbin(GT, ToBinary).lbin(GE, ToBinary).precedence()
             7             .post(AS + type, ToCasting).post(IS + type, ToIsType).precedence()
             8             .lbin(EE, ToBinary).lbin(NE, ToBinary).precedence()
             9             .lbin(BITAND, ToBinary).precedence()
            10             .lbin(XOR, ToBinary).precedence()
            11             .lbin(BITOR, ToBinary).precedence()
            12             .lbin(AND, ToBinary).precedence()
            13             .lbin(OR, ToBinary).precedence()
            14             .lbin(QQ, ToNullChoice).precedence()
            15             .lbin(QT + (expression << COLON(NeedColon)), ToChoice).precedence()
            16             .rbin(OPEQ, ToBinaryEq).rbin(EQ, ToAssignment).precedence()
            17             ;

                binop組合子的參數(shù)代表整個帶優(yōu)先級的最高優(yōu)先級表達(dá)式組合字(參考上面給出的>+*()文法,可以知道這里的exp0是什么意思)。binop給出了四個子組合子,分別是pre(前綴一元操作符)、post(后綴一元操作符)、lbin(左結(jié)合二元操作符)和rbin(右結(jié)合二元操作符)。precedence代表一個優(yōu)先級的所有操作符定義結(jié)束。這里我做了一個小限制,也就是每一個precedence只能包含pre、post、lbin和rbin的其中一種。實(shí)踐表明這種限制不會帶來任何問題。因此這里我們得到了一張操作符和優(yōu)先級的關(guān)系表。到了這里我們就可以在Parser Combinator的框架下寫一個手寫的語法分析器(下載源代碼并打開Library\Combinator\_Binop.h)來做了。至于如何手寫語法分析器,我之前給出了一篇文章,大家可以參考這個來閱讀_Binop.h。

                binop比起簡單的用lrec做同樣的事情,性能在debug下提高了100多倍,release下面則少一點(diǎn)。到了這里,Parser Combinator重新滿足了性能要求,我們可以放心大膽的用一點(diǎn)點(diǎn)無所謂的性能換取一千多倍的編譯時間了。在這里貼出當(dāng)binop還沒出現(xiàn)的時候我用lrec給出的操作符文法的實(shí)現(xiàn):
             1      exp1     = exp0
             2            | ((ADD_SUB | NOT_BITNOT | INC_DEC) + exp1)[ToUnary]
             3            ;
             4 
             5      exp2     = lrec(exp1 + *((MUL_DIV_MOD + exp1)[ToBinaryLrec]), ToLrecExpression);
             6      exp3     = lrec(exp2 + *((ADD_SUB + exp2)[ToBinaryLrec]), ToLrecExpression);
             7      exp4     = lrec(exp3 + *((((LT << LT) | (GT >> GT)) + exp3)[ToBinaryShiftLrec]), ToLrecExpression);
             8      exp5     = lrec(exp4 + *(((LT | LE | GT | GE) + exp4)[ToBinaryLrec] | (AS + type)[ToCastingLrec] | (IS + type)[ToIsTypeLrec]), ToLrecExpression);
             9      exp6     = lrec(exp5 + *(((EE | NE) + exp5)[ToBinaryLrec]), ToLrecExpression);
            10      exp7     = lrec(exp6 + *((BITAND + exp6)[ToBinaryLrec]), ToLrecExpression);
            11      exp8     = lrec(exp7 + *((XOR + exp7)[ToBinaryLrec]), ToLrecExpression);
            12      exp9     = lrec(exp8 + *((BITOR + exp8)[ToBinaryLrec]), ToLrecExpression);
            13      exp10     = lrec(exp9 + *((AND + exp9)[ToBinaryLrec]), ToLrecExpression);
            14      exp11     = lrec(exp10 + *((OR + exp10)[ToBinaryLrec]), ToLrecExpression);
            15      exp12     = lrec(exp11 + *((QQ + exp11)[ToNullChoiceLrec]), ToLrecExpression);
            16      exp13     = lrec(exp12 + *((QT + (exp12 + (COLON(NeedColon) >> exp12)))[ToChoiceLrec]), ToLrecExpression);
            17      expression    = (exp13 + OPEQ + expression)[ToBinaryEq]
            18            | (exp13 + EQ + expression)[ToAssignment]
            19            | exp13
            20            ;
            21 
            22 


            posted @ 2011-06-04 21:45 陳梓瀚(vczh) 閱讀(3613) | 評論 (10)編輯 收藏
                 摘要:     經(jīng)歷了大約一個多星期的挑選,Vczh Library++3.0托管語言的語法樹大約就定下來了。跟本地語言有一個缺省的語法叫NativeX一樣,這次的語法就叫ManagedX了。當(dāng)然ManagedX只是設(shè)計來直接反映托管語言語法樹的一門語言,如果愿意的話,這個語法樹還能被編譯成其他長得像(嗯,只能是像,因為必須是托管的,有類沒指針等)Basic啦Pascal那樣...  閱讀全文
            posted @ 2011-05-28 00:33 陳梓瀚(vczh) 閱讀(3234) | 評論 (17)編輯 收藏
                我至今依稀還記得畢業(yè)前做Vczh Library++3.0的偉大目標(biāo),就是實(shí)現(xiàn)把動態(tài)語言通過“動態(tài)語言”->“托管語言”->“本地語言”->“低級中間指令”->“X86代碼”最終編譯成機(jī)器碼,同時開放出所有中間過程。這樣的話添加一個就變成一個寫parser的簡單工作,添加一個類庫會惠及所有語言,添加一個運(yùn)行是目標(biāo)(譬如說可以輸出ARM等)可以讓在這上面的所有語言都能運(yùn)行在該目標(biāo)是。現(xiàn)在第一個階段完成了,就是把本地語言和低級中間指令給做了。

                其實(shí)現(xiàn)在叫本地語言不太合適,只是那個東西就是C+泛型+concept mapping的組合體,可以輕易被翻譯成X86,所以才這么叫的。暫時還是寫了個虛擬機(jī)直接執(zhí)行低級中間指令。在整個開發(fā)過程中,我還給這種語言寫了一個基本的函數(shù)庫:字符串、數(shù)學(xué)函數(shù)、內(nèi)存管理、垃圾收集(只是函數(shù)庫,而不是語法,目前只有簡單的暫時的實(shí)現(xiàn))、線程、同步原語和線程池。有了這些設(shè)施之后就可以開始做托管語言了。

                托管語言比較麻煩的地方在于類庫是必需的。譬如說字符串、數(shù)組和函數(shù)對象這些東西其實(shí)是無法靠語言本身做出來的,所以只能成為預(yù)定義的類庫。那這些類庫用什么寫呢?當(dāng)然是我們的本地語言(之前還寫了一個叫NativeX的parser)啦。現(xiàn)在的設(shè)想就有,先把預(yù)定義的類庫的聲明用托管語言本身寫出來,然后編譯器會提供一個功能將托管語言編譯成本地語言(目標(biāo)箭頭之一),然后把所有標(biāo)記成“外部函數(shù)”的函數(shù)跳過。每一個函數(shù)在生成本地語言的時候都會給出一個經(jīng)過計算的名字。然后只需要再用NativeX寫出這些同名的函數(shù)實(shí)現(xiàn)就好了。剩下的一些能夠用托管語言自己實(shí)現(xiàn)的函數(shù),就可以整個被編譯成本地語言。編譯出來的本地語言會依賴與之前寫出來的一個垃圾收集函數(shù)庫。這種函數(shù)庫在本地語言只有一個聲明,腳本引擎在運(yùn)行的時候可以給這些名字bind上一個實(shí)現(xiàn)。所以實(shí)際上垃圾收集函數(shù)庫的實(shí)現(xiàn)是可以替換的(只是必須在初始化的時候指定)。將來萬一我重寫的實(shí)現(xiàn)不夠好,人們還能自己搞一套出來換掉,達(dá)到他們不可告人的目的。

                至于托管語言本身有什么功能,肯定是抄自這個世界上最先進(jìn)的弱類型以面向?qū)ο笞鳛橹饕妒降耐泄苷Z言——C#啦,啊哈哈哈哈。Java的語言本身根本沒有被抄的價值。至于之后的動態(tài)語言,肯定是被編譯到托管語言的。只是這個過程不會跟DLR那么簡單直接把類型和表達(dá)式拿去映射。這里面可以做很多有趣的事情的,譬如盡量推導(dǎo)出動態(tài)語言里面每一個變量的類型約束(我們很多時候其實(shí)都知道動態(tài)語言里面的某個變量是有限若干個類型的集合的),然后為他們產(chǎn)生出更加有效的代碼。這里可能會將一個函數(shù)編譯成目的相同但是類型不同的幾份(注意這里不是在做泛型展開)。

                第二個階段就開始了。
            posted @ 2011-05-15 01:59 陳梓瀚(vczh) 閱讀(3607) | 評論 (21)編輯 收藏
                主要目的是那個覺得不寫代碼就要死的室友想干點(diǎn)什么事情,覺得TFS太大了,所以做了個SVN。因此我們裝了一個SVN的插件“Ankh SVN2”到Visual Studio 2010里面。然后嘗試添加了個solution。Team Foundation Client有Source Control Explorer,因此這個破svn也得有個東西吧,然后我就在View目錄下看到了Repository Explorer。一打開,有目錄,欣喜若狂。然后我就在那個solution的目錄下右鍵點(diǎn)delete,想看看效果。

                臥槽,沒有進(jìn)Pending Changes!

                臥槽,History不能Revert!!

                臥槽,client端文件夾還在,對他任何操作都失敗!!!

                臥槽,渣都不剩了啊!!!!

                幸好那只是一個臨時的solution。要是在Repository Explorer里面手一抖在trunk文件夾上面Delete了,后果不堪設(shè)想啊。然后我就獲得了一個教訓(xùn)。想看client端的文件夾的source control狀態(tài),去Working Copy Explorer,那里面的Delete是進(jìn)Pending Changes的。Reposiory Explorer刪除個文件夾,直接就在服務(wù)器端刪掉了,神馬都沒有了。這一輩子都不要打開Repository Explorer。然后我想起了以前看過的一篇文章《Unix Haters》里面說到unix的哲學(xué)就是,不警告,不報告,不禱告。像Delete這種東西,要是真他媽不進(jìn)Pending Changes,至少告訴我他不進(jìn)Pending Changes……

                瞬間想起來,各位讀者們,這篇文章僅跟客戶端插件有關(guān),這里不涉及任何svnadmin命令行內(nèi)容。謝謝合作。
            posted @ 2011-05-03 06:56 陳梓瀚(vczh) 閱讀(5455) | 評論 (14)編輯 收藏
                從某種意義上來說,做圖形也好,做GUI也好,做編譯器也好,大概都是一種情結(jié)。其實(shí)只要稍微想一想就知道,能把它們?nèi)哂袡C(jī)統(tǒng)一起來的,就只有游戲。我很久以前的確是為了想開發(fā)游戲才對編程產(chǎn)生興趣的,而學(xué)習(xí)游戲開發(fā)占據(jù)了我前六年的時間。雖然現(xiàn)在不做了,不過偶爾總是會覺得手癢。但是做游戲沒美工做不好怎么辦呢?就只好寫游戲代碼了。但是沒有資源寫出來的游戲又不好玩,于是就只好寫庫。那寫什么庫呢,自然就只有渲染器、界面引擎和腳本引擎了。我的博客的大部分文章也是圍繞著這三件事情建立起來,而且在中間不斷切換的。

                撒,所以今天就輪到GUI了。我一直很想做出一個自繪的GUI出來,無奈一直設(shè)計不出一個好的架構(gòu)。后來嘗試用原生API,但是卻又很不喜歡MFC的設(shè)計,就嘗試自己照著.NET Framework和Delphi那套VCL的樣子封裝了一個控件庫出來。無奈原生API細(xì)節(jié)無敵多,后來沒做把所有的功能都全部做完。因此后來一段時間凡是需要界面我都直接用C#做。然后CEGUI出了,WPF和Silverlight也出了,我發(fā)現(xiàn)如今要做一個漂亮的GUI非自繪已經(jīng)做不到了,所以我又做了一次嘗試。

                去年在美帝的時候曾經(jīng)試圖再設(shè)計一次,得到了一些結(jié)果。后來我發(fā)現(xiàn)其實(shí)根本沒辦法為GDI、DirectX、OpenGL和其它繪圖設(shè)備抽象一個公用的接口,否則就會遭遇大量性能問題。因為在很多細(xì)節(jié)上,譬如說渲染文字,為了達(dá)到較高的性能,OpenGL和DirectX需要使用幾乎相反的策略來做。因此這次我又換了一個方法,而且在Vczh Library++ 3.0的Candidate目錄下已經(jīng)checkin了一個試驗品。

                我把一個自繪的GUI分成了下面若干個層次。
                1、NativeWindow。NativeWindow表示的是一個頂層窗口的實(shí)現(xiàn)。譬如說我們想用Windows的窗口作為自繪窗口的頂層窗口(游戲里面的很多頂層窗口是繪制在游戲窗口里面的,所以頂層窗口并不一定是Windows的窗口)。
                2、控件庫。控件庫包含了這個自繪GUI庫的所有預(yù)定義控件。控件本身包含對用戶輸入的相應(yīng)邏輯,但是每一個控件的繪制以及鼠標(biāo)點(diǎn)中測試不在此范圍內(nèi)。
                3、控件皮膚接口。每一個最終控件都會擁有一個控件皮膚接口。每當(dāng)控件的狀態(tài)發(fā)生了變化,控件會調(diào)用皮膚接口更新控件的當(dāng)前狀態(tài)。每當(dāng)控件需要知道某一個點(diǎn)是否位于一個控件里面的時候,他也會去調(diào)用該控件的皮膚獲得結(jié)果。因此控件皮膚接口包含了一切關(guān)于繪制(因此理所當(dāng)然也就包含了點(diǎn)中測試)的邏輯。

                為了達(dá)到最高的性能,一套皮膚的實(shí)現(xiàn)只能綁定在某種繪圖設(shè)備上,也就是說缺省狀態(tài)下一套為GDI設(shè)備設(shè)計出來的皮膚是不能直接使用在DirectX設(shè)備上面的。當(dāng)然我這個框架的設(shè)計也是足夠開放的,如果你非得用同一套代碼來實(shí)現(xiàn)不同繪圖設(shè)備上的皮膚,那么你是可以自己動手豐衣足食,做到給GDI和DirectX設(shè)計一個公共接口并插入我的GUI框架的(只不過這種做法一般情況下都會慘死)。

                那么如何添加繪圖設(shè)備呢?目前NativeWindow有一個基于Windows窗口的實(shí)現(xiàn),并且NativeWindow的接口要求該實(shí)現(xiàn)在創(chuàng)建、銷毀、接收到很多窗口事件的時候都調(diào)用某一個回調(diào)對象。我們可以通過注冊一個全局回調(diào)對象或者具體窗口的回調(diào)對象來獲得NativeWindow狀態(tài)的變更。基于Windows窗口的NativeWindow實(shí)現(xiàn)還提供了一個額外函數(shù),可以讓你獲得一個NativeWindow的HWND(但這個函數(shù)并不被控件庫依賴)。現(xiàn)在我還實(shí)現(xiàn)了一個基于HWND+HDC的繪圖設(shè)備,主要方法就是先注冊全局回調(diào)對象,每當(dāng)知道一個NativeWindow被創(chuàng)建了,我就會注冊一個NativeWindow的回調(diào)對象,用來維護(hù)一個窗口里面的一塊32位DIBSections位圖緩沖區(qū)。窗口的大小如果變化了,我也會在適當(dāng)?shù)臅r候重新創(chuàng)建一塊合適的緩沖區(qū)。不過為了避免每一次大小變化都會創(chuàng)建新的緩沖區(qū),我創(chuàng)建的緩沖區(qū)的大小總會比窗口大一點(diǎn)。然后這個GDI繪圖設(shè)備就暴露了一個函數(shù),可以獲得一個NativeWindow的HDC和WinGDIElementEnvironment。

                WinGDIElementEnvironment是基于HWND+HDC的這一套實(shí)現(xiàn)上專有的、為了GDI皮膚設(shè)計出來的一個公共的資源庫(譬如用來保存各種面向業(yè)務(wù)邏輯的pen啊brush什么的,比如說disable的時候什么顏色,選中的時候什么顏色等等)。如果你想設(shè)計一個基于HWND+DirectX的皮膚,那么類似WinGDIElementEnvironment的這套東西要重新做一次——因為為了達(dá)到相同的性能。具體細(xì)節(jié)相差太大。當(dāng)然HWND+HDC上面可以有多套皮膚,WinGDIElementEnvironment是公用的。WinGDIElementEnvironment要求繪制是通過一個具體的WinGDIElement對象達(dá)到的,而一套皮膚可以有自己的一套WinGDIElement的實(shí)現(xiàn)。WinGDIElement被設(shè)計成面向業(yè)務(wù)的、一套皮膚的基本元素組成部分,譬如說按鈕邊框啦、焦點(diǎn)長方形啦、文字啦,而不是帶有pen和brush的長方形啊,文字啊,各種亂七八糟的最低等級的繪圖元素。舉一個例子,按鈕邊框跟菜單邊框很像,都可以用Rectangle來組成。但是Element里面就直接是按鈕邊框和菜單邊框,而不是一個可以讓你自由修改顏色的Rectangle。因為不同的控件要共享配色方案,而配色方案是由業(yè)務(wù)邏輯+空間狀態(tài)的集合實(shí)現(xiàn)的,因此WinGDIElement還是一個比較高層次的概念。當(dāng)一個WinGDIElement被渲染的時候,他會給你一個HDC,然后你根據(jù)被設(shè)置的狀態(tài)來調(diào)用GDI函數(shù)繪制到HDC指向的32位DIBSections位圖緩沖區(qū)上面。

                那么,當(dāng)我們使用HWND+HDC的實(shí)現(xiàn),創(chuàng)建了一個布滿了控件的窗口,那實(shí)際上是發(fā)生了什么事情呢?首先控件自己會組成一棵樹。其次,控件的皮膚也會組成一棵樹。現(xiàn)在就有控件樹跟皮膚樹兩顆樹了。控件樹負(fù)責(zé)所有用戶輸入變更狀態(tài)的邏輯部分,而皮膚樹負(fù)責(zé)繪圖和點(diǎn)中測試。而一個HWND+HDC實(shí)現(xiàn)的皮膚樹,會在皮膚組合成樹的時候,在底下又組合出了一顆WinGDIElement樹。因此大局上就是:
                控件樹(負(fù)責(zé)相應(yīng)輸入變更狀態(tài))--> 皮膚樹(負(fù)責(zé)儲存控件狀態(tài)的可視部分并決定什么時候需要刷新)-->WinGDIElement樹(負(fù)責(zé)繪圖整個窗口)

                這個時候,如果我們僅僅需要簡單的重新繪制窗口的話,那么控件樹跟皮膚樹都不需要被訪問到,底層僅需要讓W(xué)inGDIElement樹重新繪制一遍即可。而WinGDIElement的粒度實(shí)際上也不小,因此不會每一個圖元都有一個WinGDIElement從而使得創(chuàng)建出了一大堆對象的。

                最后一個設(shè)計就是在什么時候才重繪窗口的問題。假設(shè)說我們現(xiàn)在收到了一個WM_KEYDOWN消息,最終傳播到了控件樹里面去,然后修改了10個控件上面的文字。每當(dāng)你修改文字的時候?qū)嶋H上都需要重繪,那如何將無數(shù)次不可控的重繪合并成一次呢?SendMessage(WM_PAINT)是立刻執(zhí)行的,所以Windows自帶的合并WM_PAINT的方法在這個時候是無效的。我所采取的解決方法就是:反正控件樹的所有消息來源都是從NativeWindow里面來的,那實(shí)際上控件樹發(fā)出一個重繪請求的時候,我就會把NativeWindow的HWND實(shí)現(xiàn)里面的一個bool變量設(shè)成true,然后當(dāng)NativeWindow每一個傳播到控件樹的消息結(jié)束傳播之后,才讀一次那個變量,如果是true,那么就調(diào)用WinGDIElement進(jìn)行重繪并把變量設(shè)計成false。壞處是每一個傳播到控件樹的消息在處理完之后都必須檢查是否需要重繪,好處是這個東西被封裝在了NativeWindow的HWND實(shí)現(xiàn)里面里面,無論是控件樹、皮膚樹還是WinGDIElement樹也好,都在也不需要關(guān)心繪圖時機(jī)的事情了。

                因為GUI被分割成了很多層,而且每一層的都關(guān)心業(yè)務(wù)邏輯的不同部分,所以他們都是可以被替換的。譬如說我們可以做成:
                HWND+HDC實(shí)現(xiàn):最普通的方法
                HWND+DirectX:WPF和Silverlight地方法
                單一HWND+多個虛擬窗口+DirectX:可以在游戲里面用

                無論下面的繪圖設(shè)備和窗口實(shí)現(xiàn)如何發(fā)生變化,GUI控件的邏輯部分都跟這些實(shí)現(xiàn)嚴(yán)格分離,因此不會受到影響。而且大部分情況下,我們是不需要擁有一個跨繪圖設(shè)備的皮膚庫的,譬如說游戲和應(yīng)用程序,外表總不能做成一樣的。對于那些需要同時在DirectX和OpenGL上面運(yùn)行的程序(譬如說3dsmax),它已經(jīng)有DirectX和OpenGL的公共接口了,因此這些軟件可以利用它們的公共接口來實(shí)現(xiàn)GUI的繪圖設(shè)備部分,從而在上面構(gòu)造起來的皮膚自然是可以跨DirectX和OpenGL的。

                這比起一年前作的GUI實(shí)現(xiàn)又進(jìn)了一大步。上一次的GUI嘗試為不同的繪圖設(shè)備抽象一套公共接口,后來慘死。不知道這次實(shí)際上做出來的效果如何,拭目以待吧。
            posted @ 2011-04-29 19:50 陳梓瀚(vczh) 閱讀(5649) | 評論 (13)編輯 收藏
                為了避免留言再次被刪掉,我還是直接在這里說幾句話好了。

                在這里展示一下飯同學(xué)所珍愛的原創(chuàng)代碼“http://www.shnenglu.com/johndragon/archive/2011/04/27/145123.html”。

                匹配一個通配符的方法很多。譬如說我之前還寫過處理正則表達(dá)式的“http://www.shnenglu.com/vczh/archive/2008/05/22/50763.html”,或者說飯同學(xué)的那個帖子,或者說《beautiful code》里面那個遞歸的做法。飯同學(xué)在cppblog上還算是出鏡率比較高的,因此他以前在博客上干過些什么事情我都是看了的。我猜他大概就不知道那個《beautiful code》(結(jié)果他自己承認(rèn)了),因此靠著記憶貼了出來。我們都知道沒有編譯過的代碼出了點(diǎn)bug是正常的。后面還說了一句啥“寥寥幾行瞬間搞定”,其實(shí)也就是調(diào)侃一下。《beautiful code》這本書很出名,我不會認(rèn)為會有什么人會誤以為那個遞歸的算法是我自己原創(chuàng)的,當(dāng)然也就猜不出飯同學(xué)后面竟然會說我是為了證明自己聰明。

                不過事情的發(fā)展比較出乎我意料。因為留言都被刪掉了,所以我拿不出證據(jù),大家要質(zhì)疑也隨便你們。

                飯同學(xué)自己說努力研讀了“我的”代碼,然后指出這個問題有bug。好,這都是正常的。那他雖然文章里面寫了bug出現(xiàn)在*的處理里面,但是實(shí)際上這是后來加上去的,在留言里面他從來沒說bug在哪里,取而代之的是什么我為了證明自己聰明得逞啦,對人態(tài)度不好啦,對待程序的態(tài)度不好啦,各種亂七八糟的東西。我就想說一句“臥槽”。

                在這里對z某同學(xué)再次感激。雖然言辭比較激動,但好歹不會隨便覺得人家在轉(zhuǎn)發(fā)別人的代碼是為了證明自己聰明(怎么可能呢)。

                后面還有,我簡單回應(yīng)了一下這代碼是我貼過來的,然后說了幾句飯同學(xué)不應(yīng)該反應(yīng)大,不要隨便猜測我是為了如何如何。然后飯同學(xué)回復(fù)了一句大概說的是我的留言沒有意思的事情。沒意思你就忽略嘛,你覺得整個事情就向著沒意思的方向發(fā)展你可以關(guān)閉回復(fù)嘛。你還回復(fù)我豈不是更沒意思。我最后一句留言說的是“還是說代碼吧,說我更沒意思”,然后所有留言就壽終正寢了。

                所以說做程序員還是不能太激動。有人貼代碼你看代碼就好了,何必要通過否定一個人的行為來否定他所寫的代碼(更何況這是別人寫的)呢? 還有,要是動不動就覺得別人貼代碼是在挑戰(zhàn)你的話,那只會浪費(fèi)時間在處理這些破事情而已。還是寫自己的代碼吧,這么做劃不來。

            ---------------------------------這里refer一下后來多出來的那個文章的部分--------------------------------------

            話說我從來沒有“堅持自己是在做學(xué)術(shù)研究”,那其實(shí)是飯同學(xué)在被刪掉的那部分留言中堅持自己做學(xué)術(shù)研究。而且也沒有“不少人匿名來支持”,我看到的就是z某同學(xué)一個人而已(難道后來人數(shù)暴增?)。態(tài)度問題的話那隨便你怎么看,我又不吝嗇傳播別人的知識,你愛看不看。

            關(guān)于遞歸的方法:
            VCZH提供了一個遞歸的解法,并且“寥寥數(shù)行,瞬間搞定”。
            不過,遞歸會帶來堆棧的問題。
            而且他的方法里存在BUG,我就不貼上來了。
            據(jù)他稱那種方法來自一本 beautiful code的書。此書我沒看過,所以不清楚。
            從他的方法本身看,他只能提供是否匹配的一個結(jié)果,并且匹配模版和待匹配的字符串必須是0結(jié)尾,并且不返回結(jié)束匹配時的匹配進(jìn)度。
            并且在處理*的時候,有些許小BUG。

            雖然他一直在堅持自己是在做學(xué)術(shù)研究,也有不少人匿名來支持他,不過我覺得他還是有些態(tài)度問題。
            總是喜歡在別人的貼上表現(xiàn)自己。做的太過了就是顯擺了。
            從他回帖說的那些話,比如“寥寥數(shù)行,瞬間搞定”這些,以及并不完善的代碼看來,他根本就沒有看過我的代碼,只是憑字面意思就開始貼代碼。
            我實(shí)在不清楚他說這些話和貼代碼的原因是什么。這些我就不再討論了,我也刪除了他的回復(fù)。
            不過我想說,如果你一直以這種態(tài)度來回別人的帖子,那你會成為一個令人討厭的人。
            posted @ 2011-04-27 23:04 陳梓瀚(vczh) 閱讀(3438) | 評論 (20)編輯 收藏

                每次完成一個任務(wù)的時候,都要看看有沒有什么潛在的可以把功能是現(xiàn)成庫的地方。這十分有利于提高自己的水平。但至于你寫出來的庫會不會有人用,那是另一回事情了。

                這次為了完成一個多編程語言+多自然語言的文檔編寫工具,不得不做一個可以一次生成一大批文本文件的模板結(jié)構(gòu)出來。有了模板必然有元數(shù)據(jù),元數(shù)據(jù)必然是類似字符串的東西,所以順手就支持了xml和json。為了應(yīng)付巨大的xml和json,必然要做出xml和json的流式讀寫。大家應(yīng)該聽說過SAX吧,大概就是那樣的。

                有了xml和json之后,就可以在上面實(shí)現(xiàn)query了。把xml和json統(tǒng)一起來是一個比較麻煩的問題,因為本身從結(jié)構(gòu)上來看他們是不相容的。而且為了便于給vlscript.dll添加compiler service的支持,勢必要在dll上面做出字符串到語法樹或者語法樹到字符串這樣子的操作。本地dll接口顯然不可能做出一個順眼的異構(gòu)樹結(jié)構(gòu),因此勢必要把語法樹表示成xml或者json。因此就有了下面的結(jié)構(gòu):

                1、一顆簡潔但是功能強(qiáng)大的字符串,在上面可以存放xml、json和強(qiáng)類型數(shù)據(jù)。
                2、可以把xml和樹相互轉(zhuǎn)換。
                3、可以把json和樹相互轉(zhuǎn)換。
                4、當(dāng)樹存放的是強(qiáng)類型數(shù)據(jù)(基本類型都會用字符串存放)的時候,樹可以映射到一個帶有約束的xml或者json結(jié)構(gòu)上去。

                在這里可以展示一下什么是強(qiáng)類型數(shù)據(jù)。我們知道json表達(dá)的結(jié)構(gòu)是弱類型的。當(dāng)你拿到一個object的時候你不知道它的類型,你只知道他有多少個成員變量。強(qiáng)類型結(jié)構(gòu)要求object有一個類型標(biāo)記,數(shù)組也有一個類型標(biāo)記(因為我們不能從數(shù)組的元素類型推斷出數(shù)組本身的類型——因為類型可以擁有繼承關(guān)系),基本類型還要可擴(kuò)展(json只支持?jǐn)?shù)字、字符串、布爾值和null,顯然是不夠的)。下面貼一個強(qiáng)類型數(shù)據(jù)結(jié)構(gòu)的xml和json的表現(xiàn)形式:

             1 <Developer>
             2     <Languages>
             3         <array:String>
             4             <primitive:String value = "C++"/>
             5             <primitive:String value = "C#"/>
             6             <primitive:String value = "F#"/>
             7             <primitive:String value = "Haskell"/>
             8         </array:String>
             9     </Languages>
            10     <Name>
            11         <primitive:String value = "vczh"/>
            12     </Name>
            13     <Project>
            14         <Project>
            15             <Host>
            16                 <primitive:String value = "Codeplex"/>
            17             </Host>
            18             <Language>
            19                 <primitive:String value = "C++"/>
            20             </Language>
            21         </Project>
            22     </Project>
            23 </Developer>

             1 {
             2     "$object" : "Developer",
             3     "Languages" : {
             4         "$array" : "String",
             5         "value" : [{
             6             "$primitive" : "String",
             7             "value" : "C++"
             8         }, {
             9             "$primitive" : "String",
            10             "value" : "C#"
            11         }, {
            12             "$primitive" : "String",
            13             "value" : "F#"
            14         }, {
            15             "$primitive" : "String",
            16             "value" : "Haskell"
            17         }]
            18     },
            19     "Name" : {
            20         "$primitive" : "String",
            21         "value" : "vczh"
            22     },
            23     "Project" : {
            24         "$object" : "Project",
            25         "Host" : {
            26             "$primitive" : "String",
            27             "value" : "Codeplex"
            28         },
            29         "Language" : {
            30             "$primitive" : "String",
            31             "value" : "C++"
            32         }
            33     }
            34 }

                上面的xml和json都是對一個相同的強(qiáng)類型數(shù)據(jù)結(jié)構(gòu)的表示。我們可以看到$array、$object和$primitive是用來區(qū)分他們的實(shí)際類型的。

                下面是流式xml和json的讀寫的接口。可以很容易的看出用這種方法來讀寫xml和json必須將代碼做成一個超級復(fù)雜的狀態(tài)機(jī)才可以。這里太長貼不下,如果大家有興趣的話可以去Vczh Library++3.0下載最新代碼并打開
                Library\Entity\TreeXml.cpp
                Library\Entity\TreeJson.cpp
                Library\Entity\TreeQuery.cpp
             1 class XmlReader
             2 {
             3 public:
             4     enum ComponentType
             5     {
             6         ElementHeadOpening,        // name
             7         ElementHeadClosing,        //
             8         ElementClosing,            //
             9         Attribute,                // name, value
            10         Text,                    // value
            11         CData,                    // value
            12         Comment,                // value
            13 
            14         BeginOfFile,
            15         EndOfFile,
            16         WrongFormat,
            17     };
            18 public:
            19     XmlReader(stream::TextReader& _reader);
            20     ~XmlReader();
            21 
            22     ComponentType                       CurrentComponentType()const { return componentType; }
            23     const WString&                      CurrentName()const { return name; }
            24     const WString&                      CurrentValue()const { return value; }
            25     bool                                Next();
            26     bool                                IsAvailable()const { return componentType!=EndOfFile && componentType!=WrongFormat; }
            27 };
            28 
            29 class XmlWriter
            30 {
            31 public:
            32     XmlWriter(stream::TextWriter& _writer, bool _autoNewLine=trueconst WString& _space=L"    ");
            33     ~XmlWriter();
            34 
            35     bool                                OpenElement(const WString& name);
            36     bool                                CloseElement();
            37     bool                                WriteElement(const WString& name, const WString& value);
            38     bool                                WriteAttribute(const WString& name, const WString& value);
            39     bool                                WriteText(const WString& value);
            40     bool                                WriteCData(const WString& value);
            41     bool                                WriteComment(const WString& value);
            42 };
            43 
            44 class JsonReader
            45 {
            46 public:
            47     enum ComponentType
            48     {
            49         ObjectOpening,
            50         ObjectClosing,
            51         Field,
            52         ArrayOpening,
            53         ArrayClosing,
            54         Bool,
            55         Int,
            56         Double,
            57         String,
            58         Null,
            59 
            60         BeginOfFile,
            61         EndOfFile,
            62         WrongFormat,
            63     };
            64 public:
            65     JsonReader(stream::TextReader& _reader);
            66     ~JsonReader();
            67 
            68     ComponentType                       CurrentComponentType()const { return componentType; }
            69     const WString&                      CurrentValue()const { return value; }
            70     bool                                Next();
            71     bool                                IsAvailable()const { return componentType!=EndOfFile && componentType!=WrongFormat; }
            72 };
            73 
            74 class JsonWriter
            75 {
            76 public:
            77     JsonWriter(stream::TextWriter& _writer, bool _autoNewLine=trueconst WString& _space=L"    ");
            78     ~JsonWriter();
            79 
            80     bool                                OpenObject();
            81     bool                                CloseObject();
            82     bool                                AddField(const WString& name);
            83     bool                                OpenArray();
            84     bool                                CloseArray();
            85     bool                                WriteBool(bool value);
            86     bool                                WriteInt(vint value);
            87     bool                                WriteDouble(double value);
            88     bool                                WriteString(const WString& value);
            89     bool                                WriteNull();
            90 };

                有xml自然要有xpath,只不過xpath用來處理json和強(qiáng)類型數(shù)據(jù)結(jié)構(gòu)都有點(diǎn)力不從心,所以我打算修改xpath,做成適合查詢我這種跟它們稍微有點(diǎn)不同的樹的查詢語句,然后加入到Vczh Library++3.0里面去。有了這個之后就可以做很多事情了,譬如說在模板生成器里面使用query來查詢復(fù)雜的配置,譬如說在腳本語言里面支持xml和json,還有很多其他的等等。query寫完之后就可以開始寫一個可以一次生成一大批文本文件的模板生成器了。我會將模板生成本身寫成一個可以擴(kuò)展功能的庫,最后再寫一個DocWrite.exe利用這個庫實(shí)現(xiàn)一個文檔生成器。
            posted @ 2011-04-18 05:34 陳梓瀚(vczh) 閱讀(3298) | 評論 (3)編輯 收藏
                在微軟亞洲研究院上班已經(jīng)長達(dá)4天了。我找了我本科的同班同學(xué)一起住,他也在微軟,如果他不加班的話還可以一起吃飯上下班。頭一個星期四處見同學(xué),也結(jié)識了幾個新朋友,然后在家里寫寫代碼。

                Vczh Library++3.0最近的進(jìn)展比較緩慢。我要用一種類似模板+生成器的方法來讓我可以很方便地給多個編程語言撰寫中英雙語的文檔,然后分開編譯成多個文檔,當(dāng)然每個文檔只有一門程序語言以及一門自然語言。因此最近正在著手給VL++3.0添加xml、json以及模板文件的支持。這個模板的編譯過程跟xml+xslt比較類似,唯一的區(qū)別是我的編譯過程可以控制多個步驟產(chǎn)生一大堆文件,譬如說文檔本身,譬如說各個腳本實(shí)例及其makefile等等。

                室友也很喜歡沒事寫代碼,所以我在維護(hù)VL++3.0的同時還會跟他一起做一些有意思的小工程來玩一玩。最近在搞一個跟鋼琴譜有關(guān)的軟件及其一些小研究。恰好我跟他小時候都學(xué)過鋼琴(唯一的區(qū)別是他沒有斷,我斷了……),總之都是閑著沒事充實(shí)生活用的。

                工作之后沒有在學(xué)校的那么多時間,無法維持兩天至少能做一個東西來寫博客的速度了,降低到了一個月兩篇。不容易啊……
            posted @ 2011-04-11 05:32 陳梓瀚(vczh) 閱讀(6133) | 評論 (16)編輯 收藏
                項目主頁:http://vlpp.codeplex.com
                下載頁面:http://vlpp.codeplex.com/releases/view/62850
                該Release對應(yīng)的源代碼:http://vlpp.codeplex.com/SourceControl/changeset/view/70602

                第一個release沒有文檔,因此接下去我得做一個雙語的文檔生成工具了,然后會在下一個release里面添加完整的NativeX語言和腳本引擎API的文檔。因此現(xiàn)在要學(xué)習(xí)如何使用NativeX和腳本引擎的API的話,可以下載源代碼然后到下面的目錄尋找資料:
                1、UnitTest\Binary\TestFiles\Code:我的單元測試工程使用的NativeX腳本代碼
                2、UnitTest\Binary\ScriptCoreLibrary:NativeX預(yù)定義函數(shù)庫的源代碼
                3、Tools\Release\VleSource\VlScriptDotNet:vlscript.dll的.NET封裝
                4、Tools\Release\VleSource\VlTurtle:使用vlscriptdotnet.dll開發(fā)的“強(qiáng)大的烏龜”

                因此下載release之后,可以打開Turtle\VlTurtle.exe,然后玩之。一個簡單的方法是,打開內(nèi)置的sample然后點(diǎn)Run,如圖:


            posted @ 2011-03-19 21:07 陳梓瀚(vczh) 閱讀(11492) | 評論 (16)編輯 收藏
                這是一個小Demo,用來介紹如何使用C#來調(diào)用我C++給出的NativeX編譯器和虛擬機(jī)的。具體的代碼可以在Vczh Library++3.0里面找到。

                VlTurtle.exe的工作原理十分簡單。首先界面由Ribbon和Intellisense構(gòu)成(超難寫,難免會有些問題……),其次按Run的時候會將代碼保存到Script\NativeX\NativeX.txt里面,使用Vle.exe編譯Script\NativeX\Make.txt,生成assembly文件。如果編譯失敗,就會出現(xiàn)Error.txt,然后這個編輯器將這個文件讀回去顯示在界面上。編譯成功之后,使用參數(shù)“Execute”再啟動自己一次,新進(jìn)程會讀生成的assembly文件并使用vlscript.dll的虛擬機(jī)函數(shù)初始化,尋找main函數(shù)并執(zhí)行。

                第一個alpha版本的Release我并不打算把編譯器也做進(jìn)vlscript.dll(其實(shí)代碼都在,就是沒extern),而打算讓Vle.exe充當(dāng)編譯器的作用。目前這個破Demo還沒做完,New/Open/Save/Save As/Stop點(diǎn)了沒反應(yīng),而且Run是阻塞的——也就是執(zhí)行進(jìn)程沒退出,編輯器就會假死。先偷懶了,過幾天再改好他,順便給那只破烏龜加點(diǎn)功能美化一下……

                下面先貼圖。





            posted @ 2011-03-11 06:20 陳梓瀚(vczh) 閱讀(3560) | 評論 (3)編輯 收藏
            僅列出標(biāo)題
            共35頁: First 6 7 8 9 10 11 12 13 14 Last 
            久久99久久99精品免视看动漫| 久久人人爽人人爽人人片av麻烦| 91精品国产色综合久久| 久久青青草原国产精品免费 | 国产亚洲精品自在久久| 青青草原精品99久久精品66| 成人久久精品一区二区三区| 久久综合久久美利坚合众国| 久久国产精品无码HDAV| 中文字幕久久精品无码| 精品久久久久久久国产潘金莲| 91久久精品国产91性色也| 久久久久免费看成人影片| 无码久久精品国产亚洲Av影片 | 伊人久久大香线蕉综合Av| 日韩十八禁一区二区久久| 久久综合九色综合久99 | 久久久久久久尹人综合网亚洲 | 久久国产乱子伦精品免费午夜| 色综合久久中文综合网| 国产精品成人久久久久久久| 国产精品天天影视久久综合网| 996久久国产精品线观看| 99热热久久这里只有精品68| 久久国产成人精品国产成人亚洲| 久久久久久久久久久久久久| 中文字幕人妻色偷偷久久| 777久久精品一区二区三区无码| 久久精品无码一区二区app| 久久久久久国产精品免费无码| 国产精品99久久精品爆乳| 思思久久99热只有频精品66| 亚洲午夜久久久影院伊人| 久久久久久无码国产精品中文字幕 | 亚洲精品白浆高清久久久久久| 老司机国内精品久久久久| 久久久久久伊人高潮影院| 久久久99精品一区二区| 久久综合九色综合精品| 久久精品午夜一区二区福利| 久久伊人精品一区二区三区|