Forward declarations de enumerados

Continuando las discusiones con los forward declarations, podemos destacar un pequeño tipo de dato que nunca tuvo esta capacidad, por lo menos no hasta C++11: Los _enum_erados.

Los enum siempre fueron un problema en la definición de APIs. Actualmente, si queremos usar un enumerado en una función o como miembro de una clase, debemos tener su definición completa con anterioridad, inclusive el listado de valores posibles que puede tomar el enumerado.

Esto es muy sencillo de ver en el siguiente ejemplo. Imaginemos que nuestro programa se comunica con el usuario por medio de una GUI (ventanas, botones, controles de todo tipo). Cada control puede recibir mensajes desde el sistema operativo, por ejemplo, si el usuario clickea un botón, tiene sentido que recibamos el mensaje BotonMousePresionado.

Aquí un enumerado con el listado de posibles mensajes que podemos recibir (imaginemos esto en un archivo tipo_mensaje.h):

enum TipoMensaje {
  AbrirVentana,
  CerrarVentana,
  BotonMousePresionado,
  TeclaPresionada
};

En el caso de que declaremos una clase que utiliza el enumerado, necesitamos de la definición de TipoMensaje completa:

#include "tipo_mensaje.h"   // Necesitamos la definición del enumerado

class Mensaje {
  TipoMensaje mensaje;
public:
  // ...
};

Inclusive aunque no utilicemos los valores posibles del enumerado, no hay forma de desacoplarlo. Esto se debe a que según el estándar de C++, la cantidad de memoria usada por un enumerado depende de la implementación del compilador y del rango de valores permitidos del enumerado. Ejemplo: Un compilador podría decidir usar un char en vez de un int para representar nuestro TipoMensaje.

¿Cuál es la desventaja?
Debido a que el compilador decide, según reglas internas y los valores posibles del enum, qué tipo de dato usar para representarlo, cada vez que agreguemos un nuevo valor al enumerado debemos recompilar todos los archivos que lo estaban referenciando.

¿Cómo podemos solucionarlo?
C++11 introduce el concepto de los “enumerados fuertemente tipados” (strongly typed enumerations). Con lo cual podemos decidir qué tamaño específico tiene el enumerado.

Si conocemos con anterioridad cuántos valores queremos codificar en nuestro enum, podemos especificar la cantidad de memoria requerida por el mismo. Podemos definir un enumerado que ocupe sólo 8 bits:

enum TipoMensaje : char {
  AbrirVentana,
  CerrarVentana,
  BotonMousePresionado,
  TeclaPresionada
}

Y si necesitamos referenciarlo:

enum TipoMensaje : char;    // Forward declaration

class Mensaje {
  TipoMensaje mensaje;
public:
  // ...
};

¿Por qué se llaman “enumerados fuertemente tipados”?
Ese es otro tema que lo dejo para un próximo post.


Referencias: N2347 (pdf)


 10 April 2012 | cpp programacion cpp11