É possível determinar a cardinalidade de um c ++ enum class
:
enum class Example { A, B, C, D, E };
Tentei usar sizeof
, no entanto, ele retorna o tamanho de um elemento enum.
sizeof(Example); // Returns 4 (on my architecture)
Existe uma maneira padrão de obter a cardinalidade (5 no meu exemplo)?
c++
c++11
cardinality
enum-class
bquenin
fonte
fonte
enum
eenum class
esses são conceitos muito diferentes.enum class
valor, então qual seria o benefício em saber o número?Respostas:
Não diretamente, mas você pode usar o seguinte truque:
enum class Example { A, B, C, D, E, Count };
Então a cardinalidade está disponível como
static_cast<int>(Example::Count)
.Claro, isso só funciona bem se você permitir que os valores do enum sejam atribuídos automaticamente, começando de 0. Se não for o caso, você pode atribuir manualmente a cardinalidade correta para Count, o que realmente não é diferente de ter que manter uma constante separada de qualquer forma:
enum class Example { A = 1, B = 2, C = 4, D = 8, E = 16, Count = 5 };
A única desvantagem é que o compilador permitirá que você use
Example::Count
como um argumento para um valor enum - portanto, tenha cuidado ao usar isso! (Eu pessoalmente acho que isso não é um problema na prática, no entanto.)fonte
enum class
es em vez deenum
s simples . Vou editar em um elenco para ficar claro.Para C ++ 17, você pode usar
magic_enum::enum_count
de lib https://github.com/Neargye/magic_enum :magic_enum::enum_count<Example>()
-> 4.Onde está a desvantagem?
Esta biblioteca usa um hack específico do compilador (baseado em
__PRETTY_FUNCTION__
/__FUNCSIG__
), que funciona no Clang> = 5, MSVC> = 15.3 e GCC> = 9.Percorremos o intervalo de intervalo dado e encontramos todas as enumerações com um nome, esta será a sua contagem. Leia mais sobre as limitações
Muito mais sobre este hack neste post https://taylorconor.com/blog/enum-reflection .
fonte
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;
Isso é derivado da resposta do UglyCoder, mas a melhora de três maneiras.
BEGIN
eSIZE
) ( a resposta de Cameron também tem esse problema.)Ele mantém a vantagem de UglyCoder sobre a resposta de Cameron de que os enumeradores podem receber valores arbitrários.
Um problema (compartilhado com o UglyCoder, mas não com o Cameron ) é que ele cria novas linhas e comentários significativos ... o que é inesperado. Assim, alguém poderia adicionar uma entrada com espaço em branco ou um comentário sem ajustar
TEST_SIZE
o cálculo de.fonte
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;
fonte
short
pode ser aumentado, porint
exemplo, ao fazer uma construção de unidade. (Eu diria que este é mais um problema com a construção de unidade do que com seu truque proposto, no entanto.)Existe um truque baseado em X () - macros: imagem, você tem o seguinte enum:
enum MyEnum {BOX, RECT};
Reformate para:
#define MyEnumDef \ X(BOX), \ X(RECT)
Em seguida, o código a seguir define o tipo de enum:
enum MyEnum { #define X(val) val MyEnumDef #undef X };
E o código a seguir calcula o número de elementos enum:
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 ...
fonte
#define MyEnumDef
(e inserindo-a#define X(val) val
), que permite contar o número de elementos usando apenas#define X(val) +1
constexpr std::size_t len = MyEnumDef;
.Um truque que você pode tentar é adicionar um valor enum no final da sua lista e usá-lo como o tamanho. No seu exemplo
enum class Example { A, B, C, D, E, ExampleCount };
fonte
enum
s simples , isso não funcionará comoExampleCount
estáExample
. Para obter o número de elementos emExample
,ExampleCount
teria que ser convertido em um tipo inteiro.Se você fizer uso dos utilitários de pré-processador do boost, poderá obter a contagem usando
BOOST_PP_SEQ_SIZE(...)
.Por exemplo, pode-se definir a
CREATE_ENUM
macro da seguinte forma:#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 MACRO
Em seguida, chamando a macro:
geraria o seguinte código:
enum class Example : std::int32_t { A, B, C, D, E }; static constexpr std::int32_t ExampleCount = 5;
Isso é apenas arranhar a superfície com relação às ferramentas do pré-processador boost. Por exemplo, sua macro também pode definir de / para utilitários de conversão de string e operadores ostream para seu enum fortemente tipado.
Mais sobre as ferramentas de pré-processador de boost aqui: https://www.boost.org/doc/libs/1_70_0/libs/preprocessor/doc/ApethoscopeA-AnIntroductiontoPreprocessorMetaprogramming.html
Como um aparte, eu concordo totalmente com @FantasticMrFox que o
Count
valor enumerado adicional empregado na resposta aceita criará muitas dores de cabeça de advertência do compilador se usar umaswitch
instrução. Acho ounhandled case
aviso do compilador bastante útil para uma manutenção de código mais segura, portanto, não quero prejudicá-lo.fonte
Isso pode ser resolvido por um truque com 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; }
fonte
Existe outra maneira que não depende de contagens de linha ou modelos. O único requisito é colar os valores enum em seu próprio arquivo e fazer com que o pré-processador / compilador faça a contagem assim:
my_enum_inc.h
ENUMVAL(BANANA) ENUMVAL(ORANGE=10) ENUMVAL(KIWI) ... #undef ENUMVAL
my_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" ;
Isso permite que você coloque comentários com os valores de enum, reatribua valores e não injeta um valor de enum 'contagem' inválido que precisa ser ignorado / contabilizado no código.
Se você não se importa com os comentários, não precisa de um arquivo extra e pode fazer como alguém mencionado acima, por exemplo:
#define MY_ENUM_LIST \ ENUMVAL(BANANA) \ ENUMVAL(ORANGE = 7) \ ENUMVAL(KIWI)
e substitua as
#include "my_enum_inc.h"
diretivas por MY_ENUM_LIST, mas você precisará fazer isso#undef ENUMVAL
após cada uso.fonte
Outro tipo de solução "estúpida" para isso é:
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; }();
Compilando isso com
-Werror=switch
você, certifique-se de obter um aviso do compilador se omitir ou duplicar qualquer caso de switch. Também é constexpr, portanto, é calculado em tempo de compilação.Mas observe que mesmo para um en,
enum class
o valor inicializado padrão é 0, mesmo se o primeiro valor do enum não for 0. Portanto, você deve iniciar em 0 ou usar explicitamente o primeiro valor.fonte
Não, você tem que escrever no código.
fonte
Você também pode considerar o
static_cast<int>(Example::E) + 1
que elimina o elemento extra.fonte
Example::E
o último valor no enum. Mesmo que esse não seja o caso,Example::E
o valor literal de pode mudar.Reflexão TS: reflexão estática de enums (e outros tipos)
O Reflection TS , particularmente [reflect.ops.enum] / 2 da versão mais recente do rascunho do Reflection TS oferece a
get_enumerators
TransformationTrait
operação:[reflect.ops.objseq] do rascunho cobre as
ObjectSequence
operações, onde particularmente [reflect.ops.objseq] / 1 cobre oget_size
traço para extrair o número de elementos para um meta-objeto que satisfaçaObjectSequence
:Assim, no Reflection TS para ser aceito e implementado em sua forma atual, o número de elementos de um enum pode ser calculado, em tempo de compilação, da seguinte forma:
enum class Example { A, B, C, D, E }; using ExampleEnumerators = get_enumerators<Example>::type; static_assert(get_size<ExampleEnumerators>::value == 5U, "");
onde provavelmente veremos modelos de alias
get_enumerators_v
eget_type_v
para simplificar ainda mais a reflexão:enum class Example { A, B, C, D, E }; using ExampleEnumerators = get_enumerators_t<Example>; static_assert(get_size_v<ExampleEnumerators> == 5U, "");
Status no Reflection TS
Conforme declarado pelo relatório de viagem de Herb Sutter : Reunião de padrões ISO C ++ de verão (Rapperswil) da reunião de verão do comitê ISO C ++ de 9 de junho de 2018, o Reflection TS foi declarado como recurso completo
e foi inicialmente planejado para C ++ 20 , mas não está claro se o Reflection TS ainda terá a chance de torná-lo no lançamento do C ++ 20.
fonte