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. C++11 boost TR1 TR2曾经参考过的网址整理

    boost::bind的用法:http://blog.csdn.net/adcxf/article/details/3970116

  2. Vue(二) 计算属性

    模板内的表达式常用于简单的运算,当过长或逻辑复杂时,难以维护,计算属性就是解决该问题的 什么是计算属性 表达式如果过长,或逻辑更为复杂,就会变得臃肿甚至难以维护,比如: <div> {{ ...

  3. 在python程序中的进程操作

    multiprocess模块 multiprocess不是一个模块而是python中一个操作.管理进程的包. 之所以叫multi是取自multiple的多功能的意思,在这个包中几乎包含了和进程有关的所 ...

  4. OSI七层模型与TCP/IP五层模型(转)

    reference:https://www.cnblogs.com/qishui/p/5428938.html         博主是搞是个FPGA的,一直没有真正的研究过以太网相关的技术,现在终于能 ...

  5. Java EntityMapper

    package org.rx.util; import org.rx.common.Func2; import org.rx.common.Action2; import org.rx.common. ...

  6. css样式问题解决

    1.关于滚动条 (1)布局后由于写了 overflow-y: scroll; 在内容还没有超出就出现了滚动条. 我的解决方法是直接去掉了滚动条: .class::-webkit-scrollbar { ...

  7. asp.net mvc + dapper(ORM框架) + easyui框架简洁的信息管理项目

    1.目录结构: 2.效果图: 3.IndexController控制器: using System; using System.Collections; using System.Collection ...

  8. 3.5 unittest生成测试报告HTMLTestRunner

    3.5 unittest生成测试报告HTMLTestRunner 前言批量执行完用例后,生成的测试报告是文本形式的,不够直观,为了更好的展示测试报告,最好是生成HTML格式的.unittest里面是不 ...

  9. 《Java编程思想》读书笔记-对象导论

    计算机是头脑延伸的工具,是一种不同类型的表达媒体.本文以背景性的和补充性的材料,介绍包括开发方法概述在内的面向对象程序设计(Object-oriented Programming,OOP)的基本概念. ...

  10. java实现四则运算应用(基于控制台)

    项目地址:https://gitee.com/wxrqforever/object_oriented_exp1.git 一.需求分析: 一个基于控制台的四则运算系统,要能实现生成并计算含有真,假分数, ...