cpp11利用宏、模板以及lambda表达式实现python like的装饰器语义(更新)

【CPP】【函数装饰器宏】cpp11利用宏、模板以及lambda表达式实现python like的装饰器语义

前言

python函数的装饰器是一个轻量级的函数功能拓展和复用工具,相比传统的派生重写虚函数的方式来说,避免了继承带来的耦合负担,控制也相对更加的简单,但cpp并没有相关的原生关键字支持,我参考了一些知乎和CSDN上的文章,但始终没有达到我想要的效果于是准备自己也动动手。
cpp11的lambda表达式提供了最基本的特性,基于此特性,我实现了类似语义的宏,跟python的函数装饰器相比创建形式并不相同,但是功能是相似的。
我事先在CSDN上发了一版,这一版是更新优化后的版本,装饰语法更加简洁,并且提供了cpp11兼容void返回值的解决方案。

用法

style1

int CCar::oilfeed(int degress)
DECORATE(logging,,
DECORATE(novoid,ARGS(),//对于装饰器的使用来说,如果没有参数,那么需要一个ARGS()占位或留空,留空看上去就是两个连续的','
DECORATE(maybe_void,,
//使用UNDECORATE宏可以单点禁用一个装饰器,原理是去掉了该层的 [&](){}包装器并悬空checkRotateSpeed以及ARGS里面的东西只保留代码块
UNDECORATE(checkRotateSpeed, ARGS(this, __FUNCTION__), //传递多个参数
{
    m_n_rotat_speed += degress;
    return m_n_rotat_speed;
})))) //用了多少个装饰器后面就要多少个),没办法cpp只能这样了,想不出更好的办法,本质是宏嵌套

style2

void CCar::brake(int degress)
DECORATE(logging, ARGS(__FUNCTION__),
//DECORATE(novoid,ARGS(), 使用novoid装饰器无法直接通过编译,因为不支持 void 作为返回值的偏特化
DECORATE(maybe_void,,
//推荐用仿函数的方式实现装饰器,通过构造函数传参封装性更好,如果舍弃函数类型的装饰器的话,就可以少一个宏参数占位了不用打奇怪的两个逗号了,
DECORATE(CheckRotateSpeed(this, __FUNCTION__),,
{
    m_n_rotat_speed -= degress;
})))

msvc2015 x64执行结果

完整测试代码

decorator.h

#ifndef DECORATOR_H
#define DECORATOR_H
#include <functional>


template<bool T>
//!
//! \brief The bool_trait struct
//! \usecase bool_trait<静态表达式->bool> 用于模拟模板偏特化
//!
struct bool_trait{};

template <typename T>
class func_caller final
{
public:
    using return_type = typename std::result_of<T&()>::type;
    using void_trait = bool_trait<std::is_same_v<void, return_type>>;

    explicit func_caller(T const &func)
        : stack(nullptr)
    {
        init(void_trait());
        call(func, void_trait());
    }

    ~func_caller()
    {
        release(void_trait());
    }

    return_type ret() const
    {
        return ret(void_trait());
    }

private:

    void* operator new(std::size_t) = delete;

    func_caller(func_caller const &) = delete;

    func_caller& operator=(func_caller const &) = delete;

    void init(bool_trait<true> const&)
    {
    }

    void init(bool_trait<false> const&)
    {
        stack = new return_type;
    }

    void release(bool_trait<true> const&)
    {

    }

    void release(bool_trait<false> const&)
    {
         delete stack;
    }

    void call(T &func, bool_trait<true> const&)
    {
        func();
    }

    void call(T const &func, bool_trait<false> const&)
    {
        *stack = std::move(func());//尽量调用移动构造函数
    }

    void ret(bool_trait<true> const&) const
    {

    }

    return_type ret(bool_trait<false> const&) const
    {
        return *stack;
    }

    return_type* stack;
};

//打包参数的宏,仿函数装饰器可以不使用
#define ARGS(...)\
    ,__VA_ARGS__

//启用装饰器语义宏定义
#ifndef NO_DECORATOR

//装饰宏,其实就是用 [&](){}把代码块包起来
#define DECORATE(decorator, args, codeblock) \
{\
    return decorator( [&]() codeblock args);\
    }
