设为首页 加入收藏

TOP

Visual C++ 2015 引入更新的 C++ 特性到 Windows API(二)
2015-02-03 22:27:38 来源: 作者: 【 】 浏览:74
Tags:Visual 2015 引入 更新 特性 Windows API
Box编写的<>(Addison-Wesley Professional,1998)一书中,阅读关于实现你自己的IUnknown的奇妙的和不可思议的方法。需要注意的是,虽然这是一本关于COM的优秀书籍,但是它是基于C++98的,并没有呈现出任何现代C++的特征。为了节省时间,我假定你已经熟悉了QueryInterface的实现过程,并集中于如何用现代C++实现它。下面是虚函数本身:


?


给定一个GUID用来标识一个特别的接口之后,QueryInterface应该来决定一个对象是否实现了需要的接口。如果实现了,它必须减少这个对象的引用计数,同时通过外部参数来返回所需的接口指针。如果没有实现,它必须返回一个空指针。因此,我将以一个粗略的轮廓来作为开始:


QueryInterface首先会尝试设法查找所需的接口。如果接口受不支持,则返回E_NOINTERFACE错误码。请注意,我是如何按照要求处理接口指针不支持的情况。你应该把QueryInterface接口看作是二元的操作。它要么成功找到所需的接口,要么查找失败。不要尝试发挥创造性,只需要依据条件响应即可。尽管COM规范有一些限制项,但是大多数消费者都会简单的假定接口不受支持,而不管你会返回何种错误码。在你的实现中的任何错误,都毫无疑问的会导致你陷入调试的深渊。QueryInterface是非常基础的,不能胡乱对待。最后,AddRef由接口指针再次调用,用来支持某种极少的而又允许的类组合场景。这些不受实现类模板的显式支持,但是我情愿在这里做一个表率。重要的是,记住???用计数操作是面向接口的,而不是面向对象的。你不能 简单的,在属于一个对象的任意接口上面,调用AddRef或者Release。你必须依赖COM规则来管理对象,否则你会冒险引入以不可思议的方式崩溃的非法代码。


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


?


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


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


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


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


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


IsInspectable 也是基于is_base_of特性的,但是当前适用于匹配接口。如果没找到基于IInspectable 的接口则终止:


我会禁用掉稀奇古怪的默认参数。假定 IsInspectable 返回的是 true,我需要找到第一个IInspectable-based 接口:


?


再次使用?is_base_of 特性,但这次要返回一个真实匹配的接口指针:


?


BaseQueryInterface 这时可以利用IsInspectable 和 FindInspectable 一起来支持查询 IInspectable:


?


然后指定具体的?Hen 类:


?


实现类的模板,可以确保编译器能生成更高效的代码,不管 IHen、Hen2 来自 IInspectable?还是?IIUnknown (或者其他接口)。现在,我可以最后实现 QueryInterface 的递归部分,以及任何追加的接口,例如上面例子中的 IHen2。BaseQueryInterface 是靠调用 FindInterface 函数模板结束的:?


注意,我调用这个FindInterface函数模板,大致等同于我原来调用的BaseQueryInterface,在这个例子中,我向它传递接口的其余部分。我特意再次扩大参数包,这样它可以在列表的其余部分识别第一接口。但会提示一个故障。由于模板参数包不是以函数实参来扩展的,这可能会变得棘手,编程语言写不出来我想要的。更多的时候,这种“递归的”FindInterface可变模板正是你想要的:


?


它会从模板参数的其余部分中分离,如果有匹配就返回调整过的接口指针。另外,它也会调用自己,直到list取

首页 上一页 1 2 3 4 下一页 尾页 2/4/4
】【打印繁体】【投稿】【收藏】 【推荐】【举报】【评论】 【关闭】 【返回顶部
分享到: 
上一篇一个通用的C/C++ Makefile 下一篇Python zipfile报错解决一例

评论

帐  号: 密码: (新用户注册)
验 证 码:
表  情:
内  容: