c++ - utiliza - tipo enum java
¿Cómo puedo extender un elenco léxico para admitir tipos enumerados? (2)
Tengo la siguiente función que convertirá una cadena en un tipo de datos numéricos:
template <typename T>
bool ConvertString(const std::string& theString, T& theResult)
{
std::istringstream iss(theString);
return !(iss >> theResult).fail();
}
Sin embargo, esto no funciona para los tipos enumerados, así que he hecho algo como esto:
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;
}
(Estoy asumiendo que theString tiene un valor válido para el tipo enumerado; lo estoy usando principalmente para la serialización simple)
¿Hay alguna manera de crear una sola función que combine ambos?
He jugado un poco con los argumentos de la plantilla, pero no he encontrado nada; sería agradable no tener que llamar a una función para tipos enumerados y otra para todo lo demás.
Gracias
Tienes que hacer dos pasos. Encontrar un tipo integral lo suficientemente grande como para almacenar los valores. Puede usar unsigned long
, pero los valores pueden ser negativos. Entonces podría usar long
pero los valores podrían extenderse dentro del rango de unsigned long
. Entonces no hay realmente un tipo apto para todo.
Sin embargo, hay un truco al usar una resolución de sobrecarga. Aquí es
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);
Puede cambiarlo apropiadamente para cubrir también long long
o unsigned long long
si su implementación tiene soporte para eso. Ahora, al pasar un tipo enum, preferirá uno de estos sobre todos los demás; es un tipo que puede almacenar todos los valores del mismo. Solo necesita pasar el sizeof
del tipo de devolución a alguna plantilla.
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; };
Ahora, puedes obtener un tipo correcto. Todo lo que necesita ahora es ver si algún tipo es una enumeración. Cómo hacer esto se describe en el libro "Plantillas C ++ - La guía completa", y desafortunadamente es una gran cantidad de código. Entonces usaría el boost is_enum
. Poniéndolo juntos, podría parecerse
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;
}
Espero que esto ayude.
Y solo para "completar" la pregunta, en C ++ 0x podemos hacer esto:
typedef typename std::underlying_type<T>::type safe_type;
En lugar de Johannes get_etype
truco.