C++使用宏實(shí)現(xiàn)動態(tài)庫加載
前言
開發(fā)的時候,有些項(xiàng)目不能靜態(tài)鏈接動態(tài)庫,需要程序運(yùn)行時加載動態(tài)庫,這個時候根據(jù)不同平臺我們通常使用LoadLibrary或dlopen將動態(tài)庫加載到程序中,并且還需要定義函數(shù)指針然后在獲取函數(shù)地址,這一系列的操作其實(shí)時比較麻煩的,尤其是方法較多的情況,基本沒法這么做,這時候就需要將這一過程進(jìn)行適當(dāng)?shù)暮喕W寗討B(tài)加載在方法較多的情況下盡量減少工作量。
一、為什么使用宏
一般的動態(tài)庫加載流程
1、Windows加載
#include<Windows.h> extern "C" { #include "libavformat/avformat.h" } //定義方法類型 typedef AVFormatContext*(*delegate_avformat_alloc_context)(void); //定義方法指針 delegate_avformat_alloc_context dl_avformat_alloc_context; HMODULE _hLibrary = nullptr; //加載動態(tài)庫 void ImportMethod() { auto _hLibrary = LoadLibraryA("avformat-58.dll"); if (!_hLibrary) { printf("error:load %s failed!\n", "avformat-58.dll"); return; } dl_avformat_alloc_context=(delegate_avformat_alloc_context)GetProcAddress(_hLibrary, "avformat_alloc_context"); if (!dl_avformat_alloc_context) { printf("error:%s load %s method failed!\n","avformat-58.dll", "avformat_alloc_context"); } } //卸載動態(tài)庫 void UnImport() { if (_hLibrary) { FreeLibrary(_hLibrary); _hLibrary = nullptr; } } //使用方法 void UseMethod() { auto ic = dl_avformat_alloc_context(); }
2、Linux加載
#include <dlfcn.h> extern "C" { #include "libavformat/avformat.h" } //定義方法類型 typedef AVFormatContext*(*delegate_avformat_alloc_context)(void); //定義方法指針 delegate_avformat_alloc_context dl_avformat_alloc_context; void* _hLibrary = nullptr; //加載動態(tài)庫 void ImportMethod() { auto _hLibrary = dlopen("libavformat.so", RTLD_LAZY); if (!_hLibrary) { printf("error:load %s failed!\n", "libavformat.so"); return; } dl_avformat_alloc_context=(delegate_avformat_alloc_context)dlsym(_hLibrary, "avformat_alloc_context"); if (!dl_avformat_alloc_context) { printf("error:%s load %s method failed!\n","libavformat.so", "avformat_alloc_context"); } } //卸載動態(tài)庫 void UnImport() { if (_hLibrary) { dlclose(_hLibrary); _hLibrary = nullptr; } } //使用方法 void UseMethod() { auto ic = dl_avformat_alloc_context(); }
3、宏加載
很明顯上述流程對于加載一個方法來說流程過于復(fù)雜,在遇到庫比較多以及方法比較多的情況下,這種方法是很影響開發(fā)效率的。但是如果我們對上述流程進(jìn)行宏包裝,事情將會變得簡單很多。比如上述引入ffmpeg的avformat_alloc_context方法只需要兩行即可:
extern "C" { #include "libavformat/avformat.h" } //使用宏動態(tài)加載方法 DLL_IMPORT("libavformat.so", avformat_alloc_context); #define avformat_alloc_context DLL_IMPORT_NAME(avformat_alloc_context) //使用方法 void UseMethod() { //與原方法名稱一致,直接調(diào)用 auto ic = avformat_alloc_context(); }
二、具體實(shí)現(xiàn)
我們通過宏包裝上述兩個流程即可,同時還需要結(jié)合c++11的decltype關(guān)鍵字。
DllImportUtils.h
// // Created by xin on 2022/6/15. // #ifndef DLLIMPORTUTILS_H #define DLLIMPORTUTILS_H void* GetDllMethodPtr(const char*dll,const char*method); #define DLL_IMPORT(dll,method) decltype (method)* dllImport_##method; \ namespace { \ class A##method{ \ public: A##method() { \ dllImport_##method = (decltype(dllImport_##method))GetDllMethodPtr(dll, #method); \ } \ }; \ A##method a##method; \ } #define DLL_IMPORT_NAME(name) dllImport_##name #endif
DllImportUtils.cpp
#include"DllImportUtils.h" #include<map> #include<string> #include<stdio.h> #ifdef _WIN32 #include<Windows.h> #define ACLoadLibrary(name) LoadLibraryA(name) #define ACGetProcAdress(dll,name) GetProcAddress((HMODULE)dll,name) #else #include <dlfcn.h> #define ACLoadLibrary(name) dlopen(name,RTLD_LAZY) #define ACGetProcAdress(dll,name) dlsym(dll,name) #endif // _Win32 std::map<std::string, void*>* _dllMap = nullptr; class DllMapDisposer { public: ~DllMapDisposer() { if (_dllMap) delete _dllMap; } }; static DllMapDisposer _disposer; void* GetDllMethodPtr(const char* dll, const char* method) { if (!_dllMap) _dllMap = new std::map<std::string, void*>; auto iter = _dllMap->find(dll); void* hm; if (iter == _dllMap->end()) { hm = (void*)ACLoadLibrary(dll); if (hm) { (*_dllMap)[dll] = hm; } else { printf("warnning:load %s failed!\n", dll); } } else { hm = iter->second; } if (hm) { auto methodPtr = ACGetProcAdress(hm, method); if (!methodPtr) { printf("error:%s load %s method failed!\n", dll, method); } return methodPtr; } return nullptr; }
三、如何使用
1、引用頭文件
引用需要導(dǎo)入方法的頭文件
extern "C" { //需要導(dǎo)入方法的頭文件 #include "libavformat/avformat.h" } #include"DllImportUtils.h"
2、添加導(dǎo)入宏
//參數(shù)為庫的名稱和需要導(dǎo)入的方法 DLL_IMPORT("libavformat.so", avformat_alloc_context); #define avformat_alloc_context DLL_IMPORT_NAME(avformat_alloc_context)
3、直接調(diào)用
void UseMethod() { //與原方法名稱一致,直接調(diào)用 auto ic = avformat_alloc_context(); }
注:當(dāng)前版本不支持卸載庫,程序啟動時方法就會被立刻加載。支持跨平臺,Windows、Linux都可以使用
總結(jié)
以上就是今天要講的內(nèi)容,本文講述的方法很大程度的減少了工作量,而且可以不需要改代碼原有邏輯和方法名稱,適合需要動態(tài)加載不同版本的庫或者依賴庫的glibc不相同時的場景使用。
到此這篇關(guān)于C++使用宏實(shí)現(xiàn)動態(tài)庫加載的文章就介紹到這了,更多相關(guān)C++動態(tài)庫加載內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
詳解C++內(nèi)存的代碼區(qū),全局區(qū),棧區(qū)和堆區(qū)
這篇文章主要為大家介紹了C++內(nèi)存的代碼區(qū),全局區(qū),棧區(qū)和堆區(qū),具有一定的參考價值,感興趣的小伙伴們可以參考一下,希望能夠給你帶來幫助2021-12-12C++11/14 線程調(diào)用類對象和線程傳參的方法
這篇文章主要介紹了C++11/14 線程調(diào)用類對象和線程傳參的方法,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2019-01-01C++結(jié)構(gòu)體中變長數(shù)組的使用問題分解刨析
變長數(shù)組在C++中指的是集合(也叫容器)如vector就是C語言中,所有的數(shù)組都不定長,沒有下標(biāo)越界的概念,數(shù)組實(shí)質(zhì)就是一個指針(由數(shù)組名充當(dāng))因此C語言中數(shù)組的長度沒有任何意義平常在C語言中講的不定長數(shù)組,其實(shí)就是指針2022-08-08C++中volatile和mutable關(guān)鍵字用法詳解
這篇文章主要介紹了C++中volatile和mutable關(guān)鍵字用法詳解,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-02-02