• 第25课 可变参数模板(6)_function_traits和ScopeGuard的实现


    1. function_traits

    (1)function_traits的作用:获取函数的实际类型、返回值类型、参数个数和具体类型等。它能获取所有函数语义类型信息。可以获取普通函数、函数指针、std::function、函数对象和成员函数等的相关信息。

    (2)实现function_traits的关键技术

      ①通过模板特化可变参数模板来实现。

      ②针对成员函数和仿函数的特化版本需要注意const和volatile版本的定义。

      ③function_traits函数的入参是可变参数模板,其类型和个数都是任意的,要获取指定位置的类型,可以通过std::tuple_element<N, std::tuple<Args…>>::type来获取。

    【编程实验】

    //function_traits.hpp

    #ifndef _FUNCTION_TRAITS_H_
    #define _FUNCTION_TRAITS_H_
    
    #include <functional>
    #include <tuple>
    
    //普通函数
    //函数指针
    //function/lambda
    //成员函数
    //函数对象
    
    template<typename T>
    struct function_traits;   //前向声明
    
    //普通函数
    template<typename Ret, typename... Args>
    struct function_traits<Ret(Args...)>
    {
    public:
        enum {arity = sizeof...(Args)};//arity : 参数的数量
    
        //函数别名
        typedef Ret function_type(Args...); //<==> using function_type = Ret(Args...);
        
        typedef Ret return_type; //返回值类型
        using stl_function_type = std::function<function_type>;
        typedef Ret(*pointer)(Args...);
        
        //获取可变参数模板中第I个位置的参数类型。
        template<size_t I, class = typename std::enable_if<(I<arity)>::type>
        using args = typename std::tuple_element<I, std::tuple<Args...>>;
    };
    
    //函数指针
    template<typename Ret, typename... Args>
    struct function_traits<Ret(*)(Args...)> : function_traits<Ret(Args...)>{};
    
    //std::function
    template<typename Ret, typename... Args>
    struct function_traits<std::function<Ret(Args...)>> : function_traits<Ret(Args...)>{};
    
    //成员函数
    #define FUNCTION_TRAITS(...)   
    template <typename ReturnType, typename ClassType, typename... Args>  
    struct function_traits<ReturnType(ClassType::*)(Args...) __VA_ARGS__> : function_traits<ReturnType(Args...)>{};
    
    FUNCTION_TRAITS()
    FUNCTION_TRAITS(const)   //const成员函数
    FUNCTION_TRAITS(volatile)
    FUNCTION_TRAITS(const volatile)
    
    //函数对象
    template<typename Callable>
    struct function_traits :  function_traits<decltype(&Callable::operator())>{};
    
    //将lambda转为std::function
    template<typename Function>
    typename function_traits<Function>::stl_function_type
    to_function(const Function& lambda)
    {
        return static_cast<typename function_traits<Function>::stl_function_type>(std::forward<Function>(lambda));
    }
    
    template<typename Function>
    typename function_traits<Function>::stl_function_type
    to_function(Function&& lambda)
    {
        return static_cast<typename function_traits<Function>::stl_function_type>(lambda);
    }
    
    //将lambda转为函数指针,如
    template<typename Function>
    typename function_traits<Function>::pointer
    to_function(const Function& lambda)
    {
        //    typedef int(*FUN)(int);
        //    auto f = FUN([](int x){return x + 10;});
        //  cout << f(10) << endl;  //20
        return static_cast<typename function_traits<Function>::pointer>(lambda);
    }
    
    #endif  //_FUNCTION_TRAITS_H_

    //test_function_traits.cpp

    #include <iostream>
    #include <typeinfo>
    #include "function_traits.hpp"
    
    using namespace std;
    
    template<typename T>
    void PrintType()
    {
        cout << typeid(T).name() << endl;
    }
    
    float (*castfunc)(string, int);
    float free_function(const string& a, int b)
    {
        return (float) a.size() / b;
    }
    
    struct Test
    {
        int func(int a, int b) volatile
        {
            return a + b;
        }
        
        int operator()(int) const
        {
            return 0;
        }
    };
    
    void TestFunctionTraits()
    {
        std::function<int(int)> f = [](int a){return a;};
        PrintType<function_traits<std::function<int(int)>>::function_type>();  //FiiE
        PrintType<function_traits<std::function<int(int)>>::args<0>::type>();    //i
        PrintType<function_traits<decltype(f)>::function_type>();
        PrintType<function_traits<decltype(free_function)>::function_type>();
        
        PrintType<function_traits<decltype(castfunc)>::function_type>();
        
        PrintType<function_traits<Test>::function_type>(); //FiiE, int operator()(int)
        using T = decltype(&Test::func);
        PrintType<T>();  //M4TestVFiiiE
        
        PrintType<function_traits<decltype(&Test::func)>::function_type>(); //FiiiE
        
        cout << std::is_same<function_traits<decltype(f)>::return_type, int>::value << endl; //1
    }
    
    int main()
    {
        typedef int(*FUN)(int);
        auto f = FUN([](int x){return x + 10;});
        cout << f(10) << endl;
        
        TestFunctionTraits();
        
        return 0;
    }
    /*
    //g++测试结果
    E:StudyC++1125>g++ -std=c++11 test_function_traits.cpp
    E:StudyC++1125>a.exe
    20
    FiiE
    i
    FiiE
    FfRKNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEEiE
    FfNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEEiE
    FiiE
    M4TestVFiiiE
    FiiiE
    1
    
    //VC2015测试结果:
    e:StudyC++1125>cl test_function_traits.cpp /EHsc
    e:StudyC++1125>test_function_traits.exe
    20
    int __cdecl(int)
    int
    int __cdecl(int)
    float __cdecl(class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > const &,int)
    float __cdecl(class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >,int)
    int __cdecl(int)
    int (__thiscall Test::*)(int,int)volatile
    int __cdecl(int,int)
    1
    */

    2. ScopeGuard的实现

    (1)ScopeGuard的作用:

      ①确保资源在非正常返回时总能被成功释放。(如函数在中途提前返回,或者中途抛异常而返回)

      ②ScopeGuard是RAII的一种泛化实现。它与RAII的不同是ScopeGuard只关注清理的部分——资源申请你来做,ScopeGuard帮你清除。清理(回收)资源有很多办法,如调用一个函数/仿函数,调用一个对象的成员函数。它们都可能需要0个,1个或多个参数。

    (2)实现ScopeGuard的关键技术:

      ①通过局部变量析构函数来管理资源,根据是否正常退出来确定是否需要清理资源。

      ②通过m_dismiss标志来决定是否执行清除操作。

    【编程实验】ScopeGuard的实现

    //ScopeGuard.hpp

    #ifndef _SCOPE_GUARD_H_
    #define _SCOPE_GUARD_H_
    
    #include <iostream>
    using namespace std;
    
    template <typename F>
    class ScopeGuard
    {
       F m_func; //异常处理函数
       bool m_dismiss; //正常退出为true,非正常退出时为false
       
       ScopeGuard();
       ScopeGuard(const ScopeGuard&);
       ScopeGuard& operator=(const ScopeGuard&);
    public:
        void dismiss()
        {
            m_dismiss = true;
        }
        
        explicit ScopeGuard(F&& f) : m_func(f), m_dismiss(false){}
        explicit ScopeGuard(const F& f) : m_func(f), m_dismiss(false){}
        
        ScopeGuard(ScopeGuard&& rhs) : m_func(std::move(rhs.m_func)),
                                       m_dismiss(rhs.m_dismiss)
        {
            rhs.dismiss();
        }
        
        ~ScopeGuard()
        {
            if(!m_dismiss && m_func != nullptr){
                m_func();
            }
        }
    };
    
    //辅助函数
    template<typename Function>
    ScopeGuard<typename std::decay<Function>::type> MakeGuard(Function&& f)
    {
        //调用ScopeGuard(const F& f)或ScopeGuard(F&& f)
        return ScopeGuard<typename std::decay<Function>::type>(std::forward<Function>(f)); 
    }
    
    #endif

    //test_ScopeGuard.cpp

    #include <iostream>
    #include <functional>
    #include <exception>
    #include "ScopeGuard.hpp"
    
    using namespace std;
    
    void TestScopeGuard()
    {
        //资源清理函数
        std::function<void()> f = []{cout << "clean up from unnormal exit" << endl;};
        
        //正常退出
        {
            auto gd = MakeGuard(f);
            
            //... 在MakeGuard和dismiss之间的操作是异常安全的。一旦出现导常,导致gd在
            //    离开作用域时,会调用传入在MakeGuard中注册的清理函数进行释放资源。
            
            gd.dismiss(); //解除ScopeGuard
        }
        
        //异常退出
        {
            auto gd = MakeGuard(f);
            
            //... 其它操作,假设在这里出现异常。由于这里的代码被MakeGuard保护
            //所以异常发生时,仍会调用清理函数来正确释放资源
            throw std::exception(std::out_of_range("excetion occur!"));
            
            gd.dismiss(); 
        }
        
        //非正常退出
        {
            auto gd = MakeGuard(f);
            
            return;
            
            gd.dismiss(); 
        }    
    }
    
    int main()
    {
        TestScopeGuard();
        return 0;
    }
  • 相关阅读:
    使用kindeditor获取不到富文本框中的值
    ASP.NET中的文件操作(文件信息,新建,移动,复制,重命名,上传,遍历)
    2018 遇到selenium.common.exceptions.WebDriverException问题
    DataTable与实体类互相转换
    反射load,loadfile,LoadFrom区别
    DbSet.Attach(实体)与DbContext.Entry(实体).State = EntityState.Modified 区别
    .net 面试总结
    C# Linq 常用查询操作符
    windows下nginx的安装及使用
    熊猫热土-环汶川50公里越野赛赛记
  • 原文地址:https://www.cnblogs.com/5iedu/p/7853337.html
Copyright © 2020-2023  润新知