2013-04-04 48 views
4

對於特定的類層次結構,我需要知道基類引用是否是特定派生類的實例。 由於不同的原因,我不能在這裏使用標準的C++ RTTI,我需要實現一個自定義的instanceof機制。自動化LLVM樣式RTTI代碼

LLVM-stle RTTI將滿足我的需求,但我想知道它是否會存在一種方式(以某種方式使用模板)來自動執行classof方法?

是否有其他/更簡單的實現這種機制,可以知道基類是否是派生類的實例?

我的約束:

  • 我沒有多重繼承,但我有繼承的幾個級別。
  • 內存佔用空間必須儘可能小,並且不可能執行動態分配。
+0

如何添加像'get_class_id()'這樣的虛函數? – 2013-04-04 14:58:50

+1

@KerrekSB基本上LLVM風格的RTTI是如何工作的。每個類都有一個靜態標識符。問題不在於如何唯一標識一個班級。 – greydet 2013-04-04 15:04:46

回答

4

我想知道它是否會存在方式(以某種方式使用模板)來自動classof方法的實現?

是的,有辦法來自動classof方法,我真的不明白,爲什麼LLVM頁面將證明手卷組classof方法,因爲它是如此更可擴展,如果你自動執行非常簡單的過程。

這裏是一個非常基本的解決方案:

class TypedObject { 
    public: 
    virtual ~TypedObject() { }; 

    virtual int getClassId() const { return 0; }; 
    static int getStaticClassId() { return 0; }; 

    virtual bool isOfType(int aID) const { return (aID == 0); }; 

    template <typename T> 
    bool isOfClass() const { return isOfType(T::getStaticClassId()); }; 

}; 

運行時鑄(即dynamic_cast)函數是這樣的:

template <typename T> 
T* runtime_ptr_cast(TypedObject* p) { 
    if((p) && (p->isOfClass<T>())) 
    return static_cast<T*>(p); 
    return NULL; 
}; 

template <typename T> 
typename std::enable_if< 
    std::is_const<T>::value, 
T* >::type runtime_ptr_cast(const TypedObject* p) { 
    if((p) && (p->isOfClass<T>())) 
    return static_cast<T*>(p); 
    return NULL; 
}; 

然後,所有你需要的是宏自動化創建虛擬和靜態功能:

#define MY_RTTI_SYSTEM_CREATE_TYPE_1_BASE(NEWCLASSID, BASECLASSNAME) \ 
    public: \ 
    virtual int getClassId() const { return NEWCLASSID; }; \ 
    static int getStaticClassId() { return NEWCLASSID; }; \ 
    \ 
    virtual bool isOfType(int aID) const { \ 
     return ((aID == NEWCLASSID) || BASECLASSNAME::isOfType(aID)); \ 
    }; 

然後,你可以創建E中的新類這樣的:

class Foo : public TypedObject { 

    // ... some code, as usual ... 

    // call the macro with a given ID number and the name of the base-class: 
    MY_RTTI_SYSTEM_CREATE_TYPE_1_BASE(1, TypedObject) 
}; 

導致:

int main() { 
    Foo f; 
    TypedObject* b = &f; 

    // check the type: 
    if(b->isOfClass<Foo>()) 
    std::cout << "b is indeed for class Foo!" << std::endl; 

    // make a dynamic cast: 
    Foo* pf = runtime_ptr_cast<Foo>(b); 
    if(pf) 
    std::cout << "cast to 'Foo*' was successful!" << std::endl; 

    const TypedObject* cb = b; 
    const Foo* cpf = runtime_ptr_cast<const Foo>(cb); 
    if(cpf) 
    std::cout << "cast to 'const Foo*' was successful!" << std::endl; 

    Foo* pf2 = runtime_ptr_cast<Foo>(cb); // ERROR: no such function (invalid cast). 
}; 

和當然,你可以只創建註冊類型的多個宏擴展,以多重繼承也。這個方案也有無數的變化(個人,my implementation,我註冊到全球存儲庫的類型,並允許訪問工廠功能)。

我不認爲有任何實際的方法可以避免在您創建的每個類中使用MACRO調用。我已經考慮了一段時間(前一段時間,因爲我是自己做的),並且我得出結論:最簡單和最乾淨的解決方案是在課堂上進行MACRO調用(即使我非常鄙視MACRO一般)。但我不知道,也許其他人有一個更好的(基於模板的)解決方案,不會造成太多混亂或不太侵擾。我一直在使用這個方案多年,而且它非常乾淨。

我沒有多繼承,但我有幾個級別的繼承。

上述方案適用於任何級別的繼承(即它是可擴展的解決方案)。如果有一天你想這樣做,它也可以很容易地適應多重繼承。我知道LLVM傾向於沒有任何虛擬函數的溶液,並使用而不是在基類的完全一體的id數據構件上的內存佔用

影響必須儘可能

爲最小。用這種方案實現與上述相同的功能變得有點困難(但可能)。使用虛擬函數要容易得多,它只佔用一個指針(vtable指針)的空間,這個指針通常不會比整數id數據成員大得多。如果課程已經是多態的,那麼成本根本不算什麼。而且,當然,上述內容比內置的C++ RTTI輕得多。因此,除非你真的想要用積分id(或enum)解決方案來擠壓那些可以節省的字節,否則我會建議你使用基於虛擬功能的解決方案,就像我上面展示的那樣。

無法執行動態分配。

一般不需要動態分配。只有更復雜(功能豐富)的RTTI實現需要一些動態分配。如果你只想做「classof()」(因此是動態強制轉換),肯定不需要動態內存分配。

+0

感謝您的好評!我想我完全同意你的分析。與LLVM枚舉相比,虛擬方法實現的另一個優點是您不需要知道基類中的每個派生類。我自己離這個解決方案並不太遠,但是想避免使用這樣的宏。我終於無法想出一個更好的基於模板的解決方案(我不是一個超模板的英雄:))。我想我會嘗試解決您的問題。 – greydet 2013-04-04 21:03:59

+1

+1,與模板非常優雅的解決方案。據我所知,雖然LLVM不會因爲vptr空間成本而避免虛函數(它們有多態性),但是因爲調用虛函數比調用非虛函數要慢,所以它們使用這種類RTTI機制a *很多*。 – Oak 2013-04-05 08:32:29

0

你想要某種類似的數據結構作爲全局變量樹來存儲你的類層次結構

class Foo : public Foo_Parent { 
    IS_PART_OF_HIERARCHY 

public: 
    Foo(); 
    ... 
} 

#define IS_PART_OF_HIERARCHY 
    private: 
    static Hierarchy<string> *node; 
    public: 
    bool isChildOf(string parent) const; 
    bool isParentOf(string child) const; 

在.cpp文件

INSERT_INTO_HIERARCHY(Foo, Foo_Parent) 

Foo::Foo() {} 
.... 

#define INSERT_INTO_HIERARCHY(class_name, parent_class_name) 
    Hierarchy<string> class_name::node = classes_hierarchy.insertAfter(#parent_class_name); 
    bool class_name::isChildOf const(string) { 
     auto *node = class_name::node; 
     // traverse the parents of node 
    } 
    bool class_name::isParentOf const(string) { 
     auto *node = class_name::node; 
     // traverse the children of node 
    } 

我找不到在層次結構類STL,實現它有點棘手,我不知道這是否值得付出努力。