Подтвердить что ты не робот

Как я могу расширить лексический бросок, чтобы поддерживать перечисленные типы?

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

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

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

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

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

Спасибо

4b9b3361

Ответ 1

Вам нужно сделать два шага. Поиск интегрального типа, достаточно большого для хранения значений. Вы можете использовать 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; };

Теперь вы можете получить правильный тип. Теперь вам нужно увидеть, является ли какой-то тип перечислением. Как это сделать описано в книге "С++ 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;
}

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

Ответ 2

И просто, чтобы "заполнить" вопрос, в С++ 0x мы можем просто сделать это:

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

Вместо трюка Йоханнеса get_etype.