2009-10-06 12 views
21

Mam następującą funkcję, która będzie przekonwertować ciąg do numerycznej typ danych:Jak mogę rozszerzyć obsadę leksykalną do obsługi typów wyliczeniowych?

template <typename T> 
bool ConvertString(const std::string& theString, T& theResult) 
{ 
    std::istringstream iss(theString); 
    return !(iss >> theResult).fail(); 
} 

to nie działa dla wymienionych typów jednak tak zrobiłem coś takiego:

template <typename T> 
bool ConvertStringToEnum(const std::string& theString, T& theResult) 
{ 
    std::istringstream iss(theString); 
    unsigned int temp; 
    const bool isValid = !(iss >> temp).fail(); 
    theResult = static_cast<T>(temp); 
    return isValid; 
} 

(Podejmuję założenie, że ciąg znakówString ma poprawną wartość dla typu wyliczeniowego, używam tego głównie do prostej serializacji)

Czy istnieje sposób utworzenia pojedynczej funkcji, która łączy oba te elementy?

Grałem trochę z argumentami szablonu, ale nie wymyśliłem niczego; byłoby miło, gdyby nie trzeba było wywoływać jednej funkcji dla typów wyliczeniowych i innej dla wszystkiego innego.

Dziękujemy

Odpowiedz

42

Musisz zrobić dwa kroki. Znalezienie typu integralnego wystarczająco dużego, aby zapisać wartości. Można użyć wartości unsigned long, ale wartości mogą być ujemne. Wtedy możesz użyć long, ale wartości mogą rozciągać się na zakres unsigned long. Tak naprawdę nie jest to odpowiedni typ.

Istnieje jednak pewna sztuczka, dzięki zastosowaniu rozdzielczości przeciążenia. Oto ona

template<typename T> 
struct id { typedef T type; }; 

id<char[1]>::type &find_etype(int); 
id<char[2]>::type &find_etype(unsigned int); 
id<char[3]>::type &find_etype(long); 
id<char[4]>::type &find_etype(unsigned long); 

Można go zmienić odpowiednio do pokrycia również long long lub unsigned long long jeśli realizacja ma wsparcia dla tego. Teraz przekazanie typu wyliczeniowego będzie preferowało jeden z nich nad wszystkimi innymi - jest to typ, który może przechowywać wszystkie jego wartości. Po prostu musisz przekazać sizeof typu zwracanego do jakiegoś szablonu.

template<int> struct get_etype; 
template<> struct get_etype<1> { typedef int type; }; 
template<> struct get_etype<2> { typedef unsigned int type; }; 
template<> struct get_etype<3> { typedef long type; }; 
template<> struct get_etype<4> { typedef unsigned long type; }; 

Teraz możesz uzyskać odpowiedni typ. Teraz wystarczy sprawdzić, czy jakiś typ jest wyliczeniem. Jak to zrobić jest opisane w książce "Szablony C++ - Kompletny przewodnik" i niestety jest dużo kodu. Więc użyłbym boosta is_enum. Łączenie go może wyglądać tak, jakby to miało pomóc. Mam nadzieję, że to pomoże.

+0

+1. Miałem też napisać komentarz o tym, dlaczego nie było to w standardowej bibliotece, dopóki nie przeczytam odpowiedzi GMana poniżej. – Jon

+0

Zwiększenie nie jest już potrzebne, ponieważ std :: enable_if i std :: is_enum zapewniają te funkcjonalności w C++ 11 – moala

10

I właśnie do "pełna" kwestia, w C++ 0x możemy po prostu to zrobić:

typedef typename std::underlying_type<T>::type safe_type; 

W miejsce Johannes get_etype sztuczki.

+2

Awesome; jaka wygodna funkcja. Byłem takim noobem, gdy zadałem to pytanie ... To znaczy, nadal jestem noobem, ale byłem wtedy bardziej noobem. –

Powiązane problemy