CEGUI(Crazy Eddie’s GUI)是一個使用C++實現的基於LGPL協議的的開源GUI庫,主要應用於遊戲介面。連著名的開源遊戲引擎Orge3D也放棄了自己的介面引擎而使用第三方的開源介面引擎CEGUI。
對於開源的代碼工程,我首先會觀察它的代碼規範。比如,使用什麼命名法,是否使用異常等等,這很有用。看不同的開源工程,可以看到各種不同的代碼規範,其中有很多是值得借鑒的。
代碼規範本身並沒有絕對的對錯之分,但我們往往為了某一個代碼規範爭論的面紅耳赤。比如,是否使用匈牙利命名法,使用BOOL還是bool等等。這時,就必須綜合考慮團隊現有的使用習慣和既有代碼的風格,制定出一套適合自己的統一的規範。所以,規範除了對與錯,最關鍵的地方還是要統一。存在即合理,我們不要一味去排斥其他人的使用習慣和規範,而應該多讀讀別人的代碼,看看著名的開原始碼,瞭解別人的做法,然後對自己的做法進行一些反思。這才是面對代碼規範正確的心態。
CEGUI是一套不錯的介面庫,介面布局使用XML來描述,提供了專用的介面編輯器,讓介面製作變得更簡單。CEGUI作為一個成熟的開源產品,代碼已經經曆了無數的千錘百鍊,整體風格比較統一,是一個很好的學習範本。接下來,我粗淺的分析一下CEGUI一些值得學習的地方。
1. Camel命名法
CEGUI完全擯棄了匈牙利命名法,而使用更貼近語義的不使用變數類型首碼的Camel(駱駝)命名法,這也是現今代碼通用的做法。匈牙利命名法在較老的工程中比較常見。無數使用Camel的著名開源工程,很好的告訴了匈牙利命名法的支援者,不使用匈牙利命名法,代碼依然清晰可讀,甚至會更好。
2. 成員變數首碼
CEGUI中無論是class還是struct,成員變數都統一加d_首碼。通常的兩種做法是不加首碼和加m_首碼。
3. struct
CEGUI中,struct的命名沒有使用全大寫,而是和class的大小寫命名一樣。在struct中,只可能出現資料成員,操作符重載函數,建構函式等。struct只用來資料結構儲存使用,不使用任何其他的成員函數。
4. 字串處理
CEGUI中的字串處理沒有使用char, wchar_t, TCHAR,std:string,ATL::CString,而是自己實現了一套String類(CEGUIString.h)。自己實現的String類優勢非常明顯,可以讓程式員從繁瑣的字元指標操作中解脫出來,將更多的精力放在功能的實現上。自訂的String可以實現更多方便、切合實際使用的函數。(比如CEGUI中的find_first_not_of,replace等函數。)如果要實現一個String類,CEGUI中的CEGUIString是一個很好的範本。
5. 使用bool
使用bool和BOOL都有各自的理由,CEGUI中全部使用bool,是現在流行,最常見的,也是我喜歡的做法。
6. 異常機制
CEGUI中完全使用異常機制,通過重載std::exception定義了一套完整的異常機制。通過異常的處理,使得程式員從繁瑣的錯誤檢查和錯誤傳回值中解放出來。如果想瞭解C++中如何能夠很好的使用異常,看看CEGUI中異常的實現吧(CEGUIExceptions.h)。
7. 不用goto
幾乎每一本教材都會告誡使用者,不要使用goto,這會讓你的代碼變得淩亂不堪。然而當真正成為一個程式員,加入一個公司甚至大型公司,裡面的代碼照樣充斥了無數的goto語句。goto的濫用是絕對不允許的,goto在某些情況下的確有它的合理之處(比如保證函數單一出口,釋放資源)。還是那句話,存在即合理。CEGUI中完全不使用goto,有了錯誤,立即拋異常,或者直接返回。我本人也是比較喜歡這種提前返回的方式。
8. 簡單易用的Log系統
如果想實現一個既簡單,又易用的Log系統,就看看CEGUI中的實現吧(CEGUILogger)。支援Log等級(Error,Warning等),輸出日期、當前程式碼等功能。
9. Singleton的實現
CEGUI中使用了一個通用的Singleton<T>模板基類,這個技巧非常好使。實現也非常簡單:
template <typename T> class CEGUIEXPORT Singleton
{
protected:
static
#ifdef __MINGW32__
CEGUIEXPORT
#endif
T* ms_Singleton;
public:
Singleton( void )
{
assert( !ms_Singleton );
ms_Singleton = static_cast<T*>(this);
}
~Singleton( void )
{ assert( ms_Singleton ); ms_Singleton = 0; }
static T& getSingleton( void )
{ assert( ms_Singleton ); return ( *ms_Singleton ); }
static T* getSingletonPtr( void )
{ return ( ms_Singleton ); }
private:
Singleton& operator=(const Singleton&) { return this; }
Singleton(const Singleton&) {}
};
如上面的Logger類,要使用Singleton,則這樣定義:
class CEGUIEXPORT Logger : public Singleton <Logger>
{
}
這樣做最大的一個好處是統一,不會在代碼中出現各種稀奇古怪不同的Singleton實現。同時哪個class是Singleton也一目瞭然。
總結
本文僅僅討論CEGUI中的代碼規範,而且也只是其中很小的一部分,如有疏漏歡迎一起討論。之後我會更加關注CEGUI現細節,如有收穫將與大家分享。
最後,我還是那句話:不要完全排斥任何一種規範,將每種規範瞭解和使用過後,再來思考自己應該怎樣做。
CEGUI首頁:http://www.cegui.org.uk
源碼下載 :http://www.cegui.org.uk/wiki/index.php/Downloads