微软的Proxy学习
这几天看下https://github.com/microsoft/proxy,学习下如何避免使用继承的情况下,高效方便地使用C++多态( polymorphic programming)的方法。先对着README.md看看怎么阅读这个源代码(实际上就是翻译了一下README.md)。
怎么使用Proxy库?这里不展示整个的用法了,只把抽象那块的东西抽出来:
最关健的用法如下所示,简单来说就是需要组合一些行为(表达式expression)构建出来具体的外观模式
-
// 定义一个facade type: Formattable, struct Formattable : pro::facade_builder ... ::build {}
具体的pro::facade_builder请参考https://microsoft.github.io/proxy/docs/ProFacade.html,是Proxy库提供的运行时抽象(这里稍微动一下脑子理解下,代码是compile time的时候制定了runtime满足什么行为)。
相应的解释请看
pro::facade_builder
: 提供在编译时构建外观类型(模式)的能力。support_format
: 相当于concept的概念,支持某种功能 (via standard formatting functions).build
: 将上下文构建为具体的facade模式
-
让我们再看另一个例子
struct Streamable : pro::facade_builder ... ::build {}
: 定义一个Streamable
外观模式,相应的功能为pro::facade_builder
: 准备定义另一种外观模式.add_convention
: 添加调用约定,调用约定由dispatch和overload构成。这里说起来有一些拗口:什么是dispatch?什么是overload?overload好理解,可以理解为函数签名,就是具体函数的形式。dispatch呢?这里我个人认为就是功能,名字,用法之类的抽象。这里我觉得简单理解为concept就可以了pro::operator_dispatch
<"<<", true>
: Specifies a dispatch for operator<<
expressions where the primary operand (proxy
) is on the right-hand side (specified by the second template parametertrue
). Note that polymorphism in the “Proxy” library is defined by expressions rather than member functions, which is different from C++ virtual functions or other OOP languages.std::ostream&(std::ostream& out) const
: 调用约定里面的overload,函数签名部分,和std::move_only_function
. 一样的。const
指明这个函数是const
的,不会改动对象内部.build
: 将上下文构建为具体的facade模式.
- .
如果不清楚到底什么是facade type,可以参考这个解释:
“Facade” 是一种软件设计模式,通常被称为 “外观模式”。这种设计模式提供了一个更高层次的接口,用于简化复杂系统的使用。Facade 模式的核心思想是为子系统中的一组接口提供一个统一的接口,从而让这个子系统更容易使用。
在软件开发中,Facade 模式的典型特征包括:
- 简化接口:通过提供一个简单的接口来隐藏系统的复杂性,从而减少使用者与系统之间的交互复杂度。
- 分离代码:将客户端与复杂的类库或 API 分离,使客户端代码更简洁,减少对外部复杂系统的依赖。
- 提高可维护性:通过引入 Facade,可以在不影响客户端的情况下更改子系统。
- 降低耦合度:客户端与子系统的耦合度降低,因为它们通过 Facade 进行交互,而不是直接依赖子系统的具体实现。
Facade 模式常用于提供简单的接口来处理与库、框架或一组复杂类的交互,是一种结构型设计模式,有助于提高系统的模块化和可维护性。
除了刚才提到的一些,还有一些其他有用的feature
- 重载:
facade_builder::add_convention
比上面展示的两个例子功能更强大。它可以接受任意数量的重载类型(严格来说,任何满足ProOverloadhttps://microsoft.github.io/proxy/docs/ProOverload.html要求的类型),在调用proxy时执行对函数执行重载解析。 - 外观模式组合: facade_builder::add_facade允许不同抽象的灵活组合。
- 概念:为了便于使用“proxy”进行模板编程,从facade类型中导出了三个概念。即,proxiable、proxiable_target和inplace_proxiable_target。
- 分配器感知: 函数模板allocate_proxy具备从任何自定义分配器的值创建一个proxy对象的能力。在 C++11 中,std::function和std::packaged_task的构造函数接受指定自定义分配器的功能以进行性能调优,但在 C++17 中这些被移除,因为“语义不明确,并且在未存储类型信息的上下文中,后续复制赋值期间如何再次拿到类型信息的技术问题”。这些问题不适用于allocate_proxy
- 可配置的约束: facade_builder为约束配置提供全面支持,包括内存布局(通过restrict_layout)、可复制性(通过support_copy)、可重定位性(通过support_relocation)以及可析构性(通过support_destruction)</
- 反射:proxy支持基于类型的编译时反射,这个反射支持进行运行时查询。有关更多详细信息,请参考facade_builder::add_reflection和函数模板proxy_reflect。
- 非接管(管理)代理:尽管proxy可以像智能指针一样有效地管理对象的生命周期,但有时我们希望对其进行解引用传递再传递到非接管上下文。3.2.0 版本以来,将这种能力作为扩展实现。有关更多详细信息,请参考函数模板make_proxy_view
- 运行时类型信息(RTTI):运行时类型信息(RTTI,run-time-type-information)自上世纪以来在 C++中提供了“较弱”的反射能力。虽然它不像其他一些语言中的反射那么强大(例如 C#中的 Object.GetType()或 Java 中的 Object.getClass()),但它在运行时为类型安全地强制类型转换提供了基本的基础设施。自 3.2 版本以来,“针对proxy的运行时类型信息”已作为扩展实现,并允许用户为每个外观模式定义决定是否实现rtti。有关更多详细信息,请参考facade_builder::support_rtti。
- 共享和弱所有权:虽然proxy可以从std::shared_ptr创建,但自 3.3.0 起的拓展支持用户可以更高效地创建具有共享和弱所有权的proxy对象。更多详细信息请参考函数模板make_proxy_shared、allocate_proxy_shared、别名模板weak_proxy
- 弱dispatch:当一个对象未实现调用约定,而我们不希望它构建的时候直接触发编译错误导致构建失败时,可以指定一个在调用时抛出异常的weak_dispatch。
结尾
唉,尴尬