網站首頁 編程語言 正文
前言
開發的時候,有些項目不能靜態鏈接動態庫,需要程序運行時加載動態庫,這個時候根據不同平臺我們通常使用LoadLibrary或dlopen將動態庫加載到程序中,并且還需要定義函數指針然后在獲取函數地址,這一系列的操作其實時比較麻煩的,尤其是方法較多的情況,基本沒法這么做,這時候就需要將這一過程進行適當的簡化。讓動態加載在方法較多的情況下盡量減少工作量。
一、為什么使用宏
一般的動態庫加載流程
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;
//加載動態庫
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");
}
}
//卸載動態庫
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;
//加載動態庫
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");
}
}
//卸載動態庫
void UnImport()
{
if (_hLibrary) {
dlclose(_hLibrary);
_hLibrary = nullptr;
}
}
//使用方法
void UseMethod() {
auto ic = dl_avformat_alloc_context();
}
3、宏加載
很明顯上述流程對于加載一個方法來說流程過于復雜,在遇到庫比較多以及方法比較多的情況下,這種方法是很影響開發效率的。但是如果我們對上述流程進行宏包裝,事情將會變得簡單很多。比如上述引入ffmpeg的avformat_alloc_context方法只需要兩行即可:
extern "C"
{
#include "libavformat/avformat.h"
}
//使用宏動態加載方法
DLL_IMPORT("libavformat.so", avformat_alloc_context);
#define avformat_alloc_context DLL_IMPORT_NAME(avformat_alloc_context)
//使用方法
void UseMethod() {
//與原方法名稱一致,直接調用
auto ic = avformat_alloc_context();
}
二、具體實現
我們通過宏包裝上述兩個流程即可,同時還需要結合c++11的decltype關鍵字。
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、引用頭文件
引用需要導入方法的頭文件
extern "C"
{
//需要導入方法的頭文件
#include "libavformat/avformat.h"
}
#include"DllImportUtils.h"
2、添加導入宏
//參數為庫的名稱和需要導入的方法
DLL_IMPORT("libavformat.so", avformat_alloc_context);
#define avformat_alloc_context DLL_IMPORT_NAME(avformat_alloc_context)
3、直接調用
void UseMethod() {
//與原方法名稱一致,直接調用
auto ic = avformat_alloc_context();
}
注:當前版本不支持卸載庫,程序啟動時方法就會被立刻加載。支持跨平臺,Windows、Linux都可以使用
總結
以上就是今天要講的內容,本文講述的方法很大程度的減少了工作量,而且可以不需要改代碼原有邏輯和方法名稱,適合需要動態加載不同版本的庫或者依賴庫的glibc不相同時的場景使用。
原文鏈接:https://blog.csdn.net/u013113678/article/details/125355481
相關推薦
- 2022-08-25 python自動化測試之破解圖文驗證碼_python
- 2022-10-25 python繪制三維圖的詳細新手教程_python
- 2022-05-19 C#?漢字與拼音互轉的實現示例_C#教程
- 2023-05-14 opencv繪制矩形和圓的實現_python
- 2022-04-20 Python設計模式結構型組合模式_python
- 2022-06-08 Spring Cloud Alibaba Nacos 客戶端服務注冊心跳和健康監測
- 2023-07-09 Golang syncMap 詳解
- 2022-11-03 Python入門教程之三元運算符的使用詳解_python
- 最近更新
-
- window11 系統安裝 yarn
- 超詳細win安裝深度學習環境2025年最新版(
- Linux 中運行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎操作-- 運算符,流程控制 Flo
- 1. Int 和Integer 的區別,Jav
- spring @retryable不生效的一種
- Spring Security之認證信息的處理
- Spring Security之認證過濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權
- redisson分布式鎖中waittime的設
- maven:解決release錯誤:Artif
- restTemplate使用總結
- Spring Security之安全異常處理
- MybatisPlus優雅實現加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務發現-Nac
- Spring Security之基于HttpR
- Redis 底層數據結構-簡單動態字符串(SD
- arthas操作spring被代理目標對象命令
- Spring中的單例模式應用詳解
- 聊聊消息隊列,發送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠程分支