Linux 平臺相關代碼帶來的問題
目前市場上存在着許多不同的 Linux 平臺(例如:RedHat, Ubuntu, Suse 等),各大廠商和社區都在針對自己支持的平臺進行優化,爲使用者帶來諸多方便的同時也對軟件研發人員在進行編碼時帶來不少問題:
- 由於程序中不可避免的存在平臺相關代碼(系統調用等),軟件研發人員爲了保證自己的產品在各個 Linux 平臺上運行順暢,一般都需要在源代碼中大量使用預編譯參數,這樣會大大降低程序的可讀性和可維護性。
- 接口平臺無關性的原則是研發人員必須遵循的準則。但是在處理平臺相關代碼時如果處理不當,此原則很有可能被破壞,導致不良的編碼風格,影響代碼的擴展和維護。
本文將針對這兩個問題循序漸進依次提出解決方案。
(1)通過設置預編譯選項來處理平臺相關代碼
通過爲每個平臺設置相關的預編譯宏能夠解決 Linux 平臺相關代碼的問題,實際情況下,很多軟件開發人員也樂於單獨使用這種方法來解決問題。
假設現有一動態庫 Results.so,SomeFunction() 是該庫的一個導出函數,該庫同時爲 Rhel,Suse,Ubuntu 等三個平臺的 Linux 上層程序服務。(後文例子均基於此例並予以擴展。)
清單 1. 設置預編譯選項示例代碼如下:
// Procedure.cpp
void SomeFunction()
{
//Common code for all linux
......
......
#ifdef RHEL
SpecialCaseForRHEL();
#endif
#ifdef SUSE
SpecialCaseForSUSE();
#endif
#ifdef UBUNTU
SpecialCaseForUBUNTU();
#endif
//Common code for all linux
......
......
#ifdef RHEL
SpecialCase2ForRHEL();
#endif
#ifdef SUSE
SpecialCase2ForSUSE();
#endif
#ifdef UBUNTU
SpecialCase2ForUBUNTU();
#endif
//Common code for all linux
......
....
}
開發人員可以通過設置 makefile 宏參數或者直接設置 gcc 參數來控制實際編譯內容。
例如:
gcc -D RHEL Procedure.cpp -o Result.so -lstdc++ // Use RHEL marco
SpecialCaseForRHEL(),SpecialCaseForSUSE(),SpecialCaseForUBUNTU() 分別在該庫 (Results.so) 的其他文件中予以實現。
圖 1. 清單 1 代碼的結構圖
帶來的問題
SomeFunction() 函數代碼冗餘,格式混亂。本例僅涉及三個預編譯選項,但實際情況中由於 Linux 版本衆多並且可能涉及操作系統位數的問題,增加對新系統的支持會導致預編譯選項不斷增多,造成 SomeFunction() 函數結構十分混亂。新增其他平臺相關接口(例如:增加 SpecialCase3ForRHEL(),SpecialCase3ForSUSE(),SpecialCase3ForUBUNTU),會成倍增加代碼中預編譯宏的數量。
破壞了接口平臺無關性的原則。SpecialCaseForRHEL(),SpecialCaseForSUSE(),SpecialCaseForUBUNTU() 只是同一功能各個平臺的不同實現,屬於封裝內容,不應該分開暴露給調用者。
可見,簡單利用預編譯宏來解決平臺相關代碼產生的問題不是一個好的方法,並沒有解決本文開始提出的兩個問題。後文將通過三個方案依次解決這些問題。