条款27:考虑__uuidof与uuid在关键字在不同编译器上的兼容问题
你可能还记得条款11中提出的“不轻易舍弃编译器为我们提供的安全机制”,我们因为提倡更加安全的代码而使用了uuid 和__uuidof等关键字。它确实方便了开发,也使得开发的程序在类型上更加安全。
但你可能会考虑到移植问题。我们不指望COM在非windows平台运行,但却不是说C++开发COM只能在VC这个编译器上编译。如果没有VC的话,使用__uuidof和uuid的关键字的智能指针能否继续使用呢?
_com_ptr_t为我们提供了一个折中方案。我们可以在他定义只是决定是否使用uuidof关键字:
//不使用__uuidof关键字
_COM_SMARTPTR_TYPEDEF(ICalculator, IID_ICALCULATOR);
//使用uuidof关键字
_COM_SMARTPTR_TYPEDEF(ICalculator, __uuidof(ICalculaor));
而CComPtr则显得不那么客气了。他在代码实现过程中直接使用了__uuidof关键字。这使得我们要在非VC编译器下使用这类智能指针碰到了不少麻烦。
那让我来介绍一个技巧,它可以让你在几乎所有的C++编译器中都可以用__uuidof这个关键字。而条件是,你的C++编译器应该支持模版特化这种特性【11】【12】。
首先我们设计一个可以将"XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX"形式的GUID转换成GUID类型的函数。当然我们还应当考虑GUID字串两端加入大括号的情况,下面这个函数可以很方便的完成这一点
inline STDMETHODIMP_(GUID) GUIDFromString(LPOLESTR lpsz)
{
HRESULT hr;
GUID guid;
if (lpsz[0]=='{')
{
hr=CLSIDFromString(lpsz,&guid);
}
else
{
std::basic_string<OLECHAR> strGuid;
strGuid.append(1,'{');
strGuid.append(lpsz);
strGuid.append(1,'}');
hr = CLSIDFromString((LPOLESTR)strGuid.c_str(),&guid);
}
assert(hr==S_OK);
return guid;
}
我们再来写一个trait类以及针对于这个trait类的一个特化版本。不过这个特化版本我们将其放入到一个宏内部。让用户通过宏来定义出特化的特化后的trait类。
template <class Class>
struct _UuidTraits {
};
#define _DEFINE_UUID(Class,uuid) \
template <> \
struct _UuidTraits<Class>{ \
static const GUID& Guid(){ \
static GUID guid=GUIDFromString(L## uuid); \
return guid; \
} \
}
之后就能定义一个__uuidof宏。用以模仿vc编译器中__uuidof宏的功能,他实际上会去调用特化后的Trait类:
#define __uuidof(Class) _UuidTraits<Class>::Guid()
最后我们定义一个将GUID和接口绑定起来的宏,那么以后只要使用这个宏就可以将GUID和类绑定在一起。
#define DEFINE_CLSID(Class,guid) \
class Class; \
_DEFINE_UUID(Class,guid)
#define DEFINE_IID(Interface,iid) \
struct Interface; \
_DEFINE_UUID(Interface,iid)
OK~,一切都完成了,我们稍微测试一下。
DEFINE_IID(Interface,"{B372C9F6-1959-4650-960D-73F20CD479BB}")
DEFINE_IID(Class,"{B372C9F6-1959-4650-960D-73F20CD479AA}")
void test()
{
CLSID clsid=__uuidof(Class);
IID iid=__uuidof(Interface);
...
}
利用以上这些技巧我们可以很方便的塑造出跨编译器的__uuidof关键字,智能指针代码可以复用,而类型也变得更加的安全。
嗯~ 看来CComPtr的设计者也非圣贤。这种使得接口不易被误用的技术并非一开始则被引入到了这套智能指针中。但随着智能指针被广泛使用,误操作的情况也开始引起智能指针设计者的重视。ATL对此问题也就采取了补救措施。
我们应当时刻警惕,且时刻谨记这一条款“接口应当容易被使用而不易被误用”。