语法糖:萃取lambda表达式

2023-05-16

背景

现在手头主负责的服务代码,基本上都用C++11来开发了,异步编程使用的是TAF的future/promise。

future的then函数,接受的是一个Callback对象,该对象通过promise::bind来生成。

Callback和bind是参考chromium的base::Callback,base::Bind实现的,该版本并不支持C++11,所以bind() 不接受 lambda 作为 Currying 的载体(Currying ,译为柯里化是把接受多个参数的函数变换成接受一个单一参数的函数,并且返回接受余下的参数且返回结果的新函数的技术)。

使用TAF的promise进行异步编程,如果能够使用lambda表达式来完成回调,代码将更加清晰直观。

由于TAF的bind不接受 lambda表达式,因此回调有以下两种直观的方案:

方案1:使用独立的函数来产生 Callback

优点是,直观明确而且很传统不容易出错;

缺点是,对于代码量少的回调函数,要单独写一个函数,造成阅读代码时的上下文割裂。

方案2:使用 non-capturing lambda,自己手动 cast 到函数指针

优点是,代码连贯,可读性好,不需要专门去写一个函数,编码方便;

缺点是,而且手动 cast 非常的冗长繁琐。

于是对方案2进行了一些改进,通过template来对lambda表达式进行traits,将 non-capturing lambda 自动转换为等价的函数指针。

萃取lambda表达式

lambda其实是个C++对象(函数对象),只不过里面包含int operator()(int a,int b)之类的成员函数,从而可以被当做函数来使用,因此lambda表达式被当做参数来传递时,其实是传递的C++对象。

借助模板的神力、我们可以将lambda表达式的函数类型给萃取(traits)出来:

#include <iostream>
#include <typeinfo>

#include <tuple>
#include <type_traits>

namespace stx
{

    namespace lambda_detail
    {
        // 将是否is_mutable、返回类型、参数包中元素个数、参数类型萃取出来
        template<class Ret, class Cls, class IsMutable, class... Args>
        struct types
        {
            using is_mutable = IsMutable;

            enum { arity = sizeof...(Args) };

            using return_type = Ret;

            template<size_t i>
            struct arg
            {
                typedef typename std::tuple_element<i, std::tuple<Args...>>::type type;
            };
        };
    }

    // 模板参数LambdaType是lambda表达式的类型,是独有且无名的
    // 可让编译器自动推导,或者使用decltype(lamdba)推导
    // &T::operator()获得指向类成员函数的指针
    // decltype推导得到lambda表达式的函数签名
    // 利用继承,当实例化lambda_type<LambdaType>时,
    // 通过decltype推导函数签名,从而实例化下面两种特化版本中的一种
    template<class LambdaType>
    struct lambda_type
        : lambda_type<decltype(&LambdaType::operator())>
    {};

    // 特化版本,函数为非const(对应的lambda表达式为mutable)
    template<class Ret, class Cls, class... Args>
    struct lambda_type<Ret(Cls::*)(Args...)>
        : lambda_detail::types<Ret,Cls,std::true_type,Args...>
    {};

    // 特化版本,函数为const
    template<class Ret, class Cls, class... Args>
    struct lambda_type<Ret(Cls::*)(Args...) const>
        : lambda_detail::types<Ret,Cls,std::false_type,Args...>
    {};

};

int main(void){
    std::cout << "[is mutable lambda]" << std::endl;
    {
        auto test = [](int a) mutable->long{return static_cast<long>(a); };
        std::cout << "ret type : " << std::is_same<stx::lambda_type<decltype(test)>::return_type,long>::value << std::endl;
        std::cout << "arg size : " << stx::lambda_type<decltype(test)>::arity << std::endl;
        std::cout << "arg 0 type : " << std::is_same<stx::lambda_type<decltype(test)>::arg<0>::type,int>::value << std::endl;
        std::cout << "is mutable : " << std::is_same<stx::lambda_type<decltype(test)>::is_mutable,std::true_type>::value << std::endl;
    }
    std::cout << "[is normal lambda]" << std::endl;
    {
        auto test = [](int a, int b)->long{return static_cast<long>(a); };
        std::cout << "ret type : " << std::is_same<stx::lambda_type<decltype(test)>::return_type,long>::value << std::endl;
        std::cout << "arg size : " << stx::lambda_type<decltype(test)>::arity << std::endl;
        std::cout << "arg 0 type : " << std::is_same<stx::lambda_type<decltype(test)>::arg<0>::type,int>::value << std::endl;
        std::cout << "is mutable : " << std::is_same<stx::lambda_type<decltype(test)>::is_mutable,std::true_type>::value << std::endl;
    }
}

