C++語言中的元類編程(八)

這篇的重點內容是講解算法,但是在正式開始之前,我強烈建議你再回顧一下上一篇的內容,因爲要理解這個算法,就需要意識到協議的具體細節不重要,重要的是這種協議是否可以用一種樹形結構來表達,而且在這個結構中,每一個節點(即field)要麼是一個必要節點,要麼是一個可選節點,且它最多隻依賴於一個之前的(如果我們以文件目錄樹來想像這棵樹的話,一個節點更靠前就意味着這個節點更靠近上方的位置)節點,即,不能出現一個節點依賴於之前的多個節點——除非這些節點可以合併爲一個複合節點(即子協議field)——或之後的某個節點,但是多個節點都依賴於之前的同一個節點的情況是允許的(通常,我們也可以把這些節點合併爲一個複合節點)。通俗的講,就是我們接下來要討論的這個算法,與這棵協議樹長得什麼樣子無關(對於同一個協議,不同的人可能將它描述成不同的樣子,這種情況是可能的,也是允許的),而只與每個節點的性質有關。正是這個特性,使得這個算法具有極強的通用性。這樣,我們只要保證我們描述出的協議樹與協議文檔是一致的(檢查其正確性也要比分析一堆的if else和位運算容易得多),那麼就可以期待我們能夠得到一個正確的結果。

聽起來,這個算法好像很複雜,其實不然,它意外的非常簡單,但需要你有較好的想像力。是不是有點迫不及待了呢?好,現在我們先從一個特例開始:所有節點都是必要的節點,沒有任何兩個節點有依賴關係(即所有節點都是獨立的)。對於這種情況,我們想像一下把這棵樹“拍平”,只留下葉子節點(但先後順序不變),那麼這個葉子節點組成的節點序列,不就是我們要的field序列了嗎(因爲每個複合節點在bit流中的起始位置,就是這個複合節點的第一個葉子節點在bit流中的起始位置,而大小就是所有葉子節點的大小的和,然後想象將bit流映射到這個field序列上)?而要得到一個“拍平”的樹,我們只需要執行一個深度優先的遍歷算法就可以,是不是很簡單呢?(事實上這個特例有更簡單的處理方法,就是按協議寫一個大大的嵌套式的struct,然後直接對buffer指針做一個強制類型轉換即可,這個例子提醒我們,程序設計是一門非常靈活的藝術,我們不能教條的去看待它,這也是我本人不欣賞《設計模式》一書的原因)

那麼當我們引入了可選節點後,是否情況就不一樣了呢?其實是一樣的,還是想像我們把一棵樹“拍平”,只不過現在,有些葉子節點我們需要刪除,刪除的原則就是,先一個一個的檢查這個葉子是否是必要節點,如果是,就保留這個節點,同時修正bit流的訪問偏移量(即指向下一個field的起始位置);而如果不是,就向(已生成的)序列的前方找到它的條件節點,然後看看它的條件節點的內容是不是允許這個可選節點出現(即計算出這個可選節點的個數),如果允許,就保留(並修正bit流的訪問偏移量),否則,就刪除。看到了嗎?差別只有一點點,是不是也很簡單?(現在你應該明白,爲什麼上一篇文章中,我們的協議描述接口要設計成那樣了吧?同時,正因爲大部分的協議都是這種情況,所以我們不能象上一種情況那樣,用一個靜態的struct去處理它們)

具體的代碼實現不妨自己來寫一寫,畢竟我這裏只是寫博客,不是寫論文。如果實在懶得寫,又想獲得源代碼,可以給我留言或發郵件。好,我們的討論就到這……“等等”,那裏有位朋友站起來說,“你只解釋了從bit流產生field序列的算法,可還沒討論它的逆向算法呢!”,其實……這裏我故意賣了一個關子,呵呵……正所謂“好戲還在後頭”,現在,我將向各位展示最神奇的部分:同樣的算法,也可以用於產生一個bit流,是的,你沒有聽錯!不過,首先你需要轉變觀念,不要用一個函數的眼光去看待這個算法(即一個bit流進去了,出來一個field序列),而要用流的觀念去看待這個算法(即一個bit流進去以後,每次出來一個field,通過不斷的獲取下一個field來產生一個序列),前者bit流只能是輸入,而後者bit流既可以是輸入,也可以是輸出。
當我們需要產生一個bit流時,因爲任何協議的第一個葉子節點理所當然的是一個必要節點,所以我們僅僅根據它的field描述就可以產生一個field,而我們需要做的就是讓這個field記住它在bit流中的偏移量(field的大小可以通過其元類的FieldSize函數求得,參見上一篇文章),然後我們就可以在得到了這個field之後,再往bit流的buffer中填入合適的數據,然後再來處理下一個節點。可以證明(具體證明過程略),其後的每一個節點都可以根據其field描述或已產生的field序列(其條件field必然存在於已產生的序列中)來判斷是否應產生一個該節點的field,然後再往bit流的buffer中填入合適的數據。
看到上面的結論,你是否會大吃一驚呢?這個算法由於引入了元類和流的編程思想,它的通用性居然會有如此大的飛躍,而算法卻又是如此的簡單,我們甚至不用寫代碼就可以把它解釋清楚(really?)!這是否是你曾經考慮到了的呢?寫到這裏,這次的話題就快要結束了(如果是一部戲劇,以這樣的一種戲劇性的方式結束是再好不過的了!),最後再做一點總結,希望通過上面的這個例子,能夠讓各位領悟到元類編程思想的巨大魅力。然而凡事都有兩面,不可一味追求技術的巧妙(或正相反,我見過的許多程序員都是懶得追求這種東西的,這也是爲什麼我們的程序質量參差不齊的原因之一!),而忽視了它對解決具體問題的適用性,從而讓我們的思想變得僵化。
最後,感謝大家關注此文,有任何錯誤或疏漏還請批評指正!祝各位工作順利,學習進步!


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章