Visual C++ 2015 引入更新的 C++ 特性到 Windows API(3)

但是我如何得知,请求的GUID是否就代表着类想要实现的接口呢?我需要回到实现类模板收集的类型信息的地方,其中类型信息通过它的模板参数包来收集。请记住,我的目标是准许编译器为我实现它。我希望最终代码,和我手写的一样高效,甚至更好。我会通过可变函数模板集合来进行查询,函数模板自身包括模板参数包。我将以BaseQueryInterface函数模板作为开始:

virtual HRESULT __stdcall QueryInterface(   GUID const & id, void ** object) noexcept override {   *object = BaseQueryInterface<Interfaces ...>(id);

BaseQueryInterface本质上是IUnknown QueryInterface的现代C++版本。它直接返回接口指针而不是HRESULT类型。空指针则表明失败的情况。它接受单一函数参数,GUID标识着要查找的接口。更重要的是,我拓展了类模板参数包为完整模式,这样,BaseQueryInterface函数就可以开始枚举接口的处理过程。 起初你可能会认为,由于BaseQueryInterface是实现类模板的成员函数,所以它可以简单直接的访问接口的链表,但是我需要准许这个函数剥离链表中的第一个接口,就像下面这样:

template <typename First, typename ... Rest> void * BaseQueryInterface(GUID const & id) noexcept { }

按照这种方式,BaseQueryInterface函数能识别第一个接口,并且给接下来的搜索留有余地。看吧,COM有一定数量的特殊规则来支持QueryInterface 实现或至少接受对象识别。尤其是请求IUnknown,必须总是返回确切相同的指针,客户端才能确定两个接口的指针是否来自同一个对象。因此,BaseQueryInterface函数最棒的地方就是实现了这些假设。所以,可以从首个代表类想要实现的第一个接口的模板参数的GUID请求的对比开始。如果不匹配,我会检查IUnknown是否开始请求了:

if (id == __uuidof(First) || id == __uuidof(::IUnknown)) {   return static_cast<First *>(this); }

假设有一个匹配的,我直接准确无误的返回了第一个接口的指针。static_cast 能确保编译器基于IUnknown的多种接口不会引起歧义。cast只是校准了指针,让类的vtable能找到正确的指针位置,因为所有vtable接口是以IUnknown的三个方法开始的,这非常符合逻辑。

而我不妨同样添加IInspectable查询的可选支持。IInspectable相当变态,在某种意义上,它是Windows运行时接口,因为每个Windows运行时预计编程语言(如 C# 和 JavaScript)必须直接来自IInspectable,而不仅仅只是IUnknown接口。相对于C++的工作方式和COM传统的定义方式,以适应公共语言运行库的方式实现对象和接口是不幸的事实。更不幸的是当对象组合的时候对性能的影响,我会在下文中讨论。至于QueryInterface,我只需确保IInspectable能被查询,它应该是一个Windows运行时类的实现,而不是一个简单的典型COM类。虽然关于IUnknown的明确的COM规则不适用于IInspectable,我可以简单的用相同的方式对待后者。但这两个挑战。首先,需要了解是否有任何IInspectable派生出来的接口实现。第二,需要了解接口的类型,这样就可以正确的返回一个没有歧义的调整过的接口指针。假定列表中的第一个接口都是基于IInspectable,那可以只更新BaseQueryInterface 如下

if (id == __uuidof(First) ||   id == __uuidof(::IUnknown) ||   (std::is_base_of<::IInspectable, First>::value &&   id == __uuidof(::IInspectable))) {   return static_cast<First *>(this); }

注意,我用的是C++ 11中的is_base_of 的特性,来确定第一个模板参数是一个IInspectable的衍生接口。万一实现典型的COM类不支持Windows运行时,就能确保随后的对照是由编译器排除的。这样我可以无缝地支持Windows运行时和经典的COM类,即没有增加组件开发人员的语句复杂性,也没有任何不必要的运行时开销。但是,如果恰好遇列举出来得首位不是IInspectable接口,就会有不容易察觉的Bug的隐患。所需要做的就是,用某种方法替代is_base_of来扫描整个接口的列表:

template <typename First, typename ... Rest> constexpr bool IsInspectable() noexcept {   return std::is_base_of<::IInspectable, First>::value ||     IsInspectable<Rest ...>(); }

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/e72489af08f8fb85966939de82b5812b.html