第24课 可变参数模板(5)_DllHelper和lambda链式调用
1. dll帮助类
(1)dll的动态链接
①传统的调用方式:先调用LoadLibrary来加载dll,再定义函数指针类型,接着调用GetProcAddress获取函数地址。然后通过函数指针调用函数,最后通过FreeLibrary卸载dll
②问题:使用dll的过程存在重复逻辑。此外,如果dll中的函数较多,就需要频繁的定义函数指针和反复调用GetProcAddress。
(2)解决方案
①封装GetProcAddress函数,将FARPROC类型的函数指针转换成std::function。
②由于函数的入参数目、类型及返回值各不相同,可以通过result_of和可变参数模板来解决(见ExecuteFunc函数)。
【编程实验】简化dll的调用
//Dll.h
#ifndef _DLL_H_
#define _DLL_H_ #ifdef BUILD_DLL /* DLL export */
#define EXPORT __declspec(dllexport)
#else /* EXE import */
#define EXPORT __declspec(dllimport)
#endif #ifdef __cplusplus
extern "C"{
#endif //求最大值
EXPORT int Max(int a, int b); //求和
EXPORT int Sum(int a, int b); #ifdef __cplusplus
}
#endif #endif
//Dll.c
#include "dll.h" //编译动态dll库:
//gcc -Wall -shared dll.c -o Mydll.dll
//或者
//gcc --share dll.c -o Mydll.dll
//调用dll库生成exe文件:
//gcc test.c Mydll.dll -o test
#define BUILD_DLL //求最大值
EXPORT int Max(int a, int b)
{
return a > b ? a : b;
} //求和
EXPORT int Sum(int a, int b)
{
return a + b;
}
//DllHelper.hpp
#include <windows.h>
#include <string>
#include <functional>
#include <map>
#include <exception>
using namespace std; class DllHelper
{
private:
HMODULE m_hMod; //FARPROC宏: typedef int (FAR WINAPI *FARPROC)();
std::map<string, FARPROC> m_map; //将己加载的函数指针保存起来
public:
DllHelper(): m_hMod(nullptr){} //封装GetProcAddress,将函数指针转为std::function对象
template<typename T> //T为要查找的函数类型
std::function<T> GetFunction(const string& funcName)
{
auto it = m_map.find(funcName); //函数是否己被载入 if(it == m_map.end()){
auto addr = GetProcAddress(m_hMod, funcName.c_str()); //addr为FARPROC类型 if(!addr)
return nullptr; m_map.insert(std::make_pair(funcName, addr)); //保存addr地址(FARPROC类型) it = m_map.find(funcName); //取出addr(FARPROC类型)
} return std::function<T>((T*)(it->second)); //将FARPROC类型的addr强转为T类型。
} //调用函数
template<typename T, typename...Args> //T为函数类型,Args为参数列表
typename std::result_of<std::function<T>(Args...)>::type //原函数的返回类型
ExecuteFunc(const string& funcName, Args&&...args)
{
auto f = GetFunction<T>(funcName);
if(f == nullptr){
string s = "Can not find this function " + funcName;
throw std::exception(runtime_error(s.c_str()));
} return f(std::forward<Args>(args)...);
} bool Load(const string& path)
{
m_hMod = LoadLibraryA(path.data());
if(nullptr == m_hMod)
{
cout <<"LoadLibrary failed!" << endl;
} return (m_hMod != nullptr);
} bool UnLoad()
{
bool ret = true;
if(m_hMod != nullptr){
ret = FreeLibrary(m_hMod); //返回非零表示成功,零表示失败 if(ret)
m_hMod = nullptr;
} return ret;
} ~DllHelper(){UnLoad();}
};
//testDll.cpp
#include <iostream>
#include <windows.h>
#include "DllHelper.hpp"
using namespace std; //传统方法
void testDll_1()
{
typedef int(*PMax)(int, int);
typedef int(*PSum)(int, int);
HINSTANCE hDll=LoadLibrary("MyDll.dll"); if(hDll == nullptr)
return; PMax Max = (PMax)GetProcAddress(hDll,"Max");
if(Max == nullptr)
return; int ret = Max(, ); cout << ret << endl; PSum Sum = (PSum)GetProcAddress(hDll,"Sum");
if(Sum == nullptr)
return; ret = Sum(, ); cout << ret << endl; FreeLibrary(hDll);
} //2. 利用DLLHelper类来简化调用
void testDll_2()
{
DllHelper dh;
dh.Load("MyDll.dll"); cout << dh.ExecuteFunc<int(int,int)>("Max", , ) << endl; //
cout << dh.ExecuteFunc<int(int,int)>("Sum", , ) << endl; //
} int main()
{
testDll_1();
testDll_2();
return ;
}
2. Lambda链式调用
(1)链式调用使用场合:在一些延迟计算的场景下较为常见,目的是将多个函数按照前一个的输出作为下一个输入串起来,然后再推迟到某个时刻计算。
(2)实现细节
①先创建一个task对象,然后连续调用Then函数,其实该函数中的lambda形参可以是任意类型,只要保证前一个函数的输出为后一个函数的输入就行。
②每调用Then函数都生成一个task,并将这些task串起来,最后在需要的时候调用Run去计算结果。
③Then函数里,会生成一个新的Lambda表达式,该lambda的功能是将参数传给上一个lambda(func)计算,并将计算结果作为参数传回给f,以供新的lambda调用。最后将这个新的lambda表达式传入新Task并保存起来,以供后面的Run调用。
④Run函数的调用过程:会将参数传给最开始的函数,计算出结果后传给第2个函数作为入参,这样一直计算到最后一个,从而得到最终结果。
//Lambda.cpp
#include <functional>
#include <iostream>
#include <type_traits>
using namespace std; template <typename T> class Task; //前向声明 template<typename R, typename...Args>
class Task<R(Args...)> //R为返回值类型,Args为参数类型
{
private:
std::function<R(Args...)> m_fn;
public:
Task(std::function<R(Args...)>&& f) : m_fn(std::move(f)) {}
Task(std::function<R(Args...)>& f) : m_fn(f) {} R Run(Args&& ...args)
{
return m_fn(std::forward<Args>(args)...); //完美转发
} //连续调用该函数,将生成一个个新的Task。相当于将函数不断地串联起来
//Then函数的返回值为Task<R(Args...)>类型,注意R表示返回值类型
template<typename F>
auto Then(F&& f)-> Task<typename std::result_of<F(R)>::type(Args...)>
{
//result_of<F(R)>:表示求参数为R类型的F函数的返回值类型,
//其中的R表示上一个函数输出类型作为本F函数的输入参数类型
using ret_type = typename std::result_of<F(R)>::type; //获取F的返回值类型 auto func = std::move(m_fn); //调用Task<ret_type(Args...)>()构造函数,并将新产生的lambda保存在新Task的m_fn中。
return Task<ret_type(Args...)>([func, f](Args...args)
{
//注意,这里不是将args直接传给f,即f(args...),而是先由前一个func(args...)计算结果作为f的入参。
return f(func(std::forward<Args>(args)...));//将前一个函数的输出作为后一个函数的输入
});
}
}; void TestTask()
{
Task<int(int)> task([](int i) {return i; });
auto ret = task.Then([](int i) {return i + ; })
.Then([](int i) {return i + ; })
.Then([](int i) {return i + ; }).Run(); //Run的延迟调用
cout << ret << endl; //
} int main()
{
TestTask();
return ;
}
第24课 可变参数模板(5)_DllHelper和lambda链式调用的更多相关文章
- 第27课 可变参数模板(8)_TupleHelper
1. TupleHelper的主要功能 (1)打印:由于tuple中的元素是可变参数模板,外部并不知道内部到底是什么数据,有时调试时需要知道其具体值,希望能打印出tuple中所有的元素值. (2)根据 ...
- 第26课 可变参数模板(7)_any和variant类的实现
1. any类的实现 (1)any类: ①是一个特殊的,只能容纳一个元素的容器,它可以擦除类型,可以将何任类型的值赋值给它. ②使用时,需要根据实际类型将any对象转换为实际的对象. (2)实现any ...
- 第25课 可变参数模板(6)_function_traits和ScopeGuard的实现
1. function_traits (1)function_traits的作用:获取函数的实际类型.返回值类型.参数个数和具体类型等.它能获取所有函数语义类型信息.可以获取普通函数.函数指针.std ...
- 第23课 可变参数模板(4)_Optional和Lazy类的实现
1. optional类的实现 (1)optional的功能 ①optional<T>的内部存储空间可能存储了T类型的值,也可能没有.只有当optional被T初始化之后,这个option ...
- 泛化之美 —— C++11 可变参数模板的妙用
概述 首先这篇文章出自博客园作者:[qicosmos ],我对本文的实例代码进行了学习.思考和整理纠正,理清了文章的全部细节,觉得这是一篇让我受益匪浅的文章.之所以会接触「可变参数模板」这部分的内容, ...
- C++反射机制:可变参数模板实现C++反射
1. 概要 本文描述一个通过C++可变参数模板实现C++反射机制的方法.该方法非常实用,在Nebula高性能网络框架中大量应用,实现了非常强大的动态加载动态创建功能.Nebula框架在Github ...
- C++ 0x 使用可变参数模板类 实现 C# 的委托机制
#ifndef _ZTC_DELEGATE_H_ #define _ZTC_DELEGATE_H_ #include <vector> #include <functional> ...
- c++11 可变参数模板类
c++11 可变参数模板类 #define _CRT_SECURE_NO_WARNINGS #include <iostream> #include <string> #inc ...
- c++11 可变参数模板函数
c++11 可变参数模板函数 #define _CRT_SECURE_NO_WARNINGS #include <iostream> #include <string> #in ...
随机推荐
- Linq中datetime的处理以及asp.net下拉列表控件的selectitem,text等的设置显示处理
dhl:报错:LINQ to Entities 不支持指定的类型成员“Date” Linq如: var v = from l in _dal.Share where l.PingcoId == pin ...
- 防止asp马后门
好多朋友都拿的有webshell吧,基本上都加了密的... 可是,没见到源码,很难测试它到底有没有后门, 指不定给别人打工了... 下面贴种很简单的方法,大家别扔蛋哈 (asp的哦) 在代码的最 ...
- 【阅读笔记】《C程序员 从校园到职场》第六章 配置文件,makefile 文件 (Part 2)
Contents: 1.配置文件(通常以 ini 结尾) 2.makefile文件 (Linux) PS: 这篇文章的内容,不太理解. 一.配置文件 本文以一个实际的小软件为例,介绍了C语言中配置文 ...
- effective java——31用实例域代替序数
1,永远不要根据枚举的序数导出与它关联的值,而是要将他保存在一个实例域中.(ordinal()) public enum Ensemble { SOLO, DUET, TRIO, QUARTET, Q ...
- oracle 汉字转拼音
oracle汉字转拼音(获得全拼/拼音首字母/拼音截取等) 效果如下: Oracle 字符集 GBK 没有问题 , UTF -8 需要修改一下 Sql代码 --oracle汉字转拼音 PACKAGE ...
- nodejs -- fs模块 ---> readFile 函数 1) fs.readFile(filename, "binary", function(error, file) 2) response.write(file, "binary");
一:代码: 1.1 入口文件: index.js var server = require('./server'); var router = require("./router" ...
- 弹性布局(Flex布局)整理
一. 弹性布局 一个好的网站都有让用户看上去很舒服的布局,一个网站的布局也会或多或少影响到它的浏览量,看完阮大神的博客,就想把弹性布局整理一下. 在平时的我们常用的布局类型有以下几种: 1.浮动+定 ...
- Windows文件夹、文件源代码对比工具--WinMerge
/********************************************************************** * Windows文件夹.文件源代码对比工具--WinM ...
- 通用base.css —— 《编写高质量代码 web前端开发修炼之道》
@charset "utf-8"; /*CSS reset*/ html{color:#000;background:#FFF;} body,div,dl,dt,dd,ul,ol, ...
- algernon 基于golang 的独立的支持redis lua pg。。。 的web server
algernon 看到github 的介绍很很强大,一下子想到了openresty,功能看着很强大,支持 redis pg lua markdown quic http2 mysql 限速 pongo ...