Qt QtPromise源码剖析
Prise概念
Prise是一种异步编程的解决方案.
Prises 是用于传递异步计算结果的回调的替代方法.
QtPrise开源模板库
使用Qt框架的朋友如果对异步编程有需求,建议可使用此模板库对异步操作做处理。
下文对QtPrise模板库的源码做一些分析以及其所用到的一些CPP的技巧共同做些探讨。
本文并不会教你如何更好的使用QPrise而是分享其内部实现的思想和流程。
QtPrise模板库中所使用的设计模式
1. QtPrise中的构建模式
a. 一般我们在构造复杂对象时为了简化初始化通常会把多个成员属性拆分成多个set/with成员方法来用于构造对象。
b. 同样为了让QPrise的调用更加符合Prise风格和达到链式调用的目的。QPrise类对外接口也都是基于此模式的变种。
QPrise 父类QPriseBase源码示例:
1 // 摘自QPrise类的部分成员函数声明 2 template<typename T> 3 class QPriseBase ) const; 27 28 template<typename E = QPriseTimeoutException> 29 inline QPrise<T> timeout(std::chrono::milliseconds msec, E&& error = E) const; 30 31 inline QPrise<T> delay(int msec) const; 32 inline QPrise<T> delay(std::chrono::milliseconds msec) const; 33 34 inline QPrise<T> wait() const; 35 };我们看到QPriseBase大部分函数都返回了QPrise实例对象符合构建模式的思想但与构建模式又存在一定区别,构建模式的设置方法是作用于同一对象且返回的也是同一对象。 但QPriseBase中每次返回是一个新的Prise实例对象。
2. QtPrise中的装饰模式
a. 业务逻辑请使用组合方式来实现。 摘自微服务架构设计模式一书
组合模式带来的好处:
类型间更弱的耦合
运行期灵活性更好,能够根据业务需要随时替换组合对象
对于客户代码更加友好
b. 基于PriseResolver的装饰
我们先来看看PriseResolver类的源码
1 template<typename T> 2 class PriseResolver 3 } 6 ; 8 } 9 10 template<typename E> 11 void reject(E&& error) 12 20 } 21 22 void reject() 23 ); 28 prise>m_d>dispatch(); 29 release(); 30 } 31 } 32 33 template<typename V> 34 void resolve(V&& value) 35 43 } 44 45 void resolve() 46 54 } 55 56 private: 57 struct Data : public QSharedData 58 ; 61 62 QExplicitlySharedDataPointer<Data> m_d; 64 void release() 65 71 }; 72 73 // 74 75 template<class T> 76 class QPriseResolve 77 80 83 84 85 ~QPriseResolve() 88 89 template<typename V> 90 void operator()(V&& value) const 91 94 95 void operator()() const 96 97 private: 98 mutable QtPrisePrivate::PriseResolver<T> m_resolver; 99 }; 100 101 template<class T> 102 class QPriseReject 103 106 107 108 template<typename E> 109 void operator()(E&& error) const 110 113 114 void operator()() const 115 116 private: 117 mutable QtPrisePrivate::PriseResolver<T> m_resolver; 118 };从源码分析我们知道PriseResolver的功能比较简单单一,持有一个QPrise实例, 对客户代码提供操作成功和失败的方法进而把外部操作结果转发到持有的prise实例内部处理。
QPrise为了达到更加简单易用的方式(更加符合现代的函数式编程范式)以及语义上更加符合Prise风格,继续定义了QPriseResolve&QPriseReject类用来单独处理成功和失败的情况。 我们看这三者类并不是教科书上的标准的装饰模式, 但实际上QPriseResolve&QPriseReject类的实现组合PriseResolver实例, 重载调用()运算符(使其成为一个可调用对象)内部转发对PriseResolver的调用基本符合了装饰模式的思想只是缺少了基类接口的存在,对外缺少统一调用接口但()操作符的重载很好的弥补了这一缺陷。
PriseResolver&QPriseResolve&QPriseReject UML 类图
从Prise的使用情况看, 对于客户代码只需要关心在处理结果时对于QPriseResolve&QPriseReject调用操作,由于QPriseResolve&QPriseReject类组合PriseResolver,持有QPrise对象且存在整个QPrise执行链中。个人在这将PriseResolver&QPriseResolve&QPriseReject 统一称作QPrise上下文类。
QtPrise模板库中使用到的元编程技巧
由于QtPrise是一个轻量级的模板库,内部用到了大量模板元技术。 故在分析QtPrise源码前我们需对QtPrise中用到的CPP模板元技巧概念做些介绍
1. 模板元函数
a. 元函数函数特征&定义
元函数并不是一个传统的语言函数,它通常是一个struct或class.
通常返回一个或多个类型
元函数并不是语言的一部分也没有正式的语言支持它
它们作为现有语言功能的惯用用法而存在
它们的使用不是由语言强制执行的
b. 值元函数&型别元函数
值元函数通常返回一个值
1 template<class T> 2 class ValueMetaFunction ;我们声明&定义了一个名为ValueMetaFunction的元函数,返回了一个value为int的值.
值元函数的plus版本
1 template<auto T> 2 class ValueMetaFunction ; 5 6 template<class T, T value> 7 class ValueMetaFunction ;型别元函数通常返回一个类型
1 template<class T> 2 class TypeMetaFunction ;声明&定义一个名为TypeMetaFunction的元函数,返回了一个型别为T的type类型
c.元函数的调用
针对于上述的示例代码我们现在来获取元函数的返回值
1 ValueMetaFunction<int>::value 2 ValueMetaFunction<int, 100>::value 3 TypeMetaFunction<int>::type > typename TypeMetaFunction<int>::type我们在调用元函数返回值时一般书写都比较长,阅读起来也比较不友好。CPP提供了using关键字可以用于定义更加便捷的调用方式。
使用using关键字为模板(元函数)定义别名。
通常value元函数使用以 _v结尾的变量模板
1 template<typename T> 2 using value_v = ValueMetaFunction<T>::value通常type 元函数使用以 _t结尾的变量模板
1 template<typename T> 2 using type_t = typename ValueMetaFunction<T>::type使用便捷的调用方式
1 value_v<int> 2 type_t<int>d. 一个有用的元函数
1 // STRUCT TEMPLATE integral_constant 2 template <class _Ty, _Ty _Val> 3 struct integral_constant 12 13 _NODISCARD constexpr value_type operator()() const noexcept 16 }; 17 18 // 摘自MSVC xtr1cm文件integral_constant应用于在系统位数判断上
1 static const std::uint64_t default_buffer_size = 2 std::conditional<sizeof(void *) == 8, 3 std::integral_constant<std::uint64_t, 100 * 1024 * 1024>, 4 std::integral_constant<std::uint64_t, 1024 * 1024 * 1024> 5 >::type::value这样编码的优势:
integral_constant、sizeof(void *) == 8 数值等都在编译期执行计算不占用运行时开销。
default_buffer_size 可在编译时优化成一个编译常量控件在编译时就完成分配,提升程序运行时效率。
2. 全特化&偏特化构建ifthenelse
模板的全特化与偏特化特性构建出了元编程中的条件判断
模板偏特化&全特换的示例:
1 // 标准模板 2 template<typename T, typename U> 3 struct Sample ; 4 5 // 对于T为指针的偏特化 6 template<typename T, typename U> 7 struct Sample<T*, U> ; 8 9 // 对于T为int的偏特化 10 template<typename U> 11 struct Sample<int, U> ; 12 13 // 对于T&U都为int类型的全特化 14 template<> 15 struct Sample<int, int> ;声明ifthenelse:
1 template< bool CONDITION, class THEN, class ELSE > struct IF ; 2 3 template<class THEN, class ELSE> struct IF< false, THEN, ELSE > ; 4 5 template<class THEN, class ELSE> struct IF< true, THEN, ELSE > ;ifthenelse SampleCode:
1 template< bool Condition > 2 class IF 5 }; 6 7 // 全特化IF模板,当模板参数为false时匹配该版本 8 class IF< false > 11 }; 12 13 std::cout << IF<sizeof(void*) == 8>::EXEC()<< std::endl;3. Type Traits 类型特征
a. type traits是什么?
它是C++泛型编程的支柱之一
检查与转换类型的属性
它通常是一个简单的模板结构
b. MSVC中的type_traits文件中定义了大量常用的类型特征模板
下图摘自<type_traits> C++ Reference部分
具体说明及用法大家可以点击链接进行查看。上述类型特性模板在常见的模板设计程序中会经常有使用到。
c. call_traits
示例
1 // CLASS TEMPLATE binder1st 2 template <class _Fn> 3 class binder1st : public unary_function<typename _Fn::second_argument_type, 4 typename _Fn::result_type> 11 12 // @1 13 result_type operator()(const argument_type& _Right) const 16 17 result_type operator()(argument_type& _Right) const 20 21 protected: 22 _Fn op; 23 typename _Fn::first_argument_type value; // the left operand 24 }; 25 26 27 // 摘自MSVC functional头文件MSVC实现的binder1st 结构中存在着如果代码段@1中 argument_type如果是个引用类型,则binder1st重载()调用运算符存在引用引用的的问题。 这在C++中是不被允许的
call_traits 工具 摘自boost程序库
call_traits 的目的是确保像“引用引用”这样的问题永远不会发生,并且以最有效的方式传递参数。
1 template <typename T, bool small_> 2 struct ct_imp2 ; 5 6 template <typename T> 7 struct ct_imp2<T, true> ; 10 11 template <typename T, bool isp, bool b1, bool b2> 12 struct ct_imp ; 15 16 template <typename T, bool isp, bool b2> 17 struct ct_imp<T, isp, true, b2> ; 20 21 template <typename T, bool isp, bool b1> 22 struct ct_imp<T, isp, b1, true> ; 25 26 template <typename T, bool b1, bool b2> 27 struct ct_imp<T, true, b1, b2> ; 30 31 32 template <typename T> 33 struct call_traits 34 ; 46 47 template <typename T> 48 struct call_traits<T&> 49 ; 55 template <typename T> 56 struct call_traits<T& const> 57 ; 64 65 // 摘自boost程序库 call_traits.hpp 部分binder1st 对于调用操作符的重载最终可修改为
1 result_type operator()(typename call_traits<argument_type>::param_type _Right) constb. QtPrise中的型别特性的应用
QtPrise中定义了ArgsTraits元函数用于提取函数的形参列表元信息。
1 template<typename... Args> 2 struct ArgsTraits 3 ; 10 11 // 全特化参数列表为空的情况 12 template<> 13 struct ArgsTraits<> 14 ; 19 20 21 // 摘自 qpriseglobal.h 头文件定义ArgsTraits的扩展类ArgsOf元函数,由ArgsTraits的声明可知,ArgsTraits元函数接收的是有个类型参数包。 扩展类ArgsOf的作用则用于收集函数列表类型信息。
1 // 标准的ArgsOf类型的声明定义 2 template<typename T, typename Enabled = void> struct ArgsOf : public ArgsTraits<> ; 3 4 // 对nullptr_t的偏特化 5 template<> struct ArgsOf<std::nullptr_t> : public ArgsTraits<>; 6 7 // 对operator () 成员函数指针偏特化 8 template<typename T> 9 struct ArgsOf<T, typename std::enable_if<HasCallOperator<T>::value>::type>: public ArgsOf<decltype(&T::operator())> ; 10 11 // 对引用类型的偏特化 12 template<typename T> struct ArgsOf<T&> : public ArgsOf<T> ; 13 // 对右值类型的偏特化 14 template<typename T> struct ArgsOf<T&&> : public ArgsOf<T> ; 15 16 // 对可调用类型的偏特化 17 template<typename R, typename... Args> struct ArgsOf<R(Args...)> : public ArgsTraits<Args...> ; 18 19 // 对函数指针的偏特化 20 template<typename R, typename... Args> struct ArgsOf<R (*)(Args...)> : public ArgsTraits<Args...> ; 21 22 // 对成员函数指针偏特化 23 template<typename R, typename T, typename... Args> struct ArgsOf<R (T::*)(Args...)> : public ArgsTraits<Args...> ; 24 25 // 对const的成员函数指针偏特化 @7 26 template<typename R, typename T, typename... Args> struct ArgsOf<R (T::*)(Args...) const> : public ArgsTraits<Args...> ; 27 28 // 对volatile的成员函数指针偏特化 @8 29 template<typename R, typename T, typename... Args> struct ArgsOf<R (T::*)(Args...) volatile> : public ArgsTraits<Args...> ; 30 31 // 偏特化类型T为 const volatile的成员函数指针 @9 32 template<typename R, typename T, typename... Args> struct ArgsOf<R(T::*)(Args...) const volatile> : public ArgsTraits<Args...> ; 33 34 // 摘自 qpriseglobal.h 头文件我们看到ArgsOf对于大部分函数指针类型的情况都进行了偏特化从而对特定的函数形参列表参数包进行提取,进而传递给其父类。完成对函数形参列表信息的提取。
我们其实发现ArgsOf作用紧紧是用于提取函数的形参列表类型,至于类的成员函数是否是 const、volatile、const volatile等类型是不关心的。但为了满足类的成员函数所有类型的提取则必须尽可能的偏特化出所有成员函数类型的ArgsOf版本 见代码段@7& 8 & 9。细心的读者可能会发现ArgsOf对于类的成员函数形参列表类型的元信息提取其实是不全的,因为ArgsOf并没有偏特化全部的成员函数类型。例如 const&、const&&、const volatile&、const volatile&&、volatile&、volatile&&成员函数类型就没有进行偏特化, 对于这一类型的成员函数类型在编译器实例化ArgsOf时会去匹配标准的ArgsOf版本所以函数形参列表会作为空处理。
如果你的项目中有上述提到的未偏特化的成员函数则在使用QPrise时就出现错误了。 我们该如何去优化以下ArgsOf呢?
项目开发中我们可以提供一个基础的大而全的type_traits, 用于提取裸函数类型。 无论你是否使用QtPrise模板库, 至少在项目其他需求中也是可以作为一个基础类而存在的。
提供一个is_const_or_volatile_member_function 成员函数类型特征工具类, 声明定义如下:
1 template <typename T> 2 struct is_const_or_volatile_member_function : std::false_type ; 3 4 template <typename R, typename T, typename... Args> 5 struct is_const_or_volatile_member_function<R(T::*)(Args...) const> : std::true_type; 8 template <typename R, typename T, typename... Args> 9 struct is_const_or_volatile_member_function<R(T::*)(Args...) const&> : std::true_type ; 12 template <typename R, typename T, typename... Args> 13 struct is_const_or_volatile_member_function<R(T::*)(Args...) const&&> : std::true_type ; 16 template <typename R, typename T, typename... Args> 17 struct is_const_or_volatile_member_function<R(T::*)(Args...) const volatile> : std::true_type ; 20 template <typename R, typename T, typename... Args> 21 struct is_const_or_volatile_member_function<R(T::*)(Args...) const volatile&> : std::true_type ; 24 template <typename R, typename T, typename... Args> 25 struct is_const_or_volatile_member_function<R(T::*)(Args...) const volatile&&> : std::true_type ; 28 template <typename R, typename T, typename... Args> 29 struct is_const_or_volatile_member_function<R(T::*)(Args...) volatile> : std::true_type ; 32 template <typename R, typename T, typename... Args> 33 struct is_const_or_volatile_member_function<R(T::*)(Args...) volatile&> : std::true_type ; 36 template <typename R, typename T, typename... Args> 37 struct is_const_or_volatile_member_function<R(T::*)(Args...) volatile&&> : std::true_type ;is_const_or_volatile_member_function偏特化出了成员函数的所有的类型, 且继承与std::false_type or std::true_type用于给出一个bool值判断传入的类型是否为成员函数类型。最终返回一个裸成员函数指针类型type(此处裸代表 成员函数是非 const、 const&、const&&、const volatile、const volatile&、const volatile&&、volatile、volatile&、volatile&& 类型)
is_const_or_volatile_member_function使用:
这里对于之前ArgsOf定义的代码段@7、8、9 统一可以修改为下图的代码段@2。 这样可以由is_const_or_volatile_member_function元函数去提取所有的非裸成员函数类型也节省了ArgsOf偏特化的版本数量。
1 // @1 2 // 对指针成员函数类型的偏特化 3 template<typename R, typename T, typename... Args> 4 struct ArgsOf<R(T::*)(Args...)> : public ArgsTraits<Args...> 5 ; 8 9 // @2 10 // 对 const、 const&、const&&、const volatile、const volatile&、 11 // const volatile&&、volatile、volatile&、volatile&& 成员函数指针偏特化 12 template<typename T> 13 struct ArgsOf <T, std::enable_if_t<is_const_or_volatile_member_function<T>::value> >: 14 public ArgsOf<typename is_const_or_volatile_member_function<T>::type> 15 ;此处我们来验证下对于优化后ArgsOf的使用
use sample code:
1 qDebug() << ArgsOf<int(TestClass::*)(int, int)>::value; 2 qDebug() << ArgsOf<int(TestClass::*)(int, int) const>::value; 3 qDebug() << ArgsOf<int(TestClass::*)(int, int) const&>::value; 4 qDebug() << ArgsOf<int(TestClass::*)(int, int) volatile>::value; 5 qDebug() << ArgsOf<int(TestClass::*)(int, int) volatile&>::value; 6 qDebug() << ArgsOf<int(TestClass::*)(int, int) const volatile&>::value;输出:
1 R(T::*)(Args...) 2 R(*)(Args...) const or volatile 3 R(*)(Args...) const or volatile 4 R(*)(Args...) const or volatile 5 R(*)(Args...) const or volatile 6 R(*)(Args...) const or volatile从输出我们可以分析出非裸成员函数指针类型都匹配到上述代码段@2的ArgsOf偏特化版本,裸成员函数类型匹配到了代码段@1的ArgsOf偏特化版本。至此我们完成了对QtPrise模板库中ArgsOf的偏特化类的成员函数类型不足的优化。
4. SFINAE
a. SFINAE 是什么?
替换失败不是错误(Substitution failure is not an error)
如果你重载一个函数并调用它,只要有一个重载是有效的。其他重载可以默认失败, 在所有重载函数选择最佳匹配。
对于需要重载多个函数是非常有用的
b. SFINAE 能做什么?
检查一个类中是否含有成员XXMember
检查一个结构中是否定义函数XXFunction
一个类型能做什么
编译时的布尔值检查/条件编译
c. SFINAE在QtPrise中的应用
检查一个类中是否函数成员函数
1 template<typename T> 2 struct HasCallOperator 3 ; 11 12 // 摘自qpriseglobal.h文件QPriseBase构造函数重载
1 template<typename F, typename std::enable_if<QtPrisePrivate::ArgsOf<F>::count == 1, int>::type = 0> 2 inline QPriseBase(F resolver); 3 template<typename F,typename std::enable_if<QtPrisePrivate::ArgsOf<F>::count != 1, int>::type = 0> 4 inline QPriseBase(F resolver);根据实参类型中的形参列表数量编译器来进行最佳的构造函数生成。
QtPrise源码分析和实现思想
1. QtPrise的UML时序图
下面我结合此时序图对QtPrise 源码的主要流程进行分析
2. QtPrise源码分析
a.QPrise的构造&生命周期
先看一个基本的使用QPrise Sample Code:
1 ).then([](int) > std::string ); 8 }上述代码段构建了一个基本的Prise链式调用并返回链式最后一个QPrise实例,不知道大家在使用QPrise时有没有这样的一个疑问,就是在上述代码段执行完成后prise实例都销毁了。 它是如何保证异步继续执行下有个then方法函数体的??? 随着我们对QPrise对象的构造分析慢慢来解答这个问题。
QPrise构造函数分析
1 template<typename T> 2 class QPriseBase 3 } 11 ; 14 15 try catch (...) 21 } 22 23 // @2 24 template<typename F, 25 typename std::enable_if<QtPrisePrivate::ArgsOf<F>::count != 1, int>::type = 0> 26 inline QPriseBase(F resolver) : m_d} 27 ; 30 31 try catch (...) 37 } 38 }; 39 40 template<typename T> 41 class QPrise : public QPriseBase<T> 42 48 };QPrise构造函数接收一个任意实参(形参类型为万能引用类型),但通常为一个可调用对象(函数指针、成员函数、Lambda表达式、重载()运算符的类、std::function). 从上图代码段@4&5中我们不难发现此规则, 且该调用对象的形参数量必须为1或者2 , 为1时形参类型必须为QPriseResolve、为2是形参类型为QPriseResolve&QPriseReject。
QPrise将万能引用形参resolver作为实参转发到父类QPriseBase的构造函数上。上图代码段@3 对万能引用类型使用std::forward进行转发(参考effective modern C++)。
QPriseBase存在代码段@1&2 两个模板函数重载版本 (参考上文的SFINA),最终选择哪个版本则根据上文介绍的ArgsOf类型特性模板来提取类型F的参数列表数量,如果为1则匹配代码段@1否则匹配代码段@2。
QPriseBase函数体实现:代码段@6&7 构造上下文类PriseResolver局部对象, 将自己的控制权交由PriseResolver对象 。基于PriseResolver对象构造QPriseResolve 或 QPriseResolve&QPriseReject(根据构造QPrise对象时的实参(可调用类型)的形参数量决定)。
QPrise的构造过程中会立马对构造实参进行回调,进而进入到客户代码的函数栈中。 当客户代码调用形参1或者2 则相当于把操作结果交到的QPrise上下文类中。
我们此处仅对操作做成功的情况讨论其他流程基本一致,结合QPrise Sample Code(本节开头处的示例)进行流程梳理.
当客户代码将操作结果100 通知到QPriseResolve 对象, resolve对结果进行转发,进而调用装饰对象QPriseResolver的resolve函数, 个人认为该方法没必要为模板方法,形参类型为T就行。
prise>m_d>resolve(std::forward<V>(value)); 将客户代码的操作结果存入到成员属性m_d
prise>m_d>dispatch(); 把当前结果已异步的方式分发到下个待处理的QPrise对象
release(); 释放当前上下文持有的QPrise堆对象,所以在QPrise构造函数中回调用户代码时,用户代码应该调用resolve或者reject 避免内存泄漏。
相信看到此处我们知道QtPrise内部是如何保证其生命周期了。
得益于PriseResolver上下文对QPrise封装,其副本已保存在上下文类中,其生命周期的长短取决于客户代码对resolve与reject的调用。
保证后续方法得以执行则是依赖Qt event runloop机制。 Event 封装可调用对象(内部为Lambda)基于异步事件实现异步链式调用。
请保证在一定要执行resolve与reject可调用对象,不然会有内存泄漏。
QPrise then核心函数分析
then提供了接收下一步(异步)处理的函数入口(下文中称nextPrise对象)
then函数体源码
1 template<typename T> 2 template<typename TFulfilled, typename TRejected> 3 inline typename QtPrisePrivate::PriseHandler<T, TFulfilled>::Prise 4 QPriseBase<T>::then(const TFulfilled& fulfilled, const TRejected& rejected) const 5 ); 16 17 if (!m_d>isPending()) 20 21 return next; 22 } 23 24 template<typename T> 25 template<typename TFulfilled> 26 inline typename QtPrisePrivate::PriseHandler<T, TFulfilled>::Prise 27 QPriseBase<T>::then(TFulfilled&& fulfilled) const 28then函数形参通常接收有个可调用对象
代码段@1 通过PriseHandler型别特征提取当前待返回Prise的模板类型与返回类型 ,参考使用QPrise Sample Code示例,PriseType为QPrise<std::string>。
构造局部对象QPrise 并立即执行实参Lambda表达式。
通过PriseHandler 创建可调用对象(std::function)并将当前then的形参以及nextPrise下的上下文保存在该可调用对象中
将PriseHandler构建的可调用对象保存在当前的成员属性QPriseData中
返回Prise对象继续以链式的方式接收下一个处理者
c. QPriseData结构&Prise链式内存模型
QPrise 类从其父类PriseBase继承了唯一的数据属性成员m_d。
QExplicitlySharedDataPointer<QtPrisePrivate::PriseData<T>> m_d;QPriseData结构, 下图源码仅给出了Prise成员属性,
1 template<typename T, typename F> 2 class PriseDataBase : public QSharedData ; 18 19 20 template<typename T> 21 class PriseData : public PriseDataBase<T, void(const T&)> ; 28 29 // 全特化QPrise返回值类型为void的case 30 template<> 31 class PriseData<void> : public PriseDataBase<void, void()> 32 35 36 37 // 对数据T使用RAII手法来管理其生命周期 38 template<typename T> 39 class PriseValue 40 43 PriseValue(const T& data) : m_data(QSharedPointer<T>::create(data)) 44 PriseValue(T&& data) : m_data(QSharedPointer<T>::create(std::forward<T>(data))) 45 bool isNull() const 46 const T& data() const 47 48 private: 49 QSharedPointer<T> m_data; 50 };从Prise类的属性成员 QPriseData的结构我们可以给出其内存模型
QPriseResolve&QPriseReject为PriseResolver的装饰对象。
PriseResolver是一个携带QPrise对象的上下文,即handler与catcher callable对象中成员属性resolve、reject装饰对象分别又指向了QPriseData对象即形成了QPrise 链式内存模式。
d. QPrise 上下文类的作用
封装QtPrise链式头对象,专注于首次结果。
隔离客户代码与QPrise的关联,让客户代码调用更加简单只需要关心结果即可。
转移QPrise对象生命周期,并由客户代码来管理QPrise生命周期。
简化客户业务代码,分离成功与失败逻辑,依赖Qt显示共享技术。
e. Qt消息循环实现Prise非阻塞调用
Prise完成异步调用最终函数体,依赖Qt异步消息机制。 下一个待处理的可调用对象(Lambda表达式)封装着nextPrise的上下文对象。
F: HandlerCallable
Event: 自定义QEvent携带Callable基于消息循环异步调用nextPrise
thread: 保证线程单一性
设计模式与元编程的混合
对于喜欢元编程的读写朋友在这里推荐去阅读Loki 库的源码,其中对于元编程的各项技巧都有深入涉及。Loki主要使用模板元编程去实现通用的设计模式。看看模板元编程与设计模式在一起组合会碰撞出什么样的火花。 (#^.^#)
1. 元编程与单例模式sampleCode
策略结构的定义
对象创建策略结构
1 template <class T> struct CreateUsingNew 5 static void Destroy(T* p) 8 };对象生命周期策略结构
1 template <class T> 2 struct DefaultLifetime 6 static void OnDeadReference() 9 };单例构造模板类的定义
模板单例类接收两个默认的创建对象以及生命周期管理类型。 实例化时用户代码可重新扩展该策略类型。
以上内容摘自Loki库,为做简单示例有简化该单例模式构造模板类。
2. 元编程与其他模式
Loki template Library库还实现了以下模板元与设计模式的混合
抽象工厂模式
工厂模式
访问者模式
至于具体的讲解大家可以阅读modern C++ programming 一书. 此书就是基于Loki库来编写的。
上一篇:太赫兹时域光谱与频域光谱
下一篇:用TortoiseGit上传代码到GitHub的步骤
Qt