Можно ли определить количество элементов c ++ enum class
:
enum class Example { A, B, C, D, E };
Я пробовал использовать sizeof
, но возвращает размер элемента перечисления.
sizeof(Example); // Returns 4 (on my architecture)
Есть ли стандартный способ получить мощность (5 в моем примере)?
c++
c++11
cardinality
enum-class
bquenin
источник
источник
enum
иenum class
es - это очень разные концепции.enum class
значения, так какая польза от знания числа?Ответы:
Не напрямую, но вы можете использовать следующий трюк:
enum class Example { A, B, C, D, E, Count };
Тогда мощность доступна как
static_cast<int>(Example::Count)
.Конечно, это работает хорошо только в том случае, если вы позволяете автоматически присваивать значения перечисления, начиная с 0. Если это не так, вы можете вручную назначить правильное количество элементов для Count, что на самом деле ничем не отличается от необходимости поддерживать отдельную константу так или иначе:
enum class Example { A = 1, B = 2, C = 4, D = 8, E = 16, Count = 5 };
Единственным недостатком является то, что компилятор позволит вам использовать
Example::Count
в качестве аргумента значение перечисления - поэтому будьте осторожны, если вы используете это! (Лично я считаю, что на практике это не проблема.)источник
enum class
es, а не с простымenum
s. Я отредактирую гипс, чтобы было понятно.Для С ++ 17 вы можете использовать
magic_enum::enum_count
из библиотеки https://github.com/Neargye/magic_enum :magic_enum::enum_count<Example>()
-> 4.Где недостаток?
Эта библиотека использует специфичный для компилятора хак (на основе
__PRETTY_FUNCTION__
/__FUNCSIG__
), which works on Clang >= 5, MSVC >= 15.3 and GCC >= 9.Мы проходим заданный диапазон интервалов и находим все перечисления с именем, это будет их количество. Узнать больше об ограничениях
Подробнее об этом взломе читайте в этом посте https://taylorconor.com/blog/enum-reflection .
источник
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;
Это получено из ответа UglyCoder, но улучшает его тремя способами.
BEGIN
иSIZE
) нет дополнительных элементов ( ответ Кэмерона также имеет эту проблему.)Он сохраняет преимущество UglyCoder перед ответом Кэмерона, что счетчикам могут быть присвоены произвольные значения.
Проблема (общая с UglyCoder, но не с Кэмероном ) заключается в том, что он делает новые строки и комментарии значительными ... что неожиданно. Таким образом, кто-то может добавить запись с пробелом или комментарий, не изменяя
TEST_SIZE
расчет.источник
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;
источник
short
может быть увеличено,int
например, при сборке единства. (Я бы сказал, что это больше проблема с сборками единства, чем с предложенным вами трюком.)Есть одна хитрость, основанная на X () - macros: image, у вас есть следующее перечисление:
enum MyEnum {BOX, RECT};
Отформатируйте его так:
#define MyEnumDef \ X(BOX), \ X(RECT)
Затем следующий код определяет тип перечисления:
enum MyEnum { #define X(val) val MyEnumDef #undef X };
И следующий код вычисляет количество элементов перечисления:
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 ...
источник
#define MyEnumDef
(и вставив ее#define X(val) val
), что позволяет подсчитывать количество элементов, используя только#define X(val) +1
constexpr std::size_t len = MyEnumDef;
.Один из приемов, который вы можете попробовать, - это добавить значение перечисления в конец вашего списка и использовать его в качестве размера. В вашем примере
enum class Example { A, B, C, D, E, ExampleCount };
источник
enum
s, это не будет работать, какExampleCount
is typeExample
. Чтобы получить количество элементов вExample
,ExampleCount
необходимо привести к целочисленному типу.Если вы используете утилиты препроцессора boost, вы можете получить счет, используя
BOOST_PP_SEQ_SIZE(...)
.Например, можно определить
CREATE_ENUM
макрос следующим образом:#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
Затем вызов макроса:
сгенерирует следующий код:
enum class Example : std::int32_t { A, B, C, D, E }; static constexpr std::int32_t ExampleCount = 5;
Что касается инструментов препроцессора ускорения, это только верхушка на поверхности. Например, ваш макрос может также определять утилиты преобразования строк в / из и операторы ostream для строго типизированного перечисления.
Подробнее об инструментах препроцессора Boost здесь: https://www.boost.org/doc/libs/1_70_0/libs/preprocessor/doc/AppendixA-AnIntroductiontoPreprocessorMetaprogramming.html
В стороне, я полностью согласен с @FantasticMrFox, что дополнительное
Count
перечисляемое значение, используемое в принятом ответе, создаст изобилие головных болей с предупреждениями компилятора при использованииswitch
оператора. Я считаюunhandled case
предупреждение компилятора весьма полезным для более безопасного обслуживания кода, поэтому не хочу его нарушать.источник
Это можно решить с помощью трюка с 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__}); \ }(); \ };
Применение:
#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; }
источник
Есть еще один способ, который не зависит от количества строк или шаблонов. Единственное требование - вставить значения перечисления в их собственный файл и заставить препроцессор / компилятор выполнять подсчет следующим образом:
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" ;
Это позволяет вам помещать комментарии со значениями перечисления, повторно назначать значения и не вводить недопустимое значение перечисления count, которое необходимо игнорировать / учитывать в коде.
Если вас не интересуют комментарии, вам не нужен дополнительный файл, и вы можете действовать так, как указано выше, например:
#define MY_ENUM_LIST \ ENUMVAL(BANANA) \ ENUMVAL(ORANGE = 7) \ ENUMVAL(KIWI)
и замените
#include "my_enum_inc.h"
директивы на MY_ENUM_LIST, но это потребуется#undef ENUMVAL
после каждого использования.источник
Другой вид "глупого" решения этой проблемы:
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; }();
Компилируя это с
-Werror=switch
вы убедитесь, что получите предупреждение компилятора, если вы пропустите или дублируете любой случай переключения. Это также constexpr, поэтому он вычисляется во время компиляции.Но обратите внимание, что даже для en
enum class
инициализированное значение по умолчанию равно 0, даже если первое значение перечисления не равно 0. Таким образом, вы должны либо начать с 0, либо явно использовать первое значение.источник
Нет, вы должны написать это в коде.
источник
Вы также можете подумать,
static_cast<int>(Example::E) + 1
что устраняет лишний элемент.источник
Example::E
последним значением в перечислении. Даже если это не так,Example::E
буквальное значение может измениться.Reflection TS: статическое отражение перечислений (и других типов)
Reflection TS , в частности [Reflection.ops.enum] / 2 последней версии проекта Reflection TS, предлагает следующие
get_enumerators
TransformationTrait
операции:[Reflect.ops.objseq] черновика охватывает
ObjectSequence
операции, где, в частности, [detect.ops.objseq] / 1 охватывает типажget_size
для извлечения количества элементов для мета-объекта, удовлетворяющегоObjectSequence
:Таким образом, в Reflection TS должны были быть приняты и реализованы в его текущей форме, количество элементов перечисления может быть вычислено во время компиляции следующим образом:
enum class Example { A, B, C, D, E }; using ExampleEnumerators = get_enumerators<Example>::type; static_assert(get_size<ExampleEnumerators>::value == 5U, "");
где мы, вероятно, увидим шаблоны псевдонимов
get_enumerators_v
и ещеget_type_v
больше упростим отражение:enum class Example { A, B, C, D, E }; using ExampleEnumerators = get_enumerators_t<Example>; static_assert(get_size_v<ExampleEnumerators> == 5U, "");
Статус на Reflection TS
Как указано в отчете о поездке Херба Саттера : Летнее собрание стандартов ISO C ++ (Rapperswil) на летнем заседании комитета ISO C ++ 9 июня 2018 г., Reflection TS был объявлен полностью функциональным.
и изначально планировался для C ++ 20 , но несколько неясно, будет ли у Reflection TS еще шанс попасть в выпуск C ++ 20.
источник