這篇是延續前一篇《基本使用》,繼續來整理一些 Boost.DLL 的其他功能。
Alias(別名)
首先,是「alias」(別名)的部分。
在前一篇裡面,已經提過基本的匯出、匯入的方法了。在基本的方法中,要匯出和匯入時,都是直接使用本來的變數、函式名稱;但是有的時候,如果不方便直接使用原有的變數、函式名稱,但是又不好直接去改名的時候,就可以透過 alias 的機制,在匯出時採用另一個名字來匯出。
而如果要改用 alias 的形式來匯出一個變數的話,要使用 BOOST_DLL_ALIAS 這個巨集(定義於 boost/dll/alias.hpp),程式碼可以寫成:
std::string sModuleName = "Module A";
BOOST_DLL_ALIAS(sModuleName, NAME)
如此一來,sModuleName 這個變數就會以 NAME 這個別名被匯出。
不過,使用別名和本來的名字匯出,在實作上還是有有所不同的,所以在匯入的時候,使用的函式也必須要從 boost::dll::import<>() 改成 boost::dll::import_alias<>() 才行。
以要匯入 NAME 來說,可以寫成:
boost::shared_ptr<std::string> pVar = boost::dll::import_alias<std::string>( pathDLL, "NAME" );
基本上,這樣就可以了~
個人覺得比較討厭的,是使用 alias 的時候,import 也要使用不同的函式,算是比較麻煩的…
另外,其實 Boost.DLL 的 alias 還有「section」(應該算是「章節」)的功能,可以幫匯出的東西分類。如果要使用的話,就是在匯出時要改用 BOOST_DLL_ALIAS_SECTIONED 這個巨集;而實際上,Boost.DLL 的 BOOST_DLL_ALIAS 預設會以 boostdll 作為 section 名稱來匯出,所以如果寫成
BOOST_DLL_ALIAS_SECTIONED(sModuleName, NAME, boostdll)
的話,會和前面的匯出方法完全相同。而如果想要改用其他 section 名稱的話,就只要把 boostdll 換掉就可以了。
而有需要的話,也可以透過 BOOST_DLL_SECTION(官方文件)來設定存取權限;不過老實說,Heresy 不知道什麼時候會要用到就是了…
另外,看來 Boost.DLL 的 section 設計主要是給 boost::dll::library_info 用的,在使用 boost::dll::import_alias<>() 匯入時,並沒有辦法、也不需要指定 section 名稱。
這邊的範例可以參考:https://github.com/KHeresy/Boost.DLL.Example/tree/AliasExample
library_info
在 Boost.DLL 裡,boost::dll::library_info(官方文件)是設計用來讀取一個動態函式庫的資料用的類別。
它的使用非常簡單,只要把 DLL / SO 的路徑傳給他,就可以建立出對應的 library_info 物件了。而之後,只要透過他的 sections() 和 symbols() 這兩個函式,就可以知道所指定的檔案裡面有哪些 section、以及匯出了哪些東西了。
下面是一個簡單的使用範例:
// Load library information boost::dll::library_info infDLL(pathDLL); // get all section name std::cout << "List sectionsn"; std::vector<std::string> vSections = infDLL.sections(); for (const std::string& sSection : vSections) { std::cout << " Section: " << sSection << "n"; // get all symbol name std::vector<std::string> vSymbols = infDLL.symbols(sSection); for (const std::string& sSymbol : vSymbols) { std::cout << " - " << sSymbol << "n"; } }
在上面的例子裡面,首先是先建立了一個 library_info 的物件 infDLL,他會去讀取 pathDLL 這個檔案的內容。而接下來則是先透過 sections() 這個函式、來取得他的 section 列表;之後再透過 for 迴圈,並使用 symbols() 這個函式去讀取裡面每個 section 所匯出的符號、然後再依序輸出。
不過,由於編譯器在建置動態函式庫的時候,似乎是會在裡面建立一些特殊、內建的 section,所以在讀取的時候,應該會看到很多以「.」開頭的 section。像下面就是在 Windows + MSVC 跑的結果:
Section: .textbss Section: .text Section: .rdata Section: .data Section: .pdata Section: .idata Section: boostdll - NAME Section: XD - GET_NAME Section: .gfids Section: .00cfg Section: .rsrc Section: .reloc
而 Heresy 試過,在 Ubuntu 下用 g++ 編譯的話,這類的東西會更多。
另外,在 MSVC 下,想透過 symbols() 去讀取這些 section 中的資訊是會出問題的,所以建議在上面的程式中,建議把這些內建的 section 綠調會比較好。
而如果不想管 section 的話,也可以不指定 section 名稱、直接呼叫 symbols() 來取得所有匯出的東西;他的寫法可以寫成:
std::vector<std::string> vSymbols = infDLL.symbols(); for (const std::string& sSymbol : vSymbols) { std::cout << " " << sSymbol << "n"; }
不過老實說,Heresy 不太確定這樣列出來他匯出了哪些東西,到底該怎麼用?因為實際上,他並沒有告訴使用者,這個符號到底是代表變數、還是函式啊…而如果知道是函式的話,其實也還是不知道她需要那些引數啊… @@
所以感覺上,library_info 這個類別,應該還是只能當作輔助用的吧?例如,如果想確定這個動態函式庫裡面是否有自己需要的符號名稱,就可以透過這個方法來做事前的檢查。
這部分的範例程式,可以參考:https://github.com/KHeresy/Boost.DLL.Example/tree/library_info。
shared_library
boost::dll::shared_library 這個類別,是一個可以比較好地用來控制動態函式庫的生命週期的東西,同時他也提供了簡單的檢查功能,可以用來確認所讀取的模組是否有指定的符號。
根據官方文件的說法,它內部的參考計算方法,可以處理得很好、不管使用相對路徑或絕對路徑,都不用擔心會被重複建立,所以在記憶體的管理上、算是非常有效率了。
而它的使用方法,大致上就是先透過指定檔案所在的路徑建立出 boost::dll::shared_library 的物件後,然後再透過 get<>() 和 get_alias<>() 來匯入所需要的東西了~
下面就是一個簡單的範例:
boost::dll::shared_library libDLL(pathDLL); if (libDLL.has("NAME")) { std::string sVar = libDLL.get_alias<std::string>("NAME"); std::cout << "Variable: " << sVar << std::endl; } if (libDLL.has("GET_NAME")) { std::function<std::string()> funcExt = libDLL.get_alias<std::string()>("GET_NAME"); std::cout << "Function: " << funcExt() << std::endl; } libDLL.unload();
可以看到,這邊的使用方法大致上都和直接使用 boost::dll::import<>() 和 boost::dll::import_alias<>() 相同,不過由於可以在匯入之前,先透過 has() 來檢查是否有要匯入的東西,算是比較方便操作的了~
另外,比較不一樣的地方,則是使用 get<>() 或 get_alias<>() 匯入變數時,所回傳的型別並不是 boost::shared_ptr<>,而是實際的型別;所以如果是指標、會是參考的話,就必須要自己管理他的生命週期了。
這裡的範例可以參考:https://github.com/KHeresy/Boost.DLL.Example/tree/SharedLibrary。
這部分就先寫到這裡了,預計關於 Boost.DLL 的部分,之後應該還會有一篇吧?
不過,老實說,自己在寫這篇的時候拖了很久、時間拉得很長,很多地方用語應該有不一致的地方,這點就尚請見諒了。