2008-09-28 111 views
4

從'現代C++設計'的以下技術,我正在實現一個具有各種編譯時優化的持久性庫。我想派遣功能,模板化的成員變量的能力,如果該變量從給定的類派生:基於編譯時類型的調度

template<class T, template <class> class Manager = DefaultManager> class Data 
{ 
private: 
    T *data_; 

public: 
    void Dispatch() 
    { 
     if(SUPERSUBCLASS(Container, T)) 
     { 
     data_->IKnowThisIsHere(); 
     } 
     else 
     { 
     Manager<T>::SomeGenericFunction(data_); 
     } 
    } 
} 

凡SUPERSUBCLASS是一個編譯時宏來確定對象繼承。當然,在所有T從Container繼承的情況下(或者T是一個內部類型等),這都會失敗,因爲編譯器會正確地抱怨IKnowThisIsHere()不是數據成員,即使這條代碼路徑永遠不會被遵循,如T = int預處理後所示:

private: 
    int *data_; 

public: 
    void Dispatch() 
    { 
     if(false) 
     { 
     data_->IKnowThisIsHere(); 

編譯器明顯抱怨此代碼,即使它永遠不會執行。使用的dynamic_cast的建議也不起作用,因爲再次類型轉換試圖在編譯時這是不可能的(例如使用T =雙,的std :: string):

void Dispatch() 
    { 
     if(false) 
     { 
     dynamic_cast<Container*>(data_)->IKnowThisIsHere(); 

error: cannot dynamic_cast '((const Data<double, DefaultManager>*)this)->Data<double, DefaultManager>::data_' (of type 'double* const') to type 'class Container*' (source is not a pointer to class) 
error: cannot dynamic_cast '((const Data<std::string, DefaultManager>*)this)->Da<sttad::string, DefaultManager>::data_' (of type 'struct std::string* const') to type 'class Container*' (source type is not polymorphic) 

我真的需要模擬(或確實勸說!)如果T從Container繼承,則編譯器發出一組代碼;如果不從Container繼承,則編譯器發出一組代碼。

有什麼建議嗎?

回答

3

超載可能是有用的實施編譯時DIS:;運行時調用))編譯器,如果你在非多晶型或類類型傳遞,以類似的方式來之前抱怨正如Alexandrescu在他的書「現代C++設計」中提出的那樣。

您可以使用像這樣一類在編譯時的布爾或整數變換成一個類型:

template <bool n> 
struct int2type 
{ enum { value = n}; }; 

下面的源代碼顯示了一個可能的應用:

#include <iostream> 

#define MACRO() true // <- macro used to dispatch 

template <bool n> 
struct int2type 
{ enum { value = n }; }; 

void method(int2type<false>) 
{ std::cout << __PRETTY_FUNCTION__ << std::endl; } 

void method(int2type<true>) 
{ std::cout << __PRETTY_FUNCTION__ << std::endl; } 

int 
main(int argc, char *argv[]) 
{ 
    // MACRO() determines which function to call 
    // 

    method(int2type<MACRO()>()); 

    return 0; 
} 

當然什麼真的使得工作成爲MACRO()或更好的實現作爲元功能

+0

感謝長久 - 這正是我之後。 – user23167 2008-09-28 16:07:10

0

查找到升壓模板元編程庫。另外,根據你正在嘗試完成的內容,查看boost序列化庫,因爲它可能已經有了你所需要的。

0

我有興趣做這個'從第一原則'作爲教育的好奇心。不過,我會看看Boost庫。

在任何情況下,我不認爲is_base_of是任何幫助 - 它完全一樣的SUPERSUBCLASS宏...

0

不幸的是我經歷過的太(它是上也是如此,

error: cannot dynamic_cast '((const Data<double, DefaultManager>*)this)->Data<double, RawManager>::data_' (of type 'double* const') to type 'class Container*' (source is not a pointer to class) 

error: cannot dynamic_cast '((const Data<std::string, DefaultRawManager>*)this)->Data<std::string, DefaultManager>::data_' (of type 'struct std::string* const') to type 'class Container*' (source type is not polymorphic) 
2

您需要一種編譯時if。然後根據哪種情況調用函數true。這樣,編譯器就不會偶然發現它無法編譯的代碼(因爲它安全地存儲在另一個永遠不會實例化的函數模板中)。

實現這種編譯時有幾種方法if。最常見的是採用SFINAE成語:substitution failure is not an error。 Boost的is_base_of實際上是這個成語的一個實例。要正確使用它,您不會將它寫入if表達式中,而是將其用作函數的返回類型。

未經測試的代碼:

void Dispatch() 
{ 
    myfunc(data_); 
} 

private: 

// EDIT: disabled the default case where the specialisation matched 
template <typename U> 
typename enable_if_c<is_base_of<Container, U>::value, U>::type myfunc(U& data_) { 
    data_->IKnowThisIsHere(); 
} 

template <typename U> 
typename disable_if_c<is_base_of<Container, U>::value, U>::type myfunc(U& data_) { // default case 
    Manager<U>::SomeGenericFunction(data_); 
} 
+0

該方法將導致模糊的重載。當is_base_of <>元函數成功時,您應該讓編譯器刪除第二個方法(默認情況)。 – 2008-09-28 15:32:56