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(5, 8); cout << ret << endl; PSum Sum = (PSum)GetProcAddress(hDll,"Sum"); if(Sum == nullptr) return; ret = Sum(5, 8); cout << ret << endl; FreeLibrary(hDll); } //2. 利用DLLHelper类来简化调用 void testDll_2() { DllHelper dh; dh.Load("MyDll.dll"); cout << dh.ExecuteFunc<int(int,int)>("Max", 5, 9) << endl; //9 cout << dh.ExecuteFunc<int(int,int)>("Sum", 4, 8) << endl; //12 } int main() { testDll_1(); testDll_2(); return 0; }
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 + 1; }) .Then([](int i) {return i + 2; }) .Then([](int i) {return i + 3; }).Run(1); //Run的延迟调用 cout << ret << endl; //7 } int main() { TestTask(); return 0; }