目录
- 前言
- 一、为什么使用宏
- 1、Windows加载
- 2、Linux加载
- 3、宏加载
- 二、具体实现
- 三、如何使用
- 1、引用头文件
- 2、添加导入宏
- 3、直接调用
- 总结
前言
开发的时候,有些项目不能静态链接动态库,需要程序运行时加载动态库,这个时候根据不同平台我们通常使用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-.dll");
if (!_hLibrary)
{
printf("error:load %s failed!\n", "avformat-.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-.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/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 _WIN
#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 // _Win
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都可以使用