zoukankan      html  css  js  c++  java
  • 第24课 可变参数模板(5)_DllHelper和lambda链式调用

    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
    View Code

    //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;
    }
    View Code

    //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;
    }
  • 相关阅读:
    ant
    hudson
    【消息队列MQ】各类MQ比较
    日本出行
    使用Eclipse Installer安装Eclipse
    Firebug控制台详解
    浮动
    CSS3
    HTML制作视频简介
    使用I/O流复制指定文件
  • 原文地址:https://www.cnblogs.com/5iedu/p/7823607.html
Copyright © 2011-2022 走看看