第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:\Study\C++11\25>g++ -std=c++11 test_function_traits.cpp
E:\Study\C++11\25>a.exe
20
FiiE
i
FiiE
FfRKNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEEiE
FfNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEEiE
FiiE
M4TestVFiiiE
FiiiE
1

//VC2015测试结果:
e:\Study\C++11\25>cl test_function_traits.cpp /EHsc
e:\Study\C++11\25>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;
}

原文链接: https://www.cnblogs.com/5iedu/p/7853337.html

欢迎关注

微信关注下方公众号,第一时间获取干货硬货;公众号内回复【pdf】免费获取数百本计算机经典书籍;

也有高质量的技术群,里面有嵌入式、搜广推等BAT大佬

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

原创文章受到原创版权保护。转载请注明出处:https://www.ccppcoding.com/archives/392835

非原创文章文中已经注明原地址,如有侵权,联系删除

关注公众号【高性能架构探索】,第一时间获取最新文章

转载文章受原作者版权保护。转载请注明原作者出处!

(0)
上一篇 2023年4月3日 下午3:00
下一篇 2023年4月3日 下午3:00

相关推荐