当前位置: 首页 > news >正文

C++11实用技术(五)泛型编程加载dll接口函数

C++11泛型编程简化加载dll代码

常见的加载dll方式:

HMODULE m_hDataModule;
m_hDataModule = LoadLibrary("myDll.dll");typedef int (*PfunA)(int a, int b);//定义函数指针
PfunA fun = (PfunA)(GetProcAddress(m_hDataModule , "funA"));//加载接口
int ret = fun(1, 2);//执行函数

加载dll中的函数需要分为3步:

  • 1.定义函数指针
  • 2.从dll中加载接口
  • 3.执行函数

3步看起来不多,但是如果加载上百个这样的函数,那就非常繁琐,可能会遇到重复命名、参数定义不一致等各种问题。

C++11提供了方法可以通过泛型编程的方式,提供一个加载执行dll接口函数的通用函数。

示例代码如下:

std::map<string, FARPROC> _funcMap;
HMODULE _dataModule;template <typename T>
std::function<T> LoadFunction(const string& functionName)
{auto it = _funcMap.find(functionName);if (it == _funcMap.end()){auto addr = GetProcAddress(_dataModule, functionName.c_str());if (!addr) return nullptr;_funcMap.insert(std::make_pair(functionName, addr));it = _funcMap.find(functionName);}return std::function<T>((T*)(it->second));
}
template <typename T, typename... Args>
typename std::result_of<std::function<T>(Args...)>::type ExcuteFunc(const string& funcName, Args&&... args)
{auto func = LoadFunction<T>(funcName);if (func == nullptr){std::cout << "Load " << funcName << " error!" << std::endl;}return func(std::forward<Args>(args)...);
}int main()
{_dataModule = LoadLibrary("myDll.dll");//一行代码即可加载并执行接口,并且支持各种类型的接口函数int funA = ExcuteFunc<int(int, int)>("funA", 1, 2);//有两个入参,返回int类型bool funB = ExcuteFunc<bool(int)>("funB", 1);//有一个入参,返回bool类型ExcuteFunc<void()>("funC");//没有入参,也没有返回值return 0;

上述加载dll过程分为两个函数:

  • 一个是LoadFunction函数,通过泛型编程提供加载dll中的函数指针。
  • 一个ExcuteFunc函数,用于执行加载后的函数。

LoadFunction

LoadFunction 函数用于加载函数指针。

它接受一个 functionName 参数,表示要加载的函数名。

首先,它在 _funcMap 中查找是否已经加载了该函数指针,如果找到了就直接返回。如果没有找到,则使用 GetProcAddress 函数从 _dataModule 中获取函数指针的地址,并将其插入到 _funcMap 中缓存起来。

最后,返回一个 std::function<T> 对象,其中 T 是函数指针的类型。

template <typename T>
std::function<T> LoadFunction(const string& functionName)
{auto it = _funcMap.find(functionName);if (it == _funcMap.end()){auto addr = GetProcAddress(_dataModule, functionName.c_str());if (!addr) return nullptr;_funcMap.insert(std::make_pair(functionName, addr));it = _funcMap.find(functionName);}return std::function<T>((T*)(it->second));
}

注意使用

auto addr = GetProcAddress(_dataModule, functionName.c_str());

得到的addr类型只是一个输入参数为空,void*返回值类型的函数类型,所以说它是不能直接使用的。我们会在后面的代码中将加载的函数指针转换为对应的函数指针类型。如何去转换就是本文最核心的点


在函数最后返回值的时候

return std::function<T>((T*)(it->second));

作用是将 it->second(函数指针的地址)强制转换为类型为 T 的函数指针(T就是我们具体的函数指针类型),并使用 std::function 对象进行封装。

ExcuteFunc

ExcuteFunc 函数用于执行加载的函数。它接受一个 funcName 参数,表示要执行的函数名,以及可变参数 args,表示函数的实际参数。首先,它调用 LoadFunction 函数来加载函数指针。如果加载失败(即函数指针为空),则输出错误信息。然后,使用加载的函数指针 func 调用 std::function 对象,传递给定的参数,并返回执行结果。

template <typename T, typename... Args>
typename std::result_of<std::function<T>(Args...)>::type ExcuteFunc(const string& funcName, Args&&... args)
{auto func = LoadFunction<T>(funcName);if (func == nullptr){std::cout << "Load " << funcName << " error!" << std::endl;}return func(std::forward<Args>(args)...);
}

函数中

typename std::result_of<std::function<T>(Args...)>::type ExcuteFunc(const string& funcName, Args&&... args)

用到一个traits技巧result_of,用来获取函数的返回值类型。

http://www.lryc.cn/news/130024.html

相关文章:

  • 使用wxPython和PyMuPDF提取PDF页面指定页数的内容的应用程序
  • k8s的pv和pvc创建
  • 记K8S集群工作节点,AnolisOS 8.6部署显卡驱动集成Containerd运行时
  • JavaScript 性能优化
  • 架构演进及常用架构
  • WinCC V7.5 中的C脚本对话框不可见,将编辑窗口移动到可见区域的具体方法
  • 【实战】十一、看板页面及任务组页面开发(二) —— React17+React Hook+TS4 最佳实践,仿 Jira 企业级项目(二十四)
  • Vue2.7.14、vuecli@5.0.8 升级 vite@4.4.8
  • LeetCode[面试题04.12]求和路径
  • 骑行运动耳机哪款好?五年骑行爱好者给你分享分享
  • SpringBoot3集成ElasticSearch
  • 详解23种设计模式优缺点以及解决方案
  • Oracle 数据库中删除表空间的详细步骤与示例
  • <kernel>kernel 6.4 笔记
  • 介绍一些编程语言— Perl 语言
  • 原型与继承
  • Flink流批一体计算(14):PyFlink Tabel API之SQL查询
  • JRebel插件扩展-mac版
  • C语言中常见的一些语法概念和功能
  • Python土力学与基础工程计算.PDF-钻探泥浆制备
  • 【机器学习】— 2 图神经网络GNN
  • QT的布局与间隔器介绍
  • 深入浅出Pytorch函数——torch.nn.Linear
  • Vue3.2+TS的defineExpose的应用
  • 牛客网Python入门103题练习|【08--元组】
  • Jenkins改造—nginx配置鉴权
  • (二)VisionOS平台概述
  • 菜单中的类似iOS中开关的样式
  • Vue 2 动态组件和异步组件
  • MongoDB升级经历(4.0.23至5.0.19)