2014-01-06 89 views
0

我正在使用this魔法頭來獲得輕鬆序列化STL容器的功能。擴展enable_if類型以排除匹配類型

不過,我現在已經轉移到更看中的HTML序列化器我的類型,和部分我希望做的是推廣了operator <<功能到我的新類型ohtmlstringstream這是由stringstream支持。

這裏是我的(功能)試圖做到這一點(ohtmlstringstream::write是傳遞到私有成員stringstreamoperator<<其ARG公共模板方法):

namespace std { 
    template<typename T> 
    inline typename enable_if< ::pretty_print::is_container<T>::value, ohtmlstringstream&>::type 
    operator<<(ohtmlstringstream& os, const T& container) { 
     auto it = std::begin(container); 
     const auto the_end = end(container); 
     os.write("<div class='container'>"); 
     while(it != the_end) { 
      os << *it; 
      it++; 
     } 
     os.write("</div>"); 
     return os; 
    } 
} 

,我遇到的第一個問題如果在ohtmlstringstream上使用std::string,則將其視爲容器,這是我不想要的;我想將字符串視爲字符串,而不是容器。當然,就pretty_print而言,std :: string肯定是一個chars的容器。

這是prettyprint.hpp摘錄:

namespace pretty_print 
{ 

    // SFINAE type trait to detect whether T::const_iterator exists. 

    template<typename T> 
    struct has_const_iterator 
    { 
    private: 
     typedef char      yes; 
     typedef struct { char array[2]; } no; 

     template <typename C> static yes test(typename C::const_iterator*); 
     template <typename C> static no test(...); 
    public: 
     static const bool value = sizeof(test<T>(0)) == sizeof(yes); 
     typedef T type; 
    }; 

    // SFINAE type trait to detect whether "T::const_iterator T::begin/end() const" exist. 

    template <typename T> 
    struct has_begin_end_OLD 
    { 
     struct Dummy { typedef void const_iterator; }; 
     typedef typename std::conditional<has_const_iterator<T>::value, T, Dummy>::type TType; 
     typedef typename TType::const_iterator iter; 

     struct Fallback { iter begin() const; iter end() const; }; 
     struct Derived : TType, Fallback { }; 

     template<typename C, C> struct ChT; 

     template<typename C> static char (&f(ChT<iter (Fallback::*)() const, &C::begin>*))[1]; 
     template<typename C> static char (&f(...))[2]; 
     template<typename C> static char (&g(ChT<iter (Fallback::*)() const, &C::end>*))[1]; 
     template<typename C> static char (&g(...))[2]; 

     static bool const beg_value = sizeof(f<Derived>(0)) == 2; 
     static bool const end_value = sizeof(g<Derived>(0)) == 2; 
    }; 

    template <typename T> 
    struct has_begin_end 
    { 
     template<typename C> static char (&f(typename std::enable_if< 
              std::is_same<decltype(static_cast<typename C::const_iterator (C::*)() const>(&C::begin)), 
              typename C::const_iterator(C::*)() const>::value, void>::type*))[1]; 

     template<typename C> static char (&f(...))[2]; 

     template<typename C> static char (&g(typename std::enable_if< 
              std::is_same<decltype(static_cast<typename C::const_iterator (C::*)() const>(&C::end)), 
              typename C::const_iterator(C::*)() const>::value, void>::type*))[1]; 

     template<typename C> static char (&g(...))[2]; 

     static bool const beg_value = sizeof(f<T>(0)) == 1; 
     static bool const end_value = sizeof(g<T>(0)) == 1; 
    }; 

    // Basic is_container template; specialize to derive from std::true_type for all desired container types 

    template<typename T> struct is_container : public ::std::integral_constant<bool, 
    has_const_iterator<T>::value && has_begin_end<T>::beg_value && has_begin_end<T>::end_value> { }; 

    template<typename T, std::size_t N> struct is_container<T[N]> : public ::std::true_type { }; 

    template<std::size_t N> struct is_container<char[N]> : public ::std::false_type { }; 

    template <typename T> struct is_container< ::std::valarray<T>> : public ::std::true_type { }; 

...<snip> 

這裏的問題是,它不是我清楚我如何使用SFINAE和enable_if和這個東西剩下來構建另一個謂詞演算值爲真所有集裝箱除外std::string

現在,這只是第一個問題。第二個問題是我的第一個代碼清單中的行os.write("<div class='container'>");。請注意這是多麼惱人的不確定性。我真的希望容器的序列化例程報告容器的實際類型(無論是std::map還是std::forward-liststd::vector)。

我想知道的是,是否存在一些(合理理智的)方法來實現這個模板,或者我真的應該使用宏來明確定義一系列模板,每個STL容器類型一個:這樣我就可以輕鬆地構建任何給定容器所需的確切種類的HTML。

確實,使用模板枚舉所有STL容器可以解決這兩個問題。我想我會開始這樣做。但我仍然想知道第一個問題的答案。我如何使用enable_if排除特定類型?

+0

'我想將字符串視爲字符串,而不是容器'什麼 –

+0

':: pretty_print :: is_container :: value' =='true'。它有一個'const_iterator',它有開始和結束。不過,我還想知道「has_begin_end」實際上在做什麼以及它爲什麼起作用。 –

+0

@StevenLu,在SFINAE部分沒有那麼好,我可以告訴你'f'是一個函數,它返回一個N個字符數組的引用。它在參數的類型中使用SFINAE。我想它的工作原理與複雜的一個不同,如果該成員不存在,則不會在底部的調用中考慮。我不知道它是否必須在參數中,但'decltype' [在返回類型中](http://stackoverflow.com/questions/16762263/what-does-after-a-function-prototype-意思是)可讀性更強。我想這個也確保它是一個正確類型的函數。 – chris

回答

0

&&!std::is_same<Foo, std::string>::value加到您的enable_if測試中。

enable_if<條件,可選類型>::type的語法可以幫助 - 條件是任何編譯時bool。你可以在那裏扔更多的東西!

如果您想要重複使用一個特徵,只需創建一個從std::integral_constant<bool,邏輯繼承的特徵>{};

如果您有完整的C++ 11支持,請嘗試編寫一個constexpr函數,其類型爲T,並返回true而不是特徵類。在C++ 1y中,當lite概念到達時這可能會有用。

+0

處理第二個問題的任何技巧?我想我可以寫更多的部分模板專業化或東西? –

+0

@stevenlu只需編寫一個快速traits類,該類具有返回cotainer名稱的靜態方法。要麼保持未被特化的特徵不被破壞以獲得錯誤,要麼給它一個通用名稱。這與代碼的其餘部分是正交的... – Yakk

+0

ahh .....謝謝 –