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链式调用的更多相关文章

  1. 第27课 可变参数模板(8)_TupleHelper

    1. TupleHelper的主要功能 (1)打印:由于tuple中的元素是可变参数模板,外部并不知道内部到底是什么数据,有时调试时需要知道其具体值,希望能打印出tuple中所有的元素值. (2)根据 ...

  2. 第26课 可变参数模板(7)_any和variant类的实现

    1. any类的实现 (1)any类: ①是一个特殊的,只能容纳一个元素的容器,它可以擦除类型,可以将何任类型的值赋值给它. ②使用时,需要根据实际类型将any对象转换为实际的对象. (2)实现any ...

  3. 第25课 可变参数模板(6)_function_traits和ScopeGuard的实现

    1. function_traits (1)function_traits的作用:获取函数的实际类型.返回值类型.参数个数和具体类型等.它能获取所有函数语义类型信息.可以获取普通函数.函数指针.std ...

  4. 第23课 可变参数模板(4)_Optional和Lazy类的实现

    1. optional类的实现 (1)optional的功能 ①optional<T>的内部存储空间可能存储了T类型的值,也可能没有.只有当optional被T初始化之后,这个option ...

  5. 泛化之美 —— C++11 可变参数模板的妙用

    概述 首先这篇文章出自博客园作者:[qicosmos ],我对本文的实例代码进行了学习.思考和整理纠正,理清了文章的全部细节,觉得这是一篇让我受益匪浅的文章.之所以会接触「可变参数模板」这部分的内容, ...

  6. C++反射机制:可变参数模板实现C++反射

    1. 概要   本文描述一个通过C++可变参数模板实现C++反射机制的方法.该方法非常实用,在Nebula高性能网络框架中大量应用,实现了非常强大的动态加载动态创建功能.Nebula框架在Github ...

  7. C++ 0x 使用可变参数模板类 实现 C# 的委托机制

    #ifndef _ZTC_DELEGATE_H_ #define _ZTC_DELEGATE_H_ #include <vector> #include <functional> ...

  8. c++11 可变参数模板类

    c++11 可变参数模板类 #define _CRT_SECURE_NO_WARNINGS #include <iostream> #include <string> #inc ...

  9. c++11 可变参数模板函数

    c++11 可变参数模板函数 #define _CRT_SECURE_NO_WARNINGS #include <iostream> #include <string> #in ...

随机推荐

  1. 一次完整的http事务的过程

    1.域名解析 2.发起TCP三次握手 3.建立TCP连接以后发起http请求 4.服务器端响应请求,浏览器得到html代码 5.浏览器解析html代码并请求html中的资源 6.浏览器对页面进行渲染呈 ...

  2. mysql中关于关联索引的问题——对a,b,c三个字段建立联合索引,那么查询时使用其中的2个作为查询条件,是否还会走索引?

    情况描述:在MySQL的user表中,对a,b,c三个字段建立联合索引,那么查询时使用其中的2个作为查询条件,是否还会走索引? 根据查询字段的位置不同来决定,如查询a,     a,b    a,b, ...

  3. 数组去重复的时候遇到length变成1 的bug

    arrUnique:function(arr){ //传入空数组的时候有bug,length会变成1 所以修复下 if(arr.length == 0){ return []; } arr.sort( ...

  4. day11_python_1124

    01 昨日内容回顾 函数名的运用: 1,特殊的变量. 2,函数名可以当做变量赋值. 3,函数名可以当做容器类类型的元素. 4,函数名可以当做函数的参数. 5,函数名可以当做函数的返回值. 闭包: 1, ...

  5. 一月分四周的JAVA实现方法

    需求:给定任意一个月,如何按照中国周的习惯,把一个月分成四个时间段 (1)以自然周为划分依据 (2)不能跨月 (3)把首尾自然周,天数较少的合并到其最近的自然周里面 (4)最后结果应该是吧一个月分成四 ...

  6. js--------1.时间

    //获取当前时间 yyyy-MM-dd function getNowFormatDate() { var date = new Date(); var seperator1 = "-&qu ...

  7. faker模块

    faker模块中有很多函数,可以直接生成手机号,身份证号,姓名等 1.安装faker pip install faker 2.faker的使用 from faker import Faker f=Fa ...

  8. python+flask开发小白第二天

    使用VSCode编译python web页面 1.先从最基础的说起吧,关于VSCode的使用: 运行python程序与运行java,c,c++程序一样,需要新建一个文件,第一个文件建议不要新建在本地的 ...

  9. CDMA码片序列问题

    要想知道到底是怎么算的 建议看见这篇博客的任何一位去先看一下这篇博客:https://blog.csdn.net/dog250/article/details/6420427 在CDMA中.每一个比特 ...

  10. Linux下不停止服务,清空nohup.out文件

    转载:http://www.sucheasy.com/OracleFusionMiddleware/640.html 1.nohup.out的由来及作用 用途:LINUX命令用法,不挂断地运行命令. ...