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. 数据结构算法之冒泡排序——Java语言实现

    今天来谈下冒泡排序算法,这次实现由两种形式如下所示: 1.对于长度为N的数据序列,没有加标签限制,针对一开始就是有序的数据序列,仍然需要排序N-1趟来完成排序. 2.对于长度为N的数据序列,加标了签限 ...

  2. Debian/Ubuntu/Deepin下AndroidStudio2/3打开AVD模拟器无反应

    Debian系AS无法启动模拟器 问题描述 早在半年前将开发环境从windows迁移到了Linux:当时用的是Debian系统,也是在安装完成AndroidStudio之后无法开启模拟器,也没出现什么 ...

  3. Crontab中的除号(slash)到底怎么用?(转载)

    转载于:https://www.cnblogs.com/cocowool/p/5865397.html crontab 是Linux中配置定时任务的工具,在各种配置中,我们经常会看到除号(Slash) ...

  4. vuex实现原理

    一.Store的层次结构 Store,负责管理整个数据访问.修改等: 提高API: State,数据结构: 所有的getters.mutations,全部都注册到store里:结构大概是: { 'xx ...

  5. Python使用MySQL数据库(新)(转)

    http://www.cnblogs.com/fnng/p/3565912.html 一,安装mysql 如果是windows 用户,mysql 的安装非常简单,直接下载安装文件,双击安装文件一步一步 ...

  6. R语言安装xlsx包,读入excel表格

    开学的时候,男神给了数据(.xlsx格式)让用R语言分析分析,作为编程小白,读了一天都没读近R,更别提如何分析了. 现在小伙伴们都喜欢读txt 和csv格式的,好多xlsx的表格读不进R,将xlsx格 ...

  7. L333 Should You Listen to Music While You Work?

    Should You Listen to Music While You Work? "Whistle while you work" is classic advice, str ...

  8. linux重启查看日志及历史记录 查询原因

    linux重启查看日志及历史记录 查询原因 linux系统文件通常在/var/log中下面是对下面常出现的文件进行解释 /var/log/message ----------------------- ...

  9. requests模块的使用

    requests模块 什么是request模块:requests是python原生一个基于网络请求的模块,模拟浏览器发起请求. requests-get请求 # get请求 import reques ...

  10. 2018.5.2 file结构体

    f_flags,File Status Flag f_pos,表示当前读写位置 f_count,表示引用计数(Reference Count): dup.fork等系统调用会导致多个文件描述符指向同一 ...