将 non-capturing lambda 自动转换为等价的函数指针,可实现如下:

template<typename T>
struct dememberize;

// C: ClassType  R: ReturnType 
// 将lambda函数对象的成员函数类型萃取出来,注意只支持非mutable、non-capturing的lambda
template<typename C, typename R, typename... Args>
struct dememberize<R(C::*)(Args...) const> {
    using type = R(*)(Args...);
};

template<typename T>
struct lambda_pointerize_impl {
    // &T::operator()取类成员函数的指针,decltype推导其函数类型
    // 通过dememberize萃取得到的函数签名作为类型lambda_pointerize_impl::type
    using type = typename dememberize<decltype(&T::operator())>::type;
};

// 取个别名
template<typename T>
using lambda_pointerize = typename lambda_pointerize_impl<T>::type;

template<typename F>
lambda_pointerize<F> lambda_decay(F lambda)
{
    // lambda_pointerize<F> 萃取得到lambda对应的函数指针类型
    // lambda_pointerize<F>(lambda)对lambda进行cast,转换为等价的函数指针
    return lambda_pointerize<F>(lambda);
}

// 结合TAF的promise使用
Future<void> mainFuture = makeFuture();
mainFuture = mainFuture.then(promise::bind(
                        lambda_decay([](const std::string& s, const Future<void> & f)
                        {
                            std::cout << s << std::endl;
                            return makeFuture();
                        }),
                        "hello"));

其实这里的lambda_decay,就是一个语法糖,使得我们不再必须为回调写一个单独的函数。

这里无法使用捕获型的lambda表达式,也正是我们想要的,如果允许捕获,在多线程环境下十分不安全。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

语法糖:萃取lambda表达式 的相关文章

