C/C++的開源庫大部分還是比較好的,體現在:
1、開源,意味着可以使用lib或源碼,這點很靈活。
2、功能現成,意味着我們不需要再重複勞動。
3、一般較符合標準國際,通用性好。
4、一般免費,license較寬鬆,具體見GPL LGPL BSD等license,然而我們是天朝的特殊情況,你懂的。。
然而好的庫,其實未必好用,有很多坑,甚至不明顯,甚至坑很深:
1、即便成熟的庫(尤其是C的),一般都是需要初始化。
例如pjsip裏的pjlib pjutil pjnath等基本都有個xxx_init(),而上來就用或者忘記使用這些init的代碼就會帶來問題。
2、函數功能的坑,比如pj_str_t pj_str(char * str)這個函數,不翻源碼,你能知道這是個淺拷貝函數嗎?
假如你給它傳了個臨時變量的str,那你就給自己挖了個坑,什麼時候掉進去是未知的。
2、異常的崩潰問題,即便你定位到該位置,缺發現原本很正常的代碼卻崩潰了。
註釋掉幾行代碼,發現崩潰延後了,這說明是其他問題引起的堆棧崩潰,而不是該幾行代碼的問題。
而這種問題的解決基本是無跡可尋的,只有個“0x0000005”不可訪問之類的異常。
這樣的問題很可能是該庫或者該接口有依賴:
比如,使用pjlib庫的外部線程必須被註冊給pjlib庫,本人使用pjnath庫就遇到了這樣的問題。
vs2012 編譯的pjnath icedemo例子沒問題,自己改來用在dll裏 結果 出現 堆棧被破壞
# define PJ_CHECK_STACK()// pj_thread_check_stack(__FILE__, __LINE__)
把該宏空置失效,可以解決,但是百思不得其解。
後來終於發現是外部使用線程並未註冊給pjlib,還是尼瑪翻源碼發現的。
pj_thread_register的說明:
This function must be called in the context of the thread being registered.
* When the thread is created by external function or API call,
* it must be 'registered' to PJLIB using pj_thread_register(), so that it can
* cooperate with PJLIB's framework.
這引起了我的懷疑,在外部線程函數優先調用下邊的函數來嘗試
- bool ice_register_thread()
- {
- if(!pj_thread_is_registered())
- {
- pj_thread_desc desc;
- pj_thread_t* thed;
- if (pj_thread_register(NULL,desc,&thed) == PJ_SUCCESS)
- {
- return true;
- }
- }
- return false;
- }
原來存在的正常位置代碼堆棧崩潰問題消失了。
這個現象裏是pjnath依賴pjlib和pjutil,而pjlib依賴“調用pjlib庫的外部線程必須註冊給它”這個條件。
而這個條件在pjnath的文檔或wiki裏並未體現,所以有時候要調查清楚所有的依賴問題來定位異常。
3、使用源碼和lib或dll的經常有區別。
比如pthread for win32的版本使用靜態庫的時候,必須使用成對的初始化和銷燬函數。而用dll就沒這問題。
4、跨平臺問題。
越成熟的庫跨平臺性能越好,linux和windows下的編譯和使用文檔健全,用的人多,出了問題,度娘、谷哥一般能解決問題。
最後,談下自己的經驗:
選庫條件:1、出的早2、用的人多3、依賴其他庫少4、各個平臺開發使用文檔詳細5、最好用c++的庫(c庫有些回調函數是滿天飛的)6、能滿足自己需要,其他功能越少越好。
用庫方法:1、涉及的文檔務必仔細看2、遇到問題不要輕易放棄,可選用排除法定位問題類型3、務必多嘗試4、搞不定的,先變通實現最好
最後,駕馭開源庫是一個時間未知的工作,但是我們儘量縮短時間。