//单独禁用某个装饰器
#define UNDECORATE(decorator, args, codeblock) \
    codeblock
#else

//禁用所有装饰器
#define DECORATE(decorator, args, codeblock) \
    codeblock
#define UNDECORATE(decorator, args, codeblock) \
    codeblock
#endif // NO_DECORATOR

//隐藏模板语义,定义一个装饰器模板函数
#define DEF_DECIRATOR(name, .../*args*/) \
    template <typename T>\
    auto name(T const &func, __VA_ARGS__)

//实现临时快速禁用一个装饰器模板函数的功能,修改原装饰器为直接回调,然后将原本的逻辑添加到一个起始参数为void*的同名函数里面
#define DIS_DECIRATOR(name, .../*args*/) \
    template <typename T>\
    auto name(T const &func, __VA_ARGS__) \
    { return func(); }\
    template <typename T>\
    auto name(void *, T const &func, __VA_ARGS__)

//简化func_caller的使用方法
#define CALL() \
    func_caller<decltype(func)>(func)

#endif // DECORATOR_H

rm_decorator.h

#ifndef RM_DECORATOR_H
#define RM_DECORATOR_H

//禁用所有装饰器相关宏
#undef ARGS
#undef DECORATE
#undef UNDECORATE
#undef CALL
#undef DEF_DECIRATOR
#undef DIS_DECIRATOR
#endif // RM_DECORATOR_H

main.cpp

#include <functional>
#include <iostream>
#include <chrono>
#include <string>
//#define NO_DECORATOR
#include "base/decorator.h"
class CCar{
public:
    explicit CCar(){

    }

    int oilfeed(int degress);

    void brake(int degress);
    int rotat_speed() const {return m_n_rotat_speed;}
private:
    int m_n_rotat_speed = 2000;
};

/**
 * 注:此装饰器原理实为创建引用捕获的lambda表达式并调用,所以性能肯定会有所下降,但是增加了可读性,减少了维护难度
**/

//用原汁原味的写法定义装饰器函数,但是不能用过宏快速禁用,因为快速禁用实际是用重载实现的,所以只能增加代码分支进行控制如下面的写法
template <typename T>
auto checkRotateSpeed(T const &func/*第一个参数必须为函数*/, CCar const *obj, std::string const &funcname)
{
#if 1
    std::cout << "checkRotateSpeed before " << funcname  << ", rotat_speed:" << obj->rotat_speed()  << std::endl;
    auto const &caller = func_caller<decltype(func)>(func);//用func_caller模板来处理返回值为void的情况,缺点是非void返回值会多一些内存拷贝,所以条件允许的话对于大对象尽量使用智能指针作为返回值
    std::cout << "checkRotateSpeed after "  << funcname  << ", rotat_speed:" << obj->rotat_speed() << std::endl;
    return caller.ret();
#else
    return func();
#endif
}

//如果不用考虑返回值为 void,装饰器函数直接这么写就可以了,即简单性能又好
template <typename T>
auto novoid(T const &func)
{
    std::cout << "novoid before " << std::endl;
    auto const &ret = func();//直接调用被包装的函数获取结果,不用再返回一个包装器了是多此一举,包装器我是用装饰宏 DECORATE 封装了 [&]() 语句实现的,从这点看比python要简洁
    std::cout << "novoid after " << std::endl;
    return ret;
}

DEF_DECIRATOR //用宏语义快速定义装饰器函数
//DIS_DECIRATOR //快速禁用装饰器函数,原理为自动替换实现为 return func();
(logging, std::string const &funcname = "unknow func", int others = 0)
{
    (void) others;
    //在调用被装饰函数之前的操作
    std::cout <<"log before " << funcname  << std::endl;
    auto start = std::chrono::high_resolution_clock::now();
    auto const &caller = CALL();//使用CALL宏精简代码 func_caller<decltype(func)>(func)
    auto const &end = std::chrono::high_resolution_clock::now();
    std::chrono::duration<double> time_span = std::chrono::duration_cast<std::chrono::duration<double>>(end - start);
    std::cout << "log after " << funcname << ": elapsed time(s): " << time_span.count() << std::endl;
    return caller.ret();
}

