¿Es posible determinar la cardinalidad de un c ++ enum class:
enum class Example { A, B, C, D, E };
Intenté usar sizeof, sin embargo, devuelve el tamaño de un elemento de enumeración.
sizeof(Example); // Returns 4 (on my architecture)
¿Existe una forma estándar de obtener la cardinalidad (5 en mi ejemplo)?
c++
c++11
cardinality
enum-class
bquenin
fuente
fuente

enumyenum classes son conceptos muy diferentes.enum classvalores, entonces, ¿cuál sería el beneficio de saber el número?Respuestas:
No directamente, pero podrías usar el siguiente truco:
enum class Example { A, B, C, D, E, Count };Entonces la cardinalidad está disponible como
static_cast<int>(Example::Count).Por supuesto, esto solo funciona bien si permite que los valores de la enumeración se asignen automáticamente, comenzando desde 0.Si ese no es el caso, puede asignar manualmente la cardinalidad correcta a Count, que en realidad no es diferente de tener que mantener una constante separada de todas formas:
enum class Example { A = 1, B = 2, C = 4, D = 8, E = 16, Count = 5 };La única desventaja es que el compilador le permitirá usarlo
Example::Countcomo argumento para un valor de enumeración, ¡así que tenga cuidado si usa esto! (Aunque personalmente considero que esto no es un problema en la práctica).fuente
enum classes en lugar de simpleenums. Editaré en un elenco para que quede claro.Para C ++ 17 puede usar
magic_enum::enum_countlib https://github.com/Neargye/magic_enum :magic_enum::enum_count<Example>()-> 4.¿Dónde está el inconveniente?
Esta biblioteca utiliza un truco específico del compilador (basado en
__PRETTY_FUNCTION__/__FUNCSIG__), que funciona en Clang> = 5, MSVC> = 15.3 y GCC> = 9.Pasamos por el rango de intervalo dado y encontramos todas las enumeraciones con un nombre, este será su recuento. Leer más sobre limitaciones
Mucho más sobre este truco en esta publicación https://taylorconor.com/blog/enum-reflection .
fuente
constexpr auto TEST_START_LINE = __LINE__; enum class TEST { // Subtract extra lines from TEST_SIZE if an entry takes more than one ONE = 7 , TWO = 6 , THREE = 9 }; constexpr auto TEST_SIZE = __LINE__ - TEST_START_LINE - 3;Esto se deriva de la respuesta de UglyCoder, pero la mejora de tres maneras.
BEGINySIZE) ( la respuesta de Cameron también tiene este problema).Conserva la ventaja de UglyCoder sobre la respuesta de Cameron de que a los enumeradores se les pueden asignar valores arbitrarios.
Un problema (compartido con UglyCoder pero no con Cameron ) es que hace que las nuevas líneas y los comentarios sean significativos ... lo cual es inesperado. Entonces, alguien podría agregar una entrada con espacios en blanco o un comentario sin ajustar
TEST_SIZEel cálculo.fuente
enum class TEST { BEGIN = __LINE__ , ONE , TWO , NUMBER = __LINE__ - BEGIN - 1 }; auto const TEST_SIZE = TEST::NUMBER; // or this might be better constexpr int COUNTER(int val, int ) { return val; } constexpr int E_START{__COUNTER__}; enum class E { ONE = COUNTER(90, __COUNTER__) , TWO = COUNTER(1990, __COUNTER__) }; template<typename T> constexpr T E_SIZE = __COUNTER__ - E_START - 1;fuente
shortpodría aumentarse, porintejemplo, al hacer una construcción de unidad. (Yo diría que esto es más de un problema con la unidad que construye con su truco propuesto, sin embargo.)Hay un truco basado en X () - macros: image, tienes la siguiente enumeración:
enum MyEnum {BOX, RECT};Vuelva a formatearlo para:
#define MyEnumDef \ X(BOX), \ X(RECT)Entonces el siguiente código define el tipo de enumeración:
enum MyEnum { #define X(val) val MyEnumDef #undef X };Y el siguiente código calcula el número de elementos de enumeración:
template <typename ... T> void null(T...) {} template <typename ... T> constexpr size_t countLength(T ... args) { null(args...); //kill warnings return sizeof...(args); } constexpr size_t enumLength() { #define XValue(val) #val return countLength(MyEnumDef); #undef XValue } ... std::array<int, enumLength()> some_arr; //enumLength() is compile-time std::cout << enumLength() << std::endl; //result is: 2 ...fuente
#define MyEnumDef(y colocándola#define X(val) val), lo que le permite contar el número de elementos usando solo#define X(val) +1constexpr std::size_t len = MyEnumDef;.Un truco que puede probar es agregar un valor de enumeración al final de su lista y usarlo como tamaño. En tu ejemplo
enum class Example { A, B, C, D, E, ExampleCount };fuente
enums simples , esto no funcionará comoExampleCountes del tipoExample. Para obtener el número de elementos enExample,ExampleCounttendría que convertirse en un tipo entero.Si utiliza las utilidades del preprocesador de boost, puede obtener el recuento usando
BOOST_PP_SEQ_SIZE(...).Por ejemplo, se podría definir la
CREATE_ENUMmacro de la siguiente manera:#include <boost/preprocessor.hpp> #define ENUM_PRIMITIVE_TYPE std::int32_t #define CREATE_ENUM(EnumType, enumValSeq) \ enum class EnumType : ENUM_PRIMITIVE_TYPE \ { \ BOOST_PP_SEQ_ENUM(enumValSeq) \ }; \ static constexpr ENUM_PRIMITIVE_TYPE EnumType##Count = \ BOOST_PP_SEQ_SIZE(enumValSeq); \ // END MACROLuego, llamando a la macro:
generaría el siguiente código:
enum class Example : std::int32_t { A, B, C, D, E }; static constexpr std::int32_t ExampleCount = 5;Esto es solo un rasguño de la superficie con respecto a las herramientas del preprocesador de impulso. Por ejemplo, su macro también podría definir a / desde utilidades de conversión de cadenas y operadores ostream para su enumeración fuertemente tipada.
Más sobre las herramientas de preprocesador boost aquí: https://www.boost.org/doc/libs/1_70_0/libs/preprocessor/doc/AppendixA-AnIntroductiontoPreprocessorMetaprogramming.html
Aparte, estoy totalmente de acuerdo con @FantasticMrFox en que el
Countvalor enumerado adicional empleado en la respuesta aceptada creará muchos dolores de cabeza de advertencia al compilador si se usa unaswitchdeclaración. Encuentro launhandled caseadvertencia del compilador bastante útil para un mantenimiento más seguro del código, por lo que no querría socavarlo.fuente
Se puede resolver con un truco con std :: initializer_list:
#define TypedEnum(Name, Type, ...) \ struct Name { \ enum : Type{ \ __VA_ARGS__ \ }; \ static inline const size_t count = []{ \ static Type __VA_ARGS__; return std::size({__VA_ARGS__}); \ }(); \ };Uso:
#define Enum(Name, ...) TypedEnum(Name, int, _VA_ARGS_) Enum(FakeEnum, A = 1, B = 0, C) int main() { std::cout << FakeEnum::A << std::endl << FakeEnun::count << std::endl; }fuente
Hay otra forma que no depende de los recuentos de líneas o las plantillas. El único requisito es pegar los valores de enumeración en su propio archivo y hacer que el preprocesador / compilador haga el recuento así:
my_enum_inc.h
ENUMVAL(BANANA) ENUMVAL(ORANGE=10) ENUMVAL(KIWI) ... #undef ENUMVALmy_enum.h
typedef enum { #define ENUMVAL(TYPE) TYPE, #include "my_enum_inc.h" } Fruits; #define ENUMVAL(TYPE) +1 const size_t num_fruits = #include "my_enum_inc.h" ;Esto le permite poner comentarios con los valores de enumeración, reasignar valores y no inyecta un valor de enumeración de 'recuento' no válido que debe ignorarse / contabilizarse en el código.
Si no le importan los comentarios, no necesita un archivo adicional y puede hacer lo mismo que alguien mencionado anteriormente, por ejemplo:
#define MY_ENUM_LIST \ ENUMVAL(BANANA) \ ENUMVAL(ORANGE = 7) \ ENUMVAL(KIWI)y reemplace las
#include "my_enum_inc.h"directivas con MY_ENUM_LIST, pero deberá hacerlo#undef ENUMVALdespués de cada uso.fuente
Otro tipo de solución "estúpida" para esto es:
enum class Example { A, B, C, D, E }; constexpr int ExampleCount = [] { Example e{}; int count = 0; switch (e) { case Example::A: count++; case Example::B: count++; case Example::C: count++; case Example::D: count++; case Example::E: count++; } return count; }();Al compilar esto
-Werror=switch, asegúrese de obtener una advertencia del compilador si omite o duplica cualquier caso de cambio. También es constexpr, por lo que se calcula en tiempo de compilación.Pero tenga en cuenta que incluso para un en,
enum classel valor inicializado predeterminado es 0 incluso si el primer valor de la enumeración no es 0. Por lo tanto, debe comenzar en 0 o usar explícitamente el primer valor.fuente
No, tienes que escribirlo en el código.
fuente
También puede considerar
static_cast<int>(Example::E) + 1cuál elimina el elemento extra.fuente
Example::Ecomo último valor en la enumeración. Incluso si este no es el caso,Example::Eel valor literal de 'puede cambiar.Reflexión TS: reflexión estática de enumeraciones (y otros tipos)
Reflection TS , particularmente [reflect.ops.enum] / 2 de la última versión del borrador de Reflection TS ofrece la
get_enumeratorsTransformationTraitoperación:[reflect.ops.objseq] del borrador cubre
ObjectSequenceoperaciones, donde particularmente [reflect.ops.objseq] / 1 cubre elget_sizerasgo para extraer el número de elementos para un metaobjeto que satisfaceObjectSequence:Por lo tanto, en Reflection TS debían aceptarse e implementarse en su forma actual, el número de elementos de una enumeración se puede calcular, en tiempo de compilación, de la siguiente manera:
enum class Example { A, B, C, D, E }; using ExampleEnumerators = get_enumerators<Example>::type; static_assert(get_size<ExampleEnumerators>::value == 5U, "");donde es probable que veamos plantillas de alias
get_enumerators_vyget_type_vsimplifiquemos aún más la reflexión:enum class Example { A, B, C, D, E }; using ExampleEnumerators = get_enumerators_t<Example>; static_assert(get_size_v<ExampleEnumerators> == 5U, "");Estado en Reflection TS
Como se indica en el informe de viaje de Herb Sutter : Reunión de estándares ISO C ++ de verano (Rapperswil) de la reunión de verano del comité ISO C ++ del 9 de junio de 2018, Reflection TS ha sido declarado como completo
e inicialmente se planeó para C ++ 20 , pero no está claro si Reflection TS aún tendrá la oportunidad de llegar a la versión de C ++ 20.
fuente