2012-01-24 52 views
2

我正在尋找一些適當的界面來處理方面類(處理類),但不是他們正在處理的實際類(元方面)的一部分的建議。這需要一些解釋...什麼是處理類的元方面的適當接口?

在我的具體示例中,我需要實現一個自定義的RTTI系統比C++提供的更復雜一點(我不會去探討爲什麼我需要這個)。我的基礎對象是FooBase,並且此基數的每個子類都與一個FooTypeInfo對象關聯。

// Given a base pointer that holds a derived type, 
// I need to be able to find the actual type of the 
// derived object I'm holding. 
FooBase* base = new FooDerived; 

// The obvious approach is to use virtual functions... 
const FooTypeInfo& info = base->typeinfo(); 

使用虛擬函數來處理對象的運行時類型並不適合我。我傾向於將對象的運行時類型視爲超出類範圍的東西,因此它不應該成爲其顯式接口的一部分。下面的界面讓我感覺舒服很多......

FooBase* base = new FooDerived; 
const FooTypeInfo& info = foo::typeinfo(base); 

然而,即使接口不是類的一部分,實施仍必須使用虛擬函數,爲了這個工作:

class FooBase 
{ 
    protected: 
    virtual const FooTypeInfo& typeinfo() const = 0; 
    friend const FooTypeInfo& ::foo::typeinfo(const FooBase*); 
}; 

namespace foo 
{ 
    const FooTypeInfo& typeinfo(const FooBase* ptr) { 
     return ptr->typeinfo(); 
    } 
} 

你覺得我應該用這個第二個接口(即感覺更適合我),處理稍微複雜的實現,或者768,16我只是第一個接口去?


@Seth卡內基

這是一個困難的問題,如果你甚至不想派生類知道作爲RTTI的一部分......因爲你不能真正做到構造函數中的任何東西都依賴於被實例化的類的運行時類型(出於同樣的原因,您不能在ctor或dtor中調用虛擬方法)。

FooBase是層次結構的共同基礎。我還有一個單獨的CppFoo<>類模板,可以減少樣板的數量,並使類型的定義更加容易。還有另一個PythonFoo類與Python派生對象一起工作。有關如何在系統的工作原理可以在這裏找到

template<typename FooClass> 
class CppFoo : public FooBase 
{ 
    private: 
    const FooTypeInfo& typeinfo() const { 
     return ::foo::typeinfo<FooClass>(); 
    } 
}; 

class SpecificFoo : public CppFoo<SpecificFoo> 
{ 
    // The class can now be implemented agnostic of the 
    // RTTI system that works behind the scenes. 
}; 

的詳細原因:
https://stackoverflow.com/a/8979111/627005

+0

我認爲所有將參與您的自定義RTTI的類必須從'FooBase'繼承。會不會讓每個類都重寫虛函數來賦予它們的類型,而是用一個唯一的ID調用一個構造函數'FooBase(int)'並且初始化一個'const int'成員,這樣一來,函數' foo :: typeinfo'只是一個變量檢索而不是虛函數調用? –

+0

@SethCarnegie我需要自定義RTTI的部分原因是我的程序將Python嵌入爲腳本語言,因此RTTI系統需要在兩種語言之間保持一致。在兩種語言之間保持ID是唯一的可能會出現一些問題。 [續] –

+0

@SethCarnegie [續]還有一個問題就是你的建議引入了一些樣板。我不希望腳本定義的類型必須處理像這樣的任何實現細節;目前您只需像在普通Python應用程序中那樣編寫該類,並且希望保持這種狀態。 –

回答

0

您可以通過typeid關鍵字和使用配合動態類型靜態類型返回std::type_info對象作爲識別手段。此外,如果您將typeid應用到專門爲此目的而創建的單獨課程中,那麼對於您所介入的課程而言,它將是完全非侵入式的,儘管他們的名字仍需提前知曉。對支持動態多態的類型應用typeid非常重要 - 它必須具有一些virtual函數。

這裏是例子:

#include <typeinfo> 
#include <cstdio> 

class Base; 
class Derived; 

template <typename T> class sensor { virtual ~sensor(); }; 

extern const std::type_info& base = typeid(sensor<Base>); 
extern const std::type_info& derived = typeid(sensor<Derived>); 

template <const std::type_info* Type> struct type 
{ 
    static const char* name; 

    static void stuff(); 
}; 

template <const std::type_info* Type> const char* type<Type>::name = Type->name(); 

template<> void type<&base>::stuff() 
{ 
    std::puts("I know about Base"); 
} 

template<> void type<&derived>::stuff() 
{ 
    std::puts("I know about Derived"); 
} 

int main() 
{ 
    std::puts(type<&base>::name); 
    type<&base>::stuff(); 

    std::puts(type<&derived>::name); 
    type<&derived>::stuff(); 
} 

不用說了,因爲std::type_info有合適的對象,他們是獨一無二的,有序的,你可以在集合中進行管理,從而消除從界面查詢類型:

template <typename T> struct sensor {virtual ~sensor() {}}; 

struct type 
{ 
    const std::type_info& info; 

    template <typename T> 
    explicit type(sensor<T> t) : info(typeid(t)) 
    {}; 
}; 

bool operator<(const type& lh, const type& rh) 
{ 
    return lh.info.before(rh.info); 
} 

int main() 
{ 
    std::set<type> t; 
    t.insert(type(sensor<Base>())); 
    t.insert(type(sensor<Derived>())); 

    for (std::set<type>::iterator i = t.begin(); i != t.end(); ++i) 
    std::puts(i->info.name()); 
} 

當然,如果您認爲合適,您可以將兩者混合搭配。

兩個限制:

  • 這裏沒有實際的反省。你可以通過聰明的元編程把它添加到template struct sensor,它是非常廣泛的主題(有時會彎曲)。
  • 您想要支持的所有類型的名稱必須提前知道。

一種可能的變化是增加RTTI「框架鉤」,如static const sensor<Myclass> rtti_MyClass;實施文件,其中的類名稱是已知的,並讓構造函數做的工作。在這一點上,它們也必須是完整的類型,以便在傳感器中進行反思。

相關問題