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. dubbo 异步回调

    dubbo 异步回调的使用 业务接口: public interface HelloService { String sayHello(); void sayHi(String name); } 回调 ...

  2. 为什么会出现container、injection技术?发展历史及未来发展趋势

    container 原因: 随着软件开发的发展,相比于早期的集中式应用部署方式,现在的应用基本都是采用分布式的部署方式,一个应用可能包含多种服务或多个模块,因此多种服务可能部署在多种环境中,如虚拟服务 ...

  3. 禁用cookie后的方法

    保存session id的方式可以采用cookie,这样在交互过程中浏览器可以自动的按照规则把这个标识发送给 服务器.一般这个cookie的名字都是类似于SEEESIONID.但cookie可以被人为 ...

  4. MapServer Tutorial——MapServer7.2.1教程学习——第一节用例实践:Example1.2 Static Map with Two Layers

    MapServer Tutorial——MapServer7.2.1教程学习——第一节用例实践:Example1.2 Static Map with Two Layers 一.前言 上一篇博客< ...

  5. java使用StringBuilder的方法反转字符串输出

    public class FanZhuan { public static void main(String[] args) { /**第一种方法*/ String s = "9876543 ...

  6. java输入最大10位数,倒数输出(很鸡肋)

    public class D { public static void main(String[] args) { System.out.println("请输入数字(最大十位数):&quo ...

  7. 简单易懂的 Vue.js 基础知识 !

    根 vue 实例 let viewModel = new Vue({ // 包含数据.模板.挂载元素.方法.生命周期钩子等选项 }) Hello Wrold  <!-- 这是我们的 View - ...

  8. VC++、MFC Sqlite3数据库的使用

    SQLite数据库是一种本地的轻型数据库,在存储一些本地的数据的时候,或者不需要用到Oracle,SQL2008之类的大型数据库的时候,Sqlite的优势就能够得到发挥.程序需要采集数据存储起来,可以 ...

  9. 剑指Offer 15. 反转链表 (链表)

    题目描述 输入一个链表,反转链表后,输出新链表的表头. 题目地址 https://www.nowcoder.com/practice/75e878df47f24fdc9dc3e400ec6058ca? ...

  10. spring ico

    代码非常简单.如果缺少jar包将导致报错. 需要5个spring jar包和1个logging jar,否则报错. org.springframework.asm.jarorg.springframe ...