//兼容 void 返回值,不用 func_caller 的高性能的写法,考虑兼容c++11的话缺点是要把实现拷贝一份,有代码冗余不好维护,而且比较复杂要用一些奇技淫巧
//如果您的环境是c++17就不用看这个 maybe_void 了,直接用if constexpr吧
template<typename T>
auto maybe_void(T const &func)
{
    using ReturnType = typename std::result_of<T&()>::type;
    using IsVoidReturn = bool_trait<std::is_same_v<void, ReturnType>>;
    return maybe_void(func, IsVoidReturn());
}

using ReturnIsVoid = bool_trait<true>;
using ReturnIsntVoid = bool_trait<false>;

// 对于非void返回值的重载
template<typename T>
auto maybe_void(T const &func, ReturnIsntVoid const &)
{
    std::cout << "maybe_void before " << std::endl;
    auto const &ret = func();
    std::cout << "maybe_void after " << std::endl;
    return ret;
}

// 对于void返回值的重载
template<typename T>
void maybe_void(T const &func, ReturnIsVoid const &)
{
    std::cout << "maybe_void before " << std::endl;
    func();
    std::cout << "maybe_void after " << std::endl;
}

//定义一个仿函数装饰器,无论是函数装饰器还是仿函数装饰器,第一个参数都得是std::function<T ()> 类型否则装饰器宏无法正常展开
class CheckRotateSpeed
{
public:
    CheckRotateSpeed(CCar const *obj, std::string const &funcname)
        :m_p_obj(obj)
        ,m_str_funcname(funcname)
    {

    }

    //仿函数同样适用快速定义或禁用宏,模板的风格请参考 novoid 和 novoid_
    DEF_DECIRATOR
    //DIS_DECIRATOR
    (operator ())
    {
        //在调用被装饰函数之前的操作
        std::cout << "checkRotateSpeed before " << m_str_funcname  << ", rotat_speed:"<< m_p_obj->rotat_speed()  << std::endl;
        auto const &caller = CALL();
        //在调用被装饰函数之前的操作
        std::cout << "checkRotateSpeed after " << m_str_funcname  << ", rotat_speed:"<< m_p_obj->rotat_speed()  << std::endl;
        return caller.ret();
    }

private:

    CCar const *m_p_obj;
    std::string m_str_funcname;
};


int CCar::oilfeed(int degress)
DECORATE(logging,,
DECORATE(novoid,ARGS(),//对于装饰器的使用来说,如果没有参数,那么需要一个ARGS()占位或留空,留空看上去就是两个连续的','
DECORATE(maybe_void,,
//使用UNDECORATE宏可以单点禁用一个装饰器,原理是去掉了该层的 [&](){}包装器并悬空checkRotateSpeed以及ARGS里面的东西只保留代码块
UNDECORATE(checkRotateSpeed, ARGS(this, __FUNCTION__), //传递多个参数
{
    m_n_rotat_speed += degress;
    return m_n_rotat_speed;
})))) //用了多少个装饰器后面就要多少个),没办法cpp只能这样了,想不出更好的办法,本质是宏嵌套

void CCar::brake(int degress)
DECORATE(logging, ARGS(__FUNCTION__),
//DECORATE(novoid,ARGS(), 使用novoid装饰器无法直接通过编译,因为不支持 void 作为返回值的偏特化
DECORATE(maybe_void,,
//推荐用仿函数的方式实现装饰器,通过构造函数传参封装性更好,如果舍弃函数类型的装饰器的话,就可以少一个宏参数占位了不用打奇怪的两个逗号了,
DECORATE(CheckRotateSpeed(this, __FUNCTION__),,
{
    m_n_rotat_speed -= degress;
})))

//移除decorator相关宏,如果后文遇到重名的宏的话
#include "base/rm_decorator.h"

int main(int , char *[])
{
    CCar car;
    car.oilfeed(45);
    car.brake(1000);
    return 0;
}

各位如果觉得有用,请把 “有用” 打到评论区🤪

  • 5
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值