c++ - code - enum#c
Enum en C++ como Enum en Ada? (8)
En un momento dado, había analizado la implementación de una clase / plantilla en C ++ que admitiría un Enum que se comportaría como lo hace en Ada. Ha pasado un tiempo desde que pensé en este problema y me preguntaba si alguien alguna vez ha resuelto este problema.
EDITAR:
Mis disculpas, debería aclarar qué funcionalidad creía que era útil en la implementación de Ada de Enum. Dada la enumeración
type fruit is (apple, banana, cherry, peach, grape);
Sabemos que la fruta es una de las frutas listadas: manzana, plátano, cereza, melocotón, uva. Nada realmente diferente de C ++.
Lo que es muy útil son las siguientes piezas de funcionalidad que obtienes con cada enumeración en Ada sin ningún trabajo adicional:
- imprimir un valor enumerado genera la versión de cadena
- puedes incrementar la variable enumerada
- puedes disminuir la variable enumerada
Espero que esto defina el problema un poco más.
Notas agregadas de los comentarios :
- Ver: Wikipedia (Judah Himango, 2008-11-19 a las 0:09)
- Ver: Wikilibros
Características útiles de las enumeraciones de Ada
- El primer valor en la enumeración es
fruit''first
que daapple
. - El último valor en la enumeración es
fruit''last
que dagrape
. - La operación de incremento es
fruit''succ(apple)
que dabanana
. - La operación de disminución es
fruit''pred(cherry)
que también dabanana
. - La conversión de enumeración a entero es
fruit''pos(cherry)
que devuelve2
porque Ada usa enumeraciones basadas en 0. - La conversión de entero a enumeración es
fruit''val(2)
que devuelvecherry
. - La conversión de enumeración a cadena es
fruit''Image(apple)
que devuelve la cadena (mayúscula)"APPLE"
. - La conversión de cadena a enumeración es
fruit''Value("apple")
que devuelve el valorapple
.
Ver también preguntas relacionadas SO:
No existe una manera fácil de hacerlo en C ++, entre otras cosas porque las constantes de enumeración no son obligatorias para ser únicas o contiguas. La conversión de valor a cadena tampoco es trivial; las soluciones que conozco implican hackers de preprocesador C / C ++, y ese es un uso peyorativo del término hackeo.
Estoy tentado de decir "No"; No estoy seguro de que sea correcto, pero ciertamente no es trivial.
Si estás interesado en enumgen, hice una demostración simple con tu ejemplo. Como ya mencioné, lo implementé usando lisp común, por lo que el archivo de entrada que escribe es lispy, pero traté realmente de hacer la sintaxis razonable.
Aquí está:
$ cat Fruit.enum
(def-enum "Fruit" (("apple")
("banana")
("cherry")
("peach")
("grape")
("INVALID_")))
$ enumgen Fruit.enum
Using clisp
;; Loading file /tmp/enumgen/enumgen.lisp ...
;; Loaded file /tmp/enumgen/enumgen.lisp
loading def file:
;; Loading file /tmp/enumgen/enumgen.def ...
;; Loaded file /tmp/enumgen/enumgen.def
generating output:
Fruit.cpp
Fruit.ipp
Fruit.hpp
DONE
Para ver el código generado, visite esta url: http://code.google.com/p/enumgen/source/browse/#svn/trunk/demo
Si bien es bastante rico en funciones, también hay muchas cosas que se pueden ajustar, estableciendo variables en el archivo de entrada o especificando los atributos de los enumeradores.
Por ejemplo, por defecto representa los nombres de cadena usando std :: string, pero puede usar char const * o cualquier clase de cadena definida por el usuario con un poco de esfuerzo.
Puede hacer que múltiples nombres se correlacionen con el mismo valor de enumeración, pero debe elegir uno para que sea el "primario", de modo que asignar el valor a una cadena dará como resultado este nombre (a diferencia de los demás).
Puede proporcionar valores explícitamente a las enumeraciones, y no es necesario que sean únicos. (Los duplicados son alias implícitos para la enumeración previa con el mismo valor).
Además, puede iterar sobre todos los valores únicos y para cada valor sobre todos sus alias, lo cual es útil si desea generar "envoltorios" de lenguaje de scripts para estos, como hacemos con ruby.
Si está interesado en usar esto y tiene preguntas, no dude en ponerse en contacto conmigo por correo electrónico. (cuzdav en gmail).
Espero que esto ayude. (No hay mucha documentación aparte del conjunto de pruebas y el código de demostración, y la fuente si le interesa).
Chris
Uno de mis colegas ha implementado una herramienta para generar clases que hacen la mayoría (si no todas) de lo que quiere:
http://code.google.com/p/enumgen/
La implementación actual está en Lisp, pero no lo tengas en contra de él :-)
puede echar un vistazo a java enum ( http://madbean.com/2004/mb2004-3/ ) y esta idea: http://en.wikipedia.org/wiki/Curiously_Recurring_Template_Pattern
Este artículo muestra cómo generar la versión de cadena de un valor enumerado, aunque requiere que usted escriba el código para hacerlo usted mismo. También proporciona una macro de preprocesador para permitir fácilmente incrementar y disminuir las variables enumeradas, siempre que su enumeración sea continua.
Esta biblioteca proporciona incrementos y decrementos más flexibles.
La biblioteca enum_rev4.6.zip en Boost Vault proporciona conversiones de cadenas sencillas. Parece que admite incrementos y reducciones utilizando iteradores (lo que probablemente sea menos conveniente, pero funciona). Básicamente no está documentado, aunque el directorio libs / test contiene algún buen código de ejemplo.
De acuerdo, dejemos C ++ a un lado por un momento. C ++ es solo un superconjunto de C (lo que significa que todo lo que se puede hacer en C se puede hacer en C ++ también). Así que concentrémonos en el plain-C (porque ese es un idioma que conozco bien). C tiene enumeraciones:
enum fruit { apple, banana, cherry, peach, grape };
Esto es perfectamente legal C y los valores son contiguos, y apple tiene el valor cero y banana tiene el valor apple + 1. Puedes crear enumeraciones con agujeros, pero solo si haces agujeros explícitos de esta manera
enum fruit { apple = 0, banana, cherry = 20, peach, grape };
Mientras que la manzana es 0 y el plátano es 1, la cereza es 20, por lo tanto, el durazno es 21 y la uva es 22 y todo entre 1 y 20 es indefinido. Por lo general, no quieres agujeros. Puedes hacer lo siguiente:
enum fruit { apple = 0, banana, cherry, peach, grape };
enum fruit myFruit = banana;
myFruit++;
// myFruit is now cherry
printf("My fruit is cherry? %s/n", myFruit == cherry ? "YES" : "NO");
Esto imprimirá SÍ. También puede hacer lo siguiente:
enum fruit { apple = 0, banana, cherry = 20, peach, grape };
enum fruit myFruit = banana;
myFruit++;
// myFruit is now cherry
printf("My fruit is cherry? %s/n", myFruit == cherry ? "YES" : "NO");
Esto imprimirá NO, y el valor de myFruit no es el mismo que cualquiera de las constantes de enumeración.
Por cierto, para evitar eso debes decir "enum fruit myFruit", puedes evitar la enumeración con un typedef. Solo use "typedef enum fruit fruit"; en una línea propia. Ahora puedes decir "fruit myFruit" sin enumerar al frente. A menudo se hace directamente cuando se define la enumeración:
typedef enum fruit { apple = 0, banana, cherry, peach, grape } fruit;
fruit myFruit;
La desventaja es que ya no sabes que la fruta es una enumeración, puede ser un objeto, una estructura o cualquier otra cosa. Normalmente evito este tipo de typedefs, prefiero escribir enum en el frente si enum y struct en el frente si es struct (los usaré aquí porque se ve mejor).
Obtener el valor de la cadena no es posible. En tiempo de ejecución, una enumeración es solo un número. Eso significa que no es posible si no sabes qué tipo de enumeración es (como 0 podría ser manzana, pero también podría ser algo diferente de un conjunto de enumeración diferente). Sin embargo, si sabes que es una fruta, entonces es fácil escribir una función que lo haga por ti. El preprocesador es tu amigo :-)
typedef enum fruit {
apple = 0,
banana,
cherry,
peach,
grape
} fruit;
#define STR_CASE(x) case x: return #x
const char * enum_fruit_to_string(fruit f) {
switch (f) {
STR_CASE(apple); STR_CASE(banana); STR_CASE(cherry);
STR_CASE(peach); STR_CASE(grape);
}
return NULL;
}
#undef STR_CASE
static void testCall(fruit f) {
// I have no idea what fruit will be passed to me, but I know it is
// a fruit and I want to print the name at runtime
printf("I got called with fruit %s/n", enum_fruit_to_string(f));
}
int main(int argc, char ** argv) {
printf("%s/n", enum_fruit_to_string(banana));
fruit myFruit = cherry;
myFruit++; // myFruit is now peach
printf("%s/n", enum_fruit_to_string(myFruit));
// I can also pass an enumeration to a function
testCall(grape);
return 0;
}
Salida:
banana
peach
I got called with fruit grape
Esto es exactamente lo que quería o ¿estoy totalmente equivocado aquí?
Es un software inédito pero parece que BOOST_ENUM de Frank Laub podría encajar en la factura. La parte que me gusta es que puedes definir una enumeración dentro del alcance de una clase que la mayoría de las enumeraciones basadas en macros normalmente no te dejan hacer. Está ubicado en Boost Vault en: http://www.boostpro.com/vault/index.php?action=downloadfile&filename=enum_rev4.6.zip&directory=& No ha visto ningún desarrollo desde 2006, así que no saber qué tan bien se compila con las nuevas versiones de Boost. Busque en libs / test un ejemplo de uso. También hay Boost smart_enum (no lanzado tampoco). Hace la parte del iterador de su pregunta, pero no la salida a una cadena. http://cryp.to/smart-enum/
Escribí un enum_iterator
que hace esto, junto con una macro ENUM
usando Boost.Preprocessor:
#include <iostream>
#include "enum.hpp"
ENUM(FooEnum,
(N)
(A = 1)
(B = 2)
(C = 4)
(D = 8));
int main() {
litb::enum_iterator< FooEnum, litb::SparseRange<FooEnum> > i = N, end;
while(i != end) {
std::cout << i.to_string() << ": " << *i << std::endl;
++i;
}
}
Declara la enumeración como simple enumeración antigua, por lo que aún puede utilizarla para fines "normales". El iterador se puede usar también para otras enumeraciones normales que tienen valores secuenciales, por eso tiene un segundo parámetro de plantilla que se establece por defecto en litb::ConsequtiveRange<>
. Se ajusta a los requisitos del iterador bidireccional.
El código tonto se puede descargar desde aquí