2011-01-19 27 views
3

我正在使用一個簡單的對象模型,其中對象可以實現接口來提供可選功能。在它的心臟,一個對象必須實現getInterface方法,該方法被賦予一個(唯一的)接口ID。然後該方法返回一個指向接口的指針 - 或null,以防對象沒有實現所請求的接口。這裏有一個代碼草圖來說明這一點:這個基於類型列表的類層次結構生成代碼的替代方案是什麼?

struct Interface { }; 
struct FooInterface : public Interface { enum { Id = 1 }; virtual void doFoo() = 0; }; 
struct BarInterface : public Interface { enum { Id = 2 }; virtual void doBar() = 0; }; 
struct YoyoInterface : public Interface { enum { Id = 3 }; virtual void doYoyo() = 0; }; 

struct Object { 
    virtual Interface *getInterface(int id) { return 0; } 
}; 

爲了讓事情變得更容易了誰在這個框架內合作的客戶,我用一個小模板,自動生成「getInterface」實施,使客戶只需要執行接口所需的實際功能。這個想法是從Object以及所有的接口派生出一個具體類型,然後讓getInterface只返回指向this(轉換爲正確類型)的指針。這裏的模板和演示用法:

struct NullType { }; 
template <class T, class U> 
struct TypeList { 
    typedef T Head; 
    typedef U Tail; 
}; 

template <class Base, class IfaceList> 
class ObjectWithIface : 
    public ObjectWithIface<Base, typename IfaceList::Tail>, 
    public IfaceList::Head 
{ 
public: 
    virtual Interface *getInterface(int id) { 
     if (id == IfaceList::Head::Id) { 
      return static_cast<IfaceList::Head *>(this); 
     } 
     return ObjectWithIface<Base, IfaceList::Tail>::getInterface(id); 
    } 
}; 

template <class Base> 
class ObjectWithIface<Base, NullType> : public Base 
{ 
public: 
    virtual Interface *getInterface(int id) { 
     return Base::getInterface(id); 
    } 
}; 

class MyObjectWithFooAndBar : public ObjectWithIface< Object, TypeList<FooInterface, TypeList<BarInterface, NullType> > > 
{ 
public: 
    // We get the getInterface() implementation for free from ObjectWithIface 
    virtual void doFoo() { } 
    virtual void doBar() { } 
}; 

這工作得很好,但有兩個問題,這是醜陋的:

  1. 一種我阻滯劑是,這並不MSVC6工作(對模板的支持很差,但不幸的是我需要支持它)。編譯時,MSVC6會產生C1202錯誤。

  2. 通過遞歸ObjectWithIface模板生成整個範圍的類(線性層次結構)。這對我本身來說不是問題,但不幸的是,我不能只用一條switch語句將接口ID映射到getInterface中的指針。相反,層次結構中的每個步驟都會檢查單個接口,然後將請求轉發給基類。

有沒有人有建議如何改善這種情況?要麼通過修復ObjectWithIface模板的上述兩個問題,要麼通過建議使對象/接口框架更易於使用的替代方法。

+0

如果您需要支持VC6,我認爲您在模板元編程技巧方面的選擇是有限的。 – jalf 2011-01-19 13:05:45

+0

@jalf:我肯定是有限的,是的 - 我仍然希望在這些限制內有所改善。 : - ] – 2011-01-22 08:55:40

回答

2

那樣的事情呢?

struct Interface 
{ 
    virtual ~Interface() {} 
    virtual std::type_info const& type() = 0; 
}; 

template <typename T> 
class InterfaceImplementer : public virtual Interface 
{ 
    std::type_info const& type() { return typeid(T); } 
}; 

struct FooInterface : InterfaceImplementer<FooInterface> 
{ 
    virtual void foo(); 
}; 

struct BarInterface : InterfaceImplementer<BarInterface> 
{ 
    virtual void bar(); 
}; 

struct InterfaceNotFound : std::exception {}; 

struct Object 
{ 
    void addInterface(Interface *i) 
    { 
     // Add error handling if interface exists 
     interfaces.insert(&i->type(), i); 
    } 

    template <typename I> 
    I* queryInterface() 
    { 
     typedef std::map<std::type_info const*, Interface*>::iterator Iter; 
     Iter i = interfaces.find(&typeid(I)); 
     if (i == interfaces.end()) 
      throw InterfaceNotFound(); 

     else return static_cast<I*>(i->second); 
    } 

private: 
    std::map<std::type_info const*, Interface*> interfaces; 
}; 

你可能想的東西比type_info const*更復雜,如果你想跨越動態庫的界限做到這一點。像std::stringtype_info::name()這樣的東西可以正常工作(儘管有點慢,但這種極端派遣可能需要一些緩慢的東西)。您也可以製造數字標識,但這可能難以維護。

存儲type_infos的哈希值是另一種選擇:

template <typename T> 
struct InterfaceImplementer<T> 
{ 
    std::string const& type(); // This returns a unique hash 
    static std::string hash(); // This memoizes a unique hash 
}; 

,並使用FooInterface::hash()當您添加的接口,以及虛擬Interface::type()當您查詢。

3

dynamic_cast在語言中存在以解決這個確切的問題。

用法示例:

class Interface { 
    virtual ~Interface() {} 
}; // Must have at least one virtual function 
class X : public Interface {}; 
class Y : public Interface {}; 

void func(Interface* ptr) { 
    if (Y* yptr = dynamic_cast<Y*>(ptr)) { 
     // Returns a valid Y* if ptr is a Y, null otherwise 
    } 
    if (X* xptr = dynamic_cast<X*>(ptr)) { 
     // same for X 
    } 
} 

dynamic_cast也將無縫地處理類似的事情多和虛擬繼承,你可能掙扎。

編輯:

你可以檢查COM的QueryInterface公司招聘他們使用了類似的設計與編譯器擴展。我從來沒有見過COM代碼的實現,只使用了頭文件,但你可以搜索它。

相關問題