跨模組介面與動態庫

xuelei20發表於2022-04-09

模組

首先定義下模組的概念,在C++中可以認為每個二進位制檔案為一個模組。比如一個exe可執行程式、一個dll或so動態庫。通常來說一個exe會依賴於幾個dll動態庫。
我們寫一個帶介面的聊天程式,exe為入口主程式 是主模組,它可能依賴於QtWidgets QtCore等Qt模組、libevent第三方模組、我們自己寫的基礎庫xxbase模組。

介面/跨模組介面

模組自己內部呼叫比較簡單,因為編譯環境和平臺都一致,不存在不相容的問題。如果想把我們的功能提供給其他人使用,就需要匯出介面和dll二進位制檔案了。每種語言有自己的介面定義形式,介面在C或C++裡就是一些.h標頭檔案。標頭檔案裡定義了結構體、函式等,供其他模組呼叫。

也可以認為模組由介面和二進位制檔案組成。windows編譯出來的dll肯定不能在linux上呼叫、32位編譯出的dll又不能被64位程式呼叫、Debug模式和Release模式也存在很多差異。C++不像Java,Java是編譯一次在任意作業系統和平臺都能跑起來。C++呢?不同作業系統、不同CPU、不同系統位數、甚至不同優化引數,編譯出來的二進位制檔案都不通用。

動態庫

動態庫就是編譯好的、可供其他模組呼叫的二進位制檔案。在windows是dll形式,在類Unix是so形式。

動態庫相比原始碼和靜態庫有以下優勢:

  • 若以原始碼或靜態庫方式提供給別人用,如果後期有一個bug需要修改,那麼所有呼叫者都需要重新編譯、測試、打包釋出,成本很高。
  • 以動態庫方式提供,使用者只需要替換dll或so即可,簡單高效。

動態庫的劣勢:

  • 動態庫版本維護比較麻煩,得思考下如何避免“dll地獄”。

跨模組介面編寫規範

  • 跨平臺的介面應該使用C++11新統一的資料型別,比如int32_t int64_t等。因為原始型別在不同平臺編譯器可能位數不同,比如long有的是32位、有的是64位。
  • 不能使用STL標準庫裡的vector、list等作為函式引數。因為模組提供者和使用者可能使用不同的C++編譯器,STL實現方式不一樣。
  • 只能傳遞裸指標作為函式引數,不能使用智慧指標。因為不確定呼叫者是否支援智慧指標、智慧指標實現方式是否一樣。
  • 介面一經發布,不允許做任何修改(.h不能改、只允許改.cpp釋出dll更新版本)。因為修改介面後所有使用者都要重新編譯,即使用不到新增的功能。

“介面一經發布,不允許做任何修改”這聽上去很難,其實是有辦法解決的。我們可以參考COM的思想,提供一個QueryInterface函式,用以查詢所有介面。呼叫者先判斷這個介面是否存在,存在則呼叫、不存在則給出錯誤提示。如果後期需要增加新介面,只需要更改下QueryInterface內部實現,就能擴充套件新介面。

即使把現在所有能想到的功能都抽象成介面,也免不了後期增加新功能。可以提前預留一些欄位、函式,或者設計成方便擴充套件的形式。

相關文章