2011-04-24 18 views
11

Chcę mieć kilka przeciążonych, globalnych funkcji to_string(), które pobierają pewien typ T i przekształcają go na jego reprezentację ciągów. W ogólnym przypadku, chcę być w stanie napisać:Używanie SFINAE do sprawdzania globalnego operatora <<?

template<typename T,class OutputStringType> inline 
typename enable_if<!std::is_pointer<T>::value 
       && has_insertion_operator<T>::value, 
        void>::type 
to_string(T const &t, OutputStringType *out) { 
    std::ostringstream o; 
    o << t; 
    *out = o.str(); 
} 

Moja implementacja has_insertion_operator tak daleko jest:

struct sfinae_base { 
    typedef char yes[1]; 
    typedef char no[2]; 
}; 

template<typename T> 
struct has_insertion_operator : sfinae_base { 
    template<typename U> static yes& test(U&); 
    template<typename U> static no& test(...); 

    static std::ostream &s; 
    static T const &t; 

    static bool const value = sizeof(test(s << t)) == sizeof(yes); // line 48 
}; 

(pożycza on z this i this.) To wydaje się działać . Ale teraz chcę mieć przeciążony wersję to_string dla typów, które wykonują nie mieć operator<< ale zrobić mają własne to_string()członek funkcję, a mianowicie:

template<class T,class OutputStringType> inline 
typename enable_if<!has_insertion_operator<T>::value 
       && has_to_string<T,std::string (T::*)() const>::value, 
        void>::type 
to_string(T const &t, OutputStringType *out) { 
    *out = t.to_string(); 
} 

Realizacja has_to_string jest:

#define DECL_HAS_MEM_FN(FN_NAME)          \ 
    template<typename T,typename S>          \ 
    struct has_##FN_NAME : sfinae_base {        \ 
    template<typename SignatureType,SignatureType> struct type_check; \ 
    template<class U> static yes& test(type_check<S,&U::FN_NAME>*); \ 
    template<class U> static no& test(...);       \ 
    static bool const value = sizeof(test<T>(0)) == sizeof(yes); \ 
    } 

DECL_HAS_MEM_FN(to_string); 

(Ta część działa dobrze, została zaadaptowana z this.) Jednak kiedy mam:

struct S { 
    string to_string() const { 
    return "42"; 
    } 
}; 

int main() { 
    string buf; 
    S s; 
    to_string(s, &buf); // line 104 
} 

uzyskać:

foo.cpp: In instantiation of ‘const bool has_insertion_operator<S>::value’: 
foo.cpp:104: instantiated from here 
foo.cpp:48: error: no match for ‘operator<<’ in ‘has_insertion_operator<S>::s << has_insertion_operator<S>::t’ 

Wydaje się SFINAE nie dzieje. Jak poprawnie napisać has_insertion_operator, aby określić, czy dostępny jest globalny operator<<?

FYI: Używam g ++ 4.2.1 (który jest dostarczany jako część Xcode na Mac OS X). Również chciałbym, aby kod był tylko standardowym C++ 03 bez bibliotek 3rd party, np. Boost.

Dzięki!

+1

To wszystko na pewno możliwe, ale * dlaczego *? – Potatoswatter

+2

@Potatoswatter: dlaczego nie jest ważne. Proszę przyjąć, że przez resztę mojego projektu wiem, co robię. Jeśli musisz wiedzieć, jest to część struktury do przekazywania parametrów dowolnego typu, aby utworzyć część zlokalizowanego komunikatu o błędzie.Szczegóły wszystkich, które są niepotrzebne na to pytanie. Jeśli wiesz, jak to zrobić, odpowiedz na pytanie. Byłoby to bardzo doceniane. –

+2

Dlaczego zawsze jest ważne. – GManNickG

Odpowiedz

10

Po prostu powinienem być bardziej wierny odpowiedzi this. Implementacja pracy jest:

namespace has_insertion_operator_impl { 
    typedef char no; 
    typedef char yes[2]; 

    struct any_t { 
    template<typename T> any_t(T const&); 
    }; 

    no operator<<(std::ostream const&, any_t const&); 

    yes& test(std::ostream&); 
    no test(no); 

    template<typename T> 
    struct has_insertion_operator { 
    static std::ostream &s; 
    static T const &t; 
    static bool const value = sizeof(test(s << t)) == sizeof(yes); 
    }; 
} 

template<typename T> 
struct has_insertion_operator : 
    has_insertion_operator_impl::has_insertion_operator<T> { 
}; 

wierzę, że robi nie rzeczywiście polegać na SFINAE.

+0

Cudowny: czy działa w kompilatorach C++ 03? –

+0

Spróbuj i się przekonaj. –

+0

dzięki za kod w każdym razie :) –

1

Inicjator value na linii 48 nie znajduje się w kontekście, w którym działa SFINAE. Spróbuj przenieść wyrażenie do deklaracji funkcji.

#include <iostream> 

struct sfinae_base { 
    typedef char yes[1]; 
    typedef char no[2]; 
}; 

template<typename T> 
struct has_insertion_operator : sfinae_base { 

    // this may quietly fail: 
    template<typename U> static yes& test(
     size_t (*n)[ sizeof(std::cout << * static_cast<U*>(0)) ]); 

    // "..." provides fallback in case above fails 
    template<typename U> static no& test(...); 

    static bool const value = sizeof(test<T>(NULL)) == sizeof(yes); 
}; 

Muszę jednak zakwestionować stopień zaawansowania, jaki się w tym stanie. Widzę nieortogonalne mechanizmy, które będą się mielić ze sobą (to_string vs. operator<<) i słyszę słabe założenia rzucane dookoła (na przykład, że operator<< jest globalne w stosunku do członka, chociaż kod jako zaimplementowany wygląda OK w tym zakresie).

+1

Nie kompiluje się z kilkoma błędami. Pierwszy błąd to: nie ma żadnych argumentów na "test", które zależą od parametru szablonu, więc deklaracja "testu" musi być dostępna. –

+0

BTW: operator << musi być globalny, jeśli ma być operatorem wstawiania, ponieważ pierwszy argument musi być ostream &. –

+0

BTW # 2: ostream :: ostream() jest chroniony. –

Powiązane problemy