2011-09-06 182 views
40

使用C++模板進行元編程時,是否有一種方法可以像調試器那樣使用,以便逐步瞭解如何實例化和編譯模板?現在看來,在創建一個複雜的模板網絡時,除了查看編譯器錯誤消息以查看如何實例化模板(如果存在任何編譯器錯誤),實際上並沒有很好的調試方法,並嘗試從錯誤消息中反向工作,如果出現意想不到的情況。我不確定是否存在我正在尋找的東西,因爲它必須是在編譯時完成的東西,但基本上它會是一種方法,有點像遍歷代碼並檢查堆棧框架gdb在運行時,其中可以停止編譯器,並且環境檢查模板或嵌套模板集實例化的順序。調試模板實例化

舉例來說,假設我創建了一些簡單的代碼如下所示:

template<typename T, typename R = void> 
struct int_return_type {}; 

template<typename R> 
struct int_return_type<int, R> 
{ 
    typedef R type; 
}; 

template<typename T, typename R = void> 
struct float_return_type {}; 

template<typename R> 
struct float_return_type<float, R> 
{ 
    typedef R type; 
}; 

template<typename T> 
typename int_return_type<T>::type test() 
{ 
    cout << "T type is int" << endl; 
} 

template<typename T> 
typename float_return_type<T>::type test() 
{ 
    cout << "T type is float" << endl; 
} 

int main() 
{ 
    test<int>(); 
    test<float>(); 
    return 0; 
} 

我知道這是比較容易的代碼跟隨,但這樣做的元編程時,模板可以更多地參與了不少,尤其是,遞歸等等。我知道編譯器會發出錯誤消息,可以用來推斷如何實例化模板,但是我也想知道當實際模板代碼在句法意義上是正確的時候可以做什麼,但是運行時結果仍然不正確。例如,有一種方法可以停止編譯器,並查看test以及int_return_typefloat_return_type正在被實例化或哪些實例失敗。

是目前唯一可用於調試具有此粒度級別的模板的代碼1)代碼不正確時的編譯器錯誤消息,以及2)反彙編程序和調試程序的組合以查看運行時生成的實例化代碼時間結果不正確?還是有其他一些實用工具可以幫助「觀察」模板的實例化,並查看/檢查編譯器生成什麼代碼來調查和調試模板錯誤?

+3

除'static_assert'以外,你想要觀察錯誤的東西我不認爲有什麼可以幫助 – Flexo

回答

26

這些都很基本,但在大多數情況下它們都適用於我。我很想看看別人也有什麼要說的。

道歉的人爲的例子。

使用沙箱

儘快與小沙箱測試模板代碼開始,因爲它開始行爲怪異,或者你正在做的事情複雜化。我對模板非常舒服,而且我幾乎立即做到了這一點。簡而言之,它可以更快地發現錯誤。你已經在這裏爲我們完成了,所以我認爲這是沒有意義的。

指定臨時類型

臨時變量可以混淆您的意圖沒有得到滿足。我已經看到了很多類似於下面的代碼。

template<typename T> 
    T calc(const T &val) { 
    return some_other_calc(val)/100.0; 
    } 

告訴你期望什麼類型將失敗更快,而且可能會給你一個更好的消息來處理編譯器。

template<typename T> 
    T calc(const T &val) { 
    T val_ = some_other_calc(val); 
    return val_/100.0; 
    } 

使用TYPEID

使用typeid(T).name()打印在調試報表模板名稱。這會給你一個字符串,你可以用它來看看編譯器是如何決定實現這種類型的。

template<typename T> 
    typename void test() { 
    std::cout << "testing type " << typeid(T).name() << std::endl; 
    // ... 
    } 

避免不必要的默認實現

寫模板以這樣的方式,他們有默認的實現。

template<typename T, bool is_integral = boost::is_numeric<T>::value > 
    struct my_traits; 

template<typename T> 
    struct my_traits<T, true> { 
    typedef uint32_t cast_type; 
    }; 

template<typename T> 
    void print_whole_number(T &val) { 
    std::cout << static_cast<my_traits<T>::cast_type>(val) << std::endl; 
    } 

這種強制的print_whole_number用戶都有自己my_traits專業化。他們將得到一個編譯器錯誤,而不是一半的工作,因爲你無法爲所有類型提供良好的實現。誠然,如果在代碼庫的不同部分中使用編譯器錯誤,將不會立即有所幫助。

+9

+1 *以不具有默認實現的方式編寫模板* – Manu343726

3

我喜歡使用優秀的基於Web的Comeau編譯器進行調試。它可以注意到在其他編譯器不能執行的標準彙編方面的錯誤...

Comeau具有比GCC或MSVC提供更多可讀錯誤消息的巨大優勢。

另外,請記住儘可能在任何地方使用static_assert - 即使您確定答案是正確的。

+0

順便說一句,我知道這是一個小題外話,但我很好奇,我的代碼示例似乎是[編譯就好](http://ideone.com/zcLmt),或者我不會發布它(沒有什麼比發佈代碼不會編譯)...... Comeau編譯器中的什麼設置是你的使用? – Jason

+0

@Jason,使用C++ 0x擴展的嚴格模式。 –

+0

@Jason,哦,我愚蠢,沒有粘貼整個代碼^^ –