随机推荐

  • 运算放大器基本运算

    转自 xff1a http www 21ic com jichuzhishi analog amplifier 2014 11 11 606654 html 运算放大器组成的电路五花八门 xff0c 令人眼花瞭乱 xff0c 是模拟电路中学
  • KEIL 注解和去注解 快捷键添加

    KEIL 注解和去注解 快捷键添加方法 xff1a 菜单栏Edit gt Configuration gt Shortcut Keys 1 例如设置 注解快捷键 xff1a Ctrl 43 2 例如设置 去注解快捷键 xff1a Ctrl
  • git、vscode免密登录

    1 git配置 git config global list 查看当前配置 git config global user name 34 xiaoyaozi 34 git config global user name 34 xiaoyao
  • 555 单稳态电路

    555 定时器成本低 xff0c 性能可靠 xff0c 只需要外接几个电阻 电容 xff0c 就可以实现多谐振荡器 单稳态 触发器及施密特触发器等脉冲产生与变换电路 它内部包括两个电压比较器 xff0c 三个5K欧姆的等值串联分压电阻 xf
  • Allegro 铺铜设置

    软件版本 xff1a Allegro16 6 敷铜 xff1a 放置禁止敷铜区域 xff1a Setup Areas Route Keepout 1 标题栏选Shap gt Global Dynamic Params Shape Polyg
  • OVP 过压保护电路

    过压保护电路 OVP 为下游电路提供保护 xff0c 使其免受过高电压的损坏 OVP电路监测外部电源 如 xff1a 离线电源或电池 的直流电压 xff0c 通过下述两种方式中的一种保护后续电路 xff1a 撬棍钳位电路或串联开关 撬棍电路
  • 超全蓝牙芯片原厂总结(含芯片型号)

    转自 xff1a https blog csdn net weixin 42583147 article details 80923946 作者 xff1a XCODER 蓝牙芯片原厂 1 CSR 高通 xff08 被高通收购 xff09
  • ST-Link的internal command error问题的解决方法

    问题 xff1a 显示 xff1a internal command error 这是由于stlink无法识别到芯片的情况 xff0c 通过解决这个问题我找到几个原因和解决方法 xff1a 1 xff0c 芯片睡眠 xff0c 停机 xff
  • 蓝牙 UUID 解释

    一 xff0c 什么是 UUID UUID 可以简单理解为编号 xff0c 唯一的编号 xff0c 用于区分不同的个体 服务和特性都有各自的UUID 比如经典的9527 UUID 就跟身份证一样 xff0c 不管是你是局长还是科长 xff0
  • 【人工智能】传教士和野人问题(M-C问题)

    摘要 本题需要解决的是一般情况下的传教士和野人问题 xff08 M C问题 xff09 通过对问题的一般化 xff0c 我们用一个三元组定义了问题的状态空间 xff0c 并根据约束条件制定了一系列的操作规则 xff0c 最后通过两个启发式函
  • 【算法设计与数据结构】为何程序员喜欢将INF设置为0x3f3f3f3f?

    在算法竞赛中 xff0c 我们常常需要用到一个 无穷大 的值 xff0c 对于我来说 xff0c 大多数时间我会根据具体问题取一个99999999之类的数 xff08 显得很不专业啊 xff01 xff09 在网上看别人代码的时候 xff0
  • 【slighttpd】基于lighttpd架构的Server项目实战(7)—http-parser

    对于http服务器 xff0c http request的解析是比较麻烦的 xff0c 由于我们的重点并不在这上面 xff0c 所以这一部分不打算自己编写 xff0c 而是使用开源的http parser库 xff0c 下面我们将使用该库来
  • select和epoll 原理概述&优缺点比较

    这个问题在面试跟网络编程相关的岗位的时候基本都会被问到 xff0c 刚刚看到一个很好的比喻 xff1a 就像收本子的班长 xff0c 以前得一个个学生地去问有没有本子 xff0c 如果没有 xff0c 它还得等待一段时间而后又继续问 xff
  • 笔记-关于神经网络黑盒模型可解释性,可视化

    原博地址 xff1a 深度学习黑盒可视化指南 xff0c 从隐藏层开始 摘 xff1a 一旦神经网络接收到相当大的所需数据集后 xff0c 该网络就会使用其精确的知识 权重 来证明或识别未知数据样本上的模式 即在经过大量数据集训练以后 xf
  • C++11 多线程 future/promise简介

    1 lt future gt 头文件简介 Classes std future std future error std packaged task std promise std shared futureFunctions std as
  • C++异步调用利器future/promise实现原理

    前言 在异步编程中 xff0c 各种回调将让人眼花缭乱 xff0c 代码分散 xff0c 维护起来十分困难 boost和C 43 43 11 的 future promise 提供了一个很好的解决方案 xff0c 使得代码更加漂亮 易维护
  • 【Heydrones】飞手百科第一篇:一定要看的无人机原理总结

    飞手百科 知识是最好的保险 本文目录 1 xff0c 无人机的飞行原理 2 xff0c 无人机的几大系统 3 xff0c 无人机的外观介绍 4 xff0c 无人机的专业术语 xff08 一 xff09 无人机的飞行原理 旋翼和轮子一样 xf
  • 【Tars】腾讯微服务框架Tars介绍

    目录 1 介绍2 设计思路3 整体架构4 平台特性 1 介绍 Tars是 基于名字服务 使用Tars协议 的高性能 RPC 开发框架 xff0c 同时配套一体化的 服务治理平台 xff0c 帮助个人或者企业快速的以微服务的方式构建自己稳定可
  • C++11常用新特性快速一览

    最近工作中 xff0c 遇到一些问题 xff0c 使用C 43 43 11实现起来会更加方便 xff0c 而线上的生产环境还不支持C 43 43 11 xff0c 于是决定新年开工后 xff0c 在组内把C 43 43 11推广开来 xff
  • 语法糖:萃取lambda表达式

    背景 现在手头主负责的服务代码 xff0c 基本上都用C 43 43 11来开发了 xff0c 异步编程使用的是TAF的future promise future的then函数 xff0c 接受的是一个Callback对象 xff0c 该对