separado - ¿Es posible tener una enumeración de enumeraciones en c++?
token c++ (4)
¿Es posible tener una enumeración de enumeraciones en c ++. Tengo que tener algo como:
Tipos de error:
- Tipo 1
- Tipo 2
- Tipo 3
Tipo 1:
- causa1
- causa2
Tipo 2:
- causa 3
- causa 4
Type3:
- causa5
- causa6
Cada uno de estos son valores enteros. Se supone que deben usarse en una pila de protocolos de comunicación. En el lado receptor, el receptor tiene que descodificar el tipo y la causa del error a partir de los valores recibidos. Si no se pueden usar las enumeraciones, ¿cuál sería la mejor manera de hacerlo?
Como dijo Jerry, no es posible directamente. Una forma de resolver esto es tener dos enumeraciones. Uno para la categoría, y otro para la subcategoría.
Sin embargo, como dijo Georgesl, es probable que sea peligroso hacerlo en un protocolo. Definitivamente debes definir explícitamente los valores de enumeración:
struct Error
{
enum Type {
UNKNOWNTYPE = 0,
TYPE1 = 1,
TYPE2 = 2,
TYPE3 = 3
};
enum Subtype {
UNKNOWNSUBTYPE = 0,
// subtype for error type 1
CAUSE1 = 1001,
CAUSE2 = 1002,
CAUSE3 = 1003,
// subtype for error type 2
CAUSE4 = 2001,
CAUSE5 = 2002
};
Type type;
Subtype subtype;
};
int main()
{
Error error;
error.type = Error::TYPE1;
error.subtype = Error::CAUSE1;
}
Asegúrese de elegir los números con prudencia para futuras extensiones.
Actualización: hecho que el ejemplo realmente funcione.
Alternativa, solución más segura:
struct ErrorType
{
enum type {
UNKNOWNTYPE = 0,
TYPE1 = 1,
TYPE2 = 2,
TYPE3 = 3
};
};
struct ErrorSubtype
{
enum type {
UNKNOWNSUBTYPE = 0,
// subtype for error type 1
CAUSE1 = 1001,
CAUSE2 = 1002,
CAUSE3 = 1003,
// subtype for error type 2
CAUSE4 = 2001,
CAUSE5 = 2002
};
};
struct Error
{
ErrorType::type type;
ErrorSubtype::type subtype;
};
int main()
{
Error error;
error.type = ErrorType::TYPE1;
error.subtype = ErrorSubtype::CAUSE1;
}
Ni siquiera estoy seguro de lo que significaría una enumeración de enumeraciones. Pero la forma habitual de manejar esto es definir los rangos en una sola enumeración:
enum Errors
{
type1 = 0x000,
cause1,
cause2,
type2 = 0x100,
cause3,
cause4,
...
causeMask = 0xFF,
typeMask = 0xFF00
};
O simplemente defina enumeraciones separadas, en palabras separadas, y use unsigned
(o unsigned short
, o lo que sea) y un poco de casting para las diferentes causas.
Cualquiera que sea la solución adoptada, la encapsularía en una clase, de modo que el código del cliente solo tiene que lidiar con errorType()
y errorCause()
; errorCause()
incluso podría ser una plantilla en el valor del tipo de error. (Pero en algún lugar, necesitará especializaciones explícitas para cada valor de tipo, porque el compilador de otra manera no sabrá cómo asignar el valor para causar el tipo).
No recomendaría hacer esto. Prefiere utilizar un tipo de error explícito, que contenga información sobre errores (podría agregar cadenas, etc.). Además esto no es muy seguro. Véase también la respuesta de James.
Pero de todos modos aquí está la versión macro malvada:
#define DECL_ERROR_TYPE(errorType, value) , errorType = value << 16
#define DECL_ERROR(errorType, cause, value) , errorType##_##cause = (errorType + value)
#define GET_ERROR_TYPE(error) (error & 0xFFFF0000)
enum Error
{
NoError = 0
DECL_ERROR_TYPE(Type1, 1)
DECL_ERROR(Type1, Cause1, 1)
DECL_ERROR(Type1, Cause2, 2)
DECL_ERROR_TYPE(Type2, 2)
DECL_ERROR(Type2, Cause1, 1)
DECL_ERROR_TYPE(Type3, 3)
DECL_ERROR(Type3, Cause1, 1)
DECL_ERROR(Type3, Cause2, 2)
};
Esto te permite usarlo así:
Error err1 = Type1_Cause1;
if(Type1 == GET_ERROR_TYPE(err1))
return 0; // Works
Simplemente no podía soportar usar enumeraciones. Así que tengo otra respuesta usando tipos explícitos. No está completo, pero muestra la dirección correcta y contiene la posible extensión de agregar descripciones, etc.
Aquí está el código para la declaración:
struct Error
{
public:
struct ErrorType
{
int _code;
ErrorType(int code) : _code(code << 16) {}
};
private:
friend struct Errors;
ErrorType _type;
int _code;
Error(ErrorType type, int causeCode)
: _type(type), _code(causeCode)
{
}
static std::map<int, Error> _errors;
public:
Error() : _type(-1), _code(-1) {}
static Error FromCode(int code) { return _errors[code]; }
bool IsOfType(const ErrorType& type )
{
return _type._code == type._code;
}
operator int()
{
return _code | _type._code;
}
bool operator == (Error const& other) const
{
return _code == other._code && _type._code == other._type._code;
}
bool operator != (Error const& other) const
{
return _code != other._code || _type._code != other._type._code;;
}
};
std::map<int, Error> Error::_errors;
struct Errors
{
#define BEGIN_TYPE(type, code) struct type : Error::ErrorType { type() : ErrorType(code) {} typedef Errors::##type CurrentType;
#define CAUSE(cause, code) struct cause : Error { cause() : Error(CurrentType(),code) { Error::_errors[*this] = *this; } };
#define END_TYPE() };
// first type is coded manually to show what the macros do...
struct Type1 : Error::ErrorType
{
Type1() : ErrorType(1) { }
typedef Errors::Type1 CurrentType;
struct Cause1 : Error
{
Cause1() : Error(CurrentType(),1) { Error::_errors[*this] = *this; }
};
struct Cause2 : Error
{
Cause2() : Error(CurrentType(),2) { Error::_errors[*this] = *this; }
};
};
BEGIN_TYPE(Type2, 2)
CAUSE(Cause1, 1)
CAUSE(Cause2, 2)
END_TYPE()
};
Y aquí hay algunos ejemplos de usos:
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
Error err = Errors::Type1::Cause1();
Q_ASSERT( err.IsOfType(Errors::Type1()) );
Q_ASSERT( Errors::Type1::Cause1() == Errors::Type1::Cause1() );
Q_ASSERT( Errors::Type1::Cause1() != Errors::Type2::Cause1() );
int code = err;
qDebug() << code;
Q_ASSERT( Error::FromCode(code) == Errors::Type1::Cause1() );
Q_ASSERT( Error::FromCode(code) != Errors::Type2::Cause1() );
Q_ASSERT( Error::FromCode(code).IsOfType(Errors::Type1()) );
return a.exec();
}
No es una solución perfecta, pero muestra cómo esto se puede manejar de una manera más explícita. Hay muchas mejoras que uno puede hacer ...