更好地仿真VC++关键字__uuidof
2009-05-10 20:15
190 查看
转帖请注明出处 http://www.cppblog.com/cexer/archive/2008/07/05/55419.html
世界上有个叫__uuidof的关键字。这是一个家喻户晓且其被广泛使用的关键字,几乎可以说,有COM程序员的地方,就有它 __uuidof的存在。其很好很强大的程度是人所共见的,夸张一点比喻:离开它的COM程序员,就像失去了点火器的火箭,虽然可以人工点火,但是不安全且无效率。
不过很多人并不知道,这其实是一个编译器扩展关键字,提供了此关键字的仅VC一家别无它店。幸运的是,强大的C++让我们能够轻易仿真出这个关键字的大部分功能。
网上能够找到一种仿真的方法,见许式伟:《仿真VC++提供的关键字__uuidof》。该方法的实现是:特化模板类的成员函数,然后运行时调用函数根据UUID字符串产生出UUID,由于是生成于运行时,所以它无可避免地有两个缺点:
存在运行时消耗。
无法作为非类型模板参数传递给模板。
那些整天流着口水追求效率的C++程序员们,是不能忍受任何不必要的运行时消耗的。对于第二点,VC的关键字__uuidof取出来的UUID是能够作为非类型模板参数传递的,ATL中就大量地使用了这样的参数传递形式,所以目前的这种实现功能有限,仿真度还不够高。
其实只要能让它能够编译期决定UUID的值,那么这两个问题就迎刃而解了。而这是肯定可以实现的,并且很简单。我曾经在自己写的一个COM库里实现过这样的方法,虽然那个库已经不知丢到哪里去了,不过那个方法还记得。
解决的途径还是离不开模板特化。类的成员包括成员函数和成员变量,函数是运行时作用的,然而static const的成员变量可以是编译期就决定。所以解决的方法就在眼前了:特化模板的成员变量。
以下是我的实现方法。
先定义一个类模板,它有一个static const ,UUID类型的成员变量:
有了这个简单的东西就好办了,只需要针对某个接口特它的成员变量就行了,如:
然后我们就可以这样取得接口的UUID:
作为非类型模板参数传递:
不过现在这种实现还有一些问题,看以下代码:
注意我们并没有事先对模板__uuid_of_impl特化ITypeLib的版本。但是以上语句却能够编译通过,在运行时,__uuid_of_impl<ITypeLib>的值将会是错误的值GUID_NULL。这是因为,我们定义模板的时候,同时在模板外定义了模板的静态成员变量并赋值为GUID_NULL,所以没有用特化的方法定义UUID的接口,都将使用GUID_NULL这个通用值。这当然不是我们想要的。所以我们想在没有定义UUID的时候让编译器警告我们,要达到这样的效果只需要去掉上面那句:
现在再进行编译,编译器会告诉你,有一个无法解析的符号。根据编译器提供的相关信息,很容易就能确定问题所在。这样能够在编译期极大地减小安全隐患。
最后加上我们定义的几个宏,这是最后的全部实现:
用以下代码测试通过:
需要注意的是DEFINE_UUID应该在实现文件(*.cpp,*.cxx,……)当中使用。到这里,仍有一些使用方法与VC的关键字是不一样的,所以仍没做到仿真度100%。不过我相信通过预处理元编程,能够相当程度地逼近它,只是我对预处理元编程不是很了解,所以就不在这里献丑了。
世界上有个叫__uuidof的关键字。这是一个家喻户晓且其被广泛使用的关键字,几乎可以说,有COM程序员的地方,就有它 __uuidof的存在。其很好很强大的程度是人所共见的,夸张一点比喻:离开它的COM程序员,就像失去了点火器的火箭,虽然可以人工点火,但是不安全且无效率。
不过很多人并不知道,这其实是一个编译器扩展关键字,提供了此关键字的仅VC一家别无它店。幸运的是,强大的C++让我们能够轻易仿真出这个关键字的大部分功能。
网上能够找到一种仿真的方法,见许式伟:《仿真VC++提供的关键字__uuidof》。该方法的实现是:特化模板类的成员函数,然后运行时调用函数根据UUID字符串产生出UUID,由于是生成于运行时,所以它无可避免地有两个缺点:
存在运行时消耗。
无法作为非类型模板参数传递给模板。
那些整天流着口水追求效率的C++程序员们,是不能忍受任何不必要的运行时消耗的。对于第二点,VC的关键字__uuidof取出来的UUID是能够作为非类型模板参数传递的,ATL中就大量地使用了这样的参数传递形式,所以目前的这种实现功能有限,仿真度还不够高。
其实只要能让它能够编译期决定UUID的值,那么这两个问题就迎刃而解了。而这是肯定可以实现的,并且很简单。我曾经在自己写的一个COM库里实现过这样的方法,虽然那个库已经不知丢到哪里去了,不过那个方法还记得。
解决的途径还是离不开模板特化。类的成员包括成员函数和成员变量,函数是运行时作用的,然而static const的成员变量可以是编译期就决定。所以解决的方法就在眼前了:特化模板的成员变量。
以下是我的实现方法。
先定义一个类模板,它有一个static const ,UUID类型的成员变量:
template<typename T> struct _uuid_of_impl { static const UUID id; }; template<typename T> const UUID _uuid_of_impl<T>::id=GUID_NULL;
有了这个简单的东西就好办了,只需要针对某个接口特它的成员变量就行了,如:
template<> const UUID _uuid_of_impl<IUnknown>::id=IID_IUnknown; template<> const UUID _uuid_of_impl<IDispatch>::id=IID_IDispatch;
然后我们就可以这样取得接口的UUID:
IID IunknownID=_uuid_of_impl<IUnknown>::id; IID IdispatchID=_uuid_of_impl<IDispatch>::id;
作为非类型模板参数传递:
template<const IID* t_iid> struct __uuid_of_test { __uuid_of_test() {} void test() { t_iid; } }; __uuid_of_test<&(_uuid_of_impl<IDispatch>::id) > obj;
不过现在这种实现还有一些问题,看以下代码:
IID ITypelibID=_uuid_of_impl<ITypeLib>::id;
注意我们并没有事先对模板__uuid_of_impl特化ITypeLib的版本。但是以上语句却能够编译通过,在运行时,__uuid_of_impl<ITypeLib>的值将会是错误的值GUID_NULL。这是因为,我们定义模板的时候,同时在模板外定义了模板的静态成员变量并赋值为GUID_NULL,所以没有用特化的方法定义UUID的接口,都将使用GUID_NULL这个通用值。这当然不是我们想要的。所以我们想在没有定义UUID的时候让编译器警告我们,要达到这样的效果只需要去掉上面那句:
template<typename T> const UUID _uuid_of_impl<T>::id=GUID_NULL;
现在再进行编译,编译器会告诉你,有一个无法解析的符号。根据编译器提供的相关信息,很容易就能确定问题所在。这样能够在编译期极大地减小安全隐患。
最后加上我们定义的几个宏,这是最后的全部实现:
template<typename T> struct _uuid_of_impl { static const UUID id; }; #define uuid_of(x) _uuid_of_impl<x>::id #define DEFINE_UUID(x,l,w1,w2,b1,b2,b3,b4,b5,b6,b7,b8) / template<> / const UUID _uuid_of_impl<x>::id={l,w1,w2,{b1,b2,b3,b4,b5,b6,b7,b8}}
用以下代码测试通过:
struct ITest{}; DEFINE_UUID(ITest,0x96289151,0xf059,0x4049,0x88,0x19,0x61,0xa6,0xe9,0x79,0xc,0xf1); template<const IID* t_iid> struct uuid_of_test { uuid_of_test(){} }; int main() { IID xxxxID=uuid_of(ITest); uuid_of_test<&(uuid_of(ITest))> obj; return 0; }
需要注意的是DEFINE_UUID应该在实现文件(*.cpp,*.cxx,……)当中使用。到这里,仍有一些使用方法与VC的关键字是不一样的,所以仍没做到仿真度100%。不过我相信通过预处理元编程,能够相当程度地逼近它,只是我对预处理元编程不是很了解,所以就不在这里献丑了。
相关文章推荐
- 仿真VC++提供的关键字__uuidof
- 仿真VC++提供的关键字__uuidof
- VC下这样创建实例m_pRecordset.CreateInstance(_uuidof(Connection))不一定成功!...
- 仿真VC++提供的关键字__uuidof
- C++编译期函数/变量检测技术,仿真VC关键字__if_exists
- C++编译期函数/变量检测技术,仿真VC关键字__if_exists
- VC下这样创建实例m_pRecordset.CreateInstance(_uuidof(Connection))不一定成功!...
- 有效的使用和设计COM智能指针——条款27:考虑__uuidof与uuid在关键字在不同编译器上的兼容问题
- VC++的LNK1102 error out of memory 问题
- 电力仿真, 电力可视化源码库,MATLAB仿真,Multisim仿真,PSASP仿真, 电力地理接线图,VC++ 控件源程序
- Java instanceof 关键字是如何实现的?
- VC助手使用问题————all instance of the license"*" are in use .VA will be disabled
- C++ VC strncasecmp函数仿真
- 基于proteus的51单片机仿真实例十六、51单片机的C语言的标识符和关键字
- How to detect the presence of the VC 8.0 runtime redistributable package
- List of Best Open Souce projects written in VC++/MFC
- CreateInstance(_T("ADODB.Connection"))和CreateInstance(__uuidof(Connection))的区别
- 打开一个VC工程提示"Not all of the windows in the workspace could be opened."
- VC错误:编译程序中出现“fatal error C1010: unexpected end of file”
- linux内核宏container_of前期准备之gcc扩展关键字typeof