??前面我們討論了CUB開源庫中的AutoMsg組件。今天我們再看下其中所實現的一種優(yōu)雅的類型轉換方式DCI Unknow(在此我們不討論其實現以及原理,而只是探討其中個人認為實現不好的地方并進行修改漓帚。需要了解其原理的同學可以自行查閱源碼)。
UNKNOWN_INTERFACE(Unknown, 0xFFFFFFFE)
{
virtual void* castTo(const ::details::InterfaceId iid) const = 0;
};
??上述示例定義了最原始轉換接口午磁。由于我們不知道最終的轉換目標尝抖,所以必須為每一個角色人為注入不同的特性IID(例如上述的 0xFFFFFFFE)以保證最終轉換對象的正確性。人是懶惰的以及具有極強的好奇心迅皇。初次使用的人不明原理經常會問道“這個IID我們應該怎么定義“(其實隨意定義就好昧辽,只要你能保證其唯一性)。為了結束這種無休止的追問以及滿足我們懶惰的人性登颓,我們能不能將這個IID優(yōu)化掉或者對外不可見呢搅荞?這個也就是本文所需要解決的問題。
??首先我們需要解決的問題是“如何產生針對不同角色的特性IID框咙?“我們首先應該想到是類的靜態(tài)成員變量咕痛,其屬於類本身而不屬於對象,因此也就保證了針對不同類的唯一性喇嘱。從此我們可以尋找突破口茉贡。實現如下:
typedef unsigned long TypeId;
template <typename T>
class TypeIdHelper {
public:
static bool dummy_;
};
template <typename T> bool TypeIdHelper<T>::dummy_ = false;
template <typename T>
TypeId GetTypeId()
{
return (TypeId)&(TypeIdHelper<T>::dummy_);
}
??上述我們利用模板編譯期實例化的特性,創(chuàng)建不同的實例化對象從而保證靜態(tài)成員變量的唯一性(即地址的唯一性)者铜,將其地址直作為IID腔丧。上述應用過程中要注意靜態(tài)成員不要定義成常量放椰。因為編譯器可能對其進行優(yōu)化,從而不能保證唯一性愉粤。
??既然已經獲取到了唯一的ID砾医,那么后面我們就只需要將其應用到其中就OK 了。
struct Dummy {};
template <typename T_B1, typename T_B2, typename T_B3>
struct BaseTraits
{
struct Base : T_B1, T_B2, T_B3
{
virtual ~Base() {}
int IID;
};
};
template <typename T_B1, typename T_B2>
struct BaseTraits<T_B1, T_B2, Dummy>
{
struct Base : T_B1, T_B2
{
virtual ~Base() {}
int IID;
};
};
template <typename T_B1>
struct BaseTraits<T_B1, Dummy, Dummy>
{
struct Base:T_B1
{
virtual ~Base() {}
int IID;
};
};
template <>
struct BaseTraits<Dummy, Dummy, Dummy>
{
struct Base
{
virtual ~Base() {}
int IID;
};
};
template <typename T
,typename T_B1 = Dummy
, typename T_B2 = Dummy
, typename T_B3 = Dummy>
struct Interface : BaseTraits<T_B1, T_B2, T_B3>::Base
{
Interface()
{
BaseTraits<T_B1, T_B2, T_B3>::Base::IID = GetTypeId<T>();
}
virtual ~Interface() {}
};
新的宏定義如下
#define DEF_INTERFACE(iface, ...) \
struct iface :Interface<iface,##__VA_ARGS__>
??上述我們只是將原先的IID由Enum常量類型更改為了非常量類型衣厘。再沒有任何大得修改藻烤,也算比較簡潔了。經過上述修改后我們角色的定義變成了下述方式
UNKNOWN_INTERFACE(Unknown)
{
virtual void* castTo(const ::details::InterfaceId iid) const = 0;
};
可以看到不需要再人為的注入IID了头滔,是不是清爽很多怖亭。存在的問題有二:
1.需要提供一個接口來最外展示IID。
2.些許內存開銷坤检。(這個大家自行尋找吧~~)
最近有點感冒就寫這么多了兴猩。有問題歡迎互相討論。