Как я могу расширить лексическое приведение для поддержки перечислимых типов? - PullRequest
22 голосов
/ 07 октября 2009

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

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

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

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

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

Спасибо

Ответы [ 2 ]

43 голосов
/ 07 октября 2009

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

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

10 голосов
/ 22 января 2011

И чтобы просто «завершить» вопрос, в C ++ 0x мы можем просто сделать это:

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

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

...