栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > C/C++/C#

C++ 使用宏加载动态库

C/C++/C# 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

C++ 使用宏加载动态库

文章目录
  • 前言
  • 一、为什么使用宏?
    • 1、Windows加载
    • 2、Linux加载
    • 3、宏加载
  • 二、具体实现
  • 三、如何使用
    • 1、引用头文件
    • 2、添加导入宏
    • 3、直接调用
  • 总结


前言

开发的时候,有些项目不能静态链接动态库,需要程序运行时加载动态库,这个时候根据不同平台我们通常使用LoadLibrary或dlopen将动态库加载到程序中,并且还需要定义函数指针然后在获取函数地址,这一系列的操作其实时比较麻烦的,尤其是方法较多的情况,基本没法这么做,这时候就需要将这一过程进行适当的简化。让动态加载在方法较多的情况下尽量减少工作量。


一、为什么使用宏?

一般的动态库加载流程

1、Windows加载
#include
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 
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
#include
#include
#include
#ifdef _WIN32
#include
#define ACLoadLibrary(name) LoadLibraryA(name)
#define ACGetProcAdress(dll,name) GetProcAddress((HMODULE)dll,name)
#else
#include 
#define ACLoadLibrary(name) dlopen(name,RTLD_LAZY)
#define ACGetProcAdress(dll,name) dlsym(dll,name)
#endif // _Win32
 std::map& ACGetDllMap();
#define DLL_IMPORT(dll,method) decltype (method)* dllImport_##method;   								
namespace {																								
	class A##method {																					
	public: A##method() {																				
		auto iter = ACGetDllMap().find(dll);																	
		void* hm;																						
		if (iter == ACGetDllMap().end())																		
		{																								
			hm = (void*)ACLoadLibrary(dll);																
			if (hm)																						
			{																							
				ACGetDllMap()[dll] = hm;																		
			}																							
			else																						
			{																							
				printf("error:load %s failed!n", dll);												    
			}																							
		}																								
		else																							
		{																								
			hm = iter->second;																			
		}																								
		dllImport_##method = (decltype(dllImport_##method))ACGetProcAdress(hm, #method);				
		if (!dllImport_##method)																		
		{																								
			printf("error:%s load %s method failed!n",dll, #method);								    
		}																								
	}																									
	};																									
	A##method a##method;																				
}
#define DLL_IMPORT_NAME(name) dllImport_##name
#endif 

DllImportUtils.cpp

#include"DllImportUtils.h"
 std::map* _dllMap=nullptr;
 std::map& ACGetDllMap()
 {
	 if (!_dllMap)
		 _dllMap = new std::map;
	 return *_dllMap;
 }
 class DllMapDisposer {
 public:
	 ~DllMapDisposer() {
		 if (_dllMap)
			 delete _dllMap;
	 }
 };
 static DllMapDisposer _disposer;

三、如何使用 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不相同时的场景使用。

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/983385.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号