2009-10-06 2 views
21

У меня есть следующая функция, которая преобразует строку в числовой тип данных:Как я могу расширить лексический бросок для поддержки перечисленных типов?

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

Это не работает для перечисляемых типов, однако, так что я сделал что-то вроде этого:

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; 
} 

(Я исхожу из предположения, что строка имеет допустимое значение для перечисляемого типа, я использую это в основном для простой сериализации)

Есть ли способ создать единую функцию, которая сочетает в себе обе эти функции?

Я немного поиграл с аргументами шаблона, но ничего не придумал; было бы неплохо не называть одну функцию для перечисленных типов, а другую для всего остального.

Спасибо

ответ

42

Вы должны сделать два шага. Поиск интегрального типа, достаточно большого для хранения значений. Вы можете использовать unsigned long, но значения могут быть отрицательными. Затем вы можете использовать long, но значения могут распространяться на диапазон unsigned long. Таким образом, нет действительно подходящего типа.

Есть трюк, хотя, используя разрешение перегрузки. Вот это

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); 

Вы можете изменить его соответствующим образом, чтобы покрыть также long long или unsigned long long если ваша реализация имеет поддержку для этого. Теперь передача типа перечисления будет предпочтительнее одного из них над всеми другими - это тип, который может хранить все его значения. Вам просто нужно передать sizeof типа возврата в какой-либо шаблон.

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; }; 

Теперь вы можете получить правильный тип. Теперь вам нужно увидеть, является ли какой-то тип перечислением. Как это сделать описано в книге «C++ Templates - полное руководство», и, к сожалению, это целый код. Поэтому я бы использовал boost is_enum. Объединение это могло бы выглядеть так:

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

template <typename T> 
typename boost::enable_if< boost::is_enum<T>, bool>::type 
ConvertString(const std::string& theString, T& theResult) 
{ 
    typedef typename get_etype<sizeof find_etype(theResult)>::type 
     safe_type; 

    std::istringstream iss(theString); 
    safe_type temp; 
    const bool isValid = !(iss >> temp).fail(); 
    theResult = static_cast<T>(temp); 
    return isValid; 
} 

Надеюсь, это поможет.

+0

+1. Я также собирался написать комментарий о том, почему этого не было в стандартной библиотеке, пока я не прочитал ответ GMan ниже. – Jon

+0

boost больше не нужен, поскольку std :: enable_if и std :: is_enum предоставляют эти функции в C++ 11 – moala

10

И только "полный" вопрос, в C++ 0x мы можем просто сделать это:

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

Вместо Johannes get_etype трюком.

+2

Awesome; какая удобная функция. Я был таким noob, когда я задал этот вопрос ... Я имею в виду, я все еще ноб, но тогда я был намного больше. –